用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 前言 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的人脸表情识别 该项目较…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...
Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
