import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { ProFormCascader, ProFormSelect } from '@ant-design/pro-components';
|
import {
|
App as AntdApp,
|
Button,
|
Drawer,
|
Form,
|
Input,
|
InputNumber,
|
Space,
|
Spin,
|
} from 'antd';
|
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';
|
|
interface UserFormDrawerProps {
|
open: boolean;
|
mode: UserFormMode;
|
/** 编辑 / 详情对象;新增时为空。 */
|
record: EmployeeVO | null;
|
/** 组织树,用于所属组织的二级联动选择。 */
|
orgTree: OrgTreeNode[];
|
roleOptions: SelectOption[];
|
onClose: () => void;
|
onSuccess: () => void;
|
}
|
|
const toFormValues = (
|
employee: EmployeeVO,
|
orgTree: OrgTreeNode[],
|
): EmployeeFormValues => ({
|
name: employee.name,
|
phone: employee.phone,
|
orgPath: findOrgPath(getSelectableOrgNodes(orgTree), employee.orgId) ?? [],
|
roleId: employee.roleId,
|
position: employee.position,
|
email: employee.email,
|
holdLimit: employee.holdLimit,
|
region: employee.region,
|
});
|
|
const UserFormDrawer = ({
|
open,
|
mode,
|
record,
|
orgTree,
|
roleOptions,
|
onClose,
|
onSuccess,
|
}: UserFormDrawerProps) => {
|
const [form] = Form.useForm<EmployeeFormValues>();
|
const [submitting, setSubmitting] = useState(false);
|
const [loading, setLoading] = useState(false);
|
const { message } = AntdApp.useApp();
|
const isDetail = mode === 'detail';
|
const orgCascaderOptions = useMemo(
|
() => toCascaderOptions(getSelectableOrgNodes(orgTree)),
|
[orgTree],
|
);
|
|
useEffect(() => {
|
if (!open) return;
|
|
let active = true;
|
|
const initForm = async () => {
|
form.resetFields();
|
|
if (mode === 'detail') {
|
if (!record) return;
|
|
setLoading(true);
|
try {
|
const data = await getEmployeeDetail({ id: record.id });
|
if (active) form.setFieldsValue(toFormValues(data, orgTree));
|
} catch {
|
// 错误已由 request 统一提示
|
if (active) onClose();
|
} finally {
|
if (active) setLoading(false);
|
}
|
return;
|
}
|
|
if (mode === 'edit' && record) {
|
form.setFieldsValue(toFormValues(record, orgTree));
|
}
|
};
|
|
void initForm();
|
|
return () => {
|
active = false;
|
};
|
}, [form, mode, onClose, open, orgTree, record]);
|
|
const handleSubmit = useCallback(async () => {
|
if (isDetail) return;
|
|
let values: EmployeeFormValues;
|
try {
|
values = await form.validateFields();
|
} catch {
|
return;
|
}
|
|
setSubmitting(true);
|
|
const { orgPath, ...rest } = values;
|
const orgId = orgPath[orgPath.length - 1];
|
|
try {
|
if (mode === 'edit' && record) {
|
await updateEmployee({ id: record.id, ...rest, orgId });
|
message.success('员工信息已更新');
|
} else {
|
await createEmployee({ ...rest, orgId });
|
message.success('员工创建成功');
|
}
|
|
onSuccess();
|
} catch {
|
// 错误已由 request 统一提示
|
} finally {
|
setSubmitting(false);
|
}
|
}, [form, isDetail, message, mode, onSuccess, record]);
|
|
const title = mode === 'create' ? '新增员工' : mode === 'edit' ? '编辑员工' : '员工详情';
|
|
return (
|
<Drawer
|
title={title}
|
width={420}
|
open={open}
|
forceRender
|
onClose={onClose}
|
footer={
|
<Space style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
<Button onClick={onClose}>{isDetail ? '关闭' : '取消'}</Button>
|
{!isDetail && (
|
<Button type="primary" loading={submitting} onClick={handleSubmit}>
|
{mode === 'edit' ? '保存' : '创建'}
|
</Button>
|
)}
|
</Space>
|
}
|
>
|
<Spin spinning={loading}>
|
<Form form={form} layout="vertical" preserve={false}>
|
<Form.Item
|
name="name"
|
label="姓名"
|
rules={[{ required: true, message: '请输入姓名' }]}
|
>
|
<Input placeholder="请输入员工姓名" disabled={isDetail} />
|
</Form.Item>
|
<Form.Item
|
name="phone"
|
label="手机号"
|
rules={[
|
{ required: true, message: '请输入手机号' },
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' },
|
]}
|
>
|
<Input placeholder="请输入手机号" maxLength={11} disabled={isDetail} />
|
</Form.Item>
|
<ProFormCascader
|
name="orgPath"
|
label="所属组织"
|
rules={[{ required: true, message: '请选择所属组织' }]}
|
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: '请输入客户持有上限' }]}
|
>
|
<InputNumber
|
min={0}
|
precision={0}
|
placeholder="请输入客户持有上限"
|
style={{ width: '100%' }}
|
disabled={isDetail}
|
/>
|
</Form.Item>
|
<Form.Item name="position" label="职位">
|
<Input placeholder="请输入职位" disabled={isDetail} />
|
</Form.Item>
|
<Form.Item name="region" label="负责区域">
|
<Input placeholder="请输入负责区域" disabled={isDetail} />
|
</Form.Item>
|
<Form.Item
|
name="email"
|
label="邮箱"
|
rules={[{ type: 'email', message: '请输入正确的邮箱' }]}
|
>
|
<Input placeholder="请输入邮箱" disabled={isDetail} />
|
</Form.Item>
|
</Form>
|
</Spin>
|
</Drawer>
|
);
|
};
|
|
export default UserFormDrawer;
|