Archived
资料文件真产出来了;E04/I01 补到验收线,顺手填了几个「墙拦住却不给路」的坑
**资料文件全链**(用户:审核资料这一步要真产出 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
This commit is contained in:
@@ -0,0 +1,464 @@
|
||||
/**
|
||||
* I01 品牌形象与销售工具包管理的案例模型 —— **双表示同版本**:CMMN XML(机器面)与
|
||||
* 案例说明书(叙事面)。方法论与文件结构照抄 E04(`crm-relationship-coverage`)已验证过的样子。
|
||||
*
|
||||
* 业务出处:`docs/SS01-客户与市场/i01-html/I01-活动流程与工单说明.md`(7 步业务链条,
|
||||
* 工单只针对生产流程①→⑤,复盘⑥⑦独立拎出不并入)与
|
||||
* `docs/SS01-客户与市场/Demo-E04-I01-Activity台账关联与设计.html`(L-01/L-05 台账字段表)。
|
||||
*
|
||||
* 7 个业务步收成 6 个案例模型项,非逐一对应:
|
||||
* ① 需求识别与受理 → S1_identify_requirement(AI 起草)+ S2_requirement_confirm(人核对);
|
||||
* ②③ 市场依据采集 + 内容创作 → S3_gather_and_draft(一项,AI,纯加工态);
|
||||
* ④ 内部审核与调性把关 → S4_draft_review(决策卡循环,通过/驳回+AI重做);
|
||||
* ⑤ 发布入库 → S5_publish_prep(AI 拟发布)+ T_release(终局验收放行)。
|
||||
* ⑥⑦ 分发追踪与复盘迭代**不在这份案例模型里**(业务说明明文:工单只针对生产流程,
|
||||
* 复盘是独立触发的另一条时间轴,不并入这一次生产任务)。
|
||||
*
|
||||
* 与 E04 的一处刻意不同:I01 没有「AI 做不到、必须人去线下执行」的环节
|
||||
* (内容初稿由 AI 草拟,人只审不做)——所以只有 1 张执行工单(S2),
|
||||
* 少 E04 那张「执行经营动作」;验收工单反倒多一张(S4 审初稿 + T_release 发布确认),
|
||||
* 与 E04(S4 审计划 + T_release 台账更新)结构同型。
|
||||
*/
|
||||
|
||||
export const CMMN_XML = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL">
|
||||
<case id="brand-content-toolkit">
|
||||
<!--
|
||||
案卷项的 id 就是任务视图里的键名。入参顶层是 trigger/reason/requester/targetCustomer/note
|
||||
五个字段(不进这份 caseFileModel——没有谓词引用它们,S1 的步骤 reads 里单独声明);
|
||||
content_brand_* 与 content_asset_* 那几个是基座读数(行数 / 缺必填 / 按值计数,
|
||||
由基座现算,模型碰不着)。
|
||||
-->
|
||||
<caseFileModel>
|
||||
<caseFileItem id="content_brand_requirement_rows" name="内容需求行数(基座现数)"/>
|
||||
<caseFileItem id="requirement_confirmed_total" name="内容需求已核对确认(人环走完的凭据)"/>
|
||||
<caseFileItem id="content_asset_rows" name="内容资产行数(基座现数)"/>
|
||||
<caseFileItem id="content_asset_missing" name="内容资产缺必填的行数(基座现数)"/>
|
||||
<caseFileItem id="draft_approved_total" name="内容初稿已审核通过(仅通过时置一)"/>
|
||||
<caseFileItem id="content_asset_content_status_published" name="已置为已发布状态的行数(基座现数)"/>
|
||||
<caseFileItem id="publish_prepared_total" name="发布准备已收口(S5 写完时置一)"/>
|
||||
</caseFileModel>
|
||||
<casePlanModel id="plan" name="品牌形象与销售工具包管理" autoComplete="true">
|
||||
<!-- ① 内容需求已起草 —— 人核对那一步的入场前提 -->
|
||||
<sentry id="s_requirement_drafted">
|
||||
<onPart id="op_rd" caseFileItemRef="content_brand_requirement_rows"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_rd"><condition>content_brand_requirement_rows > 0</condition></ifPart>
|
||||
</sentry>
|
||||
<!-- ① 内容需求已核对 —— 采集与创作那一步的入场前提 -->
|
||||
<sentry id="s_requirement_confirmed">
|
||||
<onPart id="op_rc" caseFileItemRef="requirement_confirmed_total"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_rc"><condition>requirement_confirmed_total > 0</condition></ifPart>
|
||||
</sentry>
|
||||
<!-- ②③ 内容初稿已草拟且齐 —— 审核那一步的入场前提 -->
|
||||
<sentry id="s_draft_ready">
|
||||
<onPart id="op_dr" caseFileItemRef="content_asset_rows"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_dr"><condition>content_asset_rows > 0 and content_asset_missing = 0</condition></ifPart>
|
||||
</sentry>
|
||||
<!-- ④ 内容初稿已审核通过 —— 拟发布那一步的入场前提 -->
|
||||
<sentry id="s_draft_approved">
|
||||
<onPart id="op_da" caseFileItemRef="draft_approved_total"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_da"><condition>draft_approved_total > 0</condition></ifPart>
|
||||
</sentry>
|
||||
<!--
|
||||
⑤ 发布已准备好 —— 放行门的入场前提。
|
||||
认「办完了没有」:发布状态已置且不缺必填、且 S5 自己显式记了账
|
||||
(publish_prepared_total 是写完之后才产生的数,不是「写过一笔就算数」)。
|
||||
-->
|
||||
<sentry id="s_publish_ready">
|
||||
<onPart id="op_pr" caseFileItemRef="publish_prepared_total"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_pr"><condition>publish_prepared_total > 0 and content_asset_content_status_published > 0 and content_asset_missing = 0</condition></ifPart>
|
||||
</sentry>
|
||||
|
||||
<!-- ① 识别内容需求,铸造 round_ref(能算的不问人) -->
|
||||
<humanTask id="S1_identify_requirement" name="识别本轮内容需求"/>
|
||||
|
||||
<!-- ① 交人核对需求清单(增删改)—— 触达世界前的人环,不给 doneWhen -->
|
||||
<humanTask id="S2_requirement_confirm" name="需求受理:核对需求清单,增删改">
|
||||
<entryCriterion id="ec_s2" sentryRef="s_requirement_drafted"/>
|
||||
<itemControl><manualActivationRule id="m_s2"/></itemControl>
|
||||
</humanTask>
|
||||
|
||||
<!-- ②③ 采集市场依据 → 草拟多语言内容初稿(一项,不触达世界) -->
|
||||
<humanTask id="S3_gather_and_draft" name="采集市场依据、按 BT 层级草拟内容初稿">
|
||||
<entryCriterion id="ec_s3" sentryRef="s_requirement_confirmed"/>
|
||||
</humanTask>
|
||||
|
||||
<!-- ④ 内容初稿审核 —— 决策卡循环,通过或驳回+AI重做,不给 doneWhen -->
|
||||
<humanTask id="S4_draft_review" name="内容初稿审核:通过或驳回(驳回则AI修订重提交)">
|
||||
<entryCriterion id="ec_s4" sentryRef="s_draft_ready"/>
|
||||
<itemControl><manualActivationRule id="m_s4"/></itemControl>
|
||||
</humanTask>
|
||||
|
||||
<!-- ⑤(前半)拟发布:版本号递增、状态流转、登记场景与 BT 标签(不触达世界,给 doneWhen) -->
|
||||
<humanTask id="S5_publish_prep" name="拟发布:版本递增、状态流转、登记场景与标签">
|
||||
<entryCriterion id="ec_s5" sentryRef="s_draft_approved"/>
|
||||
</humanTask>
|
||||
|
||||
<!-- ⑤(后半)发布确认:终局唯放行,必须人工激活 -->
|
||||
<humanTask id="T_release" name="发布确认放行">
|
||||
<entryCriterion id="ec_rel" sentryRef="s_publish_ready"/>
|
||||
<itemControl><manualActivationRule id="m_rl"/></itemControl>
|
||||
</humanTask>
|
||||
</casePlanModel>
|
||||
</case>
|
||||
</definitions>`;
|
||||
|
||||
/**
|
||||
* 案例说明书 —— **只写整单一以贯之的事**(逐步做法归剧本,两处不重复写同一件事)。
|
||||
*/
|
||||
export const NARRATIVE = `§0 开案:**这一轮要生产哪份对外内容资产,产完把它审核发布**。
|
||||
入参五个顶层字段:
|
||||
· \`trigger\`——**必有**,\`"proactive"\`(主动触发)或 \`"reactive"\`(被动触发·使用方请求),
|
||||
翻成人读标签写进 \`content_brand.verdict.trigger\`(报告上不摆系统内英文码)。
|
||||
· \`reason\`——仅 \`trigger:"proactive"\` 时可能有,四种之一:\`"strategy_shift"\`(战略调整)/
|
||||
\`"new_market"\`(进入新市场)/ \`"tool_outdated"\`(工具过期)/ \`"monthly_update"\`
|
||||
(月度定期内容更新)。
|
||||
· \`requester\`——仅 \`trigger:"reactive"\` 时可能有,四种之一:\`"E04"\`(关系覆盖需要价值
|
||||
传递素材)/ \`"T03"\`(触达需要材料)/ \`"T04"\`(攻坚需要定制内容)/ \`"E03"\`(方案需要支撑)——
|
||||
T03/E03 在本仓库尚未实现,只是描述来源,不必真去调它们。
|
||||
\`reason\`/\`requester\` 二选一有值,翻成人读标签一起写进
|
||||
\`content_brand.verdict.trigger_detail\`(对照表见下方 S1 的 guide)。
|
||||
· \`targetCustomer\`——选填,定向客户的编码(如为特定客户定制素材);通用内容留空。
|
||||
· \`note\`——本轮背景说明(选填),帮你判断内容侧重与紧迫程度。
|
||||
|
||||
**成品是三样东西,缺一不可**:①一份**核对过的内容需求清单**;②一份**经审核通过的
|
||||
内容初稿**(多语言,按 BT 层级定制结构);③**写入 L-05 品牌内容库的正式发布记录**
|
||||
(版本号、状态流转、场景与 BT 标签登记齐全)。
|
||||
|
||||
**AI 承担信息汇总、记录与初稿生成,人负责判断与决策**:识别需求、采集市场依据、
|
||||
草拟初稿、拟发布,都是你的活;核对需求清单、审核初稿、签字发布,三道都要人过。
|
||||
|
||||
**没有物理执行环节**:与 E04 不同,I01 没有「AI 做不到、必须人去线下办」的步骤——
|
||||
内容初稿由你草拟,人只审核通过或驳回,不亲自动笔、不用到场。
|
||||
|
||||
**五步走**:识别需求 → 人核对 → 采集依据+草拟初稿(一步) → 人审核(可能来回改一轮)→
|
||||
拟发布 → 人签字放行。
|
||||
|
||||
【这一单唯一的岔路口 —— 内容初稿审核,长这样,无论哪一拍都照这个发,别自己另编一张】
|
||||
审核不是一次性的:人可能驳回、要求你按他的意见改。**这一步只在 S4_draft_review
|
||||
这一项里发生**,发决策卡之前先在轨迹里找有没有已经答过、还没处理的 \`[card-reply]\`:
|
||||
· **\`value\` 是 \`"approve"\`**——写 \`draft_approved_total=1\`,把这一轮草拟的每一行
|
||||
\`content.asset\` 的 \`content_status\` 改成 \`reviewed\`(同 PK 覆盖写),finish 这一项,
|
||||
别再发卡;
|
||||
· **\`value\` 是 \`"reject"\` 或以 \`"custom:"\` 开头**——都算驳回:\`"reject"\` 是点了固定
|
||||
按钮、没写具体意见,**不许再发卡追问理由**(那两个按钮点出来的回复永远没有 note,
|
||||
再问一遍是把人指向一扇打不开的门);以 \`"custom:"\` 开头的,\`note\` 就是他写的具体
|
||||
意见,按这条意见改。两种都要对 \`content.asset\` 做出至少一处实质性修订(同 PK
|
||||
覆盖写,不开新计划项、不重新进入 sentry),然后**换一种问法**再发一张 decide 卡
|
||||
(措辞不换会被系统当成同一张卡静默吞掉);
|
||||
· **没有**——才发第一张 decide 卡。
|
||||
{"kind":"decide","itemId":"S4_draft_review","allowCustom":true,
|
||||
"ask":"内容初稿已草拟,共 <N> 份,请审核。",
|
||||
"options":[{"value":"approve","label":"通过","recommended":true},
|
||||
{"value":"reject","label":"驳回(不写具体意见)"}]}
|
||||
三个 \`value\` 只有这三种形态:\`"approve"\`、\`"reject"\`、\`"custom:"\` 开头(人自己写的话)——
|
||||
**别自己造第四种**。
|
||||
|
||||
【提交放行之前,先把三条裁量判据裁掉 —— 同一串 judge×3 + submit】
|
||||
这三条机器判不了,**没裁过就提交必被拒**(拒回会点名是哪三条):
|
||||
· "版本号与状态是否正确:版本号已更新、状态流转无误"
|
||||
· "场景与 BT 标签是否登记完整"
|
||||
· "多语言版本是否齐全"
|
||||
criterion 照上面整句原样抄;裁成 satisfied=false 照样提交得了 —— 理由连着卡交给签字的人。
|
||||
**这条写在这儿而不是只写在 T_release 的剧本里**:拟发布与提交常被压成同一拍,
|
||||
那一拍摊开的是拟发布那一步的做法,T_release 的剧本这时看不见 —— 说明书每拍都在。
|
||||
|
||||
【一路上要说话】负责人正看着这一单。**每一拍带一句 say** —— 说业务的话:
|
||||
识别到几条需求、初稿写了几份、发布准备好了什么;不说系统词。
|
||||
到了要找人的那一拍,先说清这一轮办完了什么、为什么现在需要他。
|
||||
|
||||
【怎么把一项干完 · 一拍连做】write 和 tool 都不会自动收步 —— 做完用 finish 宣告。
|
||||
动作排进同一拍的 actions 串一口气给;只有发卡 / 提交(挂起)必须停拍。
|
||||
**这一单常路 7-9 拍就该收口**(比常路多两道人环:需求核对 + 初稿审核 + 放行,共三次;
|
||||
审核若被驳回会多出一到两拍)。
|
||||
|
||||
§Z 结案:AM Lead 在验收单上签字,系统把草稿合并进 L-05 正式版 ——
|
||||
这一轮生产的内容资产正式上线,可供 E04/T03/T04/E03 调用(分发追踪与复盘迭代是
|
||||
独立触发的另一条时间轴,不在这一单里)。`;
|
||||
|
||||
/**
|
||||
* 步骤剧本 —— 一个计划项一条;`doneWhen` 是「成果已存在」的判据,不是「该不该做」。
|
||||
*
|
||||
* 三处不给 `doneWhen`:`S2_requirement_confirm`(交人核对触达世界)、`S4_draft_review`
|
||||
* (要人拍板,且这一项内部可能循环)、`T_release`(签下去触达世界)——
|
||||
* 给了等于给引擎一把替人签字的钥匙。
|
||||
*/
|
||||
export const CONTENT_STEPS = [
|
||||
{
|
||||
item: 'S1_identify_requirement',
|
||||
intent: '识别本轮内容需求来源与类型,铸造本轮案号',
|
||||
writes: ['content_brand.requirement', 'content_brand.verdict'],
|
||||
/* 入参顶层字段不声明就会被整表段截断刀砍掉 —— 被动触发那几条路全靠这五个字段 */
|
||||
reads: ['trigger', 'reason', 'requester', 'targetCustomer', 'note', 'content.asset'],
|
||||
view: 'requirement-list',
|
||||
tools: [],
|
||||
doneWhen: 'content_brand_requirement_rows > 0',
|
||||
guide: `第一件事:把这一轮**内容需求**列出来。
|
||||
|
||||
【先铸造本轮案号 round_ref】格式固定:\`'CTB-' + case:openedAt 按 YYYYMMDDHHmmss 格式化\`
|
||||
(引擎注入的开案时刻,你不必也不许自己编)。**整单只有一个 round_ref**,写
|
||||
\`content_brand.requirement\` / \`.verdict\` 时都用这同一个串 —— 造出两个号就是把一单裂成两单。
|
||||
|
||||
【识别需求】读 \`content.asset\`(已有资产,判断是否已有类似内容、避免重复):
|
||||
· \`trigger:"proactive"\`——按 \`reason\` 映射:\`strategy_shift\`(战略调整)/
|
||||
\`new_market\`(进入新市场)通常对应"公司介绍"/"能力声明"这类通用内容;
|
||||
\`tool_outdated\`(工具过期)对应更新既有工具包;\`monthly_update\`(月度定期更新)
|
||||
对应例行内容刷新,可参考已有资产里哪些版本号最旧、最久未更新。
|
||||
· \`trigger:"reactive"\`——按 \`requester\` 映射到内容类型:\`E04\` 请求价值传递素材
|
||||
→系统方案白皮书/案例集;\`T03\` 触达需要材料→销售工具包/选型指南;\`T04\` 攻坚需要
|
||||
定制内容→系统方案白皮书(定向 \`targetCustomer\`);\`E03\` 方案需要支撑→技术规格书/
|
||||
替代对照表。\`targetCustomer\` 有值就把这条需求的 \`target_customer\` 填成它。
|
||||
|
||||
一轮可以识别出不止一条需求(如月度更新可能同时刷新两三份内容)。
|
||||
|
||||
【写 \`content_brand.requirement\`,一条需求一行】(rows 收数组)
|
||||
round_ref、req_seq(**必填**,本轮内从 1 递增)、content_type(**必填**,如
|
||||
"系统方案白皮书"/"公司介绍"/"能力声明"/"销售工具包"/"案例集"/"品牌调性标准"/
|
||||
"方案模板")、source(选填,一句需求来源,如"使用方请求(E04 价值传递)"或
|
||||
"战略调整(进入俄罗斯市场)")、target_customer(选填,定向客户;通用内容写"—(通用)")、
|
||||
scene(选填,**写人读标签**"首触"/"方案"/"攻坚"/"展会"/"报价"之一,按用途判断——这一列
|
||||
在编辑面上是普通格子、不是徽标,不带翻译层,写 ASCII 码人看到的就是 ASCII 码)、
|
||||
priority(选填,ASCII \`high\`/\`medium\`/\`low\`,按紧迫程度判断)、origin(固定写 \`ai\`)。
|
||||
|
||||
【同一拍写 \`content_brand.verdict\`(一行)】round_ref、trigger(**翻成人读标签写,
|
||||
不写入参那个英文码**——\`"proactive"\`→"主动触发"、\`"reactive"\`→"被动触发")、
|
||||
trigger_detail(把 \`reason\` 或 \`requester\` 也翻成人读标签写进来——
|
||||
\`strategy_shift\`→"战略调整"、\`new_market\`→"进入新市场"、\`tool_outdated\`→"工具过期"、
|
||||
\`monthly_update\`→"月度定期内容更新"、\`E04\`→"E04 关系覆盖需要价值传递素材"、
|
||||
\`T03\`→"T03 触达需要材料"、\`T04\`→"T04 攻坚需要定制内容"、\`E03\`→"E03 方案需要支撑")、
|
||||
target_customer(入参原样抄,没有留空)、note(入参原样抄,没有留空)、summary 一句人话
|
||||
(本轮识别到几条需求、依据是什么)。写完 finish 这一项。`,
|
||||
},
|
||||
{
|
||||
item: 'S2_requirement_confirm',
|
||||
intent: '交人核对需求清单:增删改,确认本轮真正要生产什么',
|
||||
writes: ['content_brand.verdict'],
|
||||
reads: ['content_brand.requirement'],
|
||||
view: 'requirement-list',
|
||||
tools: [],
|
||||
/* 不给 doneWhen —— 交人核对触达世界(manualActivation),引擎不替人收 */
|
||||
guide: `需求清单起草好了,**该内容运营核对了** —— 这是本活动第一张执行单,零输入。
|
||||
工作区摆的是需求清单的报告(编辑闸开着就能增删改),他在那份件上核对:AI 规则之外的
|
||||
目标客户、适用场景、优先级、定制需求(如使用方要一份方案,他线下知道要针对某客户
|
||||
做定向版)。
|
||||
|
||||
这一步标了人工激活:发卡就算启动,不必单发 start;人交回后你自己 finish。
|
||||
|
||||
【照这个骨架发,别少字段】(卡面是纯文本,steps/standard 里别写星号记号,别绑 \`blocks\`
|
||||
或 \`fields\` —— 整份清单在工作区那份件上,执行卡零输入)
|
||||
{"kind":"human","itemId":"S2_requirement_confirm","workOrder":"execution","card":{
|
||||
"title":"需求受理:本轮共 <N> 条内容需求",
|
||||
"ask":"AI 已按规则识别出 <N> 条内容需求,请核对确认。",
|
||||
"steps":["工作区的件上核对需求清单:内容类型是必填,缺一条这一单走不到内容创作那一步",
|
||||
"加一行 = 补充新需求(AI 规则之外的目标客户/场景/优先级/定制)",
|
||||
"删一行 = 排除(AI 识别但实际不需要生产的需求)",
|
||||
"改单元格 = 修正 AI 识别的内容 —— 尤其是目标客户、适用场景、优先级",
|
||||
"改完保存,回这张卡提交"],
|
||||
"standard":"需求清单里每一行内容类型都齐;该补充的都补充、该排除的都排除了。",
|
||||
"basis":[{"k":"触发口径","v":"<照 content_brand.verdict 刚写的 trigger/trigger_detail 说,
|
||||
如「主动触发:月度定期内容更新」或「被动触发:E04 关系覆盖需要价值传递素材」>"},
|
||||
{"k":"AI 识别","v":"<N> 条内容需求"}]}}
|
||||
发完这一拍就结束了(发卡挂起)。
|
||||
|
||||
【人交回之后】这一步你只做两件事,然后 finish:
|
||||
① 写 \`content_brand.verdict.requirement_confirmed_total = 1\`;他在件上撤掉了行或加了行
|
||||
的话,下一步的市场依据采集与内容创作按当下 \`content_brand.requirement\` 的实际行数走,
|
||||
不必你重写。
|
||||
② finish 这一项。**别在这一拍采集市场依据或草拟初稿** —— 那是下一步的活,这里做了也白做。
|
||||
|
||||
【这张卡一单只发一次】人交回来之后不许再发同一张卡 —— 缺必填有提交闸兜着,
|
||||
卡回来时内容类型一定是齐的(或那几行已被他撤掉)。`,
|
||||
},
|
||||
{
|
||||
item: 'S3_gather_and_draft',
|
||||
intent: '采集市场依据、按 BT 层级草拟多语言内容初稿',
|
||||
writes: ['content.asset', 'content_brand.verdict'],
|
||||
reads: ['content_brand.requirement', 'market.signal', 'content.asset'],
|
||||
view: 'draft-review',
|
||||
tools: [],
|
||||
doneWhen: 'content_asset_rows > 0 and content_asset_missing = 0',
|
||||
guide: `需求确认了,**现在读市场依据、草拟内容初稿** —— 一拍连做,这一步纯加工态,
|
||||
不必找人。
|
||||
|
||||
【采集市场依据】读 \`market.signal\`(L-01 宏观市场台账):按需求里的 \`target_customer\`/
|
||||
业务背景筛选相关的 \`market_region\`/\`industry\`/\`chip_category\` 信号,归纳产业趋势与
|
||||
差异化定位。**读不到相关信号是正常结果**(不是每条内容需求都有现成的市场信号支持)——
|
||||
读不到就如实基于已有资产与客户信息拟定,别在报告里编一条不存在的市场依据。
|
||||
|
||||
【按 BT 层级草拟内容初稿】逐条 \`content_brand.requirement\` 草拟一份初稿:
|
||||
· BT-01(采购/供应链层)侧重:供货能力 / 资质文件 / 渠道政策;
|
||||
· BT-02(技术层)侧重:技术规格书 / 替代对照表 / 认证报告;
|
||||
· BT-03(高层)侧重:系统方案白皮书 / 参考设计 / 成功案例。
|
||||
需求没指明 BT 层级的,按内容类型判断("系统方案白皮书"多半是 BT-03,"技术规格书"多半
|
||||
是 BT-02)。**维护多语言版本**:中文必备,俄罗斯市场相关(俄语区客户/进入俄罗斯市场
|
||||
的需求)加俄语,其余按需求背景判断要不要加英语。同一份内容的多语言版本写进**同一行**
|
||||
的 \`language\` 列(用" · "连接,如"中文 · Русский"),不必为每种语言各开一行。
|
||||
|
||||
读 \`content.asset\` 里已有资产与品牌调性标准,保持措辞一致、避免重复生产同一份内容。
|
||||
|
||||
【写 \`content.asset\`,一份内容初稿一行】(rows 收数组)
|
||||
content_id(**必填**,格式固定 \`'CT-' + 本年年份 + '-' + 本轮内四位序号\`,如
|
||||
"CT-2026-0001"、"CT-2026-0002",本轮内递增、不与已有资产的编号相撞)、content_type
|
||||
(**必填**,照抄需求的 content_type)、title(**必填**,具体标题,不写"内容初稿"这类
|
||||
占位词)、language(**必填**,如"中文"或"中文 · Русский")、version(**必填**,本步一律
|
||||
写 \`v0.1\`)、scene(选填,照抄需求的 scene 人读标签)、content_status(固定写 \`draft\`)、
|
||||
brand_tone(选填,按品牌调性标准挑 1-2 个最贴合的,如"专业 · 可信赖")、bt_tag(选填,
|
||||
**写人读值** "BT-01"/"BT-02"/"BT-03" 之一,按上面那三档判 —— 这一列在件上是普通格子、
|
||||
不带翻译层,写 ASCII 码人看到的就是 ASCII 码;验收工单要人核「BT 标签登记完整」,
|
||||
核的就是它)、target_customer(选填,照抄需求的 target_customer)、file_ref(选填,
|
||||
留空即可)、reviewer(选填,本步留空,S4 审核通过后再填)。
|
||||
|
||||
【**正文与格式:这一步真正要产出的资料**】(\`body_md\` 与 \`file_format\`,两列都**必填**)
|
||||
人在下一步要审的是**资料本身**,不是一行标题 —— 件上点资料名就打开这份文件,
|
||||
所以正文得真写出来。
|
||||
|
||||
· \`body_md\` = 这份资料的**全文,Markdown 原文**。照 BT 层级该有的结构写实内容:
|
||||
一级标题一句话说清这是什么,再按"##"分节。**每一节都要有实打实的内容** ——
|
||||
一句"回头再补"或者把人指去别处的话,等于这一份资料没产出。认得的写法就这几样,够用:
|
||||
"# 标题" / "## 小节" / 段落 / "- 要点" / "1. 步骤" / "| 列 | 列 |" 表格 / "---" 分隔线 /
|
||||
"**重点**"。多语言版本写进同一份正文的不同小节
|
||||
(如 "## Русская версия"),不为每种语言另开一行。
|
||||
· \`file_format\` = 这份资料**该以哪种格式交出去**,四选一,按内容形态挑,别一律写 md:
|
||||
- \`xlsx\`:主体是表 —— 替代对照表、参数对比表、价格/交期清单;
|
||||
- \`docx\`:正式文档 —— 系统方案白皮书、技术规格书、认证报告、渠道政策;
|
||||
- \`html\`:要在网页上直接看的 —— 落地页文案、案例展示页;
|
||||
- \`md\`:短说明、要点清单、内部速记。
|
||||
挑 \`xlsx\` 的那一份,正文里**必须真有一张 Markdown 表格**(否则导出来是一张只有
|
||||
标题的空表)。同一轮里几份内容各按自己的形态挑,不必统一。
|
||||
|
||||
【同一拍写 \`content_brand.verdict\`】summary 补一句人话(本轮草拟了几份内容、依据是
|
||||
什么市场信号或已有资产)。
|
||||
|
||||
写完这一拍就结束(不必你 finish)——\`content_asset_rows > 0 and content_asset_missing = 0\`
|
||||
一旦为真,引擎自己收这一步。`,
|
||||
},
|
||||
{
|
||||
item: 'S4_draft_review',
|
||||
intent: '内容初稿审核:通过就往下走,驳回则按意见修订后换一种问法再提交',
|
||||
writes: ['content.asset', 'content_brand.verdict'],
|
||||
reads: ['content.asset'],
|
||||
view: 'draft-review',
|
||||
tools: [],
|
||||
/* 决策卡循环 + 要人拍板 —— 不给 doneWhen,引擎不许替人收 */
|
||||
guide: `**动手之前先在轨迹里找一行 \`[card-reply]\` 带这一项的答复:**
|
||||
|
||||
· **没找到** —— 这是第一次,跳到下面【发一张决策卡】那一段;
|
||||
· **\`value\` 是 \`"approve"\`** —— 把这一轮 \`content.asset\` 里状态还是 \`draft\` 的每一行
|
||||
改成 \`content_status = "reviewed"\`(同 PK 覆盖写,\`reviewer\` 补一句"AM Lead 审核通过"),
|
||||
写 \`content_brand.verdict.draft_approved_total = 1\`,finish 这一项,**不许再发卡**;
|
||||
· **\`value\` 是 \`"reject"\` 或以 \`"custom:"\` 开头** —— 都算驳回,跳到下面【驳回之后】
|
||||
那一段,修订初稿再重新发卡。
|
||||
|
||||
【这一步办完了没有,看一个数】\`draft_approved_total\`:它写成 1 就是这一步办完了,
|
||||
引擎不会再把这一项交给你(它归零 doneWhen 帮不上忙,只有你自己 finish)。
|
||||
|
||||
【发一张决策卡,照这个骨架】(选项少于两个基座拒发;\`allowCustom\` 别漏 —— 少了它,人只有
|
||||
两个固定按钮可点,连意见都没地方写;\`ask\` 每一轮都要按下面的规则换措辞)
|
||||
{"kind":"decide","itemId":"S4_draft_review",
|
||||
"allowCustom":true,
|
||||
"ask":"内容初稿已草拟,共 <N> 份,请审核。",
|
||||
"options":[
|
||||
{"value":"approve","label":"通过","recommended":true,"consequence":"内容进入发布准备阶段"},
|
||||
{"value":"reject","label":"驳回(不写具体意见)","consequence":"AI按调性标准复核一遍初稿,重新提交审核"}],
|
||||
"basis":[{"k":"本轮初稿","v":"<N> 份,覆盖 <涉及的内容类型>"},
|
||||
{"k":"调性依据","v":"专业 / 可信赖 / 高效 / 本土化"},
|
||||
{"k":"想说明理由","v":"不点上面两个按钮,直接在下方输入框写你的意见再提交"}]}
|
||||
发完这一拍就结束了(发卡挂起)。
|
||||
|
||||
【驳回之后 —— 不管有没有具体意见,都只处理这一轮,不许再追问一遍】
|
||||
① \`value\` 以 \`"custom:"\` 开头 —— \`note\` 就是他写的具体意见,**按这条意见**修订
|
||||
\`content.asset\`(同 PK 覆盖写对应的行,不开新计划项、不重新进入 sentry)。
|
||||
② \`value\` 是 \`"reject"\`(点了固定按钮,没写具体意见)—— **不许再发卡追问理由**
|
||||
(那两个按钮点出来的回复永远没有 note,再问一遍是把人指向一扇打不开的门,
|
||||
会被同一句话拒回,连问出真话的机会都没有)。改成你自己复核:对照调性标准
|
||||
(专业/可信赖/高效/本土化)重新审视措辞,技术数据有没有可疑处;复核后**至少要有
|
||||
一处实质性变化**(改措辞、补细节、调标签,不能原样重发)。
|
||||
③ **重新发卡的 \`ask\` 必须换一种说法**,带上修订轮次与改了什么,例如
|
||||
"已复核并修订(第2稿:调整了措辞、补充了认证细节)请再次审核"——
|
||||
措辞不换,系统会把它当成同一张卡:前两次静默吞掉(人看不到新卡),第三次直接拒。
|
||||
**每一轮的 \`ask\` 都要与上一轮明显不同**,不止改个数字。
|
||||
④ 再走一遍【发一张决策卡】那一段(用新措辞,\`allowCustom\` 仍然要给),然后这一拍结束
|
||||
(发卡挂起)。
|
||||
|
||||
**这一步只在这一项里循环,不许把驳回意见带去改别的项**(S1/S2/S3 写下的东西不动)。`,
|
||||
},
|
||||
{
|
||||
item: 'S5_publish_prep',
|
||||
intent: '拟发布:版本号递增、状态流转、登记场景与 BT 标签',
|
||||
writes: ['content.asset', 'content_brand.verdict'],
|
||||
reads: ['content.asset', 'content_brand.requirement'],
|
||||
view: 'publish-confirm',
|
||||
tools: [],
|
||||
doneWhen: 'publish_prepared_total > 0 and content_asset_content_status_published > 0 and content_asset_missing = 0',
|
||||
guide: `审核通过了,**现在拟发布** —— 一拍连做。
|
||||
|
||||
【写 \`content.asset\`,把审核通过的每一行(\`content_status = "reviewed"\`)改成拟发布态】
|
||||
(同 PK 覆盖写)version(**必填**,由 \`v0.1\` 改成 \`v1.0\`;若这份内容是已发布内容的
|
||||
修订版本(\`content.asset\` 里已有同标题的 \`published\` 行),小版本递增,如 \`v1.1\`)、
|
||||
content_status(**必填**,固定写 \`published\`)、scene(**必填**,若上一步没填齐,这一步
|
||||
补上 —— 发布前场景必须登记完整)、bt_tag(**必填**,同上,人读值 "BT-01"/"BT-02"/"BT-03";
|
||||
场景与 BT 标签这两列**声明里钉的就是「发布态才必填」**,状态一写成 published,
|
||||
它们空着就会被红带、提交闸、放行门 L2 同时拦下)、brand_tone(选填,若上一步没填,
|
||||
这一步按调性标准补)。
|
||||
**这一步只是把状态写进本单草稿层,不是真的对外发布** —— 铁律②:草稿层的变更要等
|
||||
T_release 人签字放行才合并进 L-05 正式版、真正对外可见。
|
||||
|
||||
【同一拍写 \`content_brand.verdict\`】publish_prepared_total = 1、
|
||||
content_produced_total = 这一轮拟发布的内容份数、summary 补一句人话(这一轮生产了
|
||||
哪几类内容、准备发布几份)。
|
||||
|
||||
写完 finish 这一项 —— 或者不必你 finish,\`doneWhen\` 会在必填列都齐的那一刻自动收步。`,
|
||||
},
|
||||
{
|
||||
item: 'T_release',
|
||||
intent: '发布已准备好,提交放行交 AM Lead 验收;被打回就按意见改完再交',
|
||||
/* 被打回重做时要改的就是这两张 —— 不声明就写不进去(写的授权面是台账声明) */
|
||||
writes: ['content.asset', 'content_brand.verdict'],
|
||||
reads: ['content.asset', 'content_brand.requirement'],
|
||||
view: 'publish-confirm',
|
||||
tools: [],
|
||||
/* 放行触达世界,必须人按一下 —— 引擎不自动收它 */
|
||||
guide: `拟发布内容齐了、必填也补齐了,**可以走放行了**
|
||||
(T_release 人工激活:动它就算启动,不必单发 start)。
|
||||
|
||||
【**先看一眼轨迹里有没有「验收被打回重做」**】有的话,这一拍**不是**再提交一遍 ——
|
||||
人已经看过这份成果并把它退回来了,原样递上去他看见的还是刚被他打回的那一份
|
||||
(基座那道闸也会当场拒:「打回之后台账一个字没改」)。照他的意见改**台账本身**,
|
||||
改完这一拍再 judge + submit:
|
||||
· 他说**版本号/状态不对** → 改 \`content.asset\` 的 \`version\`/\`content_status\`(同 PK 覆盖写);
|
||||
· 他说**场景或 BT 标签没登记全** → 补 \`scene\`/\`bt_tag\`;
|
||||
· 他说**语言版本不齐** → 补 \`language\`(同一行用" · "连接,不新开行);
|
||||
· 他说**标题/调性不合** → 改 \`title\`/\`brand_tone\`;
|
||||
· 他**没写具体意见**(只点了重做)→ 自己把这几份内容复核一遍,挑最站不住的那处改实,
|
||||
**至少改对一处**;实在判断不出他要什么,发一张零输入执行工单问清楚,别原样重提。
|
||||
**改的是值,不是行**:同一行覆盖写,别新开行 —— 那会把收步判据与发布份数弄假。
|
||||
|
||||
【没被打回(或已按意见改过了)】先把裁量判据裁掉,然后 submit,同一拍连做:
|
||||
|
||||
· judge criterion="版本号与状态是否正确:版本号已更新、状态流转无误" —— 逐条看
|
||||
\`content.asset\` 的 \`version\`/\`content_status\`:版本号已从 \`v0.1\` 更新、状态流转
|
||||
(草稿 → 已审核 → 已发布)每一步都走过没有跳步,就 satisfied=true;有一条对不上,
|
||||
satisfied=**false**,rationale 逐条写清,交签字的人定。
|
||||
· judge criterion="场景与 BT 标签是否登记完整" —— 核对每条拟发布内容的 \`scene\`/
|
||||
\`bt_tag\` 是否都有值(这两列在声明里是「发布态必填」,基座也数得出来;
|
||||
你要判的是**登记得对不对**,不只是有没有值)。
|
||||
· judge criterion="多语言版本是否齐全" —— 核对 \`language\` 是否覆盖了这条内容该有的
|
||||
语言版本(如面向俄罗斯市场的内容至少要有俄语)。
|
||||
**裁成不满足照样能提交** —— 理由连着卡一起交给签字的人。criterion 照判据名整句原样抄,
|
||||
写别的名字对不上。
|
||||
|
||||
这一单没有分支去向 —— 全程线性,只发一口 \`content-published\`,不必你选出口。
|
||||
|
||||
终局唯放行:你不能自己宣布完成。`,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 阶段 —— 进度条的刻度,只给人看。除放行外每个计划项恰属一个阶段;
|
||||
* 放行那一格由基座统一加,人看到的是 x/3。
|
||||
*/
|
||||
export const CONTENT_PHASES = [
|
||||
{ name: '需求识别与受理', items: ['S1_identify_requirement', 'S2_requirement_confirm'] },
|
||||
{ name: '采集与创作', items: ['S3_gather_and_draft'] },
|
||||
{ name: '审核与发布', items: ['S4_draft_review', 'S5_publish_prep'] },
|
||||
];
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* I01 · 品牌形象与销售工具包管理 —— 活动包代码入口(ADR-004:默认导出 defineActivity 的返回值)。
|
||||
*
|
||||
* 这个活动没有 `run`。案例活动的推进由 Loop 驱动,行为全部写在清单里:
|
||||
* 案例模型 + 说明书 + 步骤剧本 + 三条技能 + 台账声明 + 三张 HTML 报告模版。
|
||||
* 这个文件之所以还存在,是因为清单的 `entry` 指着它 ——
|
||||
* 它是**被静态审扫描的产物文本**(查 import 与全局引用,证明活动包零环境能力)。
|
||||
*/
|
||||
import { defineActivity } from '@sdk';
|
||||
|
||||
export default defineActivity({ name: 'brand-content-toolkit' });
|
||||
@@ -0,0 +1,526 @@
|
||||
/**
|
||||
* 活动包清单 —— I01 品牌形象与销售工具包管理(案例活动)。
|
||||
*
|
||||
* 业务出处:`docs/SS01-客户与市场/i01-html/I01-活动流程与工单说明.md`(7 步业务链条,
|
||||
* 工单只针对生产流程①→⑤,收成 1 张执行工单 + 2 张验收工单)、
|
||||
* `docs/SS01-客户与市场/Demo-E04-I01-Activity台账关联与设计.html`(L-01 宏观市场台账 /
|
||||
* L-05 品牌内容库的字段表与读写关系)。
|
||||
*
|
||||
* 这是本仓库第二个不属于 `quote-*` A1-A7 报价管线的活动,方法论照抄第一个
|
||||
* (`crm-relationship-coverage` / E04)已验证过的样子:双级下拉入参、活动自有工作台账
|
||||
* + 共享主数据、decide 决策卡循环做中间验收、六步案例模型。
|
||||
*
|
||||
* 两处与 E04 的刻意不同:
|
||||
* ① E04 核心是**读写**共享主数据(`customer.profile`/`customer.contact`),
|
||||
* I01 核心是**只读** L-01(`market.signal`)、**读写**它独有的 L-05
|
||||
* (`content.asset`)——本 Demo 范围内 I01 是 `content.asset` 唯一的读写者,
|
||||
* 不追加列合并,是这份活动包**第一次**完整声明这两张台账的物理结构。
|
||||
* ② I01 没有 E04 那种「AI 做不到、必须人去线下执行」的环节(内容初稿由 AI 草拟,
|
||||
* 人只审不做)——所以只有 1 张执行工单(S2_requirement_confirm),
|
||||
* 验收工单反倒有 2 张(S4_draft_review 审初稿 + T_release 发布确认),
|
||||
* 与 E04(1 张验收决策 + 1 张终局放行)结构同型,只是执行工单少一张。
|
||||
*/
|
||||
import type { ActivityManifest, DmnModel } from '@contracts';
|
||||
import { activityIdSchema } from '@contracts';
|
||||
import { CONTENT_SKILLS } from './skills.ts';
|
||||
import { CMMN_XML, NARRATIVE, CONTENT_PHASES, CONTENT_STEPS } from './case-model.ts';
|
||||
import { REQUIREMENT_LIST, DRAFT_REVIEW, PUBLISH_CONFIRM } from './views/report.ts';
|
||||
|
||||
const D = 'content_brand';
|
||||
const ACTIVITY_ID = activityIdSchema.parse('brand-content-toolkit');
|
||||
|
||||
/** 没有判定表 —— 触发事件→内容类型、BT 层级→内容结构都是文字规则,不是离散分支命中表。 */
|
||||
const EMPTY_DMN: DmnModel = { drd: [], tables: [] };
|
||||
|
||||
export const manifest: ActivityManifest = {
|
||||
identity: {
|
||||
activityId: ACTIVITY_ID,
|
||||
name: 'I01 · 品牌形象与销售工具包管理',
|
||||
packageVersion: '1.2.2',
|
||||
category: '内容生产',
|
||||
sdkVersion: '0.1.0-m5',
|
||||
checksum: 'dev-checksum-brand-content-toolkit',
|
||||
},
|
||||
|
||||
/* ── 契约七件 ────────────────────────────────────────────────── */
|
||||
contract: {
|
||||
activityId: ACTIVITY_ID,
|
||||
version: '1.0.0',
|
||||
input: {
|
||||
/*
|
||||
* 不是来件解析(没有唯一「这封信」),是触发参数 —— 与 E04 同一处置
|
||||
* (两级下拉 + 选填客户/说明):
|
||||
* ① `trigger` 必填 —— 主动 / 被动,显式下拉,不接受省略;
|
||||
* ② `reason`/`requester` 选填,只在对应 `trigger` 值时现身(`showWhen`);
|
||||
* ③ `targetCustomer` 选填字符串 —— 定向客户编码,通用内容留空。
|
||||
*/
|
||||
data: {
|
||||
type: 'object',
|
||||
required: ['trigger'],
|
||||
properties: {
|
||||
trigger: {
|
||||
type: 'string',
|
||||
enum: ['proactive', 'reactive'],
|
||||
enumNames: ['主动触发(战略调整/新市场/工具过期/月度更新)', '被动触发(使用方请求)'],
|
||||
title: '触发方式',
|
||||
},
|
||||
reason: {
|
||||
type: 'string',
|
||||
enum: ['strategy_shift', 'new_market', 'tool_outdated', 'monthly_update'],
|
||||
enumNames: ['战略调整', '进入新市场', '工具过期', '月度定期内容更新'],
|
||||
title: '主动触发的原因(仅主动触发时选)',
|
||||
showWhen: { field: 'trigger', equals: 'proactive' },
|
||||
},
|
||||
requester: {
|
||||
type: 'string',
|
||||
enum: ['E04', 'T03', 'T04', 'E03'],
|
||||
enumNames: [
|
||||
'E04 关系覆盖需要价值传递素材',
|
||||
'T03 触达需要材料',
|
||||
'T04 攻坚需要定制内容',
|
||||
'E03 方案需要支撑',
|
||||
],
|
||||
title: '被动触发的使用方(仅被动触发时选)',
|
||||
showWhen: { field: 'trigger', equals: 'reactive' },
|
||||
},
|
||||
targetCustomer: {
|
||||
type: 'string',
|
||||
title: '定向客户编码(选填,为特定客户定制素材时填;通用内容留空)',
|
||||
examples: ['CU-0142'],
|
||||
},
|
||||
note: {
|
||||
type: 'string',
|
||||
title: '本轮说明(选填,背景信息 —— 帮AI判断内容侧重与紧迫程度)',
|
||||
examples: ['进入俄罗斯市场,需要一份中俄双语的公司介绍与能力声明,月底展会要用'],
|
||||
},
|
||||
},
|
||||
},
|
||||
messageAllowed: true,
|
||||
messageHint: '内容运营可在此说明本轮生产的特殊要求、目标受众、或需要重点突出的内容侧重',
|
||||
},
|
||||
output: {
|
||||
/* 单一必发口,无分支:I01 生产流程线性(①→②③→④→⑤),没有岔路口。 */
|
||||
mandatory: [
|
||||
{
|
||||
name: 'content-published',
|
||||
description:
|
||||
'本轮已发布的内容资产 —— 供 E04/T03/T04/E03 分发调用;当前代码库尚未实现这些下游活动的读取消费者,此口暂无消费者,仅按"载荷=已落地事实"的契约纪律预先声明',
|
||||
payload: {
|
||||
type: 'object',
|
||||
required: ['roundRef', 'publishedContentTotal'],
|
||||
properties: {
|
||||
roundRef: { type: 'string', title: '本轮案号' },
|
||||
publishedContentTotal: { type: 'integer', title: '本轮已发布内容份数' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
selective: [],
|
||||
},
|
||||
/*
|
||||
* 三份必交付物,逐一对应三张工单产出的成果。全部 `editableAtRelease: []`——
|
||||
* 业务说明明文"验收工单只读展示,不可修改"(内容初稿与拟发布明细都是 AI 的结论,
|
||||
* 人只做通过/驳回或签字,不在验收页上就地改)。
|
||||
*/
|
||||
requiredDeliverables: [
|
||||
{
|
||||
key: 'requirement-confirm',
|
||||
label: '内容需求清单',
|
||||
ledger: `${D}.requirement`,
|
||||
ledgers: [`${D}.requirement`, `${D}.verdict`],
|
||||
editableAtRelease: [],
|
||||
},
|
||||
{ key: 'draft-review', label: '内容初稿', ledger: 'content.asset', editableAtRelease: [] },
|
||||
{ key: 'publish-confirm', label: '拟发布内容', ledger: 'content.asset', editableAtRelease: [] },
|
||||
],
|
||||
/*
|
||||
* 验收判据 —— **机检(L1/L2)只读基座数得出来的数**:`_rows`/`_missing`/按值计数基座现数,
|
||||
* 与执行卡的提交闸同一个数同一套算法。L4 三条是纯裁量(版本状态对不对、标签齐不齐、
|
||||
* 多语言够不够)——机器数得出「有没有值」,数不出「对不对、够不够」,那是人对结果负责的地方。
|
||||
*/
|
||||
acceptanceCriteria: [
|
||||
/*
|
||||
* 两条 L1 的「人过了没有」一半靠记账旗(只有人回卡之后模型才记 —— 与 A1 的
|
||||
* confirmed_total 同款,规范 §5.3-4 背书的人环凭据);但**每条都必须再钉一半
|
||||
* 基座数**(2026-08-23 照 E04 的账整改):旗是模型写的,单独成谓词就是
|
||||
* 「机检只读基座数」第 N 次违反的伪装形 —— E04 那单抢跑写旗、活单熔断,
|
||||
* 正是踩着这半边过的门。
|
||||
*/
|
||||
{
|
||||
key: 'requirement-confirmed',
|
||||
label: '内容需求已经内容运营核对确认(执行工单已交回,清单有行且内容类型齐)',
|
||||
level: 'L1',
|
||||
predicate: `requirement_confirmed_total > 0 and ${D}_requirement_rows > 0 and ${D}_requirement_missing = 0`,
|
||||
},
|
||||
{
|
||||
key: 'draft-approved',
|
||||
label: '内容初稿已经 AM Lead 审核通过(验收工单1已过,不处在驳回待改状态)',
|
||||
level: 'L1',
|
||||
predicate: 'draft_approved_total > 0 and content_asset_rows > 0 and content_asset_missing = 0',
|
||||
},
|
||||
{
|
||||
key: 'draft-complete',
|
||||
label: '内容初稿逐份完整:标题、内容类型、语言版本、状态都有',
|
||||
level: 'L2',
|
||||
predicate: 'content_asset_rows > 0 and content_asset_missing = 0',
|
||||
},
|
||||
/*
|
||||
* **资料文件真产出了没有** —— 两边都是基座现数:每一行都报了一种格式
|
||||
* (四档计数之和 = 行数),而正文空着的行会被 `_missing` 数出来(body_md 是必填)。
|
||||
* 从前这一步只产出一行标题,人点不开也下不来 —— 判据得能指向一个看得见的东西:
|
||||
* 这一条为真,件上每个资料名都点得开。
|
||||
*/
|
||||
{
|
||||
key: 'asset-file-ready',
|
||||
label: '每份内容都产出了资料文件(格式已定、正文已写,件上点资料名即可打开)',
|
||||
level: 'L2',
|
||||
predicate:
|
||||
'content_asset_rows > 0 and content_asset_missing = 0 and ' +
|
||||
'content_asset_file_format_md + content_asset_file_format_html + ' +
|
||||
'content_asset_file_format_docx + content_asset_file_format_xlsx = content_asset_rows',
|
||||
},
|
||||
{
|
||||
key: 'publish-registered',
|
||||
label: '拟发布内容已登记为已发布状态且逐项完整',
|
||||
level: 'L2',
|
||||
predicate: 'content_asset_content_status_published > 0 and content_asset_missing = 0',
|
||||
},
|
||||
{ key: 'version-status-correct', label: '版本号与状态是否正确:版本号已更新、状态流转无误', level: 'L4' },
|
||||
{ key: 'scene-tag-complete', label: '场景与 BT 标签是否登记完整', level: 'L4' },
|
||||
{ key: 'language-complete', label: '多语言版本是否齐全', level: 'L4' },
|
||||
/* L5 是责任关:那张卡本身就是这道关,卡面上不摆它(基座统一滤) */
|
||||
{ key: 'owner-release', label: '发布入库放行:签下去即正式生效并写入 L-05', level: 'L5' },
|
||||
],
|
||||
revisableAtRelease: [],
|
||||
prohibitions: [
|
||||
{ key: 'no-fabricate-signal', statement: '市场信号读不到相关内容时不得编造 —— 如实基于已有资产与客户信息拟定' },
|
||||
{ key: 'no-skip-review', statement: '内容状态流转不得跳步 —— draft → reviewed → published 每一步都要真的写过' },
|
||||
{ key: 'no-external-write', statement: '放行前不得触达外部系统 —— 触达世界的动作集中在放行那一刻' },
|
||||
],
|
||||
authorization: {
|
||||
discretion: [],
|
||||
preauthorizedActions: [],
|
||||
/* 基线三次(S2/S4通过/T_release)+ 决策卡循环预留两轮(每轮驳回+重提交各算一次,+4),留够余量 */
|
||||
humanCallBudget: 10,
|
||||
},
|
||||
resources: {
|
||||
tools: [],
|
||||
skills: CONTENT_SKILLS.map((s) => s.id),
|
||||
models: ['deepseek-v4-flash'],
|
||||
roles: ['AM Lead'],
|
||||
budget: { steps: 60, tokens: 400_000, activeMinutes: 60, humanCalls: 10 },
|
||||
},
|
||||
},
|
||||
|
||||
/* ── 声明 ────────────────────────────────────────────────────── */
|
||||
declarations: {
|
||||
models: ['deepseek-v4-flash'],
|
||||
tools: [],
|
||||
ledger: [
|
||||
/*
|
||||
* ── L-01 宏观市场台账(kind:'master',I01 只读)──
|
||||
* 首次完整声明:本仓库尚无其它活动读写它,不是追加列。
|
||||
*/
|
||||
{
|
||||
domain: 'market',
|
||||
object: 'signal',
|
||||
kind: 'master',
|
||||
label: '宏观市场台账',
|
||||
fields: [
|
||||
{ name: 'signal_id', type: 'text', primaryKey: true, masked: false, label: '信号ID' },
|
||||
{
|
||||
name: 'signal_type',
|
||||
type: 'text',
|
||||
primaryKey: false,
|
||||
masked: false,
|
||||
tally: ['macro_economy', 'industry_trend', 'competition', 'policy', 'customer_market'],
|
||||
label: '信号类型',
|
||||
},
|
||||
{ name: 'title', type: 'text', primaryKey: false, masked: false, label: '标题' },
|
||||
{ name: 'content', type: 'text', primaryKey: false, masked: false, label: '详情' },
|
||||
{ name: 'source', type: 'text', primaryKey: false, masked: false, label: '信号来源' },
|
||||
{ name: 'signal_date', type: 'date', primaryKey: false, masked: false, label: '信号日期' },
|
||||
{ name: 'market_region', type: 'text', primaryKey: false, masked: false, label: '所属市场' },
|
||||
{ name: 'industry', type: 'text', primaryKey: false, masked: false, label: '细分行业' },
|
||||
{ name: 'chip_category', type: 'text', primaryKey: false, masked: false, label: '关联芯片品类' },
|
||||
{ name: 'importance', type: 'integer', primaryKey: false, masked: false, label: '重要性评分(1-5)' },
|
||||
{
|
||||
name: 'impact_dir',
|
||||
type: 'text',
|
||||
primaryKey: false,
|
||||
masked: false,
|
||||
tally: ['opportunity', 'threat', 'neutral'],
|
||||
label: '影响方向',
|
||||
},
|
||||
{
|
||||
name: 'signal_status',
|
||||
type: 'text',
|
||||
primaryKey: false,
|
||||
masked: false,
|
||||
tally: ['pending', 'evaluated', 'archived', 'expired'],
|
||||
label: '信号状态',
|
||||
},
|
||||
{ name: 'version', type: 'text', primaryKey: false, masked: false, label: '版本号' },
|
||||
{ name: 'review_note', type: 'text', primaryKey: false, masked: false, label: '复盘备注' },
|
||||
],
|
||||
},
|
||||
/*
|
||||
* ── L-05 品牌内容库(kind:'master',I01 唯一读写者)──
|
||||
* 首次完整声明并持有全部基本列(不是追加,`declarations.ledger` 里明写 required/expect,
|
||||
* 因为没有别的活动会与 I01 在这张表上"谁先立谁的字段"打架)。
|
||||
*/
|
||||
{
|
||||
domain: 'content',
|
||||
object: 'asset',
|
||||
kind: 'master',
|
||||
label: '品牌内容库',
|
||||
fields: [
|
||||
{ name: 'content_id', type: 'text', primaryKey: true, masked: false, label: '内容ID' },
|
||||
{ name: 'content_type', type: 'text', primaryKey: false, masked: false, required: true, label: '内容类型' },
|
||||
{ name: 'title', type: 'text', primaryKey: false, masked: false, required: true, label: '内容标题' },
|
||||
{
|
||||
name: 'language',
|
||||
type: 'text',
|
||||
primaryKey: false,
|
||||
masked: false,
|
||||
required: true,
|
||||
label: '语言版本(多语言用" · "连接)',
|
||||
},
|
||||
{ name: 'version', type: 'text', primaryKey: false, masked: false, required: true, label: '版本号' },
|
||||
/*
|
||||
* **发布那一刻才必填**(2026-08-23):S5 的剧本明写「发布前场景必须登记」,
|
||||
* 而声明只标 expect —— 机器数不出来,那句话就只能靠 L4 让人肉眼核。
|
||||
* `requiredWhen` 把必填钉在**已发布**的那几行:草稿态不拦(那时还没定场景),
|
||||
* 一旦状态流到 published,红带/提交闸/放行门 L2 三处同源同数地拦。
|
||||
*/
|
||||
{
|
||||
name: 'scene',
|
||||
type: 'text',
|
||||
primaryKey: false,
|
||||
masked: false,
|
||||
expect: true,
|
||||
requiredWhen: { column: 'content_status', value: 'published' },
|
||||
label: '适用场景',
|
||||
},
|
||||
/*
|
||||
* BT 标签 —— 目标件的验收工单1 就有这一列,而从前 `content.asset` 压根没有它:
|
||||
* L4 判据「场景与 BT 标签是否登记完整」让人核一个卡面上不存在的东西
|
||||
* (判据指向的东西必须摆得出来,E04 那一课)。与 `scene` 同款:发布那一刻才必填。
|
||||
*
|
||||
* **不带 tally** —— 与 `requirement.scene` 同一条教训(2026-08-21):
|
||||
* tally 的取值域得是 ASCII(`data-class-of` 只认 ASCII),而这一列在件上是
|
||||
* 普通格子、走 `data-slot` 直显,没有翻译层 —— 存 `bt_03` 人看到的就是 `bt_03`。
|
||||
* 存人读值("BT-01"/"BT-02"/"BT-03"),屏上与目标件一字不差。
|
||||
*/
|
||||
{
|
||||
name: 'bt_tag',
|
||||
type: 'text',
|
||||
primaryKey: false,
|
||||
masked: false,
|
||||
expect: true,
|
||||
requiredWhen: { column: 'content_status', value: 'published' },
|
||||
label: 'BT 层级标签',
|
||||
},
|
||||
{
|
||||
name: 'content_status',
|
||||
type: 'text',
|
||||
primaryKey: false,
|
||||
masked: false,
|
||||
required: true,
|
||||
tally: ['draft', 'reviewed', 'published', 'deprecated'],
|
||||
label: '内容状态',
|
||||
},
|
||||
{ name: 'brand_tone', type: 'text', primaryKey: false, masked: false, expect: true, label: '品牌调性标签' },
|
||||
{ name: 'target_customer', type: 'text', primaryKey: false, masked: false, label: '定向客户版本' },
|
||||
/*
|
||||
* ── 一份内容 = 一份**资料文件**(2026-08-23 用户:「审核资料这一步要真实
|
||||
* 产出资料文件,格式可以是 word/excel/md/html,点资料名能打开渲染」)──
|
||||
*
|
||||
* 从前只有一句 `file_ref: '文件位置'`,而 S3 的剧本明写「本 MVP 阶段可留空」:
|
||||
* **这一步压根没产出过任何文件** —— 屏上一行标题,点不开、下不来,
|
||||
* 人要审的那份初稿根本不在系统里。
|
||||
*
|
||||
* 现在正文落 `body_md`(Markdown 原文,AI 写的就是它),格式落 `file_format`,
|
||||
* 基座按 `file` 段现造成真字节(见 `apps/api/src/app/file-compose.ts`):
|
||||
* .docx 用 Word 打得开、.xlsx 用 Excel 打得开,屏上渲的就是那份字节读回来的样子。
|
||||
*
|
||||
* 为什么正文只写一种(Markdown)而不是四种各写一遍:四份就是四份事实,
|
||||
* 改一处就分叉。**写一份,按格式投影** —— 与「文件是台账的投影」同一条。
|
||||
*/
|
||||
{
|
||||
name: 'body_md',
|
||||
type: 'text',
|
||||
primaryKey: false,
|
||||
masked: false,
|
||||
required: true,
|
||||
label: '内容正文(Markdown 原文)',
|
||||
},
|
||||
{
|
||||
name: 'file_format',
|
||||
type: 'text',
|
||||
primaryKey: false,
|
||||
masked: false,
|
||||
required: true,
|
||||
tally: ['md', 'html', 'docx', 'xlsx'],
|
||||
label: '资料文件格式',
|
||||
},
|
||||
{ name: 'file_ref', type: 'text', primaryKey: false, masked: false, label: '文件位置' },
|
||||
{ name: 'reviewer', type: 'text', primaryKey: false, masked: false, label: '审核人' },
|
||||
],
|
||||
/**
|
||||
* 这张表的每一行还是**一份资料文件**:标题、正文、格式各取哪一列。
|
||||
* 这一段同时是授权面 —— 不声明它,件上的资料名就点不开(基座 403)。
|
||||
*/
|
||||
file: { titleColumn: 'title', bodyColumn: 'body_md', formatColumn: 'file_format' },
|
||||
},
|
||||
/* ── I01 自有工作台账(kind:'record',caseKey:'round_ref') ── */
|
||||
{
|
||||
domain: D,
|
||||
object: 'requirement',
|
||||
kind: 'record',
|
||||
caseKey: 'round_ref',
|
||||
label: '内容需求清单',
|
||||
fields: [
|
||||
{ name: 'round_ref', type: 'text', primaryKey: true, masked: false, label: '案号' },
|
||||
{ name: 'req_seq', type: 'integer', primaryKey: true, masked: false, label: '本轮需求序号' },
|
||||
{ name: 'content_type', type: 'text', primaryKey: false, masked: false, required: true, label: '内容类型' },
|
||||
{ name: 'source', type: 'text', primaryKey: false, masked: false, expect: true, label: '需求来源' },
|
||||
{ name: 'target_customer', type: 'text', primaryKey: false, masked: false, expect: true, label: '目标客户' },
|
||||
/*
|
||||
* **不带 tally**(2026-08-21 实测逼出的修死):这一列是编辑面上的普通格子
|
||||
* (`REQUIREMENT_TABLE` 里就是一个 `<span data-col>`,不是 `data-class-of` 徽标),
|
||||
* tally 过的 ASCII 码没有翻译层就直接摆上屏——实测人看见的是裸的 "solution",
|
||||
* 不是"方案"。`content.asset.scene`(S3 照抄这一列)本来就是自由文本,两处
|
||||
* 现在口径一致:AI 写人读标签(首触/方案/攻坚/展会/报价),不写 ASCII 码。
|
||||
*/
|
||||
{ name: 'scene', type: 'text', primaryKey: false, masked: false, expect: true, label: '适用场景' },
|
||||
{
|
||||
name: 'priority',
|
||||
type: 'text',
|
||||
primaryKey: false,
|
||||
masked: false,
|
||||
tally: ['high', 'medium', 'low'],
|
||||
label: '优先级',
|
||||
},
|
||||
{ name: 'origin', type: 'text', primaryKey: false, masked: false, label: '来源(ai/human-added)' },
|
||||
],
|
||||
},
|
||||
{
|
||||
domain: D,
|
||||
object: 'verdict',
|
||||
kind: 'record',
|
||||
caseKey: 'round_ref',
|
||||
label: '本轮内容生产结论',
|
||||
fields: [
|
||||
{ name: 'round_ref', type: 'text', primaryKey: true, masked: false, label: '案号' },
|
||||
{ name: 'trigger', type: 'text', primaryKey: false, masked: false, label: '触发方式(人读标签,非入参英文码)' },
|
||||
{ name: 'trigger_detail', type: 'text', primaryKey: false, masked: false, label: '触发细节(人读标签)' },
|
||||
{ name: 'target_customer', type: 'text', primaryKey: false, masked: false, label: '定向客户' },
|
||||
{ name: 'note', type: 'text', primaryKey: false, masked: false, label: '本轮说明' },
|
||||
{ name: 'requirement_confirmed_total', type: 'integer', primaryKey: false, masked: false, label: '需求已确认' },
|
||||
{ name: 'draft_approved_total', type: 'integer', primaryKey: false, masked: false, label: '初稿已审核通过' },
|
||||
{ name: 'publish_prepared_total', type: 'integer', primaryKey: false, masked: false, label: '发布准备已收口' },
|
||||
{ name: 'content_produced_total', type: 'integer', primaryKey: false, masked: false, label: '本轮产出内容份数' },
|
||||
{ name: 'summary', type: 'text', primaryKey: false, masked: false, label: '本轮生产结论' },
|
||||
],
|
||||
},
|
||||
],
|
||||
listeners: [],
|
||||
harness: {
|
||||
memoryWindow: 24,
|
||||
/* 内容生产不是紧迫单,给够时间让人审核 */
|
||||
stallAfter: 'PT4H',
|
||||
nudge: [{ after: 'PT2H', action: 'nudge' }],
|
||||
retry: { times: 2, backoff: 'PT30S' },
|
||||
},
|
||||
},
|
||||
|
||||
/* ── 资产初始版 ──────────────────────────────────────────────── */
|
||||
assets: {
|
||||
businessModel: {
|
||||
cmmnXml: CMMN_XML,
|
||||
narrative: NARRATIVE,
|
||||
dmn: EMPTY_DMN,
|
||||
steps: CONTENT_STEPS,
|
||||
phases: CONTENT_PHASES,
|
||||
},
|
||||
skills: CONTENT_SKILLS,
|
||||
/*
|
||||
* 三个局面各一张:
|
||||
* · `requirement-list` —— S1/S2 摆,内容需求清单,可编辑;
|
||||
* · `draft-review` —— S3/S4 摆,内容初稿,只读(业务说明明文"人只审不改");
|
||||
* · `publish-confirm` —— S5/T_release 摆,拟发布内容,只读,放行后即归档件。
|
||||
*/
|
||||
viewTemplates: [
|
||||
{ name: 'requirement-list', html: REQUIREMENT_LIST },
|
||||
{ name: 'draft-review', html: DRAFT_REVIEW },
|
||||
{ name: 'publish-confirm', html: PUBLISH_CONFIRM },
|
||||
],
|
||||
cardSchemas: {},
|
||||
distillHooks: [
|
||||
{
|
||||
on: 'terminal',
|
||||
prompt: '归纳本轮内容生产:需求识别依据、审核是否被驳回过(为什么)、市场依据来自哪些信号、发布的内容覆盖哪些场景与语言。',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/*
|
||||
* ── 样例集 —— 三条形状真不同 ────────────────────────────────
|
||||
*
|
||||
* **第一条必须是满载的那条**(2026-08-23,与 E04 同一课):测试台落到一个活动上
|
||||
* 就自动铺 `samples[0]`(activity-run.tsx 的 useEffect),第一条就是人打开这一屏
|
||||
* 看见的那份入参。从前第一条是月度更新、只有两个键,表单大半是空的,看着像没预填。
|
||||
* **样例名一律 ≤14 字**:超了测试台的样例钮只显示「样例 N」,那等于没名字。
|
||||
*/
|
||||
samples: [
|
||||
{
|
||||
name: 'E04 请求定向素材',
|
||||
input: {
|
||||
trigger: 'reactive',
|
||||
requester: 'E04',
|
||||
targetCustomer: 'CU-0142',
|
||||
note: 'E04 关系覆盖与关键人经营需要一份面向 Itelma 的价值传递素材,用于下轮拜访',
|
||||
},
|
||||
expect: { ports: ['content-published'] },
|
||||
},
|
||||
{
|
||||
name: '月度定期内容更新',
|
||||
input: {
|
||||
trigger: 'proactive',
|
||||
reason: 'monthly_update',
|
||||
note: '八月例行内容刷新:优先看版本最旧、最久未更新的那几份工具包',
|
||||
},
|
||||
expect: { ports: ['content-published'] },
|
||||
},
|
||||
{
|
||||
name: '进入新市场 · 俄罗斯',
|
||||
input: {
|
||||
trigger: 'proactive',
|
||||
reason: 'new_market',
|
||||
note: '公司战略进入俄罗斯市场,需要一份中俄双语的公司介绍与能力声明,月底展会要用',
|
||||
},
|
||||
expect: { ports: ['content-published'] },
|
||||
},
|
||||
/*
|
||||
* **一轮里三种形态各要一份** —— 这一条样例是为「资料文件」那一环常备的
|
||||
* (2026-08-23):前三条要的都是成篇的文档,模型一律挑 docx 并不算错,
|
||||
* 于是表格型与网页型那两条路平时走不到。这一条把三种形态摆在同一封需求里:
|
||||
* 对照表是表(xlsx)、落地页是网页(html)、销售速记是短要点(md)。
|
||||
*/
|
||||
{
|
||||
name: '对照表与落地页',
|
||||
input: {
|
||||
trigger: 'reactive',
|
||||
requester: 'FAE 华东',
|
||||
note:
|
||||
'客户要一份 TI 车规 MCU 的替代料参数对照表(逐型号列封装/温度/认证/交期,做成表格发过去);' +
|
||||
'同时官网要上一版车规 MCU 能力的落地页文案(网页直接看);' +
|
||||
'再给销售一页随身速记,三五条要点即可。',
|
||||
},
|
||||
expect: { ports: ['content-published'] },
|
||||
},
|
||||
],
|
||||
|
||||
entry: { file: 'index.js', checksum: 'dev-entry-brand-content-toolkit' },
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* I01 技能 —— 品牌内容生产的方法经验。载方法,不载权力。
|
||||
*
|
||||
* 与剧本的分工:剧本写「这一步做什么、写哪几列」,技能写「为什么这么判、
|
||||
* 拿不准时按哪条走」。剧本改了口径,技能里同一句要跟着改(规范 §5.9)。
|
||||
*/
|
||||
import { skillIdSchema } from '@contracts';
|
||||
import type { Skill } from '@contracts';
|
||||
|
||||
export const CONTENT_SKILLS: Skill[] = [
|
||||
{
|
||||
id: skillIdSchema.parse('brand-content.requirement-mapping'),
|
||||
label: '触发事件映射到内容需求',
|
||||
applicability: '识别本轮内容需求之前,要把触发事件(主动/被动)翻成具体该产什么。',
|
||||
method: [
|
||||
'主动触发按 `reason` 映射:`strategy_shift`/`new_market` 多半对应通用类内容',
|
||||
'(公司介绍、能力声明——面向新市场或新战略方向,受众是还不认识我们的人);',
|
||||
'`tool_outdated` 对应刷新既有工具包(读 `content.asset` 找版本号最旧的那份);',
|
||||
'`monthly_update` 对应例行内容盘点,不是逢月必产新内容——先看有没有真正过期的。',
|
||||
'',
|
||||
'被动触发按 `requester` 映射:E04(关系覆盖)要的是「价值传递素材」,多半是系统方案',
|
||||
'白皮书或案例集,常常带 `targetCustomer`(定向版);T03/T04/E03 目前本仓库未实现,',
|
||||
'只是描述来源,产出物按它们的业务含义(触达材料/攻坚定制内容/方案支撑)对应到',
|
||||
'内容类型即可,不必真的调用它们。',
|
||||
'',
|
||||
'同一轮识别出的多条需求不必是同一类型 —— 一次月度更新可能同时该刷新公司介绍',
|
||||
'和某个过期的选型指南,分开列成两条需求,各自的优先级、目标客户可能完全不同。',
|
||||
].join('\n'),
|
||||
crystallized: [],
|
||||
knowledgeRefs: ['触发事件 × 内容类型映射表'],
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
id: skillIdSchema.parse('brand-content.bt-structure'),
|
||||
label: '按 BT 层级套内容结构、维护多语言',
|
||||
applicability: '市场依据采集完、要草拟内容初稿时。',
|
||||
method: [
|
||||
'内容结构按决策链层级走,不是一刀切:BT-01(采购/供应链)要的是「能不能稳定供货」',
|
||||
'——供货能力、资质文件、渠道政策;BT-02(技术层)要的是「参数对不对得上」——',
|
||||
'技术规格书、替代对照表、认证报告;BT-03(高层)要的是「值不值得投入」——',
|
||||
'系统方案白皮书、参考设计、成功案例。给错层级的内容(拿技术规格书糊弄高层)',
|
||||
'传递不出该传递的价值,人审核这一步会驳回。',
|
||||
'',
|
||||
'多语言不是全都要三语俱全:中文必备;涉及俄罗斯市场或俄语区客户的内容配俄语;',
|
||||
'其余按需求背景判断——面向国际展会或英语区客户才配英语,通用内部资料未必需要。',
|
||||
'语言版本写进同一行的 `language` 列(如"中文 · Русский"),不是同一份内容开多行,',
|
||||
'否则发布确认那一步的验收判据③(多语言版本是否齐全)没法在一行里核对齐不齐。',
|
||||
'',
|
||||
'市场依据读不到相关信号是正常结果,不是异常——不是每条需求都有现成的产业趋势',
|
||||
'支持,读不到就基于已有资产与客户信息拟定,报告里如实说依据来自哪里,不编一条',
|
||||
'不存在的市场信号撑场面。',
|
||||
].join('\n'),
|
||||
crystallized: [],
|
||||
knowledgeRefs: ['BT 层级内容结构', '多语言版本策略'],
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
id: skillIdSchema.parse('brand-content.publish-flow'),
|
||||
label: '版本受控、状态流转不跳步',
|
||||
applicability: '审核通过之后,要把内容拟发布、准备交人签字放行时。',
|
||||
method: [
|
||||
'状态流转是台阶,不是开关:`draft`(草稿)→`reviewed`(已审核)→`published`(已发布),',
|
||||
'每一步都要真的走过——审核通过那一刻先落 `reviewed`,拟发布那一步才落 `published`,',
|
||||
'不要图省事在审核通过的同一拍就直接写成 `published`(那样发布确认那一步的判据①',
|
||||
'核不出「状态流转无误」,因为 `reviewed` 这一站压根没被记录过)。',
|
||||
'',
|
||||
'版本号只在真正定稿时才递增到 v1.0——v0.x 是草稿阶段的内部编号,不对外。',
|
||||
'同一份内容之后若再修订(如客户反馈需要调整),版本号在已发布版本基础上加小版本',
|
||||
'(v1.0 → v1.1),不要退回 v0.x 或另起 content_id——同一份内容资产的版本历史',
|
||||
'要能连起来看,另起编号等于假装这是一份全新的内容。',
|
||||
'',
|
||||
'场景与 BT 标签是发布前必须补齐的两列,不是可选的元数据——它们是 E04/T03/T04',
|
||||
'将来按场景/BT 层级匹配调用这份内容时唯一的检索依据,缺了这两列,发布出去的内容',
|
||||
'实际上没人找得到、等于白产。',
|
||||
].join('\n'),
|
||||
crystallized: [],
|
||||
knowledgeRefs: ['内容状态机', '版本号递增规则'],
|
||||
version: '1.0.0',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* I01 品牌形象与销售工具包管理 · 视觉底(**I01 自己的 PRD 目标件**)。
|
||||
*
|
||||
* `docs/SS01-客户与市场/i01-html/` 下的三份 HTML(执行工单 1、验收工单 1/2)逐字量出。
|
||||
* 颜色变量、卡头渐变、判据框、表格排版与 E04(`crm-relationship-coverage`)的目标件
|
||||
* **逐字节相同**——两份目标件本就是同一套设计语言产出的(SS01 客户与市场同一批交付),
|
||||
* 不是巧合也不是抄近路;`activities/*` 只准 import `@sdk`/`@contracts`,两个活动包之间
|
||||
* 没有代码通道,只能各自持有一份(与 E04 那份原是 A1 家族版面拷贝、后改为自有目标件同理)。
|
||||
*
|
||||
* 版面照目标件分两层:这份文件是**底**(配色变量、卡壳、头/尾、判据框、说明框、表格基础排版),
|
||||
* `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; }
|
||||
|
||||
/*
|
||||
* 资料名 = 可点开的那一格(2026-08-23)。**看得出能点**才会有人点:
|
||||
* 品牌蓝 + 下划虚线 + 手型;悬停实线。点了去哪由外层接(件里一行脚本也没有)。
|
||||
*/
|
||||
.proto-root .jc-fileln { color: var(--brand); border-bottom: 1px dashed var(--brand-soft); cursor: pointer; transition: all var(--t-fast); }
|
||||
.proto-root .jc-fileln:hover { border-bottom-style: solid; background: var(--brand-wash); }
|
||||
/* 空标题不该长得像能点开 —— 那一格根本没有资料 */
|
||||
.proto-root .jc-fileln:empty { border-bottom: 0; cursor: default; background: none; }
|
||||
|
||||
/* 格式徽:摆的是这份资料交出去时的样子(md / html / docx / xlsx) */
|
||||
.proto-root .jc-fmt { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 19px;
|
||||
padding: 1px 7px; border-radius: 5px; font-size: 10px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
|
||||
background: var(--soft); color: var(--ink-3); border: 1px solid var(--line); }
|
||||
.proto-root .jc-fmt.v-docx { background: #eaf1ff; color: #2b5fd9; border-color: #c3d6ff; }
|
||||
.proto-root .jc-fmt.v-xlsx { background: #e9f7ee; color: #1f7a45; border-color: #bfe6cd; }
|
||||
.proto-root .jc-fmt.v-html { background: #fdf0e6; color: #b25e15; border-color: #f3d3b4; }
|
||||
.proto-root .jc-fmt.v-md { background: #f2f3f5; color: #5a6068; border-color: #dfe1e5; }
|
||||
.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 .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-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); }
|
||||
`;
|
||||
@@ -0,0 +1,387 @@
|
||||
/**
|
||||
* I01 品牌形象与销售工具包管理的 HTML 报告模版 —— **活动自己持有的资产**,随包版本冻结。
|
||||
*
|
||||
* 三个局面各一张:
|
||||
* · `requirement-list` —— S1/S2 摆,内容需求清单,可编辑(增删改);
|
||||
* · `draft-review` —— S3/S4 摆,内容初稿,**只读**(业务说明明文"人只审不改" ——
|
||||
* 通过/驳回的按钮在对话区的决策卡上,不在这份件里);
|
||||
* · `publish-confirm` —— S5/T_release 摆,拟发布内容,**只读**,放行后即归档件。
|
||||
*
|
||||
* 版面语言逐字量自 I01 自己的目标件(`docs/SS01-客户与市场/i01-html/` 三份 HTML,
|
||||
* `prd-style.ts` 头注写了它与 E04 目标件"逐字节相同"的原因)。
|
||||
*
|
||||
* 取值三纪律:顶层只取汇总列与单值列;逐条走 `mine:content_brand.<object>` 或
|
||||
* `mine:content.asset`;列名逐字照 `declarations.ledger` 抄。**模版不算不判** ——
|
||||
* 缺必填红带、状态徽、排序位次全部由基座算好的数经 `data-class-of` 投影,判断留在样式表。
|
||||
*/
|
||||
import { PRD_CSS } from './prd-style.ts';
|
||||
|
||||
const D = 'content_brand';
|
||||
|
||||
/* ==================== 绑定期补的样式(数据驱动的开关,判断都在这一层)==================== */
|
||||
|
||||
const BIND_CSS = `
|
||||
/* 空占位三级(全局口径):必填红 / 建议填橘 / 选填灰 —— 值真的空才轮到 */
|
||||
.proto-root .jc-tbl-in td.req > .jc-vcell:empty::after,
|
||||
.proto-root .jc-tbl-in td.sug > .jc-vcell:empty::after,
|
||||
.proto-root .jc-tbl-in td.opt > .jc-vcell:empty::after {
|
||||
display: inline-flex; align-items: center; font-style: normal; font-size: 10px; font-weight: 700;
|
||||
line-height: 1; padding: 3px 7px; border-radius: 5px; border: 1px dashed;
|
||||
}
|
||||
.proto-root .jc-tbl-in td.req > .jc-vcell:empty::after { content: "必填"; color: var(--danger); background: var(--danger-soft); border-color: #f3b6b3; }
|
||||
.proto-root .jc-tbl-in td.sug > .jc-vcell:empty::after { content: "建议填"; color: var(--warn-accent); background: var(--warn-soft); border-color: #f0cf9a; }
|
||||
.proto-root .jc-tbl-in td.opt > .jc-vcell:empty::after { content: "选填"; color: var(--ink-4); background: var(--soft); border-color: var(--line); }
|
||||
/* 归档件 / 只读件里空格子就是「没有」,不再摆「你还能填」的胶囊 */
|
||||
.proto-root .jc-tbl-in td.dimempty > .jc-vcell:empty::after { content: "—"; color: var(--ink-4); border: 0; background: none; padding: 0; }
|
||||
|
||||
/* 缺必填的整行浅红 + 行首红条 —— 由 :has() 认 */
|
||||
.proto-root .jc-tbl-in tbody tr:has(td.req > .jc-vcell:empty) td { background: #fffbfb; }
|
||||
.proto-root .jc-tbl-in tbody tr:has(td.req > .jc-vcell:empty):hover td { background: #fff5f5; }
|
||||
.proto-root .jc-tbl-in tbody tr:has(td.req > .jc-vcell:empty) td.jc-rn { color: var(--danger); font-weight: 800; }
|
||||
.proto-root .jc-tbl-in tbody tr:has(td.req > .jc-vcell:empty) td.jc-rn::after {
|
||||
content: ""; position: absolute; left: 0; top: 4px; bottom: 4px; width: 3px; border-radius: 0 3px 3px 0; background: var(--danger);
|
||||
}
|
||||
|
||||
/* 序号由 CSS 计数器排 */
|
||||
.proto-root .jc-tbl-in tbody { counter-reset: rn; }
|
||||
.proto-root .jc-tbl-in td.jc-rn::before { counter-increment: rn; content: counter(rn); }
|
||||
|
||||
/* 就地改:可改格子渲了只读与输入两份,默认锁着,表上加 on 才换孪生件 */
|
||||
.proto-root .jc-cell { display: none; }
|
||||
.proto-root .jc-tbl-in.on td > .jc-vcell { display: none; }
|
||||
.proto-root .jc-tbl-in.on td > .jc-cell { display: block; }
|
||||
/* 编辑态下必填空格子:红框 + 整行浅红(由 :placeholder-shown 认) */
|
||||
.proto-root .jc-tbl-in.on td.req > .jc-cell:placeholder-shown { border-color: #f3b6b3; background: #fffafa; }
|
||||
.proto-root .jc-tbl-in.on tbody tr:has(td.req > .jc-cell:placeholder-shown) td { background: #fffbfb; }
|
||||
|
||||
/* 铺满工作区,卡壳不再自带外边框(外层容器已经是一张卡) */
|
||||
.jc-stage { max-width: none; padding: 0 0 4px; }
|
||||
|
||||
/* 那道闸 —— 默认「已锁定 + 编辑」,开了变成「取消 / 保存」;只在可改屏现身 */
|
||||
.proto-root .jc-editgate { display: none; }
|
||||
html.editable .proto-root .jc-editgate {
|
||||
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
||||
margin: 14px 0 10px; padding: 9px 12px; border-radius: 9px;
|
||||
background: var(--soft); border: 1px solid var(--line-soft);
|
||||
font-size: 12px; color: var(--ink-3);
|
||||
}
|
||||
.proto-root .jc-editgate-t { flex: 1 1 auto; min-width: 0; }
|
||||
.proto-root .jc-editgate-t b { color: var(--ink-2); font-weight: 700; }
|
||||
.proto-root .jc-egbtn {
|
||||
flex: none; display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
|
||||
border: 1px solid var(--line); border-radius: 7px; padding: 6px 13px;
|
||||
font: inherit; font-size: 12px; font-weight: 700; color: var(--ink-2); background: #fff;
|
||||
transition: border-color var(--t-fast), color var(--t-fast), background var(--t-fast);
|
||||
}
|
||||
.proto-root .jc-egbtn:hover { border-color: var(--brand); color: var(--brand); }
|
||||
.proto-root .jc-egbtn.save { color: #fff; background: var(--human); border-color: var(--human); }
|
||||
.proto-root .jc-egbtn.save:hover { filter: brightness(1.06); color: #fff; }
|
||||
.proto-root .jc-editgate .jc-egbtn.ghost, .proto-root .jc-editgate .jc-egbtn.save { display: none; }
|
||||
.proto-root .jc-editgate.on { background: var(--human-soft); border-color: var(--human-line); color: #7c4a12; }
|
||||
.proto-root .jc-editgate.on .jc-egbtn.open { display: none; }
|
||||
.proto-root .jc-editgate.on .jc-egbtn.ghost, .proto-root .jc-editgate.on .jc-egbtn.save { display: inline-flex; }
|
||||
.proto-root .jc-editgate.on .jc-editgate-t { visibility: hidden; }
|
||||
|
||||
/* 缺必填红带 —— 只在可改屏出,一格不缺自动收(v-0 那一档 display:none) */
|
||||
.proto-root .jc-gapbar { display: none; }
|
||||
html.editable .proto-root .jc-gapbar {
|
||||
display: flex; align-items: flex-start; gap: 8px;
|
||||
margin: 0 0 10px; padding: 9px 12px; border-radius: 9px;
|
||||
background: #fff5f5; border: 1px solid #f3b6b3; color: var(--danger);
|
||||
font-size: 12px; line-height: 1.65;
|
||||
}
|
||||
.proto-root .jc-gapbar b { font-weight: 800; }
|
||||
.proto-root .jc-gapbar span { color: #9a3b36; }
|
||||
html.editable .proto-root .jc-gapbar.v-0 { display: none; }
|
||||
|
||||
/* 那条「怎么改」的提示只在开了闸之后说 */
|
||||
.proto-root .jc-editbar { display: none; }
|
||||
.proto-root .jc-editgate.on + .jc-editbar {
|
||||
display: flex; align-items: flex-start; gap: 8px;
|
||||
margin: 0 0 10px; padding: 9px 12px; border-radius: 9px;
|
||||
background: var(--brand-wash); border: 1px dashed var(--brand-soft); color: var(--ink-3);
|
||||
font-size: 12px; line-height: 1.6;
|
||||
}
|
||||
.proto-root .jc-editbar b { color: var(--ink-2); }
|
||||
|
||||
/* 增删行 */
|
||||
.proto-root .jc-rowdel { display: none; }
|
||||
.proto-root .jc-tbl-in.on td.jc-rn::before { content: none; }
|
||||
.proto-root .jc-tbl-in.on .jc-rowdel { display: block; }
|
||||
.proto-root .jc-tbl-in.on tr.jc-row-gone td { background: #fff6f6; opacity: .55; }
|
||||
.proto-root .jc-tbl-in.on tr.jc-row-gone .jc-cell { text-decoration: line-through; color: var(--ink-3); }
|
||||
.proto-root .jc-tbl-in.on tr.jc-row-gone .jc-rowdel { border-color: var(--danger); color: var(--danger); background: #fff; }
|
||||
.proto-root .jc-tbl-in.on tr.jc-row-new td { background: #f2fbf7; }
|
||||
.proto-root .jc-addrow { display: none; }
|
||||
html.editable .proto-root .jc-tbl-wrap:has(table.on) + .jc-addrow {
|
||||
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
||||
margin: -2px 0 12px; padding: 7px 12px; border-radius: 9px;
|
||||
background: var(--soft); border: 1px dashed var(--line);
|
||||
}
|
||||
|
||||
/*
|
||||
* 统一的枚举标签(优先级 / 内容状态 共用同一套机制):值是机器码,人读名与颜色一律由
|
||||
* data-class-of 投影出的 .v-xxx 决定,不写死在取值那一侧。
|
||||
*/
|
||||
.proto-root .jc-tag { font-weight: 800; }
|
||||
.proto-root .jc-tag::after { content: "待定"; color: var(--ink-4); font-weight: 600; }
|
||||
.proto-root .jc-tag.v-high::after { content: "高"; }
|
||||
.proto-root .jc-tag.v-high { color: var(--danger); font-family: var(--mono); }
|
||||
.proto-root .jc-tag.v-medium::after { content: "中"; }
|
||||
.proto-root .jc-tag.v-medium { color: var(--ink-2); font-family: var(--mono); }
|
||||
.proto-root .jc-tag.v-low::after { content: "低"; }
|
||||
.proto-root .jc-tag.v-low { color: var(--ink-4); font-family: var(--mono); }
|
||||
.proto-root .jc-tag.v-draft::after { content: "草稿"; }
|
||||
.proto-root .jc-tag.v-draft { color: var(--ink-3); }
|
||||
.proto-root .jc-tag.v-reviewed::after { content: "已审核"; }
|
||||
.proto-root .jc-tag.v-reviewed { color: var(--warn-accent); }
|
||||
.proto-root .jc-tag.v-published::after { content: "已发布"; }
|
||||
.proto-root .jc-tag.v-published { color: var(--ok); }
|
||||
.proto-root .jc-tag.v-deprecated::after { content: "已废弃"; }
|
||||
.proto-root .jc-tag.v-deprecated { color: var(--ink-4); text-decoration: line-through; }
|
||||
`;
|
||||
|
||||
/** 三层壳照目标件:少一层样式表全失效 */
|
||||
/**
|
||||
* 三层壳照目标件。卡头/页脚的共用动态位照说明书 §6.2 补齐(2026-08-23):
|
||||
* 收件人(case:owner)、**关联任务**(case:taskId,基座注入)、**步骤**(step,
|
||||
* 每份工单盖哪几步是写清单时定死的静态文案)、页脚**单号**(round_ref)。
|
||||
* 目标件还有一格「最迟交回」—— 本仓没有工单期限这个事实,**不摆假数据**,那一格不做
|
||||
* (与 E04 同一口径)。
|
||||
*/
|
||||
const doc = (input: {
|
||||
tone: 'human' | 'ledger';
|
||||
badge: string;
|
||||
tag: string;
|
||||
kicker: string;
|
||||
title: string;
|
||||
titleRef: string;
|
||||
/** 目标件 side 第二行:静态步骤文案 */
|
||||
step: string;
|
||||
state: string;
|
||||
body: string;
|
||||
foot: string;
|
||||
}): string =>
|
||||
`<style>${PRD_CSS}${BIND_CSS}</style>
|
||||
<div class="proto-root"><div class="app-shell"><div class="jc-stage">
|
||||
<article class="jc-report tone-${input.tone}">
|
||||
<header class="jc-rp-head">
|
||||
<div class="jc-rp-badge">${input.badge}</div>
|
||||
<div class="jc-rp-tit">
|
||||
<em>${input.kicker}</em>
|
||||
<h3>${input.title} <span data-slot="${input.titleRef}"></span></h3>
|
||||
<div class="jc-rp-sub">收件人:<span data-slot="case:owner"></span> · 关联任务:<span class="jc-rp-taskid" data-slot="case:taskId" data-blank></span></div>
|
||||
</div>
|
||||
<div class="jc-rp-side">
|
||||
<span class="jc-rp-tag">${input.tag}</span>
|
||||
<span class="jc-rp-step">${input.step}</span>
|
||||
${input.state}
|
||||
</div>
|
||||
</header>
|
||||
<div class="jc-rp-bd">${input.body}</div>
|
||||
<footer class="jc-rp-foot">${input.foot}</footer>
|
||||
</article>
|
||||
</div></div></div>`;
|
||||
|
||||
const part = (label: string, aside = ''): string =>
|
||||
`<p class="jc-rp-part"><b>【${label}】</b>${aside === '' ? '' : ` —— ${aside}`}</p>`;
|
||||
|
||||
const criteria = (title: string, items: readonly string[]): string =>
|
||||
`<div class="jc-criteria"><div class="jc-criteria-t">${title}</div><ul>${items.map((i) => `<li>${i}</li>`).join('')}</ul></div>`;
|
||||
|
||||
const flow = (text: string): string => `<div class="jc-flow">${text}</div>`;
|
||||
|
||||
/** 页脚右侧照目标件长「工单类型:执行 · <单号>」—— 目标件的 card.id 没有真数据源,摆本轮案号 */
|
||||
const sign = (note: string, tag: string): string =>
|
||||
`<span>${note}</span><span>${tag} · <span data-slot="round_ref" data-blank></span></span>`;
|
||||
|
||||
/** 编辑闸 —— `label` 是闸上那句话(照目标件:「AI 已识别 N 个内容需求,点「编辑」增删改」) */
|
||||
const editGate = (address: string, label: string): string => `<div class="jc-editgate" data-edit-for="${address}">
|
||||
<span class="jc-editgate-t">${label}</span>
|
||||
<button type="button" class="jc-egbtn open" data-edit-open="${address}">编辑</button>
|
||||
<button type="button" class="jc-egbtn ghost" data-edit-cancel="${address}">取消</button>
|
||||
<button type="button" class="jc-egbtn save" data-edit-save="${address}">保存</button>
|
||||
</div>`;
|
||||
|
||||
/* ==================== 局面一 · 内容需求清单(S1/S2)==================== */
|
||||
|
||||
const REQUIREMENT_TABLE = `<div class="jc-tbl-wrap"><table class="jc-tbl-in">
|
||||
<thead><tr>
|
||||
<th style="width:36px;min-width:36px"></th>
|
||||
<th style="width:56px;min-width:56px">序</th>
|
||||
<th class="req" style="min-width:150px">内容类型</th>
|
||||
<th class="sug" style="min-width:170px">需求来源</th>
|
||||
<th class="sug" style="min-width:120px">目标客户</th>
|
||||
<th class="sug" style="min-width:100px">适用场景</th>
|
||||
<th style="width:80px;min-width:80px">优先级</th>
|
||||
</tr></thead>
|
||||
<tbody data-rows="mine:${D}.requirement" data-edit="${D}.requirement"><tr>
|
||||
<td class="jc-rn"><button type="button" class="jc-rowdel" data-edit-del="${D}.requirement" title="撤掉这一行">×</button></td>
|
||||
<td data-h="序"><span class="jc-vcell mono" data-slot="req_seq" data-col="req_seq" data-blank></span></td>
|
||||
<td class="req" data-h="内容类型"><span class="jc-vcell" data-slot="content_type" data-col="content_type" data-blank></span></td>
|
||||
<td class="sug" data-h="需求来源"><span class="jc-vcell" data-slot="source" data-col="source" data-blank></span></td>
|
||||
<td class="sug" data-h="目标客户"><span class="jc-vcell" data-slot="target_customer" data-col="target_customer" data-blank></span></td>
|
||||
<td class="sug" data-h="适用场景"><span class="jc-vcell" data-slot="scene" data-col="scene" data-blank></span></td>
|
||||
<td data-h="优先级"><span class="jc-tag" data-class-of="priority"></span></td>
|
||||
</tr></tbody>
|
||||
</table></div>`;
|
||||
|
||||
export const REQUIREMENT_LIST = doc({
|
||||
tone: 'human',
|
||||
badge: '执行',
|
||||
tag: '执行工单',
|
||||
kicker: 'I01 品牌形象与销售工具包管理 · 执行工单 1/3',
|
||||
title: '需求受理与信息补充',
|
||||
titleRef: 'round_ref',
|
||||
step: '步骤 ① 需求识别与受理',
|
||||
state: `<span class="jc-rp-wait">共 <b data-slot="${D}_requirement_rows"></b> 条需求</span>`,
|
||||
body:
|
||||
part('做什么', '确认本轮要生产的内容') +
|
||||
`<div class="jc-derive">
|
||||
<p><span class="done">AI 已完成</span>:按规则识别本轮内容需求来源(战略调整 / 新市场 / 工具过期 / 使用方请求)与
|
||||
需求类型(公司介绍 / 能力声明 / 销售工具包 / 案例集 / 白皮书等),产出下方需求清单(候选基线)。</p>
|
||||
<p class="jc-note-line">本轮触发:<b data-slot="trigger" data-blank></b> <b data-slot="trigger_detail" data-blank></b></p>
|
||||
<p class="jc-note-line">本轮说明:<span data-slot="note" data-blank></span></p>
|
||||
<p><span class="need">需要你查漏补缺</span>:点「编辑」在清单上增删改 —— <b>加一行 = 补充新需求</b>、
|
||||
<b>删一行 = 排除</b>、<b>改单元格 = 修正</b>;尤其要补「目标客户、适用场景、优先级」这些 AI 规则之外的信息。</p>
|
||||
</div>` +
|
||||
part('交回什么', '核对后的内容需求清单(*为必填)') +
|
||||
`<div class="jc-gapbar" data-class-of="${D}_requirement_missing">
|
||||
<span><b>还差 <span data-slot="${D}_requirement_missing"></span> 行内容类型没填</b>
|
||||
—— <span data-slot="${D}_requirement_missing_keys"></span>。
|
||||
按右上角「编辑」开表,红框那几格就是要填的。补齐了执行单才提交得了。</span>
|
||||
</div>` +
|
||||
editGate(
|
||||
`${D}.requirement`,
|
||||
`<b>AI 已识别 <span data-slot="${D}_requirement_rows"></span> 个内容需求</b>,点「编辑」增删改`,
|
||||
) +
|
||||
`<div class="jc-editbar">
|
||||
<span>加一行 = 补充新需求,删一行 = 排除,改单元格 = 修正 AI 识别的内容。
|
||||
<b>内容类型</b>是必填,缺一条这一单走不到内容创作那一步。
|
||||
改动只落任务草稿层,<b>保存不等于放行</b>。</span>
|
||||
</div>` +
|
||||
REQUIREMENT_TABLE +
|
||||
`<div class="jc-addrow">
|
||||
<button type="button" class="jc-egbtn ghost" data-edit-add="${D}.requirement">+ 加一行(补充新需求)</button>
|
||||
<em>新加行的「内容类型」为必填;「目标客户」填定向客户,通用内容可留空;<b>序号留空即可</b> —— 系统按本轮现有最大值往下续。</em>
|
||||
</div>` +
|
||||
flow('<b>审批 / 提交:</b>本工单只承载内容;「提交回执」请在对话区的【执行卡】中进行。'),
|
||||
foot: sign('I01 品牌形象与销售工具包管理 · 执行工单 1/3 · 需求受理', '工单类型:执行'),
|
||||
});
|
||||
|
||||
/* ==================== 局面二 · 内容初稿(S3/S4,只读)==================== */
|
||||
|
||||
/*
|
||||
* **资料名点得开**(2026-08-23 用户定):标题那一格挂 `data-file-of="content_id"`,
|
||||
* 渲染时换成 `data-file="content.asset/<内容ID>"` —— 点它,工作区就跳到那一份资料的
|
||||
* 查看面(基座按台账那一行现造成 docx / xlsx / md / html 再读回来渲)。
|
||||
* 这份件仍然只做取值:「点了去哪」不在 HTML 里,它只说清「这一格是哪一份资料」。
|
||||
*
|
||||
* 格式一列摆在标题右边 —— 人点之前就知道点开的是张表还是一份文档。
|
||||
*/
|
||||
const DRAFT_TABLE = `<div class="jc-tbl-wrap"><table class="jc-tbl-in">
|
||||
<thead><tr>
|
||||
<th style="width:40px;min-width:40px"></th>
|
||||
<th style="min-width:200px">内容标题</th>
|
||||
<th style="width:70px;min-width:70px">资料</th>
|
||||
<th style="min-width:130px">内容类型</th>
|
||||
<th style="width:120px;min-width:120px">语言版本</th>
|
||||
<th style="width:80px;min-width:80px">BT 标签</th>
|
||||
<th style="width:110px;min-width:110px">品牌调性</th>
|
||||
<th style="width:90px;min-width:90px">状态</th>
|
||||
</tr></thead>
|
||||
<tbody data-rows="mine:content.asset">
|
||||
<tr>
|
||||
<td class="jc-rn"></td>
|
||||
<td class="dimempty" data-h="内容标题"><span class="jc-vcell cust jc-fileln" data-file-of="content_id" data-slot="title" data-blank></span></td>
|
||||
<td data-h="资料"><span class="jc-fmt" data-class-of="file_format" data-slot="file_format" data-blank></span></td>
|
||||
<td class="dimempty" data-h="内容类型"><span class="jc-vcell" data-slot="content_type" data-blank></span></td>
|
||||
<td class="dimempty" data-h="语言版本"><span class="jc-vcell mono" data-slot="language" data-blank></span></td>
|
||||
<td class="dimempty" data-h="BT 标签"><span class="jc-vcell mono" data-slot="bt_tag" data-blank></span></td>
|
||||
<td class="dimempty" data-h="品牌调性"><span class="jc-vcell mono" data-slot="brand_tone" data-blank></span></td>
|
||||
<td data-h="状态"><span class="jc-tag" data-class-of="content_status"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></div>`;
|
||||
|
||||
export const DRAFT_REVIEW = doc({
|
||||
tone: 'ledger',
|
||||
badge: '验收',
|
||||
tag: '验收工单',
|
||||
kicker: 'I01 品牌形象与销售工具包管理 · 验收工单 2/3',
|
||||
title: '内容初稿审核',
|
||||
titleRef: 'round_ref',
|
||||
step: '步骤 ②③④ 产出',
|
||||
state: `<span class="jc-rp-wait">共 <b data-slot="content_asset_rows"></b> 份初稿</span>`,
|
||||
body:
|
||||
part('AI 结论说明', '结论是怎么算出来的') +
|
||||
`<div class="jc-derive">
|
||||
<p><b>AI 已完成的计算</b>:读取 L-01 宏观市场信号(产业趋势、市场依据),按 BT 层级草拟内容初稿——
|
||||
BT-01 侧重供货能力 / 资质文件、BT-02 侧重技术规格书 / 替代对照表 / 认证报告、
|
||||
BT-03 侧重系统方案白皮书 / 参考设计 / 成功案例;维护多语言版本(中 / 英 / 俄)。</p>
|
||||
</div>` +
|
||||
part('验收判据', '你据此判断「通过 / 驳回」') +
|
||||
criteria('判断这份内容初稿是否合格,请逐项核对', [
|
||||
'<b>品牌调性是否一致</b>:是否符合品牌"气质"标准(专业 / 可信赖 / 高效 / 本土化);',
|
||||
'<b>技术内容是否准确</b>:规格、数据、认证信息有无错误;',
|
||||
'<b>合规与多语言校对</b>:合规检查、多语言校对是否通过。',
|
||||
]) +
|
||||
part('AI 产出的内容初稿', '只读展示') +
|
||||
DRAFT_TABLE +
|
||||
flow('<b>审批:</b>本工单只承载内容;「通过 / 驳回」请在对话区的【验收卡】中进行。通过后进入发布,驳回由 AI 重做。'),
|
||||
foot: sign('I01 品牌形象与销售工具包管理 · 验收工单 2/3 · 内容初稿审核', '工单类型:验收'),
|
||||
});
|
||||
|
||||
/* ==================== 局面三 · 拟发布内容(S5/T_release,只读)==================== */
|
||||
|
||||
const PUBLISH_TABLE = `<div class="jc-tbl-wrap"><table class="jc-tbl-in">
|
||||
<thead><tr>
|
||||
<th style="width:40px;min-width:40px"></th>
|
||||
<th style="min-width:110px">内容 ID</th>
|
||||
<th style="min-width:200px">标题</th>
|
||||
<th style="width:120px;min-width:120px">语言版本</th>
|
||||
<th style="width:80px;min-width:80px">版本号</th>
|
||||
<th style="min-width:100px">适用场景</th>
|
||||
<th style="width:80px;min-width:80px">BT 标签</th>
|
||||
<th style="min-width:150px">状态流转</th>
|
||||
</tr></thead>
|
||||
<tbody data-rows="mine:content.asset">
|
||||
<tr>
|
||||
<td class="jc-rn"></td>
|
||||
<td class="dimempty" data-h="内容 ID"><span class="jc-vcell mono" data-slot="content_id" data-blank></span></td>
|
||||
<td class="dimempty" data-h="标题"><span class="jc-vcell cust jc-fileln" data-file-of="content_id" data-slot="title" data-blank></span></td>
|
||||
<td class="dimempty" data-h="语言版本"><span class="jc-vcell mono" data-slot="language" data-blank></span></td>
|
||||
<td class="dimempty" data-h="版本号"><span class="jc-vcell mono" data-slot="version" data-blank></span></td>
|
||||
<td class="dimempty" data-h="适用场景"><span class="jc-vcell" data-slot="scene" data-blank></span></td>
|
||||
<td class="dimempty" data-h="BT 标签"><span class="jc-vcell mono" data-slot="bt_tag" data-blank></span></td>
|
||||
<td data-h="状态流转"><span class="jc-vcell dim">已审核</span> → <span class="jc-tag" data-class-of="content_status"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></div>`;
|
||||
|
||||
export const PUBLISH_CONFIRM = doc({
|
||||
tone: 'ledger',
|
||||
badge: '验收',
|
||||
tag: '验收工单',
|
||||
kicker: 'I01 品牌形象与销售工具包管理 · 验收工单 3/3',
|
||||
title: '发布确认',
|
||||
titleRef: 'round_ref',
|
||||
step: '步骤 ⑤ 发布入库',
|
||||
state: `<span class="jc-rp-wait">共 <b data-slot="content_asset_content_status_published"></b> 份拟发布</span>`,
|
||||
body:
|
||||
part('AI 结论说明', 'AI 做了什么') +
|
||||
`<div class="jc-derive">
|
||||
<p><b>AI 已完成</b>:内容初稿通过审核后,拟正式发布 —— 更新版本号、状态流转(待审核 → 已审核 → 已发布)、
|
||||
登记适用场景与品牌调性标签,写入 L-05 品牌内容库。本轮产出:<span data-slot="summary" data-blank></span></p>
|
||||
</div>` +
|
||||
part('验收判据', '你据此判断「通过 / 驳回」') +
|
||||
criteria('判断这次发布是否无误,请逐项核对', [
|
||||
'<b>版本号与状态是否正确</b>:版本号已更新、状态流转(草稿 → 已审核 → 已发布)无误;',
|
||||
'<b>场景与 BT 标签是否登记完整</b>;',
|
||||
'<b>多语言版本是否齐全</b>。',
|
||||
]) +
|
||||
part('拟发布的内容', '只读展示') +
|
||||
PUBLISH_TABLE +
|
||||
flow('<b>审批:</b>本工单只承载内容;「通过入库 / 驳回」请在对话区的【验收卡】中进行。通过即落账写入 L-05,本次内容生产结束。'),
|
||||
foot: sign('I01 品牌形象与销售工具包管理 · 验收工单 3/3 · 发布确认', '工单类型:验收 → 完结'),
|
||||
});
|
||||
Reference in New Issue
Block a user