diff --git a/frontend/src/components/FormDesigner/Designer.tsx b/frontend/src/components/FormDesigner/Designer.tsx index d9174611..5fab1882 100644 --- a/frontend/src/components/FormDesigner/Designer.tsx +++ b/frontend/src/components/FormDesigner/Designer.tsx @@ -46,8 +46,12 @@ import PropertyPanel from './components/PropertyPanel'; import FormPreview, { type FormPreviewRef } from './components/FormPreview'; import { COMPONENT_LIST } from './config'; import type { FieldConfig, FormConfig, FormSchema } from './types'; +import type { ComponentMeta } from './config'; import './styles.css'; +// 🔌 组件列表 Context(用于插件式扩展) +export const ComponentsContext = React.createContext(COMPONENT_LIST); + export interface FormDesignerProps { value?: FormSchema; // 受控模式:表单 Schema onChange?: (schema: FormSchema) => void; // 受控模式:Schema 变化回调 @@ -55,6 +59,7 @@ export interface FormDesignerProps { readonly?: boolean; // 只读模式 showToolbar?: boolean; // 是否显示工具栏 extraActions?: React.ReactNode; // 额外的操作按钮 + extraComponents?: ComponentMeta[]; // 🆕 扩展字段组件(如工作流字段) } const FormDesigner: React.FC = ({ @@ -63,7 +68,8 @@ const FormDesigner: React.FC = ({ onSave, readonly = false, showToolbar = true, - extraActions + extraActions, + extraComponents = [], }) => { // 表单预览 ref const formPreviewRef = useRef(null); @@ -149,6 +155,9 @@ const FormDesigner: React.FC = ({ // 选中的字段 const [selectedFieldId, setSelectedFieldId] = useState(); + // 待选中的字段ID(用于确保字段添加后再选中) + const [pendingSelectId, setPendingSelectId] = useState(null); + // 剪贴板(用于复制粘贴) const [clipboard, setClipboard] = useState(null); @@ -170,6 +179,11 @@ const FormDesigner: React.FC = ({ }) ); + // 🔌 合并核心组件和扩展组件 + const allComponents = useMemo(() => { + return [...COMPONENT_LIST, ...extraComponents]; + }, [extraComponents]); + // 自定义碰撞检测策略:基于边界判断,区分"插入"和"拖入" const customCollisionDetection = useCallback((args: any) => { const pointerCollisions = pointerWithin(args); @@ -280,7 +294,7 @@ const FormDesigner: React.FC = ({ // 从组件面板拖拽新组件到栅格列 if (activeData?.isNew && overData?.gridId && overData?.colIndex !== undefined) { const componentType = active.id.toString().replace('new-', ''); - const component = COMPONENT_LIST.find((c) => c.type === componentType); + const component = allComponents.find((c) => c.type === componentType); if (component) { const newField: FieldConfig = { @@ -312,7 +326,8 @@ const FormDesigner: React.FC = ({ return updateGrid(prev); }); - setSelectedFieldId(newField.id); + // 🔄 使用 pendingSelectId 确保字段添加后再选中 + setPendingSelectId(newField.id); message.success(`已添加${component.label}到栅格列`); } return; @@ -321,7 +336,7 @@ const FormDesigner: React.FC = ({ // 从组件面板拖拽新组件到画布或字段 if (activeData?.isNew && overData?.accept === 'field') { const componentType = active.id.toString().replace('new-', ''); - const component = COMPONENT_LIST.find((c) => c.type === componentType); + const component = allComponents.find((c) => c.type === componentType); if (component) { const newField: FieldConfig = { @@ -351,7 +366,8 @@ const FormDesigner: React.FC = ({ return newFields; }); - setSelectedFieldId(newField.id); + // 🔄 使用 pendingSelectId 确保字段添加后再选中 + setPendingSelectId(newField.id); message.success(`已添加${component.label}`); } } @@ -432,7 +448,8 @@ const FormDesigner: React.FC = ({ const newField = deepCopyField(clipboard); setFields(prev => [...prev, newField]); - setSelectedFieldId(newField.id); + // 🔄 使用 pendingSelectId 确保字段添加后再选中 + setPendingSelectId(newField.id); message.success('已粘贴字段'); }, [clipboard, deepCopyField, readonly, setFields]); @@ -457,7 +474,8 @@ const FormDesigner: React.FC = ({ return field; })); - setSelectedFieldId(newField.id); + // 🔄 使用 pendingSelectId 确保字段添加后再选中 + setPendingSelectId(newField.id); message.success('已粘贴字段到栅格'); }, [clipboard, deepCopyField, readonly, setFields]); @@ -652,6 +670,20 @@ const FormDesigner: React.FC = ({ const selectedField = findFieldById(fields, selectedFieldId) || null; + // 🔄 当有待选中的字段时,等待该字段被添加到 fields 后再选中 + useEffect(() => { + if (pendingSelectId) { + const field = findFieldById(fields, pendingSelectId); + if (field) { + console.log('✅ 字段已添加,现在选中:', pendingSelectId, field.type); + setSelectedFieldId(pendingSelectId); + setPendingSelectId(null); // 清除待选中状态 + } else { + console.log('⏳ 等待字段添加到 fields:', pendingSelectId); + } + } + }, [fields, pendingSelectId]); + // 获取所有字段(扁平化,包括grid中的嵌套字段) const getAllFields = useCallback((fieldList: FieldConfig[]): FieldConfig[] => { const result: FieldConfig[] = []; @@ -719,7 +751,8 @@ const FormDesigner: React.FC = ({ }, [selectedFieldId, clipboard, readonly, handleCopyField, handlePasteToCanvas, handleDeleteField]); return ( -
+ +
{showToolbar && (
表单设计
@@ -768,7 +801,7 @@ const FormDesigner: React.FC = ({
{/* 左侧组件面板 */}
- +
{/* 中间设计画布 */} @@ -809,7 +842,7 @@ const FormDesigner: React.FC = ({ boxShadow: '0 4px 12px rgba(0,0,0,0.15)', cursor: 'grabbing', }}> - {COMPONENT_LIST.find(c => `new-${c.type}` === activeId)?.label || '组件'} + {allComponents.find(c => `new-${c.type}` === activeId)?.label || '组件'}
) : null} @@ -845,7 +878,8 @@ const FormDesigner: React.FC = ({ > -
+
+
); }; diff --git a/frontend/src/components/FormDesigner/Renderer.tsx b/frontend/src/components/FormDesigner/Renderer.tsx index 072344bd..3d494250 100644 --- a/frontend/src/components/FormDesigner/Renderer.tsx +++ b/frontend/src/components/FormDesigner/Renderer.tsx @@ -3,16 +3,21 @@ * 根据 Schema 渲染可交互的表单 */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import { Form, Button, message } from 'antd'; import type { Rule } from 'antd/es/form'; +import dayjs from 'dayjs'; import type { FieldConfig, FormSchema, ValidationRule, LinkageRule } from './types'; +import type { ComponentMeta } from './config'; +import { COMPONENT_LIST } from './config'; +import { ComponentsContext } from './Designer'; import FieldRenderer from './components/FieldRenderer'; import GridFieldPreview from './components/GridFieldPreview'; import './styles.css'; export interface FormRendererProps { schema: FormSchema; // 表单 Schema + extraComponents?: ComponentMeta[]; // 🆕 扩展组件(如工作流字段) value?: Record; // 表单值(受控) onChange?: (values: Record) => void; // 值变化回调 onSubmit?: (values: Record) => void | Promise; // 提交回调 @@ -30,6 +35,7 @@ export interface FormRendererProps { const FormRenderer: React.FC = ({ schema, + extraComponents = [], value, onChange, onSubmit, @@ -45,6 +51,11 @@ const FormRenderer: React.FC = ({ }) => { const { fields, formConfig } = schema; const [form] = Form.useForm(); + + // 🔌 合并核心组件和扩展组件 + const allComponents = useMemo(() => { + return [...COMPONENT_LIST, ...extraComponents]; + }, [extraComponents]); const [formData, setFormData] = useState>(value || {}); const [fieldStates, setFieldStates] = useState = ({ const collectDefaultValues = (fieldList: FieldConfig[]) => { fieldList.forEach(field => { if (field.defaultValue !== undefined && field.name) { - defaultValues[field.name] = field.defaultValue; + // 🔧 日期字段需要转换为 dayjs 对象 + if (field.type === 'date' && typeof field.defaultValue === 'string') { + defaultValues[field.name] = dayjs(field.defaultValue); + } else { + defaultValues[field.name] = field.defaultValue; + } } if (field.type === 'grid' && field.children) { field.children.forEach(columnFields => { @@ -384,8 +400,9 @@ const FormRenderer: React.FC = ({ const wrapperColSpan = formConfig.labelAlign === 'left' || formConfig.labelAlign === 'right' ? 18 : undefined; return ( -
-
+
+ = ({ )} -
+
+ ); }; diff --git a/frontend/src/components/FormDesigner/components/ComponentPanel.tsx b/frontend/src/components/FormDesigner/components/ComponentPanel.tsx index b873f9c5..08e99c50 100644 --- a/frontend/src/components/FormDesigner/components/ComponentPanel.tsx +++ b/frontend/src/components/FormDesigner/components/ComponentPanel.tsx @@ -44,9 +44,27 @@ const DraggableComponent: React.FC<{ component: ComponentMeta }> = ({ component ); }; +interface ComponentPanelProps { + extraComponents?: ComponentMeta[]; // 额外的组件(如工作流扩展字段) +} + // 组件面板 -const ComponentPanel: React.FC = () => { - const componentsByCategory = getComponentsByCategory(); +const ComponentPanel: React.FC = ({ extraComponents = [] }) => { + const coreComponents = getComponentsByCategory(); + + // 合并核心组件和扩展组件 + const allComponents = React.useMemo(() => { + const merged = { ...coreComponents }; + + extraComponents.forEach(comp => { + if (!merged[comp.category]) { + merged[comp.category] = []; + } + merged[comp.category].push(comp); + }); + + return merged; + }, [coreComponents, extraComponents]); // 定义分类显示顺序 const categoryOrder = ['布局字段', '基础字段', '高级字段']; @@ -64,7 +82,7 @@ const ComponentPanel: React.FC = () => { bordered={false} items={categoryOrder .map((category) => { - const components = componentsByCategory[category]; + const components = allComponents[category]; if (!components || components.length === 0) return null; return { diff --git a/frontend/src/components/FormDesigner/components/FieldRenderer.tsx b/frontend/src/components/FormDesigner/components/FieldRenderer.tsx index 3a7e1a9f..665f2957 100644 --- a/frontend/src/components/FormDesigner/components/FieldRenderer.tsx +++ b/frontend/src/components/FormDesigner/components/FieldRenderer.tsx @@ -3,7 +3,8 @@ * 根据字段类型渲染对应的表单组件 */ -import React from 'react'; +import React, { useContext } from 'react'; +import { ComponentsContext } from '../Designer'; import { Form, Input, @@ -69,6 +70,20 @@ const FieldRenderer: React.FC = ({ const cascadeOptions = useCascaderOptions(field); const loadData = useCascaderLoadData(field); + // 🔌 检查是否有自定义渲染组件(插件式扩展) + const allComponents = useContext(ComponentsContext); + const componentMeta = allComponents.find(c => c.type === field.type); + + if (componentMeta?.FieldRendererComponent) { + const CustomRenderer = componentMeta.FieldRendererComponent; + console.log('✅ FieldRenderer: 使用自定义渲染组件', { + fieldType: field.type, + isPreview, + hasRenderer: !!componentMeta.FieldRendererComponent + }); + return ; + } + // 布局组件特殊处理 if (field.type === 'divider') { return {field.label}; @@ -253,6 +268,8 @@ const FieldRenderer: React.FC = ({ ); + // approver-selector 等扩展字段已通过插件机制处理,不在此硬编码 + case 'cascader': return ( (({ fields, form const collectDefaultValues = (fieldList: FieldConfig[]) => { fieldList.forEach(field => { if (field.defaultValue !== undefined && field.name) { - defaultValues[field.name] = field.defaultValue; + // 🔧 日期字段需要转换为 dayjs 对象 + if (field.type === 'date' && typeof field.defaultValue === 'string') { + defaultValues[field.name] = dayjs(field.defaultValue); + } else { + defaultValues[field.name] = field.defaultValue; + } } if (field.type === 'grid' && field.children) { field.children.forEach(columnFields => { diff --git a/frontend/src/components/FormDesigner/components/PropertyPanel.tsx b/frontend/src/components/FormDesigner/components/PropertyPanel.tsx index 6272c7cc..2b485475 100644 --- a/frontend/src/components/FormDesigner/components/PropertyPanel.tsx +++ b/frontend/src/components/FormDesigner/components/PropertyPanel.tsx @@ -3,7 +3,8 @@ * 配置选中字段的属性 */ -import React from 'react'; +import React, { useContext } from 'react'; +import { ComponentsContext } from '../Designer'; import { Form, Input, @@ -44,6 +45,9 @@ const PropertyPanel: React.FC = ({ fieldNames, }) => { const [form] = Form.useForm(); + + // 🔌 获取所有组件(包括扩展组件)- 必须在顶层调用 useContext + const allComponents = useContext(ComponentsContext); React.useEffect(() => { if (selectedField) { @@ -180,6 +184,35 @@ const PropertyPanel: React.FC = ({ ); } + // 🔌 检查是否有自定义配置组件(插件式扩展) + const componentMeta = allComponents.find(c => c.type === selectedField.type); + + console.log('🔍 PropertyPanel Debug:', { + selectedFieldType: selectedField.type, + allComponentsCount: allComponents.length, + allComponentTypes: allComponents.map(c => c.type), + componentMeta: componentMeta ? { + type: componentMeta.type, + label: componentMeta.label, + hasPropertyConfig: !!componentMeta.PropertyConfigComponent + } : null + }); + + if (componentMeta?.PropertyConfigComponent) { + const CustomConfig = componentMeta.PropertyConfigComponent; + console.log('✅ 使用自定义配置组件:', componentMeta.type); + return ( +
+ +
+ ); + } + + console.log('⚠️ 未找到自定义配置组件,使用默认配置'); + const hasOptions = ['select', 'radio', 'checkbox'].includes(selectedField.type); const isCascader = selectedField.type === 'cascader'; @@ -432,6 +465,35 @@ const PropertyPanel: React.FC = ({ )} + {/* 🎯 审批人选择器专属配置 */} + {selectedField.type === 'approver-selector' && ( + <> + + + 按用户 + 按角色 + 按部门 + + + + + + 单人审批 + 会签 + 或签 + + + +
+ + • 单人审批:仅需一人审批
+ • 会签:所有审批人都必须同意
+ • 或签:任一审批人同意即可 +
+
+ + )} + {hasOptions && (
diff --git a/frontend/src/components/FormDesigner/config.ts b/frontend/src/components/FormDesigner/config.ts index e66bafb3..d1f8bffb 100644 --- a/frontend/src/components/FormDesigner/config.ts +++ b/frontend/src/components/FormDesigner/config.ts @@ -2,6 +2,7 @@ * 表单设计器组件配置 */ +import React from 'react'; import { FormOutlined, FontSizeOutlined, @@ -23,13 +24,33 @@ import { } from '@ant-design/icons'; import type { FieldType, FieldConfig } from './types'; -// 组件元数据 +// 🔌 属性配置组件 Props +export interface PropertyConfigProps { + field: FieldConfig; + onChange: (field: FieldConfig) => void; +} + +// 🔌 字段渲染组件 Props +export interface FieldRendererProps { + field: FieldConfig; + value?: any; + onChange?: (value: any) => void; + isPreview?: boolean; +} + +// 🔌 组件元数据(支持插件式扩展) export interface ComponentMeta { type: FieldType; label: string; icon: any; category: '基础字段' | '高级字段' | '布局字段'; defaultConfig: Partial; + + // 🆕 插件式扩展:自定义属性配置组件 + PropertyConfigComponent?: React.FC; + + // 🆕 插件式扩展:自定义字段渲染组件 + FieldRendererComponent?: React.FC; } // 组件列表配置 diff --git a/frontend/src/components/FormDesigner/extensions/README.md b/frontend/src/components/FormDesigner/extensions/README.md new file mode 100644 index 00000000..0b6c2ce7 --- /dev/null +++ b/frontend/src/components/FormDesigner/extensions/README.md @@ -0,0 +1,281 @@ +# 表单设计器扩展字段 + +本目录包含表单设计器的扩展字段,采用松耦合的架构设计,不影响核心组件功能。 + +## 📁 目录结构 + +``` +extensions/ +└── workflow/ # 工作流扩展字段 + ├── index.ts # 导出文件 + ├── config.ts # 字段配置 + ├── ApproverSelector.tsx # 审批人选择器组件 + └── README.md # 使用文档(本文件) +``` + +--- + +## 🎯 工作流扩展字段 + +### 1. 审批人选择器 (`approver-selector`) + +与审批节点功能保持一致的审批人选择组件。 + +#### 功能特性 + +- ✅ **审批模式**:单人审批、会签(所有人同意)、或签(任一人同意) +- ✅ **选择模式**:按用户、按角色、按部门 +- ✅ **数据源集成**:使用项目统一的 DataSourceType (USERS/ROLES/DEPARTMENTS) +- ✅ **实时数据加载**:通过 `useFieldOptions` Hook 获取数据 +- ✅ **多选支持**:会签和或签模式自动启用多选 +- ✅ **视觉提示**:清晰的模式说明和选择提示 + +--- + +## 🚀 快速开始 + +### 基础使用 + +```tsx +import { FormDesigner } from '@/components/FormDesigner'; +import { WORKFLOW_COMPONENTS } from '@/components/FormDesigner/extensions/workflow'; + +function MyPage() { + const [schema, setSchema] = useState(); + + return ( + + ); +} +``` + +### 完整示例 + +查看 `/src/pages/FormDesigner/WorkflowExample.tsx` 获取完整的使用示例。 + +--- + +## 📊 数据结构 + +### FormSchema 中的审批人字段 + +```typescript +{ + id: 'field_xxx', + type: 'approver-selector', + label: '审批人', + name: 'approver', + required: true, + selectionMode: 'user', // 'user' | 'role' | 'department' + approvalMode: 'single', // 'single' | 'countersign' | 'or-sign' + placeholder: '请选择审批人' +} +``` + +### 提交的表单数据 + +**单人审批**: +```json +{ + "approver": "user_1" +} +``` + +**会签/或签**: +```json +{ + "approver": ["user_1", "user_2", "user_3"] +} +``` + +--- + +## 🔧 开发指南 + +### 添加新的扩展字段 + +1. **创建组件文件** (`MyExtensionField.tsx`) + +```typescript +import React from 'react'; +import type { FieldConfig } from '../../types'; + +interface MyExtensionFieldProps { + field: FieldConfig; + value?: any; + onChange?: (value: any) => void; + disabled?: boolean; + isPreview?: boolean; +} + +export const MyExtensionField: React.FC = ({ + field, + value, + onChange, + disabled, + isPreview, +}) => { + return ( +
+ {/* 你的组件实现 */} +
+ ); +}; +``` + +2. **更新配置** (`config.ts`) + +```typescript +export const WORKFLOW_COMPONENTS: ComponentMeta[] = [ + // ... existing + { + type: 'my-extension-field', + label: '我的扩展字段', + icon: MyIcon, + category: '高级字段', + defaultConfig: { + id: '', + type: 'my-extension-field', + label: '我的扩展字段', + name: 'myField', + // ... 其他配置 + }, + }, +]; +``` + +3. **更新类型定义** (`types.ts`) + +```typescript +export type FieldType = + | 'input' + // ... existing types + | 'my-extension-field'; +``` + +4. **更新 FieldRenderer** (`FieldRenderer.tsx`) + +```typescript +case 'my-extension-field': + const { MyExtensionField } = require('../extensions/workflow/MyExtensionField'); + return ( + + ); +``` + +5. **(可选)添加属性配置** (`PropertyPanel.tsx`) + +```typescript +{selectedField.type === 'my-extension-field' && ( + + + +)} +``` + +--- + +## 🎨 设计原则 + +### 1. 松耦合 +- 扩展字段独立于核心组件 +- 通过 `extraComponents` 参数注入 +- 不影响现有功能 + +### 2. 按需加载 +- 使用动态 `require()` 加载扩展组件 +- 只在使用时加载代码 +- 减少打包体积 + +### 3. 数据源统一 +- 使用项目统一的 DataSourceType +- 复用现有的数据加载逻辑 +- 保持数据格式一致 + +### 4. 类型安全 +- 完整的 TypeScript 类型定义 +- 扩展字段类型纳入 FieldType +- IDE 自动补全支持 + +--- + +## 🧪 测试 + +### 访问测试页面 + +1. 启动开发服务器 +```bash +npm run dev +``` + +2. 访问工作流表单示例 +``` +http://localhost:3000/form-designer/workflow-example +``` + +### 测试步骤 + +1. **拖拽审批人字段**到设计画布 +2. **配置字段属性**: + - 选择模式(用户/角色/部门) + - 审批模式(单人/会签/或签) +3. **预览表单**:测试审批人选择功能 +4. **查看数据**:检查提交的数据格式 + +--- + +## 📝 API 参考 + +### ApproverSelector Props + +| 属性 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| field | FieldConfig | - | 字段配置对象 | +| value | string \| string[] | - | 当前选中值 | +| onChange | (value) => void | - | 值变化回调 | +| disabled | boolean | false | 是否禁用 | +| isPreview | boolean | false | 是否为预览模式 | + +### 字段配置属性 + +| 属性 | 类型 | 可选值 | 说明 | +|------|------|--------|------| +| selectionMode | string | 'user' \| 'role' \| 'department' | 选择模式 | +| approvalMode | string | 'single' \| 'countersign' \| 'or-sign' | 审批模式 | + +--- + +## 🤝 贡献 + +欢迎贡献新的扩展字段!请遵循以下步骤: + +1. Fork 项目 +2. 在 `extensions/` 下创建你的扩展目录 +3. 实现组件和配置 +4. 添加测试和文档 +5. 提交 Pull Request + +--- + +## 📚 相关文档 + +- [表单设计器主文档](../README.md) +- [类型定义](../types.ts) +- [组件配置](../config.ts) +- [工作流示例](/src/pages/FormDesigner/WorkflowExample.tsx) + +--- + +Made with ❤️ for Deploy Ease Platform + diff --git a/frontend/src/components/FormDesigner/extensions/workflow/ApproverPropertyConfig.tsx b/frontend/src/components/FormDesigner/extensions/workflow/ApproverPropertyConfig.tsx new file mode 100644 index 00000000..a141135e --- /dev/null +++ b/frontend/src/components/FormDesigner/extensions/workflow/ApproverPropertyConfig.tsx @@ -0,0 +1,377 @@ +/** + * 审批人选择器 - 属性配置组件 + * 工作流扩展字段的配置面板 + */ + +import React, { useEffect } from 'react'; +import { + Form, + Input, + InputNumber, + Radio, + Switch, + Select, + Divider, + Typography, +} from 'antd'; +import type { PropertyConfigProps } from '../../config'; +import { useFieldOptions } from '../../hooks/useFieldOptions'; +import { DataSourceType } from '@/domain/dataSource'; + +const { Text } = Typography; +const { TextArea } = Input; + +/** + * 审批人选择器属性配置组件 + * 包含9个配置项,分为3组:审批配置、审批界面、高级设置 + */ +export const ApproverPropertyConfig: React.FC = ({ + field, + onChange, +}) => { + const [form] = Form.useForm(); + + // 🔄 按需加载数据源(只加载当前选中的审批人类型对应的数据源) + const currentApproverType = field.approverType || 'USER'; + + const { options: userOptions } = useFieldOptions({ + dataSourceType: currentApproverType === 'USER' ? 'predefined' : 'static', // 只在选中时加载 + predefinedDataSource: { sourceType: DataSourceType.USERS }, + options: [] // 未选中时使用空数组 + }); + + const { options: roleOptions } = useFieldOptions({ + dataSourceType: currentApproverType === 'ROLE' ? 'predefined' : 'static', + predefinedDataSource: { sourceType: DataSourceType.ROLES }, + options: [] + }); + + const { options: departmentOptions } = useFieldOptions({ + dataSourceType: currentApproverType === 'DEPARTMENT' ? 'predefined' : 'static', + predefinedDataSource: { sourceType: DataSourceType.DEPARTMENTS }, + options: [] + }); + + // 同步 field 到表单 + useEffect(() => { + form.setFieldsValue(field); + }, [field, form]); + + // 监听表单变化,合并到 field 并回调 + const handleValuesChange = (changedValues: any, allValues: any) => { + onChange({ + ...field, + ...allValues, + }); + }; + + return ( +
+ {/* ========== 审批配置 ========== */} +
+ 审批配置 +
+ + + + 单人审批 + 会签 + 或签 + + + +
+ + • 单人审批:仅需一人审批
+ • 会签:所有审批人都必须同意
+ • 或签:任一审批人同意即可 +
+
+ + + + + 指定用户 + + + 指定角色 + + + 指定部门 + + + 变量指定 + + + + + {/* 🔄 根据审批人类型和审批模式显示不同的选择器 */} + + prevValues.approverType !== currentValues.approverType || + prevValues.approvalMode !== currentValues.approvalMode + }> + {({ getFieldValue }) => { + const approverType = getFieldValue('approverType'); + const approvalMode = getFieldValue('approvalMode'); + const isMultiple = approvalMode === 'ALL' || approvalMode === 'ANY'; // 会签和或签允许多选 + + // 指定用户 + if (approverType === 'USER') { + return ( + + + (option?.label ?? '').toLowerCase().includes(input.toLowerCase()) + } + /> + + ); + } + + // 指定部门 + if (approverType === 'DEPARTMENT') { + return ( + + + + ); + } + + return null; + }} + + + {/* ========== 审批界面 ========== */} +
+ 审批界面 +
+ + + + + + +