云风网
云风笔记
云风知识库
首先从逻辑上,用户管理只限制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'}]
五、最终效果如下