feat(签署文书): 通用文本项按出现位置独立成项并插入文本框占位符

This commit is contained in:
yelan
2026-09-18 13:30:06 +08:00
parent d2cd3f2489
commit 11fe83d807
5 changed files with 184 additions and 47 deletions
+13 -18
View File
@@ -18,11 +18,10 @@
import type { SigningAnswers, SigningFieldDef, SigningFieldKind } from '@/api/workbench/types'
import {
createFieldTokenPattern,
FIELD_TOKEN_OPEN,
PRIMARY_SIGNATURE_FIELD_ID,
resolveAnswersFromSystem,
resolveFieldByLabel,
scanFieldTokens,
SIGNING_DOCUMENT_HEADER,
SIGNING_OPTION_MARKERS,
type SigningSystemSnapshot,
@@ -260,6 +259,11 @@ function buildFieldControl(
* 占位符渲染:把 {患者姓名} 就地替换成填写项控件,保留同一文本节点内的前后文字。
* 模板未收录的占位符按自定义文本项处理,仍可在文书上填写,
* 避免出现"写了占位符但签署时既填不上也看不见"的情况。
*
* 解析统一走 scanFieldTokens,且计数器跨文本节点共用一个:
* 自定义文本项靠"同名占位符第几次出现"编号,这里数出的序号必须和
* 填写项统计(collectFieldDefs)数出来的完全一致,否则同一处内容
* 在面板里是一个字段键、落到 DOM 上是另一个,取值就悄悄对不上了。
*/
function renderTokenFields(
root: Element,
@@ -270,6 +274,7 @@ function renderTokenFields(
const doc = root.ownerDocument
const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT)
const targets: Text[] = []
const counters = new Map<string, number>()
let node = walker.nextNode()
while (node) {
@@ -282,28 +287,18 @@ function renderTokenFields(
targets.forEach((textNode) => {
const text = textNode.textContent ?? ''
const pattern = createFieldTokenPattern()
const fragment = doc.createDocumentFragment()
let cursor = 0
let match: RegExpExecArray | null
while ((match = pattern.exec(text)) !== null) {
if (match.index > cursor) {
fragment.appendChild(doc.createTextNode(text.slice(cursor, match.index)))
scanFieldTokens(text, counters).forEach(({ start, end, field }) => {
if (start > cursor) {
fragment.appendChild(doc.createTextNode(text.slice(cursor, start)))
}
fragment.appendChild(
buildFieldWidget(
doc,
resolveFieldByLabel(match[1] ?? ''),
answers,
interactive,
signatureActionable,
),
)
fragment.appendChild(buildFieldWidget(doc, field, answers, interactive, signatureActionable))
cursor = pattern.lastIndex
}
cursor = end
})
if (cursor < text.length) {
fragment.appendChild(doc.createTextNode(text.slice(cursor)))