原型 V5 · 同一个系统的三个形态

不是三个产品
是同一个 MediaBrandHub,在三个地方各长一遍

三个形态 = PC 浏览器 / 手机 APP / 微信小程序。PC 管干重活,APP 管随时和随口,小程序管转给别人看。
上一版我把第三个位置放成了 H5 —— 那不是端,是渲染层:APP 里跑的是它,小程序里跑的也是它, 微信里点开的分享页还是它。把它算成一端,等于把「纸质」算成一种书。
证据不是这句话,是下面这几个文件:一份内容 system.js,一份数据 app.js, 一套交互 shell.js(语音也在这层)。三个形态都读它们。改一处,三端同时变。

对齐 本原型已逐条反写回《全局终极优化蓝图 V1.3》—— 三形态定义见 §1.4,语音规范见 §7.6, 两张纸单据见 §11.6,验证状态与未证明项见 §16。 蓝图里写的,这里跑得起来;这里没跑通的,蓝图 §16.3 已如实标为「未证明」。
一份内容

system.js

13 个页面的标题、正文、字段、按钮文案,只在里面出现一次。三个形态都从这里取,没有任何一页在自己文件里重写一遍。

一份数据

app.js

订阅 ¥598(299×2 月)、附件一 ¥8,292(13 位真实媒体 + 自有内容)、词池 40(32+8)、工单 8、链接 8、DiD +6.4pp。三形态读同一个对象,不各算各的。

一套交互

shell.js

勾清单、点条款、手写签名、付两笔钱、按住说话,三种形态点的是同一段代码。不会有「电脑上能签、手机上签不了」。

三个形态

三个形态,同一个入口:一个对话框
PC 端
形态一 · PC 浏览器

桌面 1440 · 干重活

首页就是一个对话框,右边一栏实时显示 Agent 干到哪了。资料确认前,左边 13 步是灰的 —— 想跳也跳不过去。表格是真的表格,一屏能看 8 行清单。

对话式入口真表格13 页全量鼠标手写签名
打开 PC 端 →
APP 端
形态二 · 手机 APP

iOS / Android · 随时随口

全屏对话,输入框贴在 tab 栏上方,麦克风就在输入框里。老板在饭桌上想说什么,掏手机说一句就行 —— 不用先学会填表。

全屏对话输入框带麦克风系统推送13 页全量
打开 APP 端 →
小程序端
形态三 · 微信小程序

微信内 · 转给别人看

同一个对话框,换成微信绿。资料没确认前,底部 5 个 tab 也是灰的。不用下载,转发一条卡片就能打开。

胶囊 + 原生 tab同一段对话微信绿
打开小程序端 →
运营
后台:客户看不到的那一套 不是第四个形态 —— 是 PC 上换个账号登录,权限跟着账号走。12 屏:工作台 / 客户 / 知识库审核 / 采集与引擎 / 媒体库存 / 工单台 / 链接与回收 / 报价单生成 / 合同与收款 / 效果报告 / 红线拦截日志 / 配置与权限矩阵。
打开运营后台 →
9 角色
系统里一共 9 个角色,不是一个后台一种人 平台内部 5:交付主管 / 合规审核 / 商务 / 媒体运营 / 管理者。平台外部 3:渠道商 / 交付服务商 / OEM 租户。客户端 1:客户(三形态同一份内容)。后台一共 47 屏,同一个壳按角色切菜单 —— 点进去看到每个角色的「界面里根本不存在什么」和「最容易犯的错」。
看 9 个角色 →

首页:只做一个对话框

一个字段都不留 —— 客户说一句话,剩下的 Agent 干

客户说不出「40 个词、8 成干预、2 成留出」,他只会说「想让搜种植牙的人找到我」

表单逼他先学会我们的术语;对话不用,翻译是我们的活。所以第 1 步没有公司名输入框、没有需求下拉、没有行业选择 —— 只有一个输入框、一个麦克风、一个发送键。下面四张图不是设计稿,是原型跑出来的四步(截自 APP 端,PC 与小程序同源)。

① 回读
把一句话拆成 5 个槽位摆回去:主体 / 行业 / 地域 / 目标 / 周期。推断的那一项标「推断,可改」,不冒充查证过。
② 采集
7 类公开源逐条打勾,哪个超时单独标 —— 不转一个圈糊弄 40 秒。超时的源能单独重试,不用整批重跑。
③ 确认
分两段摆:我核得动的 5 项/我不敢自己拍板的 3 项。混在一起,出了事责任全在平台。
④ 补缺
每项写清「为什么缺 / 怎么补 / 补不了怎么办」,三种补法:上传凭证、说一句、这条不补。
三条路径都在同一段对话里走通: 资料齐全 → 确认完直接进第 2 步;资料不齐 → 逐项说清缺什么怎么补;客户说「你说的不对」→ 就地改,不把他踢回表单重填。
六条硬约束里最要命的两条: ① 采不到就标「缺」,不许用模型编一段填上 —— 编出来的「累计服务 3 万人次」,将来是客户拿去见监管的数字; ② 资料没确认,「进第 2 步」按钮不存在(不是置灰),左侧 13 步和底部 5 个 tab 一起上锁 —— 灰按钮会被「能不能帮我点一下」绕过,不存在的按钮绕不过。
演示路径:点任意一个示例 → 看 Agent 回读 → 看 7 个源逐个打勾 → 点「都对,往下走」→ 补一个缺口 → 点「跳过剩下的 2 项」→ 进第 2 步。

