当前位置: 首页 > news >正文

从0开始的中后台管理系统-5(部门管理以及菜单管理页面功能实现)

        上篇实现的用户管理功能实现,主要是通过表单发送请求携带参数访问接口,然后服务器去对数据进行筛选,部门管理以及菜单管理同理,也是通过表单发送请求携带参数访问对应的接口,然后服务器处理数据返回,前端展示。

    1.部门(菜单)管理页面

                

        本质上和用户管理页面是一样的,只不过部门需要嵌套上级部门,但是对于前端来说只需要携带参数访问接口就可以了。这里是代码。

        

import React, { useEffect, useRef, useState } from 'react'
import { Button, Form, Input, message, Modal, Space, Table } from 'antd'
import { useForm } from 'antd/es/form/Form'
import api from '@/api'
import type { Dept } from '@/types/api'
import CreateDept from './CreateDept'
import type { TableColumnsType } from 'antd'
import type { IAction } from '@/types/modal'
import { toLocalDate } from '@/utils'
export default function DeptList() {const [form] = useForm()const deptRef = useRef<{open: (type: IAction, data?: Dept.EditParams | { parentId: string }) => void}>(null)const [data, setData] = useState<Dept.DeptItem[]>([])useEffect(() => {getDeptList()}, [])const getDeptList = async () => {const data = await api.getDeptList(form.getFieldsValue())setData(data)}//重置功能const handleReset = () => {form.resetFields()}//创建部门const handleCreate = () => {deptRef.current?.open('create')}//创建部门const handleSubCreate = (id: string) => {deptRef.current?.open('create', { parentId: id })}//编辑部门const handleEdit = (record: Dept.DeptItem) => {deptRef.current?.open('edit', record)}//删除部门const handleDelete = (id: string) => {Modal.confirm({title: '确认删除吗',content: '确认删除该部门吗?',okText: '确定',cancelText: '取消',onOk() {handleDelSubmit(id)}})}//删除提交const handleDelSubmit = async (id: string) => {await api.deleteDept({id: id})message.success('删除成功')getDeptList()}const columns: TableColumnsType<Dept.DeptItem> = [{title: '部门名称',dataIndex: 'deptName',key: 'deptName',width: 200},{title: '负责人',dataIndex: 'userName',key: 'userName',width: 150},{title: '更新事件',dataIndex: 'updateTime',key: 'updateTime',render(updateTime) {return toLocalDate(updateTime)}},{title: '创建时间',dataIndex: 'createTime',key: 'createTime',render(createTime) {return toLocalDate(createTime)}},{title: '操作',dataIndex: 'action',width: 200,//新增的按钮没办法直接渲染 需要用render手动添加render(, record) {return (<Space><Buttontype='text'onClick={() => {handleSubCreate(record._id)}}>新增</Button><Buttontype='text'onClick={() => {handleEdit(record)}}>编辑</Button><Buttontype='text'onClick={() => {handleDelete(record._id)}}>删除</Button></Space>)}}]return (<div><Form className='search-form' layout='inline' form={form}><Form.Item label='部门名称' name='deptName'><Input placeholder='部门名称' /></Form.Item><Form.Item><Button type='primary' className='mr10' onClick={getDeptList}>搜索</Button></Form.Item><Form.Item><Button type='default' onClick={handleReset}>重置</Button></Form.Item></Form><div className='base-table'><div className='header-wrapper'><div className='title'>部门列表</div><div className='action'><Button type='primary' onClick={handleCreate}>新增</Button></div></div><TableborderedrowKey='_id'columns={columns}dataSource={data}pagination={false}/></div><CreateDept mRef={deptRef} update={getDeptList} /></div>)
}

        这是主页面,下面的弹出框

