309 lines
11 KiB
JavaScript
309 lines
11 KiB
JavaScript
/**
|
||
* 流程设计器 —— 枚举与节点元数据。
|
||
*
|
||
* 取值全部来自《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 || ''
|
||
}
|