前端React实战项目 全球新闻发布系统
1.项目概述
本项目技术栈全面且新颖,非常适合大家学习参考。如有问题请留言,谢谢。
部分组件未展示,文档只展示关键页面的组件 如需查看请前往项目仓库项目地址
1.1 项目名称
该项目名为全球新闻发布系统
1.2 背景
该系统旨在解决传统新闻管理方式存在的流程繁琐、权限混乱、发布效率低等问题。本新闻管理系统旨在通过现代化的前端技术栈,构建一个功能完善、操作便捷、权限清晰的新闻内容管理平台,实现新闻从创建、编辑、审核到发布的全流程管理,满足不同角色用户的使用需求。
该系统解决了新闻管理过程中的核心痛点:
- 缺乏统一的内容管理平台,信息分散
- 审核流程不规范,责任不明确
- 权限控制不精细,存在安全隐患
1.3 功能定位 / 目标用户
该项目作为新闻管理系统,其目标用户是面相非游客使用人员如管理员,审核员,编辑等角色
1.4 项目亮点 / 核心优势
- 1.现代化技术栈组合
- 2.完整的权限控制
- 3.模块化、分层结构清晰
- 4.功能覆盖较全面
- 5.响应式布局
2.技术栈
2.1 前端技术栈:
技术栈使用:
JavaScriptReact18React-rotuer v7现代路由管理Redux状态管理vite脚手架工具SASScss预处理器ReduxToolkitredux管理工具
页面组件:Ant Design
2.2 富文本方案与内容处理
React Draft Wysiwyg 基于Draft.js的可视化编辑器组件
Draft.js 富文本编辑器
2.3 工具库
Axios Dayjs lodashjs
2.4 模拟后端 API:
Jsonserver v0.17.4
2.5 代码质量 与风格
ESLint插件规范代码质量与格式
基础规则来源:
- 继承了
ESLint官方推荐的基础规则(js.configs.recommended) - 集成了 React Hooks 最新推荐规则(
reactHooks.configs['recommended-latest']) - 包含
React Refresh针对 Vite 的配置(reactRefresh.configs.vite)
文件范围:
适用于所有 .js 和 .jsx 文件
语言特性:
- 支持
ES2020及最新ECMAScript特性 - 启用
JSX语法支持 - 使用 ES 模块(
sourceType: 'module’) - 识别浏览器环境全局变量
语言风格:整体风格偏向现代 React 开发规范,强调代码质量和 Hooks 正确使用,同时兼容 Vite 开发环境的特性。
3.项目结构与模块说明
3.1 目录结构

3.2 各层职责说明
- 1.
components负责各个页面中可复用的组件 如表单 富文本编辑器 页头等等 - 2.
router负责管理整个系统的路由部分 - 3.
Store负责管理Redux状态管理的实例与Store - 4.
unti负责存放一些可以复用的函数 和axios请求拦截器 - 5.
view负责的是整个系统的页面部分 也是整个开发部分中最重要的部分 - 6.其他部分 包括了vite配置 jsonserver的模拟数据等等
3.3 模块划分与功能简介
新闻管理模块:
组件:包括了新闻列表组件 新闻审核管理组件 新闻预览组件 新闻分类组件
介绍新闻管理模块的核心地位,阐述其涵盖新闻录入、编辑、发布等操作,及对新闻标题、内容、分类等信息管理功能,强调其对新闻平台有序运作的关键作用。
用户权限模块:
组件:权限列表组件 角色列表组件 用户列表组件
说明用户权限模块的重要性,讲解其对用户访问资源和执行操作能力的控制配置,如不同用户角色权限设置,以及通过RBAC模型等方式实现权限管理,保障系统数据安全。
(RBAC 基于角色的访问控制管理 比如先定义权限 设置为对应角色身份分配权限 并分配给部分用户角色身份 )
审核发布模块:
组件:审核列表组件(展示从草稿箱提交给审核模块的新闻的审核状态) 审核新闻组件 已发布列表组件 未发布列表组件 已下线新闻组件
解释审核发布模块在内容输出环节的关键作用,讲述稿件审核流程,包括合规性、错别字等检查,以及发布功能,如选择发布渠道、时间,确保发布内容质量和及时性。
系统框架模块:
组件 首页展示组件 登录页面 layout布局组件
阐述系统框架模块对整个系统的支撑意义,介绍其从业务设计思路、拓扑结构到功能模块组成,像项目管理系统从项目管理周期等维度设计架构,为各功能模块提供运行基础。
3.4 路由设计 / 路由守卫机制
1.路由设计
- 路由库与模式
- 使用
react-router-dom v7作为路由管理库,采用createHashRouter创建路由实例,采用哈希模式(URL 带#)管理路由。 - 路由配置集中在
src/router/index.jsx,通过嵌套结构定义路由层级。
- 使用
- 路由结构
- 公共路由:登录页(
/login)、新闻浏览页(/news)、新闻详情页(/detail/:id),无需权限即可访问。 - 私有路由:系统主功能区(
/路径下),包含首页、用户管理、新闻管理等模块,需登录后访问。 - 嵌套路由:主功能区通过
children配置子路由,如/user-manage/list、/news-manage/draft等,对应不同功能页面。
- 公共路由:登录页(
- 路由组件挂载
- 通过
RouterProvider在App.jsx中挂载路由实例,实现路由的全局管理
- 通过
2.路由守卫设计
1.登录状态守卫(PrivateRouter.jsx)
-
作用:验证用户是否登录,未登录则重定向到登录页。
-
实现逻辑:
-
从
localStorage读取token(存储用户信息)。 -
若
token不存在,通过navigate重定向到/login。 -
若
token存在,渲染被包裹的子组件(如系统主页面NewSandBox)。// 核心代码 const PrivateRouter = ({ children }) => {const navigate = useNavigate();useEffect(() => {if (!localStorage.getItem('token')) {navigate('/login', { replace: true });}}, [navigate]);return localStorage.getItem('token') ? children : null; };
2.权限细粒度校验(
OutletDefend.jsx)-
作用:验证登录用户是否有权限访问当前路由,无权限则重定向到 404 页面。
-
实现逻辑:
-
获取权限数据:通过 API 加载所有系统权限(
/rights和/children)和当前用户的权限(从token中解析角色 ID,再请求/roles/:id)。 -
路径匹配:处理带参数的路由(如
/news-manage/preview/:id),通过替换参数为:id实现权限匹配。 -
权限校验:
- 检查用户权限列表是否包含当前路径。
- 检查该权限的
pagepermisson或routerpermisson是否为1(启用状态)。
-
无权限处理:重定向到
/404页面;有权限则渲染子路由(Outlet)// 核心校验逻辑 const hasPermission = () => {const currentPath = location.pathname;const checkPath = currentPath.replace(/\/\d+$/, "/:id"); // 处理参数路由const hasUserRight = userRights.includes(checkPath) || userRights.includes(currentPath);if (!hasUserRight) return false;const rightInfo = allRights.find(item => item.key === checkPath || item.key === currentPath);return rightInfo ? (rightInfo.pagepermisson || rightInfo.routerpermisson) === 1 : true; };
-
-
4.主要功能详解
4.1用户管理模块:
1.用户列表组件
关键页面展示:
主页面:
添加用户表单:

功能说明:
用户列表组件是系统用户管理的核心操作入口,支持管理员对用户信息进行全生命周期管理:
- 超级管理员可查看和管理系统内所有用户,区域管理员仅能管理所属区域内的用户及区域编辑角色用户
- 提供用户信息的新增、编辑、删除功能,支持通过区域和角色筛选快速定位目标用户
- 可通过用户状态开关(Switch 组件)控制用户的登录权限,默认用户(系统初始管理员)的状态不可修改
- 操作按钮权限差异化:默认用户的编辑和删除按钮处于禁用状态,确保系统基础权限稳定性
技术实现要点:
这里我们将逐一对该模块的状态管理,接口调用,数据结构,以及数据处理做出解释。
-
数据管理
- 用户数据: 包含
idusernamepasswordroleState(用户状态)default(是否是默认用户)regionroleId(用户联查角色数据) - 关联数据: 通过
_expand=role联查角色信息,用来获取角色名称等关联数据 - 下拉列表筛选数据: 首先是区域数据
optionArea与角色名称数据optionRole这两个数据用与antd中的Select组件用来筛选用户列表
- 用户数据: 包含
-
状态管理
- 使用
useState管理用户列表数据(dataSource)、表单弹窗状态(open/updataOpen)、表单引用(FormRef/updataFormRef),表单数据(optionRole/optionArea/),管理region是否可见(isUpdateDisable) - 通过使用
useRef获取表单实例 实现表单验证和数据去重 - 利用
isUpdateDisable状态控制编辑表单中区域选择框的禁用状态
- 使用
-
接口调用与数据管理
采用
Axios进行RESTful API调用,实现用户数据的增删改查:- 获取用户列表:
GET /users?_expand=role - 添加用户:
POST /users,并在成功后本地更新列表数据 - 编辑用户:
PATCH /users/:id,同步更新本地缓存的角色信息 - 删除用户:
DELETE /users/:id,同时过滤本地列表数据 - 权限过滤逻辑:根据登录用户角色 ID(
role.id)过滤数据,超级管理员(id=1)获取全部数据,区域管理员仅获取所属区域数据
- 获取用户列表:
-
数据处理与交互函数:
1.数据加载函数:
(1)用户列表数据加载
const getRightList = async () => {const res = await axios.get("http://localhost:3000/users?_expand=role");if (id === 1) { // 超级管理员setDataSource(res.data);} else { // 区域管理员/编辑setDataSource([...res.data.filter(item => item.username === username), // 自身数据...res.data.filter(item => item.region === region && item.roleId === 3) // 管辖区域的编辑]);}
};
- 作用:根据登录用户权限加载对应范围的用户数据(超级管理员可见全部,区域管理员仅见自身及管辖区域编辑)
- 数据关联:通过
_expand=role联查角色信息,避免前端二次请求
(2)表单选项数据加载
const getFormData = async () => {// 加载区域选项const formRegionRes = await axios.get("http://localhost:3000/regions");setOptionArea(formRegionRes.data);// 加载角色选项并格式化const formRoleRes = await axios.get("http://localhost:3000/roles");formRoleRes.data.forEach(element => {element.label = element.roleName; // 显示文本element.value = element.id; // 实际值});setOptionRole(formRoleRes.data);
};
- 作用:加载区域和角色下拉框的选项数据,并格式化为 Ant Design Select 组件所需的
label-value结构 - 解释:这里必须使用
lable-value结构 如果继续使用roleName和id的结构 就会导致optionRole数据中的roleName和id是两个数组类型的数据,进而引发再下拉菜单中无法渲染出角色名称的问题 - 时机:组件挂载时执行,确保表单渲染时选项已就绪
- 数据操作函数
(1)新增用户
const handleOK = () => {FormRef.current.validateFields().then(res => {setOpen(false);FormRef.current.resetFields();const postData = { ...res, roleState: true, default: false };axios.post("http://localhost:3000/users", postData).then(response => {setDataSource([...dataSource,{ ...response.data, role: optionRole.filter(item => item.id === res.roleId)[0] }]);});});
};
- 流程:表单验证 → 构造提交数据 → 发送 POST 请求 → 本地数据即时更新(避免二次请求)
- 挂载位置:这个函数主要用在新增用户的模态框表单中 点击模态框的确定按钮就会触发
- 数据处理:手动关联角色信息(因后端返回数据不含完整角色对象)
(2)更新用户
const handleUpdataOK = () => {updataFormRef.current.validateFields().then(res => {setupdataOpen(false);// 本地更新setDataSource(dataSource.map(item => item.id === currentUpdate.id ? { ...item, ...res, role: optionRole.filter(data => data.id === res.roleId)[0] }: item));// 后端同步axios.patch(`http://localhost:3000/users/${currentUpdate.id}`, res);});
};
- 特点:采用乐观更新策略,先更新本地数据提升体验,再同步至后端(乐观更新策略:在发送请求到后端之前,先假设请求会成功,并提前更新本地界面状态,待后端确认成功后再最终确认状态)
- 挂载位置:挂载在更新用户信息的模态框表单中
- 关联处理:通过角色 ID 过滤本地角色列表,补充完整角色信息
(3)删除用户
const confirmDelete = (item) => {// 本地删除setDataSource(dataSource.filter(data => data.id !== item.id));// 后端删除axios.delete(`http://localhost:3000/users/${item.id}`);
};
- 逻辑:确认后立即从本地数据源移除该用户,同时发送删除请求
- 权限控制:默认用户(
item.default === true)的删除按钮禁用
- 辅助处理函数
(1)更新表单初始化
const handleUpdate = (item) => {setupdataOpen(true);if (item.roleId === 1) {//禁用地区 角色是超级管理setIsUpdateDisable(true);} else {//取消禁用setIsUpdateDisable(false);}// console.log(isUpdateDisable);setTimeout(() => {//setFieldsValue(item)将初表单的初始值填充成当前行表示的数据updataFormRef.current.setFieldsValue(item); // 增加一定的延迟确保 Modal 已渲染}, 100);setCurrentUpdate(item);};
- 作用:打开编辑弹窗时初始化表单数据,并根据角色 ID 控制区域选择框权限
- 设置延迟:设置延迟是为了保证Modal模态框已经完成渲染,保证表单的
DOM能够正常被获取到
(2)表格筛选逻辑
// 区域筛选
onFilter: (value, record) => {if (value === "全球") return record.region === "";return value === record.region;
}// 角色筛选
onFilter: (value, record) => {return value === record.role.roleName;
}
- 特点:针对 “全球区域” 做特殊处理(后端存储为空字符串),通过角色名称匹配实现筛选
2.用户信息填写(更新)表单:
1.组件作用
用户信息表单组件,用于用户的新增和编辑场景,提供用户名、密码、区域和角色的输入功能。通过权限控制动态限制可选选项,并支持跨组件表单操作(校验、重置、赋值)。 该组件就是上面在用户列表组件中展示的模态框表单。
2.组件通信与组件方法
这些属性都是父组件向子组件传递的必要属性
| 属性名 | 类型 | 说明 | 示例 |
|---|---|---|---|
optionRole | array | 角色下拉框原始数据(需包含label、value、roleName) | [{label: '超级管理员', value: 1, roleName: '超级管理员'}, ...] |
optionArea | array | 区域下拉框原始数据(需包含value、title) | [{value: '亚洲', title: '亚洲'}, ...] |
isUpdateDisable | boolean | 编辑场景下控制区域选择框是否禁用(优先级低于角色选择联动) | true(编辑超级管理员时) |
表中的方法都是通过ref向外暴漏的
| 方法名 | 说明 | 参数示例 |
|---|---|---|
validateFields | 校验表单所有字段,返回校验结果 | - |
resetFields | 重置表单所有字段值为初始状态 | - |
setFieldsValue | 为表单设置初始值(用于编辑场景回显) | {username: 'admin', region: '亚洲'} |
3.核心功能实现:
-
(1)权限驱动的选项过滤:
基于当前登录用户角色(
role.id)动态过滤可选选项,防止越权操作,用来获取表单所需的角色列表与地域列表:// 区域选项过滤逻辑 const getFilteredRegionOptions = () => {if (id === 1) return optionArea; // 超级管理员:所有区域return optionArea.filter(item => item.value === region); // 区域管理员/编辑:仅自身区域 };// 角色选项过滤逻辑 const getFilteredRoleOptions = () => {if (id === 1) return optionRole; // 超级管理员:所有角色if (id === 2) return optionRole.filter(item => item.roleName === "区域编辑"); // 区域管理员:仅区域编辑return optionRole.filter(item => item.value === id); // 编辑:仅自身角色 }; -
(2)角色与区域联动控制
选择超级管理员角色时自动禁用区域选择并清空区域值,保证数据一致性:
<SelectonChange={(value) => {if (value === 1) { // 选择超级管理员setIsDisable(true);ref.current.setFieldsValue({ region: "" }); // 清空区域} else {setIsDisable(false);}}} /> -
(3)跨组件通信设计
通过
forwardRef和useImperativeHandle暴露表单方法,简化父组件操作:逐行解析:
-
const [form] = Form.useForm();这是 Ant Design Form 组件提供的表单实例创建方法,通过解构赋值获取
form实例。该实例包含了一系列操作表单的核心方法(如校验、重置、赋值等),用于管理表单的状态和行为。 -
useImperativeHandle(ref, () => ({ ... }));这是 React 的 Hook,用于自定义子组件暴露给父组件的
ref内容。它接收两个参数:- 第一个参数
ref:父组件传递过来的ref对象。 - 第二个参数:一个函数,返回一个对象,该对象定义了父组件可通过
ref访问的方法。
- 第一个参数
-
暴露的方法:
-
validateFields: () => form.validateFields()暴露表单校验方法,父组件调用时会触发 Ant Design Form 的校验逻辑,返回校验结果(通常是包含表单数据的 Promise)。
-
resetFields: () => form.resetFields()暴露表单重置方法,调用后会将表单所有字段重置为初始值。
-
setFieldsValue: (values) => form.setFieldsValue(values)暴露表单赋值方法,父组件可通过传递键值对对象(如{
username: 'admin'}),为表单字段设置值(常用于编辑场景的数据回显)。
-
核心作用:
通过
useImperativeHandle精确控制暴露给父组件的方法,避免父组件直接访问子组件内部的form实例,既满足了跨组件操作表单的需求(如父组件触发校验、重置),又保证了组件的封装性,防止父组件误操作内部状态。const [form] = Form.useForm(); useImperativeHandle(ref, () => ({validateFields: () => form.validateFields(),resetFields: () => form.resetFields(),setFieldsValue: (values) => form.setFieldsValue(values), })); -
4.2权限管理模块:
1.权限列表组件:
关键页面展示:
主页面:
子权限节点树:

操作按钮:

功能说明:
权限列表组件用于管理系统内所有权限项的展示、状态切换和删除操作,支持一级权限与二级子权限的树形结构展示,实现了权限启用 / 禁用的动态切换及权限项的删除功能,是系统权限管理模块的核心组件。 也是RBAC模式的重要组成部分。
技术实现要点:
-
数据管理
- 采用树形层级结构存储权限数据,包含两类权限项:
- 一级权限:包含
id、label(权限名称)、key(权限路径,如/user-manage)、grade: 1(标识一级)、children(二级权限数组,无二级权限时设为null)、pagepermisson(页面访问权限,1 为启用,0 为禁用)等字段。 - 二级权限:包含
id、label、key(如/user-manage/add)、grade: 2(标识二级)、rightId(关联的一级权限 ID)、pagepermisson等字段。
- 一级权限:包含
- 使用
dataSource状态来保存从后端获取的Rights列表的数据,如上条所示。并对数据做了初始化,从接口获取数据后就对一级权限的children做特殊处理 —— 若children.length === 0则设为null,避免表格渲染空树形结构。
- 采用树形层级结构存储权限数据,包含两类权限项:
-
状态管理:
核心状态:
dataSource用于存储完整的权限列表(含树形结构),初始值为空数组,通过useState定义。状态更新机制:
- 组件挂载时,通过
useEffect调用接口初始化dataSource。 - 权限删除或状态切换时,通过
setDataSource([...dataSource])触发组件重新渲染(因为侧边栏上的视图和权限列表是强制关联的 所以在我们将数据的i)。 - 二级权限删除时,直接修改对应一级权限的
children数组,再通过状态更新刷新视图。
- 组件挂载时,通过
-
数据调用与接口管理:
- 数据获取:组件挂载时调用
/rights?_embed=children",一次性获取所有一级权限及其关联的二级子权限(通过_embed=children实现关联查询)。 - 权限删除:
- 一级权限:调用
/rights/${item.id}",同时从dataSource中过滤掉该权限项。 - 二级权限:调用
/children/${item.id}",并从对应一级权限的children数组中移除该子项。
- 一级权限:调用
- 权限状态切换:
- 一级权限:调用
/rights/${item.id}", { pagepermisson: 新状态 }更新后端状态。 - 二级权限:调用
/children/${item.id}", { pagepermisson: 新状态 }同步状态变更。
- 一级权限:调用
- 数据获取:组件挂载时调用
-
数据处理与交互函数:
-
confirmDelete(删除权限):-
const confirmDelete = (item) => {// 删除前端状态setDataSource(dataSource.filter((data) => data.id !== item.id));if (item.grade === 1) {axios.delete(`http://localhost:3000/rights/${item.id}`).then((err) => {if (err) {console.log(err);}});} else {//二级子项数据删除let list = dataSource.filter((data) => data.id === item.rightId);// console.log(list);list[0].children = list[0].children.filter((data) => data.id !== item.id);setDataSource([...dataSource]);axios.delete(`http://localhost:3000/children/${item.id}`);} }; -
区分权限等级(
grade)执行不同删除逻辑:一级权限直接从dataSource中移除,二级权限需从父级children数组中过滤。 -
前后端同步:删除操作先更新前端状态(优化用户体验),再调用对应接口同步至后端。
-
-
switchPermission(切换权限状态):-
const switchPermission = (item) => {item.pagepermisson = item.pagepermisson === 1 ? 0 : 1;setDataSource([...dataSource]); //用来触发重新渲染// 控制权限状态 分为一级权限和二级权限 控制后端的pagepermissonif (item.grade === 1) {// 使用patch请求(补丁更新) 只更新pagepermissonaxios.patch(`http://localhost:3000/rights/${item.id}`, {//item.pagepermisson在前端状态的时候已经更新了pagepermisson: item.pagepermisson,});} else {axios.patch(`http://localhost:3000/children/${item.id}`, {pagepermisson: item.pagepermisson,});}}; -
前端状态反转:通过
item.pagepermisson = item.pagepermisson === 1 ? 0 : 1直接修改权限状态。 -
强制刷新:通过
setDataSource([...dataSource])触发表格重新渲染,展示最新状态。 -
接口同步:根据权限等级调用不同
patch接口,仅更新pagepermisson字段(局部更新,减少数据传输)。
-
-
4.3角色管理模块:
关键页面展示:
主页面:

操作页面:

功能说明:
角色列表组件是系统权限管理的核心模块之一,用于展示和管理系统内所有角色信息,支持角色的删除操作及权限分配功能。通过树形结构的权限选择器,实现角色与权限的关联配置,是 RBAC(基于角色的访问控制)模型的关键实现载体。
技术实现要点:
-
数据管理
- 核心数据结构:
- 角色数据:包含
id(角色 ID)、roleName(角色名称)、roleType(角色类型)、rights(权限路径数组,如['/user-manage', '/news-manage'])等字段。 - 权限数据:采用树形结构,一级权限包含
id、label、key、grade:1、children(二级权限数组);二级权限包含id、label、key、grade:2、rightId(关联一级权限 ID)等字段(与RightList组件共用权限数据结构)。
- 角色数据:包含
- 数据关联:角色的
rights字段存储所选权限的key值数组,与权限数据的key字段一一对应,实现角色与权限的映射。 - 特殊处理:通过
_embed=children参数一次性获取权限数据及其子权限,避免多次请求。
- 核心数据结构:
-
状态管理
- 核心状态:
dataSource:存储角色列表数据,初始为空数组,通过useState定义。rightList:存储完整权限树形数据,用于权限分配弹窗的树形选择器渲染,保存的是权限数据isModalOpen:控制权限分配弹窗的显示 / 隐藏状态。currentRight:暂存当前角色的权限配置(用于弹窗内权限选择的实时更新)。currentID:记录当前操作的角色 ID,用于权限提交时的精准更新。
- 状态更新机制:
- 组件挂载时,通过
useEffect并行请求角色列表和权限列表数据,初始化dataSource和rightList。 - 角色删除时,通过
filter生成新数组更新dataSource,确保引用变化触发重渲染。 - 权限分配时,通过
map更新目标角色的rights字段,保持其他角色数据不变。
- 组件挂载时,通过
- 核心状态:
-
数据调用与接口管理
-
数据获取:
- 角色列表:
/roles获取所有角色信息。 - 权限列表:
/rights?_embed=children获取带二级权限的完整权限树。
- 角色列表:
-
角色删除:调用
/roles/${item.id},同步删除后端角色数据,前端通过filter即时移除对应角色。 -
权限分配:
-
打开弹窗时,记录当前角色的
rights到currentRight,作为树形选择器的初始选中值。 -
currentRight只保存一个数组 数组中是树形组件所有被勾选的权限 -
提交时,调用
/roles/${currentID},更新数据{ rights: currentRight },将更新后的权限数组同步至后端。
-
-
-
数据处理与交互函数
-
confirmDelete(删除角色):-
const confirmDelete = (item) => {//前端删除逻辑setDataSource(dataSource.filter((data) => data.id !== item.id));//后端删除逻辑axios.delete(`http://localhost:3000/roles/${item.id}`).then((err) => {if (err) {console.log(err);}});}; -
前端逻辑:通过
dataSource.filter过滤掉目标角色 ID,生成新数组更新状态,实现即时删除效果。 -
后端同步:根据角色 ID 调用
delete接口,确保前后端数据一致。 -
交互反馈:通过
Popconfirm组件实现删除确认,防止误操作。
-
-
showModal(打开权限分配弹窗):-
const showModal = (item) => {setIsModalOpen(true);//将当前未操作的权限保存起来setCurrentRight(item.rights);//记录当前操作的角色idsetCurrentID(item.id); }; -
状态初始化:记录当前角色的
rights到currentRight,作为树形选择器的checkedKeys初始值。 -
上下文保存:存储当前角色 ID 到
currentID,用于后续提交时定位角色。
-
-
handleOK(提交权限分配):-
const handleOK = (currentID) => {setIsModalOpen(false);//同步datasourcesetDataSource(dataSource.map((item) =>item.id === currentID ? { ...item, rights: currentRight } : item));//同步后端axios.patch(`http://localhost:3000/roles/${currentID}`, {rights: currentRight,}); }; -
状态更新:通过
map遍历角色列表,仅更新当前角色(id === currentID)的rights字段为currentRight,保持其他角色数据不变。 -
接口同步:使用
patch请求局部更新角色的权限配置,减少数据传输量。
-
-
onCheck(权限选择变化):-
const handleOK = (currentID) => {setIsModalOpen(false);//同步datasourcesetDataSource(dataSource.map((item) =>item.id === currentID ? { ...item, rights: currentRight } : item));//同步后端axios.patch(`http://localhost:3000/roles/${currentID}`, {rights: currentRight,}); }; -
实时同步:树形选择器勾选状态变化时,将最新的选中
key数组(checkedKeys.checked)更新到currentRight,确保弹窗内选择状态与状态同步。
-
-
表格与树形组件配置:
- 表格通过
rowKey={(item) => item.id}指定唯一标识,优化渲染性能。 - 树形选择器
Tree配置:checkable开启勾选功能,checkedKeys={currentRight}实现受控组件逻辑。checkStrictly={true}关闭父子关联勾选,支持独立选择任意权限。fieldNames={{ title: "label", key: "key" }}映射权限数据字段与树形组件要求的字段,确保正确渲染。
- 表格通过
-
4.4新闻管理模块:
说明:
新闻管理模块是系统核心功能之一,提供新闻全生命周期管理,包括新闻的创建、编辑、预览、草稿管理及分类维护。基于富文本编辑技术实现内容创作,通过分步流程引导用户完成新闻发布,支持草稿保存与审核提交,满足专业新闻发布流程需求。
1.撰写新闻组件
关键页面展示:
首页:

撰写:

提交新闻:

功能说明:
撰写新闻组件是新闻管理系统的核心创作入口,提供三步式新闻发布流程,支持草稿保存与审核提交功能。通过分步引导用户完成新闻标题填写、富文本内容编辑及最终提交操作,实现规范化的内容创作流程。
技术实现要点:
1. 分步流程设计
- 流程拆解:将新闻创作拆分为 3 个逻辑步骤,降低用户认知负荷
- 步骤 1(基本信息):收集新闻标题与分类信息
- 步骤 2(新闻内容):通过富文本编辑器创作内容
- 步骤 3(提交选项):提供保存草稿或提交审核的出口
- 状态管理:
current:记录当前步骤索引(0-2),控制步骤切换与内容展示steps:配置步骤元数据(标题、描述、内容组件)- 步骤切换通过
next()/prev()方法实现,包含表单验证逻辑
- 核心代码
const next = () => {if (current === 0 && (!newsTitle || !newsCategory)) {alert("请填写完整信息");return;}if (current === 1 && (news.content === "" || news.content === "<p></p>\n")) {alert("请填写内容");return;}setCurrent(current + 1);
};
2.表单数据处理
- 核心状态:
newsTitle:存储新闻标题(受控组件值)newsCategory:存储所选分类 IDnewsContent:存储富文本转换后的 HTML 内容categoryList:存储分类选项数据(格式化为 Ant Design Select 要求的{label, value}结构)
- 数据关联:
- 通过
useMemo缓存派生状态news,聚合标题、分类 ID 和内容,避免不必要的重计算 - 分类数据从
/categories接口获取,实现动态加载
- 通过
3.富文本集成
- 组件交互:集成
NewsEditor子组件,通过getContent回调接收 HTML 格式内容 - 数据流转:富文本编辑器内容变化时同步更新
newsContent状态,确保表单数据一致性
4.技术要点实现:
表单数据处理
-
受控组件设计:
- 新闻标题:通过
newsTitle状态和onChange事件实现双向绑定 - 新闻分类:使用
Select组件,通过handleChange同步选中的分类 ID
- 新闻标题:通过
-
分类数据加载:
useEffect(() => {axios.get("http://localhost:3000/categories").then((res) => {const formattedData = res.data.map(item => ({label: item.title,value: item.id}));setCategoryList(formattedData);}); }, []); -
派生状态优化:使用
useMemo缓存新闻对象,避免不必要的重计算const news = useMemo(() => ({title: newsTitle,categoryId: newsCategory,content: newsContent, }), [newsTitle, newsCategory, newsContent]);
富文本编辑器集成
-
组件通信:通过
getContent····················转换后的 HTML 内容<NewsEditorgetContent={(value) => {setNewsContent(value);}} /> -
内容转换:在子组件
NewsEditor中通过draftToHtml将编辑内容转为 HTML 格式存储
数据提交与状态管理
-
提交逻辑:根据
auditState参数区分保存草稿(0)和提交审核(1)const handleSave = (auditState) => {axios.post("http://localhost:3000/news", {...news,region: user.region || "全球",author: user.username,roleId: user.roleId,auditState,publishState: 0,createTime: Date.now(),star: 0,view: 0}).then(() => {// 处理成功逻辑}); }; -
操作反馈:使用 Ant Design 的
notification组件提供操作结果提示const [api, contextHolder] = notification.useNotification(); const openNotification = (placement, auditState) => {api.success({message: auditState === 0 ? "保存成功" : "提交成功",description: `您可以在${auditState === 0 ? "草稿箱" : "审核列表"}中查看`,placement,duration: 3,}); };
路由与权限关联
-
页面跳转:提交成功后根据操作类型跳转到对应列表页
if (auditState === 0) navigator("/news-manage/draft"); else navigator("/audit-manage/list"); -
用户信息获取:从
localStorage中读取当前登录用户信息,自动填充作者、区域等元数据const user = JSON.parse(localStorage.getItem("token"));
错误处理机制
-
提交失败处理:捕获 API 请求错误并显示错误提示
.catch((error) => {api.error({message: "操作失败",description: "保存新闻时发生错误,请重试",placement: "bottomRight",});console.error("Save error:", error); }); -
前置校验:在步骤切换时进行数据完整性检查,阻止无效提交
相关联组件:新闻更新组件
实现方法:新闻更新组件本质上和撰写新闻组件是一样的 差异在于会在处理副作用的时候预先将表格信息填写到Form表单中,并且最后的提交的请求发送的是patch更新储存
useEffect(()=>{axios.get(`http://localhost:3000/news/${id}?_expand=category&_expand=role`).then((res)=>{const newsData = res.data; // 设置状态setNewsContent(newsData.content || "")setNewsTitle(newsData.title || "")setNewsCategory(newsData.categoryId || "")// 设置表单值form.setFieldsValue({title: newsData.title || "",categoryId: newsData.categoryId || ""});}).catch(error => {console.error('获取数据失败:', error);})},[id, form])
2.预览新闻组件
关键页面展示:
功能说明:
新闻预览组件用于展示新闻的完整内容及相关元数据,提供新闻查看、返回上一页和编辑功能。该组件主要应用于新闻管理系统中,供用户预览已创建的新闻内容,验证新闻发布效果,并支持对自己创建的新闻进行编辑操作。
技术实现要点:
-
数据管理
- 数据获取:通过
useParams获取当前新闻 ID,结合useEffect钩子从接口http://localhost:3000/news/${id}?_expand=category&_expand=role请求新闻详情数据,包括关联的分类和角色信息 - 数据状态:使用
useState定义newsData状态存储新闻完整信息,初始值为空对象 - 数据依赖:将
id作为useEffect的依赖项,确保路由参数变化时重新请求数据 - 数据展示:通过条件渲染
{newsData && ...}避免数据请求延迟导致的空值错误
- 数据获取:通过
-
状态管理
- 用户信息:从
localStorage中读取当前登录用户信息(username),用于控制编辑按钮的权限 - 导航状态:使用
useNavigate获取导航函数,实现页面跳转功能 - 动态状态:通过
getStatusTag函数根据auditState(审核状态)和publishState(发布状态)动态生成状态标签,直观展示新闻当前状态
- 用户信息:从
-
数据调用与接口管理
- 接口设计:采用
RESTful风格 API,通过GET请求获取单条新闻详情,请求时使用_expand参数关联查询分类信息 - 时间格式化:使用
dayjs库对时间戳进行格式化处理,统一展示格式为YYYY-MM-DD HH:mm:ss - 内容渲染:通过
dangerouslySetInnerHTML属性解析富文本内容(HTML 格式),实现新闻内容的完整展示
- 接口设计:采用
-
数据处理与交互函数
-
返回功能:
handleBack函数通过window.history.back()实现返回上一页操作-
useEffect(()=>{axios.get(`http://localhost:3000/news/${id}?_expand=category&_expand=role`).then(res=>{setNewsData(res.data)})},[id]) // 添加 id 作为依赖项const handleBack = () => {console.log('返回按钮被点击');window.history.back()// 您的返回逻辑};
-
-
编辑功能:
handleEdit函数通过navigate跳转到新闻编辑页面(/news-manage/update/${id}),并通过disabled属性控制只有新闻作者可编辑-
const handleEdit = () => {console.log('编辑按钮被点击');// 您的编辑逻辑navigate(`/news-manage/update/${id}`) };
-
-
状态标签生成:
getStatusTag函数根据审核状态和发布状态的组合条件,返回不同颜色和文本的Tag组件,清晰标识新闻状态-
const getStatusTag = (auditState, publishState) => {if (auditState === 0 || auditState===1) {return <Tag color="orange">未审核</Tag>;} else if (auditState === 2) {if (publishState === 0) {return <Tag color="green">审核通过</Tag>;} else if (publishState === 2) {return <Tag color="cyan">已发布</Tag>;} else if(publishState===3){return <Tag color="red">已下线</Tag>;}else{return <Tag color="orange">待发布</Tag>;}} else {return <Tag color="red">审核不通过</Tag>;} };
-
-
空值处理:使用可选链操作符(
category?.title)和默认值(|| '未分类')处理可能的空数据,增强组件健壮性
-
4.5审核管理模块:
1.审核新闻组件
关键页面展示:

功能说明:
审核组件用于新闻审核流程中的审核操作,主要面向管理员角色(超级管理员和区域管理员),提供对提交审核的新闻进行审核通过或驳回的功能。该组件根据管理员权限展示不同范围的待审核新闻,支持查看新闻详情并执行审核操作,是新闻发布流程中的关键环节。
技术实现要点:
-
数据管理:
-
数据源定义:通过
dataSource状态存储待审核新闻的列表 -
数据筛选规则:
- 超级管理员(
id=1)可查看所有待审核新闻(auditState=1) - 区域管理员仅能查看本区域内、且非本人提交的待审核新闻(
region=${region}&author_ne=${username}) - 通过
_expand=category关联查询分类信息,用于展示新闻所属分类名称
- 超级管理员(
-
数据结构:每条数据包含新闻标题、作者、分类、ID 等核心字段,支持审核操作的标识与关联
-
-
状态管理
- 用户信息状态:从
localStorage中读取当前登录用户的region(区域)、role.id(角色 ID)和username(用户名),用于权限控制和数据筛选 - 列表数据状态:
dataSource状态实时存储待审核新闻列表,操作后立即更新本地数据以优化用户体验 - 权限状态区分:通过角色 ID 区分超级管理员和区域管理员,实现不同的数据访问权限控制
- 用户信息状态:从
-
数据调用与接口管理
-
数据请求接口:使用查询参数实现数据筛选,
_expand=category关联查询分类信息// 超级管理员获取所有待审核新闻 axios.get(`http://localhost:3000/news?_expand=category&auditState=1`)// 区域管理员获取权限范围内的待审核新闻 axios.get(`http://localhost:3000/news?region=${region}&_expand=category&auditState=1&author_ne=${username}`) -
状态更新接口:
- 审核通过:
axios.patch(http://localhost:3000/news/${item.id}, {auditState:2, publishState:1}) - 审核驳回:
axios.patch(http://localhost:3000/news/${item.id}, {auditState:3})通过patch请求部分更新新闻的审核状态和发布状态
- 审核通过:
-
-
数据处理与交互函数
-
源数据获取:
通过使用
LocalStorage中存储的当前登录用户信息中的角色id来判断身份,以获取不用的数据源const getRightList = async () => {if (id === 1) {const res = await axios.get(`http://localhost:3000/news?_expand=category&auditState=1`);setDataSource(res.data);} else {//普通管理员 只负责自己的区域 并且不能审核自己的新闻const res = await axios.get(`http://localhost:3000/news?region=${region}&_expand=category&auditState=1&author_ne=${username}`);setDataSource(res.data);} }; -
表格列定义:
- 新闻标题:通过锚点链接跳转到预览页面(
#/news-manage/preview/${item.id}),支持查看完整内容 - 作者:直接展示新闻作者信息
- 新闻分类:渲染关联查询到的分类名称(
category.title) - 操作列:提供 “通过” 和 “驳回” 按钮,执行对应的审核操作
- 新闻标题:通过锚点链接跳转到预览页面(
-
审核操作函数:
-
审核通过:点击按钮后,从本地列表中移除该新闻(
filter方法),同时调用接口将auditState更新为 2(审核通过),publishState更新为 1(待发布)()=>{ setDataSource(dataSource.filter((data) => data.id !== item.id)); axios.patch(`http://localhost:3000/news/${item.id}`,{auditState:2,publishState:1}) } -
审核驳回:点击按钮后,从本地列表中移除该新闻,调用接口将
auditState更新为 3(审核不通过)()=>{ setDataSource(dataSource.filter((data) => data.id !== item.id)); axios.patch(`http://localhost:3000/news/${item.id}`,{auditState:3}) }
-
-
权限控制逻辑:通过角色 ID 动态调整数据请求范围,确保不同级别管理员只能看到权限范围内的待审核新闻,区域管理员无法审核自己提交的新闻
-
2.已审核列表组件
关键页面展示:

功能说明:
审核 List 组件用于展示当前登录编辑用户提交的新闻审核状态列表,支持查看新闻详情、根据不同审核状态执行不同操作(撤销提交、发布新闻、修改内容)。该组件主要面向普通编辑角色,帮助其跟踪自己提交的新闻在审核流程中的状态变化,并进行相应的后续操作。
技术实现要点:
-
数据管理:
- 数据源定义:通过
dataSource状态存储审核列表数据,初始值为空数组 - 数据过滤规则:
- 仅展示当前登录用户(
username)提交的新闻 - 排除草稿状态(
auditState=0)、已发布(publishState=2)和已下线(publishState=3)的新闻 - 通过
_expand=category关联查询分类信息,用于展示新闻分类名称
- 仅展示当前登录用户(
- 数据获取时机:组件挂载时通过
useEffect钩子触发数据请求,依赖项为空数组确保只执行一次 - 数据结构:每条数据包含新闻标题、作者、分类、审核状态等核心字段,以及用于操作的
id标识
- 数据源定义:通过
-
状态管理
- 用户信息状态:从
localStorage中读取当前登录用户的username,用于数据筛选和权限控制 - 导航状态:通过
useNavigate获取路由导航函数,实现操作后的页面跳转 - 列表数据状态:
dataSource状态实时反映当前列表数据,支持动态更新(如撤销、发布操作后) - 审核状态映射:通过
getAuditStateStatusTag函数将数字类型的auditState转换为可视化的标签组件
- 用户信息状态:从
-
数据调用与接口管理
-
数据请求接口:
// 获取当前用户的审核中新闻 axios.get(`http://localhost:3000/news/?author=${username}&_expand=category`)- 使用查询参数
author筛选当前用户的新闻,_expand=category关联查询分类信息
- 使用查询参数
-
状态更新接口:
-
撤销提交:
-
axios.patch(\`http://localhost:3000/news/${item.id}, {auditState:0})
-
-
发布新闻:
-
axios.patch(`http://localhost:3000/news/${item.id}, {publishState:2, publishTime:Date.now()})
-
-
-
接口交互时机:在相应操作按钮的点击事件中触发,同步更新本地数据和服务器数据
-
-
数据处理与交互函数:
-
渲染数据获取:
-
使用过滤函数分别排除 草稿状态(
auditState=0)、已发布(publishState=2)和已下线(publishState=3)的新闻 -
const getRightList = async () => {const res = await axios.get(`http://localhost:3000/news?author=${username}&_expand=category`);setDataSource(res.data.filter((item)=>{return item.auditState!=0&&item.publishState!=2&&item.publishState!=3}));};
-
-
审核状态标签生成:
const getAuditStateStatusTag = (auditState) => {if (auditState === 1) return <Tag color="orange">未审核</Tag>;if (auditState === 2) return <Tag color="green">审核通过</Tag>;if (auditState === 3) return <Tag color="red">审核不通过</Tag>; };根据审核状态返回不同颜色和文本的标签组件,直观展示状态信息
-
表格列定义:
- 新闻标题:通过锚点链接跳转到预览页面(
#/news-manage/preview/${item.id}) - 新闻分类:渲染关联查询到的分类名称(
category.title) - 审核状态:调用
getAuditStateStatusTag生成状态标签 - 操作列:根据当前审核状态动态展示操作按钮
- 新闻标题:通过锚点链接跳转到预览页面(
-
操作处理函数:
-
撤销提交(
auditState=1):过滤本地列表数据并更新服务器审核状态为草稿(0)-
onClick={()=>{setDataSource(dataSource.filter((data) => data.id !== item.id));// 将新闻发布状态改为待发布axios.patch(`http://localhost:3000/news/${item.id}`,{publishState:2,publishTiem:Date.now()}).then(()=>{navigate('/publish-manage/published')});}}
-
-
发布新闻(
auditState=2):更新发布状态为已发布(2)并记录发布时间,成功后跳转到已发布列表-
()=>{ setDataSource(dataSource.filter((data) => data.id !== item.id)); // 将新闻发布状态改为待发布 axios.patch(`http://localhost:3000/news/${item.id}`,{publishState:2,publishTiem:Date.now()}).then(()=>{ navigate('/publish-manage/published') }); }
-
-
修改内容(
auditState=3):跳转到新闻编辑页面(/news-manage/update/${item.id})
-
-
列表更新策略:操作后通过
setDataSource立即更新本地列表数据,提升交互体验,同时同步更新服务器数据
-
4.6发布管理模块:
关键页面展示:
待发布页面

已发布页面

已下线页面

功能说明:
发布管理模块主要用于管理新闻的发布状态,包含三个核心子页面:
- 未发布新闻(
Unpublished.jsx):展示审核通过但尚未发布的新闻,支持发布操作 - 已发布新闻(
Published.jsx):展示当前处于发布状态的新闻,支持下线操作 - 已下线新闻(
Sunset.jsx):展示已从发布状态下线的新闻,支持删除操作
模块面向具有新闻发布权限的编辑角色,提供直观的新闻状态管理界面,支持通过操作按钮快速变更新闻发布状态,并实时更新展示列表。
技术实现要点
- 数据管理
- 数据源设计:通过
usePublishInfo自定义 Hook 统一管理发布列表数据,根据publishState参数区分不同状态的新闻publishState=1:未发布新闻publishState=2:已发布新闻publishState=3:已下线新闻
- 数据筛选规则:
- 仅展示当前登录用户创建的新闻(通过
author=${user.username}筛选) - 关联查询新闻分类信息(通过
_expand=category参数) - 仅包含审核通过的新闻(
auditState=2)
- 仅展示当前登录用户创建的新闻(通过
- 数据结构:每条数据包含新闻 ID、标题、作者、分类、发布状态等核心字段,支持操作标识与关联
- 状态管理
- 用户状态:从
localStorage读取当前登录用户信息(username),用于数据筛选和权限控制 - 列表状态:
dataSource状态存储当前页面的新闻列表数据,setDataSource方法用于更新列表状态 - 发布状态:通过
publishState属性区分不同子页面的新闻状态,实现组件复用 - 操作状态:操作按钮点击后立即更新本地列表状态,同时同步服务器数据,提升交互体验
- 数据调用与接口管理
-
数据请求接口:
// 获取指定状态的新闻列表 axios.get(`http://localhost:3000/news?author=${user.username}&auditState=2&_expand=category&publishState=${publishState}`) -
状态更新接口:
- 发布新闻:
axios.patch(http://localhost:3000/news/${item.id}, {publishState: 2, publishTime: Date.now()}) - 下线新闻:
axios.patch(http://localhost:3000/news/${item.id}, {publishState: 3}) - 删除新闻:
axios.delete(http://localhost:3000/news/${item.id})
- 发布新闻:
-
接口封装:通过自定义 Hook
usePublishInfo封装数据请求逻辑,实现数据获取与状态管理的复用
- 数据处理与交互函数
-
通用列表组件:
PublishList.jsx作为通用列表组件,通过接收publishState属性实现不同状态新闻列表的复用 -
动态操作按钮:根据
publishState动态展示操作按钮:- 未发布页面:显示 “发布” 按钮
- 已发布页面:显示 “下线” 按钮
- 已下线页面:显示 “删除” 按钮
-
操作处理函数:
// 发布新闻 const uploadNews = async (item) => {await axios.patch(`http://localhost:3000/news/${item.id}`, {publishState: 2,publishTime: Date.now()});setDataSource(dataSource.filter((data) => data.id !== item.id)); };// 下线新闻 const BreakdownNews = async (item) => {await axios.patch(`http://localhost:3000/news/${item.id}`, {publishState: 3,});setDataSource(dataSource.filter((data) => data.id !== item.id)); };// 删除新闻 const deleteNews = async (item) => {await axios.delete(`http://localhost:3000/news/${item.id}`);setDataSource(dataSource.filter((data) => dataid !== item.id)); }; -
表格列定义:
- 新闻标题:通过锚点链接跳转到预览页面
- 新闻分类:展示关联的分类名称
- 操作列:根据发布状态展示对应的操作按钮
组件设计
-
页面组件:
Unpublished.jsx:未发布新闻页面,传递publishState=1给列表组件Published.jsx:已发布新闻页面,传递publishState=2给列表组件Sunset.jsx:已下线新闻页面,传递publishState=3给列表组件
-
通用组件:
PublishList.jsx:核心列表组件,接收publishState属性,渲染对应状态的新闻列表usePublishInfo.jsx:自定义 Hook,封装数据请求与状态管理逻辑
-
权限控制:仅展示当前登录用户创建的新闻,确保数据隔离
-
流程控制:严格遵循新闻发布流程:
- 审核通过(
auditState=2)→ 未发布(publishState=1)→ 已发布(publishState=2)→ 已下线(publishState=3)
- 审核通过(
-
操作限制:根据新闻当前状态提供合法操作,避免无效状态转换
4.7页面框架模块:
1.顶部导航栏
关键页面展示:

功能说明:
顶部导航栏是新闻管理系统的全局导航组件,位于页面顶部,提供系统核心操作入口和用户信息展示。主要功能包括:
- 侧边栏折叠 / 展开控制
- 当前登录用户信息展示
- 用户角色显示
- 退出登录功能
该组件适配系统整体设计风格,与侧边导航栏联动,为用户提供一致的操作体验。
技术实现要点:
1.状态管理
-
侧边栏折叠状态:通过
Redux全局状态管理(collapsedStore)维护侧边栏折叠状态-
使用
useSelector获取当前折叠状态(collapsed) -
通过
dispatch(changeCollapsed())触发状态变更 -
//collapsedStore的Redux实例 import { createSlice } from "@reduxjs/toolkit";/* 持久化collapsedStore */ const collapsedStore = createSlice({name: 'collapsed',initialState:{collapsed: false},reducers: {changeCollapsed(state, action) {state.collapsed = action.payload}} })export const { changeCollapsed } = collapsedStore.actions const collapsedReducer = collapsedStore.reducer export default collapsedReducer
-
-
用户信息:从
localStorage中读取登录用户信息(token),包含用户名和角色信息
2.组件结构
- 基于 Ant Design 的
Layout.Header构建容器 - 左侧放置侧边栏折叠 / 展开按钮
- 右侧展示用户信息和下拉菜单
- 使用
Dropdown组件实现用户操作菜单
3.核心功能实现
-
侧边栏控制:
// 切换侧边栏折叠状态 <Buttontype="text"icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}onClick={()=>dispatch(changeCollapsed(!collapsed))} /> -
用户信息展示:
// 显示当前登录用户名 <span>欢迎{users.username}使用</span>// 头像与下拉菜单 <Dropdown menu={{ items }}><a onClick={(e) => e.preventDefault()}><Space><Avatar icon={<UserOutlined />} /><DownOutlined /></Space></a> </Dropdown> -
退出登录功能:
// 清除本地存储并刷新页面 onClick={() => {localStorage.removeItem("token");window.location.reload(); }}
2.侧边导航栏
关键页面展示:

功能说明:
侧边导航栏,根据登录用户的权限动态的生成侧边领,当前当前路由自动高亮选中菜单项,与顶部导航栏联动的折叠 / 展开功能,支持多级菜单展示(父菜单与子菜单层级结构)
技术实现要点
-
数据获取与权限过滤
-
获取用户信息:
const {role: { rights } } = JSON.parse(localStorage.getItem("token")); -
获取当前路由为当前选中菜单高亮:
const [selectedKeys, setSelectedKeys] = useState([location.pathname]); ...other codereturn(<Menu//....selectedKeys={selectedKeys}//.../> ) -
从后端获取权限列表并处理数据
newRes:用来保存处理完的权限列表数据newResChildren: 用来储存权限的子权限
const getItems = async () => {const res = await axios.get("http://localhost:3000/rights?_embed=children");//处理首页部分标签不需要展示问题//处理完的新结果let newRes = [];res.data.forEach((item) => {// 分开处理 处理存在children的和不存在的if (item.children.length === 0 && rights.includes(item.key)) {delete item.children;newRes.push(item);} else {let newResChildren = [];item.children.forEach((element) => {//如果是首页就添加到新的children数组中去 并且还要判断当前登录用户是不是包含该权限if (element.pagepermisson === 1 && rights.includes(element.key)) {newResChildren.push(element);}});//只有当用户有父级权限或者有子级权限时,才添加该菜单项// 如果用户没有某个模块的权限,且该模块下没有任何可访问的子菜单,该模块就不会显示在侧边菜单中// 如果用户有父级权限,无论子菜单如何,都会显示该模块if (rights.includes(item.key) || newResChildren.length > 0) {newRes.push({ ...item, children: newResChildren });}}});setItems(newRes);const setIcon = (item, iconMap) => {// 创建副本以避免直接修改原数组const newItems = JSON.parse(JSON.stringify(item));newItems.forEach((element) => {for (let key in iconMap) {if (key === element.label) {element.icon = iconMap[key];}}});return newItems;};setItems(setIcon(newRes, iconMap)); };
-
-
菜单ICON映射
-
icon的映射表:
const iconMap = {首页: <HomeOutlined />,用户管理: <UserOutlined />,用户列表: <UserOutlined />,权限管理: <SettingOutlined />,新闻管理: <FormOutlined />,审核管理: <AuditOutlined />,发布管理: <UploadOutlined />, }; -
映射函数:
const setIcon = (item, iconMap) => {// 创建副本以避免直接修改原数组const newItems = JSON.parse(JSON.stringify(item));newItems.forEach((element) => {for (let key in iconMap) {if (key === element.label) {element.icon = iconMap[key];}}});return newItems;};
-
-
联动路由
-
使用
useLocation获取当前路由路径,自动高亮对应菜单项 -
通过
selectedKeys状态维护选中状态 -
点击菜单项时通过
useNavigate实现路由跳转 -
// 路由联动核心逻辑 const location = useLocation(); const [selectedKeys, setSelectedKeys] = useState([location.pathname]);// 点击菜单跳转路由 onClick={(item) => {navigater(item.key);window.scrollTo(0, 0); }}
-
-
折叠状态管理
-
通过
Redux的collapsedStore获取全局折叠状态 -
与顶部导航栏的折叠 / 展开按钮联动
-
响应式调整侧边栏宽度
// 折叠状态获取 const {collapsed}=useSelector(state=>state.collapsed)// 折叠组件配置 <Sider trigger={null} collapsible collapsed={collapsed}>{/* 菜单内容 */} </Sider>
-
-
菜单数据处理
-
将处理好的菜单item重新使用过滤函数过滤,保证
antd组件不会因为数据问题而出错 -
const filterMenuItems = (items) => {return items.map((item) => {// 创建一个新的对象,只包含Menu组件需要的属性const filteredItem = {key: item.key,label: item.label,icon: item.icon,children: item.children ? filterMenuItems(item.children) : undefined,};return filteredItem;}); };
-
5.部署 / 运行说明
5.1 环境要求(Node 版本、包管理器等)
Node.js >= 16.0.0
npm >= 8.0.0 或 yarn >= 1.22.0
5.2 克隆 / 安装依赖步骤
git clone https://gitee.com/lenlers/news-release-system---act.git
cd newssystem
5.3 启动开发环境(前端 dev + 启动 JSON Server)
安装依赖
npm install
# 或者
yarn installnpm run server 启动后端服务
npm run dev 启动前端页面 