语音输入:能调原生,就别自己造

两条通道,不是一条:系统听写打底,应用内语音键只管要引导的地方

先把三种「原生」分清,别把一条路当成三条

① 系统键盘听写。iOS / Android 键盘右下角那颗麦克风、Windows 的 Win+H、macOS 双击 Fn —— 这是操作系统级的能力,任何一个输入框里都能用,APP 和小程序一行代码都不用写,也不用申请麦克风权限(权限在输入法那边)。
② 平台 ASR。iOS SFSpeechRecognizer、Android SpeechRecognizer、小程序 wx.getRecorderManager + 后端识别(或微信同声传译插件)—— 这是应用级能力,能带上下文、能限时长、能直接落成字段。
③ Web Speech API。只有桌面 Chrome / Edge 靠得住。iOS Safari 至今没有实现(WebKit 官方标为不计划支持),微信内置浏览器直接禁用 —— 所以 H5 那条路本来就走不通,这也再一次说明 H5 只能当渲染层,不能当形态。

PC · 居中弹窗(演示态)
原型里这个是自绘面板,演的是「收音 → 转写 → 确认 → 落字」这条链路。真实版在 PC 上第一选择是让用户直接按 Win+H,我们一个像素都不用画。
APP · 底部升起(演示态)
手机上有 90% 的场景用系统键盘那颗麦克风就够了。只有在「补这一条缺口」这种要引导的字段上,才值得自己弹面板。
小程序 · 底部升起(演示态)
小程序拿不到系统 ASR,只能录音上传后端,或用微信同声传译插件。这是三端里唯一必须自己写的一段。
两条通道怎么分工(这条分工比面板长什么样重要得多):
A · 开放式输入 → 走系统听写。首页那一句话、补充说明、随便说两句。客户已经在微信里用惯了这套,学习成本为 0,我们一个字节的 ASR 成本都不付。
B · 结构化字段 → 走应用内语音键。缺口补录、证照编号、日期、金额。因为这里必须知道「现在问的是哪一项」—— 系统听写给你「浙卫医证字二零一九第三三一号」,字段里要的是 2019 第 0331 号,还得当场校验格式。这一步只有应用内做得到。
所以「语音只有一条路」是错的。真正的判断是:能不自己造就不自己造,只在必须知道上下文的地方才接管。
三端各自的实现路径(同一件事,四条不同的技术栈 —— 这处不能同源):
形态首页那句话(开放式)缺口字段(要引导)坑
PC 浏览器 系统听写 Win+H / 双击 Fn,零代码 桌面 Chrome / Edge 的 Web Speech,或录音上传后端 国内 Chrome 的 Web Speech 走境外云,基本不可用;必须 HTTPS + 用户手势触发
PC 客户端 同上 同上,可加本地 ASR(离线、不进云) Electron 要显式声明麦克风权限,否则静默失败
iOS APP 系统键盘听写,零代码 SFSpeechRecognizer,支持离线 Info.plist 两个权限文案缺一个,一调用就崩
Android APP 系统键盘听写,零代码 SpeechRecognizer 或厂商 ASR 国产 ROM 无 GMS 时 SpeechRecognizer 可能直接不可用,必须能降级到录音上传
微信小程序 键盘上的语音键,零代码 wx.getRecorderManager + 后端 ASR;或微信同声传译插件 拿不到系统 ASR;插件有每日免费额度,超出按次付费;真机与模拟器录音格式不一致
H5(微信内) 键盘上的语音键 录音上传后端(MediaRecorder) Web Speech 在微信内置浏览器被禁用,别指望
上表按各平台官方文档与公开资料整理,未在真机上逐个实测;小程序插件额度与计费以微信官方当期说明为准。
哪一段能共用,哪一段不能 —— 这处我之前说错了,现在改过来:
共用的是落字之后:文本一旦进了输入框,走的都是同一个 input 事件 —— 发送键点亮、回车能发、缺口字段校验、Agent 解析,三端一字不差。
不能共用的是录音与识别:那一段必须按平台各写一遍,因为底层 API 完全不同(SFSpeechRecognizer / SpeechRecognizer / 小程序录音 / 系统键盘)。 原型里的「三端只差一个 CSS 类」只对面板长什么样成立,对怎么把声音变成字不成立 —— 之前把这两件事混着说了,是错的。
所以三端的验收标准也不一样:PC 要验「Win+H 能不能落进输入框、落完能不能直接发」;APP 要验「权限被拒时能不能提示并降级到键盘输入」;小程序要验「额度耗尽时会不会静默失败」。

同一页,三种排布

三张图都截自第 8 步「执行清单」,没有一页是单独做的

第 8 步 · 一笔钱,两张纸

三张图里的金额、行数、文案完全一致
PC · 真表格
8 行一屏看完,「进哪张纸」单独一列,订阅行显示锁标记不可拆。
APP · 卡片列表
同一份表数据,窄屏渲染成 8 张卡,每张标题是动作名。
小程序 · 卡片列表
再紧一点,字号和间距按小程序规范收,主色换成微信绿。
这三张图出自同一个函数。 system.js 里第 8 页的表只定义了一次(表头 + 8 行数据), mode==='pc' 时输出 <table>,否则输出卡片列表。宽屏和窄屏的差别只有渲染方式, 数据一个字都没变,所以你不可能遇到「电脑上 8,890、手机上 8,236」。

