feat(签署文书): 统一签署态文书样式并精简演示种子为单份麻精同意书

This commit is contained in:
yelan
2026-09-24 17:08:12 +08:00
parent 9a6ac93e21
commit 5ea79178cb
30 changed files with 3787 additions and 2878 deletions
@@ -4,11 +4,15 @@ import { computed, onMounted, ref, watch } from 'vue'
import type { SigningAnswers, SigningTaskRecord } from '@/api/workbench/types'
import {
applyAnswerToFieldHost,
applySelectedItemsToRoot,
applySignatureToRoot,
buildSigningDocumentHtml,
collectSelectedItemsFromRoot,
describeFieldProgress,
OPTION_TICK_SELECTOR,
readAnswersFromRoot,
SIGNATURE_ACTION_SELECTOR,
toggleOptionAt,
type SigningDocumentData,
type SigningFieldProgress,
type SigningSignatureRequest,
@@ -92,8 +96,8 @@ const signatureStateLabel = computed(() => {
})
/**
* 从渲染后的 DOM 读回填写项取值。
* 生成 HTML 时已把取值写成 attribute,这里再按取值同步一次 DOM 状态,
* 从渲染后的 DOM 读回填写项取值与勾选清单。
* 生成 HTML 时已把取值写成属性/文本,这里再按取值同步一次 DOM 状态,
* 保证「签完再打开时填写项/勾选还在」这件事不依赖序列化细节。
*/
function syncFromDom() {
@@ -106,17 +110,13 @@ function syncFromDom() {
const answers = readAnswersFromRoot(container)
fieldAnswers.value = answers
progress.value = describeFieldProgress(container, answers)
const checked = Array.from(
container.querySelectorAll<HTMLInputElement>('input.sign-doc-checkbox:checked'),
)
selectedItems.value = checked.map((item) => item.getAttribute('data-sign-item') ?? '')
selectedItems.value = collectSelectedItemsFromRoot(container)
emit('field-change', { ...answers }, { ...progress.value })
emit('selection-change', [...selectedItems.value])
}
/** 按已固化的取值还原 DOM 控件状态(存量勾选表 + 新填写项都覆盖) */
/** 按已固化的取值还原 DOM 状态(存量勾选表 + 新填写项都覆盖) */
function applyStoredValues() {
const container = containerRef.value
@@ -124,11 +124,7 @@ function applyStoredValues() {
return
}
const selected = new Set(props.task.selectedItems ?? [])
container.querySelectorAll<HTMLInputElement>('input.sign-doc-checkbox').forEach((input) => {
input.checked = selected.has(input.getAttribute('data-sign-item') ?? '')
})
applySelectedItemsToRoot(container, props.task.selectedItems ?? [])
const answers: SigningAnswers = { ...(props.task.fieldAnswers ?? {}) }
@@ -145,7 +141,7 @@ function applyStoredValues() {
}
// 读写的判据都在 signing-document 里(readAnswersFromRoot 的镜像),
// 这里不自己写 data-sign-* 选择器,否则多选组这类"同 DOM 不同语义"的控件会两处漂移
// 这里不自己写 data-sign-* 选择器,否则选项组这类"同 DOM 不同语义"的控件会两处漂移
applyAnswerToFieldHost(host, value)
})
}
@@ -189,11 +185,30 @@ function requestSignature(host: HTMLElement) {
})
}
/**
* 点击文书里的 ☑ / ☐ 选项。
*
* 与签名位分开处理:签名位是"请求采集"(要弹签字板),选项是就地切换。
* 切换逻辑本身在 utils/signing-document 的 toggleOptionAt 里,
* 组件只负责把点击翻译成一次调用 —— 单选互斥的判据不该在组件里再推一遍。
*/
function handleDocumentClick(event: MouseEvent) {
if (!props.interactive) {
return
}
const target = event.target
if (target instanceof Element) {
const option = target.closest<HTMLElement>(OPTION_TICK_SELECTOR)
const container = containerRef.value
if (option && container && toggleOptionAt(container, option)) {
syncFromDom()
return
}
}
const host = resolveSignatureHost(event.target)
if (host) {
@@ -439,189 +454,7 @@ defineExpose({ getDocumentElement })
max-width: 100%;
}
/* 固定页眉:患者基本信息由 HIS 自动带出,与正文之间用一条横线分隔。
四项要在一行内放下,取值下划线的宽度由字段定义给出(内联 min-width),样式里不再写死 */
.sign-doc-body :deep(.sign-doc-header) {
display: flex;
flex-wrap: wrap;
gap: 10px 18px;
padding-bottom: 10px;
margin-bottom: 16px;
border-bottom: 1.5px solid #52646e;
}
.sign-doc-body :deep(.sign-doc-header__item) {
display: inline-flex;
gap: 3px;
align-items: baseline;
}
.sign-doc-body :deep(.sign-doc-header__label) {
flex-shrink: 0;
color: #52646e;
font-size: 12.5px;
}
.sign-doc-body :deep(.sign-doc-header__value) {
padding: 0 5px 1px;
color: #1c2b33;
font-size: 12.5px;
text-align: center;
border-bottom: 1px solid #52646e;
}
.sign-doc-body :deep(.sign-doc-checkbox) {
width: 15px;
height: 15px;
accent-color: var(--brand);
cursor: pointer;
}
.sign-doc-body :deep(.sign-doc-checkbox:disabled) {
cursor: not-allowed;
opacity: 0.55;
}
/* 正文行内选项(□否 □是):用 label 包住方框与选项文字,点文字也能选中 */
.sign-doc-body :deep(.sign-doc-option) {
display: inline-flex;
gap: 3px;
align-items: center;
margin-right: 14px;
white-space: nowrap;
cursor: pointer;
}
.sign-doc-body :deep(.sign-doc-signature) {
max-width: 150px;
max-height: 52px;
vertical-align: middle;
}
/* ===== 填写项 ===== */
.sign-doc-body :deep(.sign-field) {
display: inline-flex;
gap: 4px;
align-items: center;
padding: 2px 7px;
margin: 0 2px;
vertical-align: middle;
background: #f7fbfd;
border: 1px solid #d4e3e8;
border-radius: 5px;
}
.sign-doc-body :deep(.sign-field__label) {
flex-shrink: 0;
color: #587786;
font-size: 11.5px;
}
.sign-doc-body :deep(.sign-field__required) {
margin-left: 2px;
color: var(--err);
}
.sign-doc-body :deep(.sign-field__control) {
display: inline-flex;
gap: 8px;
align-items: center;
}
.sign-doc-body :deep(.sign-field__input) {
width: 9em;
min-width: 0;
padding: 1px 5px;
color: #1c2b33;
font: inherit;
font-size: 12.5px;
background: #fff;
border: 1px solid #cfe0e6;
border-radius: 4px;
}
.sign-doc-body :deep(.sign-field__input:focus) {
border-color: var(--brand);
outline: none;
}
.sign-doc-body :deep(.sign-field__input:disabled) {
color: #1c2b33;
background: transparent;
border-color: transparent;
opacity: 1;
-webkit-text-fill-color: #1c2b33;
}
.sign-doc-body :deep(.sign-field__choices) {
display: inline-flex;
flex-wrap: wrap;
gap: 10px;
}
.sign-doc-body :deep(.sign-field__radio),
.sign-doc-body :deep(.sign-field__checkbox) {
width: 15px;
height: 15px;
accent-color: var(--brand);
}
.sign-doc-body :deep(.sign-field__signature-empty) {
padding: 12px 18px;
color: #a96900;
font-size: 11px;
background: #fff9ec;
border: 1px dashed #f0a21a;
border-radius: 4px;
}
/* 可点击采集的签名位:整块胶囊都是点击热区(子节点会冒泡到宿主),
hover / 聚焦给出明确反馈,否则操作人员不知道这里能点 */
.sign-doc-body :deep(.sign-field--actionable) {
cursor: pointer;
background: #fffaf0;
border-color: #f0a21a;
}
.sign-doc-body :deep(.sign-field--actionable:hover) {
background: #fff3d8;
border-color: #d98c05;
box-shadow: 0 0 0 2px rgb(240 162 26 / 20%);
}
.sign-doc-body :deep(.sign-field--actionable:focus-visible) {
outline: 2px solid var(--brand);
outline-offset: 1px;
}
.sign-doc-body :deep(.sign-field--actionable .sign-field__signature-empty) {
font-weight: 600;
}
/* 签名位比其他填写项更宽,避免签名图被压扁 */
.sign-doc-body :deep(.sign-field--signature) {
padding: 3px 8px;
}
.sign-doc-body :deep(.sign-field--signature .sign-field__control) {
min-width: 150px;
justify-content: center;
}
.sign-doc-body :deep(.sign-field--abnormal) {
background: #fdf0f0;
border-color: #efb9bb;
}
.sign-doc-body :deep(.sign-field--abnormal .sign-field__label) {
color: #a42e34;
}
.sign-doc-body :deep(.sign-field__badge) {
padding: 1px 6px;
color: #fff;
font-size: 10px;
background: var(--err);
border-radius: 8px;
}
/* 填写项胶囊、选项 ☑ / ☐、系统页眉的样式在 styles/signing-document.css(全局):
它们同时出现在文书库预览、签署工作台与编辑器里,各写一份必然漂移。
这里只保留"这份文书外壳"自己的版式(正文字号、表格边框等)。 */
</style>