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

从0开始的中后台管理系统-5(userList页面功能实现)

        本篇实现了一个完整的用户列表的增删改查以及搜索和重置功能,主要是引入一个ahoos插件库然后引入了一个useAntdTable()钩子来帮助我们调用获取用户列表的请求以及在配置对象绑定form对象后可以帮我们生成search方法(本质上也是调用获取用户列表请求然后携带查询字符串参数的方式让服务器对数据进行筛选)。也会去展示后端代码。

    1.前端代码

                

import React, { useState, useEffect, useRef } from 'react'
import type { PageParams, User } from '@/types/api'
import type { TableColumnsType } from 'antd'
import { Button, Table, Form, Input, Select, Space, Modal, message } from 'antd'
import api from '@/api/index'
import { toLocalDate } from '@/utils'
import CreateUser from './CreateUser'
import type { IAction } from '@/types/modal'
import { useAntdTable } from 'ahooks'
export default function UserList() {const userRef = useRef<{open: (type: IAction, data?: User.UserItem) => void}>(null)//获取表单对象 用里面的方法 getFieldsValue获取表单中填写的内容const [form] = Form.useForm()const [userIds, setUserIds] = useState<number[]>([])//创建const handleCreate = () => {userRef.current?.open('create')}//编辑const handleEdit = (recoad: User.UserItem) => {userRef.current?.open('edit', recoad)}//删除用户const handleDel = (userId: number) => {Modal.confirm({title: '删除确认',content: <span>确认删除改用户吗</span>,onOk: () => {handleUserDelSubmit([userId])}})}//批量删除确认const handlePatchConfirm = () => {if (userIds.length === 0) {message.error('请选择要删除掉用户')return}Modal.confirm({title: '删除确认',content: <span>确认删除改批量用户吗</span>,onOk: () => {handleUserDelSubmit(userIds)}})}//公共的删除接口const handleUserDelSubmit = async (ids: number[]) => {try {const data = await api.delUser({userId: ids})message.success('删除成功')setUserIds([])search.reset()} catch (e) {}}/*先写一个函数接口调用请求
对,这就是 getTableData,负责发请求并返回 Promise,且 resolve { total, list }。把表单数据作为 Promise 实例返回对,formData 参数就是表单的值,useAntdTable 会帮你自动传进来。钩子的第一个参数是接口函数,第二个参数是配置对象
对,配置对象里你用了 form(useForm 的表单对象)和 defaultPageSize。form 绑定到表格中,配置项绑定分页和搜索对,传了 form 以后,表单和表格就能联动搜索了。search.submit() 和 search.reset() 分别触发搜索和重置对,搜索和分页都是重新调用接口,只是参数不同。*/const getTableData = ({current,pageSize}: {current: numberpageSize: number},formData: User.Params) => {return api.getUserList({...formData,pageNum: current,pageSize: pageSize}).then(data => {return {total: data.page.total,list: data.list}})}const { tableProps, search } = useAntdTable(getTableData, {form,defaultPageSize: 5})const columns: TableColumnsType<User.UserItem> = [{title: '用户ID',dataIndex: 'userId',key: 'userId'},{title: '用户名称',dataIndex: 'userName',key: 'userName'},{title: '用户邮箱',dataIndex: 'userEmail',key: 'userEmail'},{title: '用户角色',dataIndex: 'role',key: 'role',render(role: number | string) {return {0: '超级管理员',1: '管理员',2: '体验管理员',3: '普通用户'}[role]}},{title: '用户状态',dataIndex: 'state',key: 'state',render(state: number) {return {1: '在职',2: '离职',3: '试用期'}[state]}},{title: '注册时间',dataIndex: 'create', // 你的字段是 create,不是 createTimekey: 'create',render(createTime: string) {return toLocalDate(createTime)}},{title: '操作',key: 'adress',render(record) {return (<Space><Buttontype='text'onClick={() => {handleEdit(record)}}>编辑</Button><Button type='text' danger onClick={() => handleDel(record.userId)}>删除</Button></Space>)}}]return (<div className='user-list'><Formform={form}className='search-form'layout='inline'initialValues={{ state: 0 }}><Form.Item name='userId' label='用户ID'><Input placeholder='请输入用户ID' /></Form.Item><Form.Item name='userName' label='用户名称'><Input placeholder='请输入用户名称' /></Form.Item><Form.Item name='state' label='状态'><Select style={{ width: 120 }}><Select.Option value={0}>所有</Select.Option><Select.Option value={1}>在职</Select.Option><Select.Option value={2}>离职</Select.Option><Select.Option value={3}>试用期</Select.Option></Select></Form.Item><Form.Item><Space><Button onClick={search.submit} type='primary' className='mr10'>搜索</Button><Button onClick={search.reset} type='default'>重置</Button></Space></Form.Item></Form><div className='base-table'><div className='header-wrapper'><div className='title'>用户列表</div><div className='action'><Button type='primary' onClick={handleCreate}>新增</Button><Buttontype='primary'dangeronClick={() => {handlePatchConfirm()}}>批量删除</Button></div></div><TablerowKey='userId' // 保证每行有唯一 key{...tableProps}bordered//rowSelection多选rowSelection={{type: 'checkbox',selectedRowKeys: userIds,onChange: (selecterRowKeys: React.Key[]) => {setUserIds(selecterRowKeys as number[])}}}columns={columns}/></div><CreateUsermRef={userRef}updata={() => {search.reset()}}/></div>)
}

        增删改查实际上就是调用不同的api,然后把对应请求需要的数据传递过去,比如删除实际上需要传递一个userId参数,服务器拿到之后筛选,增加实际上是把在表单中填写的所有数据放在请求体中传递给服务器服务器push进数组。然后修改就是userId拿到对应的数组然后覆盖掉传过去不同的值。仅此而已。

        这里用ahoos插件库里面的useAntdTable钩子用法是1.先写一个函数接口调用请求,这就是 getTableData,负责发请求并返回 Promise,且 resolve { total, list }。2.把表单数据作为 Promise 实例返回3.formData 参数就是表单的值,useAntdTable 会帮你自动传进来。4.钩子的第一个参数是接口函数,第二个参数是配置对象5.配置对象里你用了 form(useForm 的表单对象)和 defaultPageSize。6.form 绑定到表格中,配置项绑定分页和搜索,传了 form 以后,表单和表格就能联动搜索了。7.search.submit() 和 search.reset() 分别触发搜索和重置,搜索和分页都是重新调用接口,只是参数不同。

2.后端代码

        前后端分离就是处理数据基本放在服务端,服务端通过前端请求携带的参数进行处理。

        这是服务器的结构目录,没有搭建数据库连接为了持久化存储放在了json文件中。然后userList路由存放对userslist.json中存放的数据进行增删改查的路由设置。

const express = require('express')
const fs = require('fs/promises')
const path = require('path')
const multer = require('multer')
const router = express.Router()// 读取单个用户信息
async function getUserList() {const data = await fs.readFile(path.resolve(__dirname,'../data/userList.json'))return JSON.parse(data)
}// 读取用户列表信息
async function getUsersList() {const data = await fs.readFile(path.resolve(__dirname,'../data/usersList.json'))return JSON.parse(data)
}
//将新数组重新写入文件中并且将新数据读取出来
async function updataUsersList(newUsers) {await fs.writeFile(path.resolve(__dirname, '../data/usersList.json'),JSON.stringify(newUsers, null, 2) // 转成 JSON 并美化)console.log('写入成功')const data = await fs.readFile(path.resolve(__dirname, '../data/usersList.json'))return JSON.parse(data)
}/* ------------------ 图片上传相关配置 ------------------ */// 配置 multer 存储位置和文件名
const storage = multer.diskStorage({// 设置文件保存的目录destination: (req, file, cb) => {// 将图片存储到 /public/images 目录// path.join 确保路径兼容不同系统cb(null, path.join(__dirname, '../public/images'))},// 设置文件的命名规则filename: (req, file, cb) => {// 取出文件原始的后缀名,比如 .jpg / .pngconst ext = path.extname(file.originalname)// 使用当前时间戳作为文件名,避免重名覆盖cb(null, Date.now() + ext)}
})// 使用 multer 并绑定存储配置
const upload = multer({ storage })
router.use((req,res,next)=>{if(req.user){next()}else{res.send({code: 50001,data: {},msg: 'token过期了'})}
})
// 上传接口
// upload.single('file') 表示只接收一个文件,字段名是 'file'
router.post('/upload', upload.single('file'), (req, res) => {// 生成前端可访问的图片路径(这里是相对路径)const fileUrl = http://localhost:3000/images/${req.file.filename}// 返回上传成功的响应res.send({code: 0,msg: '上传成功',url: fileUrl // 前端可以用这个 URL 来显示图片})
})
/* ------------------------------------------------------- */// 获取单个用户信息接口
router.get('/getUserInfo', async (req, res) => {const data = await getUserList()res.send(data)
})// 获取用户列表接口
router.get('/list', async (req, res) => {const data = await getUsersList()let list = data.data.list// 获取 query 参数const { state, userId, userName } = req.queryconsole.log(state,userId,userName)// 按状态筛选(0 表示全部,不筛选)if (state && state !== '0') {list = list.filter(item =>String(item.state) === String(state))}// 按用户ID筛选if (userId) {list = list.filter(item => String(item.userId).includes(String(userId)))}// 按用户名称模糊搜索if (userName) {list = list.filter(item =>item.userName && item.userName.includes(userName))}res.send({code: 0,data: {list,page: {...data.page,total:list.length}},msg: 'success'})
})
//创建用户接口
router.post('/create',async(req,res)=>{// console.log(req.body)const data = await getUsersList()const newUser={userId:Date.now(),...req.body}data.data.list.push(newUser)const newData= await  updataUsersList(data)res.send(newData)
})
//编辑用户接口
router.post('/edit',async(req,res)=>{console.log(req.body)const data = await getUsersList()let index = data.data.list.findIndex((item)=> item.userId===req.body.userId)if(index!==-1){data.data.list[index]={...data.data.list[index],...req.body}}const newData = await updataUsersList(data)res.send(newData)
})
//删除用户
router.post('/delete',async(req,res)=>{console.log(req.body)const { userId } = req.bodyif(userId &&userId.length!==0){const data = await getUsersList()data.data.list = data.data.list.filter((item)=>!userId.includes(item.userId))const newData = await updataUsersList(data)res.send(newData)}else{res.status(400).send({code:0,data:{},msg:'请输入要删除的数组userId'})}
})
module.exports = router

        首先中间件是判断是否有token,然后就是增删改查路由,前面的异步函数都是读取文件的数据,然后userslist数据也是统一处理。比如传入新数据重写到文件然后重新读取转化为对象,让服务器发送给前端,剩下的就是根据前端请求携带的请求体或者query参数中拿到数据进行增删改查然后响应给前端。

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

相关文章:

  • 自测电脑有没有木马
  • 深度学习周报(8.4~8.10)
  • 使用binutils工具解析目标文件符号表(叁)
  • Datawhale AI夏令营 多模态RAG环境问题
  • 海关 瑞数 失信企业 逆向 分析 后缀 rs
  • es查询小结
  • CSS优先级、HTTP响应状态码
  • BGP综合大实验
  • 人工智能-python-机器学习-模型选择与调优实战指南:从交叉验证到朴素贝叶斯分类
  • 为wordpress顶部header.php文件中调用不同的标题和摘要
  • 学习中的杂项知识
  • 在Word和WPS文字一页中实现一栏与多栏混排
  • 打靶日常-upload-labs(21关)
  • MyBatisPlus插件原理
  • Java 虚拟机运行时数据区组成详解
  • 【Vue2与Vue3的核心区别】响应式、运行时、编译器
  • 医学统计(随机对照研究分类变量结局数据的统计策略2)
  • 五种 IO 模型与阻塞 IO
  • Redis一站式指南二:主从模式高效解决分布式系统“单点问题”
  • 对话式BI有什么用?不懂技术也能用对话式BI搞定业务报表
  • 面对信号在时频平面打结,VNCMD分割算法深度解密
  • AMS1117-3.3 低压差线性稳压器 (LDO) 3.3V芯片 引脚图中文资料
  • 【Python 工具人快餐 · 第 4 份】
  • 【LLM】什么是Function Calling以及实现原理
  • Day41--动态规划--121. 买卖股票的最佳时机,122. 买卖股票的最佳时机 II,123. 买卖股票的最佳时机 III
  • 工业相机选择规则
  • 如何将PDF文档进行高效编辑处理!
  • Rust 实战四 | Traui2+Vue3+Rspack 开发桌面应用:通配符掩码计算器
  • Virtio 驱动初始化数据收发流程详解
  • 如何解决线上gc频繁的问题?