From d26274d89d377067c1e2648192cffbf89464b361 Mon Sep 17 00:00:00 2001
From: wsj <593216709@qq.com>
Date: Wed, 28 Oct 2026 23:34:32 +0800
Subject: [PATCH] feat: 拆包

---
 src/pages/system/users/components/UserFormDrawer.tsx |  125 +++++++++++++++++++++++++++++------------
 1 files changed, 87 insertions(+), 38 deletions(-)

diff --git a/src/pages/system/users/components/UserFormDrawer.tsx b/src/pages/system/users/components/UserFormDrawer.tsx
index aac230f..589c1a4 100644
--- a/src/pages/system/users/components/UserFormDrawer.tsx
+++ b/src/pages/system/users/components/UserFormDrawer.tsx
@@ -1,4 +1,5 @@
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useEffect, useMemo, useState } from 'react';
+import { ProFormCascader, ProFormSelect } from '@ant-design/pro-components';
 import {
   App as AntdApp,
   Button,
@@ -6,12 +7,46 @@
   Form,
   Input,
   InputNumber,
-  Select,
   Space,
   Spin,
 } from 'antd';
-import { createEmployee, getEmployeeDetail, updateEmployee } from '../server';
-import type { EmployeeFormValues, EmployeeVO, SelectOption } from '../types';
+import { createEmployee, getEmployeeDetail, updateEmployee } from '../service';
+import type {
+  EmployeeFormValues,
+  EmployeeVO,
+  OrgTreeNode,
+  SelectOption,
+} from '../types';
+
+interface OrgCascaderOption {
+  value: number;
+  label: string;
+  children?: OrgCascaderOption[];
+}
+
+/** 去掉最外层公司节点,只保留公司下的组织用于选择。 */
+const getSelectableOrgNodes = (nodes: OrgTreeNode[]): OrgTreeNode[] =>
+  nodes.flatMap((node) => (node.children?.length ? node.children : [node]));
+
+/** 组织树转为级联选择选项,按层级展示。 */
+const toCascaderOptions = (nodes: OrgTreeNode[]): OrgCascaderOption[] =>
+  nodes.map((node) => ({
+    value: node.id,
+    label: node.orgName,
+    children: node.children?.length ? toCascaderOptions(node.children) : undefined,
+  }));
+
+/** 在组织树中查找某个组织 ID 的完整路径,用于编辑 / 详情时回填级联值。 */
+const findOrgPath = (nodes: OrgTreeNode[], targetId: number): number[] | null => {
+  for (const node of nodes) {
+    if (node.id === targetId) return [node.id];
+
+    const childPath = node.children ? findOrgPath(node.children, targetId) : null;
+    if (childPath) return [node.id, ...childPath];
+  }
+
+  return null;
+};
 
 export type UserFormMode = 'create' | 'edit' | 'detail';
 
@@ -20,16 +55,20 @@
   mode: UserFormMode;
   /** 编辑 / 详情对象;新增时为空。 */
   record: EmployeeVO | null;
-  orgOptions: SelectOption[];
+  /** 组织树,用于所属组织的二级联动选择。 */
+  orgTree: OrgTreeNode[];
   roleOptions: SelectOption[];
   onClose: () => void;
   onSuccess: () => void;
 }
 
