为减少仓库噪声和重复规则,整理当前开发入口并移除已废弃资料。 - 精简根 AGENTS,补充四个应用规则与 README,统一规范入口 - 归整部署文件、SOP 原稿和参考资源,修正迁移后的脚本路径 - 取消虚拟环境、工具状态、业务导出及备份的跟踪,本机所需原件保留 - 移除停用的 graphify 配置、脚本与产物 - 删除历史业务文档、执行计划、OpenSpec 业务变更和移动 UI 原型 - 清理失效引用,历史细胞产品导入改为必须显式指定 --file 验证:git diff --cached --check、OpenSpec 严格校验、导入参数隔离检查 3/3 通过; 迁移文件完整性和现行链接已核对。未执行部署、业务数据导入或服务启停。
199 lines
6.2 KiB
TypeScript
199 lines
6.2 KiB
TypeScript
/**
|
|
* OA 打印「审批流程」行组装(无 Vue/canvas 副作用,便于单测)
|
|
*/
|
|
import { formatProgressPersonLines } from '@/utils/oa-progress-person-lines'
|
|
|
|
export type OaPrintPersonLine = {
|
|
left: string
|
|
right: string
|
|
}
|
|
|
|
export type OaPrintFlowItem = {
|
|
left: string
|
|
right: string
|
|
/** @deprecated 多人节点改用 personLines;单人/发起人可保留 */
|
|
comment?: string
|
|
/** 或签/会签按人行:左侧文案 + 右侧时间(右对齐) */
|
|
personLines?: OaPrintPersonLine[]
|
|
}
|
|
|
|
const STATUS_TEXT: Record<string, string> = {
|
|
pending: '待处理',
|
|
running: '进行中',
|
|
approved: '已同意',
|
|
rejected: '已拒绝',
|
|
completed: '已完成',
|
|
cancelled: '已撤销',
|
|
done: '已结束',
|
|
submitted: '已提交',
|
|
not_reached: '未到达',
|
|
returned: '已打回',
|
|
transferred: '已转交'
|
|
}
|
|
|
|
function statusText(s: string) {
|
|
return STATUS_TEXT[s] || s || '-'
|
|
}
|
|
|
|
/** 任务节点 cancelled=系统清待办 →「无需处理」;终态 cancelled 仍为「已撤销」 */
|
|
function progressNodeStatusText(p: any): string {
|
|
const kind = String(p?.kind || '')
|
|
const s = String(p?.status || '').trim()
|
|
if (kind !== 'terminal' && s === 'cancelled') return '无需处理'
|
|
return statusText(s)
|
|
}
|
|
|
|
function peopleDisplayNames(p: any): string[] {
|
|
const people = Array.isArray(p?.people) ? p.people : []
|
|
const fromPeople = people
|
|
.map((x: any) => String(x?.nickName || x?.id || '').trim())
|
|
.filter(Boolean)
|
|
if (fromPeople.length) return fromPeople
|
|
const name = String(p?.assigneeUserName || p?.assigneeUserId || '').trim()
|
|
return name ? [name] : []
|
|
}
|
|
|
|
function progressPersonLinesFromItem(p: any): OaPrintPersonLine[] {
|
|
const people = Array.isArray(p?.people) ? p.people : []
|
|
if (!people.length) return []
|
|
return formatProgressPersonLines(people).map((x) => ({
|
|
left: x.body || x.text,
|
|
right: x.timestamp || ''
|
|
}))
|
|
}
|
|
|
|
function isCcProgressItem(p: any): boolean {
|
|
const taskType = String(p?.taskType || p?.task_type || '').trim()
|
|
const signMode = String(p?.signMode || p?.sign_mode || '').trim()
|
|
const title = String(p?.title || p?.nodeName || '').trim()
|
|
return (
|
|
taskType === 'cc' ||
|
|
signMode === 'cc' ||
|
|
title === '抄送人' ||
|
|
/^抄送/.test(title)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 从 progress 组装审批流程行;抄送节点聚合为「抄送A, B, C」对齐钉钉导出。
|
|
* 若 progress 漏抄送,则用 tasks 兜底补一行。
|
|
*/
|
|
export function buildFlowItems(detail: any): OaPrintFlowItem[] {
|
|
const list = Array.isArray(detail?.progress) ? detail.progress : []
|
|
const items: OaPrintFlowItem[] = []
|
|
|
|
let ccBuf: { names: string[]; ts: string } | null = null
|
|
const flushCc = () => {
|
|
if (!ccBuf) return
|
|
const uniq = Array.from(new Set(ccBuf.names.map((n) => n.trim()).filter(Boolean)))
|
|
items.push({
|
|
left: uniq.length ? `抄送: ${uniq.join(', ')}` : '抄送:',
|
|
right: ccBuf.ts || ''
|
|
})
|
|
ccBuf = null
|
|
}
|
|
|
|
for (const p of list) {
|
|
const kind = String(p.kind || '')
|
|
const title = String(p.title || '-').trim()
|
|
const names = peopleDisplayNames(p)
|
|
const name = names.join(', ')
|
|
const ts = String(p.timestamp || '').trim()
|
|
const comment = String(p.commentText || '').trim()
|
|
const st = progressNodeStatusText(p)
|
|
const signMode = String(p.signMode || p.sign_mode || '')
|
|
const signLabel = signMode === 'and' ? '会签' : signMode === 'or' ? '或签' : ''
|
|
|
|
if (isCcProgressItem(p)) {
|
|
if (!ccBuf) ccBuf = { names: [], ts }
|
|
if (names.length) ccBuf.names.push(...names)
|
|
if (ts) ccBuf.ts = ts
|
|
continue
|
|
}
|
|
flushCc()
|
|
|
|
if (kind === 'submit') {
|
|
items.push({
|
|
left: name ? `【${title}】 ${name} ${st}` : `【${title}】 ${st}`,
|
|
right: ts,
|
|
comment: comment || undefined
|
|
})
|
|
continue
|
|
}
|
|
if (kind === 'terminal') {
|
|
if (String(p.status || '') === 'returned' || title === '已打回') {
|
|
continue
|
|
}
|
|
const withdrawName = String(p.assigneeUserName || p.assignee || '').trim()
|
|
const withdrawTip = comment || (String(p.status || '') === 'cancelled' ? '发起人撤销' : '')
|
|
const leftExtra =
|
|
String(p.status || '') === 'cancelled' && (withdrawName || withdrawTip)
|
|
? ` ${[withdrawName, withdrawTip].filter(Boolean).join(' ')}`
|
|
: ''
|
|
items.push({
|
|
left: `${title || st}${leftExtra}`,
|
|
right: ts
|
|
})
|
|
continue
|
|
}
|
|
if (kind === 'pending_node') {
|
|
const personLines = progressPersonLinesFromItem(p)
|
|
const head = signLabel ? `【${title}·${signLabel}】` : `【${title}】`
|
|
items.push({
|
|
left: personLines.length
|
|
? `${head} ${st}`
|
|
: name
|
|
? `${head} ${name} ${st}`
|
|
: `${head} ${st}`,
|
|
right: personLines.length ? '' : ts,
|
|
personLines: personLines.length ? personLines : undefined
|
|
})
|
|
continue
|
|
}
|
|
const head = signLabel ? `【${title}·${signLabel}】` : `【${title}】`
|
|
const personLines = progressPersonLinesFromItem(p)
|
|
if (personLines.length) {
|
|
items.push({
|
|
left: `${head} ${st}`,
|
|
right: '',
|
|
personLines
|
|
})
|
|
continue
|
|
}
|
|
items.push({
|
|
left: name ? `${head} ${name} ${st}` : `${head} ${st}`,
|
|
right: ts,
|
|
comment: comment || undefined
|
|
})
|
|
}
|
|
flushCc()
|
|
|
|
const hasCcLine = items.some((it) => it.left.startsWith('抄送'))
|
|
if (!hasCcLine) {
|
|
const tasks = Array.isArray(detail?.tasks) ? detail.tasks : []
|
|
const ccTasks = tasks.filter((t: any) => {
|
|
const tt = String(t?.taskType || t?.task_type || '').trim()
|
|
const sm = String(t?.signMode || t?.sign_mode || '').trim()
|
|
return tt === 'cc' || sm === 'cc'
|
|
})
|
|
if (ccTasks.length) {
|
|
const names = ccTasks
|
|
.map((t: any) =>
|
|
String(t?.assigneeUserName || t?.assignee_user_name || t?.assigneeUserId || '').trim()
|
|
)
|
|
.filter(Boolean)
|
|
const uniq = Array.from(new Set(names))
|
|
let ts = ''
|
|
for (const t of ccTasks) {
|
|
const tTs = String(t?.updateTime || t?.createTime || '').trim()
|
|
if (tTs && (!ts || tTs > ts)) ts = tTs
|
|
}
|
|
if (uniq.length) {
|
|
items.push({ left: `抄送: ${uniq.join(', ')}`, right: ts })
|
|
}
|
|
}
|
|
}
|
|
|
|
return items
|
|
}
|