export type AnnotationFieldKey =
|
| 'applicant'
|
| 'applicantDepartment'
|
| 'applicantTime'
|
| 'approver'
|
| 'approverDepartment'
|
| 'approveTime'
|
| 'stepName';
|
|
export interface WorkflowStep {
|
key: string;
|
label: string;
|
role: 'APPROVE' | 'START';
|
}
|
|
export interface FormField {
|
key: string;
|
label: string;
|
tag?: string;
|
formId?: string;
|
table?: string;
|
column?: string;
|
}
|
|
export interface AnnotationCatalog {
|
formFields: FormField[];
|
newFlowVersionId: string;
|
revisionFlowVersionId: string;
|
workflowSteps: WorkflowStep[];
|
}
|
|
export interface AnnotationField {
|
key: AnnotationFieldKey;
|
label: string;
|
}
|
|
export const START_FIELDS: readonly AnnotationField[] = [
|
{ key: 'applicant', label: '发起人' },
|
{ key: 'applicantDepartment', label: '发起人部门' },
|
{ key: 'applicantTime', label: '发起时间' },
|
{ key: 'stepName', label: '发起步骤名称' },
|
];
|
|
export const APPROVE_FIELDS: readonly AnnotationField[] = [
|
{ key: 'approver', label: '审核人' },
|
{ key: 'approverDepartment', label: '审核人部门' },
|
{ key: 'approveTime', label: '审核人时间' },
|
{ key: 'stepName', label: '审核步骤名称' },
|
];
|
|
export function annotationFields(step: WorkflowStep | undefined): readonly AnnotationField[] {
|
return step?.role === 'START' ? START_FIELDS : step?.role === 'APPROVE' ? APPROVE_FIELDS : [];
|
}
|
|
export function stepKey(step: WorkflowStep): string {
|
return step.key;
|
}
|
|
export function annotationTag(step: WorkflowStep, field: AnnotationField): string {
|
if (!step.key || !field.key) {
|
throw new Error('签批页标注步骤或字段无效');
|
}
|
return `dms.qianpiye.flow.${step.key}.${field.key}`;
|
}
|
|
export function annotationAlias(step: WorkflowStep, field: AnnotationField): string {
|
return `${step.label}-${field.label}`;
|
}
|
|
export function formAnnotationTag(field: FormField): string {
|
if (!field.key) {
|
throw new Error('签批页标注表单字段无效');
|
}
|
return field.tag || `dms.qianpiye.form.${field.key}`;
|
}
|
|
export function formAnnotationAlias(field: FormField): string {
|
return singleLineAnnotationText(displayFieldLabel(field.label));
|
}
|
|
export function displayFieldLabel(label: string): string {
|
return label.replace(/H/g, '');
|
}
|
|
/** 插入文本用空格连接多行名称;按钮仍显示原始字段名称。 */
|
function singleLineAnnotationText(text: string): string {
|
return text.replace(/\\r\\n|\\[rn]|\r\n|[\r\n]/g, ' ');
|
}
|
|
/** Word 文档内显示给用户的占位文本;实际解析始终使用内容控件 Tag。 */
|
export function annotationPlaceholder(alias: string): string {
|
return `{{${singleLineAnnotationText(alias)}}}`;
|
}
|
|
export interface RepeatGroup {
|
key: string;
|
label: string;
|
tag: string;
|
fields: FormField[];
|
}
|
|
export function repeatGroups(fields: FormField[]): RepeatGroup[] {
|
const groups = new Map<string, RepeatGroup>();
|
for (const field of fields) {
|
if (!field.formId || !field.table || !field.column) continue;
|
const key = `${field.formId}.${field.table}`;
|
if (!groups.has(key)) groups.set(key, {
|
key, label: field.label.split('-').slice(0, -1).join('-') || field.table,
|
tag: `dms.qianpiye.rows.${key}`, fields: [],
|
});
|
if (!groups.get(key)!.fields.some((item) => item.column === field.column)) groups.get(key)!.fields.push(field);
|
}
|
return [...groups.values()];
|
}
|
|
export function repeatFieldLabel(field: FormField): string {
|
return singleLineAnnotationText(displayFieldLabel(field.label.split('-').pop() || field.label))
|
.replace(/\s+/gu, ' ').trim();
|
}
|
|
export function repeatFieldPlaceholder(field: FormField): string {
|
return `{{${repeatFieldLabel(field)}}}`;
|
}
|
|
export function repeatPatternUsesField(pattern: string, field: FormField): boolean {
|
return pattern.includes(repeatFieldPlaceholder(field));
|
}
|
|
export function addRepeatPatternField(pattern: string, field: FormField): string {
|
const placeholder = repeatFieldPlaceholder(field);
|
if (pattern.includes(placeholder)) return pattern;
|
if (!pattern) return placeholder;
|
return /\s$/.test(pattern) ? `${pattern}${placeholder}` : `${pattern} ${placeholder}`;
|
}
|
|
export function removeRepeatPatternField(pattern: string, field: FormField): string {
|
const placeholder = repeatFieldPlaceholder(field);
|
let result = pattern;
|
let index = result.indexOf(placeholder);
|
while (index >= 0) {
|
const end = index + placeholder.length;
|
if (result[end] === ' ') result = result.slice(0, index) + result.slice(end + 1);
|
else if (index > 0 && result[index - 1] === ' ') result = result.slice(0, index - 1) + result.slice(end);
|
else result = result.slice(0, index) + result.slice(end);
|
index = result.indexOf(placeholder);
|
}
|
return result;
|
}
|
|
export function repeatPatternValidationError(group: RepeatGroup, pattern: string): string {
|
try {
|
repeatInsertSpec(group, pattern);
|
return '';
|
} catch (cause) {
|
return cause instanceof Error ? cause.message : '子表格式无效';
|
}
|
}
|
|
export function repeatInsertSpec(group: RepeatGroup, pattern: string) {
|
const bindings: Record<string, string> = {};
|
for (const field of group.fields) {
|
const label = repeatFieldLabel(field);
|
if (bindings[label] && bindings[label] !== field.column) throw new Error('子表列名称重复,请先修改表单列名称');
|
bindings[label] = field.column!;
|
}
|
const placeholders = [...pattern.matchAll(/\{\{([^{}]+)\}\}/g)];
|
if (!placeholders.length) throw new Error('请至少添加一个子表字段');
|
if (pattern.length > 2000 || /[\r\n]/.test(pattern)) throw new Error('每条记录的格式必须为单行且不超过 2000 字');
|
for (const match of placeholders) if (!bindings[match[1]!]) throw new Error(`未找到子表字段:${match[1]}`);
|
return { tag: group.tag, alias: JSON.stringify({ title: group.label, fields: bindings }), placeholder: pattern };
|
}
|