import type { Dept, User } from '@/types/api'
import type { IAction, ImodalProp } from '@/types/modal'
import { Form, Input, message, Modal, Select, TreeSelect } from 'antd'
import { useForm } from 'antd/es/form/Form'
import React, { useEffect, useImperativeHandle } from 'react'
import api from '@/api'
import { useState } from 'react'
export default function CreateDept(props: ImodalProp) {const [form] = useForm()const [action, setAction] = useState<IAction>('create')const [visible, setVisible] = useState(false)const [userList, setUserList] = useState<User.UserItem[]>([])const [deptList, setDeptList] = useState<Dept.DeptItem[]>([])useEffect(() => {getDeptList()getAllUserList()}, [])const getDeptList = async () => {const data = await api.getDeptList()setDeptList(data)}const getAllUserList = async () => {const data = await api.getAllUserList()setUserList(data)}useImperativeHandle(props.mRef, () => {return {open}})//打开弹框函数const open = async (type: IAction,data?: Dept.EditParams | { parentId: string }) => {setAction(type)setVisible(true)const deptData = await api.getDeptList()setDeptList(deptData)if (data) {form.setFieldsValue(data)} else {form.resetFields()}}//部门提交const handleSubmit = async () => {const valid = await form.validateFields()if (valid) {if (action === 'create') {await api.createDept(form.getFieldsValue())} else {await api.editDept(form.getFieldsValue())}message.success('操作成功')handleCancel()props.update()}}//关闭和重置弹框表单const handleCancel = () => {setVisible(false)form.resetFields()}return (<Modaltitle={action === 'create' ? '创建部门' : '编辑部门'}width={800}open={visible}okText='确定'cancelText='取消'onOk={handleSubmit}onCancel={handleCancel}><Form form={form} labelAlign='right' labelCol={{ span: 4 }}>{/* 隐藏域 */}<Form.Item name='_id' hidden><Input /></Form.Item><Form.Item label='上级部门' name='parentId'><TreeSelectplaceholder='请选择上级部门'allowCleartreeDefaultExpandAll//映射fieldNames={{ label: 'deptName', value: '_id' }}treeData={deptList}/></Form.Item><Form.Itemlabel='部门名称'name='deptName'rules={[{ required: true, message: '请输入部门名称' }]}><Input placeholder='请输入部门名称' /></Form.Item><Form.Itemlabel='负责人'name='userName'rules={[{ required: true, message: '请选择负责人' }]}><Select>{userList.map(item => {return (<Select.Option value={item.userName} key={item.userId}>{item.userName}</Select.Option>)})}</Select></Form.Item></Form></Modal>)
}

        菜单管理页面也是一模一样的,只是弹出框有点不同以及访问的接口响应数据不同。只展示一下效果图。

        

2.部门管理页面的路由设置

        实际上最开始生成的数据展示之后,其他的增删改查就是对json文件存储数据的增删改查,跟用户列表一样的,但是不同的是这里需要用到children,也就是数组嵌套数组,这样我们可能增删改查就需要考虑到children[]这个属性,也就是里面还有一层,那么就相当于我们判断外面一层之后,再去循环外层的每一个children调用函数去筛选,用到了递归。

