Files
xh-medical-sign-web/clinical-web/src/utils/document-paper.ts
T

175 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 文书纸的基础排版(全项目唯一一份)。
*
* 为什么单独成文件、还要在运行期注入:同一份「文书纸」原先有**两种渲染通道**——
* ① v-html 直接挂进页面(签署工作台、各类预览)→ 只能吃全局样式;
* ② iframe srcdoc(文书库的「文书内容」页签,隔离模板自带的 `<style>`)→ 只能吃字符串。
* 各写一份必然漂移:改版前实测已经有 5 份副本,表格边框在 `#52646e` 与 `#c8d7dd` 之间
* 分叉、单元格内边距 `5px 7px` 与 `5px 9px` 不一致、只有两处带宋体。
* 所以这里只维护一份字符串。
*
* **2026-09-27 起只剩 ① 一条通道**:文书库预览去掉了「文书内容」页签,
* iframe/srcdoc 那条路连同 `DocumentPaper` 的 `raw` 模式一起删除。
* 仍然保留「运行期注入」而不是普通 css 文件,是因为注入的时机可控
* (setup 期,避免首帧"纸没排版"的闪烁),且规则文本同时供导出 PDF 之类的场景复用。
*
* 选择器一律以 `.doc-paper` 开头,挂在容器 div 上。
* 例外只有一处:A4 规格的 `--doc-paper-*` 变量放在 `:root`(见下),
* 因为编辑器里的编辑面不是 `.doc-paper`(是 WangEditor 的 `[data-slate-editor]`),
* 它也要吃同一套页边距。
*
* 注意:填写项胶囊 / 选项 ☑☐ / 系统页眉的样式**不在这里**,
* 它们与「签署态」语义强相关,统一在 styles/signing-document.css。
*/
export const DOCUMENT_PAPER_CSS = `
/* ============ A4 纸规格(全项目唯一一份) ============
A4 = 210mm × 297mm。页边距:
- 左右 3.18cm(Word 中文默认的「一般文书」留白)→ 占纸宽 15.14%
- 上下 1.4cm(原型工作台 #previewBox .paper 的 56px 那一档)→ 占纸宽 6.83%
上下比左右紧是刻意的:820px 宽的纸上 2.54cm(Word 默认)会在顶部空出一大片,
屏幕上很显眼;原型用的就是这一档。
纸宽上限 820px 时对应的 px 值(124px / 56px)就是 clamp 的上界。
为什么用 clamp 而不是裸百分比:% padding 以**包含块宽度**为基准,不是自身宽。
纸在窄容器里 width:100% 时两者相等(裸百分比正好);纸一旦被 max-width 收窄,
百分比就会按更宽的包含块算 → 页边距偏大。clamp 让「纸 == 容器」和「纸被收窄」
两种情形都得到同一套页边距。
⚠️ 换纸宽时两个 --doc-paper-pad-* 要跟着改(与 --doc-paper-max-w 是一组)。
⚠️ 本段在模板字符串里,注释内**不要写反引号**,会提前结束字符串。 */
:root {
--doc-paper-max-w: 820px;
--doc-paper-pad-x: clamp(20px, 15.14%, 124px);
--doc-paper-pad-y: clamp(16px, 6.83%, 56px);
/* A4 的**最小高度**:纸宽封顶 820 时就是 1159.71px(= 820 × 297/210);
容器比 820 窄时纸也会跟着窄,那时按容器宽 × 297/210 算(cqw = 容器宽的 1%)。
两个分支在「容器 == 纸宽」处正好接上,所以 min() 挑哪个都对。
⚠️ **不要用 aspect-ratio 表达这个下限**(2026-09-27 踩过):
aspect-ratio 会把纸的**高度定死**,正文比一页高时纸不长高、内容直接溢到纸外 ——
实测深灰舞台那张纸卡在 1160px 而正文 1355px,用户看到的就是「纸张只显示一页」。
min-height 只设下限,正文多了纸一定长高(编辑器的纸一直是这么写的,没出过问题)。
⚠️ cqw 相对**最近的容器查询祖先**解析 —— 纸的父级必须带
container-type: inline-size(probe-a4-paper.cjs 会逐处断言纵向不溢出)。 */
--doc-paper-min-h: min(calc(var(--doc-paper-max-w) * 297 / 210), 141.43cqw);
}
/* 一页 A4:至少一页的高度 + 一般文书页边距。
min-height 是**下限** —— 正文超过一页时纸长高,不裁切、不内滚、不溢到纸外。
flex: none 也是必须的(2026-09-27 踩过):预览弹窗的纸舞台是 flex 列容器且高度受限,
纸作为 flex 子项默认 flex-shrink: 1 → 会被**压回 min-height**、正文又溢到纸外。
flex: none(0 0 auto)让纸既不伸也不缩,高度 = max(正文, 一页 A4)。 */
.doc-paper--a4 {
width: 100%;
max-width: var(--doc-paper-max-w);
margin: 0 auto;
flex: none;
min-height: var(--doc-paper-min-h);
padding: var(--doc-paper-pad-y) var(--doc-paper-pad-x);
}
.doc-paper {
color: #1c2b33;
font-family: 'SimSun', 'Songti SC', 'STSong', serif;
font-size: 13.5px;
line-height: 1.85;
background: #fff;
}
.doc-paper p {
margin: 0 0 6px;
}
.doc-paper h1,
.doc-paper h2 {
margin: 0 0 10px;
font-size: 18px;
text-align: center;
letter-spacing: 1px;
}
.doc-paper h3 {
margin: 14px 0 6px;
font-size: 14px;
}
.doc-paper h4 {
margin: 10px 0 4px;
font-size: 13.5px;
}
.doc-paper ol,
.doc-paper ul {
margin: 6px 0;
padding-left: 22px;
}
.doc-paper li {
margin: 0 0 4px;
}
/* 表格按内容自适应列宽并允许换行:绝对列宽在窄容器里会把整表裁掉 */
.doc-paper table {
width: 100%;
margin: 8px 0 12px;
border-collapse: collapse;
table-layout: auto;
font-size: 12.5px;
line-height: 1.6;
}
.doc-paper th,
.doc-paper td {
padding: 5px 7px;
vertical-align: middle;
border: 1px solid #52646e;
overflow-wrap: anywhere;
word-break: break-word;
}
.doc-paper th {
font-weight: 700;
background: #f2f6f8;
}
.doc-paper th p,
.doc-paper td p {
margin: 0;
}
.doc-paper img {
max-width: 100%;
}
/* 系统页眉是 flex 排的一行患者信息,纸内边距由容器给,这里只补下间距 */
.doc-paper > .sign-doc-header:first-child {
margin-top: 0;
}
`
const STYLE_TAG_ID = 'document-paper-base-style'
/**
* 把基础排版注入 `document.head`(幂等)。
*
* 用运行期注入而不是普通的全局 css 文件,是为了控制注入时机
* (setup 期而不是 onMounted,避免首帧"纸没排版"的闪烁),
* 并且让规则文本有唯一一份来源。
*/
export function ensureDocumentPaperStyle(): void {
if (typeof document === 'undefined') {
return
}
if (document.getElementById(STYLE_TAG_ID)) {
return
}
const style = document.createElement('style')
style.id = STYLE_TAG_ID
style.textContent = DOCUMENT_PAPER_CSS
document.head.appendChild(style)
}