Files
lovife 7df078e0ea docs: 同步交接摘要 / PRD / 原型,并记录品牌改名「深学在线」
- 交接摘要补 §一·三十五~§一·三十八:角色与授权、需求收敛(4 角色 / 10 菜单 / 取消二期)、
  PC 端重搭后的 UI 修复与卡片式基线、品牌改名与新标识
- PRD 与功能逻辑卷只改标题行与产品代号线;**文件名不动** —— verify-docs.py 按路径读,
  全库还有 20+ 处按文件名引用,改名会连带炸脚本
- 原型 index / admin-web / teacher-app / student-app 的品牌区改「深学在线」,
  admin-web 的 .mark(10 处)换成内嵌的新标 data URI,保持原型"单文件自包含"的约定
- 根 AGENTS.md 与 frontend-pc 的规范同步:接线检查补第 ⑨ 项(SVG 资产是不是合法 XML ——
  SVG 注释里出现连续两个减号就 not well-formed,图标裂开而控制台一句话都不报)、
  「最容易写错的约定」6 -> 7 条(新增品牌标)
- deploy/docker-compose.yml 只改头注释里的项目名(compose 的 name 与七牛对象前缀都没动)
2026-10-10 16:49:04 +08:00

1480 lines
110 KiB
HTML
Vendored
Raw Permalink 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>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>深学在线 · 管理端(PC)交互原型</title>
<style>
:root{
--ink:#1b2430; --ink2:#4a5768; --ink3:#8b95a3; --ink4:#b3b9c1;
--paper:#f4f1ea; --paper2:#fbfaf7; --card:#fff; --bg:#f7f6f2;
--line:#e0dbd0; --line2:#cfc8b8; --line3:#eceae4;
--seal:#c0392b; --seal-soft:#f9ece9;
--jade:#2f6b4f; --jade-soft:#e8f0eb;
--amber:#a9761f; --amber-soft:#f8f1de;
--slate:#5b6879; --slate-soft:#eceff3;
--font-display:"Songti SC","STSong","SimSun","Noto Serif SC",serif;
--font-body:"PingFang SC","Hiragino Sans GB","Microsoft YaHei","Source Han Sans SC",sans-serif;
--font-mono:"SF Mono","JetBrains Mono","Cascadia Mono",Consolas,monospace;
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
font-family:var(--font-body);color:var(--ink);background:var(--paper);
background-image:linear-gradient(rgba(207,200,184,.28) 1px,transparent 1px),
linear-gradient(90deg,rgba(207,200,184,.28) 1px,transparent 1px);
background-size:26px 26px;-webkit-font-smoothing:antialiased;padding:0 0 90px;
}
.ico{width:1em;height:1em;flex:none;display:inline-block;vertical-align:-.14em;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
/* ── 页头 ── */
.masthead{padding:38px 44px 24px;border-bottom:1px solid var(--line2);background:linear-gradient(180deg,var(--paper2),transparent);position:relative}
.masthead::after{content:"";position:absolute;left:0;right:0;bottom:3px;height:1px;background:var(--line)}
.kicker{font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--ink3);font-family:var(--font-mono);margin-bottom:10px}
h1{font-family:var(--font-display);font-size:32px;font-weight:600;letter-spacing:.04em;line-height:1.25}
h1 em{font-style:normal;color:var(--seal)}
.sub{margin-top:10px;color:var(--ink2);font-size:13.5px;line-height:1.75;max-width:900px}
.sub code{font-family:var(--font-mono);font-size:12px;background:#fff;border:1px solid var(--line);border-radius:4px;padding:1px 5px}
.zoom{display:flex;align-items:center;gap:8px;margin-top:18px;font-size:12px;color:var(--ink3)}
.zoom b{font-weight:500}
.zoom button{
font-family:var(--font-mono);font-size:11.5px;padding:5px 12px;border-radius:7px;cursor:pointer;
border:1px solid var(--line2);background:#fff;color:var(--ink2);transition:.18s;
}
.zoom button:hover{border-color:var(--ink3)}
.zoom button.on{background:var(--ink);border-color:var(--ink);color:#fff}
/* ── 堆叠布局 ── */
.stack{display:flex;flex-direction:column;align-items:center;gap:52px;padding:48px 24px 0;max-width:1300px;margin:0 auto}
figure{margin:0;width:100%;display:flex;flex-direction:column;align-items:center}
.frame-wrap{width:100%;overflow-x:auto;padding-bottom:6px}
figcaption{
margin-top:20px;width:100%;max-width:1180px;font-size:13px;line-height:1.85;color:var(--ink2);
border-left:3px solid var(--line2);padding-left:16px;
}
figcaption b{color:var(--ink);font-weight:600}
figcaption .tag{display:inline-block;font-family:var(--font-mono);font-size:10px;letter-spacing:.1em;color:var(--seal);border:1px solid var(--seal);border-radius:3px;padding:1px 6px;margin-right:8px;opacity:.85;vertical-align:1px}
figcaption .title{display:block;font-family:var(--font-display);font-size:16px;color:var(--ink);letter-spacing:.03em;margin-bottom:7px;font-weight:600}
figcaption .note{display:block;margin-top:8px;color:var(--ink3);font-size:12px}
/* ── 浏览器窗口 ── */
.origin{transform-origin:top center}
.win{width:1180px;background:#0e1319;border-radius:12px;padding:8px;box-shadow:0 30px 56px -22px rgba(27,36,48,.44),0 2px 6px rgba(27,36,48,.14)}
.wbar{height:34px;display:flex;align-items:center;gap:12px;padding:0 12px}
.lights{display:flex;gap:7px;flex:none}
.lights i{width:11px;height:11px;border-radius:50%;display:block;background:#3c4653}
.lights i:nth-child(1){background:#5c4a3f}
.lights i:nth-child(2){background:#57503d}
.lights i:nth-child(3){background:#3f5245}
.url{
flex:1;height:23px;border-radius:6px;background:#1a2029;display:flex;align-items:center;gap:8px;
padding:0 11px;font-family:var(--font-mono);font-size:11px;color:#7d8894;max-width:520px;margin:0 auto;
}
.url .ico{font-size:12px;color:#5f6b78}
.view{background:var(--bg);border-radius:7px;overflow:hidden;display:flex;height:718px}
/* ── 应用内 ── */
.side{
width:186px;flex:none;background:var(--paper2);border-right:1px solid var(--line);
display:flex;flex-direction:column;padding:0 0 12px;
}
.brand{padding:17px 17px 15px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:9px}
.brand .mark{
width:28px;height:28px;border-radius:7px;flex:none;
/* 深学在线 标识:方(卷宗)+ 环(在线)。与 frontend-pc 的
src/assets/images/logo.svg 同一形状,这里内嵌是为了原型
「单文件自包含、零外部依赖」—— 不能引外部图片。
原型自己的色板是墨色,所以底仍用 var(--ink),只取白色形状。 */
background:var(--ink) url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'><circle cx='36.25' cy='26.62' r='16.31' fill='none' stroke='%23fff' stroke-width='5.9' stroke-opacity='.76'/><rect x='8.5' y='22.65' width='34' height='34' rx='10.4' fill='%23fff'/></svg>") center/112% no-repeat;
}
.brand .bn{font-family:var(--font-display);font-size:15px;font-weight:600;letter-spacing:.05em}
.brand .bs{font-size:9.5px;color:var(--ink4);font-family:var(--font-mono);letter-spacing:.08em;margin-top:2px}
.nav{padding:11px 9px;flex:1}
.nav a{
display:flex;align-items:center;gap:9px;padding:9px 11px;border-radius:7px;font-size:13px;
color:var(--ink2);text-decoration:none;cursor:pointer;transition:.16s;margin-bottom:2px;
}
.nav a:hover{background:var(--line3)}
.nav a.on{background:var(--ink);color:#fff}
.nav a .ico{font-size:15px;opacity:.85}
.nav a .nb{margin-left:auto;font-family:var(--font-mono);font-size:10px;background:var(--seal);color:#fff;padding:1px 5px;border-radius:8px}
.nav a.on .nb{background:rgba(255,255,255,.26)}
.nav .grp{font-size:10px;font-family:var(--font-mono);letter-spacing:.12em;color:var(--ink4);padding:14px 11px 6px}
.side .who{border-top:1px solid var(--line);padding:11px 14px;display:flex;align-items:center;gap:9px}
.side .who .av{width:28px;height:28px;border-radius:50%;background:var(--slate-soft);border:1px solid var(--line);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink2);flex:none}
.side .who .wn{font-size:12.5px;font-weight:600}
.side .who .wr{font-size:10px;color:var(--ink4);margin-top:2px}
/* 主区 */
.main{flex:1;display:flex;flex-direction:column;min-width:0;background:var(--bg)}
.topbar{
height:52px;flex:none;background:var(--paper2);border-bottom:1px solid var(--line);
display:flex;align-items:center;gap:14px;padding:0 20px;
}
.topbar h2{font-family:var(--font-display);font-size:17px;font-weight:600;letter-spacing:.04em}
.topbar .crumb{font-size:11.5px;color:var(--ink4);font-family:var(--font-mono)}
.topbar .sp{margin-left:auto;display:flex;align-items:center;gap:10px}
.scroll{flex:1;overflow-y:auto;padding:20px;scrollbar-width:thin}
.scroll::-webkit-scrollbar{width:8px}
.scroll::-webkit-scrollbar-thumb{background:var(--line2);border-radius:4px;border:2px solid var(--bg)}
/* 卡片 */
.card{background:var(--card);border:1px solid var(--line);border-radius:10px;margin-bottom:16px}
.card>header{
padding:13px 16px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:10px;
font-size:13.5px;font-weight:600;letter-spacing:.02em;
}
.card>header .sp{margin-left:auto;display:flex;align-items:center;gap:8px}
.card>header .hint{font-size:11px;color:var(--ink4);font-weight:400;font-family:var(--font-mono)}
.card .body{padding:16px}
/* 统计卡 */
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:16px}
.stat{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:15px 16px}
.stat .lbl{font-size:11.5px;color:var(--ink3);letter-spacing:.03em;display:flex;align-items:center;gap:6px}
.stat .lbl .ico{font-size:13px}
.stat .num{font-family:var(--font-mono);font-size:27px;font-weight:700;margin-top:9px;line-height:1;letter-spacing:-.01em}
.stat .num small{font-size:13px;font-weight:500;color:var(--ink3);margin-left:3px}
.stat .foot{font-size:11px;color:var(--ink4);margin-top:7px;font-family:var(--font-mono)}
.stat.seal .num{color:var(--seal)} .stat.jade .num{color:var(--jade)} .stat.amber .num{color:var(--amber)}
/* 表格 */
table{width:100%;border-collapse:collapse;font-size:12.5px}
thead th{
text-align:left;font-size:11px;font-family:var(--font-mono);letter-spacing:.06em;color:var(--ink3);
font-weight:500;padding:9px 12px;border-bottom:1px solid var(--line2);background:#faf9f6;white-space:nowrap;
}
tbody td{padding:10px 12px;border-bottom:1px solid var(--line3);vertical-align:middle}
tbody tr:last-child td{border-bottom:0}
tbody tr:hover{background:#fdfcfa}
td.mono,.mono{font-family:var(--font-mono);font-size:11.5px;letter-spacing:.01em}
td.num{font-family:var(--font-mono);text-align:right}
/* 标签 */
.pill{display:inline-flex;align-items:center;gap:5px;font-size:11px;padding:2.5px 8px;border-radius:20px;font-weight:600;letter-spacing:.02em;white-space:nowrap}
.pill .ico{font-size:11px}
.pill.ok{background:var(--jade-soft);color:var(--jade)}
.pill.warn{background:var(--amber-soft);color:var(--amber)}
.pill.crit{background:var(--seal-soft);color:var(--seal)}
.pill.gray{background:var(--slate-soft);color:var(--slate)}
.pill.auto{background:#eef1f4;color:var(--ink2)}
/* 按钮 */
.btn{display:inline-flex;align-items:center;gap:7px;border:0;border-radius:8px;font-family:var(--font-body);font-size:13px;font-weight:600;letter-spacing:.03em;padding:9px 16px;cursor:pointer;transition:.18s;white-space:nowrap}
.btn .ico{font-size:14px}
.btn.primary{background:var(--ink);color:#fff} .btn.primary:hover{background:#2a3646}
.btn.seal{background:var(--seal);color:#fff} .btn.seal:hover{background:#a93022}
.btn.jade{background:var(--jade);color:#fff} .btn.jade:hover{background:#26563f}
.btn.ghost{background:transparent;color:var(--ink2);border:1px solid var(--line2)} .btn.ghost:hover{background:#fff;color:var(--ink)}
.btn.sm{padding:6px 11px;font-size:12px;border-radius:6px}
.btn:disabled{background:#e4e1da;color:#a8adaf;cursor:not-allowed;border-color:transparent}
/* 步骤条 */
.steps{display:flex;align-items:center;gap:0;padding:14px 16px;background:var(--paper2);border-bottom:1px solid var(--line);overflow-x:auto}
.steps::-webkit-scrollbar{display:none}
.step{display:flex;align-items:center;gap:8px;flex:none;font-size:12px;color:var(--ink4);padding-right:10px}
.step .n{
width:21px;height:21px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;
font-family:var(--font-mono);font-size:10.5px;border:1.5px solid var(--line2);color:var(--ink4);background:#fff;
}
.step.done{color:var(--jade)} .step.done .n{background:var(--jade-soft);border-color:#c3d8ca;color:var(--jade)}
.step.on{color:var(--ink);font-weight:600} .step.on .n{background:var(--ink);border-color:var(--ink);color:#fff}
.step .arw{color:var(--line2);margin:0 5px;font-size:11px}
/* 两栏 */
.cols{display:grid;grid-template-columns:376px 1fr;gap:16px;align-items:start}
/* 变量面板 */
.vars{display:flex;flex-wrap:wrap;gap:7px}
.vchip{
font-family:var(--font-mono);font-size:11.5px;padding:5px 10px;border-radius:6px;cursor:pointer;transition:.16s;
border:1px dashed var(--line2);background:#fff;color:var(--ink2);
}
.vchip:hover{border-style:solid;border-color:var(--ink);color:var(--ink)}
.tpl{
font-family:var(--font-mono);font-size:14px;line-height:1.9;padding:13px 14px;border-radius:9px;
border:1.5px solid var(--line2);background:#fff;margin:12px 0;word-break:break-all;
}
.tpl em{font-style:normal;background:var(--jade-soft);color:var(--jade);border-radius:4px;padding:1px 5px;font-weight:600}
.tpl .sep{color:var(--seal);font-weight:700}
/* 试跑 */
.ta{
width:100%;min-height:104px;border:1.5px solid var(--line2);border-radius:9px;padding:11px 13px;
font-family:var(--font-mono);font-size:11.5px;line-height:1.85;resize:vertical;color:var(--ink);background:#fff;
}
.ta:focus{outline:0;border-color:var(--ink)}
/* 差异高亮 */
.diff{background:rgba(192,57,43,.13);border-bottom:1.5px solid var(--seal);border-radius:2px;padding:0 1px}
.fixd{background:var(--jade-soft);border-bottom:1.5px solid var(--jade);border-radius:2px;padding:0 1px}
.strike{color:var(--ink4);text-decoration:line-through;text-decoration-color:var(--seal)}
/* 目录树 */
.tree{font-family:var(--font-mono);font-size:12px;line-height:2.05;color:var(--ink2)}
.tree .d{color:var(--ink);font-weight:600}
.tree .f{color:var(--jade)}
.tree .br{color:var(--line2)}
/* 提示条 */
.note{display:flex;gap:10px;padding:11px 14px;border-radius:9px;font-size:12.5px;line-height:1.7;align-items:flex-start}
.note .ico{font-size:15px;margin-top:1px;flex:none}
.note.info{background:var(--slate-soft);border:1px solid var(--line2);color:var(--ink2)}
.note.warn{background:var(--amber-soft);border:1px solid #e8d9b5;color:#7d5815}
.note.crit{background:var(--seal-soft);border:1px solid #eed5cf;color:#8e2d20}
.note.ok{background:var(--jade-soft);border:1px solid #cfe0d6;color:#245341}
.note b{font-weight:600}
/* 筛选行 */
.filters{display:flex;align-items:center;gap:9px;padding:11px 16px;border-bottom:1px solid var(--line);background:#faf9f6}
.fchip{font-size:12px;padding:5px 12px;border-radius:20px;border:1px solid var(--line2);background:#fff;color:var(--ink2);cursor:pointer;transition:.16s;display:flex;align-items:center;gap:6px}
.fchip:hover{border-color:var(--ink3)}
.fchip.on{background:var(--ink);border-color:var(--ink);color:#fff}
.fchip .cnt{font-family:var(--font-mono);font-size:10.5px;opacity:.7}
/* ── 表单设计器 ── */
.fld{
display:flex;align-items:center;gap:8px;padding:7px 10px;border:1px solid var(--line);border-radius:7px;
background:#fff;font-size:12px;color:var(--ink2);cursor:grab;transition:.16s;
}
.fld:hover{border-color:var(--ink3);background:#fdfcfa}
.fld.hot{border-color:#cfe0d6;background:var(--jade-soft);color:#245341}
.fld .ico{font-size:13px;opacity:.8}
.frow{
display:flex;align-items:center;gap:9px;padding:8px 10px;border:1px solid var(--line);border-radius:7px;
background:#fff;font-size:12.5px;margin-bottom:6px;cursor:grab;transition:.16s;
}
.frow:hover{border-color:var(--ink3)}
.frow.sel{border-color:var(--ink);box-shadow:0 0 0 3px rgba(27,36,48,.06)}
.frow.lock{background:#faf9f6}
.frow .grip{color:var(--line2);font-size:11px;letter-spacing:-2px;cursor:grab}
.frow .fn{flex:1;font-weight:500}
.frow .ft{font-size:11px;color:var(--ink4);font-family:var(--font-mono)}
/* ── 条件规则行 ── */
.rule-row{
display:flex;align-items:center;gap:9px;padding:10px 12px;border:1px solid var(--line);border-radius:8px;
background:#faf9f6;font-size:12px;margin-bottom:8px;flex-wrap:wrap;
}
.rule-row:last-child{margin-bottom:0}
.rule-row .mono{font-family:var(--font-mono);font-size:11.5px;color:var(--ink)}
.rule-row .op{color:var(--ink4)}
.rule-row .val{font-family:var(--font-mono);font-size:11.5px;background:#fff;border:1px solid var(--line);border-radius:4px;padding:1px 6px}
.rule-row .arrow{color:var(--line2);font-size:13px}
/* ── 材料清单侧栏 ── */
.mitem{
display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:8px;cursor:pointer;
transition:.16s;font-size:12.5px;margin-bottom:3px;
}
.mitem:hover{background:var(--line3)}
.mitem.on{background:var(--ink);color:#fff}
.mitem.on .ms{color:rgba(255,255,255,.62)}
.mitem .ico{font-size:17px;flex:none;opacity:.85}
.mitem .mn{font-weight:600;letter-spacing:.01em}
.mitem .ms{font-size:10.5px;color:var(--ink4);font-family:var(--font-mono);margin-top:2px}
/* ── 字段标签 ── */
.f-label{font-size:11px;font-family:var(--font-mono);letter-spacing:.08em;color:var(--ink3);margin-bottom:7px}
/* 说明区 */
.apron{max-width:1300px;margin:0 auto;padding:0 24px}
.apron h3{font-family:var(--font-display);font-size:19px;letter-spacing:.05em;margin:56px 0 18px;padding-bottom:10px;border-bottom:1px solid var(--line2)}
.aside-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:18px}
.aside-card{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:16px 18px;font-size:12.5px;line-height:1.85;color:var(--ink2)}
.aside-card h4{font-size:13.5px;color:var(--ink);margin-bottom:9px;letter-spacing:.03em;display:flex;align-items:center;gap:8px;font-weight:600}
.aside-card h4 .n{font-family:var(--font-mono);font-size:10.5px;color:var(--seal);border:1px solid var(--seal);border-radius:3px;padding:0 5px}
.aside-card code{font-family:var(--font-mono);font-size:11px;background:var(--paper);border:1px solid var(--line);border-radius:3px;padding:1px 4px}
</style>
</head>
<body>
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<symbol id="i-lock" viewBox="0 0 24 24"><rect x="4.5" y="10.5" width="15" height="10" rx="2.2"/><path d="M8 10.5V7.5a4 4 0 0 1 8 0v3"/></symbol>
<symbol id="i-home" viewBox="0 0 24 24"><path d="M3.8 10.4L12 4l8.2 6.4v9.2a1.4 1.4 0 0 1-1.4 1.4H5.2a1.4 1.4 0 0 1-1.4-1.4z"/></symbol>
<symbol id="i-task" viewBox="0 0 24 24"><rect x="4.5" y="3.5" width="15" height="17" rx="2.2"/><path d="M8.5 8.5h7M8.5 12h7M8.5 15.5h4"/></symbol>
<symbol id="i-users" viewBox="0 0 24 24"><circle cx="9" cy="8.4" r="3.4"/><path d="M3 19.2c0-3.1 2.7-5.2 6-5.2s6 2.1 6 5.2"/><path d="M16.2 5.6a3.4 3.4 0 0 1 0 6.4M17.6 14.4c2 .6 3.4 2.3 3.4 4.8"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24"><path d="M5 12.6l4.5 4.5L19 7.4"/></symbol>
<symbol id="i-check-circle" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M8.2 12.2l2.6 2.6 5-5.2"/></symbol>
<symbol id="i-alert" viewBox="0 0 24 24"><path d="M12 4.3l8.8 15.2H3.2z"/><path d="M12 10v3.9M12 16.9v.5"/></symbol>
<symbol id="i-bulb" viewBox="0 0 24 24"><path d="M12 3.5a5.4 5.4 0 0 0-3.2 9.8c.5.4.8 1 .8 1.6v1.1h4.8v-1.1c0-.6.3-1.2.8-1.6A5.4 5.4 0 0 0 12 3.5z"/><path d="M9.8 18.6h4.4M10.4 21h3.2"/></symbol>
<symbol id="i-download" viewBox="0 0 24 24"><path d="M4.5 15.6v3a1.8 1.8 0 0 0 1.8 1.8h11.4a1.8 1.8 0 0 0 1.8-1.8v-3"/><path d="M12 3.9v11.5"/><path d="M7.9 11.3l4.1 4.1 4.1-4.1"/></symbol>
<symbol id="i-chart" viewBox="0 0 24 24"><path d="M4 20.4h16.2"/><path d="M7 20.4V12M12 20.4V5.4M17 20.4v-6"/></symbol>
<symbol id="i-play" viewBox="0 0 24 24"><path d="M7.4 4.8l11.4 7.2-11.4 7.2z"/></symbol>
<symbol id="i-shield" viewBox="0 0 24 24"><path d="M12 3.5l7 2.8v5.4c0 4.2-2.9 7.6-7 8.8-4.1-1.2-7-4.6-7-8.8V6.3z"/></symbol>
<symbol id="i-bell" viewBox="0 0 24 24"><path d="M17.4 16.4V11a5.4 5.4 0 0 0-10.8 0v5.4L5.2 18.4h13.6z"/><path d="M10 18.4a2 2 0 0 0 4 0"/></symbol>
<symbol id="i-gear" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M12 2.8v2.4M12 18.8v2.4M4.5 7.5l2 1.2M17.5 15.3l2 1.2M4.5 16.5l2-1.2M17.5 8.7l2-1.2"/></symbol>
<symbol id="i-wand" viewBox="0 0 24 24"><path d="M11 3.8l1.7 4.5 4.5 1.7-4.5 1.7L11 16.2 9.3 11.7 4.8 10l4.5-1.7z"/><path d="M18 15.4l.7 1.9 1.9.7-1.9.7-.7 1.9-.7-1.9-1.9-.7 1.9-.7z"/></symbol>
<symbol id="i-folder" viewBox="0 0 24 24"><path d="M3.5 6.6A1.9 1.9 0 0 1 5.4 4.7h3.3l2.1 2.6h8A1.9 1.9 0 0 1 20.7 9.2v8.4a1.9 1.9 0 0 1-1.9 1.9H5.4a1.9 1.9 0 0 1-1.9-1.9z"/></symbol>
<symbol id="i-pencil" viewBox="0 0 24 24"><path d="M4.5 19.5h3.2L18.4 8.8a2.3 2.3 0 0 0-3.2-3.2L4.5 16.3z"/><path d="M14.2 6.6l3.2 3.2"/></symbol>
<symbol id="i-image" viewBox="0 0 24 24"><rect x="3.5" y="4.5" width="17" height="15" rx="2.2"/><circle cx="8.8" cy="9.8" r="1.6"/><path d="M4 17.5l4.5-4.5 3.5 3.5 3-3 4.5 4.5"/></symbol>
<symbol id="i-idcard" viewBox="0 0 24 24"><rect x="2.5" y="5" width="19" height="14" rx="2.2"/><circle cx="8.5" cy="11.4" r="2.1"/><path d="M5.2 16.4c.6-1.5 1.8-2.3 3.3-2.3s2.7.8 3.3 2.3"/><path d="M14.5 10.2h4.3M14.5 13.6h4.3"/></symbol>
<symbol id="i-file" viewBox="0 0 24 24"><path d="M13.5 3.5H7A1.8 1.8 0 0 0 5.2 5.3v13.4A1.8 1.8 0 0 0 7 20.5h10a1.8 1.8 0 0 0 1.8-1.8V9z"/><path d="M13.5 3.5V9h5.3"/></symbol>
<symbol id="i-stamp" viewBox="0 0 24 24"><circle cx="12" cy="8.8" r="5.4"/><path d="M9.4 13.4L8 20.5h8l-1.4-7.1"/></symbol>
</svg>
<header class="masthead">
<div class="kicker">Prototype · 管理端 PC · V1.2</div>
<h1>深学在线 · <em>管理端</em>交互原型</h1>
<p class="sub">
10 屏:<strong>工作台 → 任务向导(命名试跑) → 名单导入 → 报考范围 → 数据看板 → 终审 → 导出中心</strong>,加上 <strong>表单设计器 / 材料规则配置 / 角色与授权</strong>。<br>
移动端是「档案室」,桌面端往「<strong>台账</strong>」方向走——表格、树形、批量操作。信息密度比手机端高一个量级,因为管理员是坐在电脑前做批量决策的人。<br>
<span style="font-size:12.5px;color:var(--ink3)">后三屏是**追溯矩阵审计后补的**:三者都是复杂交互,光靠文字描述开发会理解偏。</span>
</p>
<div class="zoom">
<b>缩放</b>
<button data-z="1" class="on">100%</button>
<button data-z="0.82">82%</button>
<button data-z="0.66">66%</button>
<span style="margin-left:6px">原型按 1180px 宽度绘制,屏幕窄时请选小缩放</span>
</div>
</header>
<div class="stack">
<!-- ① 工作台 -->
<figure>
<div class="frame-wrap"><div class="origin">
<div class="win"><div class="wbar">
<div class="lights"><i></i><i></i><i></i></div>
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks</div>
</div><div class="view">
<aside class="side">
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
<nav class="nav">
<a class="on"><svg class="ico"><use href="#i-home"/></svg>工作台</a>
<div class="grp">收集管理</div>
<a><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
<a><svg class="ico"><use href="#i-users"/></svg>名单管理</a>
<a><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
<a><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
<a><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
<div class="grp">系统</div>
<a><svg class="ico"><use href="#i-gear"/></svg>规则模板</a>
<a><svg class="ico"><use href="#i-lock"/></svg>权限与日志</a>
</nav>
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
</aside>
<div class="main">
<div class="topbar"><h2>工作台</h2><span class="crumb">江西省机电技师学院 · 2026年9月</span>
<span class="sp"><button class="btn ghost sm"><svg class="ico"><use href="#i-bell"/></svg>通知</button><button class="btn primary sm"><svg class="ico"><use href="#i-task"/></svg>新建收集任务</button></span>
</div>
<div class="scroll">
<div class="stats">
<div class="stat"><div class="lbl"><svg class="ico"><use href="#i-task"/></svg>进行中任务</div><div class="num">3</div><div class="foot">覆盖 6 个班级 / 168 人</div></div>
<div class="stat seal"><div class="lbl"><svg class="ico"><use href="#i-check-circle"/></svg>待我终审</div><div class="num">23</div><div class="foot">24中技数控1班 等 3 个班</div></div>
<div class="stat amber"><div class="lbl"><svg class="ico"><use href="#i-alert"/></svg>异常待处理</div><div class="num">7</div><div class="foot">证件号 3 · 低清 2 · 串档 1</div></div>
<div class="stat jade"><div class="lbl"><svg class="ico"><use href="#i-download"/></svg>本月导出</div><div class="num">4<small>次</small></div><div class="foot">最近 09-26 14:20</div></div>
</div>
<div class="card">
<header>进行中的收集任务<span class="sp"><span class="hint">按截止时间排序</span></span></header>
<table>
<thead><tr><th>任务名称</th><th>模式</th><th>覆盖班级</th><th>应填 / 已提交</th><th>进度</th><th>截止</th><th>状态</th></tr></thead>
<tbody>
<tr><td>2026年9月机电专业认定材料收集</td><td><span class="pill gray">学校收集</span></td><td>24中技数控1班</td><td class="num">28 / 23</td>
<td><div style="display:flex;align-items:center;gap:9px"><div style="flex:1;height:6px;border-radius:3px;background:#e8e4db;overflow:hidden;min-width:76px"><i style="display:block;height:100%;width:82%;background:var(--jade)"></i></div><span class="mono" style="font-size:11px">82%</span></div></td>
<td class="mono">10-15</td><td><span class="pill ok">收集中</span></td></tr>
<tr><td>2026年9月汽车维修工认定收集</td><td><span class="pill gray">学校收集</span></td><td>24中技汽修1班</td><td class="num">42 / 42</td>
<td><div style="display:flex;align-items:center;gap:9px"><div style="flex:1;height:6px;border-radius:3px;background:#e8e4db;overflow:hidden;min-width:76px"><i style="display:block;height:100%;width:100%;background:var(--jade)"></i></div><span class="mono" style="font-size:11px">100%</span></div></td>
<td class="mono">10-12</td><td><span class="pill warn">待终审</span></td></tr>
<tr><td>2026年秋季社会人员认定报名</td><td><span class="pill gray">社会收集</span></td><td>南昌报名点</td><td class="num">98 / 61</td>
<td><div style="display:flex;align-items:center;gap:9px"><div style="flex:1;height:6px;border-radius:3px;background:#e8e4db;overflow:hidden;min-width:76px"><i style="display:block;height:100%;width:62%;background:var(--jade)"></i></div><span class="mono" style="font-size:11px">62%</span></div></td>
<td class="mono">10-20</td><td><span class="pill ok">收集中</span></td></tr>
</tbody>
</table>
</div>
<div class="card">
<header>需要我处理的<span class="sp"><span class="hint">按紧急度排序</span></span></header>
<div class="body" style="padding:0">
<div class="note crit" style="margin:14px 16px">
<svg class="ico"><use href="#i-alert"/></svg>
<div><b>24中技数控1班 · 陈子文 疑似串档</b> 一寸照片的证件号与他其他材料不一致,且两个号码校验位都通过(是两个真实的人)。需管理员判断是否驳回。<br><span style="font-size:11.5px;opacity:.8">班主任已初审:标记为待管理员确认</span></div>
</div>
<div class="note warn" style="margin:0 16px 14px">
<svg class="ico"><use href="#i-alert"/></svg>
<div><b>24中技数控1班 · 报考范围发现异常项</b> 28 人中 27 人报「车工/数控车工/四级中级工」,1 人报「车工/普通车工/三级高级工」(占比 3.6%),疑似填写错误。</div>
</div>
</div>
</div>
</div>
</div>
</div></div></div></div>
<figcaption>
<span class="tag">STEP 01</span>
<span class="title">工作台:先给结论,再给细节</span>
四张统计卡回答"现在什么状况",下面两张卡回答"我要做什么"。<b>异常按紧急度排在最上面</b>——串档(有红项)在范围异常(黄项)之上。
<span class="note">注意"异常待处理 7"下面标注了构成(证件号 3 · 低清 2 · 串档 1)——管理员不用点进去就知道是什么类型的问题。</span>
</figcaption>
</figure>
<!-- ② 任务向导 · 命名规则试跑 -->
<figure>
<div class="frame-wrap"><div class="origin">
<div class="win"><div class="wbar">
<div class="lights"><i></i><i></i><i></i></div>
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/new?step=7</div>
</div><div class="view">
<aside class="side">
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
<nav class="nav">
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
<div class="grp">收集管理</div>
<a class="on"><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
<a><svg class="ico"><use href="#i-users"/></svg>名单管理</a>
<a><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
<a><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
<a><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
</nav>
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
</aside>
<div class="main">
<div class="topbar"><h2>新建收集任务</h2><span class="crumb">草稿已保存 · 09:38</span></div>
<div class="steps">
<div class="step done"><span class="n">1</span>基本信息</div><span class="arw">›</span>
<div class="step done"><span class="n">2</span>覆盖班级</div><span class="arw">›</span>
<div class="step done"><span class="n">3</span>名单</div><span class="arw">›</span>
<div class="step done"><span class="n">4</span>表单</div><span class="arw">›</span>
<div class="step done"><span class="n">5</span>材料</div><span class="arw">›</span>
<div class="step done"><span class="n">6</span>审核流</div><span class="arw">›</span>
<div class="step on"><span class="n">7</span>预览发布</div>
</div>
<div class="scroll">
<div class="note info" style="margin-bottom:16px">
<svg class="ico"><use href="#i-bulb"/></svg>
<div><b>发布前必须完成一次命名规则试跑。</b>粘贴几条学生可能交上来的原始文件名,确认系统改出来的结果符合机构要求——这是防止"规则配错导致全员改名错误"的唯一闸门。</div>
</div>
<div class="cols">
<div>
<div class="card">
<header>命名模板</header>
<div class="body">
<div class="vars">
<span class="vchip">{姓名}</span><span class="vchip">{证件号}</span><span class="vchip">{材料名}</span>
<span class="vchip">{班级}</span><span class="vchip">{工种}</span><span class="vchip">{序号}</span>
</div>
<div class="tpl">{<em>姓名</em>}<span class="sep">-</span>{<em>证件号</em>}<span class="sep">-</span>{<em>材料名</em>}.jpg</div>
<div style="font-size:11.5px;color:var(--ink3);line-height:1.75">
与机构模板一致。连接符为半角 <span class="mono">-</span>,大小写按名单原样。
</div>
</div>
</div>
<div class="card">
<header>纠错开关</header>
<div class="body" style="font-size:12.5px;line-height:2.3;color:var(--ink2)">
<label style="display:flex;align-items:center;gap:9px"><input type="checkbox" checked style="accent-color:var(--jade);width:15px;height:15px">自动修复多余扩展名</label>
<label style="display:flex;align-items:center;gap:9px"><input type="checkbox" checked style="accent-color:var(--jade);width:15px;height:15px">自动补齐缺失的连接符</label>
<label style="display:flex;align-items:center;gap:9px"><input type="checkbox" checked style="accent-color:var(--jade);width:15px;height:15px">证件号末位统一大写</label>
<label style="display:flex;align-items:center;gap:9px"><input type="checkbox" checked style="accent-color:var(--jade);width:15px;height:15px">形近字纠错(需学生确认)</label>
<label style="display:flex;align-items:center;gap:9px"><input type="checkbox" checked style="accent-color:var(--jade);width:15px;height:15px">校验位裁决 <span class="pill auto">推荐</span></label>
</div>
</div>
</div>
<div>
<div class="card">
<header>试跑<span class="sp"><span class="hint">粘贴任意条数,不影响真实数据</span></span></header>
<div class="body">
<textarea class="ta">曾伊豪-361029200907295110身份证.jpg.jpg
于豪-360103200805166169-身份证.jpg.jpg
扬晨-360825200904303215-一寸照片.jpg
陈子文-360122200902044232-一寸照片.jpg
曹文锋-36012120081211239-一寸照片.jpg
徐程宇360981200901292334-身份证.jpg.jpg</textarea>
<div style="display:flex;align-items:center;gap:10px;margin-top:12px">
<button class="btn primary"><svg class="ico"><use href="#i-play"/></svg>批量试跑(6 条)</button>
<button class="btn ghost sm">清空</button>
<span style="margin-left:auto;font-size:11.5px;color:var(--ink3)">数据来自真实批次样本</span>
</div>
</div>
</div>
<div class="card">
<header>试跑结果<span class="sp">
<span class="pill ok"><svg class="ico"><use href="#i-check"/></svg>自动修复 4</span>
<span class="pill warn"><svg class="ico"><use href="#i-alert"/></svg>待确认 1</span>
<span class="pill crit"><svg class="ico"><use href="#i-alert"/></svg>需人工 1</span>
</span></header>
<table>
<thead><tr><th style="width:44%">原始文件名</th><th style="width:44%">规范后</th><th>判定</th></tr></thead>
<tbody>
<tr><td class="mono">曾伊豪-361029200907295110身份证<span class="diff">.jpg.jpg</span></td>
<td class="mono">曾伊豪-361029200907295110<span class="fixd">-</span>身份证.jpg</td>
<td><span class="pill auto">自动修复</span></td></tr>
<tr><td class="mono">于豪-3601032008<span class="diff">05166169</span>-身份证.jpg.jpg</td>
<td class="mono">于豪-3601032008<span class="fixd">05186619</span>-身份证.jpg</td>
<td><span class="pill auto">校验位裁决</span></td></tr>
<tr><td class="mono"><span class="strike">扬晨</span>-360825200904303215-一寸照片.jpg</td>
<td class="mono"><span class="fixd">杨晨</span>-360825200904303215-一寸照片.jpg</td>
<td><span class="pill warn">形近字待确认</span></td></tr>
<tr><td class="mono">曹文锋-3601212008<span class="diff">1211239</span>-一寸照片.jpg</td>
<td class="mono">曹文锋-3601212008<span class="fixd">12111239</span>-一寸照片.jpg</td>
<td><span class="pill warn">名册补齐</span></td></tr>
<tr><td class="mono">徐程宇<span class="diff">360981200901292334</span>-身份证.jpg.jpg</td>
<td class="mono">徐程宇<span class="fixd">-</span>360981200901292334-身份证.jpg</td>
<td><span class="pill auto">自动修复</span></td></tr>
<tr style="background:var(--seal-soft)"><td class="mono">陈子文-<span class="diff">360122200902044232</span>-一寸照片.jpg</td>
<td class="mono" style="color:var(--seal)">— 不予自动修改 —</td>
<td><span class="pill crit">疑似串档 · 转人工</span></td></tr>
</tbody>
</table>
<div class="body" style="border-top:1px solid var(--line);padding-top:14px">
<div class="note crit" style="margin-bottom:0">
<svg class="ico"><use href="#i-alert"/></svg>
<div><b>最后一条必须人工处理。</b>陈子文的照片证件号 <span class="mono">360122200902044232</span> 与名单 <span class="mono">36010520090817121X</span> 不一致,且<b>两个号码校验位都通过</b>——说明是<b>两个真实存在的人</b>,不是打字错误。系统不猜,交人工判断。</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="flex:none;padding:13px 20px;background:var(--paper2);border-top:1px solid var(--line);display:flex;align-items:center;gap:10px">
<span style="font-size:12px;color:var(--ink3)">试跑通过率 <span class="mono" style="color:var(--jade);font-weight:600">83%</span>(5/6 可自动处理)</span>
<span style="margin-left:auto;display:flex;gap:10px">
<button class="btn ghost">上一步</button>
<button class="btn primary"><svg class="ico"><use href="#i-check"/></svg>确认并发布任务</button>
</span>
</div>
</div>
</div></div></div></div>
<figcaption>
<span class="tag">STEP 02 · 最重要的一屏</span>
<span class="title">命名规则试跑:发布前的唯一闸门</span>
<b>没有这一屏,规则配错就会导致全员改名错误</b>——而且错到导出时才会发现,那时已无法挽回。所以它被设成<b>发布的前置条件</b>(未试跑不给发布)。
<span class="note">结果表用<b style="color:var(--seal)">红底高亮"改了什么"</b>、<b style="color:var(--jade)">绿底标"补了什么"</b>。最后一行陈子文的案例最有价值:系统明确说"两个号码校验位都通过 = 是两个真人,不是打字错误",并<b>拒绝自动修改</b>——把判断权交还给人。</span>
</figcaption>
</figure>
<!-- ③ 名单导入体检 -->
<figure>
<div class="frame-wrap"><div class="origin">
<div class="win"><div class="wbar">
<div class="lights"><i></i><i></i><i></i></div>
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/1/roster/import</div>
</div><div class="view">
<aside class="side">
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
<nav class="nav">
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
<div class="grp">收集管理</div>
<a><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
<a class="on"><svg class="ico"><use href="#i-users"/></svg>名单管理</a>
<a><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
<a><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
<a><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
</nav>
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
</aside>
<div class="main">
<div class="topbar"><h2>导入名单</h2><span class="crumb">2026年9月机电专业认定材料收集</span></div>
<div class="scroll">
<div class="card">
<header>① 表头识别<span class="sp"><span class="pill ok"><svg class="ico"><use href="#i-check"/></svg>已自动定位</span></span></header>
<div class="body">
<div class="note info" style="margin-bottom:14px">
<svg class="ico"><use href="#i-bulb"/></svg>
<div>系统<b>没有假设"第一行是表头"</b>,而是在工作表中逐行查找同时包含「姓名」「身份证号」的行。
本次在<b>工作表「填表注意事项」的第 6 行</b>找到表头——该文件前 5 行是填表说明与示例。</div>
</div>
<div style="border:1px solid var(--line);border-radius:9px;overflow:hidden">
<table>
<thead><tr><th style="width:52px">行</th><th>A</th><th>B</th><th>C</th><th>D</th><th>E</th><th>…</th><th style="width:104px">判定</th></tr></thead>
<tbody>
<tr style="color:var(--ink4)"><td class="num">1</td><td colspan="6" style="font-size:11.5px">特别注意:请勿调整模板!!!在第一个…</td><td><span class="pill gray">说明</span></td></tr>
<tr style="color:var(--ink4)"><td class="num">2</td><td colspan="6" style="font-size:11.5px">01、标题颜色:红色标题为必填项…</td><td><span class="pill gray">说明</span></td></tr>
<tr style="color:var(--ink4)"><td class="num">3</td><td colspan="6" style="font-size:11.5px">02、下拉选项:学历、报考等级…</td><td><span class="pill gray">说明</span></td></tr>
<tr style="color:var(--ink4)"><td class="num">4</td><td colspan="6" style="font-size:11.5px">…</td><td><span class="pill gray">说明</span></td></tr>
<tr style="color:var(--ink4)"><td class="num">5</td><td colspan="6" style="font-size:11.5px">…</td><td><span class="pill gray">说明</span></td></tr>
<tr style="background:var(--jade-soft);font-weight:600"><td class="num">6</td><td>序号</td><td>姓名</td><td>身份证号</td><td>学历</td><td>联系电话</td><td>…</td><td><span class="pill ok"><svg class="ico"><use href="#i-check"/></svg>表头</span></td></tr>
<tr><td class="num">7</td><td>1</td><td>熊宇豪</td><td class="mono">360981…4437</td><td>技工学校</td><td class="mono">199…0823</td><td>…</td><td><span class="pill auto">数据</span></td></tr>
<tr><td class="num">8</td><td>2</td><td>魏航</td><td class="mono">360427…3318</td><td>技工学校</td><td class="mono">178…3067</td><td>…</td><td><span class="pill auto">数据</span></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="cols" style="grid-template-columns:1fr 1fr">
<div class="card">
<header>② 字段映射<span class="sp"><span class="hint">21 列已自动匹配</span></span></header>
<div class="body" style="padding:0">
<table>
<thead><tr><th>Excel 列</th><th>系统字段</th><th style="width:88px">状态</th></tr></thead>
<tbody>
<tr><td class="mono">B · 姓名</td><td>姓名</td><td><span class="pill ok">已匹配</span></td></tr>
<tr><td class="mono">C · 身份证号</td><td>身份证号</td><td><span class="pill ok">已匹配</span></td></tr>
<tr><td class="mono">G · 报考职业</td><td>报考职业 <span class="pill crit" style="font-size:10px"><svg class="ico"><use href="#i-lock"/></svg>锁定</span></td><td><span class="pill ok">已匹配</span></td></tr>
<tr><td class="mono">H · 报考工种</td><td>报考工种 <span class="pill crit" style="font-size:10px"><svg class="ico"><use href="#i-lock"/></svg>锁定</span></td><td><span class="pill ok">已匹配</span></td></tr>
<tr><td class="mono">I · 报考等级</td><td>报考等级 <span class="pill crit" style="font-size:10px"><svg class="ico"><use href="#i-lock"/></svg>锁定</span></td><td><span class="pill ok">已匹配</span></td></tr>
<tr><td class="mono">D · 学历</td><td>学历 <span class="pill warn" style="font-size:10px">可改</span></td><td><span class="pill ok">已匹配</span></td></tr>
<tr><td class="mono">N · 是否在校生</td><td>是否在校生 <span class="pill warn" style="font-size:10px">可改</span></td><td><span class="pill ok">已匹配</span></td></tr>
<tr><td class="mono" style="color:var(--ink4)">— 未识别 —</td><td style="color:var(--ink4)">(其余 14 列)</td><td><span class="pill gray">忽略</span></td></tr>
</tbody>
</table>
</div>
</div>
<div class="card">
<header>③ 导入体检<span class="sp"><span class="pill ok"><svg class="ico"><use href="#i-check"/></svg>可以导入</span></span></header>
<div class="body">
<div class="stats" style="grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:14px">
<div class="stat" style="padding:12px"><div class="lbl">总行数</div><div class="num" style="font-size:22px">28</div></div>
<div class="stat jade" style="padding:12px"><div class="lbl">通过</div><div class="num" style="font-size:22px">28</div></div>
<div class="stat amber" style="padding:12px"><div class="lbl">警告</div><div class="num" style="font-size:22px">1</div></div>
</div>
<div class="note warn" style="margin-bottom:12px">
<svg class="ico"><use href="#i-alert"/></svg>
<div><b>1 项需要注意(不阻断导入)</b><br>
「报考工种」出现 2 种写法:<span class="mono">数控车工</span>(27 人)与 <span class="mono">普通车工</span>(1 人)——见下一步的「报考范围确认」。</div>
</div>
<div class="note ok">
<svg class="ico"><use href="#i-check-circle"/></svg>
<div><b>身份证号 28/28 全部通过校验位</b>,无重复、无缺失。姓名列无重复。</div>
</div>
</div>
</div>
</div>
</div>
<div style="flex:none;padding:13px 20px;background:var(--paper2);border-top:1px solid var(--line);display:flex;align-items:center;gap:10px">
<span style="font-size:12px;color:var(--ink3)">表头定位 + 字段映射已确认,可导入</span>
<span style="margin-left:auto;display:flex;gap:10px">
<button class="btn ghost">重新选择文件</button>
<button class="btn primary"><svg class="ico"><use href="#i-check"/></svg>确认导入 28 人</button>
</span>
</div>
</div>
</div></div></div></div>
<figcaption>
<span class="tag">STEP 03</span>
<span class="title">名单导入:把"系统怎么认表头"摊开给人看</span>
实测两份真实文件,<b>表头一个在第 1 行、一个在第 6 行</b>,工作表名也不同。所以系统不猜、也不要求用户改文件,而是<b>把定位过程可视化</b>——说明行标灰、表头行标绿,让人一眼确认"认对了没有"。
<span class="note">第②栏的字段映射里,<b style="color:var(--seal)">报考三项带"锁定"标记</b>——这是在导入阶段就告诉管理员:这三项由你确定,学生端只读。第③栏的体检把"警告"和"通过"分开呈现,并明确写"不阻断导入"。</span>
</figcaption>
</figure>
<!-- ④ 报考范围确认 -->
<figure>
<div class="frame-wrap"><div class="origin">
<div class="win"><div class="wbar">
<div class="lights"><i></i><i></i><i></i></div>
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/1/scope</div>
</div><div class="view">
<aside class="side">
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
<nav class="nav">
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
<div class="grp">收集管理</div>
<a class="on"><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
<a><svg class="ico"><use href="#i-users"/></svg>名单管理</a>
<a><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
<a><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
<a><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
</nav>
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
</aside>
<div class="main">
<div class="topbar"><h2>报考范围确认</h2><span class="crumb">2026年9月机电专业认定材料收集</span></div>
<div class="scroll">
<div class="note info" style="margin-bottom:16px">
<svg class="ico"><use href="#i-bulb"/></svg>
<div><b>本期报考范围由名单自动聚合而来。</b>系统不做更正,只做一致性体检——单看一行都合法,但"27 人报 A、1 人报 B"这种情况只有聚合后才能发现。</div>
</div>
<div class="card">
<header>聚合结果<span class="sp"><span class="hint">共 2 组 · 28 人</span></span></header>
<table>
<thead><tr><th>报考职业</th><th>报考工种</th><th>报考等级</th><th style="width:96px">人数</th><th style="width:110px">占比</th><th style="width:180px">判定</th></tr></thead>
<tbody>
<tr style="background:var(--jade-soft)">
<td>车工</td><td>数控车工</td><td>四级/中级工</td>
<td class="num" style="font-weight:600">27</td>
<td class="mono">96.4%</td>
<td><span class="pill ok"><svg class="ico"><use href="#i-check"/></svg>主体范围</span></td></tr>
<tr style="background:var(--amber-soft)">
<td>车工</td><td style="font-weight:600;color:var(--amber)">普通车工</td><td style="font-weight:600;color:var(--amber)">三级/高级工</td>
<td class="num" style="font-weight:600;color:var(--amber)">1</td>
<td class="mono" style="color:var(--amber)">3.6%</td>
<td><span class="pill warn"><svg class="ico"><use href="#i-alert"/></svg>疑似填错</span></td></tr>
</tbody>
</table>
<div class="body" style="border-top:1px solid var(--line)">
<div class="note warn" style="margin-bottom:0">
<svg class="ico"><use href="#i-alert"/></svg>
<div><b>「普通车工 / 三级高级工」仅 1 人,占比 3.6%,明显偏离主体范围。</b>
常见的三种可能:① 该生确实单独报考了另一个工种;② 老师录入时选错了;③ 该生是转班生,用的是上一批次的配置。<br>
请核对后再决定——<b>系统不会自动改名单</b>。</div>
</div>
</div>
</div>
<div class="cols" style="grid-template-columns:1fr 1fr">
<div class="card">
<header>定位到的人</header>
<div class="body" style="padding:0">
<table>
<thead><tr><th>姓名</th><th>身份证号</th><th>归属</th></tr></thead>
<tbody>
<tr><td>余饶平</td><td class="mono">360281…4035</td><td><span class="pill auto">24中技数控1班</span></td></tr>
</tbody>
</table>
<div class="body" style="border-top:1px solid var(--line);padding-top:12px;display:flex;gap:9px">
<button class="btn ghost sm">查看该生名单行</button>
<button class="btn ghost sm">通知班主任核对</button>
</div>
</div>
</div>
<div class="card">
<header>后续影响<span class="sp"><span class="hint">重要</span></span></header>
<div class="body" style="font-size:12.5px;line-height:1.9;color:var(--ink2)">
<b style="color:var(--ink)">· 学生端展示</b><br>
学生核验通过后会看到"你报考:车工 / 普通车工 / 三级高级工"——<b>如果这是错的,学生会发现并反馈</b>。<br>
<b style="color:var(--ink)">· 中途追加名单</b><br>
若之后补录的人报考项<b>超出已确认范围</b>,系统会告警并要求你确认后才接受。
</div>
</div>
</div>
</div>
<div style="flex:none;padding:13px 20px;background:var(--paper2);border-top:1px solid var(--line);display:flex;align-items:center;gap:10px">
<span style="font-size:12px;color:var(--ink3)">确认后,学生端将按此范围展示报考信息</span>
<span style="margin-left:auto;display:flex;gap:10px">
<button class="btn ghost">去修改名单</button>
<button class="btn primary"><svg class="ico"><use href="#i-check"/></svg>确认范围无误</button>
</span>
</div>
</div>
</div></div></div></div>
<figcaption>
<span class="tag">STEP 04 · 新概念</span>
<span class="title">报考范围确认:不做更正,只做体检</span>
「报考范围」这个实体<b>是我们拆解官方模板时才发现的</b>——模板规则 04/05/06 说"只能填当前评价计划中的职业/工种/等级",但实测确认<b>这个范围就藏在名单里</b>(老师导入时已填好),不存在独立的计划文件。
<span class="note">所以这里<b>不设计"约束"设计,而是"体检"设计</b>:系统把聚合结果摆出来,标出"少数派",给出三种常见原因,然后<b>把决定权交还给人</b>。这种"27 人一致、1 人例外"的问题,单看任何一行都合法,只有聚合后才能发现。</span>
</figcaption>
</figure>
<!-- ⑤ 数据看板 -->
<figure>
<div class="frame-wrap"><div class="origin">
<div class="win"><div class="wbar">
<div class="lights"><i></i><i></i><i></i></div>
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/1/dashboard</div>
</div><div class="view">
<aside class="side">
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
<nav class="nav">
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
<div class="grp">收集管理</div>
<a><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
<a><svg class="ico"><use href="#i-users"/></svg>名单管理</a>
<a><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
<a class="on"><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
<a><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
</nav>
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
</aside>
<div class="main">
<div class="topbar"><h2>数据看板</h2><span class="crumb">24中技数控1班 · 实时</span>
<span class="sp"><button class="btn ghost sm"><svg class="ico"><use href="#i-download"/></svg>导出异常清单</button></span>
</div>
<div class="scroll">
<div class="stats">
<div class="stat"><div class="lbl">应填人数</div><div class="num">28</div><div class="foot">1 个班级</div></div>
<div class="stat jade"><div class="lbl">已终审通过</div><div class="num">15</div><div class="foot">53.6%</div></div>
<div class="stat amber"><div class="lbl">提交待处理</div><div class="num">8</div><div class="foot">初审 4 · 待核对 3 · 异常 1</div></div>
<div class="stat seal"><div class="lbl">未提交</div><div class="num">5</div><div class="foot">距截止 3 天</div></div>
</div>
<div class="cols" style="grid-template-columns:1fr 1fr">
<div class="card">
<header>按报考工种统计<span class="sp"><span class="hint">来自正式列,实时聚合</span></span></header>
<div class="body" style="padding:0">
<table>
<thead><tr><th>报考工种</th><th>等级</th><th style="width:70px">人数</th><th>占比</th></tr></thead>
<tbody>
<tr><td>数控车工</td><td>四级/中级工</td><td class="num">27</td>
<td><div style="display:flex;align-items:center;gap:9px"><div style="flex:1;height:6px;border-radius:3px;background:#e8e4db;overflow:hidden"><i style="display:block;height:100%;width:96%;background:var(--jade)"></i></div><span class="mono" style="font-size:11px">96.4%</span></div></td></tr>
<tr><td>普通车工</td><td>三级/高级工</td><td class="num">1</td>
<td><div style="display:flex;align-items:center;gap:9px"><div style="flex:1;height:6px;border-radius:3px;background:#e8e4db;overflow:hidden"><i style="display:block;height:100%;width:4%;background:var(--amber)"></i></div><span class="mono" style="font-size:11px">3.6%</span></div></td></tr>
</tbody>
</table>
<div class="body" style="border-top:1px solid var(--line);padding-top:12px">
<div class="note ok" style="margin-bottom:0">
<svg class="ico"><use href="#i-check-circle"/></svg>
<div><b>这类统计之所以能实时出,是因为 18 个业务字段建成了正式列,而不是塞进 JSON。</b>SQL 直接 <span class="mono">GROUP BY</span>,还能吃到索引。</div>
</div>
</div>
</div>
</div>
<div class="card">
<header>材料异常清单<span class="sp"><span class="pill crit">需处理 4</span></span></header>
<div class="body" style="padding:0">
<table>
<thead><tr><th>姓名</th><th>问题</th><th style="width:96px">级别</th></tr></thead>
<tbody>
<tr><td>陈子文</td><td style="font-size:11.5px">一寸照片证件号与其它材料不符</td><td><span class="pill crit">串档</span></td></tr>
<tr><td>杨晨</td><td style="font-size:11.5px">身份证图偏小 558×745</td><td><span class="pill warn">低清</span></td></tr>
<tr><td>曹文锋</td><td style="font-size:11.5px">证件号 17 位,已按名单补齐</td><td><span class="pill warn">待核对</span></td></tr>
<tr><td>黎思明</td><td style="font-size:11.5px">证件号 17 位,已按名单补齐</td><td><span class="pill warn">待核对</span></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card">
<header>处理进度<span class="sp"><span class="hint">按状态分布</span></span></header>
<div class="body">
<div style="display:flex;height:26px;border-radius:6px;overflow:hidden;border:1px solid var(--line)">
<i style="width:53.6%;background:var(--jade)"></i>
<i style="width:14.3%;background:var(--ink)"></i>
<i style="width:10.7%;background:var(--amber)"></i>
<i style="width:3.6%;background:var(--seal)"></i>
<i style="width:17.8%;background:#eceae4"></i>
</div>
<div style="display:flex;gap:22px;margin-top:13px;font-size:11.5px;color:var(--ink2);flex-wrap:wrap">
<span style="display:flex;align-items:center;gap:7px"><i style="width:9px;height:9px;border-radius:2px;background:var(--jade);display:block"></i>已通过 15</span>
<span style="display:flex;align-items:center;gap:7px"><i style="width:9px;height:9px;border-radius:2px;background:var(--ink);display:block"></i>待初审 4</span>
<span style="display:flex;align-items:center;gap:7px"><i style="width:9px;height:9px;border-radius:2px;background:var(--amber);display:block"></i>待核对 3</span>
<span style="display:flex;align-items:center;gap:7px"><i style="width:9px;height:9px;border-radius:2px;background:var(--seal);display:block"></i>异常 1</span>
<span style="display:flex;align-items:center;gap:7px"><i style="width:9px;height:9px;border-radius:2px;background:#eceae4;display:block"></i>未提交 5</span>
</div>
</div>
</div>
</div>
</div>
</div></div></div></div>
<figcaption>
<span class="tag">STEP 05</span>
<span class="title">数据看板:把"为什么能实时统计"写出来</span>
这是当初<b>坚持把 18 个业务字段建成正式列、而不是塞进 JSON</b> 换来的能力——按工种统计、按学历筛选、按班级排名全部走 SQL 聚合 + 索引,实时出数。
<span class="note">看板上直接放了一句说明:"这类统计之所以能实时出,是因为字段建成了正式列。"——<b>把架构决策的收益摆在用户能看到的地方</b>,比写在技术文档里有用。</span>
</figcaption>
</figure>
<!-- ⑥ 终审 -->
<figure>
<div class="frame-wrap"><div class="origin">
<div class="win"><div class="wbar">
<div class="lights"><i></i><i></i><i></i></div>
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/review?status=pending_final</div>
</div><div class="view">
<aside class="side">
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
<nav class="nav">
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
<div class="grp">收集管理</div>
<a><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
<a><svg class="ico"><use href="#i-users"/></svg>名单管理</a>
<a class="on"><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
<a><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
<a><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
</nav>
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
</aside>
<div class="main">
<div class="topbar"><h2>终审</h2><span class="crumb">仅显示班主任已初审通过的填报单</span>
<span class="sp"><button class="btn ghost sm">批量驳回</button><button class="btn jade sm"><svg class="ico"><use href="#i-check"/></svg>通过所选 12 人</button></span>
</div>
<div class="filters">
<span class="fchip on">全部 <span class="cnt">23</span></span>
<span class="fchip">无异常 <span class="cnt">22</span></span>
<span class="fchip">有异常 <span class="cnt">1</span></span>
<span style="margin-left:auto;font-size:11.5px;color:var(--ink3);display:flex;align-items:center;gap:8px">
<svg class="ico" style="font-size:14px"><use href="#i-alert"/></svg>
<b style="color:var(--seal);font-weight:600">"一键全部通过"仅在零异常时可用</b>
</span>
</div>
<div class="scroll" style="padding:0">
<div style="background:var(--card);border:1px solid var(--line);border-radius:10px;margin:20px;overflow:hidden">
<table>
<thead><tr>
<th style="width:38px"><input type="checkbox" checked style="accent-color:var(--jade);width:15px;height:15px"></th>
<th>姓名</th><th>班级</th><th>初审人</th><th>材料</th><th>系统校验</th><th>提交时间</th><th style="width:130px">操作</th>
</tr></thead>
<tbody>
<tr><td><input type="checkbox" checked style="accent-color:var(--jade);width:15px;height:15px"></td>
<td>曾伊豪</td><td>24中技数控1班</td><td>李老师</td><td class="mono">4/4 · 2.9MB</td>
<td><span class="pill ok"><svg class="ico"><use href="#i-check"/></svg>无问题</span></td>
<td class="mono">09-28 09:41</td><td><button class="btn ghost sm">查看</button></td></tr>
<tr><td><input type="checkbox" checked style="accent-color:var(--jade);width:15px;height:15px"></td>
<td>李绍康</td><td>24中技数控1班</td><td>李老师</td><td class="mono">4/4 · 3.1MB</td>
<td><span class="pill ok"><svg class="ico"><use href="#i-check"/></svg>无问题</span></td>
<td class="mono">09-28 08:12</td><td><button class="btn ghost sm">查看</button></td></tr>
<tr><td><input type="checkbox" checked style="accent-color:var(--jade);width:15px;height:15px"></td>
<td>何天宇</td><td>24中技数控1班</td><td>李老师</td><td class="mono">4/4 · 2.6MB</td>
<td><span class="pill ok"><svg class="ico"><use href="#i-check"/></svg>无问题</span></td>
<td class="mono">09-27 21:03</td><td><button class="btn ghost sm">查看</button></td></tr>
<tr style="background:var(--seal-soft)"><td><input type="checkbox" style="accent-color:var(--jade);width:15px;height:15px"></td>
<td>陈子文</td><td>24中技数控1班</td><td>李老师</td><td class="mono">4/4 · 1.4MB</td>
<td><span class="pill crit"><svg class="ico"><use href="#i-alert"/></svg>疑似串档</span></td>
<td class="mono">09-27 16:55</td><td><button class="btn seal sm">处理</button></td></tr>
<tr><td><input type="checkbox" checked style="accent-color:var(--jade);width:15px;height:15px"></td>
<td>曾云天</td><td>24中技数控1班</td><td>李老师</td><td class="mono">4/4 · 2.2MB</td>
<td><span class="pill ok"><svg class="ico"><use href="#i-check"/></svg>无问题</span></td>
<td class="mono">09-27 15:20</td><td><button class="btn ghost sm">查看</button></td></tr>
</tbody>
</table>
</div>
<div class="note info" style="margin:0 20px 20px">
<svg class="ico"><use href="#i-shield"/></svg>
<div><b>"一键全部通过"按钮此刻是禁用状态</b>(因存在 1 条异常)。这是刻意的:防止管理员大而化之地放行。<b>只有整批零异常时才允许全通过。</b></div>
</div>
</div>
</div>
</div></div></div></div>
<figcaption>
<span class="tag">STEP 06</span>
<span class="title">终审:把"全选"这条路堵掉一半</span>
终审是最容易出事的环节——管理员面对 23 条记录,天然想"全选通过"。所以设计上做了两层限制:<b>异常行默认不勾选且标红</b>,<b>"一键全部通过"在没有异常时才可用</b>。
<span class="note">表格里保留"初审人"一列——终审时能看到是谁初审判过的,出问题时可以回溯到人。<b>责任可见,是审核质量的前提。</b></span>
</figcaption>
</figure>
<!-- ⑦ 导出中心 -->
<figure>
<div class="frame-wrap"><div class="origin">
<div class="win"><div class="wbar">
<div class="lights"><i></i><i></i><i></i></div>
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/export/new</div>
</div><div class="view">
<aside class="side">
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
<nav class="nav">
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
<div class="grp">收集管理</div>
<a><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
<a><svg class="ico"><use href="#i-users"/></svg>名单管理</a>
<a><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
<a><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
<a class="on"><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
</nav>
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
</aside>
<div class="main">
<div class="topbar"><h2>创建导出</h2><span class="crumb">2026年9月机电专业认定材料收集</span></div>
<div class="scroll">
<div class="note crit" style="margin-bottom:16px">
<svg class="ico"><use href="#i-alert"/></svg>
<div><b>前置检查未通过:1 份材料存在异常,无法打包。</b>
<br>陈子文的「一寸照片」疑似串档尚未处理。这是铁律——<b>不合格的文件不允许进入汇总包</b>。</div>
</div>
<div class="cols" style="grid-template-columns:1fr 1fr">
<div>
<div class="card">
<header>导出范围</header>
<div class="body" style="font-size:13px;line-height:2.2;color:var(--ink2)">
<label style="display:flex;align-items:center;gap:9px"><input type="radio" name="sc" style="accent-color:var(--ink)">全任务(23 人)</label>
<label style="display:flex;align-items:center;gap:9px"><input type="radio" name="sc" checked style="accent-color:var(--ink)">仅已终审通过(15 人)<span class="pill ok" style="font-size:10px">推荐</span></label>
<label style="display:flex;align-items:center;gap:9px"><input type="radio" name="sc" style="accent-color:var(--ink)">按班级选择</label>
<label style="display:flex;align-items:center;gap:9px"><input type="radio" name="sc" style="accent-color:var(--ink)">指定人员</label>
<div style="border-top:1px dashed var(--line);margin:12px 0;padding-top:12px">
<label style="display:flex;align-items:center;gap:9px"><input type="checkbox" style="accent-color:var(--ink)">同时包含原始文件(未经压缩)</label>
<label style="display:flex;align-items:center;gap:9px"><input type="checkbox" checked style="accent-color:var(--ink)">生成汇总 Excel</label>
<label style="display:flex;align-items:center;gap:9px"><input type="checkbox" style="accent-color:var(--ink)">证件号脱敏</label>
</div>
</div>
</div>
<div class="card">
<header>打包设置</header>
<div class="body" style="font-size:13px;line-height:2.2;color:var(--ink2)">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px">
<span style="width:88px">分卷大小</span>
<select style="padding:6px 10px;border:1.5px solid var(--line2);border-radius:7px;font-family:var(--font-body);font-size:12.5px;background:#fff">
<option>50 MB / 卷</option><option>100 MB / 卷</option><option>不分卷</option>
</select>
</div>
<div class="note info" style="margin-top:12px">
<svg class="ico"><use href="#i-bulb"/></svg>
<div>预计 <b>1 卷</b>,共 <b>约 37 MB</b>。对比原始文件总量 475 MB,<b>压缩率 92%</b>。</div>
</div>
</div>
</div>
</div>
<div class="card">
<header>目录结构预览<span class="sp"><span class="hint">按机构要求生成</span></span></header>
<div class="body" style="padding-top:14px">
<div class="tree">
<div class="d"><span class="br">├─ </span>202609机电认定-24中技数控1班/</div>
<div style="padding-left:20px"><span class="br">├─ </span><span class="d">一寸照片/</span></div>
<div style="padding-left:40px"><span class="br">│ └─ </span><span class="f">熊宇豪-360981200812084437-一寸照片.jpg</span> <span style="color:var(--ink4)">96KB</span></div>
<div style="padding-left:20px"><span class="br">├─ </span><span class="d">身份证/</span></div>
<div style="padding-left:40px"><span class="br">│ └─ </span><span class="f">熊宇豪-360981200812084437-身份证.jpg</span> <span style="color:var(--ink4)">810KB</span></div>
<div style="padding-left:20px"><span class="br">├─ </span><span class="d">学籍表/</span></div>
<div style="padding-left:40px"><span class="br">│ └─ </span><span class="f">熊宇豪-360981200812084437-学籍表.png</span> <span style="color:var(--ink4)">41KB</span></div>
<div style="padding-left:20px"><span class="br">├─ </span><span class="d">工作证明/</span></div>
<div style="padding-left:40px"><span class="br">│ └─ </span><span class="f">熊宇豪-360981200812084437-工作证明.jpg</span> <span style="color:var(--ink4)">806KB</span></div>
<div><span class="br">└─ </span><span class="f">考生线下报名信息导入模板(汇总).xlsx</span> <span style="color:var(--ink4)">18KB</span></div>
</div>
<div class="body" style="border-top:1px solid var(--line);padding:14px 0 0;margin-top:14px">
<div style="font-size:11.5px;color:var(--ink3);line-height:1.75">
共 <b style="color:var(--ink)">15 人 × 4 项 = 60 个文件</b>,已全部通过命名规范与体积校验。
</div>
</div>
</div>
</div>
</div>
</div>
<div style="flex:none;padding:13px 20px;background:var(--paper2);border-top:1px solid var(--line);display:flex;align-items:center;gap:10px">
<span style="font-size:12px;color:var(--ink3)">导出为异步任务,完成后微信通知你</span>
<span style="margin-left:auto;display:flex;gap:10px">
<button class="btn ghost">取消</button>
<button class="btn primary" disabled><svg class="ico"><use href="#i-download"/></svg>开始导出</button>
</span>
</div>
</div>
</div></div></div></div>
<figcaption>
<span class="tag">STEP 07</span>
<span class="title">导出中心:把"不允许带问题打包"做成按钮禁用</span>
PRD 里的铁律 2 是「<b>没有规范文件名、没有压到 1M 内的文件,不得进入汇总包</b>」。这里不是弹个警告了事,而是<b>直接禁用"开始导出"按钮</b>,并指出是哪一个人的哪一项卡住了。
<span class="note">右侧的<b>目录树预览</b>是关键——管理员在点导出之前就能看到最终交付的文件长什么样、名字对不对、体积标没标。不用等打完包再解压检查。<br>
底部还报了一个容易被忽略的数字:<b>原始 475MB → 最终 37MB,压缩率 92%</b>。</span>
</figcaption>
</figure>
<!-- ⑧ 表单设计器 -->
<figure>
<div class="frame-wrap"><div class="origin">
<div class="win"><div class="wbar">
<div class="lights"><i></i><i></i><i></i></div>
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/new?step=4</div>
</div><div class="view">
<aside class="side">
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
<nav class="nav">
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
<div class="grp">收集管理</div>
<a class="on"><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
<a><svg class="ico"><use href="#i-users"/></svg>名单管理</a>
<a><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
<a><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
<a><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
</nav>
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
</aside>
<div class="main">
<div class="topbar"><h2>新建收集任务</h2><span class="crumb">第 4 步 · 表单设计</span></div>
<div class="steps">
<div class="step done"><span class="n">1</span>基本信息</div><span class="arw">›</span>
<div class="step done"><span class="n">2</span>覆盖班级</div><span class="arw">›</span>
<div class="step done"><span class="n">3</span>名单</div><span class="arw">›</span>
<div class="step on"><span class="n">4</span>表单</div><span class="arw">›</span>
<div class="step"><span class="n">5</span>材料</div><span class="arw">›</span>
<div class="step"><span class="n">6</span>审核流</div><span class="arw">›</span>
<div class="step"><span class="n">7</span>预览发布</div>
</div>
<div class="scroll" style="padding:16px">
<div class="note info" style="margin-bottom:14px">
<svg class="ico"><use href="#i-bulb"/></svg>
<div><b>表单分两层:内置字段(来自官方模板,存正式列、可统计)+ 扩展字段(机构额外要求,存 JSON)。</b>内置字段的标签可改、顺序可调,但<b>不能删除</b>——它们是导出模板的固定列。</div>
</div>
<div style="display:grid;grid-template-columns:190px 1fr 268px;gap:14px;align-items:start">
<!-- 左:字段库 -->
<div class="card" style="margin:0">
<header style="font-size:12.5px;padding:11px 13px">字段库</header>
<div class="body" style="padding:11px 13px">
<div style="font-size:10px;font-family:var(--font-mono);letter-spacing:.1em;color:var(--ink4);margin-bottom:8px">基础</div>
<div style="display:flex;flex-direction:column;gap:6px;margin-bottom:14px">
<div class="fld"><svg class="ico"><use href="#i-file"/></svg>单行文本</div>
<div class="fld"><svg class="ico"><use href="#i-file"/></svg>数字</div>
<div class="fld"><svg class="ico"><use href="#i-file"/></svg>单选 / 多选</div>
<div class="fld"><svg class="ico"><use href="#i-file"/></svg>日期</div>
<div class="fld"><svg class="ico"><use href="#i-file"/></svg>下拉字典</div>
</div>
<div style="font-size:10px;font-family:var(--font-mono);letter-spacing:.1em;color:var(--ink4);margin-bottom:8px">专用</div>
<div style="display:flex;flex-direction:column;gap:6px">
<div class="fld hot"><svg class="ico"><use href="#i-idcard"/></svg>身份证号</div>
<div class="fld hot"><svg class="ico"><use href="#i-file"/></svg>手机号</div>
<div class="fld hot"><svg class="ico"><use href="#i-lock"/></svg>职业工种级联</div>
</div>
<div style="font-size:11px;color:var(--ink4);margin-top:12px;line-height:1.6">拖到右侧画布即可添加</div>
</div>
</div>
<!-- 中:画布 -->
<div class="card" style="margin:0">
<header style="font-size:12.5px;padding:11px 13px">表单画布<span class="sp"><span class="pill auto">21 内置 + 0 扩展</span></span></header>
<div class="body" style="padding:11px 13px;max-height:396px;overflow-y:auto">
<div style="font-size:10px;font-family:var(--font-mono);letter-spacing:.1em;color:var(--ink4);margin-bottom:8px">内置字段 · 来自官方模板</div>
<div class="frow lock"><span class="grip">⋮⋮</span><span class="fn">姓名</span><span class="ft">文本</span><span class="pill crit" style="font-size:10px"><svg class="ico"><use href="#i-lock"/></svg>锁定</span></div>
<div class="frow lock"><span class="grip">⋮⋮</span><span class="fn">身份证号</span><span class="ft">身份证</span><span class="pill crit" style="font-size:10px"><svg class="ico"><use href="#i-lock"/></svg>锁定</span></div>
<div class="frow lock"><span class="grip">⋮⋮</span><span class="fn">报考职业 / 工种 / 等级</span><span class="ft">级联</span><span class="pill crit" style="font-size:10px"><svg class="ico"><use href="#i-lock"/></svg>锁定</span></div>
<div class="frow sel"><span class="grip">⋮⋮</span><span class="fn">学历</span><span class="ft">下拉 11 项</span><span class="pill warn" style="font-size:10px">可改</span></div>
<div class="frow"><span class="grip">⋮⋮</span><span class="fn">联系电话</span><span class="ft">手机号</span></div>
<div class="frow"><span class="grip">⋮⋮</span><span class="fn">工作单位</span><span class="ft">文本</span></div>
<div class="frow"><span class="grip">⋮⋮</span><span class="fn">是否补考</span><span class="ft">是/否</span></div>
<div class="frow" style="padding-left:26px;border-left:3px solid var(--jade-soft)"><span class="grip">⋮⋮</span><span class="fn">补考理论 / 实操 / 综合</span><span class="ft">级联</span><span class="pill ok" style="font-size:10px">条件显示</span></div>
<div class="frow"><span class="grip">⋮⋮</span><span class="fn">是否在校生</span><span class="ft">是/否</span></div>
<div class="frow" style="padding-left:26px;border-left:3px solid var(--jade-soft)"><span class="grip">⋮⋮</span><span class="fn">在读学校 / 所学专业 / 入学时间</span><span class="ft">级联</span><span class="pill ok" style="font-size:10px">条件显示</span></div>
<div class="frow"><span class="grip">⋮⋮</span><span class="fn">本职业工作年限</span><span class="ft">数字</span></div>
<div class="frow"><span class="grip">⋮⋮</span><span class="fn">已获证书职业工种 / 等级 / 编号</span><span class="ft">文本</span></div>
<div style="font-size:10px;font-family:var(--font-mono);letter-spacing:.1em;color:var(--ink4);margin:14px 0 8px">扩展字段 · 存 JSON,不参与核心统计</div>
<div style="border:1.5px dashed var(--line2);border-radius:8px;padding:16px;text-align:center;font-size:11.5px;color:var(--ink4)">从左侧拖字段到这里</div>
</div>
</div>
<!-- 右:属性 -->
<div class="card" style="margin:0">
<header style="font-size:12.5px;padding:11px 13px">字段属性<span class="sp"><span class="hint">学历</span></span></header>
<div class="body" style="padding:13px">
<div style="font-size:11.5px;color:var(--ink3);margin-bottom:6px">标签</div>
<input value="学历" style="width:100%;height:32px;border:1.5px solid var(--line2);border-radius:7px;padding:0 10px;font-family:var(--font-body);font-size:12.5px;margin-bottom:12px">
<div style="font-size:11.5px;color:var(--ink3);margin-bottom:6px">选项来源</div>
<select style="width:100%;height:32px;border:1.5px solid var(--line2);border-radius:7px;padding:0 8px;font-family:var(--font-body);font-size:12.5px;background:#fff;margin-bottom:12px">
<option>字典 · 学历(11 项)</option>
<option>手动输入选项</option>
</select>
<div style="font-size:11.5px;color:var(--ink3);margin-bottom:8px">校验</div>
<label style="display:flex;align-items:center;gap:8px;font-size:12.5px;margin-bottom:7px"><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px">必填</label>
<label style="display:flex;align-items:center;gap:8px;font-size:12.5px;margin-bottom:12px"><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px">严格值匹配(禁止手填)</label>
<div style="font-size:11.5px;color:var(--ink3);margin-bottom:6px">学生端行为</div>
<div class="note warn" style="padding:9px 11px;font-size:11.5px;line-height:1.6">
<svg class="ico" style="font-size:13px"><use href="#i-pencil"/></svg>
<div><b>预填可改</b><br>名单有值则预填并标注"老师已填,请核对"</div>
</div>
</div>
</div>
</div>
<div class="card" style="margin-top:14px">
<header>条件显示规则<span class="sp"><span class="hint">官方模板用绿色标题标识的级联列</span></span></header>
<div class="body" style="padding:13px 16px">
<div class="rule-row">
<span class="pill gray">当</span><span class="mono">是否补考</span><span class="op">=</span><span class="val">是</span>
<span class="arrow">→</span><span class="pill ok">显示</span><span class="mono">补考理论 / 补考实操 / 补考综合</span>
<span class="pill warn" style="margin-left:auto">并校验:科目数受等级约束</span>
</div>
<div class="rule-row">
<span class="pill gray">当</span><span class="mono">是否在校生</span><span class="op">=</span><span class="val">是</span>
<span class="arrow">→</span><span class="pill ok">显示且必填</span><span class="mono">在读学校 / 所学专业 / 入学时间</span>
<span class="pill warn" style="margin-left:auto">入学时间格式 YYYY-MM-DD</span>
</div>
<div class="rule-row">
<span class="pill gray">当</span><span class="mono">认定类型</span><span class="op">=</span><span class="val">专项职业能力</span>
<span class="arrow">→</span><span class="pill crit">隐藏</span><span class="mono">报考工种 / 报考等级 / 全部补考科目</span>
<span class="pill crit" style="margin-left:auto">整组跳过校验</span>
</div>
</div>
</div>
</div>
</div>
</div></div></div></div>
<figcaption>
<span class="tag">STEP 08</span>
<span class="title">表单设计器:内置字段锁定,扩展字段自由</span>
左中右三栏是老配方(字段库 / 画布 / 属性),但有一个关键约束:<b style="color:var(--seal)">内置的 21 个字段带锁标记,可改标签、可调顺序,但不能删除</b>——它们是导出模板的固定列,删了就无法写回官方 Excel。
<span class="note">底部那三条<b>条件显示规则</b>直接对应官方模板的"绿色标题 = 级联选项"。第三条(专项职业能力 → 整组隐藏并跳过校验)最容易被漏做,单独列出来。<br>
"扩展字段"区域用虚线框标注<b>存 JSON、不参与核心统计</b>——把之前那个架构取舍摆在配置界面上,管理员就知道为什么有些字段能统计、有些不能。</span>
</figcaption>
</figure>
<!-- ⑨ 材料规则配置 -->
<figure>
<div class="frame-wrap"><div class="origin">
<div class="win"><div class="wbar">
<div class="lights"><i></i><i></i><i></i></div>
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/new?step=5</div>
</div><div class="view">
<aside class="side">
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
<nav class="nav">
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
<div class="grp">收集管理</div>
<a class="on"><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
<a><svg class="ico"><use href="#i-users"/></svg>名单管理</a>
<a><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
<a><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
<a><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
</nav>
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
</aside>
<div class="main">
<div class="topbar"><h2>新建收集任务</h2><span class="crumb">第 5 步 · 材料规则</span>
<span class="sp"><button class="btn ghost sm">从规则模板导入</button><button class="btn primary sm">保存为模板</button></span>
</div>
<div class="steps">
<div class="step done"><span class="n">1</span>基本信息</div><span class="arw">›</span>
<div class="step done"><span class="n">2</span>覆盖班级</div><span class="arw">›</span>
<div class="step done"><span class="n">3</span>名单</div><span class="arw">›</span>
<div class="step done"><span class="n">4</span>表单</div><span class="arw">›</span>
<div class="step on"><span class="n">5</span>材料</div><span class="arw">›</span>
<div class="step"><span class="n">6</span>审核流</div><span class="arw">›</span>
<div class="step"><span class="n">7</span>预览发布</div>
</div>
<div class="scroll" style="padding:16px">
<div style="display:grid;grid-template-columns:206px 1fr;gap:14px;align-items:start">
<div class="card" style="margin:0">
<header style="font-size:12.5px;padding:11px 13px">材料清单<span class="sp"><span class="hint">4 项</span></span></header>
<div class="body" style="padding:8px">
<div class="mitem on"><svg class="ico"><use href="#i-image"/></svg><div><div class="mn">一寸照片</div><div class="ms">P1 证件照 · ≤0.3M</div></div></div>
<div class="mitem"><svg class="ico"><use href="#i-idcard"/></svg><div><div class="mn">身份证</div><div class="ms">P2 证件翻拍 · ≤1M</div></div></div>
<div class="mitem"><svg class="ico"><use href="#i-file"/></svg><div><div class="mn">学籍表</div><div class="ms">P4 截图 · ≤1M</div></div></div>
<div class="mitem"><svg class="ico"><use href="#i-stamp"/></svg><div><div class="mn">工作证明</div><div class="ms">P3 扫描件 · ≤1M</div></div></div>
<button class="btn ghost sm" style="width:100%;justify-content:center;margin-top:8px">+ 添加材料项</button>
</div>
</div>
<div class="card" style="margin:0">
<header>「一寸照片」的规则<span class="sp"><span class="pill auto">实测 984×1378 → 规格化 295×413</span></span></header>
<div class="body" style="padding:16px">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:18px">
<div>
<div class="f-label">命名模板</div>
<div class="tpl" style="margin:0 0 8px;font-size:13px">{<em>姓名</em>}<span class="sep">-</span>{<em>证件号</em>}<span class="sep">-</span>{<em>材料名</em>}.jpg</div>
<div class="vars" style="margin-bottom:14px">
<span class="vchip">{姓名}</span><span class="vchip">{证件号}</span><span class="vchip">{班级}</span><span class="vchip">{序号}</span>
</div>
<div class="f-label">别名(用于识别历史文件名)</div>
<div style="font-size:12px;color:var(--ink2);line-height:1.9;background:#faf9f6;border:1px solid var(--line);border-radius:7px;padding:9px 11px">
1寸照片 · 一寸电子照片 · 证件照 · 免冠照
</div>
</div>
<div>
<div class="f-label">格式与体积</div>
<div style="display:flex;gap:8px;margin-bottom:12px">
<span class="pill ok">jpg</span><span class="pill ok">png</span><span class="pill gray">pdf</span><span class="pill gray">heic</span>
</div>
<div style="display:flex;align-items:center;gap:9px;font-size:12.5px;margin-bottom:12px">
<span style="color:var(--ink3)">体积上限</span>
<input value="300" style="width:62px;height:29px;border:1.5px solid var(--line2);border-radius:6px;padding:0 8px;font-family:var(--font-mono);font-size:12px;text-align:right">
<span class="mono" style="color:var(--ink3)">KB</span>
<span class="pill warn" style="margin-left:4px">含下限保护</span>
</div>
<div class="f-label">压缩档位</div>
<select style="width:100%;height:32px;border:1.5px solid var(--line2);border-radius:7px;padding:0 8px;font-family:var(--font-body);font-size:12.5px;background:#fff">
<option>P1 证件照(固定 295×413 @300dpi)</option>
<option>P2 证件翻拍</option><option>P3 A4 扫描件</option><option>P4 截图</option>
</select>
<div class="note ok" style="margin-top:12px;padding:9px 11px;font-size:11.5px">
<svg class="ico" style="font-size:13px"><use href="#i-check-circle"/></svg>
<div>本项有专属规则:<b>证件照规格化</b>会裁切并写入 300dpi</div>
</div>
</div>
</div>
<div style="border-top:1px solid var(--line);margin-top:16px;padding-top:14px">
<div class="f-label">来源引导语(显示在学生端)</div>
<input value="照相馆给过你电子版吗?在微信聊天记录里搜一下" style="width:100%;height:34px;border:1.5px solid var(--line2);border-radius:7px;padding:0 11px;font-family:var(--font-body);font-size:12.5px">
<div style="display:flex;gap:20px;margin-top:12px;font-size:12.5px;color:var(--ink2)">
<label style="display:flex;align-items:center;gap:8px"><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px">必传</label>
<label style="display:flex;align-items:center;gap:8px"><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px">允许一次多选</label>
<label style="display:flex;align-items:center;gap:8px"><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px">开启拍照入口<span class="pill gray" style="font-size:10px">默认关闭</span></label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div></div></div></div>
<figcaption>
<span class="tag">STEP 09</span>
<span class="title">材料规则配置:把"压缩档位"做成可解释的选择</span>
每个材料项独立配置命名模板、别名、格式白名单、体积上限、压缩档位、来源引导语。左侧列表直接显示每项的档位与上限——<b>管理员一眼能看出四项材料用了四套不同规则</b>。
<span class="note">三个细节:① 体积上限旁标"<b>含下限保护</b>"(防止过度压缩,实测有一张身份证只有 0.08MB);② 压缩档位下拉里写明每档的适用条件("P1 证件照(固定 295×413)"),不是让管理员去猜 "P1" 是什么;③ "<b>开启拍照入口</b>"默认不勾——因为实测 4 类材料无一来自现场拍照。</span>
</figcaption>
</figure>
<!-- ⑪ 角色与授权 -->
<figure>
<div class="frame-wrap"><div class="origin">
<div class="win"><div class="wbar">
<div class="lights"><i></i><i></i><i></i></div>
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/roles</div>
</div><div class="view">
<aside class="side">
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
<nav class="nav">
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
<div class="grp">组织与账号</div>
<a><svg class="ico"><use href="#i-org"/></svg>组织架构</a>
<a><svg class="ico"><use href="#i-users"/></svg>账号管理</a>
<a class="on"><svg class="ico"><use href="#i-lock"/></svg>角色与授权</a>
<div class="grp">平台运维</div>
<a><svg class="ico"><use href="#i-list"/></svg>操作日志</a>
</nav>
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
</aside>
<div class="main">
<div class="topbar">
<h2>角色与授权</h2><span class="crumb">江西省机电技师学院 · 4 个内置角色 + 1 个自定义</span>
<span class="sp"><span class="hint">内置角色的权限由代码决定,不可修改</span></span>
</div>
<div class="scroll">
<div style="display:grid;grid-template-columns:236px 1fr;gap:16px;align-items:start">
<div class="card" style="margin:0">
<header style="font-size:12.5px;padding:11px 13px">角色<span class="sp"><button class="btn primary sm">新建</button></span></header>
<div class="body" style="padding:6px">
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;border-radius:6px;font-size:12.5px">
<span style="flex:1">平台运营</span><span class="pill gray" style="font-size:10px">内置</span><span class="pill gray" style="font-size:10px">不可分配</span><span style="font-size:11px;color:var(--ink3)">0 人</span>
</div>
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;border-radius:6px;font-size:12.5px">
<span style="flex:1">学校管理员</span><span class="pill gray" style="font-size:10px">内置</span><span style="font-size:11px;color:var(--ink3)">1 人</span>
</div>
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;border-radius:6px;font-size:12.5px">
<span style="flex:1">班主任</span><span class="pill gray" style="font-size:10px">内置</span><span style="font-size:11px;color:var(--ink3)">1 人</span>
</div>
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;border-radius:6px;font-size:12.5px">
<span style="flex:1">学生</span><span class="pill gray" style="font-size:10px">内置</span><span style="font-size:11px;color:var(--ink3)">11 人</span>
</div>
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;border-radius:6px;font-size:12.5px;background:var(--jade-soft)">
<span style="flex:1;font-weight:600">年级审核员</span><span class="pill auto" style="font-size:10px">自定义</span><span style="font-size:11px;color:var(--ink3)">0 人</span>
</div>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:14px">
<div class="card" style="margin:0">
<header>
年级审核员 <span class="mono" style="font-weight:400;color:var(--ink3)">GRADE_AUDITOR</span>
<span class="sp">
<button class="btn ghost sm">刷新</button>
<button class="btn ghost sm">编辑</button>
<button class="btn ghost sm">停用</button>
<button class="btn ghost sm">删除</button>
</span>
</header>
<div class="body">
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px;font-size:12.5px;margin-bottom:12px">
<div><div style="color:var(--ink3);font-size:11px;margin-bottom:3px">类型</div>自定义</div>
<div><div style="color:var(--ink3);font-size:11px;margin-bottom:3px">用户类型</div>班主任</div>
<div><div style="color:var(--ink3);font-size:11px;margin-bottom:3px">数据范围</div>仅本人</div>
<div><div style="color:var(--ink3);font-size:11px;margin-bottom:3px">账号数</div>0</div>
</div>
<div class="note warn" style="margin-bottom:14px">
<svg class="ico"><use href="#i-alert"/></svg>
<div><b>有未保存的改动。</b>点「保存授权」后<b>立即生效</b>——已登录的账号不需要重新登录(服务端会清掉这个角色的授权缓存)。</div>
</div>
<div style="font-size:12.5px;font-weight:600;margin-bottom:4px">能做什么(权限码)</div>
<div style="font-size:11.5px;color:var(--ink3);margin-bottom:9px">决定按钮与接口能不能用;勾了才会真的生效</div>
<div style="font-size:11.5px;color:var(--ink2);margin-bottom:6px">名册</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px 14px;font-size:12.5px;margin-bottom:12px">
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 名册查看 <span class="mono" style="font-size:11px;color:var(--ink3)">roster:view</span></label>
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 名册修改(仅本班) <span class="mono" style="font-size:11px;color:var(--ink3)">roster:manageOwn</span></label>
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 名册导入与修改(全租户) <span class="mono" style="font-size:11px;color:var(--ink3)">roster:manage</span></label>
</div>
<div style="font-size:11.5px;color:var(--ink2);margin-bottom:6px">审核</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px 14px;font-size:12.5px;margin-bottom:12px">
<label><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px"> 初审(班主任) <span class="mono" style="font-size:11px;color:var(--ink3)">audit:first</span></label>
<label><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px"> 教师代传材料 <span class="mono" style="font-size:11px;color:var(--ink3)">file:proxyUpload</span></label>
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 终审(管理员) <span class="mono" style="font-size:11px;color:var(--ink3)">audit:final</span></label>
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 撤回审核 <span class="mono" style="font-size:11px;color:var(--ink3)">audit:withdraw</span></label>
</div>
<div style="font-size:11.5px;color:var(--ink2);margin-bottom:6px">平台运营<span class="hint" style="margin-left:6px">学校租户配不了这一组</span></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px 14px;font-size:12.5px;color:var(--ink3);margin-bottom:14px">
<label><input type="checkbox" disabled style="width:14px;height:14px"> 租户与套餐管理 <span class="mono" style="font-size:11px">tenant:manage</span> <span class="pill crit" style="font-size:10px">平台级,学校不可授予</span></label>
<label><input type="checkbox" disabled style="width:14px;height:14px"> 全量操作日志 <span class="mono" style="font-size:11px">log:viewAll</span> <span class="pill crit" style="font-size:10px">平台级,学校不可授予</span></label>
</div>
<div style="font-size:12.5px;font-weight:600;margin-bottom:4px">看得见哪些入口(菜单)</div>
<div style="font-size:11.5px;color:var(--ink3);margin-bottom:9px">只影响左侧菜单显不显示 —— 真正的权限在上面那一栏,绕过前端只会拿到 40301</div>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:6px 14px;font-size:12.5px">
<label><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px"> 工作台</label>
<label><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px"> 收集任务</label>
<label><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px"> 审核</label>
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 导出中心</label>
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 通知记录</label>
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 字典管理</label>
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 组织架构</label>
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 账号管理</label>
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 角色与授权</label>
</div>
<div style="margin-top:14px">
<button class="btn primary sm">保存授权</button>
<button class="btn ghost sm">撤销改动</button>
<span class="hint" style="margin-left:8px">保存后立即生效;已登录的账号不需要重新登录</span>
</div>
</div>
</div>
<div class="card" style="margin:0">
<header style="font-size:12.5px;padding:11px 13px">内置角色点开是什么样</header>
<div class="body" style="padding:13px">
<div class="note info" style="margin-bottom:0">
<svg class="ico"><use href="#i-lock"/></svg>
<div><b>只读。</b>权限矩阵照着显示(可以拿它当模板照着勾),但勾选框是禁用的、没有「保存授权」按钮。<br>
<span style="font-size:11.5px;color:var(--ink3)">要不同的能力范围,请<b>新建一个自定义角色</b>——而不是去改内置的。</span></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div></div></div></div>
<figcaption>
<span class="tag">STEP 10 · 角色与授权</span>
<span class="title">权限是契约、不是配置:内置角色只读,自定义角色可配</span>
内置角色现在只剩 4 个(平台运营 / 学校管理员 / 班主任 / 学生),权限写在后端代码里(<span class="mono">BuiltinRole</span>)、界面上<b>只读</b>。租户可另建自定义角色,于是"谁能做什么"第一次变成客户可以自己配的东西 —— 而这类功能一旦做松,代价是<b>管理员把自己锁在系统外</b>。
<span class="note">五个细节:<br>
① <b>内置角色只读</b>——可以看、不能改。它的权限是前后端契约(权限码定义在代码里),改它等于改代码;界面上正确的做法是"照着它新建一个";<br>
② <b>「看得见」与「有权限」分开</b>——菜单只决定左侧显示哪些入口,真正的访问控制是权限码校验(绕过前端只会拿到 40301);<br>
③ <b>平台级权限与平台专用菜单在界面上禁用</b>,服务端也会以 40301 拒绝——"学校管理员给自己人造一个平台账号"是这套系统里最不可逆的越权;<br>
④ <b>改完立即生效</b>——服务端在写授权时清掉该角色的缓存,所以不必"重新登录看看生效没有";停用角色同理(立刻不再给权限,而不是等 token 过期);<br>
⑤ <b>防自锁</b>——不允许去掉自己进入本页所依赖的最后一条「角色与授权」权限/菜单,也不允许删掉唯一的管理员角色;这类错误只能靠 SQL 救回来。</span>
</figcaption>
</figure>
</div>
<section class="apron">
<h3>设计依据 · 桌面端与移动端的分工</h3>
<div class="aside-grid">
<div class="aside-card">
<h4><span class="n">01</span>为什么管理端必须做在 PC</h4>
任务向导 7 步、21 列字段映射、目录树预览、批量终审——<b>这些都需要足够大的画布和多列信息并置</b>。PRD 里明确"管理员在 PC 上操作",正是因为手机屏幕放不下这些决策所需的信息密度。
</div>
<div class="aside-card">
<h4><span class="n">02</span>为什么"命名规则试跑"是最重要的一屏</h4>
规则配错的代价是<b>全员改名错误</b>,而且<b>错到导出时才会发现</b>——那时学生早就交完材料了。所以它被设成发布的前置条件(未试跑不给发布),而不是一个可选功能。
</div>
<div class="aside-card">
<h4><span class="n">03</span>为什么表头识别要可视化</h4>
实测两份真实文件:一份表头在<b>第 1 行</b>、一份在<b>第 6 行</b>,工作表名还不同。系统自动定位之后,<b>必须让人确认"认对了没有"</b>——默默猜对是运气,猜错了没人知道。
</div>
<div class="aside-card">
<h4><span class="n">04</span>为什么"报考范围"只做体检不做更正</h4>
范围来自名单(老师已填好),系统<b>没有立场去改它</b>。但"27 人一致、1 人例外"这类问题单看任何一行都合法,只有聚合后才能发现——所以给出数据、给出三种常见原因,<b>把判断权交还给人</b>。
</div>
<div class="aside-card">
<h4><span class="n">05</span>为什么导出要禁用按钮而不是弹警告</h4>
警告可以点"我知道了"绕过,禁用不能。铁律必须体现在<b>交互层面</b>而非提示层面。同时明确告知"卡在谁身上、卡在哪一项",让管理员知道去哪儿解决。
</div>
<div class="aside-card">
<h4><span class="n">06</span>为什么数据看板要标注"来自正式列"</h4>
这是把架构决策的收益摆到用户能看到的地方。当初为"存 JSON 还是建列"争论过一轮,最终选择建 <b>18 个正式列</b>——代价是 DDL 长一点,收益是<b>按工种统计、按学历筛选、按班级排名全部实时且走索引</b>。
</div>
<div class="aside-card">
<h4><span class="n">07</span>为什么内置字段要"锁"</h4>
21 个内置字段来自官方模板,机构明确"请勿调整模板"。它们既是导出 Excel 的固定列,又是统计的正式列。所以设计器里**可改标签、可调顺序,但不能删除**——删了就无法写回官方模板。
</div>
<div class="aside-card">
<h4><span class="n">08</span>为什么压缩档位要写成"P1 证件照(固定 295×413)"</h4>
如果下拉里只写"P1 / P2 / P3",管理员必须去翻文档才知道选哪个。**把档位的适用条件直接写在选项里**,配置界面本身就变成说明书。
</div>
<div class="aside-card">
<h4><span class="n">09</span>为什么内置角色只读、自定义角色才能改</h4>
内置角色的权限<b>定义在后端代码里</b>(权限码是前后端契约:后端用它校验、前端用它控制按钮显隐)。把它做成"界面上可改",等于把一份契约拆成两处维护——而两处一旦不一致,表现是"按钮显示出来了、点了报 40301"。更要紧的是:一次误操作就能把管理员锁在系统外,而这类错误<b>不可逆</b>。所以界面上只读,需要别的能力范围时<b>新建一个自定义角色</b>。
</div>
</div>
</section>
<script>
// 缩放
document.querySelectorAll('.zoom button').forEach(b=>{
b.addEventListener('click',()=>{
document.querySelectorAll('.zoom button').forEach(x=>x.classList.remove('on'));
b.classList.add('on');
const z=parseFloat(b.dataset.z);
document.querySelectorAll('.origin').forEach(o=>{
o.style.transform=`scale(${z})`;
const h=o.querySelector('.win').getBoundingClientRect().height;
o.parentElement.style.height=(h)+'px';
});
});
});
// 进入视口浮现
const figs=[...document.querySelectorAll('figure')];
figs.forEach(f=>{f.style.opacity='0';f.style.transform='translateY(24px)';
f.style.transition='opacity .7s cubic-bezier(.2,.8,.3,1),transform .7s cubic-bezier(.2,.8,.3,1)';});
const io=new IntersectionObserver(es=>{es.forEach(e=>{if(e.isIntersecting){
e.target.style.opacity='1';e.target.style.transform='translateY(0)';io.unobserve(e.target);}});},{threshold:.06});
figs.forEach(f=>io.observe(f));
// 筛选芯片
document.querySelectorAll('.fchip').forEach(c=>c.addEventListener('click',()=>{
c.parentElement.querySelectorAll('.fchip').forEach(x=>x.classList.remove('on'));c.classList.add('on');}));
</script>
</body>
</html>