什么一样,什么不一样

一样的是系统,不一样的是载体
维度PC 浏览器手机 APP微信小程序
视口1440 三栏390 单列 + 手机框375 单列 + 胶囊
导航左侧 13 步列表(未确认前整列灰掉)原生导航 + 13 段进度(第 1 步不显示)+ 底部 5 tab(未确认前灰掉)胶囊 + 底部 5 tab(未确认前灰掉)
表格真表格卡片列表卡片列表
页数13 页全量13 页全量高频 5 个入口
独有能力鼠标手写签名、一屏对账系统推送、悬浮语音键、无浏览器地址栏转发即开、无需下载
语音输入三端都有;面板与状态机同源,录音与识别各写一遍(全系统唯一不同源的模块)
配色商务蓝商务蓝微信绿
文案、字段、按钮完全相同(同一份 system.js)
金额、词数、工单数完全相同(同一份 app.js)
点击后的结果完全相同(同一套 shell.js)
签名、付款、勾清单三端都能做,不是「手机上只能看」

文件就这二十七个

六个系统 + 三个客户端 + 八个后台 + 七个取数与检查 + 三个状态件
// 系统(三形态共用,改这里三端一起变) app.js 数据:订阅、清单、合同、工单、链接、DiD;媒体金额从媒体方案算 system.js 内容:13 页的语义块 + 会话渲染器 + 表格↔卡片的切换 shell.js 交互:首页会话状态机(回读→采集→确认→补缺→放行)+ 翻页、勾清单、签名、付款、语音 base.css 视觉:块的样式 + 语音面板,不含布局 state.js 空 / 加载 / 出错:五个出口 + 骨架 + 局部刷新;缺参数挂红字,不静默 state.css 状态块样式:五种空各一色,一套代码吃客户端和后台两种密度 media.js 媒体方案:13 位,由下面的脚本从媒介优选真实库存算出(自动生成) // 形态(只有布局和配色不一样) pc.html 三栏 + 左导航 + 右摘要 app.html 手机框 + 状态栏 + 原生导航 + 分段进度 + 悬浮语音 mini.html 手机框 + 微信胶囊 + 原生 tab + 微信绿 // 后台(一个壳,8 个角色,47 屏 —— 权限跟着账号走,不跟着设备走) admin.html 壳 + 12 屏(交付主管)+ 角色切换;URL 带 #r=角色&p=页面 可直达 roles.js 8 个角色:能做什么 / 不能做什么 / 界面里「根本不存在」什么 / 最容易犯的错 ui.js 公共小件:esc / kpi / bar / 金额 —— 三份转义规则迟早不一致,所以只留一份 pg-internal.js 内部 4 角色 20 屏:合规 5 / 商务 5 / 媒体 5 / 管理者 5 pg-partner.js 外部 3 角色 15 屏:渠道 5 / 服务商 5 / 租户 5(数据靠租户边界,不靠权限) admin.js 运营数据:6 单、今日必办、审核队列、拦截日志、渠道分账 —— 金额全部从 app.js 读,不另存 admin.css 后台布局:13px 基准,密度比客户端高,刻意做得没那么好看 roles.html 9 个角色总览:三层结构 + 9 张卡 + 8×8 权限矩阵 + 三条结构原则 states.html 状态规范:四种空并排 + 加载两态 + 错误三级 + 判定表 + 11 处落点(组件实时渲染) select_media.py 取数:连媒介优选索引库,按四层规则选媒,重跑一次结果一样 shot.py 出图:三端 × 6 个会话阶段,看「长得像不像」 adm_shot.py 出图:8 角色 47 屏各一张 + 11 张状态图(adm_shot.py states) index.html ← 你正在看的这一页 verify.py 回归:三形态各 70 + 跨形态 20 + 后台 37 + 8 角色 28 + UI 24 + 状态 38,共 357 条 audit.py UI 体检:溢出 / 文字裁切 / 点击区过小 / 正文对比度,22 屏一次扫完 audit_steps.py UI 体检(客户端):13 步逐页扫,三端各跑一遍 guard.py 红线:四条反向测试,11 条断言 —— 找不到违规证据才算拦住 // 归档(不是交付物) _legacy/ V3 三角色版、H5 网页兜底版 —— 留着对照,不对外

媒体不是编的,是查出来的

