175 lines
6.3 KiB
TypeScript
175 lines
6.3 KiB
TypeScript
/**
|
||
* 文书纸的基础排版(全项目唯一一份)。
|
||
*
|
||
* 为什么单独成文件、还要在运行期注入:同一份「文书纸」原先有**两种渲染通道**——
|
||
* ① 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)
|
||
}
|