用React给XXL-JOB开发一个新皮肤(四):实现用户管理模块
目录
- 一. 简述
- 二. 模块规划
- 2.1. 页面规划
- 2.2. 模型实体定义
- 三. 模块实现
- 3.1. 用户分页搜索
- 3.2. Modal 配置
- 3.3. 创建用户表单
- 3.4. 修改用户表单
- 3.5. 删除
- 四. 结束语
一. 简述
上一篇文章我们实现登录页面和管理页面的 Layout
骨架,并对接登录和登出接口。这篇文章我们将实现用户管理的模块和相应的接口。最后效果如下:
二. 模块规划
在开发之前我们需要对 xxl-job
管理系统的用户模块进行规划。
2.1. 页面规划
一般我们都是从前端页面需要使用什么组件;后端接口需要哪些?
前端使用的组件:表格、分页、下拉框、输入框和按钮,就是一个很普通的 CRUD
管理页面,比较简单;接口也是围绕这些功能的:分页查询接口、创建用户接口、编辑用户接口和删除接口。
2.2. 模型实体定义
接着我们需要定义下前后端交互会使用的到的请求和响应实体的定义。
首先是用户分页查询接口的请求和响应:
// UserPageQueryProp 用户分页查询请求参数定义
export interface UserPageQueryProp {page: number; // 页码size: number; // 页大小role: number; // 角色 IDusername?: string; // 用户名称
}// UserTableProp 用户分页查询返回参数定义
export interface UserTableProp {id: number; // 用户IDusername: string; // 用户名称role: number; // 角色permission: string; // 权限
}
这里需要注意的是虽然我们表格中只有用户名和角色名两个显示属性,但是考虑到在编辑的时候需要根据角色显示权限信息,这里在分页查询中返回用户的权限数据。但是如果在一些复杂的分页表格中,不建议这样操作!
接着是用户创建和编辑的请求的定义:
// UserTableProp 用户创建表单属性
export interface UserCreateFormProp {username: string; // 用户名称password: string; // 密码role: number; // 角色permission: string[]; // 权限
}// UserUpdateFormProp 用户创建表单属性
export interface UserUpdateFormProp extends UserCreateFormProp{id: number; // 用户ID
}
三. 模块实现
从这个模块我们可以分为两个大部分和三个小组件组成。
其中功能部分我们可以使用 antd
的Space
中嵌套表单组件实现;表格可以使用 Table
组件(这个组件自带分页功能)实现;最后创建和编辑按钮我们使用 Modal
组件中嵌套 Form
表单组件实现就可以了。下面我们按功能一个个实现这个用户模块。
这里我们在使用
TS
这个Buff
的使用,大部分使用需要申明类型,尤其在使用不熟悉的UI
组件库的时候,大家需要多读文章,多看组件定义文件或者源码。
3.1. 用户分页搜索
上面我们分析我们要使用组件,这里就不赘述了,直接上代码:
import {Button, Divider, Input, Select, Space, Table, Tag} from "antd";
import React, {useEffect, useState} from "react";
import {User} from "@/types";
import {ColumnsType} from "antd/es/table";
import {useRequest} from "ahooks";
import UserApi from "@/api/user.ts";
import {ClearOutlined, PlusOutlined, SearchOutlined} from "@ant-design/icons";const UserPage = () => {// 定义列信息const columns: ColumnsType<User.UserTableProp> = [{title: '账号',key: 'username',dataIndex: 'username',align: 'center'},{title: '角色',key: 'role',dataIndex: 'role',align: 'center',render: (_, record) => record.role == 1 ? <Tag color="#f50">管理员</Tag> : <Tag color="#2db7f5">普通用户</Tag>},{title: '操作',key: 'active',align: 'center',width: 200,render: (_, record) => <Space><Button type="primary" onClick={() => openEdit(record.id)}>编辑</Button><Button type="primary" danger onClick={() => deleteUser(record.id)}>删除</Button></Space>,},]// 总条数const [total, setTotal] = useState<number>(0);const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]);// 用户数据const [datasource, setDatasource] = useState<User.UserTableProp[]>([]);// 分页查询属性const [pageQuery, setPageQuery] = useState<User.UserPageQueryProp>(defaultUserPageQuery());return <div><Space><Button type="primary" icon={<PlusOutlined />}>增加用户</Button><Divider type="vertical"/><div>角色:</div><SelectonChange={e => setPageQuery({...pageQuery, role: e})}placeholder="选择状态"defaultValue={-1}style={{width: 100}}options={[{value: -1, label: '全部'},{value: 1, label: '管理员'},{value: 0, label: '普通用户'}]}/><div style={{marginLeft: 20}}>用户名称:</div><InputallowClearplaceholder="请输入搜索的用户名称"value={pageQuery.username}onChange={e => setPageQuery({...pageQuery, username: e.target.value})} /><Button danger type='primary' icon={<ClearOutlined />} onClick={clearSearch}>清空</Button><Button type='primary' icon={<SearchOutlined />} onClick={() => loadUser.run(pageQuery)}>搜索</Button></Space><Tableborderedsize={'small'}columns={columns}loading={loadUser.loading}dataSource={datasource}style={{ marginTop: 10 }}rowKey={(record) => record.id}pagination={{onShowSizeChange: (current, size) => loadUser.run({...pageQuery, page: current, size: size}),onChange: (page, pageSize) => loadUser.run({...pageQuery, page: page, size: pageSize}),showTotal: () => `共 ${total} 个`,showQuickJumper: true,showSizeChanger: true,pageSize: pageQuery.size,current: pageQuery.page,size: 'default',total: total,}}rowSelection={{type: 'checkbox',selectedRowKeys: selectedRowKeys,onChange: (selectedRowKeys: React.Key[]) => {setSelectedRowKeys([...selectedRowKeys.map(item => item as number)])}}}/></div>
}export default UserPage;
这里我们需要注意一下几点:
- 表格每一个行都需要一个
Key
,默认是React.Key
,但是如果我们需要自定义的时候,可以使用rowKey={(record) => record.id}
定义自己的rowKey
,这里的record
就是定义表格属性模型:User.UserTableProp
- 关于分页属性我们可以通过
pagination
属性进行设置,可以设置属性和方法可以在分页组件文章中看到 - 最后一点就是关于表格行选中可以通过
rowSelection
属性设置;
接下来我们就需要对接分页查询的接口了,首先我们在 api/user.ts
中添加用户分页接口 api
定义:
/*** 用户分页* @param param* @constructor*/
export const UserPage = (param: User.UserPageQueryProp): Promise<PageData<User.UserTableProp>> => {return https.request({url: '/user/pageList',method: 'post',data: param})
}
接着我们看一下如何使用这个api
,并且了解下ahooks
中的useRequest
中非常好用的地方。
// 加载用户列表
const loadUser = useRequest(UserApi.UserPage, {manual: true, // 手动调用onSuccess: ({records, total}) => { // 成功之后执行的操作setTotal(total);setDatasource(records);}
});
最后我们配合 useEffect
使用,加载用户列表的接口会在加载用户管理页面的时候调用这个接口。
useEffect(() => {loadUser.run(pageQuery)
}, [])
这里我们介绍 ahooks
中的 useRequest
这个工具 hooks
。
支持的功能很多,这里我们现使用这里的 loading
返回值。在我们请求接口的时候如果遇到网络抖动之类的加载缓慢的情况,让表格出现一个加载状态的图标是非常友好了,不然用户也很懵逼。在上面antd
提供了加载属性loading={loadUser.loading}
我们只需要将这个值的变化交给 useRequest
就可以,完全不需要我们手动控制。
接下来我们实现上面搜索的功能。
这里一个是下拉框一个是输入框,我们直接使用的是 antd
的组件,我们仅需要实现清空输入和搜索两个按钮事件就可以了。对于清空搜索的点击事件,我们只需要将下拉选项设置为默认值,输入框清空就可以了,代码如下 :
// 清空搜索
const clearSearch = () => {setPageQuery({...pageQuery, role: -1, username: ""})
}
对于搜索我们仅需要手动调用分页接口就可以了,代码如下:
<Button type='primary' icon={<SearchOutlined />} onClick={() => loadUser.run(pageQuery)}
>搜索</Button>
3.2. Modal 配置
这个用户管理的部分需要用到创建用户和编辑用户两个功能,在xxl-job
中都是都通过打开弹窗进行操的,我们这里也是使用相同的逻辑。这里我们使用 antd
的 Modal
组件。在使用这个组件的时候我们需要对 Modal
组件的打开和关闭进行一个统一的控制。
// UserCreateModelProp 创建用户弹窗属性
export interface UserCreateModalProp {visible: boolean;close: (isLoad: boolean) => void; // 关闭模态框
}// UserUpdateFormProp 用户更新表单属性
export interface UserUpdateFormProp {id: number; // 用户IDpassword: string; // 密码role: number; // 角色permission: string[]; // 权限
}// 定义模态框的类型
export type ModalType = "create" | "update";// UserModelProp 用户模态框汇总属性
export interface UserModalProp {createVisible: boolean; // 创建用户模态框打开标识updateVisible: boolean; // 编辑用户模态框打开标识userData?: UserTableProp; // 编辑是存放被编辑用户信息
}
接着我们分别定义打开和关闭模态框的事件:
// 关闭模态框
const closeModal = (isLoad: boolean) => {// 在全局只能有一个弹窗打开,所以在关闭的时候把标识变量都设为 false 就可以了setUserModelProp({createVisible: false, updateVisible: false, userData: undefined})if(isLoad) {// 如果创建和编辑成功,我们需要重新加载表格数据显示最新的数据loadUser.run(pageQuery)}
}// 打开模态框
const openModal = (types: User.ModalType, data?: User.UserTableProp) => {switch (types) {case "create":setUserModalProp({createVisible: true, updateVisible: false});break;case "update":setUserModalProp({updateVisible: true, createVisible: false, userData: data});break;default:break}
}
最后我们在创建和编辑按钮上使用这些事件就可以了:
<Button type="primary" icon={<PlusOutlined />} onClick={() => openModal('create')}>增加用户</Button><Button type="primary" onClick={() => openModal('update', record)}>编辑</Button>
接着我们定一个模态框组件,在当前目录下创建 create.tsx
和 update.tsx
文件,这两个文件分别是创建用户和编辑用户模态框组件(子组件)。
import {Modal} from "antd";
import React from "react";
import {User} from "@/types";const CreateUserModal: React.FC<User.UserCreateModalProp> = ({visible, close}) => {const submitForm = () => {close(true)}return <Modaltitle="创建用户"open={visible}onOk={submitForm}onCancel={() => close(false)}><h1>创建用户</h1></Modal>
}export default CreateUserModal;
编辑类似不做展示了
这两个子组件设置组件之间的传值问题,我们在User.UserCreateModalProp
定义了创建用户模态框组件需要的参数:visible
变量和 close
函数。最后我们在 index.tsx
中使用这个子组件就可以了。
// 存放模态框状态值
const [userModalProp, setUserModalProp] = useState<User.UserModalProp>({createVisible: false, updateVisible: false});<CreateUserModalkey="create"close={closeModal} // 模态框关闭事件visible={userModalProp.createVisible} // 创建用户模态框打开状态标识变量
/>
效果如下:
3.3. 创建用户表单
这里我们接着实现创建用户表单和表单提交的相关部分,直接上代码:
推荐先看看
antd
的Form
组件的文章。
import {Checkbox, Divider, Empty, Form, Input, message, Modal, Radio, Row, Spin, Tag} from "antd";
import React, {useEffect, useState} from "react";
import {Group, User} from "@/types";
import {useRequest} from "ahooks";
import {GroupApi, UserApi} from "@/api/index.ts";
import styled from "@emotion/styled";const CreateUserModal: React.FC<User.UserCreateModalProp> = ({visible, close}) => {// 表单const [form] = Form.useForm<User.UserCreateFormProp>();// 监听表单 role 的 valueconst roleValue = Form.useWatch('role', form);// 执行器列表const [groups, setGroups] = useState<Group.JobGroupListProp[]>([]);// 执行器请求const groupLoader = useRequest(GroupApi.GroupLists, {manual: true, onSuccess: (data) => {setGroups(data);}})// 创建用户请求const createLoader = useRequest(UserApi.CreateUser, {manual: true, onSuccess: () => {message.success('创建用户成功')close(true)}});const submitForm = () => {form.validateFields().then(value => {// console.log("submit => ", value)if (value.role == 1) {value.permission = []}createLoader.run(value);})}// 监听 visible 打开关闭标识useEffect(() => {if (visible) { // 当创建用户模态框打开,请求执行器列表接口并设置角色默认值为普通用户groupLoader.run();form.setFieldValue('role', 0)} else {// 关闭模态框的时候,将表单置为空并将执行器列表设置为空数组form.resetFields();setGroups([]);}}, [visible])return <Controllertitle="创建用户"maskClosablewidth={500}open={visible}onOk={submitForm}onCancel={() => close(false)}><Spin tip="加载中......" spinning={createLoader.loading}><Form form={form} layout="vertical" name="form_create_modal"><Form.Item name="username" label="账号" rules={[{ required: true, message: '请输入账号' }]}><Input placeholder="请输入账号" /></Form.Item><Form.Item name="password" label="密码" rules={[{ required: true, message: '请输入密码' }]}><Input.Password placeholder="请输入密码" /></Form.Item><Form.Item name="role" label="角色"><Radio.Group><Radio value={0}>普通用户</Radio><Radio value={1}>管理员</Radio></Radio.Group></Form.Item>{roleValue === 0 && <Form.Item name="permission" label="权限">{groups.length > 0 ? <Checkbox.Group className="xxl-job-list">{groups.map(item =><Row key={item.id}><Checkbox value={item.appName}>{item.title}<Divider type="vertical" /><Tag color="lime">{item.appName}</Tag></Checkbox></Row>)}</Checkbox.Group> : <Empty />}</Form.Item>}</Form></Spin></Controller>
}const Controller = styled(Modal)`.ant-modal-body {padding-top: 24px;.xxl-job-list {flex-direction: column;}}
`export default CreateUserModal;
这里我们通过 Modal
包裹表单组件,使用 useEffect
监听 visible
属性,当前模态框打开的时候,需要请求执行器列表,并设置角色默认值。
还需要注意的一个点是,当角色是管理员的时候,是不需要选择执行器的,所有在切换角色为管理员的时候,需要将之前选中的执行器清空;所以在最后提交用户数据的时候,设置下执行器就可以了。
const submitForm = () => {form.validateFields().then(value => {if (value.role == 1) { // 当角色是管理员的时候,将执行器权限设置为空数据value.permission = []}createLoader.run(value);})
}
此外我们还通过 styled
修改了 Modal
组件的样式,主要是为了将多选框flex
布局从 row
改为 column
。
// 使用 styled 包裹 Modal 组件
const Controller = styled(Modal)`.ant-modal-body {padding-top: 24px;.xxl-job-list {flex-direction: column;}}
`
3.4. 修改用户表单
有了上面创建用户表单部分,我们在修改用户信息的时候,仅需要了解表单初始化的问题了;这里我们也是用使用Form.setFieldsValue
方法进行初始化表单,代码代码:
useEffect(() => {if (visible && data) {groupLoader.run();form.setFieldsValue({id: data.id, username: data.username, role: data.role, permission: data.permission})} else {form.resetFields();setGroups([]);}
}, [visible])
这里还有一个不一样的地方是我们会设置一个隐藏的用户主键,方便我们后面执行更新的时候确定要被更新用户信息:
<Controllertitle="更新用户"maskClosablewidth={500}open={visible}onOk={submitForm}onCancel={() => close(false)}><Spin tip="加载中......" spinning={updateLoader.loading}><Form form={form} layout="vertical" name="form_update_modal">// 不显示主键,在我们提交数据的时候会反给form.validateFields().then(value => {})中<Form.Item name="id" label="主键" style={{display: 'none'}}><Input /></Form.Item><Form.Item name="username" label="账号"><Input placeholder="请输入账号" readOnly /></Form.Item><Form.Item name="password" label="密码"><Input.Password placeholder="请输入新密码,为空则不更新密码" /></Form.Item><Form.Item name="role" label="角色"><Radio.Group><Radio value={0}>普通用户</Radio><Radio value={1}>管理员</Radio></Radio.Group></Form.Item>{roleValue === 0 && <Form.Item name="permission" label="权限">{groups.length > 0 ? <Checkbox.Group className="xxl-job-list">{groups.map(item =><Row key={item.id}><Checkbox value={item.appName}>{item.title}<Divider type="vertical" /><Tag color="lime">{item.appName}</Tag></Checkbox></Row>)}</Checkbox.Group> : <Empty />}</Form.Item>}</Form></Spin></Controller>
3.5. 删除
终于快要搞完了,现在我们就剩删除用户这个功能了。针对我们删除来说,一般我都需要弹出一个提示,询问用户是否确定删除这条数据。这里我们可以使用 antd
中的 删除Modal
或者气泡提示就可以了。
这里功能简单,只需要调用组件,在其回调方法中调用删除接口就可以了。代码如下:
// 移除用户
const loadRemoveUser = useRequest(UserApi.RemoveUser, {manual: true,onSuccess: () => {loadUser.run(pageQuery);message.success('移除用户成功');}
})// 删除用户
const deleteUser = (id: number) => {Modal.confirm({title: '你确认删除当前用户吗?',icon: <ExclamationCircleFilled />,content: '删除用户会导致无法登录和操作任务',okText: '确认',okType: 'danger',cancelText: '取消',onOk() {loadRemoveUser.run(id)},onCancel() {},});
}
最后在给删除按钮添加点击事件,并将用户的 ID
传给接口。
<Button type="link" danger onClick={() => deleteUser(record.id)}>删除</Button>
四. 结束语
这篇文章我们介绍了如何利用 antd
提供的组件,快速开发一个 CRUD
功能的管理模块,相信大家可以从中收获很多东西了;下一篇文章我们将介绍执行器管理的模块开发。
相关文章:

用React给XXL-JOB开发一个新皮肤(四):实现用户管理模块
目录 一. 简述二. 模块规划 2.1. 页面规划2.2. 模型实体定义 三. 模块实现 3.1. 用户分页搜索3.2. Modal 配置3.3. 创建用户表单3.4. 修改用户表单3.5. 删除 四. 结束语 一. 简述 上一篇文章我们实现登录页面和管理页面的 Layout 骨架,并对接登录和登出接口。这篇…...
某赛通电子文档安全管理系统 hiddenWatermark/uploadFile 文件上传漏洞复现
0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…...

Redis五种数据类型及应用场景
1、数据类型 String(字符串,整数,浮点数):做简单的键值对缓存 List(列表):储存一些列表类型的数据结构 Hash(哈希):包含键值对的无序散列表,结构化的数据 Set(无序集合):交集,并集…...

测试环境搭建整套大数据系统(一:基础配置,修改hostname,hosts,免密)
一:使用服务器配置。 二:修改服务器名称hostname,hosts。 在 Linux 系统中,hostname 和 /etc/hosts 文件分别用于管理主机名和主机名解析。 在三台服务器上,分别执行以下命令。 vim /etc/hostnamexdso-hadoop-test-0…...

maven helper 解决jar包冲突方法
一 概要说明 1.1 说明 首先,解决idea中jar包冲突,使用maven的插件:maven helper插件,它能够给我们罗列出来同一个jar包的不同版本,以及他们的来源,但是对不同jar包中同名的类没有办法。 1.2 依赖顺序 …...
AppSrv-文件共享(23国赛真题)
2023全国职业院校技能大赛网络系统管理赛项–模块B:服务部署(WindowServer2022) 文章目录 AppSrv-文件共享题目配置步骤创建用户主目录共享文件夹:本地目录为d:\share\users\,允许所有域用户可读可写。在本目录下为所有用户添加一个以名称命名的文件夹,该文件夹将设置为所…...

AsyncLocal是如何实现在Thread直接传值的?
一:背景 1. 讲故事 这个问题的由来是在.NET高级调试训练营第十期分享ThreadStatic底层玩法的时候,有朋友提出了AsyncLocal是如何实现的,虽然做了口头上的表述,但总还是会不具体,所以觉得有必要用文字图表的方式来系统…...

Flask 入门1:一个简单的 Web 程序
1. 关于 Flask Flask诞生于2010年, Armin Ronacher的一个愚人节玩笑。不过现在已经是一个用python语言基于Werkzeug工具箱编写的轻量级web开发框架,它主要面向需求简单,项目周期短的小应用。 Flask本身相当于一个内核,其他几乎所…...

维护管理Harbor,docker容器的重启策略
维护管理Harbor 通过HarborWeb创建项目 在 Harbor 仓库中,任何镜像在被 push 到 regsitry 之前都必须有一个自己所属的项目。 单击“项目”,填写项目名称,项目级别若设置为"私有",则不勾选。如果设置为公共仓库&#…...

Qt6入门教程 14:QToolButton
目录 一.简介 二.常用接口 1.void setMenu(QMenu * menu) 2.void setPopupMode(ToolButtonPopupMode mode) 3.void setToolButtonStyle(Qt::ToolButtonStyle style) 4.void setArrowType(Qt::ArrowType type) 5.void setDefaultAction(QAction * action) 三.实战演练 1…...

3D数据转换器HOOPS Exchange如何获取模型的几何数据? 干货预警!
一、概述 前面讲解过模型在内存中的结构,现在回顾一下,当模型导入成功后,整个模型数据会以原生结构的 PRC 组装树形式存放到内存中。(申请 HOOPS Exchange 试用) PRC结构的主要类型包含四种,分别是…...

Coremail启动鸿蒙原生应用开发,打造全场景邮件办公新体验
1月18日,华为在深圳举行鸿蒙生态千帆启航仪式,Coremail出席仪式并与华为签署鸿蒙合作协议,宣布正式启动鸿蒙原生应用开发。作为首批拥抱鸿蒙的邮件领域伙伴,Coremail的加入标志着鸿蒙生态版图进一步完善。 Coremail是国内自建邮件…...

基于CVITEK_CV1821+SOI_Q03P的IPC方案
方案概述: 该方案基于主控平台CVITEK_CV1821和sensor SOI_Q03P,运用于智能监控IP摄像头,可用于户外或室内。采用了2304x1296的分辨率,30的帧率,支持HDR。作为3M的监控摄像头,通过ISP图像调校技术ÿ…...

chromedriver安装和环境变量配置
chromedriver 1、安装2、【重点】环境变量配置(1)包的复制:(2)系统环境变量配置 3、验证 1、安装 网上随便搜一篇chromedriver的安装文档即可。这里是一个快速链接 特别提醒:截止2024.1.30,chr…...

Linux浅学笔记03
目录 有关root的命令 用户和用户组 用户组管理:(以下需要root用户执行) 创建用户组: 删除用户组: 用户管理:(以下需要root用户执行) 创建用户: 删除用户: 查看用…...

【vue】图片加载骨架
一、前言 在网速较低或者网站的服务器宽带只有几MB的情况下,网页中的图片加载时,要么空白,要么像打印机一样一行一行地“扫描”出来,为了提升用户体验,可以给图片标签外加一层骨架。 无骨架 有骨架 二、详细设计 每张…...

leetcode59. 螺旋矩阵 II
leetcode59. 螺旋矩阵 II 题目 思路 螺旋数组,一次螺旋4个方向(上行从左到右、右列从上到下、下行从右到左、左列从下到上),共执行(n//2)次螺旋。且对于n为奇数时,额外填充中心点nums[mid][mid] n 每一次螺旋圈下来…...
bash 5.2中文修订5
Grouping Commands 命令分组 Bash 提供两种方法将要执行的命令列表分组为一个单元。当命令被分组时,重定向可以应用于整个命令列表。例如,列表中所有命令的输出可以被重定向到单个流。 () 圆括号命令分组 ( list ) 将命令列表放在括号之间会强制 she…...
5GNR解调分析手持式频谱分析仪
2024年已经是5G网络全面普及的一年,手机也基本都升级了5G版本,那么同样的,5G的网络运行也是需要维护的。 我们知道,5G是新型的网络传输技术,如果一般的频谱分析仪是没有办法单独针对5G NR进行解析的。这个时候你就需要…...

互联网加竞赛 基于深度学习的人脸表情识别
文章目录 0 前言1 技术介绍1.1 技术概括1.2 目前表情识别实现技术 2 实现效果3 深度学习表情识别实现过程3.1 网络架构3.2 数据3.3 实现流程3.4 部分实现代码 4 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的人脸表情识别 该项目较…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...