【图文并茂】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,
        });
      }}
    >

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

完结

相关推荐
程序员清洒5 小时前
Flutter for OpenHarmony:GridView — 网格布局实现
android·前端·学习·flutter·华为
VX:Fegn08955 小时前
计算机毕业设计|基于ssm + vue超市管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
0思必得05 小时前
[Web自动化] 反爬虫
前端·爬虫·python·selenium·自动化
LawrenceLan5 小时前
Flutter 零基础入门(二十六):StatefulWidget 与状态更新 setState
开发语言·前端·flutter·dart
秋秋小事5 小时前
TypeScript 模版字面量与类型操作
前端·typescript
2401_892000526 小时前
Flutter for OpenHarmony 猫咪管家App实战 - 添加提醒实现
前端·javascript·flutter
Yolanda946 小时前
【项目经验】vue h5移动端禁止缩放
前端·javascript·vue.js
广州华水科技7 小时前
单北斗GNSS形变监测一体机在基础设施安全中的应用与技术优势
前端
EndingCoder7 小时前
案例研究:从 JavaScript 迁移到 TypeScript
开发语言·前端·javascript·性能优化·typescript
阿珊和她的猫9 小时前
React 路由:构建单页面应用的导航系统
前端·react.js·状态模式