refactor(文书预览): 抽出统一的文书纸与预览弹窗,消除五处重复渲染

预览原先在 5 个地方各写一遍(文书库预览、编辑器签署预览、发起签署弹窗预览、
工作台在线预览、工作台文书区),基础排版已经分叉:表格边框 #52646e 与 #c8d7dd
两套色、单元格内边距 5px 7px 与 5px 9px 两个值、只有两处带宋体。
原型本身是「一处渲染(.paper + fillDocHtml)、两种外框」,这次按那个形状收口。

新增
- utils/document-paper.ts:基础排版的**唯一一份** CSS(p / h1-h4 / 表格 / 宋体)。
  运行期幂等注入 head —— 因为同一份纸有两条渲染通道:v-html(吃全局样式)与
  iframe srcdoc(隔离模板自带 style,只能吃字符串),各写一份必然漂移。
- components/document/DocumentPaper.vue:唯一的纸渲染器。
  signing / raw 两模式、interactive、取值还原、☑☐ 就地切换、签名位请求采集、
  填写项与勾选上报、getDocumentElement()(供导出 PDF)。
- components/document/DocumentPreviewDialog.vue:唯一的预览外框。
  标题/副标题/徽标、可选页签、加载与错误态、纸舞台(浅色卡片 / 深灰 A4)、
  #footer 与 #empty 插槽。
- styles/document-preview.css:页脚按钮等**插槽内容**要用的类。
  插槽节点带的是父组件 scope id,弹窗的 scoped 样式命中不了 —— 与
  signing-document.css 同一类问题,同一套解法。

迁移
- 文书库预览、编辑器签署预览、发起签署弹窗预览、工作台在线预览全部改走统一组件;
  SigningDocumentInteractive 的纸换成 DocumentPaper,只留任务外壳
  (横幅 / 作废章 / 填写项进度)。
- 删除四处基础排版副本;SimSun 与 :deep(table) 现在各只剩一处。

行为保持
- 文书库预览仍是两个页签(文书内容走 iframe 隔离、签署预览走 v-html),
  没有正文时仍给占位版式(占位符写成 {患者姓名} 原始形态,不预填示例值)。
- 工作台预览的深灰 A4 舞台、页脚说明与两个出口按钮不变。
- 作废章原先是纸的兄弟节点、DOM 顺序在前,纸改成自带白底后会被盖住 ——
  给章补 z-index,并在探针里加命中测试锁死。

验证
- 新增 probe-doc-paper.cjs:31/31 PASS。核心是**计算样式**断言
  (边框色 / 内边距 / 宋体 / border-collapse),并断言文书库预览与编辑器预览
  两份纸的样式**逐项一致** —— 只看 DOM 存在证明不了"只有一份排版"。
- 回归:probe-new-task 104/104、probe-workbench-lib 46/46(新增作废章命中测试)、
  probe-dept-confirm 36/36、probe-method-domain 14/14、probe-report-analytics 92/92。
- vue-tsc 与 eslint 0 错。

净减 888 行(309 增 / 1197 删)。
This commit is contained in:
yelan
2026-09-26 20:56:02 +08:00
parent 8c70f01e2e
commit 3cb174ce0c
10 changed files with 1294 additions and 1202 deletions
@@ -0,0 +1,60 @@
/**
* 文书预览弹窗的**公共类**(全局)。
*
* 为什么这几条必须放全局而不是 scoped:预览弹窗的页脚由调用方通过 `#footer` 插槽提供
* (文档库要「用此模板发起签署」、工作台要「只读副本 / 下载 PDF 原件」),
* 而插槽内容带的是**父组件**的 scope id —— 弹窗自己的 scoped 选择器命中不了它,
* 每个调用方各写一份按钮样式又会漂移。
* 这与 styles/signing-document.css 是同一类问题,用同一套解法。
*
* 因此这里的类名是预览弹窗对外的**公开契约**:
* .preview-button / .preview-button--ghost 页脚按钮
* .preview-spacer 把按钮推到右侧
* .preview-note-footer 页脚左侧说明(窄屏隐藏)
*
* 弹窗自身的框(遮罩 / 标题栏 / 页签 / 滚动区 / 纸张舞台)仍是组件内的 scoped 样式。
*/
.preview-button {
flex-shrink: 0;
padding: 7px 14px;
color: #fff;
font-size: 12.5px;
font-weight: 700;
background: var(--brand);
border-radius: 7px;
transition: background 0.15s ease;
}
.preview-button:disabled {
cursor: not-allowed;
opacity: 0.6;
}
.preview-button--ghost {
color: var(--brand);
background: #fff;
border: 1px solid var(--brand);
}
.preview-button--ghost:hover:not(:disabled) {
background: var(--brand-l);
}
.preview-spacer {
flex: 1;
}
.preview-note-footer {
overflow: hidden;
color: var(--mut);
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
@media (max-width: 680px) {
.preview-note-footer {
display: none;
}
}
+3
View File
@@ -2,6 +2,9 @@
/* 文书渲染(签署态)的通用样式:占位符胶囊、选项、系统页眉。
文书库预览 / 签署工作台 / 编辑器三处共用一份,避免各写一套后配色漂移 */
@import './signing-document.css';
/* 文书预览弹窗的公共类(页脚按钮等):插槽内容带父组件 scope id,
弹窗的 scoped 样式命中不了,只能放全局 */
@import './document-preview.css';
:root {
font-family: var(--font-sans);