【图文并茂】ant design pro 如何实战使用 ProFormTreeSelect


上一节 【图文并茂】ant design pro 如何统一封装好 ProFormSelect 的查询请求

这一节的内容会利用上一篇文章的内容的。

我们经常会实现一个远程数据调用的 select 框,不过有时候这个 select 是有层级的。

就像我们这个分类一样,有父分类,子分类的。

多层级。

怎么处理呢?

还是先实现后端。

后端

数据结构:

c 复制代码
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

c 复制代码
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 ,把它的内容填充好就行。

响应的数据是这样的:

c 复制代码
{
    "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 是一个层级关系。

前端

后端搞定了,就要搞前端。

c 复制代码
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}>
      <ProFormTreeSelect
        name={name}
        rules={[{ required: false }]}
        width="md"
        label={intl.formatMessage({ id: label })}
        allowClear
        secondary
        fieldProps={{
          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 组件

然后是这里:

c 复制代码
fieldNames: {
            label: 'name',
            value: '_id',
            children: 'children',
          },

要跟我的后端响应数据对上的。

其它的没啥,编辑的时候要填充好数据,才能选中。


c 复制代码
<ProForm
      initialValues={{
        ...values,
        parent: values?.parent?._id,
      }}
      onFinish={async (values) => {
        await onFinish({
          ...values,
        });
      }}
    >

这个地方,跟之前是一样的

完结

相关推荐
lpfasd1236 小时前
node-sass 停止维护!老项目迁移 Dart Sass 完整教程
前端·rust·sass
Data analyse4569 小时前
rnweb 回放数据量太大怎么办?分片存储与断点续传的实现思路
前端·网络·算法·数据分析
xiaogai_gai9 小时前
金蝶收料通知单查询策略实战教程:从源系统拉取到中间层落地的完整路径
java·前端·数据库
环境栈笔记10 小时前
指纹浏览器 WebRTC 怎么设置?从 ICE 候选、代理出口到功能验证
前端·人工智能·后端·自动化
赵大仁10 小时前
官网性能优化实战:LCP 3.2s → 2.1s、CLS 0.107 → 0,以及被我推翻的 6 个判断
前端·性能优化·cls·lcp·core web vitals
xiancai_xianyu10 小时前
Text2SQL落地生产库前,SQL要过哪三道闸
前端·数据库·sql·sql注入·text2sql·ai agent开发·企业级ai框架
小羊没烦恼!10 小时前
T-SQL查询进阶--理解SQL SERVER中的分区表
服务器·开发语言·前端·javascript·html
史瓦辛格10 小时前
给 DeepSeek Harness 写费用插件:把「这次会话花了多少钱」算准
前端·deepseek
星光不问赶路人10 小时前
Vue 3 + TypeScript 中 enum 的使用实践
前端·vue.js
鸥梨菌Honevid11 小时前
UVM 中 REQ 和 RSP 类型详解:从通信模型到 item_done 与 put_response
java·前端·servlet