13 位,每一位都能按编号回线上库存
上一版这 12 个媒体位是我编的:口腔界、健康时报、丁香园…… 名字都像真的,报价也是我拍的。 客户只要问一句「这个号在你们库里编号多少」,就穿帮。
现在改成从媒介优选真实库存取数:全量 246,293 条,其中 GEO 可发 1,408 条(全是新闻源渠道), 按四层规则算出 13 位,合计 ¥7,092。
层选媒逻辑本次选的小计
A 行业垂媒名称含医疗健康词 + 全国 + GEO 可发,同站去重后按报价降序取 2医药卫生网 ¥1,410 · 亲贝网健康 ¥422¥1,832
B 本地新闻源省级=浙江 + GEO 可发,按报价降序取前 5(全省只有 8 个位)浙新网 ¥247 · 新蓝网 ¥163 · 浙江传媒网 ¥150 · 搜狐浙江号 ¥113 · 义乌网 ¥75¥748
C 权威门户央媒/党媒关键词 + 报价 ≥600,取最贵 1 位 + 覆盖平台最多 1 位中国日报网 ¥1,653 · 新华报业网 ¥938¥2,591
D 全国中坚全国 + 300≤报价<800,按覆盖 AI 平台数降序、同覆盖取低价,取 4 位中国教育在线 · 凤凰网科技 · 医联媒体 · 新尧网¥1,921
自有账号内容资产 30 条 × ¥40(不是媒体采购,是内容生产)¥1,200
附件一合计(13 位媒体 ¥7,092 + 自有内容 ¥1,200)¥8,292
两个必须提前说清的限制:
① 这 13 位的收录承诺大多是「不包网页收录」—— 我们只能按发布履约,不能承诺被搜索引擎抓走。
② 全库 24.6 万条里 GEO 可发只有 1,408 条且全是新闻源,浙江全省只有 8 个位。不是不想给更多,是库里就这么多。
复跑一次结果一样:python select_media.py —— 排除软文包 / 港澳台 / 同站重复各多少条,脚本都会打印出来。

5 分钟怎么演示

从说一句话,走到一张能签的单,再回到一个敢写进合同的数
1
第 1 步:点任意一个示例问法,或自己说一句Agent 会把它拆成 5 个槽位摆回给你看 —— 推断的那项标着「推断,可改」。这一屏没有表单,一个字段都没有。
2
看 7 个源逐个打勾不是转圈。超时的那个源单独标红,能单独重试 —— 整批重跑会把已经拿到的 43 条再抓一遍。
3
点那个超时的源重试7 个源里招聘那条超时,点「重试」单独跑,不用整批重来 —— 整批重跑会把已经拿到的 43 条再抓一遍。
4
点「都对,往下走」,处理 3 个缺口每个缺口都写了「为什么缺 / 怎么补」:能上传的上传,能说一句的点麦克风直接说,补不了的选「这条不补」。跳过的会记成「未提供」,不替你编。
5
第 8 步勾掉一行媒体只有附件金额变,订阅 ¥598 一分不动;订阅那三行是锁死的,点一下会提示「订阅是整包」。
6
第 10 步手写签名 + 勾同意两个条件缺一个,按钮都不放行。合同价款只写 ¥598,媒体 ¥8,292 挂在附件一。
7
第 11 步先只付技术服务费合同生效、P1 开工;跳到第 12 步,P2/P3 五个工单标「待媒体款」并置灰,顶部黄条写明原因。再回来付媒体款,黄条消失。
8
换一个形态,翻到同一页PC 上是表格,APP 和小程序上是卡片,金额、行数、文案一模一样 —— 这一条不是靠我解释,是 verify.py 跑出来的。

后台:客户看不到的那一套

12 屏起 · 不是第四个形态,是 PC 上换个账号登录(8 个角色共 47 屏)
客户在三个形态里看的是同一件事:我的单走到哪了。运营看的是另一件事:今天哪几件不做会赔钱。 这两件事不能合成一个界面 —— 堆在一起,客户端就变成后台,客户第一眼就跑了;后台也变成客户端,一屏扫不完。
所以后台没做成第四个端。它是 PC 形态下运营账号登录后看到的那套菜单,权限跟着账号走,不跟着设备走: 运营在手机上打开 APP,一样能审批、催款、结单,只是排布换成卡片。
① 工作台 · 只回答一个问题:今天不做哪几件,会赔钱
运营后台工作台
六条待办不是人工录的,是按「超时时长 × 影响金额 × 卡住下游数量」算出来的,按赔钱多少排序。 每一条都必须写「不处理会怎样」——写不出来就不许进这个列表。后台最怕的不是漏提醒,是「什么都提醒一遍」,那样等于什么都没提醒。
② 知识库审核 · N 类没有「通过」按钮
知识库审核
带数字的说法(专利数、产值、服务人次、就业率、持证比例)必须有能点开的证据。 N 类那五条的按钮是「要求补证 / 驳回并删除」——「通过」这颗按钮压根没渲染,不是置灰。
③ 报价单生成 · 媒体行拖不进合同价款区
报价单生成
试一下就知道:把 Q4(行业垂媒 2 位)拖进左边,会被拒收并告诉你为什么。 这不是交互没做好,是 R1。它要是能拖进去,合同金额就会变成 8,890,客户签字时看到的是错的数。
屏只回答一个问题不做会怎样
工作台今天哪几件不做会赔钱六件事平铺成六十件,人就会只做最吵的那件
客户与项目这一单卡在哪一步合同价款和附件一并成一列,谁都会顺手加成一个数
知识库审核这条能不能进报告口述数字写进报告 = 虚假宣传,举证责任在平台
采集与引擎哪个源坏了、哪个引擎偏了用一个偏差 31% 的引擎出报告,4 引擎会悄悄变成 3 引擎
媒体库存这 13 位锁给谁了、什么时候释放位锁着收不到钱,别人也买不走 —— 待回款是库存损耗,不是财务问题
工单台哪个工单今天到期SLA 按天算,所有工单都在最后两小时一起爆
链接与回收8 条链接还剩几条活着N 行 = N 链接的验收对不上,客户有理由扣这一行的钱
报价单生成每一行该进哪张纸媒体钱混进合同价款,签的是一个客户没同意过的数
合同与收款两笔钱各自收到哪一笔钱算两次,或者两笔钱合成一笔催
效果报告这个数敢不敢写进合同假设不披露,Ci 不写,客户拿 4 引擎的截图来对,对不上
红线拦截日志四条红线今天真被触发过吗没被触发过的红线,只是写在 PPT 上的四句话
配置与渠道什么能改、什么改不了N 类规则能被关掉,前面 11 屏全部作废

