Files
zkxw/web-admin/src/utils/oa-flow-preview-refresh.ts
T
2026-08-13 14:31:57 +08:00

131 lines
4.1 KiB
TypeScript

/** 与后端 oa_engine.INITIATOR_CONDITION_FIELD 对齐:不依赖 formData */
const INITIATOR_CONDITION_FIELD = '__initiator__'
const FORMULA_FIELD_RE = /\{([^{}]+)\}/g
function nodeType(node: any): string {
return String((node && (node.type || node.nodeType)) || '').trim()
}
function addField(name: unknown, fields: string[], seen: Set<string>) {
const key = String(name || '').trim()
if (!key || seen.has(key)) return
if (key === INITIATOR_CONDITION_FIELD) return
seen.add(key)
fields.push(key)
}
function addFormulaFields(text: unknown, fields: string[], seen: Set<string>) {
if (!text) return
FORMULA_FIELD_RE.lastIndex = 0
let m: RegExpExecArray | null
while ((m = FORMULA_FIELD_RE.exec(String(text)))) {
addField(m[1], fields, seen)
}
}
/**
* 收集流程树中非默认条件分支引用的表单字段(去重保序)。
* 语义对齐 backend `oa_engine._collect_condition_fields`。
*/
export function collectConditionFields(node: unknown): string[] {
const fields: string[] = []
const seen = new Set<string>()
function walk(n: any) {
if (!n || typeof n !== 'object') return
if (nodeType(n) === 'condition') {
for (const g of n.conditionGroups || []) {
if (!g || typeof g !== 'object') continue
const rules = g.rules
const when = g.when
if (g.isDefault || (!rules && !when)) {
if (g.child && typeof g.child === 'object') walk(g.child)
continue
}
if (Array.isArray(rules) && rules.length) {
for (const r of rules) {
if (r && typeof r === 'object' && r.field != null) {
addField(r.field, fields, seen)
}
}
} else if (typeof rules === 'string' && rules.trim()) {
addFormulaFields(rules, fields, seen)
} else if (when) {
addFormulaFields(when, fields, seen)
}
if (g.child && typeof g.child === 'object') walk(g.child)
}
if (n.child && typeof n.child === 'object') walk(n.child)
return
}
if (n.child && typeof n.child === 'object') walk(n.child)
for (const g of n.conditionGroups || []) {
if (g && typeof g === 'object' && g.child && typeof g.child === 'object') {
walk(g.child)
}
}
}
walk(node)
return fields
}
function sortKeysDeep(value: unknown): unknown {
if (Array.isArray(value)) return value.map(sortKeysDeep)
if (value && typeof value === 'object') {
const out: Record<string, unknown> = {}
for (const k of Object.keys(value as Record<string, unknown>).sort()) {
out[k] = sortKeysDeep((value as Record<string, unknown>)[k])
}
return out
}
return value
}
export function pickRelevantFormData(
formData: Record<string, unknown> | null | undefined,
conditionFields: string[] | null | undefined
): Record<string, unknown> {
const data = formData && typeof formData === 'object' ? formData : {}
const fields = Array.isArray(conditionFields) ? conditionFields : []
const out: Record<string, unknown> = {}
for (const f of fields) {
out[f] = data[f]
}
return out
}
export function buildFlowPreviewRequestKey(opts?: {
formData?: Record<string, unknown> | null
initiatorSelectByNode?: Record<string, string[]> | null
conditionFields?: string[] | null
}): string {
const payload = {
formData: pickRelevantFormData(opts?.formData, opts?.conditionFields),
initiatorSelectByNode:
opts?.initiatorSelectByNode && typeof opts.initiatorSelectByNode === 'object'
? opts.initiatorSelectByNode
: {}
}
return JSON.stringify(sortKeysDeep(payload))
}
export function shouldSkipFlowPreviewRequest(
prevKey: string | null | undefined,
nextKey: string,
opts: { inFlightKey?: string | null } = {}
): boolean {
const inFlightKey = opts.inFlightKey
if (inFlightKey != null) {
return inFlightKey === nextKey
}
return prevKey != null && prevKey === nextKey
}
/** 已有预览结果时静默刷新,避免整区 loading 闪烁 */
export function shouldShowFlowPreviewSpinner(opts?: {
hasCompletedPreview?: boolean
}): boolean {
return !opts?.hasCompletedPreview
}