Files
zkxw/docs/superpowers/plans/2026-10-11-market-remaining-mobile-parity-demo.md
T

11 KiB
Raw Blame History

Market Remaining Mobile Parity Demo Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 制作一个可切换两种现场角色和五个剩余市场模块、可演示列表与业务操作的单文件移动端 HTML Demo。

Architecture: 使用一个自包含 HTML 文件承载示例数据、移动端样式和交互脚本。角色与模块由顶层原型控制条切换,手机画布内部按统一页面壳渲染列表;模块专属字段和操作由配置与渲染函数组合,详情、表单、Picker、确认框和 Toast 由同页覆盖层模拟。

Tech Stack: HTML5、CSS3、原生 JavaScript、Vant 4 视觉语义(无外部运行时依赖)、浏览器人工视觉检查


文件结构

  • Create: docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html — 完整原型、样式、示例数据和交互逻辑。
  • Create during preview only: .superpowers/brainstorm/<session>/content/market-remaining-mobile-parity-demo.html — 浏览器伴随预览副本,不提交。

Task 1: 页面壳、品牌 Token 与原型控制条

Files:

  • Create: docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html

  • Step 1: 创建自包含 HTML 基础结构

文件必须包含:

<main class="demo-stage">
  <aside class="demo-controls" aria-label="原型控制台">
    <div id="roleSwitch" class="segmented"></div>
    <div id="moduleSwitch" class="module-grid"></div>
    <p id="scopeHint" class="scope-hint"></p>
  </aside>
  <section class="phone" aria-label="移动端页面预览">
    <header class="mobile-nav">
      <button class="nav-back" type="button" aria-label="返回">‹</button>
      <h1 id="pageTitle"></h1>
    </header>
    <div id="mobileBody" class="mobile-body"></div>
    <button id="fab" class="fab" type="button" aria-label="新增">+</button>
  </section>
</main>
<div id="overlayRoot"></div>
<div id="toast" class="toast" role="status" aria-live="polite"></div>
  • Step 2: 定义小程序品牌与移动端布局 Token

在内联 <style> 中至少定义:

:root {
  --brand: #0a9f6d;
  --brand-dark: #087f58;
  --brand-soft: #e7f7f1;
  --canvas: #f4f7f5;
  --surface: #fff;
  --text: #1f2d27;
  --muted: #73817a;
  --line: #e6ece9;
  --danger: #e5484d;
  --warning: #d98516;
}

.phone 限制为约 390×844px;.mobile-nav 为白色固定头栏;.fab 为右下角 56×56px 圆形按钮;移动断点下控制台与手机画布纵向排列。所有业务按钮最小高度 44px,不定义 backdrop-filter。

  • Step 3: 定义角色与模块注册表

内联脚本中建立稳定键名:

const roles = {
  specialist: { label: '市场专员', scope: '仅展示当前市场专员本人负责的现场单据' },
  manager: { label: '地区经理', scope: '展示负责采集地区及指定为当前经理的单据' }
}

const modules = {
  protocol: { title: '协议单', create: true, search: '搜索母亲姓名 / 协议编号 / 样本编码' },
  chase: { title: '催缴信息', create: false, search: '搜索协议号 / 客户 / 样本编码' },
  payment: { title: '缴费信息', create: false, search: '搜索协议号 / 客户 / 样本编码' },
  sample: { title: '样本信息', create: false, search: '搜索母亲姓名 / 样本编码' },
  freight: { title: '运费信息', create: true, search: '搜索批次号 / 母亲姓名 / 样本编码' }
}
  • Step 4: 做静态结构检查

Run:

python - <<'PY'
from html.parser import HTMLParser
from pathlib import Path
p = Path('docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html')
HTMLParser().feed(p.read_text(encoding='utf-8'))
print('HTML parse OK')
PY

Expected: HTML parse OK。

Task 2: 五模块卡片字段与角色差异

Files:

  • Modify: docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html

  • Step 1: 写入每个模块至少六条示例数据

示例数据对象必须使用业务字段名,并覆盖状态差异:

const demoData = {
  protocol: [
    { source: 'cell_storage', collectDate: '2026-10-11', labStorageCode: 'LAB-261011-018', motherName: '李安然', protocolNo: 'XBC20261011001', sampleTypeName: '脐带血', coStored: true, regionName: '合肥区域', hospitalName: '安徽省妇幼保健院', sampleCollectTime: '2026-10-11 09:20', payMode: '趸交', firstPayTime: '2026-10-11 10:05', salesUserName: '王敏', managerName: '赵经理', transportMode: '冷链配送', status: '生效中' }
  ],
  chase: [],
  payment: [],
  sample: [],
  freight: []
}

剩余数组按设计文档第 5 节写全字段;数量足以模拟首次 3 条、滚动追加 3 条。

  • Step 2: 为五个模块实现专属卡片渲染器

实现并由 renderCard(moduleKey, row, index) 分发:

function renderProtocolCard(row, index) {}
function renderChaseCard(row, index) {}
function renderPaymentCard(row, index) {}
function renderSampleCard(row, index) {}
function renderFreightCard(row, index) {}

每个卡片显示 Web 列表对应字段,状态 / 类型使用 .tag。操作按钮必须分别为:

  • 协议:审核、编辑、查看、删除。

  • 催缴:转账明细(有实缴时)、协议单。

  • 缴费:查看、缴费确认(允许时)、协议单。

  • 样本:查看、储存证质检信息、登记储存证 / 登记保险(允许时)。

  • 运费:编辑、关联样本、取消关联。

  • Step 3: 实现角色数据范围提示与运费脱敏