四个「会咬人」的地方

看起来像没做完,其实是故意的
1
N 类事实没有「通过」这个动作不是按钮置灰 —— 置灰的按钮能被开发者工具解开。字段不存在强于校验不通过:没有 evidence_uri 的记录,连「提交」这个函数都进不去。
2
媒体行拖不进合同价款区拒收的时候必须说理由,理由要说人话(「它要是能拖进去,合同金额就会变成 8,890」)。不说理由的拦截,第二天就有人找你要「临时的例外」。
3
解冻引擎要先核对三件事冻结随时可以点;解冻不行,要先确认「连续两周偏差 <10%、模型版本未变、接口格式未变」。单向容易,反向留痕 —— 三个月后有人问「当时为什么用了 31% 偏差的引擎」,答案在记录里。
4
结单按钮会去查链接查不到验收链接,结单点不动。清单 8 行 = 8 个工单 = 8 条链接,少一条就不算交付 —— 这个检查写在按钮里,不写在制度里。
后台也是被断言管着的。verify.py 里新增 37 条后台断言:12 屏都渲染得出来、金额与三端同源(598 / 8,292 / 8,890 一分不差)、 N 类行里查不到 data-do="pass"、拖拽后合同区金额不变、条款区不出现 8,890、冻结引擎不进合成、13 位库存合计 7,092。 全过才算交付 —— 后台最怕的不是难看,是点下去没反应还没人发现。

9 个角色,不是一个后台一套人

平台内部 5 + 伙伴 2 + 租户 1 + 客户端 1 · 后台共 47 屏

最容易做错的是「给别的角色做同一套页面、少给几个按钮」。那不是角色,那是同一份原型的阉割版。 真正的差异在信息层级:同一笔媒体款,交付看到「位锁着,占库存」,媒体看到「空转 4 天,亏 ¥84」, 商务看到「客户还差 ¥6,180 没付」,管理者看到「应收 33,872、实收 9,488」。四个数字都对,问的不是同一件事。

平台内部 · 5 个
交付主管 · 合规审核 · 商务经理 · 媒体运营 · 管理者
靠权限位划分。配错会静默泄数据,没人会发现。
平台伙伴 · 2 个
渠道商 · 交付服务商
靠合同关系划分。配错当场查不到数据,当天报障。
OEM 租户 · 1 个
租户管理员(白标)
外壳是他的,内核是平台的。N 类规则连开关都没有。
客户端 · 1 个
客户(PC / APP / 小程序)
三形态、一个角色。换设备重做布局,换角色重做权限。
这张权限表里,横线比勾重要。 勾说明谁有权限;横线说明谁连提出这个动作的入口都没有。 看「放行 N 类」那一列:七个角色是「不存在」,只有管理者是「双签」—— 没有一个是「✓」。 而且表里没有「超级管理员」这个角色:加进去,前面八张卡就全白做了,因为所有绕不过去的门最后都会通向那个账号。

看完整的 9 张角色卡 →
① 合规审核 · 12 条待审,按等了多久排
合规审核待审事实
N 类那九条只有「要求补证 / 驳回并删除」。「通过」不是置灰,是渲染分支里没有这个动作。 左侧导航里也没有「钱」这一组——看得见金额的审核人,判断会被「这单值多少」影响。
② 管理者 · 六件待批,两件要双签
管理者审批中心
AP-18 是 N 类放行,全部审批里唯一「不可恢复」的一项。他点完只到「待会签」, 第二个人必须是合规负责人。不是不信管理者,是不信「任何一个人在任何时候」。
③ OEM 租户 · 四项「无控件」
OEM 内核边界
「无控件」和「不可用」是两回事:不可用意味着有个东西在那里只是关了;无控件意味着它不存在。 N1–N16 规则开关这一行,是租户最可能去要的,也是最不可能给的。

界面质量:量出来的,不是说出来的

四类能判对错的问题 · 61 页全扫

「看着还行」不是标准。这轮把四件能判定对错的事交给脚本去量:元素有没有超出容器、 文字有没有被裁掉半截、点击目标够不够大、正文对比度够不够。前两样肉眼勉强能看出来, 后两样截图上看不出来 —— 3.2:1 的灰和 4.6:1 的灰,在截图里都是「淡淡的灰」, 但一个在手机户外屏上根本读不出来。

