业务开发之用户管理(七)

云风网
云风笔记
云风知识库

首先从逻辑上,用户管理只限制admin用户显示

一、路由限制用户管理的访问权限

config/routes.ts添加access:admin权限限制

{name: 'userManage',icon: 'table',access: 'canAdmin',path: '/userManage',component: './userManage',}

二、页面水印处理

水印是 PageContainer 的功能,layout 只是透传给 PageContainer,屏蔽方法,找到文件src/app.tsx,直接注释代码

waterMarkProps: {content: initialState?.currentUser?.username
},

三、列表的增删改查功能接口联调以及页面代码修改

1.1、node开发查询用户接口,找到node项目router/user.js,添加查询用户列表接口代码。然后重启项目
//获取用户列表数据
router.get('/getUserList',commonFunc.authMiddle,(req, res) => {const page = parseInt(req.query.page) || 1;const limit = parseInt(req.query.limit) || 10;const name = req.query.username || '';userModel.find({ username: { $regex: name, $options: 'i' }}).countDocuments().then((allTotal)=>{userModel.find({ username: { $regex: name, $options: 'i' }}).skip((page - 1) * limit).limit(limit).then((data)=>{res.send({code: 200,msg: "获取成功!",data,total:allTotal});}).catch((err)=>{res.send(err)})})
})
//改变用户名称或者密码
router.post('/changeUser',commonFunc.authMiddle,(req, res) => {const token = req.headers.authorizationif(token){var { _id,username,password } = req.body;userModel.find({ username: { $regex: username, $options: 'i' }}).then((data)=>{if (data.length != 0) { //功能存在userModel.updateOne({_id: _id,},{$set: {username: username,password: password}}).then(() => {res.send({code: 200,msg: "修改成功!",});}).catch(err => {res.send(err)});}else{res.send({ code: 400, msg: "用户不存在,修改失败!" });}}).catch((err)=>{res.send(err)})}else{res.send({code: 401,msg: "请先登录!"});}
})
//删除用户
router.delete('/delUser',commonFunc.authMiddle,(req, res) => {const token = req.headers.authorizationif(token){//req 请求对象var { ids } = req.body;// 将字符串ID转换为ObjectId// const objectIds = ids.map(id => new ObjectId(id));userModel.deleteMany({ _id: { $in: ids } }).then(() => {res.send({code: 200,msg: "删除成功!",});}).catch(err => {res.send(err)});}else{res.send({code: 401,msg: "请先登录!"});}
})
1.2、改造用户管理前端代码src/pages/userManage/index.tsx
import { register, delUser, getUserList, updateUser } from '@/services/custom/api';
import { PlusOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns, ProDescriptionsItemProps } from '@ant-design/pro-components';
import {FooterToolbar,ModalForm,PageContainer,ProDescriptions,ProFormText,ProTable,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Button, Drawer,message,Form } from 'antd';
import React, { useRef, useState,useEffect } from 'react';
import type { FormValueType } from './components/UpdateForm';
import UpdateForm from './components/UpdateForm';/*** @en-US Add node* @zh-CN 添加节点* @param fields*/
const handleAdd = async (fields: API.RuleListItem) => {const hide = message.loading('正在添加');try {await register({ ...fields });hide();message.success('添加成功');return true;} catch (error) {hide();message.error('添加失败,请重试!');return false;}
};/*** @en-US Update node* @zh-CN 更新节点** @param fields*/
const handleUpdate = async (fields: FormValueType) => {const hide = message.loading('Configuring');try {await updateUser({username: fields.username,password: fields.password,_id: fields._id,});hide();message.success('修改成功');return true;} catch (error) {hide();message.error('修改失败,请稍后再试');return false;}
};/***  Delete node* @zh-CN 删除节点** @param selectedRows*/
const handleRemove = async (selectedRows: API.RuleListItem[],actionRef:any) => {const hide = message.loading('正在删除');if (!selectedRows) return true;try {await delUser({ids: selectedRows.map((row) => row._id),});hide();message.success('删除成功');if(actionRef){actionRef.current?.reloadAndRest?.();}return true;} catch (error) {hide();message.error('删除失败,请重试');return false;}
};const TableList: React.FC = () => {/*** @en-US Pop-up window of new window* @zh-CN 新建窗口的弹窗*  */const [createModalOpen, handleModalOpen] = useState<boolean>(false);/*** @en-US The pop-up window of the distribution update window* @zh-CN 分布更新窗口的弹窗* */const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);const [showDetail, setShowDetail] = useState<boolean>(false);const actionRef = useRef<ActionType>();const [currentRow, setCurrentRow] = useState<API.RuleListItem>();const [selectedRowsState, setSelectedRows] = useState<API.RuleListItem[]>([]);/*** @en-US International configuration* @zh-CN 国际化配置* */const intl = useIntl();const [form] = Form.useForm();const handleDel = async (selectedRows: API.RuleListItem[],actionRef: any) => {await handleRemove(selectedRows,actionRef);}useEffect(() => {form.resetFields();form.setFieldsValue({});});const columns: ProColumns<API.RuleListItem>[] = [{title: <FormattedMessage id="pages.searchTable.titleDesc" defaultMessage="Description" />,dataIndex: '_id',search:false,valueType: 'textarea'},{title: (<FormattedMessageid="pages.searchTable.updateForm.ruleName.nameLabel"defaultMessage="Rule name"/>),dataIndex: 'username',// render: (dom, entity) => {//   return (//     <a//       onClick={() => {//         setCurrentRow(entity);//         setShowDetail(true);//       }}//     >//       {dom}//     </a>//   );// },},{title: (<FormattedMessageid="pages.searchTable.password"defaultMessage="Number of service calls"/>),dataIndex: 'password',sorter: false,search:false,hideInForm: true,renderText: (val: string) =>`${val}`,},{title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="Operating" />,dataIndex: 'option',valueType: 'option',render: (_, record) => [<akey="config"onClick={() => {setCurrentRow(record);handleUpdateModalOpen(true);}}><FormattedMessage id="pages.searchTable.config" defaultMessage="Configuration" /></a>,<akey="del"onClick={() => {setCurrentRow(record);handleDel([record],actionRef)}}><FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" /></a>],},];return (<PageContainer><ProTable<API.RuleListItem, API.PageParams>headerTitle={intl.formatMessage({id: 'pages.searchTable.title',defaultMessage: '用户列表',})}actionRef={actionRef}rowKey="_id"search={{labelWidth:150}}toolBarRender={() => [<Buttontype="primary"key="primary"onClick={() => {handleModalOpen(true);}}><PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="New" /></Button>]}request={getUserList}columns={columns}rowSelection={{onChange: (_, selectedRows) => {setSelectedRows(selectedRows);},}}/>{selectedRowsState?.length > 0 && (<FooterToolbarextra={<div><FormattedMessage id="pages.searchTable.chosen" defaultMessage="Chosen" />{' '}<a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a>{' '}<FormattedMessage id="pages.searchTable.item" defaultMessage="项" /></div>}><ButtononClick={async () => {await handleRemove(selectedRowsState,'');setSelectedRows([]);actionRef.current?.reloadAndRest?.();}}><FormattedMessageid="pages.searchTable.batchDeletion"defaultMessage="Batch deletion"/></Button></FooterToolbar>)}<ModalFormform = {form}title={intl.formatMessage({id: 'pages.searchTable.createForm.newUser',defaultMessage: '新建用户',})}width="400px"open={createModalOpen}onOpenChange={handleModalOpen}onFinish={async (value) => {const success = await handleAdd(value as API.RuleListItem);if (success) {handleModalOpen(false);if (actionRef.current) {actionRef.current.reload();}}}}><ProFormTextrules={[{required: true,message: (<FormattedMessageid="pages.searchTable.ruleName"defaultMessage="用户名是必填的"/>),},]}label="用户名"placeholder="请输入用户名"width="md"name="username"initialValue={''}/><ProFormTextrules={[{required: true,message: (<FormattedMessageid="pages.login.password.placeholder"defaultMessage="密码是必填的"/>),},]}label="密码"placeholder="请输入密码"width="md"name="password"initialValue={''}/></ModalForm><UpdateFormonSubmit={async (value) => {const success = await handleUpdate(value);if (success) {handleUpdateModalOpen(false);setCurrentRow(undefined);if (actionRef.current) {actionRef.current.reload();}}}}onCancel={() => {handleUpdateModalOpen(false);if (!showDetail) {setCurrentRow(undefined);}}}updateModalOpen={updateModalOpen}values={currentRow || {}}/><Drawerwidth={600}open={showDetail}onClose={() => {setCurrentRow(undefined);setShowDetail(false);}}closable={false}>{currentRow?.name && (<ProDescriptions<API.RuleListItem>column={2}title={currentRow?.name}request={async () => ({data: currentRow || {},})}params={{id: currentRow?.name,}}columns={columns as ProDescriptionsItemProps<API.RuleListItem>[]}/>)}</Drawer></PageContainer>);
};export default TableList;

其中编辑组件代码components/UpdateForm

import {ModalForm,ProFormText
} from '@ant-design/pro-components';
import { useIntl,FormattedMessage } from '@umijs/max';
import { Form } from 'antd';
import React from 'react';
import { useEffect } from 'react';
export type FormValueType = {username?: string;password?: string;_id?: string;
} & Partial<API.RuleListItem>;export type UpdateFormProps = {onCancel: (flag?: boolean, formVals?: FormValueType) => void;onSubmit: (values: FormValueType) => Promise<void>;updateModalOpen: boolean;values: Partial<API.RuleListItem>;
};
const UpdateForm: React.FC<UpdateFormProps> = (props) => {const intl = useIntl();const [form] = Form.useForm();useEffect(() => {form.resetFields();form.setFieldsValue(props.values);});return (<ModalFormtitle={intl.formatMessage({id: 'pages.searchTable.updateForm.ruleConfig',defaultMessage: '编辑配置',})}form = {form}width="400px"open={props.updateModalOpen}onOpenChange={async (value) => {if(!value){props.onCancel(value);}}}onFinish={async (value:any) => {value._id = props.values._idprops.onSubmit(value)}}><ProFormTextrules={[{required: true,message: (<FormattedMessageid="pages.searchTable.ruleName"defaultMessage="请输入用户名"/>),}]}placeholder="请输入用户名"label="用户名"width="md"name="username"initialValue={props.values.username}/><ProFormTextrules={[{required: true,message: (<FormattedMessageid="pages.login.password.placeholder"defaultMessage="请输入密码"/>),}]}placeholder="请输入密码"label="密码"width="md"name="password"initialValue={props.values.password}/></ModalForm>);
};export default UpdateForm;

src/service/custom/typings.d.ts代码改造为

 type PageParams = {page?: number;limit?: number;};type UserListItem = {_id?: string;username?: string;password?: string;};type UserList = {data?: UserListItem[];/** 列表的内容总数 */total?: number;success?: boolean;};

src/service/custom/api.ts添加接口请求方法定义

/** 获取用户列表 GET /api/user/getUserList */
export async function getUserList(params: {// query/** 当前的页码 */page?: number;/** 页面的容量 */limit?: number;},options?: { [key: string]: any },
) {return request<API.UserList>('/api/user/getUserList', {method: 'GET',params: {...params,},...(options || {}),});
}/** 更新用户信息 PUT /api/user/changeUser */
export async function updateUser(options?: { [_id: string]: any }) {return request<API.UserListItem>('/api/user/changeUser', {method: 'POST',data: {...(options || {}),},});
}/** 删除用户 DELETE /api/user/delUser */
export async function delUser(options?: { [key: string]: any }) {return request<Record<string, any>>('/api/user/delUser', {method: 'delete',data: {...(options || {}),},});
}

四、注意要点

1、ModalForm下的ProFormText设置默认值initialValue只更新一次的问题
解决方法:
import { Form } from 'antd';
import { useEffect } from 'react';
const UpdateForm: React.FC<UpdateFormProps> = (props) => {const intl = useIntl();const [form] = Form.useForm();useEffect(() => {form.resetFields();form.setFieldsValue(props.values);});return (<ModalFormform = {form}><ProFormTextinitialValue={props.values.username}/></ModalForm>)}
2、隐藏不想要显示搜索框的列数据
解决方法:

columns中配置search:false

const columns: ProColumns<API.UserListItem>[] = [{title: <FormattedMessage id="pages.searchTable.titleDesc" defaultMessage="Description" />,dataIndex: '_id',search:false,valueType: 'textarea'}]

五、最终效果如下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/392962.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flink 实时数仓(四)【DWD 层搭建(二)流量域事实表】

前言 昨天刚搬到新校区&#xff0c;新校区小的可怜&#xff0c;好在之后出去实习交通可以方便点&#xff1b;待在学院太受限了&#xff0c;早点离开&#xff01; 今天开始完成 DWD 层剩余的需求&#xff0c;上一节我们把日志数据根据不同类型分流写入到了不同的主题&#xff1b…

云端医疗解决方案:互联网医院系统的云计算架构与实现

随着云计算技术的成熟和普及&#xff0c;医疗行业开始探索云端解决方案&#xff0c;以应对数据存储、计算能力和系统扩展性等方面的挑战。互联网医院系统作为医疗信息化的重要组成部分&#xff0c;通过云计算架构实现了高效、灵活和可扩展的医疗服务。本文将深入探讨互联网医院…

2024 AI开发者大赛火热进行中!

“iFLYTEK AI 开发者大赛”是由科大讯飞发起&#xff0c;中国信息协会联合主办的人工智能竞赛平台&#xff0c;汇聚产学研各界力量&#xff0c;面向全球开发者发起数据算法及创新应用类挑战&#xff0c;推动人工智能前沿科学研究和创新成果转化&#xff0c;培育人工智能产业人才…

YOLOv10改进 | 主干篇 | YOLOv10引入CVPR2023 顶会论文BiFormer用于主干修改

1. 使用之前用于注意力的BiFormer在这里用于主干修改。 YOLOv10改进 | 注意力篇 | YOLOv10引入BiFormer注意力机制 2. 核心代码 from collections import OrderedDict from functools import partial from typing import Optional, Union import torch import torch.nn as n…

如何评估并选择最佳的国内项目管理软件?

国内外主流的10款国内项目管理软件对比&#xff1a;PingCode、Worktile、Jira 、Basecamp、Trello、Asana 、Wrike、Tower 、禅道、Teambition 。 在选择适合自己企业的项目管理软件时&#xff0c;很多人会感到无从下手&#xff0c;担心无法找到既符合预算又能满足团队需求的解…

上网防泄密,这些雷区不要碰!九招教你如何防泄密

李明&#xff1a;“最近看到不少关于信息泄露的新闻&#xff0c;真是让人担忧。咱们在工作中&#xff0c;稍有不慎就可能触碰到泄密的雷区啊。” 王芳&#xff1a;“确实&#xff0c;网络安全无小事。尤其是我们这种经常需要处理敏感信息的岗位&#xff0c;更得小心谨慎。那你…

一行代码教你使用Python制作炫酷二维码

二维码&#xff0c;我们日常生活中随处可见的编码方式&#xff0c;凭借其方便快捷的信息承载能力&#xff0c;已经渗透到各行各业。 MyQR 的介绍 MyQR 是一个 Python 库&#xff0c;用于生成自定义二维码&#xff0c;包括带有 Logo、彩色和动态的二维码。它基于 Python 的 qr…

书生大模型实战营第三期——入门岛——Git基础知识

第三关&#xff1a;Git基础知识 任务如下&#xff1a; 任务描述 破冰活动&#xff1a;自我介绍 每位参与者提交一份自我介绍。 提交地址&#xff1a;GitHub - InternLM/Tutorial: LLM&VLM Tutorial 的 camp3 分支&#xff5e;实践项目&#xff1a;构建个人项目 创建一个个人…

电脑硬盘坏了数据可以恢复吗?如何恢复硬盘数据?

电脑硬盘坏了数据可以恢复吗&#xff1f;对于这种问题&#xff0c;还需要具体问题具体分析的&#xff0c;一般是可以恢复。 硬盘损坏可以分为物理损坏和逻辑损坏两种情况&#xff1a; 1.逻辑损坏 这通常是由于软件问题&#xff0c;如文件系统错误、病毒攻击、误删除、格式化等…

未发先火,Smartbi AIChat频频“出圈”

近日&#xff0c;思迈特正式官宣&#xff0c;将于8月8日线上新品发布会上推出自研的全新AI应用——Smartbi AIChat&#xff0c;这款应用在还未正式推向市场前&#xff0c;已获得媒体、分析机构等多方关注&#xff0c;热度飙升&#xff0c;思迈特软件及其新品再一次成为业界内外…

社交媒体分享预览图片和内容修改

在facebook发帖分享链接时&#xff0c;设置预览图片和内容 设置预览图片和内容 <head> <meta name"description" content"我是内容" /> </head> <body><img src"./1.jpg" alt"SEO Image" style"dis…

VSCode在windows系统下使用conda虚拟环境配置

如何解决CondaError: Run ‘conda init‘ before ‘conda activate‘_condaerror: run conda init before conda activat-CSDN博客 首先检查自己的anaconda是否是添加到整个的环境变量里了 打开cmd如果conda和python都能够识别那么就是配置成功了 然后看插件是否安装&#xf…

SQL注入实例(sqli-labs/less-9)

0、初始页面 1、爆库名 使用python脚本 def inject_database1(url):name for i in range(1, 20):low 32high 128mid (low high) // 2while low < high:payload "1 and if(ascii(substr(database(),%d,1)) > %d ,sleep(2),0)-- " % (i, mid)res {"…

Linux进程概念

目录 一.冯诺依曼体系 为什么程序运行会加载到内存 二.进程概念 1.进程控制块PCB 2.进程标识符 使用ps命令 使用pgrep命令 使用系统调用 3.进程状态 孤儿进程 守护进程(精灵进程) 4.进程优先级 三.环境变量 一.冯诺依曼体系 数据在设备之间的传输实质是数据的来回拷…

【Qt】项目代码

main.cpp文件 argc&#xff1a;命令行参数个数。*argv[ ]&#xff1a;每一个命令行参数的内容。main的形参就是命令行参数。QApplication a(argc, argv) 编写一个Qt的图形化界面程序&#xff0c;一定需要QApplication对象。 widget w; 在创建项目的时候&#xff0c;勾选widg…

mysql源码编译启动debug

对于没有C语言基础的同学来说&#xff0c;想看看源码&#xff0c;在搞定编辑器做debug的时候就被劝退了&#xff0c;发生点啥了&#xff0c;完全看不懂&#xff0c;不知道从哪里入手去做debug&#xff1b;我为了看看 mysql 的 insert buffer 到底存的是索引页还是数据页&#x…

ViT和SwinTransformer详解

ViT是Google brain发表于ICLR21上的工作&#xff0c;开创性将transformer用在vision领域&#xff0c;且图像识别性能超CNN&#xff0c;至今引用3.8w&#xff1b;原文&#xff1a;https://arxiv.org/pdf/2010.11929 SwinTransformer是微软亚洲研究院发表于ICCV21上&#xff0c;…

双回路校园智能电表是什么?什么叫双回路校园智能电表?

在智慧校园的建设浪潮中&#xff0c;双回路校园智能电表作为一种创新的能源计量与管理解决方案&#xff0c;正逐渐成为校园电力系统改造与升级的关键要素。本文旨在深入探讨双回路校园智能电表的概念、工作原理、核心优势及其在校园能源管理中的应用实践。 一、定义与工作原理…

Harbor 仓库一键安装

文章目录 一、场景说明二、脚本职责三、参数说明四、操作示例五、注意事项 一、场景说明 本自动化脚本旨在为提高研发、测试、运维快速部署应用环境而编写。 脚本遵循拿来即用的原则快速完成 CentOS 系统各应用环境部署工作。 统一研发、测试、生产环境的部署模式、部署结构、…

一文理清生产管理的“4管”和“8理”!

一提到生产管理&#xff0c;很多人的第一反应可能是车间里忙碌的身影、流水线上飞速运转的机器&#xff0c;还有一张张密密麻麻的生产计划表。但实际上&#xff0c;生产管理远不止于此。 “科学管理之父”弗雷德里克温斯洛泰勒认为&#xff1a;管理就是确切地知道你要别人干什…