- 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>
437 lines
33 KiB
HTML
437 lines
33 KiB
HTML
<!doctype html>
|
||
<!--
|
||
工作台 · 风格探索方案 A「留白文档」
|
||
内容、数据、交互沿用 ../page-workbench.html;颜色只用 tokens.css 的色值和语义。
|
||
|
||
本方案相对 design-system.md 改了这些规则:
|
||
1. 圆角(§5.1 控件和卡片 2px、浮层 4px):控件 6px,主列白纸面 12px,侧栏当前项、提醒条、表格行悬停 8px,问题级别标签 4px。
|
||
2. 阴影(§5.1 只用于浮层):主列白纸面加一层极轻阴影,只为把白纸从校样纸上托起来;右栏、提醒条、标签都不用阴影。
|
||
3. 分隔方式(§5.1 层次靠分隔线和底色):表头去掉凹陷灰底和加粗下边线,只剩小号辅助灰字;表格不画竖线,行间只留一条极浅分隔线;
|
||
侧栏不画右边线、顶栏不画下边线;右栏三组内容不加卡片框,只用间距和小标题分组。
|
||
4. 字阶(§4.2):H1 24/32 → 32/40;区块标题改用宋体 20/28;项目名保持 16/24;新增「概要句」22/52,句中数字 Barlow 44;
|
||
正文仍为 14/22,最小 12px,字重只用 400 / 600。
|
||
5. 统计卡(§7.9 / 原型的四张统计卡):换成一句概要句,句中数字可点,点了就筛选下方项目列表。
|
||
6. 控件(§7.1):按钮高 32 → 36;分段筛选改为文字标签页,选中用 2px 墨色下划线;搜索框去掉描边,只用校样纸浅底
|
||
(放宽了 §3.2「需要看清边界的控件用 --border-control」:靠浅底、放大镜图标和占位符说明这是输入框);提醒条里的次按钮改为无描边白底。
|
||
7. 状态 / 就绪(§7.9 中性描边标签):改为纯文字;「就绪」用虚下划线提示可悬停查看说明。问题级别标签的形状规则不变(致命实心、重要描边、提示无框)。
|
||
8. 提醒条(§7.12):去掉外框,赭浅底 + 左侧 3px 赭色竖条。
|
||
9. 截止时间:日期一行(数字用 Barlow)+ 剩余时间一行,整列右对齐放在最后一列;颜色规则不变(≤72 小时赭小字,≤24 小时加粗,已截止辅助灰)。
|
||
-->
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>工作台 · 标博士</title>
|
||
<link rel="icon" href="data:,">
|
||
<link rel="stylesheet" href="../../system/tokens.css">
|
||
<link rel="stylesheet" href="../../system/fonts-local.css">
|
||
<style>
|
||
/* ---------- 本方案派生的变量(全部由 tokens.css 现有颜色 color-mix 得到,不引入新色相) ---------- */
|
||
:root {
|
||
--a-sheet-shadow: 0 1px 2px color-mix(in srgb, var(--c-ink) 5%, transparent),
|
||
0 12px 32px -16px color-mix(in srgb, var(--c-ink) 12%, transparent); /* 白纸面的极轻阴影:墨 5% / 12% */
|
||
--a-row-rule: color-mix(in srgb, var(--c-rule) 60%, var(--c-white)); /* 表格行间分隔:分隔线 60% 混白,比 --c-rule 更浅 */
|
||
--a-nav-hover: color-mix(in srgb, var(--c-white) 55%, var(--c-paper)); /* 侧栏悬停:白与校样纸之间 */
|
||
--a-underline: color-mix(in srgb, var(--c-ink) 22%, var(--c-white)); /* 概要句数字的常态下划线:墨 22% */
|
||
--a-radius-ctl: 6px; /* 按钮、输入框、图标按钮 */
|
||
--a-radius-block: 8px; /* 侧栏当前项、提醒条、行悬停 */
|
||
--a-radius-sheet: 12px; /* 主列白纸面 */
|
||
--a-ctl-h: 36px;
|
||
}
|
||
|
||
* { box-sizing: border-box; }
|
||
html { -webkit-text-size-adjust: 100%; }
|
||
body {
|
||
margin: 0; min-width: 1280px;
|
||
background: var(--bg-page); color: var(--text-primary);
|
||
font: var(--fw-regular) var(--fs-body)/var(--lh-body) var(--font-sans);
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
a { color: inherit; }
|
||
button { font: inherit; color: inherit; }
|
||
.fig { font-family: var(--font-figures); font-variant-numeric: tabular-nums; }
|
||
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
||
.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; }
|
||
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: 4px; }
|
||
.lnk { color: var(--text-primary); text-decoration: underline; text-decoration-color: var(--a-underline); text-underline-offset: 3px; }
|
||
.lnk:hover { text-decoration-color: currentColor; }
|
||
|
||
/* ---------- 框架 ---------- */
|
||
.app { display: grid; grid-template-columns: var(--sidebar-w) minmax(0, 1fr); min-height: 100vh; }
|
||
.side { position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column; padding: var(--sp-6) var(--sp-4) var(--sp-6) var(--sp-5); }
|
||
.brand { display: inline-block; padding: 0 var(--sp-2); margin-bottom: var(--sp-10); }
|
||
.brand img { display: block; height: 34px; width: auto; }
|
||
.nav { display: flex; flex-direction: column; gap: 2px; }
|
||
.nav a {
|
||
display: flex; align-items: center; gap: 10px; height: 40px; padding: 0 var(--sp-3);
|
||
border-radius: var(--a-radius-block); color: var(--text-secondary); text-decoration: none;
|
||
}
|
||
.nav a:hover { background: var(--a-nav-hover); color: var(--text-primary); }
|
||
.nav a[aria-current="page"] { background: var(--bg-surface); color: var(--text-primary); font-weight: var(--fw-bold); }
|
||
.nav .demo { margin-top: var(--sp-6); font-size: var(--fs-caption); line-height: var(--lh-caption); height: 32px; }
|
||
.side-foot { margin-top: auto; padding: 0 var(--sp-3); font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--text-secondary); }
|
||
.side-foot b { display: block; font-size: var(--fs-body); line-height: var(--lh-body); font-weight: var(--fw-bold); color: var(--text-primary); }
|
||
|
||
.main { padding: 0 var(--sp-10) var(--sp-16) var(--sp-6); min-width: 0; }
|
||
.top { display: flex; justify-content: flex-end; align-items: center; gap: var(--sp-1); height: 64px; }
|
||
.quota-chip { display: inline-flex; align-items: center; gap: 6px; height: var(--a-ctl-h); padding: 0 var(--sp-3); border-radius: var(--a-radius-ctl); color: var(--text-secondary); text-decoration: none; }
|
||
.quota-chip .fig { font-size: 16px; font-weight: var(--fw-bold); color: var(--text-primary); margin-right: 6px; }
|
||
.quota-chip:hover, .icon-btn:hover { background: var(--a-nav-hover); }
|
||
.icon-btn { position: relative; display: inline-grid; place-items: center; width: var(--a-ctl-h); height: var(--a-ctl-h); border: 0; border-radius: var(--a-radius-ctl); background: none; color: var(--text-secondary); cursor: pointer; }
|
||
.icon-btn:hover { color: var(--text-primary); }
|
||
.icon-btn .dot { position: absolute; top: 8px; right: 8px; width: 7px; height: 7px; border-radius: 50%; background: var(--warn); box-shadow: 0 0 0 2px var(--bg-page); }
|
||
|
||
.layout { display: grid; grid-template-columns: minmax(0, 1fr) clamp(256px, 20.5vw, 296px); gap: clamp(32px, 2.8vw, 48px); align-items: start; max-width: 1240px; }
|
||
|
||
/* ---------- 白纸面 ---------- */
|
||
.sheet { background: var(--bg-surface); border-radius: var(--a-radius-sheet); box-shadow: var(--a-sheet-shadow); padding: var(--sp-10) clamp(36px, 3.3vw, 52px) var(--sp-12); }
|
||
.head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-6); }
|
||
.head h1 { margin: 0; font: var(--fw-bold) 32px/40px var(--font-serif); letter-spacing: .02em; }
|
||
.head .date { margin-left: var(--sp-4); color: var(--text-secondary); font-family: var(--font-sans); font-size: var(--fs-body); font-weight: var(--fw-regular); letter-spacing: 0; }
|
||
.head .actions { display: flex; align-items: center; gap: var(--sp-4); flex: none; }
|
||
.cost { font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--text-secondary); }
|
||
.btn {
|
||
display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: var(--a-ctl-h); padding: 0 var(--sp-4);
|
||
border: 0; border-radius: var(--a-radius-ctl); font-weight: var(--fw-bold); text-decoration: none; cursor: pointer; white-space: nowrap;
|
||
}
|
||
.btn.primary { background: var(--c-ink); color: var(--text-on-dark); }
|
||
.btn.primary:hover { background: color-mix(in srgb, var(--c-ink) 92%, var(--c-white)); }
|
||
.btn.quiet { height: 32px; padding: 0 var(--sp-3); background: var(--bg-surface); color: var(--text-primary); }
|
||
.btn.quiet:hover { background: color-mix(in srgb, var(--c-white) 60%, var(--warn-soft)); }
|
||
|
||
/* 概要句:本页唯一的「大声」元素 */
|
||
.lede { margin: var(--sp-8) 0 0; max-width: 34em; font-size: 22px; line-height: 52px; color: var(--text-primary); }
|
||
.lede .half { display: block; white-space: nowrap; } /* 两半句在分号处固定断行 */
|
||
.n {
|
||
padding: 0 3px; margin: 0 1px; border: 0; background: none; cursor: pointer;
|
||
font: var(--fw-bold) 44px/1 var(--font-figures); font-variant-numeric: tabular-nums; color: var(--text-primary);
|
||
text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 7px; text-decoration-color: var(--a-underline);
|
||
vertical-align: -3px;
|
||
}
|
||
.n:hover { text-decoration-color: currentColor; }
|
||
.n[aria-pressed="true"] { text-decoration-color: currentColor; text-decoration-thickness: 3px; }
|
||
.n.due { color: var(--c-ochre); } /* ≥18px,可用 --c-ochre */
|
||
.n.fatal { color: var(--sev-fatal); }
|
||
.lede-sub { margin: var(--sp-2) 0 0; color: var(--text-secondary); }
|
||
|
||
/* 提醒条 */
|
||
.alert { position: relative; display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-8); padding: var(--sp-3) var(--sp-4) var(--sp-3) var(--sp-5); border-radius: var(--a-radius-block); background: var(--warn-soft); overflow: hidden; }
|
||
.alert::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--warn); }
|
||
.alert > .ic { color: var(--warn); }
|
||
.alert p { margin: 0; flex: 1; min-width: 0; }
|
||
.alert b { color: var(--warn-text); font-weight: var(--fw-bold); }
|
||
|
||
/* 筛选 + 搜索 */
|
||
.bar { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-6); margin-top: var(--sp-10); }
|
||
.tabs { display: flex; gap: var(--sp-6); }
|
||
.tabs button {
|
||
height: var(--a-ctl-h); padding: 0 2px; border: 0; background: none; cursor: pointer;
|
||
font-size: 15px; color: var(--text-secondary); box-shadow: inset 0 -2px 0 transparent;
|
||
}
|
||
.tabs button:hover { color: var(--text-primary); }
|
||
.tabs button[aria-pressed="true"] { color: var(--text-primary); font-weight: var(--fw-bold); box-shadow: inset 0 -2px 0 var(--c-ink); }
|
||
.tabs .fig { margin-left: 4px; font-size: 15px; }
|
||
.search { position: relative; display: block; width: 248px; }
|
||
.search .ic { position: absolute; left: 12px; top: 10px; color: var(--text-secondary); pointer-events: none; }
|
||
.search input {
|
||
width: 100%; height: var(--a-ctl-h); padding: 0 var(--sp-3) 0 36px; border: 0; border-radius: var(--a-radius-ctl);
|
||
background: var(--bg-page); font: inherit; color: var(--text-primary);
|
||
}
|
||
.search input::placeholder { color: var(--text-secondary); }
|
||
.search input:focus-visible { outline-offset: 0; border-radius: var(--a-radius-ctl); background: var(--bg-surface); }
|
||
.filter-note { display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-3); color: var(--text-secondary); }
|
||
.filter-note[hidden] { display: none; }
|
||
.filter-note b { color: var(--text-primary); font-weight: var(--fw-bold); }
|
||
.text-btn { display: inline-flex; align-items: center; gap: 4px; height: 28px; padding: 0 var(--sp-2); border: 0; border-radius: var(--a-radius-ctl); background: none; cursor: pointer; color: var(--text-primary); }
|
||
.text-btn:hover { background: var(--bg-hover); }
|
||
|
||
/* 项目列表 */
|
||
.projects { width: calc(100% + 24px); margin: var(--sp-5) -12px 0; border-collapse: separate; border-spacing: 0; table-layout: fixed; }
|
||
.projects th { padding: 0 12px var(--sp-2); text-align: left; font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: var(--fw-regular); color: var(--text-secondary); white-space: nowrap; }
|
||
.projects td { padding: var(--sp-3) 12px; vertical-align: middle; border-top: 1px solid var(--a-row-rule); }
|
||
.projects tbody tr:first-child td { border-top-color: transparent; }
|
||
.projects tbody tr.row { cursor: pointer; }
|
||
.projects tbody tr.row:hover td { background: var(--bg-hover); }
|
||
.projects tbody tr.row:hover td:first-child { border-radius: var(--a-radius-block) 0 0 var(--a-radius-block); }
|
||
.projects tbody tr.row:hover td:last-child { border-radius: 0 var(--a-radius-block) var(--a-radius-block) 0; }
|
||
.projects tbody tr.row:hover td, .projects tbody tr.row:hover + tr td { border-top-color: transparent; }
|
||
.projects .r { text-align: right; }
|
||
.c-st { width: 72px; } .c-rd { width: 72px; } .c-ft { width: 72px; } .c-due { width: 128px; }
|
||
.projects td:nth-child(2), .projects td:nth-child(3) { white-space: nowrap; }
|
||
.projects th .ic { vertical-align: -3px; margin-left: 2px; }
|
||
.pname { font-size: var(--fs-title); line-height: var(--lh-title); font-weight: var(--fw-bold); text-decoration: none; }
|
||
.row:hover .pname { text-decoration: underline; text-underline-offset: 3px; }
|
||
.psub { color: var(--text-secondary); }
|
||
.psub span + span { margin-left: var(--sp-3); }
|
||
.due { white-space: nowrap; }
|
||
.due .d1 { display: block; font-size: 15px; }
|
||
.due .d1 .fig { font-size: 16px; }
|
||
.due .d2 { display: block; font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--text-secondary); }
|
||
.due.today .d1, .due.today .d2 { color: var(--warn-text); font-weight: var(--fw-bold); }
|
||
.due.soon .d1, .due.soon .d2 { color: var(--warn-text); }
|
||
.due.past .d1 { color: var(--text-secondary); }
|
||
.dash { color: var(--text-secondary); }
|
||
.ready { text-decoration: underline dotted var(--text-secondary); text-underline-offset: 4px; cursor: help; position: relative; }
|
||
.ready:hover::after, .ready:focus-visible::after {
|
||
content: attr(data-tip); position: absolute; left: -8px; top: calc(100% + 8px); z-index: 5; width: 232px; padding: var(--sp-2) var(--sp-3);
|
||
border-radius: var(--a-radius-ctl); background: var(--c-ink); color: var(--text-on-dark); font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: var(--fw-regular);
|
||
box-shadow: var(--shadow-overlay); white-space: normal; text-align: left;
|
||
}
|
||
.empty { padding: var(--sp-10) 12px; text-align: center; color: var(--text-secondary); }
|
||
.empty b { display: block; margin-bottom: var(--sp-1); font-size: var(--fs-title); line-height: var(--lh-title); color: var(--text-primary); font-weight: var(--fw-bold); }
|
||
.note { margin: var(--sp-4) 0 0; font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--text-secondary); }
|
||
|
||
/* 问题级别标签(形状规则不变) */
|
||
.sev { display: inline-flex; align-items: center; gap: 4px; height: 20px; padding: 0 6px; border-radius: 4px; font-size: var(--fs-caption); line-height: 20px; font-weight: var(--fw-bold); white-space: nowrap; }
|
||
.sev.fatal { background: var(--sev-fatal); color: var(--text-on-dark); }
|
||
.sev .fig { font-size: 13px; }
|
||
|
||
/* 待处理的致命问题 */
|
||
.sec-h { display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-12); }
|
||
.sec-h h2 { margin: 0; font: var(--fw-bold) var(--fs-h2)/var(--lh-h2) var(--font-serif); }
|
||
.sec-h .more { margin-left: auto; }
|
||
.issues { list-style: none; margin: var(--sp-4) 0 0; padding: 0; }
|
||
.issues li { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--sp-4); align-items: baseline; padding: var(--sp-3) 0; }
|
||
.issues li + li { margin-top: var(--sp-2); }
|
||
.issues .sev { position: relative; top: -2px; }
|
||
.issues b { display: block; font-size: 15px; line-height: 24px; font-weight: var(--fw-bold); }
|
||
.issues p { margin: 2px 0 0; color: var(--text-secondary); }
|
||
.cite { display: inline-flex; align-items: center; gap: 6px; color: var(--ai); text-decoration: none; white-space: nowrap; }
|
||
.cite:hover { color: var(--ai-strong); text-decoration: underline; text-underline-offset: 3px; }
|
||
.cite .fig { font-size: 15px; }
|
||
|
||
/* ---------- 右栏:直接放在校样纸上 ---------- */
|
||
.rail { padding-top: var(--sp-10); }
|
||
.rail section + section { margin-top: var(--sp-12); }
|
||
.rail h3 { display: flex; align-items: baseline; justify-content: space-between; margin: 0 0 var(--sp-4); font-size: var(--fs-body); line-height: var(--lh-body); font-weight: var(--fw-bold); }
|
||
.rail h3 small { font-size: var(--fs-caption); font-weight: var(--fw-regular); color: var(--text-secondary); }
|
||
.rail h3 small .fig { font-size: 13px; }
|
||
.task-h { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); }
|
||
.task-h span, .task-p { font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--text-secondary); }
|
||
.progress { display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-2); }
|
||
.track { flex: 1; height: 4px; border-radius: 2px; background: var(--c-sunken); overflow: hidden; }
|
||
.track i { display: block; height: 100%; background: var(--c-ink); border-radius: 2px; }
|
||
.progress .fig { font-size: 15px; font-weight: var(--fw-bold); min-width: 34px; text-align: right; }
|
||
.queue { display: flex; gap: var(--sp-2); margin-top: var(--sp-4); color: var(--text-secondary); }
|
||
.queue .ic { margin-top: 3px; }
|
||
.rail .hint { margin: var(--sp-3) 0 0; font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--text-secondary); }
|
||
.quota { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-6); }
|
||
.quota small { display: block; font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--text-secondary); }
|
||
.quota .big { font: var(--fw-bold) var(--fs-figure)/var(--lh-figure) var(--font-figures); font-variant-numeric: tabular-nums; }
|
||
.quota .unit { margin-left: 4px; font-family: var(--font-figures); font-size: 15px; color: var(--text-secondary); }
|
||
.quota .track { margin-top: var(--sp-2); }
|
||
.rail .links { display: flex; gap: var(--sp-5); margin-top: var(--sp-4); font-size: var(--fs-caption); line-height: var(--lh-caption); }
|
||
.log { list-style: none; margin: 0; padding: 0; }
|
||
.log li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-3); padding: 6px 0; }
|
||
.log .t { font-family: var(--font-figures); font-variant-numeric: tabular-nums; font-size: 13px; color: var(--text-secondary); white-space: nowrap; }
|
||
.log .who { font-weight: var(--fw-bold); }
|
||
.log .ok { color: var(--confirmed); white-space: nowrap; }
|
||
|
||
/* 轻提示 */
|
||
.toasts { position: fixed; left: 50%; bottom: var(--sp-8); transform: translateX(-50%); display: grid; gap: var(--sp-2); z-index: 20; }
|
||
.toast { padding: 10px var(--sp-4); border-radius: var(--a-radius-block); background: var(--c-ink); color: var(--text-on-dark); box-shadow: var(--shadow-overlay); }
|
||
|
||
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
<aside class="side">
|
||
<a class="brand" href="../page-workbench.html"><img src="../../logo/svg/logo-horizontal.svg" alt="标博士"></a>
|
||
<nav class="nav" aria-label="主导航">
|
||
<a href="#" aria-current="page"><svg class="ic lg" viewBox="0 0 24 24" aria-hidden="true"><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"/></svg>工作台</a>
|
||
<a href="#" data-demo><svg class="ic lg" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 9h10M7 13h6"/><circle cx="16" cy="15" r="2"/></svg>资质库</a>
|
||
<a href="#" data-demo><svg class="ic lg" viewBox="0 0 24 24" aria-hidden="true"><path d="m12 14 4-4"/><path d="M3.34 19a10 10 0 1 1 17.32 0"/></svg>额度</a>
|
||
<a class="demo" href="../index.html"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><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"/></svg>UI demo 目录</a>
|
||
</nav>
|
||
<div class="side-foot"><b>王敏</b>南京某信息技术有限公司<br>专业版,至 2027-10-31</div>
|
||
</aside>
|
||
|
||
<main class="main">
|
||
<header class="top">
|
||
<a class="quota-chip" href="#quota" title="本期剩余额度">解析<span class="fig">37</span>检查<span class="fig">12</span></a>
|
||
<button class="icon-btn" aria-label="通知(有 1 条未读)" title="通知" data-notice><svg class="ic lg" viewBox="0 0 24 24" aria-hidden="true"><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"/></svg><span class="dot"></span></button>
|
||
<button class="icon-btn" aria-label="快捷键说明" title="快捷键说明(?)" data-keys><svg class="ic lg" viewBox="0 0 24 24" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10"/></svg></button>
|
||
</header>
|
||
|
||
<div class="layout">
|
||
<article class="sheet">
|
||
<div class="head">
|
||
<h1>工作台<span class="date">11月3日 周二</span></h1>
|
||
<div class="actions">
|
||
<span class="cost">扣 1 次解析,剩 <span class="fig">37</span> 次</span>
|
||
<a class="btn primary" href="../forms.html#s-new"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg>新建项目</a>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="lede" data-lede>
|
||
<span class="half"><button class="n" data-f="active" aria-pressed="false"><span data-to="4">4</span><span class="vh">(只看进行中的项目)</span></button> 个项目进行中,<button class="n due" data-f="due7" aria-pressed="false"><span data-to="2">2</span><span class="vh">(只看 7 天内截止的项目)</span></button> 个 7 天内截止;</span>
|
||
<span class="half"><button class="n fatal" data-f="fatal" aria-pressed="false"><span data-to="3">3</span><span class="vh">(只看有致命问题的项目)</span></button> 个致命问题待处理,<button class="n" data-f="pend" aria-pressed="false"><span data-to="4">4</span><span class="vh">(只看有待确认清单条目的项目)</span></button> 条清单待确认。</span>
|
||
</p>
|
||
<p class="lede-sub">另有 1 个已递交、1 个已归档。致命问题都在 1 个项目里,待确认的清单条目在 2 个项目里。</p>
|
||
|
||
<div class="alert" role="note">
|
||
<svg class="ic lg" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
||
<p><b>某县数据中心机房改造项目今天 17:00 截止。</b>清单还是草稿,还没有做过检查。</p>
|
||
<a class="btn quiet" href="../page-reading.html">去确认清单</a>
|
||
</div>
|
||
|
||
<div class="bar">
|
||
<div class="tabs" role="group" aria-label="按状态筛选" data-tabs>
|
||
<button aria-pressed="true" data-v="active">进行中<span class="fig">4</span></button>
|
||
<button aria-pressed="false" data-v="all">全部<span class="fig">6</span></button>
|
||
<button aria-pressed="false" data-v="已递交">已递交</button>
|
||
<button aria-pressed="false" data-v="已归档">已归档</button>
|
||
</div>
|
||
<label class="search"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg><input type="search" placeholder="搜索项目、采购人、编号" aria-label="搜索项目" data-q></label>
|
||
</div>
|
||
<div class="filter-note" data-fnote hidden><span>正在看:<b data-fname></b></span><button class="text-btn" data-clear><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>清除筛选</button></div>
|
||
|
||
<table class="projects">
|
||
<thead><tr><th scope="col">项目</th><th scope="col" class="c-st">状态</th><th scope="col" class="c-rd">就绪</th><th scope="col" class="c-ft">致命</th><th scope="col" class="c-due r" aria-sort="ascending">投标截止<svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14"/><path d="m19 12-7 7-7-7"/></svg></th></tr></thead>
|
||
<tbody data-rows></tbody>
|
||
</table>
|
||
<p class="note">按投标截止时间排序,截止临近的排在前面。<span id="ready-note">「就绪」只表示致命问题都已处理,不代表投标文件没有问题。</span></p>
|
||
|
||
<section id="fatal" aria-labelledby="fatal-h">
|
||
<div class="sec-h"><h2 id="fatal-h">待处理的致命问题</h2><span class="sev fatal">致命<span class="fig">3</span></span><a class="more lnk" href="#" data-demo>去查标页处理</a></div>
|
||
<ul class="issues">
|
||
<li><span class="sev fatal">致命</span><div><b>报价超过采购包 1 的最高限价</b><p>智慧大厅建设项目。开标一览表 287.60 万元,限价 286.00 万元。</p></div><a class="cite" href="#" data-cite>报价册 · 第 <span class="fig">1</span> 页</a></li>
|
||
<li><span class="sev fatal">致命</span><div><b>★ 条款疑似不满足:应用服务器内存</b><p>智慧大厅建设项目。响应 256G,要求 ≥ 512G。上次标记为已修改,问题仍在。</p></div><a class="cite" href="#" data-cite>技术册 · 第 <span class="fig">12</span> 页</a></li>
|
||
<li><span class="sev fatal">致命</span><div><b>授权委托书的单位签章位置未发现盖章</b><p>智慧大厅建设项目。只看有无,不鉴定真伪。</p></div><a class="cite" href="#" data-cite>商务册 · 第 <span class="fig">5</span> 页</a></li>
|
||
</ul>
|
||
</section>
|
||
</article>
|
||
|
||
<aside class="rail" aria-label="任务、额度与最近处理">
|
||
<section>
|
||
<h3>正在进行的任务</h3>
|
||
<div class="task-h"><b>解析补遗 2</b><span>提取清单</span></div>
|
||
<div class="task-p">某市医保信息系统等保测评服务</div>
|
||
<div class="progress"><div class="track" 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"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg><span>检查排队中:智慧社区平台运维,前面还有 1 个任务</span></div>
|
||
<p class="hint">可以离开本页,完成后会发站内通知。</p>
|
||
</section>
|
||
|
||
<section id="quota">
|
||
<h3>本期额度<small>至 <span class="fig">2027-10-31</span></small></h3>
|
||
<div class="quota">
|
||
<div><small>解析剩余</small><span class="big">37</span><span class="unit">/ 50</span><div class="track" 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="track" aria-hidden="true"><i style="width:40%"></i></div></div>
|
||
</div>
|
||
<div class="links"><a class="lnk" href="#" data-demo>扣点规则</a><a class="lnk" href="#" data-demo>查看流水</a></div>
|
||
</section>
|
||
|
||
<section>
|
||
<h3>最近处理<a class="lnk" href="#" data-demo style="font-size:var(--fs-caption);font-weight:400">全部记录</a></h3>
|
||
<ul class="log">
|
||
<li><span><span class="who">王敏</span> <span class="ok">✓ 已修改</span>「授权委托书签章」</span><span class="t">14:26</span></li>
|
||
<li><span><span class="who">王敏</span> 确认了清单 v2:智慧大厅</span><span class="t">11-02</span></li>
|
||
<li><span><span class="who">系统</span> 完成第 2 次检查,致命 3</span><span class="t">11-02</span></li>
|
||
<li><span><span class="who">系统</span> 并入补遗 1,更新 2 条</span><span class="t">11-02</span></li>
|
||
<li><span><span class="who">李工</span> 忽略「其他项目名称」:业绩介绍</span><span class="t">11-01</span></li>
|
||
</ul>
|
||
</section>
|
||
</aside>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
const $ = (s, el = document) => el.querySelector(s);
|
||
const $$ = (s, el = document) => [...el.querySelectorAll(s)];
|
||
const fig = (s) => String(s).replace(/(\d[\d:.]*)/g, '<span class="fig">$1</span>');
|
||
|
||
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);
|
||
}
|
||
|
||
/* 数据沿用 page-workbench.html;pend = 有待确认的清单条目(4 条在 2 个项目里) */
|
||
const P = [
|
||
{ name: "某县数据中心机房改造项目", buyer: "某县大数据管理局", lot: "标段 1", h: 6, d1: "今天 17:00", d2: "剩 6 小时", cls: "today", st: "编制中", ready: "", fatal: 0, pend: 1, href: "../page-reading.html" },
|
||
{ name: "某区智慧社区平台运维服务", buyer: "某区民政局", lot: "全部", h: 53, d1: "11月5日 16:00", d2: "剩 2 天 5 小时", cls: "soon", st: "已检查", ready: "就绪", fatal: 0 },
|
||
{ name: "某市政务服务中心智慧大厅建设项目", buyer: "某市政务服务管理办公室", lot: "采购包 1", h: 408, d1: "11月20日 09:30", d2: "剩 17 天", cls: "", st: "已检查", ready: "未就绪", fatal: 3, href: "../page-reading.html" },
|
||
{ name: "某市医保信息系统等保测评服务", buyer: "某市医疗保障局", lot: "全部", h: 600, d1: "11月28日 10:00", d2: "剩 25 天", cls: "", st: "跟进中", ready: "", fatal: 0, pend: 1 },
|
||
{ name: "某区智慧校园一期", buyer: "某区教育局", lot: "全部", h: -1, d1: "已截止", d2: "", cls: "past", st: "已递交", ready: "", fatal: 0 },
|
||
{ name: "某市不动产登记信息平台升级", buyer: "某市自然资源和规划局", lot: "采购包 2", h: -2, d1: "已截止", d2: "", cls: "past", st: "已归档", ready: "", fatal: 0 }
|
||
];
|
||
const DONE = ["已递交", "已归档"];
|
||
const LEDE = {
|
||
active: ["进行中的项目", (p) => !DONE.includes(p.st)],
|
||
due7: ["7 天内截止的项目", (p) => p.h >= 0 && p.h <= 168],
|
||
fatal: ["有致命问题待处理的项目", (p) => p.fatal > 0],
|
||
pend: ["有清单条目待确认的项目", (p) => p.pend > 0]
|
||
};
|
||
let seg = "active", lede = "", q = "";
|
||
|
||
function match(p) {
|
||
const bySeg = lede ? LEDE[lede][1](p) : seg === "all" || (seg === "active" ? !DONE.includes(p.st) : p.st === seg);
|
||
return bySeg && (!q || (p.name + p.buyer).includes(q));
|
||
}
|
||
function render() {
|
||
const rows = P.filter(match);
|
||
$("[data-rows]").innerHTML = rows.map((p, i) => `<tr class="row" data-i="${P.indexOf(p)}">
|
||
<td><a class="pname" href="${p.href || "#"}">${p.name}</a><div class="psub"><span>${p.buyer}</span><span>${fig(p.lot)}</span></div></td>
|
||
<td>${p.st}</td>
|
||
<td>${p.ready === "就绪" ? '<span class="ready" tabindex="0" aria-describedby="ready-note" data-tip="只表示致命问题都已处理,不代表投标文件没有问题。">就绪</span>' : p.ready || '<span class="dash" aria-label="无">—</span>'}</td>
|
||
<td>${p.fatal ? `<span class="sev fatal">致命<span class="fig">${p.fatal}</span></span>` : '<span class="dash" aria-label="无">—</span>'}</td>
|
||
<td class="r due ${p.cls}"><span class="d1">${fig(p.d1)}</span>${p.d2 ? `<span class="d2">${p.d2}</span>` : ""}</td></tr>`).join("")
|
||
|| `<tr><td colspan="5"><div class="empty"><b>没有符合条件的项目</b>换个关键词,或切到「全部」看看。</div></td></tr>`;
|
||
}
|
||
function setSeg(v) {
|
||
seg = v; lede = "";
|
||
$$("[data-tabs] button").forEach((b) => b.setAttribute("aria-pressed", b.dataset.v === v));
|
||
$$(".n").forEach((b) => b.setAttribute("aria-pressed", "false"));
|
||
$("[data-fnote]").hidden = true;
|
||
render();
|
||
}
|
||
function setLede(f) {
|
||
if (lede === f) { setSeg("active"); return; }
|
||
lede = f;
|
||
$$("[data-tabs] button").forEach((b) => b.setAttribute("aria-pressed", "false"));
|
||
$$(".n").forEach((b) => b.setAttribute("aria-pressed", b.dataset.f === f));
|
||
$("[data-fname]").textContent = LEDE[f][0];
|
||
$("[data-fnote]").hidden = false;
|
||
render();
|
||
}
|
||
|
||
$("[data-tabs]").addEventListener("click", (e) => { const b = e.target.closest("button"); if (b) setSeg(b.dataset.v); });
|
||
$$(".n").forEach((b) => b.addEventListener("click", () => setLede(b.dataset.f)));
|
||
$("[data-clear]").addEventListener("click", () => { setSeg("active"); $("[data-tabs] button").focus(); });
|
||
$("[data-q]").addEventListener("input", (e) => { q = e.target.value.trim(); render(); });
|
||
$("[data-rows]").addEventListener("click", (e) => {
|
||
const r = e.target.closest("tr.row"); if (!r) return;
|
||
const p = P[r.dataset.i];
|
||
if (!p.href) { e.preventDefault(); toast("(演示)只做了「读标」组合页"); return; }
|
||
if (!e.target.closest("a")) location.href = p.href;
|
||
});
|
||
document.addEventListener("click", (e) => {
|
||
const a = e.target.closest("[data-demo], [data-cite]"); if (!a) return;
|
||
e.preventDefault();
|
||
toast(a.matches("[data-cite]") ? "(演示)出处会在原文查看器里打开" : "(演示)这一页还没做");
|
||
});
|
||
$("[data-notice]").onclick = () => toast("解析完成:补遗 1 已并入清单(新增 2 条,更新 1 条)。");
|
||
$("[data-keys]").onclick = () => toast("(演示)快捷键:J / K 逐条,1 / 2 / 3 处理,? 显示说明");
|
||
|
||
render();
|
||
|
||
/* 唯一的动效:概要句数字载入时从 0 数到实际值一次 */
|
||
if (!matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
||
const els = $$("[data-to]"), t0 = performance.now(), dur = 700;
|
||
const step = (t) => {
|
||
const k = Math.min(1, (t - t0) / dur), e = 1 - Math.pow(1 - k, 3);
|
||
els.forEach((el) => { el.textContent = Math.round(+el.dataset.to * e); });
|
||
if (k < 1) requestAnimationFrame(step);
|
||
};
|
||
requestAnimationFrame(step);
|
||
}
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|