项标准修前修后怎么处理的
横向溢出不出现横向滚动 00 本来就过。1024 / 1280 / 1440 / 1920 四个宽度都测了
文字被裁切不出现省略号以外的截断 00 本来就过
点击目标≥ 30 × 22 px 550 行内按钮只有 16px 高(看原文 / 溯源 / 移出 / 不要这行)。用 padding 撑到 26px 再负 margin 抵掉,行高不变
正文对比度≥ 4.5 : 1 2430 次要文字色 #5F6B7C 在浅底上只有 3.2:1;状态色(绿 / 橙 / 红)当 11px 文字用也不够。统一按「在自身浅底上仍 ≥4.5」重定
键盘焦点Tab 走一圈能看见在哪 0 处全覆盖 全站只有输入框有 focus 样式,按钮和导航项一个都没有 —— 补 :focus-visible,深色底换白框
修前是三端 39 步 + 后台 22 屏逐页扫出来的累计值,不是抽样。修后 61 页全为 0,并抽 6 页写进 verify.py 常驻守着。
01为什么进回归

这三样会悄悄回退,而回退之后截图上看不出来

对比度、点击区、溢出,改一次配色或加一个功能就可能坏。坏了以后页面照样能跑、截图照样好看, 只有真正用的人会觉得「字有点糊」「这个按钮怎么老点不中」。

所以 verify.py 里加了 24 条:6 个代表页 × 4 项,全 0 才算过。 少了这一层,前面跑过的脚本结论就只是「那一次是对的」。

抽查而不是全扫

换色是全局变量,一处改全站变,抽样足够抓回归。全量 61 页跑一次要十几分钟,塞进每次回归没人愿意等,最后一定被跳过。

行内链接为什么不算

正文里「在 APP 上看…」这类链接,高度被行高约束,放大就破坏排版。WCAG 2.5.8 对此有明确豁免,脚本里也按这条判据跳过 —— 不是为了让数字好看而改判据。

02顺手清掉的

三处不是审美问题,是两个地方说的不一样

① 报价页有一行「本单合计(仅供参考)¥8,890」。合同页却写着「任何条款不含 8,890」。 同一个系统里同一个数,一页能出现一页不能,客户一定问。改成「两笔相加 · 不设这个数」并说明为什么不设 —— 「仅供参考」四个字在报价单上没有效力,反而暗示「可以这么算」。

② PC 窄屏(≤1280)右栏直接 display:none。金额和进度就这么没了,客户找不到自己要付多少钱。 改成底部一条横向数字带,只留最关键的两块。

③ 底部一条 38px 深色演示通栏压在客户界面上。会被当成产品功能。它其实和左导航底部那组是同一套入口 —— 删掉重复的,把「总览」并进左导航。演示控件不该占产品的位置。

同一份判据

三处都不是「不好看」,是两处说法打架。这类问题改配色改不掉,只能把其中一个删掉或改对。

没有数据的时候,页面长什么样

61 页里原来只有 1 处空态 · 现在 11 处,全都点得出来

上一轮量的是「看得见的东西正不正」。这一轮补的是另一层:没有数据的时候,这一屏长什么样。 体检时数了一遍,全站 61 个页面里只有 1 处真正的空态 —— 其余要么是空白的白卡片,要么干脆什么都不渲染。 这不是忘了做,是「先做有数据的样子」更容易:数据一多截图就好看,评审就容易过。 但真上线第一天,运营面对的全是空的。

这一层只有一个核心判断: 四种「空」长得一模一样,用户下一步却完全不同。把它们混成一句「暂无数据」, 是这个系统里最贵的一种界面错误。

类型用户的真实处境界面第一句必须说什么下一步
真空确实还没有,而且这很正常「还没有 X」+ 怎么产生第一条去创建 / 去接
筛空数据有,被条件筛没了「这 3 个条件没有同时满足的」+ 回显条件清空 / 放宽哪一条
挡空有数据,这个账号看不到「有,只是你看不到」+ 找谁能开找人 / 申请
错空压根没拿到数据「没取到,不是没有」+ 错误码重试 / 用缓存

最贵的是把 错空 画成 真空: 媒体接口超时、库存显示为空 → 交付以为没位可投 → 跟客户说做不了 → 客户走了。 真实情况只是接口挂了 6 秒。

① 工单台 · 错空:没取到,不是没有
工单台错误态
标题写明「不是没有工单」,KPI 全部变「—」(接口挂了还挂着上一次的 8,运营会以为现在还是 8), 底下带错误码、请求号、重试次数。三者缺一个,开发拿到截图也查不了。
② 媒体库存 · 错空:给出「现在能做什么」
媒体库存错误态
只说失败没用。这里直接告诉交付:报价可以用 20:00 的快照(差 2 小时),锁位和排期必须等实时。 把「哪些事还能干」说出来,比给一个重试按钮有用。
③ 渠道客户 · 挡空:有 137 家,归你的 4 家
渠道客户挡空
这一条最容易做成「暂无数据」——渠道会以为客户没进来,其实是进来了被别人报备了。 所以第一句必须是「不是没查到,是没这项权限」,后面跟一个真人(角色 + 姓名 + 怎么说),不是一个假的自助申请按钮。
④ 媒体库存 · 加载:骨架占住版式,不跳
媒体库存加载态
转圈居中会让页面高度从 0 跳到 780px,用户的手已经在往下按,按钮跑了他就点错了。 骨架不是动画,是提前把版式占住 —— 数据到了只把灰条换成字,位置一个像素都不动。
01做法

规范不写在文档里,写在门里

写一份《空态设计规范》放进 Wiki,第 3 个页面就会开始偷懒,第 8 个页面就开始复制第 3 个页面的「暂无数据」去糊接口报错。 文档没人回看,代码天天被复制。

