feat(文书管理): 增加版本工作流与版本历史

- 模板状态按后端原始状态展示与筛选,待审核与已驳回分开展示
- 仅已发布模板可发起签署,新增提交审核/审核通过/驳回等动作
- 新增版本历史弹窗,复用统一的模板时间格式化方法
- 演示数据补齐已签署任务的签名图,并提供获取签名 dataURL 的接口
- 修复编辑器插入占位符时未定位光标导致内容落到文首的问题
This commit is contained in:
yelan
2026-09-17 16:44:54 +08:00
parent 81922d774f
commit 97c5182ba4
13 changed files with 982 additions and 64 deletions
@@ -12,7 +12,7 @@ import type { DocumentRecord } from '@/api/management/types'
import type { LocalDocument, LocalDepartment } from '@/utils/mock-document-library'
import { readDocumentLibrary, writeDocumentLibrary } from '@/utils/mock-document-library'
import type { DocumentDepartment, DocumentStatus } from './types'
import type { DocumentDepartment, DocumentBackendStatus, DocumentStatus } from './types'
/**
* 演示文库与页面模型之间的归一。
@@ -108,6 +108,119 @@ export const documentStatusLabels: Record<DocumentStatus, string> = {
archived: '已归档',
}
export const documentStatusOptions = (
Object.entries(documentStatusLabels) as [DocumentStatus, string][]
).map(([key, label]) => ({ key, label }))
/**
* 后端原始状态 → 中文标签。
*
* 卡片与预览弹窗用它,而不是归一后的 documentStatusLabels:归一过程把
* PENDING_REVIEW(待审核)和 REJECTED(已驳回)都并成了 review,
* 只看「需复审」分不清是「还等着审」还是「已经被打回」。
*/
export const backendStatusLabels: Record<DocumentBackendStatus, string> = {
DRAFT: '草稿',
PENDING_REVIEW: '待审核',
REJECTED: '已驳回',
APPROVED: '审核通过',
PUBLISHED: '已发布',
DISABLED: '已停用',
ARCHIVED: '已归档',
}
/**
* 状态页签的固定展示顺序:按文书生命周期排列(草稿 → 送审 → 驳回/通过 → 发布 → 停用 → 归档)。
* 固定顺序而不是按接口返回顺序,否则同一个库里状态一多,页签会来回跳。
*/
export const backendStatusOrder: DocumentBackendStatus[] = [
'DRAFT',
'PENDING_REVIEW',
'REJECTED',
'APPROVED',
'PUBLISHED',
'DISABLED',
'ARCHIVED',
]
/** 取状态标签:优先用后端原始状态(更精确),拿不到再退回归一后的状态 */
export function readStatusLabel(template: {
status: DocumentStatus
backendStatus?: string
}): string {
const backendLabel = template.backendStatus
? backendStatusLabels[template.backendStatus as DocumentBackendStatus]
: undefined
return backendLabel ?? documentStatusLabels[template.status]
}
/**
* 按后端状态取标签。
* 版本历史只有后端状态,没有归一后的 status,因此单独开一个入口。
* 遇到未知状态原样显示,不显示成空白——后端新增状态时至少能看见它是什么。
*/
export function readBackendStatusLabel(status: string | null | undefined): string {
if (!status) {
return '—'
}
return backendStatusLabels[status as DocumentBackendStatus] ?? status
}
/**
* 状态色调:决定状态徽标用哪一套配色。
*
* 不能直接用归一后的 DocumentStatus 当配色键——PENDING_REVIEW 与 REJECTED
* 归一后都是 review,两者会共用同一个颜色,页签与文字已经区分开了、颜色却还糊在一起。
*/
export type DocumentStatusTone = 'draft' | 'review' | 'rejected' | 'published' | 'archived'
export function readStatusTone(template: {
status: DocumentStatus
backendStatus?: string
}): DocumentStatusTone {
switch (readBackendStatus(template)) {
case 'PENDING_REVIEW':
return 'review'
case 'REJECTED':
return 'rejected'
case 'APPROVED':
case 'PUBLISHED':
return 'published'
case 'DISABLED':
case 'ARCHIVED':
return 'archived'
case 'DRAFT':
default:
return 'draft'
}
}
/**
* 取文书的后端原始状态,供状态页签归类。
*
* 真实模式的列表接口会给 backendStatus;演示库与部分历史数据可能没有,
* 这时按归一后的 status 反推一个等价状态,避免这类文书在页签里被漏掉。
*
* 反推对 'review' 是有损的:归一过程把 PENDING_REVIEW 与 REJECTED 合并了,
* 只能归到 PENDING_REVIEW。这只是缺字段时的兜底,不改变页签按真实状态过滤的口径。
*/
export function readBackendStatus(template: {
status: DocumentStatus
backendStatus?: string
}): DocumentBackendStatus {
const known = backendStatusOrder.find((status) => status === template.backendStatus)
if (known) {
return known
}
switch (template.status) {
case 'published':
return 'PUBLISHED'
case 'review':
return 'PENDING_REVIEW'
case 'archived':
return 'ARCHIVED'
case 'draft':
default:
return 'DRAFT'
}
}