const express = require('express')
const fs = require('fs/promises')
const path = require('path')
const router = express.Router()
// 读取部门列表
async function getDeptList() {const data = await fs.readFile(path.resolve(__dirname,'../data/deptList.json'))return JSON.parse(data)
}
// 递归过滤函数
function filterByDeptName(list, deptName) {return list.map(item => {// 深拷贝防止修改原数据const newItem = { ...item }// 递归过滤子部门newItem.children = filterByDeptName(newItem.children || [], deptName)// 当前部门匹配 或 子部门中有匹配的if (newItem.deptName.includes(deptName) || newItem.children.length > 0) {return newItem}return null}).filter(item => Boolean(item)) // 去掉 null
}
//重写文件
async function writeDeptList(newList) {await fs.writeFile(path.resolve(__dirname, '../data/deptList.json'), JSON.stringify({ data: newList }, null, 2))
}// 递归找父节点,根据 parentId
function findDeptById(list, parentId) {for (const item of list) {if (item._id === parentId) return itemif (item.children) {const found = findDeptById(item.children, parentId)if (found) return found}}return null
}
// 递归查找并更新指定_id的部门
function updateDeptById(list, id, updates) {for (let item of list) {if (item._id === id) {//Object.assign(item, updates)item.deptName = updates.deptNameitem.userName = updates.userNameitem.updateTime = updates.updateTimereturn true // 找到并更新,结束递归}if (item.children && item.children.length>0) {const updated = updateDeptById(item.children, id, updates)if (updated) return true}}return false // 没找到
}
//递归遍历删除指定_id的部门
// 递归遍历删除指定_id的部门,返回新数组(过滤掉指定_id节点)
function delDeptById(list, id) {// 过滤当前层,剔除id匹配的节点const filtered = list.filter(item => item._id !== id)// 对每个节点的children递归调用删除return filtered.map(item => {if (item.children && item.children.length > 0) {return {...item,children: delDeptById(item.children, id)}}return item})
}
router.use((req,res,next)=>{if(req.user){next()}else{res.status(201).send({code:500001,data:{},msg:'token失效了'})}})
router.get('/list',async(req,res)=>{const data = await getDeptList()const {deptName} = req.querylet list = data.data//console.log(deptName)if(deptName){list = filterByDeptName(list,deptName)}res.send({code: 0,data:list,msg: 'success'})
})
//创建部门路由
router.post('/create',async(req,res)=>{try {const { deptName, userName, parentId } = req.body// 读原始部门数据const deptData = await getDeptList()const list = deptData.data || []// 新部门对象,假设你用 _id 作为唯一id,这里用时间戳const newDept = {_id: Date.now().toString(),deptName,userName,updateTime:new Date().toLocaleString(),createTime:new Date().toLocaleString(),children: []}if (parentId) {// 找到对应父部门const parentDept = findDeptById(list, parentId)// 确保父部门有children数组if (!parentDept.children) parentDept.children = []parentDept.children.push(newDept)} else {// 顶级部门直接 pushlist.push(newDept)}// 写回文件await writeDeptList(list)res.send({code: 0,msg: '创建成功',data: newDept})} catch (error) {console.error(error)res.status(500).send({code: 500,msg: '服务器错误'})}
})
router.post('/edit', async (req, res) => {try {const { _id, deptName, userName } = req.body// 读文件const deptData = await getDeptList()const list = deptData.data || []const updates = {updateTime: new Date().toLocaleString(),deptName:deptName,userName:userName}// 更新const updated = updateDeptById(list, _id, updates)if (!updated) {return res.status(404).send({code: 0,msg: '部门未找到',})}// 写文件await writeDeptList(list)res.send({code: 0,msg: '编辑成功',data: updates,})} catch (error) {console.error(error)res.status(500).send({code: 500,msg: '服务器错误',})}
})
//删除列表
router.post('/delete', async (req, res) => {try {const { _id } = req.bodyconst data = await getDeptList()const list = data.data || []const newList = delDeptById(list, _id)await writeDeptList(newList)res.send({code: 0,msg: '删除成功',data: null})} catch (error) {console.error(error)res.status(500).send({code: 500,msg: '服务器错误'})}
})
module.exports =router

        递归还是很难用的,至少我只是有思路但是不通过ai写不出来,但好在思路是很简单的,就是本质上增删改查就是对数据进行筛选。

        

http://www.dtcms.com/a/326628.html

相关文章:

  • 【科研绘图系列】R语言绘制散点图折线图误差棒组合图
  • 指派问题-匈牙利算法
  • 2025牛客多校第八场 根号-2进制 个人题解
  • HTTPS应用层协议-CA签名与证书
  • Vue 3 快速入门 第六章
  • MaixPy简介
  • Projects
  • 进程管理是什么
  • DeepSeek生成的高精度大数计算器
  • 自制网页并爬取,处理异常值(第十九节课内容总结)
  • .NET/C# webapi框架下给swagger的api文档中显示注释(可下载源码)
  • MP3Tag 软件功能简介
  • (二)vscode搭建espidf环境,配置wsl2
  • 第16届蓝桥杯Python青少组中/高级组选拔赛(STEMA)2025年4月真题
  • 进阶版|企业级 AI Agent 的构建实践
  • 【03】厦门立林科技——立林科技 嵌入式 校招笔试,题目记录及解析
  • 从零开始的ReAct Agent尝试
  • 应用监控工具Skywalking
  • bitbake –s:列出所有可编译的模块
  • 【STL】queue队列容器
  • priority_queue(优先级队列)和仿函数
  • ArkUI中的自定义组件(一)
  • 用于计算的程序是部署在哪里,为什么可以这样?
  • 从 WebView2 迁移至 DotNetBrowser:第一部分
  • android 换肤框架详解2-LayoutInflater源码解析
  • 《零基础入门AI:深度学习基础核心概念解析(从激活函数到反向传播)》
  • 大模型提示词工程实践:提示词工程实践-引导大模型完成任务
  • 直播美颜SDK架构设计指南:美白滤镜的高效实现与跨平台适配
  • MySQL 基本语法
  • 【网络基础】深入理解 TCP/IP 协议体系