Files
bid-doctor/docs/design/ui-demo/controls.html
YANG JIANKUAN 7f9592ad16 docs(design): 按 v0.2 重刷全部 UI demo,确认 Q1–Q13 与 U 键规则
- ui-demo:控件、表单、卡片、列表、工作台、读标六页按 v0.2 重刷;侧栏与顶栏框架、项目行、署名行、可键入日期输入、对照布局等收回 demo.css / demo.js 共用
- system:tokens 补悬停 / 按下 / 遮罩色、数字字阶与原文排版变量;components.css 按 v0.2 改写并清除裸色值
- design-system.md:写入 Q1–Q13 答案,新增开关、分页、下拉、日期选择等组件规则,类别标签、待确认区、选中态、对照布局按 v0.2 修订
- Q13:额度流水记录 0 点的「免扣」行,FR-M6-03 与领域模型同步;U 键改为撤销当前条
- decisions.md 记录 Q1–Q12、Q13 与 U 键;AGENTS.md 同步进度

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 16:07:15 +08:00

637 lines
57 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<!--
陈列页 · 控件总览(v0.2)
视觉以定稿样张 explore/workbench-d5.html 为准;共用组件来自 components.css 和 demo.css,
本页 <style> 里只放陈列用的小件(主按钮示意、选中态示例的边距、AI 产出示例的切换);日期输入、署名行、菜单、快捷键表都用共用实现。
网址加 ?open=select | date | menu | d1 | d2 | d3 可直接打开对应的浮层或对话框(截图用)。
-->
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>控件总览 · 标博士 UI demo</title>
<link rel="icon" href="../logo/favicon.svg">
<link rel="stylesheet" href="../system/tokens.css">
<link rel="stylesheet" href="../system/fonts-local.css">
<link rel="stylesheet" href="../system/components.css">
<link rel="stylesheet" href="demo.css">
<style>
/* Q12 示意:浅灰块代表页面,白卡片代表对话框 */
.q12{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--gap-card)}
.q12-page{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-4) var(--sp-5);background:var(--bg-page);border-radius:var(--radius-ctl)}
.q12-page b{font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-serif);margin-right:auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.q12-dlg{padding:var(--sp-4) var(--sp-5);background:var(--bg-page);border-radius:var(--radius-ctl)}
.q12-dlg > div{padding:var(--sp-4) var(--sp-5);background:var(--bg-surface);border-radius:var(--radius-card);box-shadow:var(--shadow-overlay)}
.q12-dlg b{display:block;margin-bottom:var(--sp-3)}
.q12-dlg .foot{display:flex;justify-content:flex-end;gap:var(--sp-2)}
/* 当前条示例(§7.18):清单项、表格行放在分区卡片里 */
.sel-demo .item{padding:var(--sp-3) var(--card-x)}
.sel-demo .item:first-of-type{border-top:var(--line) solid var(--border-subtle)}
.sel-demo .item:last-child{border-radius:0 0 var(--radius-card) var(--radius-card)}
.sel-demo .item .side .cite-src{white-space:nowrap}
.sel-demo .card-h small{margin-left:auto;font-size:var(--fs-caption);color:var(--text-secondary)}
/* AI 产出示例(Q2) */
.ai-acts{display:flex;gap:var(--sp-2);margin-top:var(--sp-3)}
.ai-block.done .ai-label,.ai-block.done .ai-acts{display:none}
.ai-block:not(.done) .sign{display:none}
</style>
</head>
<body class="demo">
<header data-demo-top></header>
<main class="d-page">
<div class="d-head">
<h1>① 控件总览</h1>
<p>按状态陈列。灰色小字是状态名,标「可以点」的可以交互。</p>
<nav class="d-toc" aria-label="本页目录">
<a href="#s-btn">01 按钮</a><a href="#s-input">02 输入框</a><a href="#s-select">03 下拉选择</a><a href="#s-date">04 日期选择</a><a href="#s-check">05 复选 · 单选 · 开关</a><a href="#s-tag">06 标签 · AI 标识</a><a href="#s-cite">07 出处</a><a href="#s-alert">08 提醒</a><a href="#s-toast">09 轻提示</a><a href="#s-dialog">10 对话框</a><a href="#s-tabs">11 标签页 · 筛选 · 分页 · 选中</a><a href="#s-empty">12 空状态 · 骨架屏 · 进度</a><a href="#s-tip">13 提示气泡 · 菜单 · 快捷键</a>
</nav>
</div>
<!-- 01 按钮 -->
<section class="d-sec" id="s-btn">
<div class="d-sec-h"><span class="no">01</span><h2>按钮</h2><span class="ref">规范 §7.1 · §8.3</span></div>
<div class="d-box">
<h3>类型 × 状态</h3>
<table class="d-matrix">
<thead><tr><th></th><th>默认</th><th>悬停</th><th>按下</th><th>键盘聚焦</th><th>禁用</th><th>加载中</th></tr></thead>
<tbody>
<tr><td>主按钮</td><td><button class="btn primary">发起检查</button></td><td><button class="btn primary is-hover">发起检查</button></td><td><button class="btn primary is-active">发起检查</button></td><td><button class="btn primary is-focus">发起检查</button></td><td><button class="btn primary" disabled>发起检查</button></td><td><button class="btn primary swap is-loading" aria-busy="true"><span class="idle">发起检查</span><span class="busy">正在发起…</span></button></td></tr>
<tr><td>次按钮</td><td><button class="btn">导出报告</button></td><td><button class="btn is-hover">导出报告</button></td><td><button class="btn is-active">导出报告</button></td><td><button class="btn is-focus">导出报告</button></td><td><button class="btn" disabled>导出报告</button></td><td><button class="btn swap is-loading" aria-busy="true"><span class="idle">导出报告</span><span class="busy">正在导出…</span></button></td></tr>
<tr><td>文字按钮</td><td><button class="btn text">查看处理记录</button></td><td><button class="btn text is-hover">查看处理记录</button></td><td><button class="btn text is-active">查看处理记录</button></td><td><button class="btn text is-focus">查看处理记录</button></td><td><button class="btn text" disabled>查看处理记录</button></td><td><span class="d-cap">不用</span></td></tr>
</tbody>
</table>
<ul class="d-rules">
<li>没有红色的危险按钮:删除用次按钮 + 确认对话框。</li>
<li>加载中把文字换成「正在…」,按钮宽度不变,不加转圈。</li>
</ul>
</div>
<div class="d-grid c3" style="margin-top:var(--gap-card)">
<div class="d-box">
<h3>尺寸</h3>
<div class="d-row"><button class="btn sm">确认无误</button><button class="btn">确认无误</button><button class="btn lg">上传招标文件</button></div>
<ul class="d-rules"><li>32 表格行内 · 36 默认 · 40 登录页、空状态</li></ul>
</div>
<div class="d-box">
<h3>带图标 · 只有图标</h3>
<div class="d-row">
<button class="btn"><i data-ic="upload"></i>上传投标文件</button>
<button class="icon-btn bordered" aria-label="下载报告" data-tip="下载报告"><i data-ic="download"></i></button>
<button class="icon-btn" aria-label="处理记录" data-tip="处理记录"><i data-ic="history"></i></button>
<button class="icon-btn" aria-label="删除" data-tip="项目已归档,不能删除" disabled><i data-ic="trash"></i></button>
</div>
<div class="d-row"><span class="btn-group"><button class="btn sm">上一处</button><button class="btn sm">下一处</button></span></div>
<ul class="d-rules"><li>只有图标时要有 aria-label 和悬停提示。</li></ul>
</div>
<div class="d-box">
<h3>会扣点的按钮 <small>可以点</small></h3>
<div class="d-row"><button class="btn swap" data-loading="检查已开始,可以离开本页,完成后会发站内通知"><span class="idle">发起检查</span><span class="busy">正在发起…</span></button><span class="cost">扣 1 次检查 · 剩 <span class="fig">12</span> 次</span></div>
<div class="d-row"><button class="btn swap" data-loading="已开始复查"><span class="idle">重新检查</span><span class="busy">正在发起…</span></button><span class="cost">24 小时内复查,不扣额度</span></div>
<div class="d-row"><button class="btn" data-nomore>发起检查</button><span class="cost">剩 <span class="fig">0</span> 次 · 仍可点</span></div>
</div>
</div>
<div class="d-box" style="margin-top:var(--gap-card)">
<h3>一屏一个主按钮,按上下文算</h3>
<div class="q12">
<div><span class="d-label">页面级:只有一个,放在页头右侧</span>
<div class="q12-page"><b>智慧大厅建设项目</b><button class="btn">导出报告</button><span class="cost">扣 1 次检查 · 剩 <span class="fig">12</span> 次</span><button class="btn primary">发起检查</button></div></div>
<div><span class="d-label">对话框:独立上下文,可以有自己的主按钮</span>
<div class="q12-dlg"><div><b>确认清单 v3?</b><div class="foot"><button class="btn">取消</button><button class="btn primary">确认清单</button></div></div></div></div>
</div>
<ul class="d-rules"><li>本页和组件预览是陈列页,为了对比会同时出现多个主按钮,不受此限。</li></ul>
</div>
</section>
<!-- 02 输入框 -->
<section class="d-sec" id="s-input">
<div class="d-sec-h"><span class="no">02</span><h2>输入框与文本域</h2><span class="ref">规范 §7.13</span></div>
<div class="d-box">
<div class="d-states wide">
<div class="d-state"><small>默认</small><div class="field"><label for="i1">项目名称 <small>(必填)</small></label><input class="input" id="i1" placeholder="例如:智慧大厅建设项目"></div></div>
<div class="d-state"><small>悬停</small><div class="field"><label>项目编号</label><input class="input is-hover" placeholder="招标文件封面上的编号"></div></div>
<div class="d-state"><small>聚焦</small><div class="field"><label>项目编号</label><input class="input is-focus" value="ZC-2026-04"></div></div>
<div class="d-state"><small>已填写</small><div class="field"><label>采购人</label><input class="input" value="某市政务服务管理办公室"></div></div>
<div class="d-state"><small>错误(赭色,不用红)</small><div class="field err"><label>联系人手机</label><input class="input" value="1381234567" aria-invalid="true"><span class="msg"><span class="ico w">!</span>手机号应为 11 位数字</span></div></div>
<div class="d-state"><small>禁用</small><div class="field dis"><label>法律体系</label><input class="input" value="政府采购" disabled></div></div>
<div class="d-state"><small>只读</small><div class="field"><label>文件页数</label><input class="input" value="186 页" readonly></div></div>
<div class="d-state"><small>带单位 · 说明</small><div class="field"><label>投标有效期</label><div class="input-unit"><input class="input" value="90"><span class="unit">天</span></div><span class="msg">招标文件第 21 页</span></div></div>
</div>
</div>
<div class="d-grid c2" style="margin-top:var(--gap-card)">
<div class="d-box">
<h3>文本域 · 字数 <small>可以输入</small></h3>
<div class="field fill" data-count-field>
<label for="ta1">忽略原因 <small>(必填,至少 5 个字)</small></label>
<textarea class="input" id="ta1" maxlength="200" placeholder="写清楚为什么不处理,报告里会如实列出"></textarea>
<div class="msg-row"><span class="msg">报告和处理记录里会显示这段原因。</span><span class="count"><span data-n>0</span> / 200</span></div>
</div>
</div>
<div class="d-box">
<h3>搜索框</h3>
<label class="search"><i data-ic="search"></i><input class="input" placeholder="搜索项目名称、采购人、项目编号" aria-label="搜索项目"><span class="kbd">/</span></label>
<div style="height:var(--sp-4)"></div>
<label class="search" style="width:240px"><i data-ic="search"></i><input class="input" value="服务器" aria-label="搜索清单"></label>
<ul class="d-rules"><li>高 36,圆角 8;聚焦时边框变墨并加 1px 墨色内圈。占位符写清楚能搜什么。</li></ul>
</div>
</div>
</section>
<!-- 03 下拉选择 -->
<section class="d-sec" id="s-select">
<div class="d-sec-h"><span class="no">03</span><h2>下拉选择</h2><span class="ref">规范 §7.21</span></div>
<div class="d-box">
<div class="d-states wide">
<div class="d-state"><small>未选择 · 可以点</small><div class="field"><label>采购方式 <small>(必填)</small></label>
<div class="pop" data-select><button type="button" class="select" aria-haspopup="listbox" aria-expanded="false"><span class="val"><span class="ph">请选择</span></span><i data-ic="chev-down"></i></button>
<ul class="menu" role="listbox"><li class="grp">政府采购</li><li role="option">公开招标</li><li role="option">邀请招标</li><li role="option">竞争性磋商</li><li role="option">竞争性谈判</li><li role="option">询价</li><li class="grp">招标投标</li><li role="option">公开招标(招标投标法)</li></ul></div></div></div>
<div class="d-state"><small>已选择 · 可以点</small><div class="field"><label>采购方式</label>
<div class="pop" data-select id="sel-demo"><button type="button" class="select" aria-haspopup="listbox" aria-expanded="false"><span class="val">竞争性磋商</span><i data-ic="chev-down"></i></button>
<ul class="menu" role="listbox"><li class="grp">政府采购</li><li role="option">公开招标</li><li role="option">邀请招标</li><li role="option" aria-selected="true">竞争性磋商</li><li role="option">竞争性谈判</li><li role="option">询价</li><li class="grp">招标投标</li><li role="option" aria-disabled="true">公开招标(招标投标法)<small>不适用于本项目</small></li></ul></div></div></div>
<div class="d-state d-open"><small>展开</small><div class="field"><label>所投的包</label>
<div class="pop"><button type="button" class="select is-focus" aria-expanded="true"><span class="val">采购包 1 · 智慧大厅软硬件集成</span><i data-ic="chev-down"></i></button>
<ul class="menu" role="listbox"><li role="option" aria-selected="true">采购包 1 · 智慧大厅软硬件集成</li><li role="option" class="is-active">采购包 2 · 机房网络设备更新</li><li role="option" aria-disabled="true">采购包 3<small>补遗 1 已终止</small></li></ul></div></div></div>
<div class="d-state"><small>错误</small><div class="field err"><label>资质类型 <small>(必填)</small></label>
<div class="pop" data-select><button type="button" class="select" aria-haspopup="listbox" aria-expanded="false" aria-invalid="true"><span class="val"><span class="ph">请选择</span></span><i data-ic="chev-down"></i></button>
<ul class="menu" role="listbox"><li role="option">资质证书</li><li role="option">体系认证</li><li role="option">软件著作权</li><li role="option">其他</li></ul></div>
<span class="msg"><span class="ico w">!</span>请选择资质类型</span></div></div>
<div class="d-state"><small>禁用</small><div class="field dis"><label>法律体系</label><div class="pop"><button type="button" class="select" disabled><span class="val">政府采购</span><i data-ic="chev-down"></i></button></div><span class="msg">由招标文件识别,在项目设置里修改</span></div></div>
<div class="d-state"><small>表格行内(高 32)</small><div class="pop inline" data-select><button type="button" class="select sm" aria-haspopup="listbox" aria-expanded="false" style="width:132px"><span class="val">技术册</span><i data-ic="chev-down"></i></button>
<ul class="menu" role="listbox"><li role="option">商务册</li><li role="option" aria-selected="true">技术册</li><li role="option">报价册</li><li role="option">资格证明册</li></ul></div></div>
</div>
<ul class="d-rules"><li>选中项加粗,右侧墨色 ✓;不用蓝色,也不用底色。悬停和键盘移动出浅灰底。</li><li>不可选的项变禁用灰,右侧写原因。键盘:↑ ↓ 移动、Enter 选中、Esc 收起。</li></ul>
</div>
</section>
<!-- 04 日期选择 -->
<section class="d-sec" id="s-date">
<div class="d-sec-h"><span class="no">04</span><h2>日期选择</h2><span class="ref">规范 §7.22 · §9.3</span></div>
<div class="d-grid c3">
<div class="d-box">
<h3>输入框 <small>可以输入、可以点</small></h3>
<div class="field">
<label for="dt1">投标截止 <small>(必填)</small></label>
<div class="dt" data-dt data-time="09:30" data-left data-marks="2026-11-9,2026-11-20" data-req="请填写投标截止时间" id="dt-demo">
<input class="input" id="dt1" value="2026-11-20 09:30" placeholder="例如:2026-11-20 09:30" autocomplete="off">
<button type="button" class="icon-btn sm" aria-label="打开日期选择器" aria-haspopup="dialog" aria-expanded="false"><i data-ic="calendar"></i></button>
<div class="cal" role="dialog" aria-label="选择投标截止日期" hidden></div>
</div>
<span class="msg" data-dt-msg>24 小时制</span>
</div>
<div class="field" style="margin-top:var(--sp-4)">
<label for="dt2">证书有效期至</label>
<div class="dt" data-dt>
<input class="input" id="dt2" placeholder="例如:2027-05-14" autocomplete="off">
<button type="button" class="icon-btn sm" aria-label="打开日期选择器" aria-haspopup="dialog" aria-expanded="false"><i data-ic="calendar"></i></button>
<div class="cal" role="dialog" aria-label="选择有效期" hidden></div>
</div>
<span class="msg" data-dt-msg>可以键入「2027/5/14」</span>
</div>
<div class="field err" style="margin-top:var(--sp-4)">
<label for="dt3">发证日期</label>
<div class="dt"><input class="input" id="dt3" value="2027-03-01" aria-invalid="true"><button type="button" class="icon-btn sm" aria-label="打开日期选择器" tabindex="-1"><i data-ic="calendar"></i></button></div>
<span class="msg"><span class="ico w">!</span>发证日期晚于有效期,请核对证书</span>
</div>
</div>
<div class="d-box">
<h3>展开 · 带时间</h3>
<div class="pop d-open" data-date data-value="2026-11-20" data-marks="2026-11-9,2026-11-20" data-time="09:30"><div class="cal"></div></div>
<ul class="d-rules"><li>周一开始,非本月的格子留空。今天:控件灰描边;选中:墨底白字;关键日期:下方小圆点。</li><li>时间 24 小时制。</li></ul>
</div>
<div class="d-box">
<h3>有不可选的日期</h3>
<div class="pop d-open" data-date data-month="2026-11" data-min="2026-11-03"><div class="cal"></div></div>
<ul class="d-rules"><li>不可选的日期只变禁用灰,没有悬停,不加删除线。</li></ul>
</div>
</div>
<ul class="d-rules"><li>输入框写「2026-11-20 09:30」,可以直接键入「2026/11/20 9:30」等写法,离开时整理;下方显示「11月20日 09:30 · 剩 17 天」,跨年时带年份。</li></ul>
</section>
<!-- 05 复选 单选 开关 -->
<section class="d-sec" id="s-check">
<div class="d-sec-h"><span class="no">05</span><h2>复选 · 单选 · 开关</h2><span class="ref">规范 §7.19 · §8.2</span></div>
<div class="d-grid c3">
<div class="d-box">
<h3>复选框</h3>
<div class="d-states">
<div class="d-state"><small>未选</small><label class="choice"><input type="checkbox" class="ck">提示</label></div>
<div class="d-state"><small>已选</small><label class="choice"><input type="checkbox" class="ck" checked>提示</label></div>
<div class="d-state"><small>半选</small><label class="choice"><input type="checkbox" class="ck" data-indeterminate>全选</label></div>
<div class="d-state"><small>聚焦</small><label class="choice"><input type="checkbox" class="ck is-focus">重要</label></div>
<div class="d-state"><small>禁用</small><label class="choice dis" data-tip="只有「提示」级可以批量处理"><input type="checkbox" class="ck" disabled>致命</label></div>
<div class="d-state"><small>禁用 · 已选</small><label class="choice dis"><input type="checkbox" class="ck" disabled checked>已归档</label></div>
</div>
<div class="choices" style="margin-top:var(--sp-5)">
<label class="choice"><input type="checkbox" class="ck" checked><span>电子投标文件已使用 CA 数字证书加密<small>需人工核对 · 招标文件第 21 页</small></span></label>
<label class="choice"><input type="checkbox" class="ck"><span>在截止时间前于电子交易平台上传,并取得回执<small>需人工核对 · 招标文件第 6 页</small></span></label>
</div>
</div>
<div class="d-box">
<h3>单选</h3>
<div class="d-states">
<div class="d-state"><small>未选</small><label class="choice"><input type="radio" class="ck" name="r0">招标投标</label></div>
<div class="d-state"><small>已选</small><label class="choice"><input type="radio" class="ck" name="r0b" checked>政府采购</label></div>
<div class="d-state"><small>禁用</small><label class="choice dis"><input type="radio" class="ck" disabled>企业自主采购</label></div>
<div class="d-state"><small>禁用 · 已选</small><label class="choice dis"><input type="radio" class="ck" disabled checked>政府采购</label></div>
</div>
<span class="d-label" style="margin-top:var(--sp-5)">单选卡片(选项需要说明时)</span>
<div class="choices">
<label class="choice-card"><input type="radio" class="ck" name="r1" checked><span><b>政府采购</b><small>按《政府采购法》和 87 号令检查,结论用「投标无效」</small></span></label>
<label class="choice-card"><input type="radio" class="ck" name="r1"><span><b>招标投标</b><small>按《招标投标法》检查,结论用「否决投标」</small></span></label>
</div>
</div>
<div class="d-box">
<h3>开关 <small>可以点</small></h3>
<div class="d-states">
<div class="d-state"><small>关</small><button type="button" class="switch" role="switch" aria-checked="false" aria-label="示例"><span class="track"></span><span class="st"></span></button></div>
<div class="d-state"><small>开</small><button type="button" class="switch" role="switch" aria-checked="true" aria-label="示例"><span class="track"></span><span class="st"></span></button></div>
<div class="d-state"><small>禁用 · 关</small><button type="button" class="switch" role="switch" aria-checked="false" aria-label="示例" disabled><span class="track"></span><span class="st"></span></button></div>
<div class="d-state"><small>禁用 · 开</small><button type="button" class="switch" role="switch" aria-checked="true" aria-label="示例" disabled><span class="track"></span><span class="st"></span></button></div>
</div>
<div class="choices" style="margin-top:var(--sp-5)">
<div class="choice" style="justify-content:space-between;width:100%;cursor:default"><span id="sw1">额度用到 80% 时站内提醒<small>每期提醒一次</small></span><button type="button" class="switch" role="switch" aria-checked="true" aria-labelledby="sw1" data-sw-toast="额度提醒"><span class="track"></span><span class="st"></span></button></div>
<div class="choice" style="justify-content:space-between;width:100%;cursor:default"><span id="sw2">资质到期前 30 天提醒</span><button type="button" class="switch" role="switch" aria-checked="false" aria-labelledby="sw2" data-sw-toast="资质到期提醒"><span class="track"></span><span class="st"></span></button></div>
</div>
<ul class="d-rules"><li>矩形轨道 + 方块滑钮,不用胶囊形;开为墨色,旁边写「开 / 关」。</li><li>只用于立即生效的设置;要提交的表单项用复选框。</li></ul>
</div>
</div>
</section>
<!-- 06 标签与标识 -->
<section class="d-sec" id="s-tag">
<div class="d-sec-h"><span class="no">06</span><h2>级别标签 · 中性标签 · AI 标识 · 署名</h2><span class="ref">规范 §3.3 · §7.2 · §7.16</span></div>
<div class="d-grid c2">
<div class="d-box">
<h3>问题级别(实心 / 描边 / 无框)</h3>
<div class="d-row"><span class="sev fatal">致命</span><span class="sev major">重要</span><span class="sev hint">提示</span><span class="d-cap" style="margin-left:var(--sp-4)">计数</span><span class="sev fatal">致命<span class="fig">3</span></span><span class="sev major">重要<span class="fig">3</span></span><span class="sev hint">提示<span class="fig">3</span></span></div>
<div class="d-row"><span class="d-cap">参数判断</span><span class="verdict fatal">疑似不满足</span><span class="verdict major">疑似不满足</span><span>满足</span><span class="verdict manual">需人工判断</span><span class="verdict major">未找到响应</span></div>
<h3 style="margin-top:var(--sp-6)">中性标签(浅灰实底,无描边)</h3>
<div class="d-row"><span class="d-cap">状态</span><span class="tag">跟进中</span><span class="tag">编制中</span><span class="tag">已检查</span><span class="tag">已递交</span><span class="tag">已归档</span></div>
<div class="d-row"><span class="d-cap">就绪</span><span class="tag">已检查</span><span class="hint-u tip-below" tabindex="0" data-tip="只表示致命问题都已处理,不代表投标文件没有问题。">就绪</span><span class="tag" style="margin-left:var(--sp-4)">已检查</span><span>未就绪</span></div>
<div class="d-row"><span class="d-cap">分类</span><span class="tag">资格条件</span><span class="tag">无效条款</span><span class="tag">技术参数</span><span class="tag">采购包 1</span></div>
<div class="d-row"><span class="d-cap">复查</span><span class="flag box">新增</span><span class="flag box">沿用</span><span class="flag box">本次已解决</span><span class="d-cap" style="margin-left:var(--sp-4)">清单</span><span class="tag">草稿 v3</span><span class="tag">已确认 v2</span></div>
</div>
<div class="d-box">
<h3>AI 产出 → 人工确认</h3>
<div class="d-row"><span class="tag ai">AI 摘要</span><span class="tag ai">AI 初稿</span><span class="tag ai">AI 提取</span><span class="d-cap" style="margin-left:var(--sp-4)">来源</span><span class="tag ai">AI</span><span class="tag">人工添加</span><span class="tag">补遗 1</span></div>
<div class="d-stack" style="margin-top:var(--sp-5)">
<div><span class="d-label">确认前:「AI」标签 + 左侧 2px 蓝铅笔虚线,正文墨色 · 可以点</span>
<div class="ai-block" data-ai>
<span class="ai-label"><span class="tag ai">AI 摘要</span></span>
预算 418.00 万元,分 2 个采购包;采购包 1 最高限价 286.00 万元。<a class="cite-src inline" href="#">招标文件 · <span class="fig">第 2 页</span></a>
<div class="ai-acts"><button class="btn sm" data-ai-ok>确认无误</button><button class="btn sm text" data-ai-edit>修改</button></div>
<div class="sign sm"><span class="ok" aria-hidden="true">✓</span><b>王敏</b><span class="fig" data-ai-time>11月3日 10:05</span><span data-ai-act>确认无误</span><button type="button" class="undo" data-ai-undo>撤销</button></div>
</div></div>
<div><span class="d-label">确认后:去掉虚线,换成署名行(绿 ✓ + 姓名 + 时间)</span>
<div class="ai-block done">预算 418.00 万元,分 2 个采购包;采购包 1 最高限价 286.00 万元。<a class="cite-src inline" href="#">招标文件 · <span class="fig">第 2 页</span></a>
<div class="sign sm"><span class="ok" aria-hidden="true">✓</span><b>王敏</b><span class="fig">11月2日 14:20</span><span>确认无误</span></div></div></div>
<div><span class="d-label">问题处理后的署名行</span>
<div class="sign" style="margin:0"><span class="ok" aria-hidden="true">✓</span><b>王敏</b><span class="fig">11月2日 14:20</span><span>已修改</span><button type="button" class="undo" data-undo-demo>撤销</button></div>
<div class="sign ign"><span class="ok" aria-hidden="true">–</span><b>王敏</b><span class="fig">11月2日 14:26</span><span>已忽略</span><button type="button" class="undo" data-undo-demo>撤销</button></div>
<p class="sign-note">原因:招标文件允许</p></div>
</div>
</div>
</div>
</section>
<!-- 07 出处 -->
<section class="d-sec" id="s-cite">
<div class="d-sec-h"><span class="no">07</span><h2>出处</h2><span class="ref">规范 §7.3</span></div>
<div class="d-grid c3">
<div class="d-box">
<h3>招标原文 → 投标原文</h3>
<div class="cite"><a class="cite-src" href="#">招标文件 · <span class="fig">第 41 页</span></a><q>★ 2U机架式服务器,CPU:配置≥2颗海光架构32C 2.7GHz,<mark>内存: ≥512G DDR4</mark>,系统盘: 2块≥480G SSD……</q></div>
<div class="cite"><a class="cite-src" href="#">技术册 · <span class="fig">第 12 页</span></a><q>第 12 行 <mark>内存:256G DDR4</mark> 无偏离</q></div>
<span class="d-label" style="margin-top:var(--sp-5)">列表行里的出处链接</span>
<a class="cite-link" href="#"><i data-ic="file"></i>技术册 · 第 <span class="fig">12</span> 页</a>
<ul class="d-rules"><li>原句照录,不加空格、不改标点;页码用 Barlow。</li></ul>
</div>
<div class="d-box">
<h3>「未找到」也是出处</h3>
<div class="cite"><a class="cite-src" href="#">招标文件 · <span class="fig">第 44 页</span></a><q>★ 支持硬件BFD,最小报文发送间隔≤50ms,<mark>提供相关证明材料(包含但不限于产品官网公开参数截图证明等)</mark>。</q></div>
<div class="cite missing"><span class="cite-src">投标文件 · 未找到</span><p>已查找:技术册(全部章节)、商务册(附件)</p></div>
<ul class="d-rules"><li>竖线改虚线,写明找过哪些分册。</li></ul>
</div>
<div class="d-box">
<h3>OCR · 截断 · 当前</h3>
<div class="cite on"><a class="cite-src" href="#">商务册 · <span class="fig">第 3 页</span><span class="ocr">OCR</span></a><q>投标人(电子签章):南京某信息技术有限公司 <mark>日期:2026年11月25日</mark></q></div>
<div class="cite" data-clamp><a class="cite-src" href="#">招标文件 · <span class="fig">第 23 页</span></a><q class="clamp"><mark>2.财务状况报告,至少提供:(1)本项目开标时间前6个月内任一时间截点的资产负债表的扫描件1份;(2)本项目开标时间前6个月内任何1月(不含开标当月)利润表月报表的扫描件1份。</mark>3.本项目开标时间前6个月内任何1月的依法缴纳税收和社会保障资金的相关材料的扫描件。</q><button class="expand" data-expand>展开原文</button></div>
<button class="more-cites">另有 2 处</button>
<ul class="d-rules"><li>当前打开的出处竖线加粗到 4px;超过 3 行截断。</li></ul>
</div>
</div>
</section>
<!-- 08 提醒 -->
<section class="d-sec" id="s-alert">
<div class="d-sec-h"><span class="no">08</span><h2>提醒</h2><span class="ref">规范 §7.12 · §7.15</span></div>
<div class="d-grid c2">
<div>
<div class="alert"><i data-ic="info" class="lg"></i><p>清单未确认,检查结果以当前清单为准。</p><button class="btn sm">确认清单</button></div>
<div class="alert warn"><i data-ic="clock" class="lg"></i><p><strong>某县数据中心机房改造项目</strong> <span class="when">今天 17:00 截止</span>,还有 <span class="fig">3</span> 项致命问题待处理。</p></div>
<div class="alert warn"><i data-ic="wallet" class="lg"></i><p>本期检查额度 <span class="when">已用 80%</span>,剩 <span class="fig">4</span> 次。</p><span class="act"><a class="lnk cap" href="#">查看额度</a></span></div>
</div>
<div>
<div class="alert warn"><i data-ic="alert-circle" class="lg"></i><div><p><span class="when">解析失败:文件已加密。</span>已退回 1 次解析额度。</p><span class="sub">关于 ZC-2026-0418 的更正公告.pdf</span></div><span class="act"><button class="btn sm">重新上传</button></span></div>
<div class="alert"><i data-ic="info" class="lg"></i><div><p>出了点问题,请稍后重试。本次操作未扣额度。</p><span class="sub">错误编号 <span class="errno">E-20261103-7Q4K</span></span></div><span class="act"><button class="btn sm">重试</button></span></div>
<div class="alert done"><i data-ic="check-circle" class="lg"></i><p>报告已导出:智慧大厅建设项目-辅助检查报告-第 2 次.pdf</p></div>
</div>
</div>
<ul class="d-rules"><li>和卡片同规格的白条,颜色只在图标和关键字上:信息墨色、提醒赭色、完成绿色。截止临近、额度、解析失败都用赭色,不用红。</li><li>放在相关内容上方,不自动消失;系统错误用中性提醒 + 错误编号。</li></ul>
</section>
<!-- 09 轻提示 -->
<section class="d-sec" id="s-toast">
<div class="d-sec-h"><span class="no">09</span><h2>轻提示</h2><span class="ref">规范 §7.12</span></div>
<div class="d-grid c2">
<div class="d-box">
<h3>样式</h3>
<div class="d-stack" style="display:flex;flex-direction:column;align-items:flex-start">
<div class="toast static"><span class="ok">✓</span><span>清单 v3 已确认</span></div>
<div class="toast static"><span class="ok">✓</span><span>已标记「已修改」 · 复查时会再查</span><span class="sep"></span><button class="lnk">撤销</button></div>
<div class="toast static"><span>已复制错误编号</span></div>
</div>
</div>
<div class="d-box">
<h3>触发 <small>可以点</small></h3>
<div class="d-row"><button class="btn" data-t1>保存资质</button><button class="btn" data-t2>批量确认无误</button><button class="btn" data-t3>连续触发三条</button></div>
<ul class="d-rules"><li>只用于操作结果,4 秒消失,悬停暂停。可撤销的操作给「撤销」。</li><li>失败不用轻提示,用页内提醒说清原因。</li></ul>
</div>
</div>
</section>
<!-- 10 对话框 -->
<section class="d-sec" id="s-dialog">
<div class="d-sec-h"><span class="no">10</span><h2>对话框</h2><span class="ref">规范 §7.14 · 不可撤销的操作和表单</span></div>
<div class="d-grid c-side">
<div class="dialog static" role="dialog" aria-labelledby="sd-t">
<h2 id="sd-t">删除项目「某区智慧社区平台运维服务」?</h2>
<div class="body">
<p>项目下的招标文件、清单、检查结果和报告会一起删除。30 天内可以在回收站恢复。</p>
<div class="field fill"><label for="sd-n">输入项目名称以确认</label><input class="input" id="sd-n" placeholder="某区智慧社区平台运维服务"></div>
</div>
<div class="foot"><button class="btn">取消</button><button class="btn primary" disabled>删除项目</button></div>
</div>
<div class="d-box">
<h3>打开对话框 <small>可以点</small></h3>
<div class="d-stack" style="display:flex;flex-direction:column;align-items:flex-start">
<button class="btn" data-d1><i data-ic="trash"></i>删除项目</button>
<button class="btn" data-d2><i data-ic="send"></i>标记已递交</button>
<button class="btn" data-d3>反馈:判断有误</button>
</div>
<ul class="d-rules"><li>宽 480(确认)/ 640(表单),圆角 12,遮罩墨色 32%。对话框不裁剪下拉和日期浮层。</li><li>标题写要做的事,正文写后果;主按钮在右,写动作,不写「确定」。对话框可以有自己的主按钮。</li><li>删除不用红色,靠输入项目名确认。Esc 关闭,焦点回到触发按钮。</li></ul>
</div>
</div>
</section>
<!-- 11 标签页 筛选 分页 选中 -->
<section class="d-sec" id="s-tabs">
<div class="d-sec-h"><span class="no">11</span><h2>标签页 · 筛选 · 分页 · 选中</h2><span class="ref">规范 §5.3 · §7.18 · §7.20</span></div>
<div class="d-grid c2">
<div class="d-box">
<h3>标签页 <small>可以点,← → 切换</small></h3>
<div class="tabs" role="tablist" aria-label="项目">
<button role="tab" aria-selected="true" aria-controls="tp1">概览</button>
<button role="tab" aria-selected="false" aria-controls="tp2" tabindex="-1">读标<span class="fig muted">26</span></button>
<button role="tab" aria-selected="false" aria-controls="tp3" tabindex="-1">查标<span class="sev fatal">致命<span class="fig">3</span></span></button>
<button role="tab" aria-selected="false" aria-disabled="true" tabindex="-1" data-tip="先完成一次检查">报告</button>
</div>
<div class="tabpanel" id="tp1" role="tabpanel"><p class="muted" style="margin:0">项目要点、关键日期、文件和检查记录。</p></div>
<div class="tabpanel" id="tp2" role="tabpanel" hidden><p class="muted" style="margin:0">项目检查清单 26 条,待确认 2 条。</p></div>
<div class="tabpanel" id="tp3" role="tabpanel" hidden><p class="muted" style="margin:0">第 2 次检查:致命 3、重要 3、提示 3。</p></div>
</div>
<div class="d-box">
<h3>筛选 <small>可以点</small></h3>
<div class="d-row"><div class="seg" role="group" aria-label="按状态筛选"><button aria-pressed="true">进行中 <span class="fig">4</span></button><button aria-pressed="false">全部 <span class="fig">6</span></button><button aria-pressed="false">已递交</button><button aria-pressed="false">已归档</button></div></div>
<div class="d-row"><div class="seg" role="group" aria-label="按级别筛选"><button aria-pressed="false">全部 <span class="fig">9</span></button><button aria-pressed="true"><span class="sev fatal">致命<span class="fig">3</span></span></button><button aria-pressed="false"><span class="sev major">重要<span class="fig">3</span></span></button><button aria-pressed="false"><span class="sev hint">提示<span class="fig">3</span></span></button></div></div>
</div>
</div>
<div class="d-box" style="margin-top:var(--gap-card)">
<h3>分页 <small>可以点</small></h3>
<div class="pagination" data-pager></div>
<ul class="d-rules"><li>当前页浅灰实底 + 加粗,和筛选的按下态一样;只在超过一页时显示。</li></ul>
</div>
<div class="d-grid c2 sel-demo" style="margin-top:var(--gap-card)">
<section class="card flush" aria-labelledby="h-sel1">
<div class="card-h"><h3 id="h-sel1">当前条 · 清单项</h3><small>可以点,↑ ↓ 移动</small></div>
<div data-sel-list role="listbox" aria-label="清单示例">
<div class="item" tabindex="-1" role="option" aria-selected="false"><span class="imp">★</span><div><div class="t">投标保证金人民币 5 万元</div><div class="meta"><span class="tag">资格条件</span><span class="tag">通用</span></div></div><div class="side"><a class="cite-src" href="#">招标文件 · <span class="fig">第 8 页</span></a></div></div>
<div class="item sel" tabindex="0" role="option" aria-selected="true"><span class="imp"></span><div><div class="t">投标有效期 90 天</div><div class="meta"><span class="tag">商务条款</span><span class="tag">通用</span></div></div><div class="side"><a class="cite-src" href="#">招标文件 · <span class="fig">第 21 页</span></a></div></div>
<div class="item" tabindex="-1" role="option" aria-selected="false"><span class="imp">★</span><div><div class="t">应用服务器内存 ≥ 512G DDR4</div><div class="meta"><span class="tag">技术参数</span><span class="tag">采购包 1</span></div></div><div class="side"><a class="cite-src" href="#">招标文件 · <span class="fig">第 41 页</span></a></div></div>
</div>
</section>
<section class="card flush" aria-labelledby="h-sel2">
<div class="card-h"><h3 id="h-sel2">当前条 · 勾选 · 表格行</h3><small>点行定位,点复选框勾选</small></div>
<table class="data hover" data-sel-table>
<thead><tr><th class="ckc"><input type="checkbox" class="ck" aria-label="全选" data-all></th><th>资质</th><th>类型</th><th>有效期至</th></tr></thead>
<tbody>
<tr><td class="ckc mid"><input type="checkbox" class="ck" checked aria-label="选择"></td><td class="mid">ISO/IEC 27001 信息安全管理体系</td><td class="mid"><span class="tag">体系认证</span></td><td class="mid"><span class="num">2027年5月14日</span></td></tr>
<tr class="sel"><td class="ckc mid"><input type="checkbox" class="ck" aria-label="选择"></td><td class="mid">信息系统建设和服务能力等级 CS3</td><td class="mid"><span class="tag">资质证书</span></td><td class="mid"><span class="num">2028年1月9日</span></td></tr>
<tr><td class="ckc mid"><input type="checkbox" class="ck" aria-label="选择"></td><td class="mid">智慧大厅综合管理平台 V2.0</td><td class="mid"><span class="tag">软件著作权</span></td><td class="mid muted">长期</td></tr>
</tbody>
</table>
</section>
</div>
<ul class="d-rules"><li>当前条(清单项、表格行、问题):最浅的灰底 + 左侧 3px 墨色竖条;键盘焦点另加 2px 墨色内框。竖条只表示「当前」,和问题级别无关,致命行不加红竖线。</li><li>勾选(批量处理)只有复选框是选中状态,不加底色和竖条,和当前条可以同时出现。</li><li>筛选、分页、分类导航的「当前项」用浅灰实底 + 加粗。蓝色只留给原文高亮和出处。</li></ul>
</section>
<!-- 12 空状态 骨架屏 进度 -->
<section class="d-sec" id="s-empty">
<div class="d-sec-h"><span class="no">12</span><h2>空状态 · 骨架屏 · 进度</h2><span class="ref">规范 §7.10 · §7.11 · §7.15</span></div>
<div class="d-grid c3">
<div class="d-box">
<h3>空状态</h3>
<div class="empty" style="padding:var(--sp-6) 0"><i data-ic="file" class="xl"></i><b>还没有招标文件</b><p>上传后,标博士会逐条提取要求,每条都附原文出处。</p><button class="btn lg primary"><i data-ic="upload"></i>上传招标文件</button><span class="cost" style="margin-top:var(--sp-2)">扣 1 次解析 · 剩 <span class="fig">37</span> 次</span></div>
</div>
<div class="d-box">
<h3>骨架屏</h3>
<div aria-busy="true" aria-label="正在加载清单">
<div class="skel-row"><span class="skel"></span><div><span class="skel w80"></span><span class="skel w30"></span></div><span class="skel"></span></div>
<div class="skel-row"><span class="skel"></span><div><span class="skel w60"></span><span class="skel w20"></span></div><span class="skel"></span></div>
<div class="skel-row"><span class="skel"></span><div><span class="skel w80"></span><span class="skel w30"></span></div><span class="skel"></span></div>
<div class="skel-row"><span class="skel"></span><div><span class="skel w60"></span><span class="skel w20"></span></div><span class="skel"></span></div>
</div>
<ul class="d-rules"><li>静止的浅灰块,不做明暗起伏或扫光;不用转圈占满屏。</li></ul>
</div>
<div class="d-box">
<h3>分阶段进度</h3>
<ol class="steps">
<li class="done"><span class="dot">✓</span><span>识别文件格式</span></li>
<li class="done"><span class="dot">✓</span><span>提取文字</span><span class="fig">186 页</span></li>
<li class="done"><span class="dot">✓</span><span>识别分包</span><span class="fig">2 个包</span></li>
<li class="now"><span class="dot"></span><span>提取清单</span><span class="fig">62%</span></li>
<li class="todo"><span class="dot"></span><span>核对出处</span></li>
</ol>
<div class="progress" style="margin-top:var(--sp-2)"><div class="bar"><i style="width:62%"></i></div><span class="fig">62%</span></div>
<p class="note">可以离开本页,完成后会发站内通知。</p>
</div>
</div>
<div class="d-grid c3" style="margin-top:var(--gap-card)">
<div class="d-box quota"><small>检查剩余</small><span class="big">12</span><span class="unit">/ 20 次</span><div class="bar"><i style="width:40%"></i></div><p>已用 8 次</p></div>
<div class="d-box quota"><small>检查剩余 · 已用 80%</small><span class="big warn">4</span><span class="unit">/ 20 次</span><div class="bar warn"><i style="width:80%"></i></div><p class="warn">已用 80%,剩 4 次</p></div>
<div class="d-box quota"><small>检查剩余 · 已用完</small><span class="big warn">0</span><span class="unit">/ 20 次</span><div class="bar warn"><i style="width:100%"></i></div><p class="warn">本期检查额度已用完,可在额度页提交续费申请</p></div>
</div>
</section>
<!-- 13 提示气泡 菜单 快捷键 -->
<section class="d-sec" id="s-tip">
<div class="d-sec-h"><span class="no">13</span><h2>提示气泡 · 操作菜单 · 快捷键</h2><span class="ref">规范 §6 · §7.21 · §8.2</span></div>
<div class="d-grid c3">
<div class="d-box">
<h3>提示气泡</h3>
<div class="d-row" style="padding-bottom:calc(var(--sp-16) + var(--sp-2))"><span class="hint-u tip-on tip-below tip-start" data-tip="只表示致命问题都已处理,不代表投标文件没有问题。">就绪</span></div>
<div class="d-row"><button class="btn sm" data-tip="我核对过,投标文件这里没问题(例如响应写在别处)">确认无误</button><button class="btn sm" data-tip="我已经在投标文件里改了,复查时会再查">已修改</button><button class="btn sm" data-tip="我知道这个问题,决定不处理(必须填原因)">忽略并注明原因</button></div>
<ul class="d-rules"><li>墨底白字,圆角 8。解释性的话放在这里,不写在界面上。</li></ul>
</div>
<div class="d-box">
<h3>操作菜单 <small>可以点</small></h3>
<div class="d-row"><span class="muted">列表行的「更多」</span><button class="icon-btn" aria-label="更多操作" aria-haspopup="menu" aria-expanded="false" data-tip="更多操作" data-more><i data-ic="more"></i></button></div>
<div class="pop d-open" style="margin-top:var(--sp-3)"><div class="menu" role="menu" aria-label="更多操作(静态展开)" style="width:248px;min-width:0" data-menu-static></div></div>
<ul class="d-rules"><li>会扣点的项第二行写扣多少、还剩多少;不可用的项变禁用灰,右侧写原因。</li></ul>
</div>
<div class="d-box">
<h3>快捷键</h3>
<div data-keys></div>
<p class="note">按页面只列能用的键:这是查标(问题列表)的一组;读标只有 J / K、Enter、?。「④ 列表与表格」和「⑥ 读标」页可以实际用。</p>
</div>
</div>
</section>
</main>
<script src="demo.js"></script>
<script>
const { $, $$, toast, dialog, icon } = Demo;
Demo.demoTop("controls.html");
/* 文本域字数 */
$$("[data-count-field]").forEach((f) => {
const t = $("textarea", f), n = $("[data-n]", f);
t.addEventListener("input", () => { n.textContent = t.value.length; });
t.addEventListener("blur", () => { const bad = t.value.trim().length > 0 && t.value.trim().length < 5; f.classList.toggle("err", bad); $(".msg", f).innerHTML = bad ? '<span class="ico w">!</span>至少写 5 个字,说明为什么不处理' : "报告和处理记录里会显示这段原因。"; });
});
/* 展开原文 */
$$("[data-expand]").forEach((b) => b.onclick = () => { const q = b.previousElementSibling; const on = q.classList.toggle("clamp"); q.style.webkitLineClamp = on ? "" : "unset"; b.textContent = on ? "展开原文" : "收起"; });
/* 额度不足:按钮仍可点,点了说明原因 */
$("[data-nomore]").onclick = () => dialog({ title: "本期检查额度已用完", ok: "提交续费申请", cancel: "关闭",
body: "<p>这次检查需要 1 次检查额度,本期 20 次已用完。可以在额度页提交续费申请(对公转账后由运营开通),或等下期(2027-11-01)发放。</p><p class='muted cap'>24 小时内对同一项目的复查不扣额度。</p>",
onOk: () => toast("(演示)打开额度页的续费申请", { ok: false }) });
/* 开关:立即生效,给轻提示 */
$$("[data-sw-toast]").forEach((s) => s.addEventListener("click", () => toast(`${s.dataset.swToast}已${s.getAttribute("aria-checked") === "true" ? "打开" : "关闭"}`)));
/* AI 产出 → 确认(Q2):去掉虚线,换成署名行 */
(function () {
const b = $("[data-ai]");
const set = (done, act) => { b.classList.toggle("done", done); if (act) $("[data-ai-act]", b).textContent = act; };
$("[data-ai-ok]", b).onclick = () => { set(true, "确认无误"); toast("已确认", { undo: () => set(false) }); };
$("[data-ai-edit]", b).onclick = () => toast("(演示)修改后同样换成署名行,动作写「已修改」", { ok: false });
$("[data-ai-undo]", b).onclick = (e) => { e.preventDefault(); set(false); $("[data-ai-ok]", b).focus(); };
})();
/* 日期输入(Q10):共用组件 Demo.dateInput(),.dt[data-dt] 自动初始化,这里不用写脚本 */
/* 当前条(§7.18):清单项 */
(function () {
const box = $("[data-sel-list]"), its = $$(".item", box);
const sel = (it, focus) => { its.forEach((x) => { const on = x === it; x.classList.toggle("sel", on); x.setAttribute("aria-selected", on); x.tabIndex = on ? 0 : -1; }); if (focus) it.focus(); };
its.forEach((it) => {
it.addEventListener("click", (e) => { if (!e.target.closest("a")) sel(it); });
it.addEventListener("focus", () => it.classList.toggle("focus", it.matches(":focus-visible")));
it.addEventListener("blur", () => it.classList.remove("focus"));
});
box.addEventListener("keydown", (e) => {
const k = its.indexOf(document.activeElement); if (k < 0) return;
const d = e.key === "ArrowDown" ? 1 : e.key === "ArrowUp" ? -1 : 0; if (!d) return;
e.preventDefault(); sel(its[(k + d + its.length) % its.length], true);
});
$$(".cite-src", box).forEach((a) => a.addEventListener("click", (e) => { e.preventDefault(); toast("(演示)在原文查看器里打开,原句用蓝铅笔浅底高亮", { ok: false }); }));
})();
/* 表格行:点行 = 当前条(tr.sel);复选框 = 勾选,只改复选框本身(§7.18) */
(function () {
const t = $("[data-sel-table]"), all = $("[data-all]", t), cks = $$("tbody .ck", t);
const sync = () => { const n = cks.filter((c) => c.checked).length; all.checked = n === cks.length; all.indeterminate = n > 0 && n < cks.length; };
cks.forEach((c) => c.addEventListener("change", sync));
all.addEventListener("change", () => { cks.forEach((c) => { c.checked = all.checked; }); sync(); });
$$("tbody tr", t).forEach((tr) => tr.addEventListener("click", (e) => { if (e.target.closest("input")) return; $$("tbody tr", t).forEach((x) => x.classList.toggle("sel", x === tr)); }));
sync();
})();
$$("[data-undo-demo]").forEach((b) => b.onclick = () => toast("(演示)撤销后回到处理按钮", { ok: false }));
/* 轻提示 */
$("[data-t1]").onclick = () => toast("已保存「ISO/IEC 27001 信息安全管理体系」");
$("[data-t2]").onclick = () => toast("已将 3 项「提示」标记为确认无误", { undo: () => toast("已撤销", { ok: false }) });
$("[data-t3]").onclick = () => { toast("清单 v3 已确认"); setTimeout(() => toast("已核对补遗更新"), 300); setTimeout(() => toast("报告已导出"), 600); };
/* 对话框 */
const openD1 = () => dialog({
title: "删除项目「某区智慧社区平台运维服务」?", ok: "删除项目",
body: `<p>项目下的招标文件、清单、检查结果和报告会一起删除。30 天内可以在回收站恢复。</p>
<div class="field fill"><label for="dn">输入项目名称以确认</label><input class="input" id="dn" placeholder="某区智慧社区平台运维服务" autocomplete="off"></div>`,
onOpen: (s) => { const ok = $("[data-ok]", s), i = $("#dn", s); ok.disabled = true; i.addEventListener("input", () => { ok.disabled = i.value.trim() !== "某区智慧社区平台运维服务"; }); },
onOk: () => toast("已删除,30 天内可在回收站恢复")
});
const openD2 = () => dialog({
title: "标记「某市政务服务中心智慧大厅建设项目」为已递交?", ok: "标记已递交",
body: `<div class="alert warn"><i data-ic="alert-circle" class="lg"></i><p>还有 <span class="when">3 项致命问题</span>未处理。标记不受影响,处理记录会如实保留。</p></div>
<p style="margin-top:var(--sp-3)">标记后不能再发起检查;清单和检查结果保留,可以导出报告。</p>`,
onOk: () => toast("已标记为已递交")
});
const openD3 = () => dialog({ // 表单对话框(640):反馈判断有误。忽略原因不用对话框,在问题行内展开(§7.4)
title: "反馈:判断有误", wide: true, ok: "提交反馈",
body: `<p><span class="sev major">重要</span> <b>开标一览表与分项价格表合计不一致</b></p>
<div class="form">
<div class="field"><label id="fb-k">哪里不对 <small>(必填)</small></label>
<div class="pop" data-select><button type="button" class="select" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="fb-k"><span class="val">结论不对</span>${icon("chev-down")}</button>
<ul class="menu" role="listbox"><li role="option" aria-selected="true">结论不对</li><li role="option">出处不对</li><li role="option">级别不合适</li><li role="option">说明看不懂</li></ul></div></div>
<div class="field"><label for="fb-p">投标文件页码 <small>(选填)</small></label><div class="input-unit"><input class="input" id="fb-p" placeholder="例如 3"><span class="unit">/ 120 页</span></div></div>
<div class="field span2"><label for="fb-t">说明 <small>(必填)</small></label><textarea class="input" id="fb-t" placeholder="例如:分项价格表第 3 页有一行是可选项,不计入合计"></textarea><span class="msg">反馈只用于改进检查,不改变这一条的处理状态。</span></div>
</div>`,
onOk: (s) => { const t = $("#fb-t", s); if (!t.value.trim()) { Demo.fieldError(t.closest(".field"), "请写一句哪里不对"); t.focus(); return false; } toast("已收到反馈"); }
});
$("[data-d1]").onclick = openD1;
$("[data-d2]").onclick = openD2;
$("[data-d3]").onclick = openD3;
/* 操作菜单:列表行「更多」,和工作台同一个浮动菜单(Demo.floatMenu);静态展开的那份用 Demo.menuHTML 生成同样的项 */
const MORE = [
{ label: "处理记录" }, { label: "复制为新项目" }, { label: "追加补遗 / 澄清", sub: '扣 1 次解析 · 剩 <span class="fig">37</span> 次' }, "-",
{ label: "标记已递交" }, { label: "归档", disabled: "递交后可用" }, { label: "删除项目" }
];
$("[data-menu-static]").innerHTML = Demo.menuHTML(MORE.map((it, i) => i === 1 ? { ...it, active: true } : it));
$("[data-menu-static]").addEventListener("click", (e) => { const b = e.target.closest("[data-i]"); if (b && !b.hasAttribute("aria-disabled")) toast(`(演示)${b.firstChild.textContent}`, { ok: false }); });
$("[data-keys]").innerHTML = Demo.keysTable(Demo.KEYS.check);
$("[data-more]").addEventListener("click", (e) => {
e.stopPropagation();
Demo.floatMenu(e.currentTarget, MORE, (act) => { if (act === "删除项目") openD1(); else if (act === "标记已递交") openD2(); else toast(`(演示)${act}`, { ok: false }); });
});
/* 分页 */
(function pager() {
const el = $("[data-pager]"); let cur = 1; const total = 86, per = 20, pages = Math.ceil(total / per);
const render = () => {
const nums = []; for (let i = 1; i <= pages; i++) nums.push(i);
el.innerHTML = `<span class="total">共 <span class="fig">${total}</span> 条</span>
<button class="pg" data-p="${cur - 1}" ${cur === 1 ? "disabled" : ""} aria-label="上一页">${icon("chev-left")}</button>
${nums.map((n) => `<button class="pg" data-p="${n}" ${n === cur ? 'aria-current="page"' : ""}>${n}</button>`).join("")}
<button class="pg" data-p="${cur + 1}" ${cur === pages ? "disabled" : ""} aria-label="下一页">${icon("chev-right")}</button>
<span class="jump">跳到 <input class="input" value="${cur}" aria-label="页码"> 页</span>`;
};
el.addEventListener("click", (e) => { const b = e.target.closest("[data-p]"); if (b && !b.disabled) { cur = +b.dataset.p; render(); } });
el.addEventListener("change", (e) => { const v = +e.target.value; if (v >= 1 && v <= pages) cur = v; render(); });
render();
})();
/* ?open=select | date | menu | d1 | d2 | d3:直接打开浮层(截图用) */
addEventListener("load", () => {
const o = new URLSearchParams(location.search).get("open");
const at = (sel, fn) => { const el = document.querySelector(sel); if (el.getBoundingClientRect().bottom > innerHeight) el.scrollIntoView({ block: "start" }); setTimeout(fn, 50); };
if (o === "select") at("#s-select", () => $("#sel-demo .select").click());
if (o === "date") at("#s-date", () => $("#dt-demo .icon-btn").click());
if (o === "menu") at("#s-tip", () => $("[data-more]").click());
if ({ d1: 1, d2: 1, d3: 1 }[o]) ({ d1: openD1, d2: openD2, d3: openD3 })[o]();
});
</script>
</body>
</html>