当 state.role === 'specialist' 时,运费卡显示:

<div class="privacy-note">同批次其他专员明细已脱敏</div>

不得显示被隐藏人数或行数;经理角色显示完整关联样本摘要。

  • Step 4: 验证字段标签与按钮文案

Run:

rg -n "协议编号|采集医院|首期缴费时间|欠缴金额|缴费进度|实验室入库编码|储存证|保险|批次号|取消关联|缴费确认" docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html

Expected: 每个关键字段和操作至少命中一次。

Task 3: 搜索、滚动分页和浮动新增

Files:

  • Modify: docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html

  • Step 1: 实现列表状态与搜索

状态模型:

const state = {
  role: 'specialist',
  module: 'protocol',
  keyword: '',
  visibleCount: 3,
  pageSize: 3,
  loadingMore: false
}

renderList() 只渲染包含关键词的记录;清空搜索时重置 visibleCount。页面不渲染任何状态筛选框。

  • Step 2: 实现滚动触底追加

监听 .mobile-body 的滚动,当 scrollTop + clientHeight >= scrollHeight - 80 时将 visibleCount 增加 pageSize,短暂显示“正在加载”,加载完成后显示新卡片;全部加载后显示“没有更多了”。

  • Step 3: 控制圆形新增按钮

只有 modules[state.module].create === true 时显示 .fab。因此协议单与运费显示,催缴、缴费、样本隐藏。

  • Step 4: 检查没有禁用元素

Run:

rg -n "全部状态|全部催缴|全部缴费|状态筛选|绿色头部|linear-gradient" docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html

Expected: 无命中。

Task 4: 详情、表单与模块专属操作

Files:

  • Modify: docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html

  • Step 1: 实现全屏覆盖页和底部固定栏

实现:

function openSheet({ title, body, footer }) {}
function closeSheet() {}
function showToast(message) {}
function confirmAction(message, onConfirm) {}

覆盖页使用 .screen-overlay,不设置 backdrop blur。表单页正文独立滚动,底部操作栏固定。

  • Step 2: 实现协议新增 / 编辑表单

表单包含协议信息、采集信息、客户信息、样本信息、附件与确认。采集地区与采集医院用只读 Field 打开底部 Picker;切换地区后清空医院,并将医院选项换成对应地区集合。附件区域第一项是虚线上传卡,后面是已上传文件卡。

  • Step 3: 实现缴费与催缴详情交互

催缴“转账明细”打开应缴 / 已缴 / 欠缴摘要和转账卡;缴费“查看”展示缴费计划及转账笔。转账登记 / 编辑表单包含转账金额、转账时间、转账方式、转账凭证、备注;提交与撤回使用确认框。

  • Step 4: 实现样本办理交互

样本“登记储存证”和“登记保险”打开同构表单:办理时间、办理人员、附件、备注。科研捐赠样本不得显示这两个按钮。

  • Step 5: 实现运费新增 / 编辑 / 关联 / 取消关联

运费表单包含运输方式、运费金额、关联样本;关联与取消关联使用卡片复选列表。专员角色的选择列表只提供自己的样本,经理角色提供负责范围内样本。

  • Step 6: 实现 Demo 内临时反馈

保存、删除、审核、缴费确认和关联操作只变更内存中的 UI 状态并显示 Toast,不请求真实 API。

Task 5: 语法、视觉与交互验证

Files:

  • Modify if needed: docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html

  • Step 1: 抽取并检查 JavaScript 语法

Run:

python - <<'PY'
from pathlib import Path
import re
text = Path('docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html').read_text(encoding='utf-8')
scripts = re.findall(r'<script>([\\s\\S]*?)</script>', text)
Path('/tmp/market-remaining-mobile-parity-demo.js').write_text('\n'.join(scripts), encoding='utf-8')
PY
node --check /tmp/market-remaining-mobile-parity-demo.js

Expected: 无输出且退出码为 0。

  • Step 2: 执行 HTML 与约束检查

Run:

python - <<'PY'
from html.parser import HTMLParser
from pathlib import Path
p = Path('docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html')
text = p.read_text(encoding='utf-8')
HTMLParser().feed(text)
assert '#0a9f6d' in text
assert '#10b981' not in text
assert 'backdrop-filter' not in text
assert '全部状态' not in text
print('HTML constraints OK')
PY

Expected: HTML constraints OK。

  • Step 3: 在浏览器伴随页做完整交互巡检

复制 Demo 到本次 screen_dir 的新文件名并打开预览。逐项检查:

  1. 切换两种角色与五个模块。
  2. 搜索清空和滚动追加。
  3. 协议 / 运费浮动新增按钮。
  4. 协议地区 / 医院级联与附件卡片。
  5. 催缴转账明细、缴费转账、样本办理、运费关联 / 取消关联。
  6. 专员运费脱敏和经理完整明细。
  • Step 4: 检查工作区差异

Run:

git diff --check -- docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html
git status --short

Expected: Demo 文件无空白错误;不修改本任务之外的现有用户文件。

  • Step 5: 提交 Demo
git add docs/demos/2026-10-11-market-remaining-mobile-parity-demo.html
git commit -m "docs: add the remaining mobile market parity demo."