docs(design): UI 风格定稿(U5),设计规范与 tokens 升到 v0.2
- ui-demo/explore/:工作台风格的六轮方案(A–F、D2–D5),D5 为定稿样张 - tokens.css v0.2:墨色派生的中性浅灰作页面底色;圆角三档(6 / 8 / 12);控件高 36;侧栏渐变与套餐标签变量;旧变量保留兼容 - design-system.md v0.2:页面框架(96px 墨色渐变侧栏、透明顶栏、内容区圆角)、只用功能图标、项目行「下一步 / 更多」列、少写说明 - decisions.md 记录 U5;AGENTS.md 与 design/README 同步 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
545
docs/design/ui-demo/explore/workbench-d.html
Normal file
545
docs/design/ui-demo/explore/workbench-d.html
Normal file
@@ -0,0 +1,545 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
工作台 · 风格方案 D「明亮工作区」(探索稿,不是定稿)
|
||||
数据、文案、交互沿用 ../page-workbench.html 与 ../demo.js。
|
||||
|
||||
本方案相对 design-system.md 改了哪些规则:
|
||||
1. §3.1 页面底色:校样纸 #F5F6F3 → --n-50(墨 3% 混白的中性冷灰);侧边栏、顶栏、卡片纯白。
|
||||
偏绿的 --c-paper / --c-sunken / --c-hover / --c-rule 一律不用,浅底和分隔线只用下面三个从墨派生的值。
|
||||
2. §5.1 层次:只有两种容器——页面(--n-50)和白卡片(圆角 12、1px --n-line、无阴影)。卡片里不再套面板。
|
||||
可点击的整张卡片(本页没有)和浮层才用阴影;行、格的悬停只出 --n-50 底色。
|
||||
3. §5.1 圆角:2px 一档 → 卡片 12、按钮 / 输入框 / 菜单 8、标签 6。
|
||||
4. §7.1 按钮:高 32 → 36(主 / 次);表格行内 32。主按钮墨底白字;次按钮白底 + --c-muted 控件边框(3.9:1);
|
||||
文字按钮悬停出 --n-100 底。普通链接仍是墨色 + 下划线,蓝铅笔只给出处。
|
||||
5. §7.7 表头:凹陷灰底 + 粗底线 → 无底色,12px 辅助灰列名,无线。行高 64,行间 --n-line 发丝线。
|
||||
6. §7.9 项目行:
|
||||
- 「状态」中性描边标签 → --n-100 实底、圆角 6、无描边;全页只有这一种中性标签。
|
||||
- 「就绪」从描边标签改为墨色普通字 + 虚线下划线(悬停说明不变),和「未就绪」并排写在状态标签后面,省掉一列。
|
||||
- 「所投包」并入项目名下方的辅助灰行。
|
||||
- 新增「下一步」列:默认是带箭头的文字链,整行悬停 / 键盘聚焦时变成次按钮,同时浮现「更多」图标按钮。
|
||||
会扣点的操作(发起检查)放在「更多」里,并写明扣多少、还剩多少;有致命问题时「导出检查报告」仍可用。
|
||||
7. 工作台统计:四张卡片 → 一张卡片四格,竖向发丝线分隔,数字 30 → 28(Barlow 600)。
|
||||
每格可点击筛选项目列表,悬停出 --n-50 底,选中格底部 2px 墨色边条。
|
||||
8. §5.3 顶栏:面包屑位置改为企业主体名;中间加全局搜索(白底 + 控件边框、圆角 8、Ctrl K),本页用它筛选项目列表。
|
||||
9. §7.12 截止提醒条:赭色浅底条 → 同规格白卡片,赭色图标 + 赭色时间字。
|
||||
10. §7.4「致命」行左侧 3px 竖线:工作台的待处理列表不用竖线,靠左侧实心「致命」标签扫读。
|
||||
未改:颜色语义、问题级别形状、字体分工、字阶(正文 14 / 标题 16 / 页标题 24)、4px 间距基准、Lucide 图标。
|
||||
-->
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>工作台 · 方案 D 明亮工作区</title>
|
||||
<link rel="icon" href="data:,">
|
||||
<link rel="stylesheet" href="../../system/tokens.css">
|
||||
<link rel="stylesheet" href="../../system/fonts-local.css">
|
||||
<style>
|
||||
/* ---------- 本方案派生变量:浅底和分隔线只从墨派生,不引入新色相 ---------- */
|
||||
:root{
|
||||
--n-50: color-mix(in srgb, var(--c-ink) 3%, white); /* 页面底、行 / 格悬停 */
|
||||
--n-100: color-mix(in srgb, var(--c-ink) 6%, white); /* 标签底、文字按钮悬停、进度槽 */
|
||||
--n-line: color-mix(in srgb, var(--c-ink) 11%, white); /* 发丝线、卡片边 */
|
||||
--ctl-border: var(--c-muted); /* 控件边框 3.9:1 */
|
||||
--primary-hover: color-mix(in srgb, var(--c-ink) 88%, white);
|
||||
|
||||
--r-card: 12px; --r-ctl: 8px; --r-tag: 6px;
|
||||
--btn-h: 36px; --btn-h-sm: 32px;
|
||||
--top-h: 56px;
|
||||
--pad: 32px; /* 页面左右内边距 */
|
||||
--gap: 16px; /* 卡片之间、主列与右栏之间:全页一个节奏 */
|
||||
--card-x: 20px; /* 卡片内左右边距:所有卡片统一 */
|
||||
--rail-w: 296px;
|
||||
--cols: minmax(0,1fr) 120px 120px 64px 168px; /* 项目行栅格 */
|
||||
--col-gap: 16px;
|
||||
}
|
||||
@media (max-width: 1399px){
|
||||
:root{ --pad: 24px; --rail-w: 256px; --cols: minmax(0,1fr) 108px 108px 56px 160px; --col-gap: 12px; }
|
||||
}
|
||||
|
||||
*,*::before,*::after{box-sizing:border-box}
|
||||
html,body{margin:0}
|
||||
body{background:var(--n-50);color:var(--text-primary);font:var(--fw-regular) var(--fs-body)/var(--lh-body) var(--font-sans);-webkit-font-smoothing:antialiased;min-width:1280px}
|
||||
h1,h2,h3,p,ul{margin:0}
|
||||
ul{padding:0;list-style:none}
|
||||
a{color:inherit}
|
||||
button{font:inherit;color:inherit}
|
||||
.fig{font-family:var(--font-figures);font-variant-numeric:tabular-nums;font-weight:var(--fw-bold)}
|
||||
.sec{color:var(--text-secondary)}
|
||||
.cap{font-size:var(--fs-caption);line-height:var(--lh-caption)}
|
||||
.ic{width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
|
||||
.ic.lg{width:20px;height:20px}
|
||||
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
||||
:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px;border-radius:var(--r-ctl)}
|
||||
|
||||
/* ---------- 链接与按钮 ---------- */
|
||||
.lnk{color:var(--text-primary);text-decoration:underline;text-decoration-color:color-mix(in srgb, var(--c-ink) 30%, white);text-underline-offset:3px;transition:text-decoration-color var(--dur-fast) var(--ease)}
|
||||
.lnk:hover{text-decoration-color:var(--c-ink)}
|
||||
.cite{display:inline-flex;align-items:center;gap:4px;color:var(--ai);text-decoration:none;white-space:nowrap;border-radius:var(--r-ctl);padding:2px 6px;margin:-2px -6px;transition:background-color var(--dur-fast) var(--ease)}
|
||||
.cite:hover{background:var(--ai-soft);color:var(--ai-strong)}
|
||||
|
||||
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;height:var(--btn-h);padding:0 14px;border-radius:var(--r-ctl);border:1px solid var(--ctl-border);background:var(--c-white);color:var(--text-primary);font-size:var(--fs-body);line-height:1;white-space:nowrap;text-decoration:none;cursor:pointer;transition:background-color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)}
|
||||
.btn:hover{background:var(--n-50);border-color:var(--c-ink)}
|
||||
.btn:active{background:var(--n-100)}
|
||||
.btn.primary{background:var(--c-ink);border-color:var(--c-ink);color:var(--c-white);font-weight:var(--fw-bold)}
|
||||
.btn.primary:hover{background:var(--primary-hover)}
|
||||
.btn.sm{height:var(--btn-h-sm);padding:0 12px}
|
||||
.tbtn{display:inline-flex;align-items:center;gap:6px;height:var(--btn-h-sm);padding:0 10px;border:0;border-radius:var(--r-ctl);background:transparent;cursor:pointer;white-space:nowrap;text-decoration:none;transition:background-color var(--dur-fast) var(--ease)}
|
||||
.tbtn:hover{background:var(--n-100)}
|
||||
.ibtn{position:relative;display:inline-grid;place-items:center;width:var(--btn-h);height:var(--btn-h);border:0;border-radius:var(--r-ctl);background:transparent;color:var(--text-primary);cursor:pointer;transition:background-color var(--dur-fast) var(--ease)}
|
||||
.ibtn:hover{background:var(--n-100)}
|
||||
.ibtn.sm{width:var(--btn-h-sm);height:var(--btn-h-sm)}
|
||||
|
||||
/* ---------- 标签:中性一种 + 问题级别 ---------- */
|
||||
.tag{display:inline-flex;align-items:center;height:22px;padding:0 8px;border-radius:var(--r-tag);background:var(--n-100);color:var(--text-primary);font-size:var(--fs-caption);white-space:nowrap}
|
||||
.sev{display:inline-flex;align-items:center;gap:4px;height:22px;padding:0 8px;border-radius:var(--r-tag);font-size:var(--fs-caption);font-weight:var(--fw-bold);white-space:nowrap}
|
||||
.sev.fatal{background:var(--sev-fatal);color:var(--c-white)}
|
||||
.sev .fig{font-weight:var(--fw-bold)}
|
||||
.dash{color:var(--text-secondary)}
|
||||
|
||||
/* ---------- 应用框架 ---------- */
|
||||
.app{display:grid;grid-template-columns:var(--sidebar-w) minmax(0,1fr);min-height:100vh}
|
||||
.side{background:var(--c-white);border-right:1px solid var(--n-line)}
|
||||
.side-in{position:sticky;top:0;height:100vh;display:flex;flex-direction:column;padding:0 12px 16px}
|
||||
.brand{display:flex;align-items:center;height:var(--top-h);padding:0 8px;margin-bottom:12px}
|
||||
.brand img{height:26px;display:block}
|
||||
.nav{display:flex;flex-direction:column;gap:2px}
|
||||
.nav a{display:flex;align-items:center;gap:10px;height:var(--btn-h);padding:0 10px;border-radius:var(--r-ctl);text-decoration:none;color:var(--text-secondary);transition:background-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}
|
||||
.nav a:hover{background:var(--n-50);color:var(--text-primary)}
|
||||
.nav a[aria-current]{background:var(--n-100);color:var(--text-primary);font-weight:var(--fw-bold)}
|
||||
.nav .grp{margin:20px 10px 6px;font-size:var(--fs-caption);color:var(--text-secondary)}
|
||||
.side-foot{margin-top:auto;padding:12px 10px 0;border-top:1px solid var(--n-line);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
|
||||
.side-foot b{display:block;color:var(--text-primary);font-size:var(--fs-body);line-height:var(--lh-body);font-weight:var(--fw-bold)}
|
||||
|
||||
.top{position:sticky;top:0;z-index:5;display:grid;grid-template-columns:1fr minmax(0,400px) 1fr;align-items:center;gap:16px;height:var(--top-h);padding:0 var(--pad);background:var(--c-white);border-bottom:1px solid var(--n-line)}
|
||||
.org{justify-self:start;display:inline-flex;align-items:center;gap:6px;color:var(--text-secondary)}
|
||||
.org b{color:var(--text-primary);font-weight:var(--fw-bold)}
|
||||
.search{position:relative;display:flex;align-items:center;height:var(--btn-h);border:1px solid var(--ctl-border);border-radius:var(--r-ctl);background:var(--c-white);transition:border-color var(--dur-fast) var(--ease)}
|
||||
.search:hover{border-color:var(--c-ink)}
|
||||
.search:focus-within{border-color:var(--c-ink);box-shadow:0 0 0 1px var(--c-ink)}
|
||||
.search > .ic{position:absolute;left:11px;color:var(--text-secondary);pointer-events:none}
|
||||
.search input{flex:1;min-width:0;height:100%;border:0;background:transparent;padding:0 8px 0 34px;font:inherit;color:var(--text-primary);outline:0}
|
||||
.search input::placeholder{color:var(--text-secondary)}
|
||||
.search input::-webkit-search-cancel-button{display:none}
|
||||
.kbd{flex:none;margin-right:6px;padding:0 6px;height:22px;display:inline-flex;align-items:center;border-radius:var(--r-tag);background:var(--n-100);color:var(--text-secondary);font-size:var(--fs-caption);font-family:var(--font-figures);font-weight:var(--fw-bold)}
|
||||
.top-r{justify-self:end;display:flex;align-items:center;gap:4px}
|
||||
.qchip{display:inline-flex;align-items:center;gap:6px;height:var(--btn-h-sm);padding:0 10px;color:var(--text-secondary)}
|
||||
.qchip .fig{color:var(--text-primary);font-size:15px}
|
||||
.qchip .sep{width:1px;height:12px;background:var(--n-line);margin:0 4px}
|
||||
.dot{position:absolute;top:8px;right:8px;width:7px;height:7px;border-radius:50%;background:var(--c-ink);box-shadow:0 0 0 2px var(--c-white)}
|
||||
|
||||
.content{padding:28px var(--pad) 48px}
|
||||
|
||||
/* ---------- 页头 ---------- */
|
||||
.ph{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;margin-bottom:20px}
|
||||
.ph h1{font-family:var(--font-serif);font-weight:var(--fw-bold);font-size:var(--fs-h1);line-height:var(--lh-h1)}
|
||||
.ph p{margin-top:2px;color:var(--text-secondary)}
|
||||
.ph .act{display:flex;align-items:center;gap:12px}
|
||||
.cost{font-size:var(--fs-caption);color:var(--text-secondary);white-space:nowrap}
|
||||
.cost .fig{color:var(--text-primary)}
|
||||
|
||||
/* ---------- 唯一的容器:白卡片 ---------- */
|
||||
.card{background:var(--c-white);border:1px solid var(--n-line);border-radius:var(--r-card)}
|
||||
.ch{display:flex;align-items:center;gap:12px;min-height:56px;padding:12px var(--card-x)}
|
||||
.ch h2,.ch h3{font-size:var(--fs-title);line-height:var(--lh-title);font-weight:var(--fw-bold)}
|
||||
.ch .more{margin-left:auto}
|
||||
.cf{padding:12px var(--card-x);border-top:1px solid var(--n-line)}
|
||||
.stack > * + *{margin-top:var(--gap)}
|
||||
|
||||
/* 截止提醒 */
|
||||
.alert{display:flex;align-items:center;gap:14px;padding:14px var(--card-x);margin-bottom:var(--gap)}
|
||||
.alert > .ic{color:var(--c-ochre)}
|
||||
.alert p{flex:1}
|
||||
.alert .when{color:var(--warn-text);font-weight:var(--fw-bold)}
|
||||
|
||||
/* 统计:一张卡片四格 */
|
||||
.stats{display:grid;grid-template-columns:repeat(4,1fr);margin-bottom:var(--gap);overflow:hidden}
|
||||
.stat{position:relative;display:grid;grid-template-rows:auto auto auto;justify-items:start;gap:2px;padding:16px var(--card-x) 16px;border:0;border-left:1px solid var(--n-line);background:transparent;text-align:left;cursor:pointer;transition:background-color var(--dur-fast) var(--ease)}
|
||||
.stat:first-child{border-left:0}
|
||||
.stat:hover{background:var(--n-50)}
|
||||
.stat::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--c-ink);transform:scaleY(0);transform-origin:bottom;transition:transform var(--dur-fast) var(--ease)}
|
||||
.stat[aria-pressed="true"]::after{transform:scaleY(1)}
|
||||
.stat[aria-pressed="true"] small{color:var(--text-primary);font-weight:var(--fw-bold)}
|
||||
.stat:focus-visible{outline-offset:-2px;border-radius:0}
|
||||
.stat small{font-size:var(--fs-body);color:var(--text-secondary)}
|
||||
.stat .big{font-family:var(--font-figures);font-variant-numeric:tabular-nums;font-weight:var(--fw-bold);font-size:28px;line-height:36px}
|
||||
.stat .big.fatal{color:var(--sev-fatal)}
|
||||
.stat > .ic{position:absolute;top:18px;right:var(--card-x);color:var(--text-secondary);opacity:0;transition:opacity var(--dur-fast) var(--ease)}
|
||||
.stat:hover > .ic,.stat:focus-visible > .ic,.stat[aria-pressed="true"] > .ic{opacity:1}
|
||||
.stat[aria-pressed="true"] > .ic{color:var(--text-primary)}
|
||||
.stat p{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
|
||||
.stat p.due{color:var(--warn-text);font-weight:var(--fw-bold)}
|
||||
|
||||
/* 两栏 */
|
||||
.wb{display:grid;grid-template-columns:minmax(0,1fr) var(--rail-w);gap:var(--gap);align-items:start}
|
||||
|
||||
/* 项目卡片头:筛选 */
|
||||
.filters{display:flex;align-items:center;gap:2px;margin-left:8px}
|
||||
.filters .tbtn{color:var(--text-secondary)}
|
||||
.filters .tbtn .fig{font-size:15px}
|
||||
.filters .tbtn[aria-pressed="true"]{background:var(--n-100);color:var(--text-primary);font-weight:var(--fw-bold)}
|
||||
.clear{margin-left:auto;color:var(--text-primary)}
|
||||
|
||||
/* 项目行 */
|
||||
.grid-cols{display:grid;grid-template-columns:var(--cols);column-gap:var(--col-gap);align-items:center;padding:0 var(--card-x)}
|
||||
.cols{padding-bottom:8px;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
|
||||
.cols .r,.row .r{justify-self:end;text-align:right}
|
||||
.cols .nx-h{padding-right:48px}
|
||||
.row{position:relative;min-height:64px;padding-top:10px;padding-bottom:10px;border-top:1px solid var(--n-line);cursor:pointer;transition:background-color var(--dur-fast) var(--ease)}
|
||||
.row:hover,.row:focus-within{background:var(--n-50)}
|
||||
.row:has(.pname:focus-visible){outline:2px solid var(--focus-ring);outline-offset:-2px}
|
||||
.pname{display:block;font-weight:var(--fw-bold);text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;outline:0}
|
||||
.pname::after{content:"";position:absolute;inset:0}
|
||||
.psub{display:block;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.due b{display:block;font-weight:var(--fw-regular);white-space:nowrap}
|
||||
.due small{display:block;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary);white-space:nowrap}
|
||||
.due.today b{color:var(--warn-text);font-weight:var(--fw-bold)}
|
||||
.due.soon b{color:var(--warn-text)}
|
||||
.due.past b{color:var(--text-secondary)}
|
||||
.st{display:flex;align-items:center;gap:8px;white-space:nowrap}
|
||||
.ready{position:relative;z-index:1;text-decoration:underline dashed;text-decoration-color:color-mix(in srgb, var(--c-ink) 40%, white);text-underline-offset:4px;cursor:help}
|
||||
.row.arch .pname,.row.arch .due b{color:var(--text-secondary);font-weight:var(--fw-regular)}
|
||||
|
||||
/* 下一步:默认文字链,行悬停 / 聚焦变次按钮,同时浮现「更多」 */
|
||||
.nx-cell{display:flex;align-items:center;justify-content:flex-end;gap:4px}
|
||||
.nx{position:relative;z-index:1;display:inline-flex;align-items:center;gap:4px;height:var(--btn-h-sm);padding:0 10px 0 12px;border:1px solid transparent;border-radius:var(--r-ctl);background:transparent;color:var(--text-primary);white-space:nowrap;cursor:pointer;transition:background-color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)}
|
||||
.nx span{text-decoration:underline;text-decoration-color:color-mix(in srgb, var(--c-ink) 30%, white);text-underline-offset:3px}
|
||||
.nx .ic{color:var(--text-secondary);transition:transform var(--dur-fast) var(--ease)}
|
||||
.row:hover .nx,.row:focus-within .nx{background:var(--c-white);border-color:var(--ctl-border)}
|
||||
.row:hover .nx span,.row:focus-within .nx span{text-decoration-color:transparent}
|
||||
.row .nx:hover{border-color:var(--c-ink)}
|
||||
.row .nx:hover .ic{transform:translateX(2px);color:var(--text-primary)}
|
||||
.row .ibtn{position:relative;z-index:1;opacity:0;transition:opacity var(--dur-fast) var(--ease),background-color var(--dur-fast) var(--ease)}
|
||||
.row:hover .ibtn,.row:focus-within .ibtn,.row .ibtn[aria-expanded="true"]{opacity:1}
|
||||
.empty{padding:32px var(--card-x);border-top:1px solid var(--n-line);text-align:center}
|
||||
.empty b{display:block;font-weight:var(--fw-bold)}
|
||||
.empty p{color:var(--text-secondary);margin-top:4px}
|
||||
|
||||
/* 待处理的致命问题 */
|
||||
.todo{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;column-gap:16px;align-items:center;padding:12px var(--card-x);border-top:1px solid var(--n-line)}
|
||||
.todo b{display:block;font-weight:var(--fw-bold)}
|
||||
.todo .psub{white-space:normal}
|
||||
.todo .cite{margin-right:2px}
|
||||
|
||||
/* 右栏 */
|
||||
.rail .ch{min-height:52px;padding-bottom:4px}
|
||||
.rbody{padding:4px var(--card-x) 16px}
|
||||
.task-h{display:flex;align-items:baseline;gap:8px}
|
||||
.task-h b{font-weight:var(--fw-bold)}
|
||||
.task-h .cap{margin-left:auto}
|
||||
.pbar{display:flex;align-items:center;gap:10px;margin-top:10px}
|
||||
.bar{flex:1;height:6px;border-radius:3px;background:var(--n-100);overflow:hidden}
|
||||
.bar i{display:block;height:100%;background:var(--c-ink);border-radius:3px}
|
||||
.queue{display:flex;gap:8px;align-items:flex-start;margin-top:14px;padding-top:12px;border-top:1px solid var(--n-line)}
|
||||
.queue .ic{margin-top:2px;color:var(--text-secondary)}
|
||||
.q2{display:grid;grid-template-columns:1fr 1fr;gap:20px}
|
||||
.q2 small{display:block;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
|
||||
.q2 .big{font-family:var(--font-figures);font-variant-numeric:tabular-nums;font-weight:var(--fw-bold);font-size:28px;line-height:36px}
|
||||
.q2 .unit{margin-left:4px;color:var(--text-secondary);font-family:var(--font-figures);font-variant-numeric:tabular-nums}
|
||||
.q2 .bar{margin-top:6px}
|
||||
.links{display:flex;gap:16px}
|
||||
.log li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;padding:8px 0;border-top:1px solid var(--n-line);font-size:var(--fs-caption);line-height:var(--lh-caption)}
|
||||
.log li:first-child{border-top:0;padding-top:0}
|
||||
.log li > span:last-child{color:var(--text-secondary);font-family:var(--font-figures);font-variant-numeric:tabular-nums;font-size:13px}
|
||||
.log .who{font-weight:var(--fw-bold)}
|
||||
.log .ok{color:var(--confirmed);white-space:nowrap}
|
||||
|
||||
/* ---------- 浮层:菜单、提示、轻提示 ---------- */
|
||||
.menu{position:fixed;z-index:20;min-width:248px;padding:6px;background:var(--c-white);border:1px solid var(--n-line);border-radius:var(--r-ctl);box-shadow:var(--shadow-overlay)}
|
||||
.menu button{display:block;width:100%;padding:7px 10px;border:0;border-radius:6px;background:transparent;text-align:left;cursor:pointer}
|
||||
.menu button:hover,.menu button:focus-visible{background:var(--n-100);outline:0}
|
||||
.menu button small{display:block;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
|
||||
.menu button small .fig{color:var(--text-primary)}
|
||||
.menu hr{border:0;border-top:1px solid var(--n-line);margin:6px 0}
|
||||
[data-tip]{position:relative}
|
||||
[data-tip]:hover::before,[data-tip]:focus-visible::before{content:attr(data-tip);position:absolute;z-index:30;top:calc(100% + 8px);left:50%;transform:translateX(-50%);width:max-content;max-width:260px;padding:6px 10px;border-radius:var(--r-ctl);background:var(--c-ink);color:var(--c-white);font-size:var(--fs-caption);line-height:var(--lh-caption);font-weight:var(--fw-regular);white-space:normal;text-align:left;pointer-events:none}
|
||||
[data-tip].tip-r:hover::before,[data-tip].tip-r:focus-visible::before{left:auto;right:0;transform:none}
|
||||
.toasts{position:fixed;left:50%;bottom:28px;transform:translateX(-50%);z-index:40;display:flex;flex-direction:column;gap:8px;align-items:center}
|
||||
.toast{padding:10px 16px;border-radius:var(--r-ctl);background:var(--c-ink);color:var(--c-white);box-shadow:var(--shadow-overlay)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<aside class="side"><div class="side-in">
|
||||
<a class="brand" href="workbench-d.html"><img src="../../logo/svg/logo-horizontal.svg" alt="标博士"></a>
|
||||
<nav class="nav" aria-label="主导航">
|
||||
<a href="workbench-d.html" aria-current="page"><i data-ic="grid" class="lg"></i>工作台</a>
|
||||
<a href="#" data-demo><i data-ic="cert" class="lg"></i>资质库</a>
|
||||
<a href="#" data-demo><i data-ic="gauge" class="lg"></i>额度</a>
|
||||
<div class="grp">UI demo</div>
|
||||
<a href="../index.html"><i data-ic="book" class="lg"></i>demo 目录</a>
|
||||
</nav>
|
||||
<div class="side-foot"><b>王敏</b>专业版 · 至 2027-10-31</div>
|
||||
</div></aside>
|
||||
|
||||
<div class="main">
|
||||
<header class="top">
|
||||
<span class="org"><b>南京某信息技术有限公司</b><span>/</span>工作台</span>
|
||||
<form class="search" role="search" data-gsearch>
|
||||
<i data-ic="search"></i>
|
||||
<input type="search" placeholder="搜索项目、采购人" aria-label="搜索项目、采购人" aria-keyshortcuts="Control+K" data-q>
|
||||
<span class="kbd" aria-hidden="true">Ctrl K</span>
|
||||
</form>
|
||||
<div class="top-r">
|
||||
<a class="qchip tbtn" href="#" data-demo data-tip="本期剩余额度:解析 37 / 50 次,检查 12 / 20 次" aria-label="本期剩余额度:解析 37 次,检查 12 次">解析<span class="fig">37</span><span class="sep"></span>检查<span class="fig">12</span></a>
|
||||
<button class="ibtn tip-r" aria-label="通知(1 条未读)" data-tip="通知" data-notice><i data-ic="bell" class="lg"></i><span class="dot"></span></button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="content">
|
||||
<div class="ph">
|
||||
<div><h1>工作台</h1><p>11月3日 周二。项目按投标截止时间排序,截止临近的排在前面。</p></div>
|
||||
<div class="act">
|
||||
<span class="cost">扣 1 次解析 · 剩 <span class="fig">37</span> 次</span>
|
||||
<a class="btn primary" href="../forms.html#s-new"><i data-ic="plus"></i>新建项目</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card alert" role="note">
|
||||
<i data-ic="clock" class="lg"></i>
|
||||
<p><b>某县数据中心机房改造项目</b> <span class="when">今天 17:00 截止</span>。清单还是草稿,还没有做过检查。</p>
|
||||
<a class="btn sm" href="../page-reading.html"><i data-ic="list"></i>确认清单</a>
|
||||
</div>
|
||||
|
||||
<section class="card stats" aria-label="项目统计,点击可筛选项目列表" data-stats>
|
||||
<button class="stat" data-f="active" aria-pressed="true"><small>进行中的项目</small><span class="big">4</span><p>已递交 1 · 已归档 1</p><i data-ic="filter"></i></button>
|
||||
<button class="stat" data-f="due7" aria-pressed="false"><small>7 天内截止</small><span class="big">2</span><p class="due">最近:今天 17:00</p><i data-ic="filter"></i></button>
|
||||
<button class="stat" data-f="fatal" aria-pressed="false"><small>待处理的致命问题</small><span class="big fatal">3</span><p>在 1 个项目里</p><i data-ic="filter"></i></button>
|
||||
<button class="stat" data-f="pend" aria-pressed="false"><small>待确认的清单条目</small><span class="big">4</span><p>在 2 个项目里</p><i data-ic="filter"></i></button>
|
||||
</section>
|
||||
|
||||
<div class="wb">
|
||||
<div class="stack">
|
||||
<section class="card" aria-labelledby="h-proj">
|
||||
<div class="ch">
|
||||
<h2 id="h-proj">项目</h2>
|
||||
<div class="filters" role="group" aria-label="按状态筛选" data-seg>
|
||||
<button class="tbtn" aria-pressed="true" data-v="active">进行中 <span class="fig">4</span></button>
|
||||
<button class="tbtn" aria-pressed="false" data-v="all">全部 <span class="fig">6</span></button>
|
||||
<button class="tbtn" aria-pressed="false" data-v="已递交">已递交</button>
|
||||
<button class="tbtn" aria-pressed="false" data-v="已归档">已归档</button>
|
||||
</div>
|
||||
<span class="more" data-chip></span>
|
||||
</div>
|
||||
<div class="cols grid-cols" aria-hidden="true"><span>项目</span><span>投标截止</span><span>状态</span><span class="r">致命</span><span class="r nx-h">下一步</span></div>
|
||||
<ul data-rows aria-label="项目列表"></ul>
|
||||
<p class="cf cap sec">「就绪」只表示致命问题都已处理,不代表投标文件没有问题。</p>
|
||||
</section>
|
||||
|
||||
<section class="card" aria-labelledby="h-todo">
|
||||
<div class="ch">
|
||||
<h2 id="h-todo">待处理的致命问题</h2>
|
||||
<span class="sev fatal">致命<span class="fig">3</span></span>
|
||||
<a class="more lnk cap" href="#" data-demo>全部在查标页</a>
|
||||
</div>
|
||||
<ul>
|
||||
<li class="todo"><span class="sev fatal">致命</span><div><b>报价超过采购包 1 的最高限价</b><span class="psub">智慧大厅建设项目 · 开标一览表 287.60 万元,限价 286.00 万元</span></div><a class="cite" href="#" data-demo><i data-ic="file"></i>报价册 · 第 <span class="fig">1</span> 页</a><a class="btn sm" href="../page-reading.html">去处理</a></li>
|
||||
<li class="todo"><span class="sev fatal">致命</span><div><b>★ 条款疑似不满足:应用服务器内存</b><span class="psub">智慧大厅建设项目 · 响应 256G,要求 ≥ 512G · 上次标记为已修改,问题仍在</span></div><a class="cite" href="#" data-demo><i data-ic="file"></i>技术册 · 第 <span class="fig">12</span> 页</a><a class="btn sm" href="../page-reading.html">去处理</a></li>
|
||||
<li class="todo"><span class="sev fatal">致命</span><div><b>授权委托书的单位签章位置未发现盖章</b><span class="psub">智慧大厅建设项目 · 只看有无,不鉴定真伪</span></div><a class="cite" href="#" data-demo><i data-ic="file"></i>商务册 · 第 <span class="fig">5</span> 页</a><a class="btn sm" href="../page-reading.html">去处理</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="stack rail">
|
||||
<section class="card" aria-labelledby="h-task">
|
||||
<div class="ch"><h3 id="h-task">正在进行的任务</h3></div>
|
||||
<div class="rbody">
|
||||
<div class="task-h"><b>解析补遗 2</b><span class="cap sec">提取清单</span></div>
|
||||
<div class="cap sec">某市医保信息系统等保测评服务</div>
|
||||
<div class="pbar"><div class="bar" role="progressbar" aria-valuenow="62" aria-valuemin="0" aria-valuemax="100" aria-label="解析补遗 2 的进度"><i style="width:62%"></i></div><span class="fig">62%</span></div>
|
||||
<div class="queue"><i data-ic="clock"></i><span class="cap sec">检查排队中 · 智慧社区平台运维 · 前面还有 1 个任务</span></div>
|
||||
<p class="cap sec" style="margin-top:8px">可以离开本页,完成后会发站内通知。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card" aria-labelledby="h-quota">
|
||||
<div class="ch"><h3 id="h-quota">本期额度</h3><span class="more cap sec">至 2027-10-31</span></div>
|
||||
<div class="rbody">
|
||||
<div class="q2">
|
||||
<div><small>解析剩余</small><span class="big">37</span><span class="unit">/ 50</span><div class="bar" aria-hidden="true"><i style="width:26%"></i></div></div>
|
||||
<div><small>检查剩余</small><span class="big">12</span><span class="unit">/ 20</span><div class="bar" aria-hidden="true"><i style="width:40%"></i></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cf links cap"><a class="lnk" href="#" data-demo>扣点规则</a><a class="lnk" href="#" data-demo>查看流水</a></div>
|
||||
</section>
|
||||
|
||||
<section class="card" aria-labelledby="h-log">
|
||||
<div class="ch"><h3 id="h-log">最近处理</h3><a class="more lnk cap" href="#" data-demo>全部记录</a></div>
|
||||
<div class="rbody">
|
||||
<ul class="log">
|
||||
<li><span><span class="who">王敏</span> <span class="ok">✓ 已修改</span>「授权委托书签章」</span><span>14:26</span></li>
|
||||
<li><span><span class="who">王敏</span> 确认了清单 v2 · 智慧大厅</span><span>11-02</span></li>
|
||||
<li><span><span class="who">系统</span> 完成第 2 次检查 · 致命 3</span><span>11-02</span></li>
|
||||
<li><span><span class="who">系统</span> 并入补遗 1 · 更新 2 条</span><span>11-02</span></li>
|
||||
<li><span><span class="who">李工</span> 忽略「其他项目名称」:业绩介绍</span><span>11-01</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const $ = (s, el = document) => el.querySelector(s);
|
||||
const $$ = (s, el = document) => [...el.querySelectorAll(s)];
|
||||
/* Lucide 图标(ISC 许可)路径 */
|
||||
const ICONS = {
|
||||
grid: '<rect x="3" y="3" width="7" height="9" rx="1"/><rect x="14" y="3" width="7" height="5" rx="1"/><rect x="14" y="12" width="7" height="9" rx="1"/><rect x="3" y="16" width="7" height="5" rx="1"/>',
|
||||
cert: '<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 9h10M7 13h6"/><circle cx="16" cy="15" r="2"/>',
|
||||
gauge: '<path d="m12 14 4-4"/><path d="M3.34 19a10 10 0 1 1 17.32 0"/>',
|
||||
bell: '<path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/>',
|
||||
book: '<path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/>',
|
||||
search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
|
||||
plus: '<path d="M5 12h14"/><path d="M12 5v14"/>',
|
||||
clock: '<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>',
|
||||
file: '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8M16 13H8M16 17H8"/>',
|
||||
list: '<path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/><path d="M13 6h8M13 12h8M13 18h8"/>',
|
||||
"chev-right": '<path d="m9 18 6-6-6-6"/>',
|
||||
more: '<circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/>',
|
||||
filter: '<path d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z"/>',
|
||||
x: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>'
|
||||
};
|
||||
const icon = (n, cls = "") => `<svg class="ic ${cls}" viewBox="0 0 24 24" aria-hidden="true">${ICONS[n] || ""}</svg>`;
|
||||
$$("i[data-ic]").forEach((el) => { el.outerHTML = icon(el.dataset.ic, el.className); });
|
||||
|
||||
function toast(msg) {
|
||||
let box = $(".toasts");
|
||||
if (!box) { box = document.createElement("div"); box.className = "toasts"; box.setAttribute("role", "status"); box.setAttribute("aria-live", "polite"); document.body.appendChild(box); }
|
||||
const t = document.createElement("div"); t.className = "toast"; t.textContent = msg; box.appendChild(t);
|
||||
setTimeout(() => t.remove(), 4000);
|
||||
}
|
||||
const DEMO = "(演示)这里只做了工作台,可以打开「某县数据中心」或「智慧大厅」看读标页";
|
||||
|
||||
/* 数据沿用 page-workbench.html;pend 为待确认清单条目数(对应统计「在 2 个项目里」)。
|
||||
next 为「下一步」,menu 为「更多」里的操作;cost 表示会扣点。 */
|
||||
const CHECK_COST = { label: "扣 1 次检查 · 剩 ", left: 12, unit: " 次" };
|
||||
const P = [
|
||||
{ name: "某县数据中心机房改造项目", buyer: "某县大数据管理局", lot: "标段 1", h: 6, due: ["今天 17:00 截止", "剩 6 小时"], cls: "today", st: "编制中", ready: "", fatal: 0, pend: 3, href: "../page-reading.html",
|
||||
next: "确认清单", menu: [["上传投标文件", ""], ["发起检查", "cost"], ["导出清单", ""]] },
|
||||
{ name: "某区智慧社区平台运维服务", buyer: "某区民政局", lot: "全部", h: 53, due: ["剩 2 天 5 小时", "11月5日 16:00"], cls: "soon", st: "已检查", ready: "就绪", fatal: 0, pend: 0,
|
||||
next: "导出检查报告", menu: [["查看检查结果", ""], ["标记为已递交", ""]] },
|
||||
{ name: "某市政务服务中心智慧大厅建设项目", buyer: "某市政务服务管理办公室", lot: "采购包 1", h: 408, due: ["剩 17 天", "11月20日 09:30"], cls: "", st: "已检查", ready: "未就绪", fatal: 3, pend: 0, href: "../page-reading.html",
|
||||
next: "处理致命问题", menu: [["重新检查", "cost"], ["导出检查报告", "致命问题未处理也可以导出"], ["标记为已递交", ""]] },
|
||||
{ name: "某市医保信息系统等保测评服务", buyer: "某市医疗保障局", lot: "全部", h: 600, due: ["剩 25 天", "11月28日 10:00"], cls: "", st: "跟进中", ready: "", fatal: 0, pend: 1,
|
||||
next: "确认清单", menu: [["查看解析进度", "补遗 2 解析中 62%"], ["上传补遗", ""]] },
|
||||
{ name: "某区智慧校园一期", buyer: "某区教育局", lot: "全部", h: -1, due: ["已截止", "已递交"], cls: "past", st: "已递交", ready: "", fatal: 0, pend: 0,
|
||||
next: "归档项目", menu: [["导出检查报告", ""]] },
|
||||
{ name: "某市不动产登记信息平台升级", buyer: "某市自然资源和规划局", lot: "采购包 2", h: -2, due: ["已截止", "已归档"], cls: "past", st: "已归档", ready: "", fatal: 0, pend: 0,
|
||||
next: "查看项目", menu: [["恢复为进行中", ""]] }
|
||||
];
|
||||
const STAT = {
|
||||
active: { label: "进行中", fn: (p) => !["已递交", "已归档"].includes(p.st) },
|
||||
due7: { label: "7 天内截止", fn: (p) => p.h >= 0 && p.h <= 168 },
|
||||
fatal: { label: "有致命问题", fn: (p) => p.fatal > 0 },
|
||||
pend: { label: "有待确认条目", fn: (p) => p.pend > 0 }
|
||||
};
|
||||
let seg = "active", stat = "", q = "";
|
||||
|
||||
function status(p) {
|
||||
let r = "";
|
||||
if (p.ready === "就绪") r = '<span class="ready" tabindex="0" data-tip="只表示致命问题都已处理,不代表投标文件没有问题。">就绪</span>';
|
||||
else if (p.ready) r = `<span>${p.ready}</span>`;
|
||||
return `<span class="st"><span class="tag">${p.st}</span>${r}</span>`;
|
||||
}
|
||||
|
||||
function render() {
|
||||
const rows = P.map((p, i) => [p, i]).filter(([p]) => {
|
||||
if (stat) { if (!STAT[stat].fn(p)) return false; }
|
||||
else if (seg !== "all" && !(seg === "active" ? STAT.active.fn(p) : p.st === seg)) return false;
|
||||
return !q || (p.name + p.buyer).includes(q);
|
||||
});
|
||||
$("[data-rows]").innerHTML = rows.map(([p, i]) => `<li class="row grid-cols ${p.st === "已归档" ? "arch" : ""}" data-i="${i}">
|
||||
<span><a class="pname" href="${p.href || "#"}" title="${p.name}" ${p.href ? "" : "data-demo"}>${p.name}</a><span class="psub">${p.buyer} · ${p.lot}</span></span>
|
||||
<span class="due ${p.cls}"><b>${p.due[0]}</b><small>${p.due[1]}</small></span>
|
||||
${status(p)}
|
||||
<span class="r">${p.fatal ? `<span class="sev fatal">致命<span class="fig">${p.fatal}</span></span>` : '<span class="dash" aria-label="无">—</span>'}</span>
|
||||
<span class="nx-cell"><button class="nx" data-next><span>${p.next}</span>${icon("chev-right")}</button><button class="ibtn sm" aria-label="更多操作:${p.name}" aria-haspopup="menu" aria-expanded="false" data-menu>${icon("more")}</button></span>
|
||||
</li>`).join("")
|
||||
|| `<li class="empty"><b>没有符合条件的项目</b><p>换个关键词,或切到「全部」看看。</p></li>`;
|
||||
$$("[data-seg] button").forEach((b) => b.setAttribute("aria-pressed", !stat && b.dataset.v === seg));
|
||||
$$("[data-stats] .stat").forEach((b) => b.setAttribute("aria-pressed", b.dataset.f === stat || (!stat && b.dataset.f === "active" && seg === "active")));
|
||||
$("[data-chip]").innerHTML = stat ? `<button class="tbtn clear" data-clear>${STAT[stat].label}${icon("x")}<span class="vh">,清除筛选</span></button>` : "";
|
||||
}
|
||||
|
||||
/* 「更多」菜单 */
|
||||
let menuEl = null, menuBtn = null;
|
||||
function closeMenu(focusBack) {
|
||||
if (!menuEl) return;
|
||||
menuEl.remove(); menuEl = null;
|
||||
if (menuBtn) { menuBtn.setAttribute("aria-expanded", "false"); if (focusBack) menuBtn.focus(); }
|
||||
menuBtn = null;
|
||||
}
|
||||
function openMenu(btn, p) {
|
||||
closeMenu();
|
||||
menuBtn = btn; btn.setAttribute("aria-expanded", "true");
|
||||
menuEl = document.createElement("div");
|
||||
menuEl.className = "menu"; menuEl.setAttribute("role", "menu");
|
||||
const items = [["打开项目", ""], ...p.menu].map(([t, s]) => {
|
||||
const sub = s === "cost" ? `${CHECK_COST.label}<span class="fig">${CHECK_COST.left}</span>${CHECK_COST.unit}` : s;
|
||||
return `<button role="menuitem" data-act="${t}">${t}${sub ? `<small>${sub}</small>` : ""}</button>`;
|
||||
});
|
||||
items.splice(1, 0, "<hr>");
|
||||
menuEl.innerHTML = items.join("");
|
||||
document.body.appendChild(menuEl);
|
||||
const r = btn.getBoundingClientRect();
|
||||
menuEl.style.top = `${r.bottom + 4}px`;
|
||||
menuEl.style.left = `${r.right - menuEl.offsetWidth}px`;
|
||||
menuEl.addEventListener("click", (e) => {
|
||||
const it = e.target.closest("[data-act]"); if (!it) return;
|
||||
const a = it.dataset.act; closeMenu(true);
|
||||
if (a === "打开项目" && p.href) location.href = p.href;
|
||||
else toast(`(演示)${a}:${p.name}`);
|
||||
});
|
||||
menuEl.addEventListener("keydown", (e) => {
|
||||
const its = $$("[role=menuitem]", menuEl), k = its.indexOf(document.activeElement);
|
||||
if (e.key === "ArrowDown") { e.preventDefault(); its[(k + 1) % its.length].focus(); }
|
||||
if (e.key === "ArrowUp") { e.preventDefault(); its[(k - 1 + its.length) % its.length].focus(); }
|
||||
});
|
||||
$("[role=menuitem]", menuEl).focus();
|
||||
}
|
||||
|
||||
$("[data-rows]").addEventListener("click", (e) => {
|
||||
const row = e.target.closest(".row"); if (!row) return;
|
||||
const p = P[+row.dataset.i];
|
||||
const m = e.target.closest("[data-menu]");
|
||||
if (m) { e.preventDefault(); e.stopPropagation(); if (menuBtn === m) closeMenu(); else openMenu(m, p); return; }
|
||||
if (e.target.closest(".ready")) return;
|
||||
if (e.target.closest("[data-next]")) {
|
||||
if (p.href) location.href = p.href;
|
||||
else toast(`(演示)${p.next}:${p.name}`);
|
||||
return;
|
||||
}
|
||||
if (!e.target.closest(".pname")) { if (p.href) location.href = p.href; else toast(DEMO); }
|
||||
});
|
||||
document.addEventListener("click", (e) => { if (menuEl && !menuEl.contains(e.target)) closeMenu(); });
|
||||
window.addEventListener("scroll", () => closeMenu(), { passive: true });
|
||||
window.addEventListener("resize", () => closeMenu());
|
||||
|
||||
$("[data-seg]").addEventListener("click", (e) => { const b = e.target.closest("button"); if (!b) return; seg = b.dataset.v; stat = ""; render(); });
|
||||
$("[data-stats]").addEventListener("click", (e) => {
|
||||
const b = e.target.closest(".stat"); if (!b) return;
|
||||
const f = b.dataset.f;
|
||||
if (f === "active") { seg = "active"; stat = ""; }
|
||||
else if (stat === f) { stat = ""; seg = "active"; }
|
||||
else stat = f;
|
||||
render();
|
||||
});
|
||||
$("[data-chip]").addEventListener("click", (e) => { if (e.target.closest("[data-clear]")) { stat = ""; seg = "active"; render(); $("[data-seg] button[aria-pressed=true]").focus(); } });
|
||||
$("[data-q]").addEventListener("input", (e) => { q = e.target.value.trim(); render(); });
|
||||
$("[data-gsearch]").addEventListener("submit", (e) => e.preventDefault());
|
||||
document.addEventListener("click", (e) => { const d = e.target.closest("[data-demo]"); if (d) { e.preventDefault(); toast(DEMO); } });
|
||||
$("[data-notice]").onclick = () => toast("解析完成:补遗 1 已并入清单(新增 2 条,更新 1 条)。");
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") { e.preventDefault(); $("[data-q]").focus(); }
|
||||
if (e.key === "Escape") {
|
||||
if (menuEl) closeMenu(true);
|
||||
else if (document.activeElement === $("[data-q]")) $("[data-q]").blur();
|
||||
}
|
||||
});
|
||||
render();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user