Django 仿博客园练习
数据库搭建
部分功能介绍
【一】注册
(1)效果显示、简单简介
- 主要亮点
- 结合了layui和forms组件
- 默认头像可以随着性别的选择发生改变
- 自定义头像可以实时更新显示
- forms组件报错信息可以局部刷新显示在对应框体下面
- 没有直接使用layui的前端验证
- 后端验证更加安全
- 报错信息随鼠标的聚焦可以消失
(2)重要代码逻辑梳理
(2.1)头像随性别切换
- 首先给性别按钮绑定事件监听
- 根据当前的性别进行进行img标签的src属性进行修改
<script>
{#头像随性别切换#}
let genderButtons = document.getElementsByName('gender');
genderButtons.forEach(function (button) {button.addEventListener('click', function () {{#定义默认路径#}let defaultMaleAvatar = '/static/avatar/default_male.png'let defaultFemaleAvatar = '/static/avatar/default_female.png'{#获取当前的值进行修改#}let selectGender = $(this).val();let avatarImg = $("#avatarImg")if (selectGender === 'm') {avatarImg.attr('src', defaultMaleAvatar)} else if (selectGender === 'f') {avatarImg.attr('src', defaultFemaleAvatar)}});
});
</script>
(2.2)自定义头像预览
- 创建
FileReader
对象 - 获取当前头像数据
$()[0].files[0]
- 转换读取文件内容为
DataURL
FileReader
对象调用方法onload实时预览
<script>
{#头像预览#}
$("#selectAvatar").change(() => {{#创建FileReader对象#}let fileReader = new FileReader();{#读取头像#}let avatarData = $("#selectAvatar")[0].files[0];{#读取文件内容转换为Data URL对象#}fileReader.readAsDataURL(avatarData){#重新赋值,实现预览功能#}fileReader.onload = () => {$("#avatarImg").attr('src', fileReader.result)}
})
</script>
(2.3)报错信息预览、添加报错红框
- 视图层代码和介绍
def register(request):register_form = RegisterForm()if request.method == "POST" and request.is_ajax():register_form = RegisterForm(request.POST)# 使用forms组件校验,不合法返回错误信息if not register_form.is_valid():return json_response(code=2001, errors=register_form.errors)# 拿出所有检验通过的数据clean_data = register_form.cleaned_data# 合法信息需要抛出confirm_password,userinfo表中没有clean_data.pop('confirm_password')# 获取头像数据avatar = request.FILES.get("avatar")# 拿不到数据就用默认头像if not avatar:if clean_data.get('gender') == 'm':clean_data['avatar'] = "static/avatar/default_male.png"else:clean_data['avatar'] = "static/avatar/default_female.png"else:clean_data['avatar'] = avatar# 注册,密码加密处理,clean_data解压赋值user_obj = UserInfo.objects.create_user(**clean_data)# 反向解析跳转地址next_url = reverse("login")return json_response(message=f"{user_obj.username}注册成功", next_url=next_url)return render(request, 'register.html', locals())
- 将视图层返回的报错信息response.errors
- 根据前端页面的文本框的ID进行标签查找
- 链式操作
- 找到span标签添加errroMessage
- 找到input标签添加layui红框样式
$.each(response.errors, (spanId, errorMessage) => {let tagId = "#id_" + spanId{#根据id找到span添加报错信息#}{#找到input的标签添加红框#} $(tagId).next().next().text(errorMessage[0]).parent().find('input').addClass("layui-form-danger")
})
【二】登录
(1)效果显示、简单介绍
- 主要亮点是
- 验证码的处理
- 背景白色、字符颜色偏暗且不会出现很亮的颜色
- 前端页面验证码刷新方法简单,点击图片即可
- 验证码图片更新方式简单,在标签内添加事件即可
(2)重要代码逻辑梳理
(2.1)验证码生成
- 使用PIL模块中Image对象生成白板
- 使用PIL模块中ImageDraw对象根据白板创建画笔
- 使用PIL模块中的ImageFont对象指定画笔的字体和大小
- 使用循环和随机颜色的方式
- 依次在不同的位置绘画不同颜色的验证码字符
- 再次使用for循环和随机颜色添加背景噪点
- 使用IO模块的BytesIO对象创建内存缓存区
- 将图片对象保存为PNG格式
- 将验证码字符和验证码图片字节流数据返回
import random
import stringfrom PIL import Image, ImageFont, ImageDraw
# Image:生成图片
# ImageDraw:图片内容绘制
# ImageFont:字体样式# BytesIO:临时存储数据,返回二进制数据
from io import BytesIO, StringIO# 列表推导式生成随机颜色
# 高亮度的还是少一点的好
def rgb_number():return tuple([random.randint(0, 200) for _ in range(3)])def create_captcha(img_type="RGB", img_size=(310, 38)):# 白板对象# 图片类型,图片大小,图片颜色# img_obj = Image.new(img_type, img_size, rgb_number())img_obj = Image.new(img_type, img_size, color=(241, 241, 241)) # 白板# 画笔对象img_draw = ImageDraw.Draw(img_obj)# 指定字体和大小img_font = ImageFont.truetype('static/font/汉仪晴空体简.ttf', 30)captcha = ''for i in range(4):choices_list = list(string.digits + string.ascii_letters)temp_captcha = random.choice(choices_list)# 开始绘制# 位置、字符、颜色、字体img_draw.text((i * 30 + 10, 2), temp_captcha, rgb_number(), img_font)captcha += temp_captcha# 添加噪点for _ in range(100):x = random.randint(0, img_size[0] - 1)y = random.randint(0, img_size[1] - 1)img_draw.point((x, y), fill=rgb_number())# 文件对象的内存缓冲区,可以用来读写二进制数据io_obj = BytesIO()# 将图像对象img_obj保存为PNG格式,并将保存后的数据写入到之前创建的BytesIO对象io_obj中。img_obj.save(io_obj, 'png')# 从BytesIO对象io_obj中获取保存的图像数据,并将其赋值给变量img_dataimg_data = io_obj.getvalue()# 返回验证码和图片数据return captcha, img_data
(2.2)验证码前端刷新方法
- 视图层
- 指定验证码图片大小
- 将验证码添加到session中用于校验
- 返回HttpResponse()对象
def get_captcha(request):# 获取验证码和图片code, img_data = create_captcha(img_size=(122, 36))# 将验证码保存在session中用于验证request.session['captcha'] = codereturn HttpResponse(img_data)
- 前端
- src为视图层的路由映射
- style指定大小
onclick
绑定事件- 由于在当前路由后添加?携带其他信息仍能找到指定路由
- 所以在每次点击之后在其后面添加时间就可以完整刷新的功能
<img src="{% url 'get_captcha' %}" style="width: 100%; height: 38px" id="captcha"onclick="this.src = '{% url 'get_captcha' %}' + '?t='+ new Date().getTime();">
【三】修改头像
(1)效果显示、简单介绍
-
使用的是Bootstrap3的模态框
-
具有登录渲染当前头像的功能
-
亮点:
- 后端保存图像的方法写了两种
(2)修改头像代码梳理
(2.1)使用request修改登录人的头像
- 首先说弊端,只能修改登录人的头像
- 优势:简单,自动保存图片,图片名称处理(上传相同的头像保存不同的文件名)
- 代码逻辑
- 首先获取头像数据
- 然后使用request.user进行头像的修改
- 最后一定要执行save方法保存
@csrf_exempt
@login_required
def set_avatar(request):if request.is_ajax():if request.FILES:# 获取头像avatar = request.FILES.get('avatar')# 使用保存更改user_obj = request.useruser_obj.avatar = avataruser_obj.save()return json_response(message='头像修改成功')return json_response(code=2001, error="请添加新的头像")return json_response(code=2002, error='非Ajax请求')
(2.2)手动保存头像
- 弊端很明显,麻烦
- 代码逻辑
- 同样的首先获取头像数据
- 然后指定文件保存位置的位置
- 存在的问题就是文件名可能重复
- 最好自己在手动对文件名进行处理
- 手动保存
- 再次拼接保存在数据库中的位置
- 最后update更新
@csrf_exempt
@login_required
def set_avatar(request):if request.is_ajax():if request.FILES:# 获取头像avatar = request.FILES.get('avatar')# 需要手动拼接路径,模型层的up_load不起作用,甚至需要图片名字手动加参数才可以不重名file_path = os.path.join('media', 'avatar', avatar.name)# 保存文件with open(file_path, 'wb') as f:# 保存图片是个易错点for line in avatar.chunks():f.write(line)# 拼接保存在数据库中的路径path = os.path.join('avatar', avatar.name)UserInfo.objects.filter(username='iron').update(avatar=path)return json_response(message='头像修改成功')return json_response(code=2001, error="请添加新的头像")return json_response(code=2002, error='非Ajax请求')
【四】广告后台管理
(1)效果显示、简单介绍
- 左侧所有广告的轮播图,使用的是layui的轮播图功能
- 中间使用form表单显示所有的广告内容,form表单提供功能
- 编辑广告功能,使用的模态框
- 删除广告功能,使用ajax的局部刷新
- 右侧预览功能,使用ajax的局部刷新功能
- 底部使用了分页器进行分页显示
- 右侧可以查看指定广告的渲染样式
(2)广告编辑功能
- 我选择使用模态框
- 及点击不同的广告
- 模态框显示不同的原始广告信息
- 所以这个有个局部渲染的要求
- 需要用到ajax
- 广告提交
- 可以使用form表单提交
- 也可以使用ajax进行提交(选择这个)
- 代码逻辑(分别发送两次
ajax
请求)- 获取当前广告ID
- 向后端发送ajax请求
- 拿到原始数据
- 将原始数据渲染到模态框的表单中
- 要点:还需要保存这篇文章的ID的表单中
- 用于修改广告信息的ID查找
- 等待修改信息
- 再次发送ajax请求
- 将新的内容发送给后端进行处理
{#编辑广告第一步#}
{#编辑广告第一步#}
$(".set-adv").click(function (event) {event.preventDefault(){#获取当前广告ID#}let id = $(this).attr('value')$.ajax({url: "{% url 'set_adv_first' %}",type: "post",data: {"id": id},success: function (response) {if (response.code === 2000) {console.log(response)$('input[name="set_adv_mobile"]').attr('value', response.mobile);$('input[name="set_adv_title"]').attr('value', response.title);$('input[name="set_adv_desc"]').attr('value', response.desc);$('#set-adv-img').attr('src', response.img);// 在指定的 input 标签后面插入一个新的 input 标签$('input[name="set_adv_mobile"]').after($('<input>').attr({type: 'hidden', // 设置为隐藏类型name: 'now_id', // 设置 input 的 name 属性为 now_idvalue: response.now_id // 设置 input 的 value 属性为 now_id 的值}))if (response.is_background_img) {{#修改前端显示效果#}$('#set_adv_background').next().addClass('layui-form-onswitch'){#修改后端可以接收到的值#}$("#set_adv_background").click()}} else {alert(response.error)}}})
}){#编辑广告第二步#}
$("#set-adv-button").click((event) => {event.preventDefault(){#创建formData#}let formData = new FormData(){#获取form数据#}$.each($("#set-avd-form").serializeArray(), (_, dataDict) => {formData.append(dataDict.name, dataDict.value)}){#获取广告数据#}let advData = $("#setSelectAdv")[0].files[0];formData.append('set_adv_img', advData)console.log(formData){#发送ajax#}$.ajax({url: "{% url 'set_adv_second' %}",type: "post",data: formData,processData: false,contentType: false,success: (response) => {console.log(response)if (response.code === 2000) {{#成功提示并跳转#}Swal.fire({title: response.message,icon: "success",customClass: {popup: 'swal2-custom',icon: 'swal2-icon-custom'}}).then(() => {window.location.reload()});} else {console.log(response.code)Swal.fire({icon: "error",title: response.error,customClass: {popup: 'swal2-custom',icon: 'swal2-icon-custom'}});}}})
})
【五】广告个数动态加载
(1)效果显示、简单介绍
- 右侧的广告
- 通过循环遍历出来
- 根据页面中间的内容多少,进行最大存放广告数量的显示
- 比如
- 现在页面中间只用5条数据,那么右侧就只显示能存放的2-3个广告
- 如果页面中间有上百条数据,也就是说页面可以向下混动很多很多,那么右侧的广告也就随之展现更多的
(2)逻辑梳理
- 页面中的广告信息默认是不显示的
- display属性写成none
- 在页面加载完以后再根据页面高度对广告进行展示
- 首先在页面加载以后得到页面的总高度
- 然后根据广告的高度进行整除向下取整
- 以防个数超出已有的广告数量
- 对个数进行处理
- 最后个根据计算得到的个数进行切分slice显示show
{#右侧广告开始#}
<div class="col-md-2 right-content">{% block right-content %}{% for adv_obj in adv_queryset %}<div class="advertisement" style="display: none;">{% adv_show adv_obj.pk %}</div>{% endfor %}{% endblock %}
</div>
{#右侧广告结束#}
<script>$(document).ready(() => {{#页面总高度#}let totalDocumentHeight = $(document).height();// 对高度进行400px的整除取整数let numAdsToShow = Math.floor(totalDocumentHeight / 400);// 确保广告数量不会少于0或超过广告对象的总数let advCount = {{ adv_queryset|length }};numAdsToShow = Math.min(numAdsToShow, advCount);// 根据计算出的数量显示广告$('.advertisement').slice(0, numAdsToShow).show();
</script>
【六】点赞点踩
(1)效果显示、简单介绍
- 首先说明这个样式是来自于博客园的,逻辑代码是自己写的
- 亮点是
- 点赞点踩都是绑定的一个前端方法
- votePost
- 前端进行三元表达式运算
- 发送数据给后端进行逻辑处理
- 点赞点踩都是绑定的一个前端方法
(2)逻辑代码介绍
- 这个方法是不能写在$(document).ready()中
- 因为需要是全局定义域的方法,才可以被页面中直接调用
- 写在外面可以在页面的任何地方被调用,包括在HTML元素的
onclick
属性中,或者在页面的其他JavaScript脚本中。
- 首先对点赞还是点踩进行三元表达式运算
- 还可以简写
- 但是无论怎么简写发送给后端的都是字符串数据
- 后端需要loads或者字符串判断
- 将操作发送给后端进行逻辑处理
- 后端的处理逻辑有多种
- 可以操作后了就不能点赞点踩
- 可以作者不能改自己点赞点踩
- 可以撤消操作等
- 拿到后端处理后的数据
- 成功进行个数的渲染
- 失败显示错误信息
<script>// 点赞功能function votePost(article_id, flag) {{#三元表达式 和C的一样 和Python的不一样#}{#flag = true ? flag === 'Digg' : false#}{#简写#}flag = flag === 'Digg'{#获取提示信息的div#}let divEle = $("#message-error"){#用于后续修改点赞点踩数量#}let upEle = $("#digg_count")let downEle = $("#bury_count")$.ajax({url: "{% url 'up_down' %}",type: "post",data: {"flag": flag,{#右边注意需要是字符串格式#}{#{% csrf_token %}写在页面中即可,没有要求说写在表单里#}"csrfmiddlewaretoken": "{{ csrf_token }}","article_id": article_id,},success: function (response) {if (response.code === 2000) {{#渲染信息#}divEle.text(response.message){#修改点赞点踩#}upEle.text(response.up_num)downEle.text(response.down_num)} else {{#失败处理#}{#html自动转义#}divEle.html(response.error)}}})}
</script>
相关文章:

Django 仿博客园练习
数据库搭建 部分功能介绍 【一】注册 (1)效果显示、简单简介 主要亮点 结合了layui和forms组件默认头像可以随着性别的选择发生改变自定义头像可以实时更新显示forms组件报错信息可以局部刷新显示在对应框体下面 没有直接使用layui的前端验证后端验证…...

MySQL(常用函数、多表查询)
文章目录 1.数据库函数1.count函数案例答案count(*)与count(列)的区别 2.sum函数案例答案 3.avg函数案例答案 4.max/min函数案例答案 5.group by 分组统计案例答案 6.字符串相关函数演示练习 7.数学相关函数演示 8.日期相关函数演…...

【Pt】马灯贴图绘制过程 01-制作基础色
目录 一、导入模型并烘焙 二、制作基础底漆 (1)底漆层 (2)水痕层 (3)指纹层 一、导入模型并烘焙 1. 导入模型,马灯模型如下所示 2. 在纹理集设置中点击“烘焙模型贴图” 设置输出大小为…...

TransmittableThreadLocal 问题杂记
0、前言 TransmittableThreadLocal,简称 TTL,是阿里巴巴开源的一个Java库,它能够实现ThreadLocal在多线程间的值传递,适用于使用线程池、异步调用等需要线程切换的场景,解决了ThreadLocal在使用父子线程、线程池时不能…...

Linux之 线程池 | 单例模式的线程安全问题 | 其他锁
目录 一、线程池 1、线程池 2、线程池代码 3、线程池的应用场景 二、单例模式的线程安全问题 1、线程池的单例模式 2、线程安全问题 三、其他锁 一、线程池 1、线程池 线程池是一种线程使用模式。线程池里面可以维护一些线程。 为什么要有线程池? 因为在…...
Composer常见错误及解决方案
Composer常见错误及解决方案 Composer是PHP的依赖管理工具,它使得在PHP项目中管理和安装依赖库变得简单。然而,在使用Composer时,开发者可能会遇到一些常见的错误。在本文中,我们将探讨一些常见的Composer错误以及相应的解决方案…...

系统架构图怎么画
画架构图是架构师的一门必修功课。 对于架构图是什么这个问题,我们可以按以下等式进行概括: 架构图 架构的表达 架构在不同抽象角度和不同抽象层次的表达,这是一个自然而然的过程。 不是先有图再有业务流程、系统设计和领域模型等&#…...

微信小程序页面生命周期和小程序api组件的生命周期
小程序组件的生命周期...

通过node 后端实现颜色窃贼 (取出某个图片的主体rgb颜色 )
1.需求 我前端轮播图的背景色 想通过每一张轮播图片的颜色作为背景色 这样的话 需要通过一张图片 取出图片的颜色 这个工作通过前端去处理 也可以通过后端去处理 前端我试了试 color-thief 的插件 但是 这个插件是基于canvas 的模式来的 我需要在小程序中使用这个插件 而且是…...

【蓝桥杯第十三届省赛B组】(详解)
九进制转十进制 #include <iostream> #include<math.h> using namespace std; int main() {cout << 2*pow(9,3)0*pow(9,2)2*pow(9,1)2*pow(9,0) << endl;return 0; }顺子日期 #include <iostream> using namespace std; int main() {// 请在此…...

网址打包微信小程序源码 wap转微信小程序 网站转小程序源码 网址转小程序开发
内容目录 一、详细介绍二、效果展示2.效果图展示 三、学习资料下载 一、详细介绍 我们都知道微信小程序是无法直接打开网址的。 这个小程序源码提供了一种将网址直接打包成微信小程序的方法, 使得用户可以在微信小程序中直接访问这些网址内容。 这个源码没有进行加…...

C# OpenCvSharp 轮廓检测
目录 效果 代码 下载 效果 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenCvSharp; using OpenCvSharp.…...

阿里云服务器安装SSL证书不起作用的解决方案
阿里云服务器安装SSL证书不起作用的解决方案 在阿里云安装SSL证书后,访问无效,各种检查证书安装没有问题。忽然想到阿里云默认连80端口都没开启,443端口应该也没开启。 登录阿里云控制台 - 云服务器 ECS - 网络与安全 - 安全组 - 管理规则 - …...
【二】【设计模式】建造者模式
建造者模式的引入 //C10_1.cpp #include <stdio.h>#include "SystemConfig.h"int main() {SystemConfig config("mysql://127.0.0.1/", "xiaomu", "xiaomumemeda","redis://127.0.0.1/", "xiaomuredis", &q…...

Linux 系统 CentOS7 上搭建 Hadoop HDFS集群详细步骤
集群搭建 整体思路:先在一个节点上安装、配置,然后再克隆出多个节点,修改 IP ,免密,主机名等 提前规划: 需要三个节点,主机名分别命名:node1、node2、node3 在下面对 node1 配置时,先假设 node2 和 node3 是存在的 **注意:**整个搭建过程,除了1和2 步,其他操作都使…...
【Python】python+requests+excel+pytest-实现接口自动化实例
目录 测试需求实现思路完整框架2.1 初始化数据 (test_data.xlsx)2.2 核心脚本 (api_client.py)2.3 测试用例 (test_interfaces.py)2.4 日志 (logging)2.5 pytest配置文件 (pytest.ini)2.6 测试报告 (pytest-html)2.7 入口函数 (run_tests.py)2.8 完整流程注意事项测试需求 简单…...
Django(四)-搭建第一个应用(3)
一、问题详情页 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>展示某个投票的问题和不带结果的选项列表</title> </head> <body><form action"{% url polls:vote questi…...

吴恩达2022机器学习专项课程(一) 4.2 梯度下降实践
问题预览/关键词 本节内容梯度下降更新w的公式梯度下降更新b的公式的含义α的含义为什么要控制梯度下降的幅度?导数项的含义为什么要控制梯度下降的方向?梯度下降何时结束?梯度下降算法收敛的含义正确更新梯度下降的顺序错误更新梯度下降的顺…...

SQL,group by分组后分别计算组内不同值的数量
SQL,group by分组后分别计算组内不同值的数量 如现有一张购物表shopping 先要求小明和小红分别买了多少笔和多少橡皮,形成以下格式 SELECT name,COUNT(*) FROM shopping GROUP BY name;SELECT name AS 姓名,SUM( CASE WHEN cargo 笔 THEN 1 ELSE 0 END)…...
关于python中常用命令(持续更新中)
目录 关于pip 卸载安装pip 更新pip 更换pip镜像源 清除缓存 更新指定包 指定清华镜像下载指定包 关于conda 更换清华镜像源 优先使用清华镜像 清除缓存 关于数据分析、数据挖掘常用 Matplotlib 3.6.0 文档(绘图实例) jupyter字体问题 jup…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...