feat(签署文书): 统一签署态文书样式并精简演示种子为单份麻精同意书
This commit is contained in:
@@ -3,14 +3,17 @@ import { computed, nextTick, ref, watch } from 'vue'
|
||||
|
||||
import {
|
||||
buildSigningDocumentHtml,
|
||||
collectSelectedItemsFromRoot,
|
||||
describeFieldProgress,
|
||||
OPTION_TICK_SELECTOR,
|
||||
readAnswersFromRoot,
|
||||
type SigningDocumentData,
|
||||
toggleOptionAt,
|
||||
type SigningFieldProgress,
|
||||
} from '@/utils/signing-document'
|
||||
|
||||
import { readStatusLabel, readStatusTone } from '../mock'
|
||||
import type { DocumentTemplate } from '../types'
|
||||
import { canInitiateSigning, readLibraryStatus, readStatusTone } from '../library'
|
||||
import { PREVIEW_PATIENT as previewPatient } from '../sample-patient'
|
||||
import type { DocumentLibraryStatus, DocumentTemplate } from '../types'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
@@ -25,22 +28,8 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
/**
|
||||
* 签署预览用的示例患者快照(脱敏演示数据)。
|
||||
*
|
||||
* 创建文书时还没有真实签署任务,用固定示例值让科室看到占位符被填充后的版式;
|
||||
* 真实签署仍以任务创建时的患者/就诊快照为准。
|
||||
* 这份数据只服务于「签署预览」这个演示页签,因此留在组件内,
|
||||
* 不再从页面 mock.ts 导出——那会让"演示数据"渗透到文书库的其它部分。
|
||||
* 签署预览用的示例患者快照见 ../sample-patient(编辑器弹窗的「签署预览」共用同一份)。
|
||||
*/
|
||||
const previewPatient: SigningDocumentData = {
|
||||
patientName: '李某某',
|
||||
sex: '男',
|
||||
age: 46,
|
||||
visitNo: 'V20260914001',
|
||||
visitDate: '2026-09-14',
|
||||
phone: '138****5678',
|
||||
signedAt: '',
|
||||
}
|
||||
|
||||
/** 预览页签:「文书内容」看原始定稿,「签署预览」看占位符填充与勾选交互化后的样子 */
|
||||
const activeTab = ref<'content' | 'signing'>('content')
|
||||
@@ -81,17 +70,22 @@ watch(
|
||||
},
|
||||
)
|
||||
|
||||
const statusLabel = computed(() => (props.template ? readStatusLabel(props.template) : ''))
|
||||
/** 状态口径统一走 library.ts:卡片、页签、预览三处必须是同一档 */
|
||||
const libraryStatus = computed<DocumentLibraryStatus>(() =>
|
||||
props.template ? readLibraryStatus(props.template) : '草稿',
|
||||
)
|
||||
|
||||
/** 徽标配色与文书卡片同口径:草稿/待审核/已驳回/已发布/已归档各不相同 */
|
||||
const statusTone = computed(() => (props.template ? readStatusTone(props.template) : 'draft'))
|
||||
const statusLabel = computed(() => libraryStatus.value)
|
||||
|
||||
/** 徽标配色与文书卡片同口径(见 library.ts 的 STATUS_TONES) */
|
||||
const statusTone = computed(() => readStatusTone(libraryStatus.value))
|
||||
|
||||
/**
|
||||
* 与文书卡片保持同一口径:只有已发布(后端 APPROVED / PUBLISHED)才能发起签署。
|
||||
* 不放行 review —— 它同时承载待审核与已驳回,两者都未定稿。
|
||||
* 与文书卡片保持同一口径:只有「启用中」的文书能发起签署。
|
||||
* 「需复审」不放行——它同时承载待审核与已驳回,两者都未定稿。
|
||||
*/
|
||||
const canInitiate = computed(
|
||||
() => Boolean(props.template?.contentHtml) && props.template?.status === 'published',
|
||||
() => Boolean(props.template?.contentHtml) && canInitiateSigning(libraryStatus.value),
|
||||
)
|
||||
|
||||
// 预置文书的 contentHtml 是完整 HTML 文档(自带版式样式);
|
||||
@@ -160,9 +154,27 @@ function syncPreviewFields() {
|
||||
|
||||
const answers = readAnswersFromRoot(body)
|
||||
signingProgress.value = describeFieldProgress(body, answers)
|
||||
signingSelection.value = Array.from(
|
||||
body.querySelectorAll<HTMLInputElement>('input.sign-doc-checkbox:checked'),
|
||||
).map((input) => input.getAttribute('data-sign-item') ?? '')
|
||||
signingSelection.value = collectSelectedItemsFromRoot(body)
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览里的 ☑ / ☐ 就地切换。
|
||||
* 选项渲染成文本(原型口径),不会触发 change 事件,必须自己接 click;
|
||||
* 切换判据在 utils/signing-document 的 toggleOptionAt 里,与签署端共用一份。
|
||||
*/
|
||||
function handlePreviewClick(event: MouseEvent) {
|
||||
const body = signingBodyRef.value
|
||||
const target = event.target
|
||||
|
||||
if (!body || !(target instanceof Element)) {
|
||||
return
|
||||
}
|
||||
|
||||
const option = target.closest<HTMLElement>(OPTION_TICK_SELECTOR)
|
||||
|
||||
if (option && toggleOptionAt(body, option)) {
|
||||
syncPreviewFields()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -224,7 +236,7 @@ function syncPreviewFields() {
|
||||
<template v-else-if="template.contentHtml && activeTab === 'signing'">
|
||||
<p class="preview-note">
|
||||
按签署时效果渲染:占位符已用示例患者「{{ previewPatient.patientName }}」填充,
|
||||
表头含「勾选」的表格已转为勾选框,可直接勾选确认交互效果(预览不保存勾选结果)。
|
||||
空白处是待补录的下划线、☑ / ☐ 可直接点击确认交互效果(预览不保存结果)。
|
||||
</p>
|
||||
<!-- eslint-disable vue/no-v-html -- 内容为本系统定稿的文书 HTML(编辑器产出、仅医务人员可改),非任意用户输入 -->
|
||||
<div
|
||||
@@ -232,6 +244,7 @@ function syncPreviewFields() {
|
||||
class="signing-preview"
|
||||
@change="syncPreviewFields"
|
||||
@input="syncPreviewFields"
|
||||
@click="handlePreviewClick"
|
||||
v-html="signingPreviewHtml"
|
||||
/>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
@@ -382,9 +395,10 @@ function syncPreviewFields() {
|
||||
background: var(--brand-l);
|
||||
}
|
||||
|
||||
/* 状态徽标配色:与文书卡片同一套色调,草稿/待审核/已驳回不再一律显示成绿色 */
|
||||
/* 状态徽标配色:与文书卡片同一套色调(见 library.ts 的 STATUS_TONES) */
|
||||
.status-badge.tone-draft,
|
||||
.status-badge.tone-archived {
|
||||
.status-badge.tone-archived,
|
||||
.status-badge.tone-stopped {
|
||||
color: #8a9aa3;
|
||||
background: #eceef0;
|
||||
}
|
||||
@@ -394,12 +408,7 @@ function syncPreviewFields() {
|
||||
background: var(--warn-l);
|
||||
}
|
||||
|
||||
.status-badge.tone-rejected {
|
||||
color: var(--err);
|
||||
background: var(--err-l);
|
||||
}
|
||||
|
||||
.status-badge.tone-published {
|
||||
.status-badge.tone-enabled {
|
||||
color: var(--ok);
|
||||
background: var(--ok-l);
|
||||
}
|
||||
@@ -503,37 +512,6 @@ function syncPreviewFields() {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* 固定页眉:患者基本信息由 HIS 自动带出,与正文之间用一条横线分隔。
|
||||
四项要在一行内放下,取值下划线的宽度由字段定义给出(内联 min-width),样式里不再写死 */
|
||||
.signing-preview :deep(.sign-doc-header) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 18px;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1.5px solid #52646e;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-doc-header__item) {
|
||||
display: inline-flex;
|
||||
gap: 3px;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-doc-header__label) {
|
||||
flex-shrink: 0;
|
||||
color: #52646e;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-doc-header__value) {
|
||||
padding: 0 5px 1px;
|
||||
color: #1c2b33;
|
||||
font-size: 12.5px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #52646e;
|
||||
}
|
||||
|
||||
.signing-preview :deep(table) {
|
||||
width: 100%;
|
||||
margin: 8px 0;
|
||||
@@ -557,123 +535,9 @@ function syncPreviewFields() {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-doc-checkbox) {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
accent-color: var(--brand);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 正文行内选项(□否 □是):用 label 包住方框与选项文字,点文字也能选中 */
|
||||
.signing-preview :deep(.sign-doc-option) {
|
||||
display: inline-flex;
|
||||
gap: 3px;
|
||||
align-items: center;
|
||||
margin-right: 14px;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-doc-signature) {
|
||||
max-width: 150px;
|
||||
max-height: 52px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* 填写项:签署预览里按签署时的控件外观渲染,样式与签署端保持一致 */
|
||||
.signing-preview :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;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field__label) {
|
||||
flex-shrink: 0;
|
||||
color: #587786;
|
||||
font-size: 11.5px;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field__required) {
|
||||
margin-left: 2px;
|
||||
color: var(--err);
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field__control) {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.signing-preview :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;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field__input:focus) {
|
||||
border-color: var(--brand);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field__choices) {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field__radio),
|
||||
.signing-preview :deep(.sign-field__checkbox) {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
accent-color: var(--brand);
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field__signature-empty) {
|
||||
padding: 12px 18px;
|
||||
color: #a96900;
|
||||
font-size: 11px;
|
||||
background: #fff9ec;
|
||||
border: 1px dashed #f0a21a;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field--signature) {
|
||||
padding: 3px 8px;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field--signature .sign-field__control) {
|
||||
min-width: 150px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field--abnormal) {
|
||||
background: #fdf0f0;
|
||||
border-color: #efb9bb;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field--abnormal .sign-field__label) {
|
||||
color: #a42e34;
|
||||
}
|
||||
|
||||
.signing-preview :deep(.sign-field__badge) {
|
||||
padding: 1px 6px;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
background: var(--err);
|
||||
border-radius: 8px;
|
||||
}
|
||||
/* 填写项胶囊、选项 ☑ / ☐、系统页眉的样式在 styles/signing-document.css(全局):
|
||||
同一份签署态文书会在文书库预览、签署工作台与编辑器三处出现,各写一份必然漂移。
|
||||
这里只保留预览容器自己的版式。 */
|
||||
|
||||
.signing-selection {
|
||||
padding: 8px 12px;
|
||||
|
||||
Reference in New Issue
Block a user