wsj
4 days ago fe1692fd7f4f307fea8054da6c6c50d5df2bf2d8
src/pages/system/users/components/UserFormDrawer.tsx
@@ -1,7 +1,8 @@
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import {
  App as AntdApp,
  Button,
  Cascader,
  Drawer,
  Form,
  Input,
@@ -11,7 +12,38 @@
  Spin,
} from 'antd';
import { createEmployee, getEmployeeDetail, updateEmployee } from '../server';
import type { EmployeeFormValues, EmployeeVO, SelectOption } from '../types';
import type {
  EmployeeFormValues,
  EmployeeVO,
  OrgTreeNode,
  SelectOption,
} from '../types';
interface OrgCascaderOption {
  value: number;
  label: string;
  children?: OrgCascaderOption[];
}
/** 组织树转为级联选择选项,按层级展示。 */
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 +52,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(orgTree, employee.orgId) ?? [],
  roleId: employee.roleId,
  position: employee.position,
  email: employee.email,
@@ -41,7 +77,7 @@
  open,
  mode,
  record,
  orgOptions,
  orgTree,
  roleOptions,
  onClose,
  onSuccess,
@@ -51,6 +87,7 @@
  const [loading, setLoading] = useState(false);
  const { message } = AntdApp.useApp();
  const isDetail = mode === 'detail';
  const orgCascaderOptions = useMemo(() => toCascaderOptions(orgTree), [orgTree]);
  useEffect(() => {
    if (!open) return;
@@ -66,7 +103,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 +114,7 @@
      }
      if (mode === 'edit' && record) {
        form.setFieldsValue(toFormValues(record));
        form.setFieldsValue(toFormValues(record, orgTree));
      }
    };
@@ -86,7 +123,7 @@
    return () => {
      active = false;
    };
  }, [form, mode, onClose, open, record]);
  }, [form, mode, onClose, open, orgTree, record]);
  const handleSubmit = useCallback(async () => {
    if (isDetail) return;
@@ -99,12 +136,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('员工创建成功');
      }
@@ -156,16 +197,17 @@
            <Input placeholder="请输入手机号" maxLength={11} disabled={isDetail} />
          </Form.Item>
          <Form.Item
            name="orgId"
            name="orgPath"
            label="所属组织"
            rules={[{ required: true, message: '请选择所属组织' }]}
          >
            <Select
            <Cascader
              options={orgCascaderOptions}
              changeOnSelect
              showSearch
              optionFilterProp="label"
              options={orgOptions}
              placeholder="请选择所属组织"
              disabled={isDetail}
              style={{ width: '100%' }}
            />
          </Form.Item>
          <Form.Item