上一节 【图文并茂】ant design pro 如何统一封装好 ProFormSelect 的查询请求
这一节的内容会利用上一篇文章的内容的。
我们经常会实现一个远程数据调用的 select 框,不过有时候这个 select 是有层级的。
就像我们这个分类一样,有父分类,子分类的。
多层级。
怎么处理呢?
还是先实现后端。
后端
数据结构:
const permissionGroupSchema = new mongoose.Schema({name: { type: String, required: true, unique: true },parent: { type: mongoose.Schema.Types.ObjectId, ref: 'PermissionGroup' },},{ timestamps: true },
);
我这里只存了一个 parent ,就是上一级
这样就能找到所有关联关系,比如当前的所有的 children
你要存 children 也行,但是操作起来会复杂,但查询变得简单。
我的数据量很小,只存 parent 就够用的。
controller
onst getPermissionGroups = handleAsync(async (req: Request, res: Response) => {const { current = '1', pageSize = '10' } = req.query;const query = buildQuery(req.query);// 执行查询const permissionGroups = await PermissionGroup.find(query).populate('parent') // Assuming you want to populate parent.sort('-createdAt') // Sort by creation time in descending order.skip((+current - 1) * +pageSize).limit(+pageSize).exec();const total = await PermissionGroup.countDocuments(query).exec();const getChildren = async (parentId: string | null): Promise<any[]> => {const children = await PermissionGroup.find({ parent: parentId }).populate('parent').exec();return Promise.all(children.map(async (child) => ({...child.toObject(),children: await getChildren(child._id),})),);};const getPermissionGroupsWithChildren = async (permissionGroups: any[],): Promise<any[]> => {return Promise.all(permissionGroups.map(async (permissionGroup) => ({...permissionGroup.toObject(),children: await getChildren(permissionGroup._id),})),);};const permissionGroupsWithChildren =await getPermissionGroupsWithChildren(permissionGroups);res.json({success: true,data: permissionGroupsWithChildren,total,current: +current,pageSize: +pageSize,});
});
接下来我去遍历循环 children ,把它的内容填充好就行。
响应的数据是这样的:
{"success": true,"data": [{"_id": "66b1b54ef8871ea52a7e3de9","name": "认证管理","createdAt": "2024-08-06T05:31:58.495Z","updatedAt": "2024-08-10T02:24:31.070Z","__v": 0,"children": [{"_id": "66b1b00bb5d937a0aef34034","name": "权限","createdAt": "2024-08-06T05:09:31.292Z","updatedAt": "2024-08-10T02:24:41.759Z","__v": 0,"parent": {"_id": "66b1b54ef8871ea52a7e3de9","name": "认证管理","createdAt": "2024-08-06T05:31:58.495Z","updatedAt": "2024-08-10T02:24:31.070Z","__v": 0},"children": []},{"_id": "66b6d2c9b9ad87dfa985f34f","name": "用户","parent": {"_id": "66b1b54ef8871ea52a7e3de9","name": "认证管理","createdAt": "2024-08-06T05:31:58.495Z","updatedAt": "2024-08-10T02:24:31.070Z","__v": 0},"createdAt": "2024-08-10T02:39:05.563Z","updatedAt": "2024-08-10T02:39:05.563Z","__v": 0,"children": []},{"_id": "66b6d2ddb9ad87dfa985f362","name": "菜单","parent": {"_id": "66b1b54ef8871ea52a7e3de9","name": "认证管理","createdAt": "2024-08-06T05:31:58.495Z","updatedAt": "2024-08-10T02:24:31.070Z","__v": 0},"createdAt": "2024-08-10T02:39:25.628Z","updatedAt": "2024-08-10T02:39:25.628Z","__v": 0,"children": []},{"_id": "66b6d2e9b9ad87dfa985f377","name": "角色","parent": {"_id": "66b1b54ef8871ea52a7e3de9","name": "认证管理","createdAt": "2024-08-06T05:31:58.495Z","updatedAt": "2024-08-10T02:24:31.070Z","__v": 0},"createdAt": "2024-08-10T02:39:37.339Z","updatedAt": "2024-08-10T02:39:37.339Z","__v": 0,"children": []},{"_id": "66b6d2fdb9ad87dfa985f38e","name": "数据权限","parent": {"_id": "66b1b54ef8871ea52a7e3de9","name": "认证管理","createdAt": "2024-08-06T05:31:58.495Z","updatedAt": "2024-08-10T02:24:31.070Z","__v": 0},"createdAt": "2024-08-10T02:39:57.756Z","updatedAt": "2024-08-10T02:39:57.756Z","__v": 0,"children": []},{"_id": "66b6d314b9ad87dfa985f3a7","name": "权限组","parent": {"_id": "66b1b54ef8871ea52a7e3de9","name": "认证管理","createdAt": "2024-08-06T05:31:58.495Z","updatedAt": "2024-08-10T02:24:31.070Z","__v": 0},"createdAt": "2024-08-10T02:40:20.528Z","updatedAt": "2024-08-10T02:40:20.528Z","__v": 0,"children": []},{"_id": "66b9ad348554e602536acc67","name": "认证管理菜单","parent": {"_id": "66b1b54ef8871ea52a7e3de9","name": "认证管理","createdAt": "2024-08-06T05:31:58.495Z","updatedAt": "2024-08-10T02:24:31.070Z","__v": 0},"createdAt": "2024-08-12T06:35:32.560Z","updatedAt": "2024-08-12T06:35:32.560Z","__v": 0,"children": []}]},{"_id": "66adec30d647a4fde5546b1c","name": "材料类目","createdAt": "2024-08-03T08:37:04.433Z","updatedAt": "2024-08-10T02:24:51.188Z","__v": 0,"children": []}],"total": 2,"current": 1,"pageSize": 10000
}
name 和 children 是关键。
最后提交给后端的数据肯定是 _id, 因为它才是唯一的。
name 是给用户看的,children 是一个层级关系。
前端
后端搞定了,就要搞前端。
import React from 'react';
import { ProFormTreeSelect } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import useQueryList from '@/hooks/useQueryList';
import { Spin } from 'antd';const PermissionGroupSelect = ({ name, label }: { name: string; label: string }) => {const intl = useIntl();const { items: permissionGroups, loading } = useQueryList('/permission-groups');return (<Spin spinning={loading}><ProFormTreeSelectname={name}rules={[{ required: false }]}width="md"label={intl.formatMessage({ id: label })}allowClearsecondaryfieldProps={{showArrow: false,treeDefaultExpandAll: true,filterTreeNode: true,showSearch: true,dropdownMatchSelectWidth: false,autoClearSearchValue: true,treeNodeFilterProp: 'name',fieldNames: {label: 'name',value: '_id',children: 'children',},treeData: permissionGroups,}}/></Spin>);
};export default PermissionGroupSelect;
主要是有两个东西,一个是 loading
我用的是 Spin 组件
然后是这里:
fieldNames: {label: 'name',value: '_id',children: 'children',},
要跟我的后端响应数据对上的。
其它的没啥,编辑的时候要填充好数据,才能选中。
<ProForminitialValues={{...values,parent: values?.parent?._id,}}onFinish={async (values) => {await onFinish({...values,});}}>
这个地方,跟之前是一样的
完结
- ant design pro 如何去保存颜色
- ant design pro v6 如何做好角色管理
- ant design 的 tree 如何作为角色中的权限选择之一
- ant design 的 tree 如何作为角色中的权限选择之二
- ant design pro access.ts 是如何控制多角色的权限的
- ant design pro 中用户的表单如何控制多个角色
- ant design pro 如何实现动态菜单带上 icon 的
- ant design pro 的表分层级如何处理
- ant design pro 如何处理权限管理
- ant design pro 技巧之自制复制到剪贴板组件
- ant design pro 技巧之实现列表页多标签
- 【图文并茂】ant design pro 如何对接登录接口
- 【图文并茂】ant design pro 如何对接后端个人信息接口
- 【图文并茂】ant design pro 如何给后端发送 json web token - 请求拦截器的使用
- 【图文并茂】ant design pro 如何使用 refresh token 可续期 token 来提高用户体验
- 【图文并茂】ant design pro 如何统一封装好 ProFormSelect 的查询请求