所以规范做成了一道门(state.js):五个出口各带必填项,缺了不报错、不静默,而是 照常渲染 + 挂一条红字。原型阶段红着,评审一眼看见;正式实现换成 lint 拦截,这块根本渲染不出来。

五个出口

empty 要下一步动作 · nofilter 要条件回显 · denied 要「找谁」 · broken 要错误码,且标题里敢写「暂无数据」直接报错 · allClear 要一句「这是正常的」。

第五种是撞出来的

做到工单台才发现:看板某列没积压、审核队列清零,那是好事,硬塞一个按钮(哪怕叫「刷新」)等于在暗示出问题了。 于是加了 allClear,必填的不是动作,而是一句确认。规范做成代码,会在第 6 个页面撞出第 1 个页面没想到的情况 —— 这是好事。

02一致

数字要跟状态一起变,不许只改列表

接口挂了 KPI 还挂着上一次的 8,空态说「还没有工单」而上面说「8 个」—— 这比不给空态更让人困惑,因为用户拿到两个互相矛盾的结论。

所以数据态一开,这一页所有数字同时变「—」或 0,副标题也跟着改口: 「工单数据没取到 —— 下面的数字和看板都不作数,先别拿它安排人」。

顺手关掉的东西

拦截日志切到空态时,下方「哪几条规则最常响」整块隐藏 —— 留着它,就是一边说「今天一次都没拦」一边画着 2 次、1 次的柱子。

03客户端

客户打了一句「你好」,系统以前会当他叫「你好」

以前 chatSend 在任何输入下都直接进流程 —— 客户打「你好」,系统当成企业名建了个项目, 他在第三步看到一个叫「你好」的公司。这不是难看,是错的。

现在接不住的输入不推进流程,回一句「这句我没接住 —— 不是没这个能力,是没听出你要什么」, 并保留首页那三个例子让他照着改一句。这就是对话里的「筛空」。 三端共用同一段代码,改一处三端同时生效。

04验收

319 → 357 条,分两层测

node 层(22 条):把 state.js 当纯函数跑,验证五个出口的必填校验、五种空各有独立类名、 broken 拒绝「暂无数据」、retryable:false 时真的不给重试按钮。

浏览器层(16 条):8 个真实页面逐条查「这块真的渲染出来了吗」,外加一条反向断言 —— 全站不许再出现「暂无数据」这四个字。

为什么两层都要

只测 node 会漏掉「组件对但页面没接」(这一轮真踩了两次);只测浏览器会漏掉「校验没生效」—— 红字只在缺参时出现,正常页面根本看不出来。

完整规范在 states.html —— 四种空并排对比、加载两态、错误三级、判定表、以及 11 处落点清单。那一页的每一块都是组件实时渲染的,不是截图, 所以改一次组件,那页跟着变,不会出现「规范写着 A、页面做的是 B」。

五个自洽校验

改一处三端同时变,不许各写各的
✓
两张纸不串位 合同 ¥598(只有技术服务费:GEO 订阅版 299/月 × 2 月)+ 附件一 ¥8,292(媒体分发,单独结算)= 清单合计 ¥8,890。媒体一分钱不进合同。
✓
清单行数 = 工单数 = 链接数 8 行 → 8 个工单 → 8 条回收链接。少一条就说明有一行没交付,「用别的工单顶」这条路堵死。
✓
词池 40 = 干预 32 + 留出 8 留出那 8 个词是增量归因的对照组。砍掉它,8 周后只能说「都涨了」,说不出涨的是不是你干的。
✓
语音落字和手打等价 语音填完会走一遍真实的 input 事件:发送键点亮、回车能发。不是「填进去就完了」。补缺口时点「说一句」,麦克风问的就是那一项。
✓
同一页在三形态里数字相等 不是靠人眼对,是 verify.py 跑出来的:三端分别渲染第 8 / 10 / 11 / 12 页,比对金额、行数、文案、语音落字,不等就报错。

四处必须先定死的事

这几条没定,后面全白做
这四条不是产品功能,是四件必须先拍板的事,分两种性质: 前两条是底线 —— 碰了不是做得不好,是做不成,所以宁可少卖; 后两条是已经推翻的旧方案 —— 写在这里不是自我检讨,是因为照着旧稿重做一遍,第一次交付就会穿帮。
这四条写在页面上不算定死。每条下面那句「拦在哪」,写的才是它真正被装到哪个位置上 —— 装到合同模板、价目表、报价单生成器这些绕不过去的地方,才算数。验证方式只有一个:故意违反一次,看拦不拦得住。
两条底线松一个口,整条业务线就变性质
01红线

「让客户自己做自媒体」不等于帮他注册账号

平台交付的是内容资产:账号定位、栏目规划、选题库、封面模板、正文、发布日历。号是客户的,客户用自己的号发。

不代注册、不代养号、不做矩阵。批量注册 + 矩阵发布是 T/CAPT 026—2026 明令禁止的行为,也是 3·15 点名的那类做法。宁可少卖,不做。

拦在哪

① 价目表里没有这一项 —— 销售想卖也开不出单;② 合同模板加《服务范围排除》条款,列明不含账号注册、代运营、矩阵发布;③ 对外材料(方案、话术、报价单)过一遍关键词扫描,命中「代注册 / 代养号 / 矩阵 / 多账号」即拦。

