刘光辉
8 小时以前 cf11e63b659afcc9792ab3e395f1b45d844431a8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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 };
}