**资料文件全链**(用户:审核资料这一步要真产出 word/excel/md/html,前端能渲、点资料名能开)
接手时这一步一个文件都产不出来 —— S3 剧本白纸黑字写着「file_ref 本 MVP 阶段可留空」。
文件是台账那一行的投影,不另存一份:
· 声明 `file:{titleColumn,bodyColumn,formatColumn}`,它同时是授权面(没声明就点不开)
· 现造真字节 —— docx/xlsx 是自己打的 OOXML 包(zip-write:store+CRC32,零依赖)
· 读回来渲 —— 解的就是造出来那份字节,渲得出来才证明文件是真的
· 件上 `data-file-of` → 工作区二级界面(Word 纸面/Excel 网格/渲好的 md/沙箱 html)+ 下载原件
放行落地前把每一份冻进 `sys.task_asset_file`(迁移 0029):归档件点开的必须是签字那一刻
那一份,而资料落在主数据上、试跑单不落正式表 —— 不存这一份,收工后一个都点不开。
工作成果里「产出的资料」自成一摞。Markdown 富文本渲染顺带销账(仍零依赖)。
**Harness**
· 画面按「这一拍办完之后」在办的那一步挑 —— 从前拿拍首的局面决定拍尾的事:
收掉 S4、发出 S5 的卡,而屏上还摆着 S4 的只读件,人无处可填(墙要配路第四次现形)
· 验收「重做」真能退回重做,且打回后台账没动就不许再提交(软硬两段,不锁死单子)
· 立监听的授权面是清单声明;发卡的授权面是调人预算,不是计划项
· 读工具同参重调有兜底;空转写身后还有行动就不废串;finish 的 doneWhen 按写完后的草稿算
**界面**
· 翻件与翻页各归各位(从前二选一,多卡多页时页翻不动);没得翻写「—」不写 0/0
· 工作成果详情能翻份;表宽超出能横滑(E04/I01 抄版式时把 overflow-x 抄丢了)
· 查看面灰底滚过一屏变白 —— 两条独立的路都治了(沙箱内不同色 / 弹性子项不长个)
· 测试台「取不到」不再当成「还活着」
**活动**
E04 关系覆盖、I01 品牌内容各自补到验收线:判据全读基座数、判据指向的东西摆上卡面、
样例满载且名字 ≤14 字;提交前要裁的判据搬进说明书(模型会压拍,压过之后只有说明书还在)。
验收:e2e i01 4/4、e04 3/3 全部 released,资料在办中渲得出、放行后存了档点得开;
门禁 typecheck/lint/depcruise/1628 测全绿。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EEJX8Z7CHejvwmDQyPQuBX
163 lines
11 KiB
TypeScript
163 lines
11 KiB
TypeScript
/**
|
||
* E04 关系覆盖与关键人经营 · 视觉底(**E04 自己的 PRD 目标件,不是借来的家族版面**)。
|
||
*
|
||
* 上一版这里写着「E04 没有自己的 PRD 目标件」——那是假的:
|
||
* `docs/SS01-客户与市场/e04-html/` 下的四份 HTML(执行工单 1/2、验收工单 1/2)正是
|
||
* E04 自己的目标件,颜色变量、卡头渐变、判据框、原生 select/textarea 全部逐字量自那四份文件,
|
||
* 不是 A1/A2 那一支「浮起卡片 + 渐变徽章 + 头像签名」的家族版面。
|
||
*
|
||
* 两处**有意**偏离目标件,原因写在这里(别处再改记得回头看):
|
||
* 1. 目标件的必填只在表头打一个「*」;本仓有个更早、已验证过的平台级三档占位系统
|
||
* (必填红 / 建议填橘 / 选填灰,2026-08-02 定,`docs/HTML报告生成规范.md` 收录),
|
||
* 比「只在表头提一句」更能在空格子上直接指路。两者都留着:表头照抄目标件的「*」,
|
||
* 空格子再叠一层三档胶囊——不冲突,目标件的例子本来就没有空格子可比对。
|
||
* 2. 候选清单「状态」列:目标件那一列是「正式/试用」(客户主数据的状态),
|
||
* 本活动的 `candidate.status` 是另一个真实字段(`pending/covered/excluded`,
|
||
* 候选行本轮的覆盖生命周期,`declarations.ledger` 里 tally 着),两者恰好同名不同义。
|
||
* 留的是本活动这个——它是真在用的数据,目标件那份是另一件事。
|
||
*
|
||
* 版面照目标件分两层:这份文件是**底**(配色变量、卡壳、头/尾、判据框、说明框、表格基础排版),
|
||
* `report.ts` 里的 `BIND_CSS` 是**面**(哪个值决定哪种颜色/文案——三档占位、缺项红带、
|
||
* 状态徽、编辑闸开合、行增删这些「数据驱动的开关」,判断在样式表,取值在模版,两处不许混)。
|
||
*/
|
||
|
||
export const PRD_CSS = `
|
||
.proto-root {
|
||
--ink:#1f2329; --ink-2:#41464e; --ink-3:#646a73; --ink-4:#8f959e;
|
||
--line:#dee0e3; --line-soft:#e8eaed; --soft:#f8f9fb;
|
||
--brand:#3370ff; --brand-deep:#245bdb; --brand-soft:#e1eaff; --brand-wash:#f0f4ff;
|
||
--human:#a9711e; --human-soft:#f6ecd8; --human-line:#e6d3a8;
|
||
--ledger:#b23a2e; --ledger-soft:#f7e4e0; --ledger-line:#ecc5bd;
|
||
--ok:#278717; --ok-soft:#e8f8e4;
|
||
--danger:#d83931; --danger-soft:#feeae9;
|
||
--warn-accent:#a9711e; --warn-soft:#f6ecd8;
|
||
--mono: "SF Mono", Menlo, Consolas, monospace;
|
||
--t-fast: .16s;
|
||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||
color: var(--ink);
|
||
font-size: 14px;
|
||
line-height: 1.7;
|
||
}
|
||
.proto-root, .proto-root * { box-sizing: border-box; }
|
||
|
||
/* ==================== 卡壳(嵌进工作区,不自带浮起——外层已经是一张卡)==================== */
|
||
.proto-root .jc-report { background: #fff; overflow: hidden; }
|
||
|
||
.proto-root .jc-rp-head { display: flex; align-items: center; gap: 14px; padding: 18px 22px; border-bottom: 2px solid var(--ink); }
|
||
.proto-root .jc-report.tone-human .jc-rp-head { background: linear-gradient(120deg, #fffaf1 0%, #fff 55%, #fbf6ea 100%); }
|
||
.proto-root .jc-report.tone-ledger .jc-rp-head { background: linear-gradient(120deg, #fdf3f1 0%, #fff 55%, #fbf2f0 100%); }
|
||
|
||
.proto-root .jc-rp-badge {
|
||
flex: none; width: 44px; height: 44px; border-radius: 10px;
|
||
display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 13px;
|
||
}
|
||
.proto-root .jc-report.tone-human .jc-rp-badge { background: var(--human); box-shadow: 0 6px 16px rgba(169,113,30,.25); }
|
||
.proto-root .jc-report.tone-ledger .jc-rp-badge { background: var(--ledger); box-shadow: 0 6px 16px rgba(178,58,46,.25); }
|
||
|
||
.proto-root .jc-rp-tit { flex: 1; min-width: 0; }
|
||
.proto-root .jc-rp-tit em { font-style: normal; font-size: 11px; letter-spacing: .08em; color: var(--ink-4); font-weight: 700; }
|
||
.proto-root .jc-rp-tit h3 { margin: 2px 0 0; font-size: 19px; font-weight: 800; line-height: 1.3; }
|
||
.proto-root .jc-rp-sub { font-size: 12px; color: var(--ink-3); margin-top: 3px; }
|
||
|
||
.proto-root .jc-rp-side { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; flex: none; }
|
||
/* 目标件 side 第二行:步骤(静态文案),同目标件 .step 的字号字色 */
|
||
.proto-root .jc-rp-step { font-size: 12px; color: var(--ink-3); }
|
||
/* 关联任务号:全长 UUID 如实给,排版上截尾 —— 截断是排版,不是数据 */
|
||
.proto-root .jc-rp-taskid {
|
||
display: inline-block; max-width: 150px; overflow: hidden; text-overflow: ellipsis;
|
||
white-space: nowrap; vertical-align: bottom; font-family: var(--mono); font-size: 11px;
|
||
}
|
||
.proto-root .jc-rp-tag { font-size: 11px; font-weight: 800; padding: 2px 10px; border-radius: 999px; border: 1px solid; }
|
||
.proto-root .jc-report.tone-human .jc-rp-tag { color: var(--human); background: var(--human-soft); border-color: var(--human-line); }
|
||
.proto-root .jc-report.tone-ledger .jc-rp-tag { color: var(--ledger); background: var(--ledger-soft); border-color: var(--ledger-line); }
|
||
.proto-root .jc-rp-wait { font-size: 12px; color: var(--ink-3); }
|
||
.proto-root .jc-rp-wait.warn { color: var(--danger); font-weight: 700; }
|
||
|
||
.proto-root .jc-rp-bd { padding: 20px 22px; }
|
||
|
||
/* 【做什么】/【交回什么】这类分区小标——括号label + 破折号说明,同一行 */
|
||
.proto-root .jc-rp-part { font-size: 11px; font-weight: 800; letter-spacing: .12em; color: var(--ink-4); margin: 20px 0 8px; }
|
||
.proto-root .jc-rp-part:first-child { margin-top: 0; }
|
||
.proto-root .jc-rp-part b { color: var(--ink-3); }
|
||
|
||
/* AI 已完成 / 需要你 —— 说明框,左侧色条按 tone 走 */
|
||
.proto-root .jc-derive { border: 1px solid var(--line); border-left: 3px solid; border-radius: 8px; background: var(--soft); padding: 14px 16px; margin-bottom: 16px; }
|
||
.proto-root .jc-report.tone-human .jc-derive { border-left-color: var(--human); }
|
||
.proto-root .jc-report.tone-ledger .jc-derive { border-left-color: var(--ledger); }
|
||
.proto-root .jc-derive p { margin: 0; font-size: 13px; color: var(--ink-2); }
|
||
.proto-root .jc-derive p + p { margin-top: 6px; }
|
||
.proto-root .jc-derive b { color: var(--ink); }
|
||
.proto-root .jc-derive .done { color: var(--ok); font-weight: 700; }
|
||
.proto-root .jc-report.tone-human .jc-derive .need { color: var(--human); font-weight: 700; }
|
||
.proto-root .jc-report.tone-ledger .jc-derive .need { color: var(--ledger); font-weight: 700; }
|
||
|
||
/* 验收判据框——目标件里两份验收工单同一色(赭红),不随 tone 变 */
|
||
.proto-root .jc-criteria { border: 1px solid var(--ledger-line); border-radius: 10px; background: var(--ledger-soft); padding: 13px 16px; margin-bottom: 18px; }
|
||
.proto-root .jc-criteria-t { font-size: 11px; font-weight: 800; letter-spacing: .08em; color: var(--ledger); margin-bottom: 7px; }
|
||
.proto-root .jc-criteria ul { margin: 0; padding-left: 18px; font-size: 12.5px; color: var(--ink-2); }
|
||
.proto-root .jc-criteria li { margin: 4px 0; }
|
||
.proto-root .jc-criteria b { color: var(--ink); }
|
||
|
||
/* 表格基础排版(交互态的显隐开关在 BIND_CSS) */
|
||
/*
|
||
* **表宽超出就得能横着滑**(2026-08-23 用户:「表格已经显示不全了,不能左右滑动」)。
|
||
* 从前这里是 overflow: hidden —— 那是为了让圆角裁住表头,可它同时把超出的列
|
||
* **直接裁掉且滑不动**:列一多(或某列为了放下下拉框加宽)就看不全,人还没有任何办法。
|
||
* quote-* 各包一直是 overflow-x: auto,这两个新包抄版式时把这一条抄丢了。
|
||
* (注释里不写反引号 —— 整段 CSS 是模版字符串,一个反引号就把它截断,本轮踩第三次。)
|
||
* 竖向仍 hidden:件是整篇铺开跟着外层滚的,表格自己不该再套一层竖滚动条。
|
||
*/
|
||
.proto-root .jc-tbl-wrap { border: 1px solid var(--line-soft); border-radius: 10px; overflow-x: auto; overflow-y: hidden; margin-bottom: 4px; }
|
||
.proto-root .jc-tbl-in { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||
.proto-root .jc-tbl-in th { text-align: left; background: var(--soft); font-size: 11px; letter-spacing: .04em; font-weight: 800; color: var(--ink-3); padding: 9px 11px; border-bottom: 1px solid var(--line); }
|
||
.proto-root .jc-tbl-in th.req::after { content: " *"; color: var(--danger); font-size: 11px; }
|
||
.proto-root .jc-tbl-in th.sug::after { content: " 建议"; color: var(--warn-accent); font-size: 10px; font-weight: 600; }
|
||
.proto-root .jc-tbl-in th.opt::after { content: " 选填"; color: var(--ink-4); font-size: 10px; font-weight: 600; }
|
||
.proto-root .jc-tbl-in td { padding: 9px 11px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
|
||
.proto-root .jc-tbl-in tr:last-child td { border-bottom: 0; }
|
||
.proto-root .jc-tbl-in td.jc-rn { width: 38px; white-space: nowrap; font-family: var(--mono); font-size: 11px; color: var(--ink-4); text-align: center; position: relative; }
|
||
|
||
.proto-root .jc-vcell { display: block; font-size: 12.5px; color: var(--ink); line-height: 1.5; overflow-wrap: anywhere; }
|
||
.proto-root .jc-vcell.mono { font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); }
|
||
.proto-root .jc-vcell.cust { font-weight: 700; }
|
||
.proto-root .jc-vcell.dim { color: var(--ink-4); text-decoration: line-through; }
|
||
.proto-root .jc-diffnew { font-weight: 800; color: var(--ok); }
|
||
|
||
.proto-root .jc-cell, .proto-root select.jc-cell, .proto-root textarea.jc-cell {
|
||
width: 100%; min-width: 60px; border: 1px solid var(--line); border-radius: 6px;
|
||
padding: 6px 8px; font: inherit; font-size: 12.5px; color: var(--ink); background: #fff;
|
||
}
|
||
/* 多行文本照目标件:最小两行高,竖向可拉 */
|
||
.proto-root textarea.jc-cell { min-height: 40px; resize: vertical; }
|
||
.proto-root .jc-cell.mono { font-family: var(--mono); font-size: 11.5px; }
|
||
.proto-root .jc-cell:focus { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-wash); outline: none; }
|
||
|
||
/* 格内次行小字(依据 / 编码)—— 主行之下,不占独立列 */
|
||
.proto-root .jc-sub { display: block; font-size: 11px; color: var(--ink-4); margin-top: 2px; line-height: 1.55; }
|
||
.proto-root .jc-sub i { font-style: normal; }
|
||
/* 取值位(i)全空的次行整行藏掉;直填文本的次行(如对象格的编码)不归这条管 */
|
||
.proto-root .jc-sub:has(i):not(:has(i:not(:empty))) { display: none; }
|
||
|
||
.proto-root .jc-rowdel {
|
||
width: 22px; height: 22px; margin: 0 auto; cursor: pointer;
|
||
border: 1px solid var(--line); border-radius: 6px; padding: 0;
|
||
font: inherit; font-size: 14px; line-height: 1; color: var(--ink-3); background: #fff;
|
||
transition: border-color var(--t-fast), color var(--t-fast), background var(--t-fast);
|
||
}
|
||
.proto-root .jc-rowdel:hover { border-color: var(--danger); color: var(--danger); background: var(--danger-soft); }
|
||
|
||
.proto-root .jc-addrow em { font-style: normal; font-size: 11.5px; color: var(--ink-4); }
|
||
.proto-root .jc-addrow button {
|
||
cursor: pointer; border: 1px solid var(--brand-soft); border-radius: 7px; padding: 5px 12px;
|
||
font: inherit; font-size: 12px; font-weight: 700; color: var(--brand-deep); background: var(--brand-wash);
|
||
}
|
||
.proto-root .jc-addrow button:hover { background: var(--brand-soft); }
|
||
|
||
/* 本工单只承载内容,提交/审批请在对话区——目标件四份文件都有这一条 */
|
||
.proto-root .jc-flow { margin-top: 16px; padding: 9px 12px; border-radius: 8px; background: var(--soft); border: 1px dashed var(--line); font-size: 12px; color: var(--ink-3); }
|
||
.proto-root .jc-flow b { color: var(--ink); }
|
||
|
||
.proto-root .jc-rp-foot { padding: 13px 22px; border-top: 1px solid var(--line-soft); font-size: 11.5px; color: var(--ink-4); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px; }
|
||
.proto-root .jc-rp-foot span:last-child { font-family: var(--mono); }
|
||
`;
|