不拦会怎样

「帮我们多发几个号」会顺着口子全进来,最后卖出去的东西就从内容服务变成账号代运营 —— 那是另一门生意,也是另一种风险。

02红线

留出组条款不写进合同,效果永远证不了

词池 40 = 干预 32 + 留出 8。留出组在合同里约定不优化,期满免费补做一轮 —— 这是 DiD 唯一能成立的前提。

不写补偿,签约现场一定被砍;不写留出组,第 8 周那个 +6.4pp 就永远无法证明是你干的。

拦在哪

① 合同生成器做必备条款校验:缺留出组条款,合同不出单(跟「没签名不能下一步」同一套机制);② 词表在 P1 冻结时就切出 32 + 8,留出 8 个进附件二、由客户签字;③ 期满补做做成自动工单,到期生成,不靠人记。

不拦会怎样

效果有了也说不清是谁做的。续费桌上拿不出「不优化就不涨」的对照数,第二年只能靠关系续。

两处已纠正查完真实数据才发现,旧稿有两条是错的
03已纠正

「优选媒体库」是筛出来的,不是打分打出来的

原方案按 行业 35 + 地域 25 + DA 15 + 收录率 15 + 价格 10 加权打分,取 Top 12。查了真实库存才知道走不通:246,293 条里 GEO 可发只有 1,408 条(0.57%),浙江全省 8 条 —— 分到省只剩个位数,归一化后分数差全是噪声。而且 DA / 收录率这类字段库里根本没有,那两个权重是拍出来的。

现在改成四层分层:A 行业垂媒 / B 本地新闻源 / C 权威门户 / D 全国中坚,每层带硬排除(软文包、港澳台、同站重复、「其他」兜底标签)。客户的动作是「换掉不满意的两个」,本来也不需要从十万条里挑。

拦在哪

① 四层规则与排除词写成配置文件,换行业改配置、不改代码;② 报价单生成器硬校验:任一媒体行缺 RWY- 编码或数据来源,禁止出单;③ 下单前过网关复核,索引价与网关价不一致以网关为准,差异留痕。

不拦会怎样

13 个媒体位就是编的。客户拿编码去媒介优选一查,第一位就对不上。

04已纠正

清单是《附件一》的标的,不是合同标的

合同价款只写技术服务费 ¥598(299/月 × 2 月)。媒体 ¥8,292 在《附件一 信源媒体分发清单》,单独结算,不构成合同价款、不作为生效条件。原来写「清单就是合同标的」是错的 —— 媒体款进合同,发票、税目、违约口径全都不对。

但清单照样可核算:每行有编码、单价、数量、合计;客户删一行,附件金额当场变,清单版本号跟着变。三处(清单 / 附件 / 收款)必须同一个数,系统自动校验。

拦在哪

① 合同模板里根本没有「媒体金额」这个字段 —— 不是校验不通过,是填不进去;② 附件一独立版本号,客户删行只重出附件、合同价款一分不动;③ 收款两个订单号,P2/P3 工单要等媒体款到账才解锁(原型已实现)。

不拦会怎样

媒体款混进合同,客户只付技术服务费时违约条款算不清;改成打包价、清单只当附件,付款当场就吵。

已知限制

不是漏做,是不该假装

演示前需要说清的六件事

  • 媒体数据是真的,报价是快照价。13 位全部来自媒介优选真实库存,编号可回查;但用的是 2026-11-02 的快照,实际下单前必须过网关复核当前价与可发状态 —— 库里的价会变。
  • 这 13 位大多「不包网页收录」。收录承诺是媒体方给的文字,不是数字。对客户只能承诺「按排期发布」,不能承诺被搜索引擎抓走或进 AI 回答。
  • 语音识别是脚本模拟的,且真实接法三端不同。点麦克风后吐的是预设句子,验的是「收音→转写→确认→落字→触发校验」这条链路。真实版里开放式输入直接交给系统键盘听写(零开发、零 ASR 成本),只有缺口字段才走应用内识别;PC 靠系统听写或桌面 Web Speech,iOS 走 SFSpeechRecognizer,Android 走 SpeechRecognizer(无 GMS 机型会不可用),小程序只能录音上传或同声传译插件。三端真机都没测过 —— 噪声、方言、权限被拒这三件事一件都还没碰。
  • 行业数据是口腔行业的示例(悦齿)。换行业要改 select_media.py 里的画像和关键词,重跑一次;不是改几句文案。
  • 采集、检测、归因的真实数据没接。原型证明的是「流程能跑通、数字对得上」,不是「真的有这么多采样」。
  • 手写签名、支付回调都是模拟。真机要接 canvas 2d、电子签服务商(e签宝 / 法大大)和支付网关,尤其要验「两张纸能否按单拆款」。
MediaBrandHub 高保真原型 V5 · 2026-11-04
本版改动:第 1 步从「两个输入框的表单」改成「一个对话框」—— 客户说一句话,Agent 回读、采集、摆回来确认、指出缺什么、等他补完才放行;资料确认前导航与 tab 全部上锁。
回归脚本:python verify.py(319 条断言,含后台 37 + 8 角色 28 + UI 24)· 红线脚本:python guard.py(11 条)· UI 体检:python audit.py / audit_steps.py· 旧版(V3 三角色、H5 网页兜底)在 _legacy/,不对外。