/** * 流程设计器 —— 枚举与节点元数据。 * * 取值全部来自《FMS工作流与审批设计》与《FMS审批流程交互重构设计》: * - 节点类型 b_node_type §4.4 / §6.4 * - 审批方式 b_approval_mode §6.4 / §9 * - 驳回策略 b_reject_mode §6.4 / §9.7 * - 审批人解析 b_actor_type §6.6 * - 条件操作符 §7.1 * - 审批行为开关 §5.2(全部落 b_config_json,不新增列) * * 数据库存稳定编码,中文文案只在本文件出现一次,页面直接引用。 * 唯一的例外是条件操作符文案:高级查询(fms-module-common/queryUtils.js)已经 * 定义了同一批操作符的文案与多语言键,这里复用而不是复制第二份翻译。 */ import { operatorLabel } from '@/components/fms-module-common/queryUtils' /** 领域草稿结构版本(《FMS审批流程交互重构设计》§6.1) */ export const SCHEMA_VERSION = 2 /** 工作流节点类型(落 wf_node.b_node_type) */ export const NODE_TYPES = { START: 'start', USER_TASK: 'user_task', CC_TASK: 'cc_task', EXCLUSIVE_GATEWAY: 'exclusive_gateway', PARALLEL_GATEWAY: 'parallel_gateway', SERVICE_TASK: 'service_task', END: 'end', } /** * 领域节点类型 ↔ LogicFlow 画布类型(《FMS工作流与审批设计》§19.5)。 * 画布类型只是前端渲染类型,落库一律用领域类型。 */ export const NODE_TYPE_TO_CANVAS = { [NODE_TYPES.START]: 'fms-start', [NODE_TYPES.USER_TASK]: 'fms-user-task', [NODE_TYPES.CC_TASK]: 'fms-cc-task', [NODE_TYPES.EXCLUSIVE_GATEWAY]: 'fms-exclusive-gateway', [NODE_TYPES.PARALLEL_GATEWAY]: 'fms-parallel-gateway', [NODE_TYPES.SERVICE_TASK]: 'fms-service-task', [NODE_TYPES.END]: 'fms-end', } /** 画布类型 → 领域节点类型(事件回调里反查用) */ export const CANVAS_TO_NODE_TYPE = Object.fromEntries( Object.entries(NODE_TYPE_TO_CANVAS).map(([nodeType, canvasType]) => [canvasType, nodeType]), ) /** * 卡片模式(钉钉式纵向卡片)允许的节点类型(§3.1)。 * 出现其它类型说明流程不再是单链路,只能留在高级模式编辑。 */ export const LINEAR_NODE_TYPES = [NODE_TYPES.START, NODE_TYPES.USER_TASK, NODE_TYPES.CC_TASK, NODE_TYPES.END] /** 会让流程超出单链路的节点类型(§3.1 单链路判定) */ export const BRANCH_NODE_TYPES = [ NODE_TYPES.EXCLUSIVE_GATEWAY, NODE_TYPES.PARALLEL_GATEWAY, NODE_TYPES.SERVICE_TASK, ] /** * 节点元数据:左侧面板顺序、显示名、形状、尺寸与配色。 * * 画布是 SVG,fill/stroke 走的是表现属性而不是 CSS,`var(--fms-*)` 在这里不生效, * 因此配色写成字面量(与 tokens.css 同色系)。深色模式下的画布配色待后续统一处理。 */ export const NODE_META = { [NODE_TYPES.START]: { label: '开始', shape: 'circle', size: { r: 26 }, style: { fill: '#ecfdf5', stroke: '#16a34a' }, /** 开始节点不可删除、不可改属性 */ fixed: true, hint: '流程入口,有且只有一个', }, [NODE_TYPES.USER_TASK]: { label: '人工审批', shape: 'rect', size: { width: 160, height: 48 }, style: { fill: '#eef2ff', stroke: '#4f46e5' }, hint: '由审批人处理的节点', }, [NODE_TYPES.CC_TASK]: { label: '抄送', shape: 'rect', size: { width: 160, height: 48 }, style: { fill: '#f0f9ff', stroke: '#0284c7' }, hint: '只通知不产生待办,自动完成', }, [NODE_TYPES.EXCLUSIVE_GATEWAY]: { label: '条件分支', shape: 'diamond', size: { rx: 34, ry: 34 }, style: { fill: '#fff7ed', stroke: '#ea580c' }, hint: '按连线条件择一分支,必须有默认出口', }, [NODE_TYPES.PARALLEL_GATEWAY]: { label: '并行分支', shape: 'diamond', size: { rx: 34, ry: 34 }, style: { fill: '#f5f3ff', stroke: '#7c3aed' }, hint: '所有出线同时激活', }, [NODE_TYPES.SERVICE_TASK]: { label: '服务节点', shape: 'rect', size: { width: 160, height: 48 }, style: { fill: '#fefce8', stroke: '#ca8a04' }, hint: '后端服务动作,第一阶段仅占位', }, [NODE_TYPES.END]: { label: '结束', shape: 'circle', size: { r: 26 }, style: { fill: '#f4f4f5', stroke: '#71717a' }, fixed: true, hint: '流程出口,可以有多个', }, } /** 左侧面板的展示顺序 */ export const NODE_PALETTE_ORDER = [ NODE_TYPES.START, NODE_TYPES.USER_TASK, NODE_TYPES.CC_TASK, NODE_TYPES.EXCLUSIVE_GATEWAY, NODE_TYPES.PARALLEL_GATEWAY, NODE_TYPES.SERVICE_TASK, NODE_TYPES.END, ] /** 卡片模式「插入节点」可选的类型(开始/结束是固定端点,不参与插入) */ export const INSERTABLE_NODE_TYPES = [NODE_TYPES.USER_TASK, NODE_TYPES.CC_TASK] /** 需要配审批人/抄送人的节点类型 */ export const ACTOR_NODE_TYPES = [NODE_TYPES.USER_TASK, NODE_TYPES.CC_TASK] /** * 审批方式 b_approval_mode(§6.4)。 * param 指向需要额外填写的参数:N 人通过要人数、按比例通过要比例。 */ export const APPROVAL_MODES = [ { value: 'single', label: '单人审批', param: null }, { value: 'any', label: '或签(任一人通过)', param: null }, { value: 'all', label: '会签(全部通过)', param: null }, { value: 'serial', label: '串行会签', param: null }, { value: 'n_of_m', label: 'N 人通过', param: 'requiredCount' }, { value: 'percentage', label: '按比例通过', param: 'requiredRatio' }, ] /** 驳回策略 b_reject_mode(§9.7) */ export const REJECT_MODES = [ { value: 'terminate', label: '驳回即结束(一票否决)' }, { value: 'return_initiator', label: '退回发起人' }, { value: 'return_previous', label: '退回上一节点' }, ] /** * 审批人解析类型 b_actor_type(§6.6)。 * control 决定属性抽屉用哪种控件取 b_actor_ref: * user/position/org_role → 选择器(提交稳定编码) * field → 字段选择器(字段必须来自当前模块 s_field) * dept → 部门来源固定选项 * resolver → 后端注册的解析器编码 * none → 无参,靠流程上下文解析 */ export const ACTOR_TYPES = [ { value: 'user', label: '指定用户', control: 'user' }, { value: 'initiator', label: '发起人', control: 'none' }, { value: 'initiator_manager', label: '发起人直属领导', control: 'none' }, { value: 'dept_manager', label: '部门负责人', control: 'dept' }, { value: 'position', label: '岗位', control: 'position' }, { value: 'org_role', label: '组织角色', control: 'org_role' }, { value: 'field_user', label: '业务字段用户', control: 'field' }, { value: 'custom', label: '自定义解析器', control: 'resolver' }, ] /** 需要填 b_actor_ref 的解析类型(由 control 派生,避免两处维护) */ export const ACTOR_TYPES_WITH_REF = ACTOR_TYPES.filter((item) => item.control !== 'none').map( (item) => item.value, ) /** 部门负责人的部门来源(§5.2「可选择发起人部门或业务部门」,编码存 b_actor_ref) */ export const DEPT_MANAGER_SOURCES = [ { value: 'initiator_dept', label: '发起人所属部门' }, { value: 'business_dept', label: '业务归属部门' }, ] /** 审批人解析为空时的兜底 b_fallback_type(§6.6) */ export const FALLBACK_TYPES = [ { value: 'none', label: '不兜底(阻塞并告警)' }, { value: 'admin', label: '转交管理员' }, { value: 'initiator', label: '转交发起人' }, { value: 'custom', label: '自定义解析器' }, ] /** 兜底策略里需要填编码的类型 */ export const FALLBACK_TYPES_WITH_REF = ['custom'] /** * 节点审批行为开关(§5.2)。 * 全部落在 wf_node.b_config_json,不为了每个开关新增一列(§7.2)。 */ export const NODE_BEHAVIOR_FLAGS = [ { key: 'allowInitiator', label: '允许申请人自审', hint: '默认关闭;关闭时发起人不会成为本节点审批人', }, { key: 'excludePreviousActors', label: '排除前序审批人', hint: '已在前面节点审批过的人不再重复收到待办', }, { key: 'allowClaim', label: '允许抢占', hint: '候选人可主动认领待办' }, { key: 'allowTransfer', label: '允许转交', hint: '审批人可把待办转交他人' }, { key: 'allowDelegate', label: '允许委托', hint: '审批人可临时委托他人处理' }, { key: 'allowAddApprover', label: '允许加签', hint: '审批人可向当前节点追加审批人' }, { key: 'commentRequired', label: '审批意见必填', hint: '提交审批动作时必须填写意见' }, ] /** 业务事件 b_event(§6.3) */ export const BINDING_EVENTS = [ { value: 'submit', label: '送审' }, { value: 'resubmit', label: '重新提交' }, { value: 'change', label: '变更' }, ] /** 版本状态 b_status(§4.2) */ export const VERSION_STATUS = [ { value: 'draft', label: '草稿', tag: 'secondary' }, { value: 'published', label: '已发布', tag: 'success' }, { value: 'retired', label: '已停用', tag: 'default' }, ] /** 草稿保存状态(§8.2 工具栏),仅前端状态 */ export const DRAFT_STATUS = { IDLE: 'idle', DIRTY: 'dirty', SAVING: 'saving', SAVED: 'saved', ERROR: 'error', } export const DRAFT_STATUS_LABEL = { idle: '未修改', dirty: '未保存', saving: '保存中', saved: '已保存', error: '保存失败', } /** 编辑器模式(§3.3)——只放组件状态,不落库、不进草稿 */ export const EDITOR_MODES = { STEP: 'step', CANVAS: 'canvas', } /** * 条件 AST 的逻辑节点(§7.1)。 * 连线条件为空表示默认出口,条件分支节点必须保留一条这样的连线。 */ export const CONDITION_LOGICS = [ { value: 'all', label: '全部满足(且)' }, { value: 'any', label: '任一满足(或)' }, ] /** * 工作流条件支持的操作符(§7.1)。 * 只列编码,文案由 queryUtils.operatorLabel 提供(高级查询与工作流共用一套翻译)。 */ export const CONDITION_OPERATORS = [ 'eq', 'ne', 'gt', 'ge', 'lt', 'le', 'in', 'not_in', 'like', 'is_null', 'is_not_null', 'between', ] /** 不需要填值的操作符 */ export const OPERATORS_WITHOUT_VALUE = ['is_null', 'is_not_null'] /** 值是多选列表的操作符 */ export const OPERATORS_WITH_LIST_VALUE = ['in', 'not_in'] /** 值是区间的操作符 */ export const OPERATORS_WITH_RANGE_VALUE = ['between'] /** 操作符显示文案:多语言键优先,缺翻译回退中文(与高级查询同一份定义) */ export function conditionOperatorLabel(operator, translate) { return operatorLabel(operator, translate) } /** 按编码取中文名,找不到回落到编码本身 */ export function labelOf(list, value) { return list.find((item) => item.value === value)?.label || value || '' }