-const toFormValues = (employee: EmployeeVO): EmployeeFormValues => ({
+const toFormValues = (
+  employee: EmployeeVO,
+  orgTree: OrgTreeNode[],
+): EmployeeFormValues => ({
   name: employee.name,
   phone: employee.phone,
-  orgId: employee.orgId,
+  orgPath: findOrgPath(getSelectableOrgNodes(orgTree), employee.orgId) ?? [],
   roleId: employee.roleId,
   position: employee.position,
   email: employee.email,
@@ -41,7 +80,7 @@
   open,
   mode,
   record,
-  orgOptions,
+  orgTree,
   roleOptions,
   onClose,
   onSuccess,
@@ -51,6 +90,10 @@
   const [loading, setLoading] = useState(false);
   const { message } = AntdApp.useApp();
   const isDetail = mode === 'detail';
+  const orgCascaderOptions = useMemo(
+    () => toCascaderOptions(getSelectableOrgNodes(orgTree)),
+    [orgTree],
+  );
 
   useEffect(() => {
     if (!open) return;
@@ -66,7 +109,7 @@
         setLoading(true);
         try {
           const data = await getEmployeeDetail({ id: record.id });
-          if (active) form.setFieldsValue(toFormValues(data));
+          if (active) form.setFieldsValue(toFormValues(data, orgTree));
         } catch {
           // 错误已由 request 统一提示
           if (active) onClose();
@@ -77,7 +120,7 @@
       }
 
       if (mode === 'edit' && record) {
-        form.setFieldsValue(toFormValues(record));
+        form.setFieldsValue(toFormValues(record, orgTree));
       }
     };
 
@@ -86,7 +129,7 @@
     return () => {
       active = false;
     };
-  }, [form, mode, onClose, open, record]);
+  }, [form, mode, onClose, open, orgTree, record]);
 
   const handleSubmit = useCallback(async () => {
     if (isDetail) return;
@@ -99,12 +142,16 @@
     }
 
     setSubmitting(true);
+
+    const { orgPath, ...rest } = values;
+    const orgId = orgPath[orgPath.length - 1];
+
     try {
       if (mode === 'edit' && record) {
-        await updateEmployee({ id: record.id, ...values });
+        await updateEmployee({ id: record.id, ...rest, orgId });
         message.success('员工信息已更新');
       } else {
-        await createEmployee(values);
+        await createEmployee({ ...rest, orgId });
         message.success('员工创建成功');
       }
 
@@ -155,29 +202,39 @@
           >
             <Input placeholder="请输入手机号" maxLength={11} disabled={isDetail} />
           </Form.Item>
-          <Form.Item
-            name="orgId"
+          <ProFormCascader
+            name="orgPath"
             label="所属组织"
             rules={[{ required: true, message: '请选择所属组织' }]}
-          >
-            <Select
-              showSearch
-              optionFilterProp="label"
-              options={orgOptions}
-              placeholder="请选择所属组织"
-              disabled={isDetail}
-            />
-          </Form.Item>
-          <Form.Item
+            placeholder="请选择所属组织"
+            disabled={isDetail}
+            fieldProps={{
+              options: orgCascaderOptions,
+              changeOnSelect: true,
+              showSearch: true,
+              style: { width: '100%' },
+            }}
+          />
+          <ProFormSelect
             name="roleId"
             label="角色"
             rules={[{ required: true, message: '请选择角色' }]}
+            placeholder="请选择角色"
+            disabled={isDetail}
+            showSearch
+            options={roleOptions}
+            fieldProps={{ optionFilterProp: 'label', style: { width: '100%' } }}
+          />
+          <Form.Item
+            name="holdLimit"
+            label="客户持有上限"
+            rules={[{ required: true, message: '请输入客户持有上限' }]}
           >
-            <Select
-              showSearch
-              optionFilterProp="label"
-              options={roleOptions}
-              placeholder="请选择角色"
+            <InputNumber
+              min={0}
+              precision={0}
+              placeholder="请输入客户持有上限"
+              style={{ width: '100%' }}
               disabled={isDetail}
             />
           </Form.Item>
@@ -186,14 +243,6 @@
           </Form.Item>
           <Form.Item name="region" label="负责区域">
             <Input placeholder="请输入负责区域" disabled={isDetail} />
-          </Form.Item>
-          <Form.Item name="holdLimit" label="客户持有上限">
-            <InputNumber
-              min={0}
-              precision={0}
-              style={{ width: '100%' }}
-              disabled={isDetail}
-            />
           </Form.Item>
           <Form.Item
             name="email"

--
Gitblit v1.9.3