mvp-demo.html 127 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828
  1. <!doctype html>
  2. <html lang="zh-CN" data-accent="indigo">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <title>Fmode v2 · MVP Demo(双端 11 屏)</title>
  7. <style>
  8. /* ==========================================================================
  9. Fmode v2 · MVP Demo
  10. 令牌值取自 ui-kit src/styles.uikit.css + indigo accent(裁决 12 / ADR-002)
  11. 本文件仅供评审,不进生产包,不污染 src/
  12. ========================================================================== */
  13. /* ---------- 令牌 ---------- */
  14. :root{
  15. --canvas:#f6f7f9; --surface:#ffffff; --surface-2:#f1f3f6; --surface-hover:#f6f7f9; --surface-active:#eef1f5;
  16. --edge:#e4e7ec; --edge-strong:#cbd2da;
  17. --ink:#1f2733; --ink-2:#4a5568; --muted:#6b7280; --faint:#9aa1ac; --inverse:#ffffff;
  18. --primary:#5856D6; --primary-hover:#4442BE; --primary-active:#3836a3; --primary-subtle:#ECEBFC; --primary-ring:rgb(88 86 214 / .35);
  19. --success:#047857; --success-subtle:#e7f5ef;
  20. --danger:#b91c1c; --danger-subtle:#fdecec;
  21. --warning:#a04b08; --warning-subtle:#fdf1dc;
  22. --info:#0369a1; --info-subtle:#e6f2fa;
  23. --fs-11:11.5px; --fs-12:12px; --fs-13:13px; --fs-15:15px; --fs-17:17px; --fs-20:20px; --fs-26:26px; --fs-42:42px;
  24. --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px; --sp-6:24px; --sp-8:32px;
  25. --r-sm:11px; --r-md:14px; --r-lg:18px; --r-tag:999px;
  26. /* 桌面栏宽(《双端布局规范》§2.1,全部令牌化,禁止硬编码) */
  27. --dk-nav-w:56px; --dk-nav-w-expanded:236px;
  28. --dk-list-w:300px; --dk-list-w-narrow:280px;
  29. --dk-main-min-w:480px;
  30. --dk-aside-w:340px; --dk-aside-w-compact:300px;
  31. /* 移动关键尺寸(§3.1,硬约束) */
  32. --touch-min:44px;
  33. --hd-h:64px; /* 栏头统一高度:列表 / 主区 / 右栏三栏底部分隔线必须对齐 */
  34. --mb-navbar-h:56px; --mb-tabbar-h:56px;
  35. --mb-list-h-1:56px; --mb-list-h-2:64px; --mb-list-h-3:88px;
  36. --mb-avatar:44px; --mb-avatar-lg:64px;
  37. --mb-gutter:16px;
  38. --mb-safe-top:env(safe-area-inset-top); --mb-safe-bottom:env(safe-area-inset-bottom);
  39. --r-sheet:32px;
  40. /* z-index 阶梯(§七,禁魔数) */
  41. --z-affix:900; --z-float:950; --z-mask:980; --z-popover:1000; --z-modal:1100; --z-toast:1400;
  42. --shadow-soft:0 1px 2px rgb(15 23 42 / .04);
  43. --shadow-card:0 1px 3px rgb(15 23 42 / .06), 0 1px 2px rgb(15 23 42 / .04);
  44. --shadow-pop:0 4px 16px rgb(15 23 42 / .10);
  45. --shadow-overlay:0 12px 40px rgb(15 23 42 / .16);
  46. --scrim:rgb(15 23 42 / .35);
  47. --stage-bg:radial-gradient(rgb(15 23 42 / .07) 1px, transparent 1px) 0 0 / 22px 22px, var(--canvas);
  48. }
  49. html.dark{
  50. --canvas:#16181d; --surface:#1e2128; --surface-2:#272b33; --surface-hover:#272b33; --surface-active:#333842;
  51. --edge:#2e333c; --edge-strong:#454b56;
  52. --ink:#f2f4f7; --ink-2:#c9cfd8; --muted:#98a1ad; --faint:#6b727d; --inverse:#16181d;
  53. --primary:#7D7AFF; --primary-hover:#9B99FF; --primary-active:#5856D6; --primary-subtle:rgb(125 122 255 / .17); --primary-ring:rgb(125 122 255 / .4);
  54. --success:#34d399; --success-subtle:rgb(52 211 153 / .14);
  55. --danger:#f87171; --danger-subtle:rgb(248 113 113 / .14);
  56. --warning:#fbbf24; --warning-subtle:rgb(251 191 36 / .14);
  57. --info:#38bdf8; --info-subtle:rgb(56 189 248 / .14);
  58. --shadow-soft:0 1px 2px rgb(0 0 0 / .30);
  59. --shadow-card:0 1px 3px rgb(0 0 0 / .40), 0 1px 2px rgb(0 0 0 / .30);
  60. --shadow-pop:0 4px 16px rgb(0 0 0 / .50);
  61. --shadow-overlay:0 12px 40px rgb(0 0 0 / .60);
  62. --scrim:rgb(0 0 0 / .55);
  63. --stage-bg:radial-gradient(rgb(255 255 255 / .07) 1px, transparent 1px) 0 0 / 22px 22px, var(--canvas);
  64. }
  65. *{box-sizing:border-box;margin:0;padding:0}
  66. html,body{height:100%}
  67. body{
  68. background:var(--canvas); color:var(--ink);
  69. font:var(--fs-15)/1.6 system-ui,-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;
  70. -webkit-font-smoothing:antialiased;
  71. }
  72. button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
  73. input,textarea{font:inherit;color:inherit}
  74. ol,ul{list-style:none}
  75. /* ---------- 图标 ---------- */
  76. .ic{width:18px;height:18px;flex-shrink:0;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
  77. .ic-sm{width:15px;height:15px}
  78. .ic-lg{width:22px;height:22px}
  79. .ic-fill{fill:currentColor;stroke:none}
  80. /* ---------- 演示外壳 ---------- */
  81. .app{display:grid;grid-template-columns:236px 1fr;grid-template-rows:56px 1fr;height:100vh}
  82. .topbar{
  83. grid-column:1/-1;display:flex;align-items:center;gap:var(--sp-4);
  84. padding:0 var(--sp-5);background:var(--surface);border-bottom:1px solid var(--edge);z-index:var(--z-affix);
  85. }
  86. .brand{display:flex;align-items:center;gap:var(--sp-3);min-width:0}
  87. .brand-mark{width:30px;height:30px;border-radius:var(--r-sm);background:var(--primary);color:var(--inverse);display:grid;place-items:center;font-weight:800;font-size:var(--fs-15);flex-shrink:0}
  88. .brand-t{font-size:var(--fs-15);font-weight:700;letter-spacing:-.01em;white-space:nowrap}
  89. .brand-s{font-size:var(--fs-12);color:var(--muted);white-space:nowrap}
  90. .top-sp{flex:1}
  91. .tool{display:flex;align-items:center;gap:var(--sp-1);background:var(--surface-2);border-radius:var(--r-sm);padding:3px}
  92. .tool-i{padding:5px var(--sp-3);border-radius:8px;font-size:var(--fs-13);color:var(--muted);white-space:nowrap;transition:.15s}
  93. .tool-i.on{background:var(--surface);color:var(--ink);font-weight:600;box-shadow:var(--shadow-soft)}
  94. .tool-i:hover:not(.on){color:var(--ink-2)}
  95. .tbtn{width:34px;height:34px;border-radius:var(--r-sm);display:grid;place-items:center;color:var(--muted);border:1px solid var(--edge);background:var(--surface)}
  96. .tbtn:hover{color:var(--ink);border-color:var(--edge-strong)}
  97. .tbtn.on{color:var(--primary);border-color:var(--primary);background:var(--primary-subtle)}
  98. .sidenav{background:var(--surface);border-right:1px solid var(--edge);overflow-y:auto;padding:var(--sp-3) var(--sp-3) var(--sp-6)}
  99. .nav-group{font-size:var(--fs-11);font-weight:700;color:var(--faint);letter-spacing:.06em;text-transform:uppercase;padding:var(--sp-4) var(--sp-2) var(--sp-2)}
  100. .nav-i{
  101. width:100%;display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) var(--sp-3);
  102. border-radius:var(--r-sm);color:var(--ink-2);font-size:var(--fs-13);text-align:left;transition:.15s;
  103. }
  104. .nav-i:hover{background:var(--surface-hover)}
  105. .nav-i.on{background:var(--primary-subtle);color:var(--primary);font-weight:600}
  106. .nav-i .ic{color:currentColor;opacity:.85}
  107. .nav-idx{margin-left:auto;font-size:var(--fs-11);color:var(--faint);font-variant-numeric:tabular-nums}
  108. .nav-i.on .nav-idx{color:var(--primary)}
  109. .stage{overflow:auto;background:var(--stage-bg);padding:var(--sp-6)}
  110. .screen{display:none}
  111. .screen.is-on{display:block;animation:fadeIn .22s ease-out}
  112. @keyframes fadeIn{from{opacity:0;transform:translateY(var(--sp-1))}to{opacity:1;transform:none}}
  113. @media (prefers-reduced-motion:reduce){.screen.is-on{animation:none}}
  114. /* ---------- 视图容器:两端布局的地基 ----------
  115. 桌面 .dk 横排 [导航轨 56 | 视图],视图内再横排 [列表 300 | 主区 flex | 右栏 340]
  116. 移动 .mb 竖排 [状态栏 | 视图 | TabBar],视图内再竖排 [顶栏 56 | 内容 flex | 输入]
  117. 此前 .view 无任何规则 → 桌面各栏上下堆叠、移动内容区高度塌陷,两端骨架均不成立 */
  118. .dk>.view{flex:1;display:flex;flex-direction:row;min-width:0;height:100%}
  119. .mb>.view{flex:1;display:flex;flex-direction:column;min-width:0;min-height:0}
  120. .view>.page,.view>.pane-list,.view>.pane-main,.view>.pane-aside{min-width:0}
  121. /* 设备外框 */
  122. .device{background:var(--surface);border:1px solid var(--edge);border-radius:var(--r-lg);box-shadow:var(--shadow-pop);overflow:hidden;margin:0 auto}
  123. .device-cap{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--edge);font-size:var(--fs-12);color:var(--muted)}
  124. .device-cap b{color:var(--ink);font-size:var(--fs-13);font-weight:650}
  125. .dk{width:1440px;height:900px;transform-origin:top left;display:flex;background:var(--canvas)}
  126. .device-dk{width:1440px;height:900px}
  127. /* 标注 */
  128. .notes{display:none;max-width:920px;margin:var(--sp-5) auto 0}
  129. body.show-notes .notes{display:block}
  130. .notes-h{display:flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-13);font-weight:700;color:var(--info);margin-bottom:var(--sp-3)}
  131. .note{background:var(--surface);border:1px solid var(--edge);border-left:3px solid var(--info);border-radius:0 var(--r-sm) var(--r-sm) 0;padding:var(--sp-3) var(--sp-4);font-size:var(--fs-13);color:var(--ink-2);line-height:1.8;margin-bottom:var(--sp-2)}
  132. .note b{color:var(--info)}
  133. .note.warn{border-left-color:var(--warning)} .note.warn b{color:var(--warning)}
  134. .note.ok{border-left-color:var(--success)} .note.ok b{color:var(--success)}
  135. .ref{display:inline-block;padding:1px 7px;border-radius:var(--r-tag);font-size:var(--fs-11);background:var(--surface-2);color:var(--muted);margin-right:var(--sp-1);white-space:nowrap}
  136. /* ---------- 通用件 ---------- */
  137. .badge{display:inline-flex;align-items:center;gap:3px;padding:1px 8px;border-radius:var(--r-tag);font-size:var(--fs-11);font-weight:600;line-height:1.7;white-space:nowrap}
  138. .badge-p{background:var(--primary-subtle);color:var(--primary)}
  139. .badge-d{background:var(--danger-subtle);color:var(--danger)}
  140. .badge-w{background:var(--warning-subtle);color:var(--warning)}
  141. .badge-s{background:var(--success-subtle);color:var(--success)}
  142. .badge-i{background:var(--info-subtle);color:var(--info)}
  143. .badge-n{background:var(--surface-2);color:var(--muted)}
  144. .dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;display:inline-block}
  145. .dot-success{background:var(--success)} .dot-warning{background:var(--warning)}
  146. .dot-danger{background:var(--danger)} .dot-info{background:var(--info)} .dot-muted{background:var(--faint)}
  147. .sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
  148. /* 头像 */
  149. .av{width:40px;height:40px;border-radius:var(--r-sm);display:grid;place-items:center;font-weight:650;font-size:var(--fs-13);flex-shrink:0;position:relative;background:var(--primary-subtle);color:var(--primary)}
  150. .av-bot{background:var(--primary-subtle);color:var(--primary)}
  151. .av-user{background:var(--surface-2);color:var(--ink-2)}
  152. .av-file{background:var(--surface-2);color:var(--ink-2)}
  153. .av-sm{width:32px;height:32px;font-size:var(--fs-11);border-radius:var(--r-sm)}
  154. .av-lg{width:64px;height:64px;font-size:var(--fs-20);border-radius:var(--r-lg)}
  155. .av-round{border-radius:50%}
  156. .av-on::after,.av-idle::after,.av-off::after{content:"";position:absolute;right:-1px;bottom:-1px;width:11px;height:11px;border-radius:50%;border:2px solid var(--surface)}
  157. .av-on::after{background:var(--success)} .av-idle::after{background:var(--warning)} .av-off::after{background:var(--faint)}
  158. .av-danger{background:var(--danger-subtle);color:var(--danger)}
  159. /* 按钮 */
  160. .btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);height:36px;padding:0 var(--sp-4);border-radius:var(--r-sm);font-size:var(--fs-13);font-weight:600;transition:.15s;border:1px solid transparent}
  161. .btn-primary{background:var(--primary);color:var(--inverse)}
  162. .btn-primary:hover{background:var(--primary-hover)}
  163. .btn-ghost{border-color:var(--edge);color:var(--ink-2);background:var(--surface)}
  164. .btn-ghost:hover{border-color:var(--edge-strong);color:var(--ink)}
  165. .btn-danger{color:var(--danger);border-color:var(--danger-subtle);background:var(--danger-subtle)}
  166. .btn-block{width:100%;height:44px;font-size:var(--fs-15)}
  167. .icon-btn{width:32px;height:32px;border-radius:var(--r-sm);display:grid;place-items:center;color:var(--muted);flex-shrink:0}
  168. .icon-btn:hover{background:var(--surface-hover);color:var(--ink)}
  169. .icon-btn:focus-visible,.btn:focus-visible,.nav-i:focus-visible,.tool-i:focus-visible{outline:2px solid var(--primary);outline-offset:2px}
  170. .mini-btn{width:28px;height:28px;border-radius:8px;display:grid;place-items:center;color:var(--muted)}
  171. .mini-btn:hover{background:var(--surface-hover);color:var(--ink)}
  172. /* 分段器 */
  173. .seg{display:inline-flex;background:var(--surface-2);border-radius:var(--r-sm);padding:3px;gap:2px}
  174. .seg-i{padding:6px var(--sp-4);border-radius:8px;font-size:var(--fs-13);color:var(--muted)}
  175. .seg-i.on{background:var(--surface);color:var(--ink);font-weight:600;box-shadow:var(--shadow-soft)}
  176. /* 标签页 */
  177. .tabs{display:flex;gap:var(--sp-5);border-bottom:1px solid var(--edge)}
  178. .tab{padding:var(--sp-3) 0;font-size:var(--fs-15);color:var(--muted);border-bottom:2px solid transparent;margin-bottom:-1px}
  179. .tab.on{color:var(--primary);border-bottom-color:var(--primary);font-weight:650}
  180. /* 筛选胶囊 */
  181. .chips{display:flex;gap:var(--sp-2);flex-wrap:wrap}
  182. .chip{position:relative;display:inline-flex;align-items:center;gap:var(--sp-1);height:var(--sp-8);padding:0 var(--sp-3);border-radius:var(--r-tag);font-size:var(--fs-12);font-weight:600;background:var(--surface-2);color:var(--muted)}
  183. .chip::after{content:"";position:absolute;inset:calc((44px - 100%) / -2) 0}
  184. .chip.on{background:var(--primary-subtle);color:var(--primary)}
  185. .chip.on.chip-w{background:var(--warning-subtle);color:var(--warning)}
  186. .chip.on.chip-d{background:var(--danger-subtle);color:var(--danger)}
  187. /* 进度条(仅用于分母已知场景) */
  188. .bar{height:6px;background:var(--surface-2);border-radius:var(--r-tag);overflow:hidden;margin-top:var(--sp-1)}
  189. .bar>i{display:block;height:100%;background:var(--primary);border-radius:var(--r-tag)}
  190. .bar.success>i{background:var(--success)} .bar.warning>i{background:var(--warning)}
  191. /* ---------- 桌面:导航轨 ---------- */
  192. .rail{width:var(--dk-nav-w);background:var(--surface);border-right:1px solid var(--edge);display:flex;flex-direction:column;align-items:center;padding:var(--sp-3) 0;gap:var(--sp-1);flex-shrink:0}
  193. .rail-logo{width:34px;height:34px;border-radius:var(--r-sm);background:var(--primary);color:var(--inverse);display:grid;place-items:center;font-weight:800;font-size:var(--fs-15);margin-bottom:var(--sp-3)}
  194. .rail-i{width:40px;height:40px;border-radius:var(--r-sm);display:grid;place-items:center;color:var(--muted);position:relative}
  195. .rail-i:hover{background:var(--surface-hover);color:var(--ink-2)}
  196. .rail-i.on{background:var(--primary-subtle);color:var(--primary)}
  197. .rail-i.has-dot::after{content:"";position:absolute;top:8px;right:8px;width:7px;height:7px;border-radius:50%;background:var(--danger);border:1.5px solid var(--surface)}
  198. .rail-sp{flex:1}
  199. /* ---------- 桌面:三栏 ---------- */
  200. .pane{display:flex;flex-direction:column;min-width:0}
  201. .pane-list{width:var(--dk-list-w);background:var(--surface);border-right:1px solid var(--edge);flex-shrink:0}
  202. .pane-main{flex:1;background:var(--canvas)}
  203. .pane-aside{width:var(--dk-aside-w);background:var(--surface);border-left:1px solid var(--edge);flex-shrink:0}
  204. .pane-head{padding:var(--sp-3) var(--sp-4);min-height:var(--hd-h);border-bottom:1px solid var(--edge);display:flex;align-items:center;gap:var(--sp-3);flex-shrink:0}
  205. .pane-body{flex:1;overflow-y:auto}
  206. .pane-body::-webkit-scrollbar,.msgs::-webkit-scrollbar{width:8px}
  207. .pane-body::-webkit-scrollbar-thumb,.msgs::-webkit-scrollbar-thumb{background:var(--edge-strong);border-radius:4px}
  208. .search{flex:1;height:var(--sp-8);background:var(--surface-2);border-radius:var(--r-sm);display:flex;align-items:center;gap:var(--sp-2);padding:0 var(--sp-3);color:var(--muted)}
  209. .search input{flex:1;border:0;background:none;outline:none;font-size:16px;min-width:0}
  210. .search input::placeholder{color:var(--faint)}
  211. .group-label{padding:var(--sp-3) var(--sp-4) var(--sp-1);font-size:var(--fs-11);font-weight:700;color:var(--faint);letter-spacing:.05em}
  212. .row{width:100%;display:flex;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);align-items:center;text-align:left;border-left:3px solid transparent;transition:.12s}
  213. .row:hover{background:var(--surface-hover)}
  214. .row.on{background:var(--primary-subtle);border-left-color:var(--primary)}
  215. .row.row-warn{border-left-color:var(--warning)}
  216. .row.row-err{border-left-color:var(--danger)}
  217. .row-meta{flex:1;min-width:0}
  218. .row-name{font-size:var(--fs-15);font-weight:600;display:flex;align-items:center;gap:var(--sp-2);min-width:0}
  219. .row-name span.nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  220. .row-prev{font-size:var(--fs-13);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:var(--sp-1)}
  221. .row-prev.is-danger{color:var(--danger)}
  222. .row-prev.is-warning{color:var(--warning)}
  223. .row-time{font-size:var(--fs-12);color:var(--faint);flex-shrink:0;align-self:flex-start;margin-top:var(--sp-1)}
  224. /* ---------- 桌面:会话主区 ---------- */
  225. .chat-head{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);min-height:var(--hd-h);background:var(--surface);border-bottom:1px solid var(--edge);flex-shrink:0}
  226. .chat-title{flex:1;min-width:0}
  227. .t-name{font-size:var(--fs-15);font-weight:650;line-height:1.35;display:flex;align-items:center;gap:var(--sp-2)}
  228. .t-sub{font-size:var(--fs-12);color:var(--muted);line-height:1.35;display:flex;align-items:center;gap:var(--sp-1)}
  229. .head-actions{display:flex;gap:2px}
  230. /* L2 状态条 —— 全页面唯一渲染「在干嘛」的地方 */
  231. .statusbar{margin:var(--sp-4) var(--sp-4) 0;background:var(--surface);border:1px solid var(--edge);border-radius:var(--r-md);padding:var(--sp-3) var(--sp-4);display:flex;align-items:center;gap:var(--sp-3);box-shadow:var(--shadow-soft);flex-shrink:0}
  232. .statusbar.is-static{cursor:default}
  233. .pulse{width:8px;height:8px;border-radius:50%;background:var(--info);flex-shrink:0;animation:pulse 1.8s ease-in-out infinite}
  234. @keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
  235. @media (prefers-reduced-motion:reduce){.pulse{animation:none}}
  236. .sb-text{flex:1;min-width:0}
  237. .sb-text b{font-size:var(--fs-13);font-weight:650;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  238. .sb-text span{font-size:var(--fs-12);color:var(--muted)}
  239. .sb-chev{color:var(--faint)}
  240. .steps{margin:var(--sp-2) var(--sp-4) 0;padding-left:var(--sp-4);border-left:2px solid var(--edge);display:flex;flex-direction:column;gap:var(--sp-2);flex-shrink:0}
  241. .step{display:flex;gap:var(--sp-2);align-items:flex-start;font-size:var(--fs-13);color:var(--muted)}
  242. .step.done{color:var(--ink-2)} .step.cur{color:var(--info);font-weight:650}
  243. .step-m{width:14px;flex-shrink:0;text-align:center;font-size:var(--fs-12)}
  244. .step-time{color:var(--faint);font-size:var(--fs-12);margin-left:var(--sp-1)}
  245. .msgs{flex:1;overflow-y:auto;padding:var(--sp-5) var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-4)}
  246. .sys-msg{align-self:center;font-size:var(--fs-12);color:var(--muted);background:var(--surface-2);padding:2px var(--sp-3);border-radius:var(--r-tag)}
  247. .msg{display:flex;gap:var(--sp-3);max-width:78%}
  248. .msg.is-me{align-self:flex-end;flex-direction:row-reverse}
  249. .bubble{background:var(--surface);border:1px solid var(--edge);padding:var(--sp-3) var(--sp-4);border-radius:var(--r-md) var(--r-lg) var(--r-lg) var(--r-lg);font-size:var(--fs-15);line-height:1.65;box-shadow:var(--shadow-soft);word-break:break-word}
  250. .msg.is-me .bubble{background:var(--primary);color:var(--inverse);border-color:var(--primary);border-radius:var(--r-lg) var(--r-md) var(--r-lg) var(--r-lg)}
  251. /* 工具调用走系统消息式弱化样式,默认折叠 */
  252. .tool-call{align-self:flex-start;max-width:78%;display:flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-12);color:var(--muted);background:var(--surface-2);border:1px dashed var(--edge-strong);border-radius:var(--r-sm);padding:var(--sp-2) var(--sp-3)}
  253. .tool-call .ic{color:var(--faint)}
  254. .composer{display:flex;align-items:flex-end;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);background:var(--surface);border-top:1px solid var(--edge);flex-shrink:0}
  255. .composer .icon-btn{width:36px;height:36px}
  256. .composer-input{flex:1;min-height:38px;max-height:132px;background:var(--surface-2);border-radius:var(--r-lg);padding:var(--sp-2) var(--sp-4);font-size:16px;line-height:1.5;border:0;outline:none;resize:none;overflow-y:auto}
  257. .composer-input::placeholder{color:var(--faint)}
  258. .send-btn{width:36px;height:36px;border-radius:var(--r-sm);background:var(--primary);color:var(--inverse);display:grid;place-items:center;flex-shrink:0}
  259. .send-btn:hover{background:var(--primary-hover)}
  260. /* 右栏 */
  261. .aside-h{padding:var(--sp-3) var(--sp-4);min-height:var(--hd-h);border-bottom:1px solid var(--edge);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
  262. .aside-h b{font-size:var(--fs-13);font-weight:650}
  263. .aside-b{flex:1;overflow-y:auto;padding:var(--sp-4)}
  264. .sect-t{font-size:var(--fs-11);font-weight:700;color:var(--faint);letter-spacing:.05em;text-transform:uppercase;margin:0 0 var(--sp-3)}
  265. .sect-t+.sect-t{margin-top:var(--sp-5)}
  266. .metric{display:flex;justify-content:space-between;align-items:center;font-size:var(--fs-13);padding:var(--sp-1) 0}
  267. .metric+.metric{margin-top:var(--sp-3)}
  268. .metric-k{color:var(--muted);display:flex;align-items:center;gap:var(--sp-1)}
  269. .metric-v{font-weight:650;font-variant-numeric:tabular-nums}
  270. .divider{height:1px;background:var(--edge);margin:var(--sp-4) 0}
  271. .file-row{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) 0}
  272. .file-row .nm{font-size:var(--fs-13);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  273. .file-row .tm{font-size:var(--fs-12);color:var(--faint)}
  274. /* ---------- 桌面:工作台 ---------- */
  275. .page{flex:1;min-width:0;padding:var(--sp-4) var(--sp-5);overflow-y:auto}
  276. .page-h{display:flex;align-items:flex-start;gap:var(--sp-4);margin-bottom:var(--sp-5)}
  277. .page-t{flex:1}
  278. .page-t h2{font-size:var(--fs-20);font-weight:700;letter-spacing:-.01em}
  279. .page-t p{font-size:var(--fs-13);color:var(--muted);margin-top:var(--sp-1)}
  280. .stat-row{display:flex;gap:var(--sp-4);margin-bottom:var(--sp-5)}
  281. .stat{flex:1;background:var(--surface);border:1px solid var(--edge);border-radius:var(--r-md);padding:var(--sp-4);box-shadow:var(--shadow-card)}
  282. .stat-k{font-size:var(--fs-12);color:var(--muted);display:flex;align-items:center;gap:var(--sp-1)}
  283. .stat-v{font-size:var(--fs-26);font-weight:750;letter-spacing:-.02em;margin-top:var(--sp-2);font-variant-numeric:tabular-nums}
  284. .stat-v small{font-size:var(--fs-13);font-weight:600;color:var(--muted);margin-left:3px}
  285. .card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(310px,1fr));gap:var(--sp-4)}
  286. .wcard{background:var(--surface);border:1px solid var(--edge);border-radius:var(--r-md);padding:var(--sp-4);box-shadow:var(--shadow-card);transition:.16s;display:flex;flex-direction:column;gap:var(--sp-3)}
  287. .wcard:hover{box-shadow:var(--shadow-pop);transform:translateY(-2px)}
  288. .wcard.edge-warn{border-left:3px solid var(--warning)}
  289. .wcard.edge-err{border-left:3px solid var(--danger)}
  290. .wcard-h{display:flex;align-items:center;gap:var(--sp-3)}
  291. .wcard-h .nm{flex:1;min-width:0}
  292. .wcard-h .nm b{font-size:var(--fs-15);font-weight:650;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  293. .wcard-h .nm span{font-size:var(--fs-12);color:var(--muted)}
  294. .wcard-task{background:var(--surface-2);border-radius:var(--r-sm);padding:var(--sp-3);font-size:var(--fs-13);color:var(--ink-2);line-height:1.6;display:flex;gap:var(--sp-2)}
  295. .wcard-task .ic{color:var(--info);margin-top:var(--sp-1)}
  296. .wcard-metrics{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3) var(--sp-4)}
  297. .wcard-f{display:flex;justify-content:space-between;align-items:baseline;font-size:var(--fs-12);color:var(--muted)}
  298. .wcard-f b{font-size:var(--fs-13);color:var(--ink);font-weight:650;font-variant-numeric:tabular-nums}
  299. .wcard-foot{display:flex;align-items:center;gap:var(--sp-3);padding-top:var(--sp-3);border-top:1px solid var(--edge);font-size:var(--fs-12);color:var(--muted)}
  300. .wcard-foot .link{color:var(--primary);font-weight:600;display:inline-flex;align-items:center;gap:3px}
  301. /* ---------- 桌面:任务看板 ---------- */
  302. .task{width:100%;display:flex;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);border-left:3px solid transparent;text-align:left;border-bottom:1px solid var(--edge);transition:.12s}
  303. .task:hover{background:var(--surface-hover)}
  304. .task.on{background:var(--surface-active)}
  305. .task.blocked{border-left-color:var(--warning)} .task.failed{border-left-color:var(--danger)}
  306. .task.done{border-left-color:var(--success)} .task.cur{border-left-color:var(--info)}
  307. .task-ico{font-size:var(--fs-15);flex-shrink:0;width:18px;text-align:center;line-height:1.4}
  308. .task-ico.w{color:var(--warning)} .task-ico.d{color:var(--danger)}
  309. .task-ico.s{color:var(--success)} .task-ico.i{color:var(--info)}
  310. .task-b{flex:1;min-width:0}
  311. .task-t{font-size:var(--fs-15);font-weight:600;line-height:1.45}
  312. .task-m{font-size:var(--fs-12);color:var(--muted);margin-top:var(--sp-1);display:flex;gap:var(--sp-3);flex-wrap:wrap;align-items:center}
  313. .task-alert{font-size:var(--fs-12);font-weight:650;margin-top:var(--sp-1);display:flex;align-items:center;gap:var(--sp-1)}
  314. .task-alert.w{color:var(--warning)} .task-alert.d{color:var(--danger)}
  315. .detail{padding:var(--sp-6);height:100%;overflow-y:auto}
  316. .detail h3{font-size:var(--fs-17);font-weight:700;line-height:1.45;letter-spacing:-.01em}
  317. .detail-id{font-size:var(--fs-12);color:var(--faint);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;display:inline-flex;align-items:center;gap:var(--sp-1)}
  318. .kv{display:grid;grid-template-columns:96px 1fr;gap:var(--sp-2) var(--sp-4);font-size:var(--fs-13)}
  319. .kv dt{color:var(--muted)}
  320. .kv dd{color:var(--ink-2);font-weight:500}
  321. .run{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) 0;font-size:var(--fs-13);border-bottom:1px solid var(--edge)}
  322. .run:last-child{border-bottom:0}
  323. .run-id{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--ink-2);font-weight:600}
  324. .run-t{color:var(--faint);font-size:var(--fs-12);margin-left:auto}
  325. blockquote{border-left:3px solid var(--edge);padding-left:var(--sp-4);color:var(--ink-2);font-size:var(--fs-13);line-height:1.75}
  326. /* ---------- 桌面:成果 ---------- */
  327. .seg-head{display:flex;align-items:center;gap:var(--sp-4);padding:var(--sp-3) var(--sp-5);min-height:var(--hd-h);border-bottom:1px solid var(--edge);background:var(--surface);flex-shrink:0}
  328. .item{border-bottom:1px solid var(--edge);padding:var(--sp-4) var(--sp-5)}
  329. .item-h{display:flex;align-items:center;gap:var(--sp-3);width:100%;text-align:left}
  330. .item-h .nm{flex:1;min-width:0}
  331. .item-h .nm b{font-size:var(--fs-15);font-weight:650;display:block}
  332. .item-h .nm span{font-size:var(--fs-12);color:var(--muted)}
  333. .item-files{display:flex;gap:var(--sp-3);flex-wrap:wrap;margin-top:var(--sp-3)}
  334. .thumb{width:96px;height:72px;border-radius:var(--r-sm);background:var(--surface-2);border:1px solid var(--edge);display:grid;place-items:center;color:var(--faint);position:relative;overflow:hidden}
  335. .thumb .cap{position:absolute;left:0;right:0;bottom:0;background:linear-gradient(transparent,rgb(0 0 0/.55));color:#fff;font-size:var(--fs-11);padding:10px 6px 3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  336. .tl{position:relative;padding-left:var(--sp-6)}
  337. .tl::before{content:"";position:absolute;left:7px;top:6px;bottom:6px;width:2px;background:var(--edge)}
  338. .tl-i{position:relative;padding-bottom:var(--sp-5)}
  339. .tl-i::before{content:"";position:absolute;left:calc(var(--sp-6) * -1 + 3px);top:5px;width:10px;height:10px;border-radius:50%;background:var(--surface);border:2px solid var(--primary)}
  340. .tl-d{font-size:var(--fs-12);color:var(--faint);margin-bottom:3px}
  341. /* ---------- 桌面:空态 / 骨架 ---------- */
  342. .empty{padding:var(--sp-8) var(--sp-4);text-align:center;color:var(--muted)}
  343. .empty .ic{width:40px;height:40px;color:var(--faint);margin-bottom:var(--sp-3)}
  344. .empty b{display:block;font-size:var(--fs-15);color:var(--ink-2);margin-bottom:var(--sp-1)}
  345. .empty p{font-size:var(--fs-13);max-width:320px;margin:0 auto}
  346. .skel{background:linear-gradient(90deg,var(--surface-2) 25%,var(--surface-hover) 37%,var(--surface-2) 63%);background-size:400% 100%;animation:shimmer 1.6s linear infinite;border-radius:var(--r-sm);height:12px}
  347. @keyframes shimmer{from{background-position:100% 0}to{background-position:0 0}}
  348. @media (prefers-reduced-motion:reduce){.skel{animation:none}}
  349. /* ==========================================================================
  350. 移动端
  351. ========================================================================== */
  352. .device-mb{width:390px}
  353. .mb{width:390px;height:844px;background:var(--canvas);display:flex;flex-direction:column;position:relative}
  354. .mb-status{height:38px;display:flex;align-items:center;justify-content:space-between;padding:0 var(--mb-gutter);font-size:var(--fs-12);color:var(--ink-2);background:var(--surface);font-weight:600}
  355. .mb-nav{min-height:var(--mb-navbar-h);background:var(--surface);border-bottom:1px solid var(--edge);display:flex;align-items:center;padding:var(--mb-safe-top) var(--sp-3) 0;gap:var(--sp-2);flex-shrink:0}
  356. .mb-nav-t{flex:1;font-size:var(--fs-17);font-weight:700;letter-spacing:-.01em;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  357. .mb-icon-btn{width:var(--touch-min);height:var(--touch-min);display:grid;place-items:center;color:var(--ink-2);flex-shrink:0;border-radius:var(--r-sm)}
  358. .mb-icon-btn:active{background:var(--surface-hover)}
  359. .mb-body{flex:1;overflow-y:auto;overscroll-behavior:contain}
  360. .mb-body::-webkit-scrollbar{width:0}
  361. /* 会话为全屏详情(FR-04):该视图激活时收起 TabBar
  362. 注意 tabbar 是 .view 的兄弟节点,故用 :has() 选择器 */
  363. .mb:has(>.view[data-view="chat"]:not([hidden]))>.mb-tabbar{display:none}
  364. .mb-tabbar{min-height:var(--mb-tabbar-h);background:var(--surface);border-top:1px solid var(--edge);display:flex;flex-shrink:0;padding-bottom:var(--mb-safe-bottom)}
  365. .mb-tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;font-size:var(--fs-11);color:var(--faint);min-height:var(--touch-min);position:relative}
  366. .mb-tab.on{color:var(--primary);font-weight:650}
  367. .mb-tab .ic{width:21px;height:21px}
  368. .mb-tab.has-dot::after{content:"";position:absolute;top:7px;right:calc(50% - 16px);width:7px;height:7px;border-radius:50%;background:var(--danger)}
  369. .mb-cell{display:flex;gap:var(--sp-3);padding:var(--sp-3) var(--mb-gutter);min-height:var(--mb-list-h-2);background:var(--surface);border-bottom:1px solid var(--edge);align-items:center;width:100%;text-align:left}
  370. .mb-cell:active{background:var(--surface-hover)}
  371. .mb-cell.warn{border-left:3px solid var(--warning)}
  372. .mb-cell.err{border-left:3px solid var(--danger)}
  373. .mb-cb{flex:1;min-width:0}
  374. .mb-ct{font-size:var(--fs-15);font-weight:600;display:flex;align-items:center;gap:var(--sp-2)}
  375. .mb-ct .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  376. .mb-cd{font-size:var(--fs-13);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:var(--sp-1)}
  377. .mb-cr{font-size:var(--fs-12);color:var(--faint);align-self:flex-start;margin-top:var(--sp-1);flex-shrink:0}
  378. .mb-sect{display:flex;align-items:baseline;justify-content:space-between;padding:var(--sp-4) var(--mb-gutter) var(--sp-2)}
  379. .mb-sect b{font-size:var(--fs-15);font-weight:700}
  380. .mb-sect a{font-size:var(--fs-13);color:var(--primary);font-weight:600}
  381. .mb-gutter{padding:var(--mb-gutter)}
  382. .mb-banner{margin:var(--mb-gutter) var(--mb-gutter) 0;background:var(--warning-subtle);border:1px solid color-mix(in srgb,var(--warning) 35%,transparent);border-radius:var(--r-md);padding:var(--sp-3);display:flex;gap:var(--sp-3);align-items:center;width:auto;text-align:left}
  383. .mb-banner .ic{color:var(--warning)}
  384. .mb-banner b{font-size:var(--fs-13);color:var(--warning);display:block}
  385. .mb-banner span{font-size:var(--fs-12);color:var(--ink-2)}
  386. .mb-input{background:var(--surface);border-top:1px solid var(--edge);padding:var(--sp-3) var(--mb-gutter) calc(var(--sp-3) + var(--mb-safe-bottom));display:flex;gap:var(--sp-2);align-items:flex-end;flex-shrink:0}
  387. .mb-input-x{flex:1;background:var(--surface-2);border-radius:var(--r-lg);padding:var(--sp-3) var(--sp-4);font-size:16px;line-height:1.5;border:0;outline:none;resize:none;max-height:110px;min-height:44px;overflow-y:auto}
  388. .mb-input-x::placeholder{color:var(--faint)}
  389. .mb-send{width:44px;height:44px;border-radius:var(--r-sm);background:var(--primary);color:var(--inverse);display:grid;place-items:center;flex-shrink:0}
  390. /* 胶囊视觉 28px,触达区由 .chip::after 撑到 44px;行高须 ≥ 触达区,避免溢出压到列表 */
  391. .mb-tabs{display:flex;align-items:center;gap:var(--sp-2);min-height:calc(var(--touch-min) + var(--sp-2) * 2);padding:var(--sp-2) var(--mb-gutter);overflow-x:auto;background:var(--surface);border-bottom:1px solid var(--edge);flex-shrink:0}
  392. .mb-tabs::-webkit-scrollbar{height:0}
  393. .mb-list-h{font-size:var(--fs-12);font-weight:700;color:var(--faint);padding:var(--sp-3) var(--mb-gutter) var(--sp-2);letter-spacing:.04em}
  394. .mb-group{background:var(--surface);border-radius:var(--r-md);margin:0 var(--mb-gutter) var(--mb-gutter);overflow:hidden;border:1px solid var(--edge)}
  395. .mb-line{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--mb-gutter);min-height:var(--mb-list-h-1);font-size:var(--fs-15);width:100%;text-align:left;border-bottom:1px solid var(--edge)}
  396. .mb-line:last-child{border-bottom:0}
  397. .mb-line:active{background:var(--surface-hover)}
  398. .mb-line .k{flex:1;color:var(--ink)}
  399. .mb-line .v{color:var(--muted);font-size:var(--fs-13);display:flex;align-items:center;gap:var(--sp-2)}
  400. .mb-line.danger .k{color:var(--danger)}
  401. .mb-card{background:var(--surface);border:1px solid var(--edge);border-radius:var(--r-md);padding:var(--sp-4);box-shadow:var(--shadow-card)}
  402. .mb-drawer{position:absolute;left:0;right:0;bottom:0;background:var(--surface);border-top:1px solid var(--edge);border-radius:var(--r-sheet) var(--r-sheet) 0 0;box-shadow:var(--shadow-overlay);transform:translateY(101%);transition:transform .26s cubic-bezier(.16,1,.3,1);max-height:74%;display:flex;flex-direction:column;z-index:var(--z-modal)}
  403. .mb-drawer.open{transform:none}
  404. @media (prefers-reduced-motion:reduce){.mb-drawer{transition:none}}
  405. .mb-drawer-h{padding:var(--sp-4) var(--sp-4) var(--sp-2);display:flex;align-items:center;gap:var(--sp-3);border-bottom:1px solid var(--edge)}
  406. .mb-drawer-h b{flex:1;font-size:var(--fs-17);font-weight:700}
  407. .mb-drawer-b{overflow-y:auto;padding:var(--sp-2) 0 var(--sp-4)}
  408. .mb-scrim{position:absolute;inset:0;background:var(--scrim);opacity:0;pointer-events:none;transition:opacity .26s;z-index:var(--z-mask)}
  409. .mb-scrim.on{opacity:1;pointer-events:auto}
  410. .mb-role{display:inline-flex;align-items:center;gap:var(--sp-1)}
  411. /* 移动端头像尺寸(§3.1:列表 44 / 详情 64;桌面 40 不在此覆盖) */
  412. .mb .av{width:var(--mb-avatar);height:var(--mb-avatar);font-size:var(--fs-15);border-radius:var(--r-md)}
  413. .mb .av-sm{width:36px;height:36px;font-size:var(--fs-12)}
  414. .mb .av-lg{width:var(--mb-avatar-lg);height:var(--mb-avatar-lg);font-size:var(--fs-26)}
  415. /* 抽屉底部含安全区 */
  416. .mb-drawer{padding-bottom:var(--mb-safe-bottom)}
  417. /* ---------- 登录 ---------- */
  418. .login-stage{display:flex;flex-direction:column;gap:var(--sp-8);align-items:center}
  419. .login-dk{width:100%;min-height:720px;background:var(--canvas);position:relative;display:flex;flex-wrap:wrap;overflow:hidden}
  420. .login-dk::before{content:"";position:absolute;inset:0;background:
  421. radial-gradient(680px 420px at 78% 12%, color-mix(in srgb,var(--primary) 16%,transparent), transparent 70%),
  422. radial-gradient(520px 420px at 12% 88%, color-mix(in srgb,var(--info) 12%,transparent), transparent 70%);}
  423. .login-brand{position:relative;flex:1 1 480px;min-width:0;padding:var(--sp-8);display:flex;flex-direction:column;justify-content:center;gap:var(--sp-4)}
  424. .login-brand .lg{display:flex;align-items:center;gap:var(--sp-3)}
  425. .login-brand .lg .brand-mark{width:44px;height:44px;font-size:var(--fs-20);border-radius:var(--r-md)}
  426. .login-brand h1{font-size:var(--fs-26);font-weight:750;letter-spacing:-.02em;line-height:1.3}
  427. .login-brand p{font-size:var(--fs-15);color:var(--ink-2);line-height:1.8;max-width:420px}
  428. .login-points{display:flex;flex-direction:column;gap:var(--sp-3);margin-top:var(--sp-4)}
  429. .login-point{display:flex;gap:var(--sp-3);align-items:center;font-size:var(--fs-13);color:var(--ink-2)}
  430. .login-point .ic{color:var(--primary)}
  431. .login-panel{position:relative;flex:1 1 360px;max-width:440px;background:var(--surface);border-left:1px solid var(--edge);display:flex;flex-direction:column;justify-content:center;padding:var(--sp-8);box-shadow:var(--shadow-overlay)}
  432. .login-panel h2{font-size:var(--fs-20);font-weight:700;letter-spacing:-.01em}
  433. .login-panel .sub{font-size:var(--fs-13);color:var(--muted);margin-top:var(--sp-1);margin-bottom:var(--sp-5)}
  434. .field{margin-bottom:var(--sp-4)}
  435. .field label{display:block;font-size:var(--fs-13);font-weight:600;color:var(--ink-2);margin-bottom:var(--sp-2)}
  436. .field .box{display:flex;align-items:center;gap:var(--sp-2);height:44px;border:1px solid var(--edge);border-radius:var(--r-sm);padding:0 var(--sp-3);background:var(--surface);transition:.15s}
  437. .field .box:focus-within{border-color:var(--primary);box-shadow:0 0 0 3px var(--primary-ring)}
  438. .field .box .ic{color:var(--faint)}
  439. .field input{flex:1;border:0;background:none;outline:none;font-size:16px;min-width:0}
  440. .field .code-btn{color:var(--primary);font-size:var(--fs-13);font-weight:650;white-space:nowrap;padding:0 var(--sp-1)}
  441. .field .code-btn:disabled{color:var(--faint);cursor:default}
  442. .field .err{font-size:var(--fs-12);color:var(--danger);margin-top:var(--sp-1);display:flex;align-items:center;gap:var(--sp-1)}
  443. .login-alt{margin-top:var(--sp-5);padding-top:var(--sp-4);border-top:1px solid var(--edge);font-size:var(--fs-12);color:var(--muted);line-height:1.9}
  444. .login-alt code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;background:var(--surface-2);padding:1px 5px;border-radius:4px;color:var(--ink-2)}
  445. .mb-login{width:390px;height:844px;background:var(--surface);display:flex;flex-direction:column;padding:var(--sp-8) var(--mb-gutter) var(--sp-6)}
  446. .mb-login .lg{display:flex;align-items:center;gap:var(--sp-3);margin-bottom:var(--sp-8)}
  447. .mb-login h1{font-size:var(--fs-26);font-weight:750;letter-spacing:-.02em;line-height:1.3}
  448. .mb-login p.sub{font-size:var(--fs-13);color:var(--muted);margin-top:var(--sp-2);margin-bottom:var(--sp-6);line-height:1.7}
  449. .mb-login .sp{flex:1}
  450. .colorswatch{display:flex;gap:var(--sp-2);flex-wrap:wrap;margin-top:var(--sp-3)}
  451. .sw{width:44px;height:44px;border-radius:var(--r-sm);border:1px solid var(--edge);display:grid;place-items:end center;padding-bottom:var(--sp-1);font-size:var(--fs-11);color:var(--inverse);font-weight:700}
  452. /* ==========================================================================
  453. 兜底 + 演示外壳响应式
  454. ========================================================================== */
  455. /* hidden 必须压过上面的 display:flex —— UA 对 [hidden] 只有 display:none,
  456. 特异性(0,1,0)低于 .dk>.view(0,2,0),不兜底会导致"隐藏的视图仍占位" */
  457. [hidden]{display:none !important}
  458. /* 演示外壳窄窗:左侧清单收为底部横向条。
  459. 注意:设备画布内保持 1440×900 固定设备态(评审用),
  460. 真正的桌面降级阶梯(1280/1150/940/768)属真机实现,见《双端布局规范》§四 */
  461. @media (max-width:1180px){ .app{grid-template-columns:208px 1fr} }
  462. @media (max-width:920px){
  463. .app{display:flex;flex-direction:column;height:auto;min-height:100vh}
  464. .topbar{order:1}
  465. .stage{order:2;padding:var(--sp-4)}
  466. .sidenav{order:3;display:flex;gap:var(--sp-2);overflow-x:auto;padding:var(--sp-2) var(--sp-3);border-top:1px solid var(--edge);border-right:0}
  467. .sidenav::-webkit-scrollbar{height:0}
  468. .nav-group,.nav-idx{display:none}
  469. .nav-i{width:auto;white-space:nowrap;flex-shrink:0}
  470. }
  471. </style>
  472. </head>
  473. <body>
  474. <!-- ============ 图标精灵 ============ -->
  475. <svg width="0" height="0" style="position:absolute" aria-hidden="true">
  476. <symbol id="i-chat" viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></symbol>
  477. <symbol id="i-users" viewBox="0 0 24 24"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
  478. <symbol id="i-folder" viewBox="0 0 24 24"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></symbol>
  479. <symbol id="i-bell" viewBox="0 0 24 24"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></symbol>
  480. <symbol id="i-grid" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/></symbol>
  481. <symbol id="i-task" viewBox="0 0 24 24"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></symbol>
  482. <symbol id="i-box" viewBox="0 0 24 24"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><path d="M3.27 6.96 12 12.01l8.73-5.05M12 22.08V12"/></symbol>
  483. <symbol id="i-user" viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol>
  484. <symbol id="i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></symbol>
  485. <symbol id="i-send" viewBox="0 0 24 24"><path d="M22 2 11 13"/><path d="M22 2l-7 20-4-9-9-4z"/></symbol>
  486. <symbol id="i-plus" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></symbol>
  487. <symbol id="i-more" viewBox="0 0 24 24"><circle cx="5" cy="12" r="1.6" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1.6" fill="currentColor" stroke="none"/><circle cx="19" cy="12" r="1.6" fill="currentColor" stroke="none"/></symbol>
  488. <symbol id="i-chev-d" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></symbol>
  489. <symbol id="i-chev-r" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></symbol>
  490. <symbol id="i-chev-l" viewBox="0 0 24 24"><path d="M15 18l-6-6 6-6"/></symbol>
  491. <symbol id="i-refresh" viewBox="0 0 24 24"><path d="M23 4v6h-6M1 20v-6h6"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></symbol>
  492. <symbol id="i-download" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5 5 5-5M12 15V3"/></symbol>
  493. <symbol id="i-alert" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><path d="M12 9v4M12 17h.01"/></symbol>
  494. <symbol id="i-ban" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M4.93 4.93l14.14 14.14"/></symbol>
  495. <symbol id="i-check" viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></symbol>
  496. <symbol id="i-circle" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/></symbol>
  497. <symbol id="i-play" viewBox="0 0 24 24"><path d="M6 3l14 9-14 9z"/></symbol>
  498. <symbol id="i-external" viewBox="0 0 24 24"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><path d="M15 3h6v6M10 14 21 3"/></symbol>
  499. <symbol id="i-cpu" viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3"/></symbol>
  500. <symbol id="i-db" viewBox="0 0 24 24"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></symbol>
  501. <symbol id="i-wallet" viewBox="0 0 24 24"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4z"/></symbol>
  502. <symbol id="i-zap" viewBox="0 0 24 24"><path d="M13 2 3 14h9l-1 8 10-12h-9z"/></symbol>
  503. <symbol id="i-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/></symbol>
  504. <symbol id="i-panel" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M15 3v18"/></symbol>
  505. <symbol id="i-moon" viewBox="0 0 24 24"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol>
  506. <symbol id="i-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4.5"/><path d="M12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4"/></symbol>
  507. <symbol id="i-eye" viewBox="0 0 24 24"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></symbol>
  508. <symbol id="i-logout" viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><path d="M16 17l5-5-5-5M21 12H9"/></symbol>
  509. <symbol id="i-smartphone" viewBox="0 0 24 24"><rect x="5" y="2" width="14" height="20" rx="2.5"/><path d="M12 18h.01"/></symbol>
  510. <symbol id="i-monitor" viewBox="0 0 24 24"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></symbol>
  511. <symbol id="i-file" viewBox="0 0 24 24"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M13 2v7h7"/></symbol>
  512. <symbol id="i-image" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></symbol>
  513. <symbol id="i-shield" viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol>
  514. <symbol id="i-filter" viewBox="0 0 24 24"><path d="M22 3H2l8 9.46V19l4 2v-8.54z"/></symbol>
  515. <symbol id="i-phone" viewBox="0 0 24 24"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.8 19.8 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.8 19.8 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.9.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z"/></symbol>
  516. <symbol id="i-key" viewBox="0 0 24 24"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3"/></symbol>
  517. <symbol id="i-download-box" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5 5 5-5M12 15V3"/></symbol>
  518. <symbol id="i-timeline" viewBox="0 0 24 24"><path d="M3 6h13M3 12h10M3 18h7"/><circle cx="19" cy="6" r="2"/><circle cx="18" cy="12" r="2"/></symbol>
  519. </svg>
  520. <!-- ============ 顶部工具条(演示外壳,非产品 UI) ============ -->
  521. <div class="app">
  522. <header class="topbar">
  523. <div class="brand">
  524. <span class="brand-mark">F</span>
  525. <span class="brand-t">Fmode v2</span>
  526. <span class="brand-s">MVP Demo · 双端 11 屏</span>
  527. </div>
  528. <div class="top-sp"></div>
  529. <div class="tool" role="tablist" aria-label="端切换">
  530. <button class="tool-i on" data-end="login" role="tab">登录</button>
  531. <button class="tool-i" data-end="dk" role="tab">桌面端</button>
  532. <button class="tool-i" data-end="mb" role="tab">移动端</button>
  533. </div>
  534. <div class="top-sp"></div>
  535. <button class="tbtn" id="btnNotes" title="显示设计依据标注"><svg class="ic"><use href="#i-eye"/></svg></button>
  536. <button class="tbtn" id="btnTheme" title="切换明暗"><svg class="ic"><use href="#i-moon"/></svg></button>
  537. </header>
  538. <!-- ============ 左侧屏幕清单 ============ -->
  539. <nav class="sidenav" aria-label="屏幕清单">
  540. <div class="nav-group">通用</div>
  541. <button class="nav-i on" data-end="login" data-mod="login"><svg class="ic"><use href="#i-shield"/></svg><span>登录</span><span class="nav-idx">1</span></button>
  542. <div class="nav-group">桌面端 · 1440×900</div>
  543. <button class="nav-i" data-end="dk" data-mod="chat"><svg class="ic"><use href="#i-chat"/></svg><span>会话(四栏)</span><span class="nav-idx">2</span></button>
  544. <button class="nav-i" data-end="dk" data-mod="workbench"><svg class="ic"><use href="#i-grid"/></svg><span>工作台</span><span class="nav-idx">3</span></button>
  545. <button class="nav-i" data-end="dk" data-mod="tasks"><svg class="ic"><use href="#i-task"/></svg><span>任务看板</span><span class="nav-idx">4</span></button>
  546. <button class="nav-i" data-end="dk" data-mod="deliverables"><svg class="ic"><use href="#i-box"/></svg><span>成果</span><span class="nav-idx">5</span></button>
  547. <button class="nav-i" data-end="dk" data-mod="contacts"><svg class="ic"><use href="#i-users"/></svg><span>通讯录</span><span class="nav-idx">6</span></button>
  548. <div class="nav-group">移动端 · 390×844</div>
  549. <button class="nav-i" data-end="mb" data-mod="home"><svg class="ic"><use href="#i-grid"/></svg><span>聚合首页</span><span class="nav-idx">7</span></button>
  550. <button class="nav-i" data-end="mb" data-mod="chat"><svg class="ic"><use href="#i-chat"/></svg><span>会话</span><span class="nav-idx">8</span></button>
  551. <button class="nav-i" data-end="mb" data-mod="workbench"><svg class="ic"><use href="#i-grid"/></svg><span>工作台</span><span class="nav-idx">9</span></button>
  552. <button class="nav-i" data-end="mb" data-mod="contacts"><svg class="ic"><use href="#i-users"/></svg><span>通讯录</span><span class="nav-idx">10</span></button>
  553. <button class="nav-i" data-end="mb" data-mod="mine"><svg class="ic"><use href="#i-user"/></svg><span>我的</span><span class="nav-idx">11</span></button>
  554. </nav>
  555. <!-- ============ 舞台 ============ -->
  556. <main class="stage" id="stage">
  557. <!-- ==================== 通用 1 · 登录(双形态) ==================== -->
  558. <section class="screen is-on" data-end="login">
  559. <div class="login-stage">
  560. <!-- 桌面形态 -->
  561. <div class="device" style="width:100%;max-width:1200px">
  562. <div class="device-cap"><b>登录 · 桌面形态</b><span>同页双形态 · 验证码 / Token 直登</span><span class="badge badge-n" style="margin-left:auto">FR-02</span></div>
  563. <div class="login-dk">
  564. <div class="login-brand">
  565. <div class="lg"><span class="brand-mark">F</span><span style="font-size:var(--fs-20);font-weight:800;letter-spacing:-.02em">Fmode</span></div>
  566. <h1>人机共生 IM 中枢</h1>
  567. <p>通过会话协作数字生命,实时掌握它的运行状态与交付成果。</p>
  568. <div class="login-points">
  569. <div class="login-point"><svg class="ic"><use href="#i-chat"/></svg>与数字生命一对一会话,随时下达指令</div>
  570. <div class="login-point"><svg class="ic"><use href="#i-box"/></svg>交付成果独立归档,与聊天解耦、随时取用</div>
  571. <div class="login-point"><svg class="ic"><use href="#i-grid"/></svg>工作台与任务看板,一眼看清谁在忙、谁卡住</div>
  572. </div>
  573. </div>
  574. <div class="login-panel">
  575. <h2>登录</h2>
  576. <p class="sub">验证码登录,或使用 Token 直登</p>
  577. <div class="tabs login-tabs" style="margin-bottom:var(--sp-5)">
  578. <button class="tab on" data-method="code">验证码登录</button>
  579. <button class="tab" data-method="token">Token 直登</button>
  580. </div>
  581. <div data-login-method="code">
  582. <div class="field">
  583. <label for="lg-phone">手机号</label>
  584. <div class="box"><svg class="ic"><use href="#i-phone"/></svg><input id="lg-phone" value="138 0000 0000"></div>
  585. </div>
  586. <div class="field">
  587. <label for="lg-code">验证码</label>
  588. <div class="box" style="border-color:var(--danger)">
  589. <svg class="ic" style="color:var(--danger)"><use href="#i-key"/></svg>
  590. <input id="lg-code" value="4821" aria-invalid="true" aria-describedby="lg-code-err">
  591. <button class="code-btn">重新获取</button>
  592. </div>
  593. <div class="err" id="lg-code-err"><svg class="ic ic-sm"><use href="#i-alert"/></svg>验证码错误,请重新输入</div>
  594. </div>
  595. <button class="btn btn-primary btn-block">登 录</button>
  596. </div>
  597. <div data-login-method="token" hidden>
  598. <div class="field">
  599. <label for="lg-token">访问 Token</label>
  600. <div class="box"><svg class="ic"><use href="#i-key"/></svg><input id="lg-token" placeholder="粘贴 Token,回车直登"></div>
  601. </div>
  602. <button class="btn btn-primary btn-block">登 录</button>
  603. </div>
  604. <div class="login-alt">
  605. <b style="color:var(--ink-2)">错误文案必须明确(AC-02.6,不得静默失败)</b><br>
  606. 手机号格式不正确 · 验证码错误 · 验证码已过期 · <code>#token=</code> 无效或已失效<br>
  607. <span style="color:var(--faint)">含 <code>#token=</code> 自动登录时,成功后清空地址栏(AC-02.2)</span>
  608. </div>
  609. </div>
  610. </div>
  611. </div>
  612. <!-- 移动形态 -->
  613. <div class="device device-mb" style="height:auto">
  614. <div class="device-cap"><b>登录 · 移动形态</b><span class="badge badge-n" style="margin-left:auto">FR-02</span></div>
  615. <div class="mb-login">
  616. <div class="lg"><span class="brand-mark">F</span><span style="font-size:var(--fs-20);font-weight:800;letter-spacing:-.02em">Fmode</span></div>
  617. <h1>欢迎回来</h1>
  618. <p class="sub">使用手机号验证码登录,与你的数字生命继续协作。</p>
  619. <div class="field">
  620. <label for="mb-phone">手机号</label>
  621. <div class="box"><svg class="ic"><use href="#i-phone"/></svg><input id="mb-phone" value="138 0000 0000"></div>
  622. </div>
  623. <div class="field">
  624. <label for="mb-code">验证码</label>
  625. <div class="box"><svg class="ic"><use href="#i-key"/></svg><input id="mb-code" placeholder="6 位验证码"><button class="code-btn">获取验证码</button></div>
  626. </div>
  627. <button class="btn btn-primary btn-block">登 录</button>
  628. <div class="sp"></div>
  629. <p style="font-size:var(--fs-12);color:var(--faint);text-align:center;line-height:1.8">
  630. 登录即表示同意服务条款 · v2.0.0
  631. </p>
  632. </div>
  633. </div>
  634. </div>
  635. <div class="notes">
  636. <div class="notes-h"><svg class="ic"><use href="#i-eye"/></svg>设计依据与留痕</div>
  637. <div class="note">
  638. <span class="ref">FR-02</span><span class="ref">AC-02.1</span><span class="ref">AC-02.2</span><span class="ref">AC-02.6</span><span class="ref">AC-02.7</span><br>
  639. <b>同页双形态</b>:桌面左右分栏(品牌叙事 + 表单),移动全屏单列。两种登录方式(验证码 / Token)在桌面用 Tab 切换。<br>
  640. 错误态就地展示、文案明确(<b>验证码错误</b> 为示例),不静默失败;重复提交需防抖。
  641. </div>
  642. <div class="note ok">
  643. <b>无障碍</b>:输入控件实际字号 <b>≥16px</b>(iOS 聚焦不放大,见《双端布局规范》§3.4);错误字段带 <code>aria-invalid</code> + <code>aria-describedby</code>;所有按钮触达区 ≥44px。
  644. </div>
  645. </div>
  646. </section>
  647. <!-- ==================== 桌面端(5 屏) ==================== -->
  648. <section class="screen" data-end="dk">
  649. <div class="device" id="dkDevice" style="max-width:1464px">
  650. <div class="device-cap">
  651. <b>桌面端 · 1440 × 900</b>
  652. <span id="dkCapSub">四栏骨架:导航 56 / 列表 300 / 主区 flex ≥480 / 右栏 340</span>
  653. <span class="badge badge-n" style="margin-left:auto">双端双套 · ADR-004</span>
  654. </div>
  655. <div class="device-dk" id="dkBox">
  656. <div class="dk" id="dkShell">
  657. <!-- 导航轨(7 模块 + 我的,默认图标态 56px) -->
  658. <nav class="rail" aria-label="主导航">
  659. <div class="rail-logo" title="Fmode">F</div>
  660. <button class="rail-i on" data-mod="chat" title="会话"><svg class="ic"><use href="#i-chat"/></svg></button>
  661. <button class="rail-i" data-mod="contacts" title="通讯录"><svg class="ic"><use href="#i-users"/></svg></button>
  662. <button class="rail-i" data-mod="projects" title="项目"><svg class="ic"><use href="#i-folder"/></svg></button>
  663. <button class="rail-i has-dot" data-mod="events" title="事件(有新告警)"><svg class="ic"><use href="#i-bell"/></svg></button>
  664. <button class="rail-i" data-mod="workbench" title="工作台"><svg class="ic"><use href="#i-grid"/></svg></button>
  665. <button class="rail-i" data-mod="tasks" title="任务看板"><svg class="ic"><use href="#i-task"/></svg></button>
  666. <button class="rail-i" data-mod="deliverables" title="成果"><svg class="ic"><use href="#i-box"/></svg></button>
  667. <div class="rail-sp"></div>
  668. <button class="rail-i" data-mod="mine" title="我的"><svg class="ic"><use href="#i-user"/></svg></button>
  669. </nav>
  670. <!-- ===== 桌面屏 2 · 会话(四栏) ===== -->
  671. <div class="view" data-view="chat">
  672. <aside class="pane pane-list">
  673. <div class="pane-head"><label class="search"><svg class="ic ic-sm"><use href="#i-search"/></svg><input placeholder="搜索会话"></label></div>
  674. <div class="pane-body">
  675. <div class="group-label">数字生命</div>
  676. <button class="row on">
  677. <span class="av av-bot av-on">数</span>
  678. <span class="row-meta">
  679. <span class="row-name"><span class="nm">数据分析助手</span></span>
  680. <span class="row-prev">已完成 3 份报表,正在生成第 4 份</span>
  681. </span>
  682. <span class="row-time">10:24</span>
  683. </button>
  684. <button class="row row-err">
  685. <span class="av av-bot av-off av-danger">运</span>
  686. <span class="row-meta">
  687. <span class="row-name"><span class="nm">运维值守</span></span>
  688. <span class="row-prev is-danger">任务失败:依赖服务不可用</span>
  689. </span>
  690. <span class="row-time">09:51</span>
  691. </button>
  692. <button class="row">
  693. <span class="av av-bot av-idle">文</span>
  694. <span class="row-meta">
  695. <span class="row-name"><span class="nm">文案生成器</span></span>
  696. <span class="row-prev">空闲,等待新任务</span>
  697. </span>
  698. <span class="row-time">昨天</span>
  699. </button>
  700. <div class="group-label">同事</div>
  701. <button class="row">
  702. <span class="av av-user av-on">李</span>
  703. <span class="row-meta">
  704. <span class="row-name"><span class="nm">李工</span><span class="badge badge-n">同事</span></span>
  705. <span class="row-prev">名下 2 个数字生命</span>
  706. </span>
  707. <span class="row-time">昨天</span>
  708. </button>
  709. </div>
  710. </aside>
  711. <section class="pane pane-main">
  712. <header class="chat-head">
  713. <span class="av av-bot av-on av-sm">数</span>
  714. <div class="chat-title">
  715. <div class="t-name">数据分析助手</div>
  716. <div class="t-sub"><span class="dot dot-success"></span>运行中 · 最后活动 2 分钟前</div>
  717. </div>
  718. <div class="head-actions">
  719. <button class="icon-btn" title="搜索消息"><svg class="ic"><use href="#i-search"/></svg></button>
  720. <button class="icon-btn" title="更多"><svg class="ic"><use href="#i-more"/></svg></button>
  721. </div>
  722. </header>
  723. <!-- L2 · 状态条:全页面唯一渲染「在干嘛」的地方 -->
  724. <div class="statusbar" role="status">
  725. <span class="pulse"></span>
  726. <div class="sb-text">
  727. <b>正在生成第 4 份季度报表</b>
  728. <span>已用时 3 分 12 秒 · 点击查看步骤</span>
  729. </div>
  730. <svg class="ic sb-chev"><use href="#i-chev-d"/></svg>
  731. </div>
  732. <ol class="steps">
  733. <li class="step done"><span class="step-m">✓</span><span>拉取数据源<span class="step-time">1.2s</span></span></li>
  734. <li class="step done"><span class="step-m">✓</span><span>清洗与聚合<span class="step-time">8.4s</span></span></li>
  735. <li class="step cur"><span class="step-m">▸</span><span>渲染图表并导出 PDF</span></li>
  736. <li class="step"><span class="step-m">○</span><span>上传至成果库</span></li>
  737. </ol>
  738. <div class="msgs" id="dkChatMsgs">
  739. <div class="sys-msg">今天 10:20</div>
  740. <div class="msg">
  741. <span class="av av-bot av-sm">数</span>
  742. <div class="bubble">早上好,我已开始处理今天的报表任务。</div>
  743. </div>
  744. <div class="msg is-me">
  745. <div class="bubble">把第 3 季度的也一起做了</div>
  746. <span class="av av-user av-sm">胡</span>
  747. </div>
  748. <div class="msg">
  749. <span class="av av-bot av-sm">数</span>
  750. <div class="bubble">收到,第 3 季度已排入队列。当前正在处理第 4 份,预计还需数分钟。</div>
  751. </div>
  752. <div class="tool-call"><svg class="ic ic-sm"><use href="#i-play"/></svg>工具调用 · 读取 sales_q4.xlsx(1.2s)· 已折叠</div>
  753. <div class="sys-msg">工具调用结果走系统消息样式,不占主视觉</div>
  754. <div class="msg">
  755. <span class="av av-bot av-sm">数</span>
  756. <div class="bubble">第 4 份报表已进入「导出 PDF」阶段,完成后会自动归档到成果库。</div>
  757. </div>
  758. <div class="msg is-me">
  759. <div class="bubble">好,完成后发我一份</div>
  760. <span class="av av-user av-sm">胡</span>
  761. </div>
  762. </div>
  763. <div class="composer">
  764. <button class="icon-btn" title="发送文件"><svg class="ic"><use href="#i-plus"/></svg></button>
  765. <textarea class="composer-input" rows="1" placeholder="发消息…(Enter 发送 / Shift+Enter 换行)"></textarea>
  766. <button class="send-btn" title="发送"><svg class="ic"><use href="#i-send"/></svg></button>
  767. </div>
  768. </section>
  769. <aside class="pane pane-aside">
  770. <div class="aside-h"><b>实况</b><svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-panel"/></svg></div>
  771. <div class="aside-b">
  772. <div class="sect-t">运行指标</div>
  773. <div class="metric"><span class="metric-k"><svg class="ic ic-sm"><use href="#i-cpu"/></svg>CPU</span><span class="metric-v">12%</span></div>
  774. <div class="bar"><i style="width:12%"></i></div>
  775. <div class="metric"><span class="metric-k"><svg class="ic ic-sm"><use href="#i-db"/></svg>内存</span><span class="metric-v">34%</span></div>
  776. <div class="bar"><i style="width:34%"></i></div>
  777. <div class="metric"><span class="metric-k"><svg class="ic ic-sm"><use href="#i-wallet"/></svg>余额</span><span class="metric-v" style="color:var(--primary);display:inline-flex;align-items:center;gap:var(--sp-1)">¥128.50<svg class="ic ic-sm"><use href="#i-external"/></svg></span></div>
  778. <div class="metric"><span class="metric-k"><svg class="ic ic-sm"><use href="#i-zap"/></svg>通道</span><span class="metric-v" style="display:inline-flex;align-items:center;gap:var(--sp-1)"><span class="dot dot-success"></span>已连接</span></div>
  779. <div class="divider"></div>
  780. <div class="sect-t">最近成果</div>
  781. <div class="file-row"><span class="av av-file av-sm">PDF</span><span style="flex:1;min-width:0"><span class="nm" style="display:block">Q2 季度报表.pdf</span><span class="tm">10:12 · 1.2 MB</span></span></div>
  782. <div class="file-row"><span class="av av-file av-sm">XLS</span><span style="flex:1;min-width:0"><span class="nm" style="display:block">销售趋势.xlsx</span><span class="tm">昨天 · 340 KB</span></span></div>
  783. <div class="divider"></div>
  784. <div class="sect-t">上下文用量</div>
  785. <div class="metric"><span class="metric-k">窗口占用</span><span class="metric-v">62%</span></div>
  786. <div class="bar"><i style="width:62%"></i></div>
  787. <div class="sect-t" style="margin-top:var(--sp-5)">可用工具</div>
  788. <div style="display:flex;flex-wrap:wrap;gap:var(--sp-1)">
  789. <span class="badge badge-n">读取文件</span>
  790. <span class="badge badge-n">执行代码</span>
  791. <span class="badge badge-n">联网检索</span>
  792. </div>
  793. </div>
  794. </aside>
  795. </div>
  796. <!-- ===== 桌面屏 3 · 工作台 ===== -->
  797. <div class="view" data-view="workbench" hidden>
  798. <div class="page">
  799. <div class="page-h">
  800. <div class="page-t">
  801. <h2>工作台</h2>
  802. <p>名下 4 个数字生命的实时状态 · 双端同一实况服务,口径一致(含 token 用量)</p>
  803. </div>
  804. <button class="btn btn-ghost"><svg class="ic ic-sm"><use href="#i-refresh"/></svg>刷新</button>
  805. </div>
  806. <div class="stat-row">
  807. <div class="stat"><div class="stat-k">数字生命</div><div class="stat-v">4<small>个</small></div></div>
  808. <div class="stat"><div class="stat-k"><span class="dot dot-success"></span>运行中</div><div class="stat-v">2</div></div>
  809. <div class="stat"><div class="stat-k"><span class="dot dot-warning"></span>空闲</div><div class="stat-v">1</div></div>
  810. <div class="stat"><div class="stat-k"><span class="dot dot-danger"></span>异常</div><div class="stat-v" style="color:var(--danger)">1</div></div>
  811. <div class="stat"><div class="stat-k">今日 token</div><div class="stat-v">128<small>k</small></div></div>
  812. </div>
  813. <div class="card-grid">
  814. <!-- 卡片 1 运行中 -->
  815. <article class="wcard">
  816. <div class="wcard-h">
  817. <span class="av av-bot av-on">数</span>
  818. <span class="nm"><b>数据分析助手</b><span>数据分析 · 3 个成果</span></span>
  819. <span class="badge badge-s">运行中</span>
  820. </div>
  821. <div class="wcard-task"><svg class="ic ic-sm"><use href="#i-play"/></svg>正在生成第 4 份季度报表(3:12)</div>
  822. <div class="wcard-metrics">
  823. <div><div class="wcard-f"><span>CPU</span><b>12%</b></div><div class="bar"><i style="width:12%"></i></div></div>
  824. <div><div class="wcard-f"><span>内存</span><b>34%</b></div><div class="bar"><i style="width:34%"></i></div></div>
  825. </div>
  826. <div class="wcard-foot">
  827. <span style="flex:1">余额 ¥128.50</span>
  828. <span style="display:inline-flex;align-items:center;gap:var(--sp-1)"><span class="dot dot-success"></span>通道正常</span>
  829. </div>
  830. </article>
  831. <!-- 卡片 2 异常 -->
  832. <article class="wcard edge-err">
  833. <div class="wcard-h">
  834. <span class="av av-bot av-off av-danger">运</span>
  835. <span class="nm"><b>运维值守</b><span>运维 · 1 个成果</span></span>
  836. <span class="badge badge-d">异常</span>
  837. </div>
  838. <div class="wcard-task" style="color:var(--danger)"><svg class="ic ic-sm" style="color:var(--danger)"><use href="#i-alert"/></svg>任务失败:依赖服务不可用,连续失败 41 次</div>
  839. <div class="wcard-metrics">
  840. <div><div class="wcard-f"><span>CPU</span><b>88%</b></div><div class="bar warning"><i style="width:88%"></i></div></div>
  841. <div><div class="wcard-f"><span>内存</span><b>71%</b></div><div class="bar"><i style="width:71%"></i></div></div>
  842. </div>
  843. <div class="wcard-foot">
  844. <span style="flex:1">余额 ¥12.00</span>
  845. <span style="color:var(--danger);display:inline-flex;align-items:center;gap:var(--sp-1)"><span class="dot dot-danger"></span>通道异常</span>
  846. </div>
  847. </article>
  848. <!-- 卡片 3 空闲 -->
  849. <article class="wcard edge-warn">
  850. <div class="wcard-h">
  851. <span class="av av-bot av-idle">文</span>
  852. <span class="nm"><b>文案生成器</b><span>内容 · 0 个成果</span></span>
  853. <span class="badge badge-w">空闲</span>
  854. </div>
  855. <div class="wcard-task"><svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-clock"/></svg>暂无任务,等待派发(请到会话页下达指令)</div>
  856. <div class="wcard-metrics">
  857. <div><div class="wcard-f"><span>CPU</span><b>2%</b></div><div class="bar"><i style="width:2%"></i></div></div>
  858. <div><div class="wcard-f"><span>内存</span><b>18%</b></div><div class="bar"><i style="width:18%"></i></div></div>
  859. </div>
  860. <div class="wcard-foot">
  861. <span style="flex:1">余额 ¥56.20</span>
  862. <span style="display:inline-flex;align-items:center;gap:var(--sp-1)"><span class="dot dot-success"></span>通道正常</span>
  863. </div>
  864. </article>
  865. <!-- 卡片 4 运行中 -->
  866. <article class="wcard">
  867. <div class="wcard-h">
  868. <span class="av av-bot av-on">研</span>
  869. <span class="nm"><b>行业研究员</b><span>研究 · 5 个成果</span></span>
  870. <span class="badge badge-s">运行中</span>
  871. </div>
  872. <div class="wcard-task"><svg class="ic ic-sm"><use href="#i-play"/></svg>汇总本周行业动态(12:40)</div>
  873. <div class="wcard-metrics">
  874. <div><div class="wcard-f"><span>CPU</span><b>27%</b></div><div class="bar"><i style="width:27%"></i></div></div>
  875. <div><div class="wcard-f"><span>内存</span><b>45%</b></div><div class="bar"><i style="width:45%"></i></div></div>
  876. </div>
  877. <div class="wcard-foot">
  878. <span style="flex:1">余额 ¥302.10</span>
  879. <span style="display:inline-flex;align-items:center;gap:var(--sp-1)"><span class="dot dot-success"></span>通道正常</span>
  880. </div>
  881. </article>
  882. </div>
  883. </div>
  884. </div>
  885. <!-- ===== 桌面屏 4 · 任务看板 ===== -->
  886. <div class="view" data-view="tasks" data-filter-scope hidden>
  887. <aside class="pane pane-list">
  888. <div class="pane-head"><label class="search"><svg class="ic ic-sm"><use href="#i-search"/></svg><input placeholder="搜索任务"></label></div>
  889. <div style="padding:var(--sp-3) var(--sp-3) var(--sp-2);border-bottom:1px solid var(--edge)">
  890. <div class="chips">
  891. <button class="chip on" data-cat="all">全部 21</button>
  892. <button class="chip" data-cat="running">进行中 2</button>
  893. <button class="chip chip-w" data-cat="blocked">阻塞 1</button>
  894. <button class="chip chip-d" data-cat="failed">失败 1</button>
  895. <button class="chip" data-cat="todo">待办 3</button>
  896. <button class="chip" data-cat="done">已完成 14</button>
  897. </div>
  898. </div>
  899. <div class="pane-body">
  900. <button class="task blocked on" data-cat="blocked">
  901. <span class="task-ico w">⚠</span>
  902. <span class="task-b">
  903. <span class="task-t">用户身份对齐 — 手机号登录复用已有超管账号</span>
  904. <span class="task-m"><span>@fullstack</span><span>未开始</span></span>
  905. <span class="task-alert w">已阻塞 436 小时 · 等待外部条件</span>
  906. </span>
  907. </button>
  908. <button class="task failed" data-cat="failed">
  909. <span class="task-ico d">⛔</span>
  910. <span class="task-b">
  911. <span class="task-t">定时同步脚本修复</span>
  912. <span class="task-m"><span>@ops</span><span>09:51</span></span>
  913. <span class="task-alert d">连续失败 41 次 · 脚本路径不存在</span>
  914. </span>
  915. </button>
  916. <button class="task cur" data-cat="running">
  917. <span class="task-ico i">▸</span>
  918. <span class="task-b">
  919. <span class="task-t">《进化工程》16 章改稿</span>
  920. <span class="task-m"><span>@writer</span><span>已用时 1h 24m</span></span>
  921. </span>
  922. </button>
  923. <button class="task cur" data-cat="running">
  924. <span class="task-ico i">▸</span>
  925. <span class="task-b">
  926. <span class="task-t">用户画像矩阵校准</span>
  927. <span class="task-m"><span>@analyst</span><span>已用时 12m</span></span>
  928. </span>
  929. </button>
  930. <button class="task" data-cat="todo">
  931. <span class="task-ico">○</span>
  932. <span class="task-b">
  933. <span class="task-t">双端布局降级阶梯实现</span>
  934. <span class="task-m"><span>@frontend</span><span>已排期</span></span>
  935. </span>
  936. </button>
  937. <button class="task" data-cat="todo">
  938. <span class="task-ico">○</span>
  939. <span class="task-b">
  940. <span class="task-t">ER 图字段核对</span>
  941. <span class="task-m"><span>@backend</span><span>待办</span></span>
  942. </span>
  943. </button>
  944. <button class="task done" data-cat="done">
  945. <span class="task-ico s">✓</span>
  946. <span class="task-b">
  947. <span class="task-t">魔镜形象分析系统 V2.0 重写</span>
  948. <span class="task-m"><span>@fullstack</span><span>昨天完成</span></span>
  949. </span>
  950. </button>
  951. <button class="task done" data-cat="done">
  952. <span class="task-ico s">✓</span>
  953. <span class="task-b">
  954. <span class="task-t">设计令牌收敛为单轨</span>
  955. <span class="task-m"><span>@design</span><span>10-12 完成</span></span>
  956. </span>
  957. </button>
  958. </div>
  959. </aside>
  960. <section class="pane pane-main" style="overflow-y:auto">
  961. <div class="detail" style="max-width:860px">
  962. <div class="detail-id">FMODE-318 <svg class="ic ic-sm"><use href="#i-external"/></svg></div>
  963. <h3 style="margin-top:var(--sp-2)">用户身份对齐 — 手机号登录复用已有超管账号</h3>
  964. <div style="display:flex;gap:var(--sp-2);margin:var(--sp-3) 0 var(--sp-5);flex-wrap:wrap">
  965. <span class="badge badge-w"><svg class="ic ic-sm"><use href="#i-alert"/></svg>阻塞</span>
  966. <span class="badge badge-n">@fullstack</span>
  967. <span class="badge badge-n">优先级 P2</span>
  968. <span class="badge badge-n">分支 fix/identity-align</span>
  969. </div>
  970. <div class="note warn" style="margin:0 0 var(--sp-5)">
  971. <b>⚠ 已阻塞 436 小时(约 18 天)</b><br>
  972. 阻塞类型:等待外部条件 · 连续失败 0 次<br>
  973. <span style="color:var(--muted)">原因:代码修复已就绪,线上云函数未部署,等待运维窗口</span>
  974. </div>
  975. <div class="sect-t">时间三件套</div>
  976. <dl class="kv" style="margin-bottom:var(--sp-5)">
  977. <dt>创建</dt><dd>2026-09-24 09:12</dd>
  978. <dt>开始</dt><dd>2026-09-24 09:40</dd>
  979. <dt>最后活动</dt><dd>2026-09-26 05:15</dd>
  980. <dt>所属数字生命</dt><dd style="display:flex;align-items:center;gap:var(--sp-2)"><span class="av av-bot av-sm" style="width:20px;height:20px;font-size:var(--fs-11)">研</span>行业研究员</dd>
  981. </dl>
  982. <div class="sect-t">运行流水</div>
  983. <div style="margin-bottom:var(--sp-5)">
  984. <div class="run"><span class="run-id">run #16</span><span class="badge badge-w">blocked</span><span class="run-t">gave_up · 09-26 05:15</span></div>
  985. <div class="run"><span class="run-id">run #15</span><span class="badge badge-s">done</span><span class="run-t">completed · 09-25 22:03</span></div>
  986. <div class="run"><span class="run-id">run #14</span><span class="badge badge-d">failed</span><span class="run-t">timeout · 09-25 21:40</span></div>
  987. </div>
  988. <div class="sect-t">评论(1)</div>
  989. <blockquote style="margin-bottom:var(--sp-5)">
  990. <b style="color:var(--ink)">fullstack</b> <span style="color:var(--faint);font-size:var(--fs-12)">09-26 05:15</span><br>
  991. 本轮(run 16)结论:代码修复已就绪,线上未生效。需等待云函数部署窗口,不再重试。
  992. </blockquote>
  993. <div class="note">
  994. <b>一期只读</b>:本页不提供派发 / 评论 / 改状态入口(AC-07b.6)。要下新任务请回<b>会话页</b>发指令。<br>
  995. <span class="ref">FR-07b</span><span class="ref">AC-07b.2</span><span class="ref">AC-07b.3</span><span class="ref">AC-07b.6</span>
  996. </div>
  997. </div>
  998. </section>
  999. </div>
  1000. <!-- ===== 桌面屏 5 · 成果 ===== -->
  1001. <div class="view" data-view="deliverables" data-seg-scope hidden>
  1002. <aside class="pane pane-list">
  1003. <div class="pane-head"><label class="search"><svg class="ic ic-sm"><use href="#i-search"/></svg><input placeholder="搜索成果"></label></div>
  1004. <div class="pane-body">
  1005. <div class="group-label">按项目分组</div>
  1006. <button class="row on">
  1007. <span class="av av-file av-sm"><svg class="ic ic-sm"><use href="#i-folder"/></svg></span>
  1008. <span class="row-meta"><span class="row-name"><span class="nm">季度经营分析</span></span><span class="row-prev">8 个成果</span></span>
  1009. </button>
  1010. <button class="row">
  1011. <span class="av av-file av-sm"><svg class="ic ic-sm"><use href="#i-folder"/></svg></span>
  1012. <span class="row-meta"><span class="row-name"><span class="nm">进化工程</span></span><span class="row-prev">5 个成果</span></span>
  1013. </button>
  1014. <button class="row">
  1015. <span class="av av-file av-sm"><svg class="ic ic-sm"><use href="#i-folder"/></svg></span>
  1016. <span class="row-meta"><span class="row-name"><span class="nm">魔镜形象分析</span></span><span class="row-prev">3 个成果</span></span>
  1017. </button>
  1018. <button class="row">
  1019. <span class="av av-file av-sm"><svg class="ic ic-sm"><use href="#i-folder"/></svg></span>
  1020. <span class="row-meta"><span class="row-name"><span class="nm">未归组</span></span><span class="row-prev">2 个成果</span></span>
  1021. </button>
  1022. </div>
  1023. </aside>
  1024. <section class="pane pane-main">
  1025. <div class="seg-head">
  1026. <span style="flex:1;font-size:var(--fs-15);font-weight:650">季度经营分析 · 8 个成果</span>
  1027. <div class="seg">
  1028. <button class="seg-i on" data-panel="byproject">按项目</button>
  1029. <button class="seg-i" data-panel="timeline">按时间线</button>
  1030. </div>
  1031. <button class="btn btn-ghost"><svg class="ic ic-sm"><use href="#i-download"/></svg>批量下载</button>
  1032. </div>
  1033. <div class="pane-body">
  1034. <div data-panel-name="byproject">
  1035. <div class="item">
  1036. <div class="item-h">
  1037. <span class="av av-file"><svg class="ic"><use href="#i-file"/></svg></span>
  1038. <span class="nm"><b>Q2 季度报表.pdf</b><span>数据分析助手 · 1.2 MB · 10:12</span></span>
  1039. <span class="badge badge-s">已交付</span>
  1040. <button class="icon-btn" title="下载"><svg class="ic"><use href="#i-download"/></svg></button>
  1041. </div>
  1042. </div>
  1043. <div class="item">
  1044. <div class="item-h">
  1045. <span class="av av-file"><svg class="ic"><use href="#i-file"/></svg></span>
  1046. <span class="nm"><b>销售趋势分析.xlsx</b><span>数据分析助手 · 340 KB · 昨天</span></span>
  1047. <span class="badge badge-s">已交付</span>
  1048. <button class="icon-btn" title="下载"><svg class="ic"><use href="#i-download"/></svg></button>
  1049. </div>
  1050. </div>
  1051. <div class="item">
  1052. <div class="item-h">
  1053. <span class="av av-file"><svg class="ic"><use href="#i-image"/></svg></span>
  1054. <span class="nm"><b>渠道转化漏斗图(4 张)</b><span>数据分析助手 · 图片组 · 昨天</span></span>
  1055. <span class="badge badge-n">可预览</span>
  1056. <button class="icon-btn" title="全屏预览"><svg class="ic"><use href="#i-eye"/></svg></button>
  1057. </div>
  1058. <div class="item-files">
  1059. <div class="thumb"><svg class="ic ic-lg"><use href="#i-image"/></svg><span class="cap">漏斗-总览.png</span></div>
  1060. <div class="thumb"><svg class="ic ic-lg"><use href="#i-image"/></svg><span class="cap">漏斗-分渠道.png</span></div>
  1061. <div class="thumb"><svg class="ic ic-lg"><use href="#i-image"/></svg><span class="cap">漏斗-趋势.png</span></div>
  1062. <div class="thumb"><svg class="ic ic-lg"><use href="#i-image"/></svg><span class="cap">漏斗-对比.png</span></div>
  1063. </div>
  1064. </div>
  1065. </div>
  1066. <div data-panel-name="timeline" hidden>
  1067. <div style="padding:var(--sp-5) var(--sp-5) var(--sp-6)">
  1068. <div class="tl">
  1069. <div class="tl-i">
  1070. <div class="tl-d">今天 · 10:12</div>
  1071. <div style="font-size:var(--fs-15);font-weight:650">Q2 季度报表.pdf</div>
  1072. <div style="font-size:var(--fs-12);color:var(--muted)">季度经营分析 · 数据分析助手</div>
  1073. </div>
  1074. <div class="tl-i">
  1075. <div class="tl-d">昨天 · 18:30</div>
  1076. <div style="font-size:var(--fs-15);font-weight:650">渠道转化漏斗图(4 张)</div>
  1077. <div style="font-size:var(--fs-12);color:var(--muted)">季度经营分析 · 数据分析助手</div>
  1078. </div>
  1079. <div class="tl-i">
  1080. <div class="tl-d">昨天 · 09:05</div>
  1081. <div style="font-size:var(--fs-15);font-weight:650">销售趋势分析.xlsx</div>
  1082. <div style="font-size:var(--fs-12);color:var(--muted)">季度经营分析 · 数据分析助手</div>
  1083. </div>
  1084. <div class="tl-i">
  1085. <div class="tl-d">10-11 · 16:20</div>
  1086. <div style="font-size:var(--fs-15);font-weight:650">《进化工程》第 15 章改稿.docx</div>
  1087. <div style="font-size:var(--fs-12);color:var(--muted)">进化工程 · 文案生成器</div>
  1088. </div>
  1089. </div>
  1090. </div>
  1091. </div>
  1092. </div>
  1093. </section>
  1094. </div>
  1095. <!-- ===== 桌面屏 6 · 通讯录 ===== -->
  1096. <div class="view" data-view="contacts" data-tab-scope hidden>
  1097. <div class="page">
  1098. <div class="page-h" style="max-width:1040px;margin:0 auto var(--sp-4)">
  1099. <div class="page-t">
  1100. <h2>通讯录</h2>
  1101. <p>全部 · AI 数字生命 · 同事 | 点数字生命行进会话,未接入的进详情页(R2 服务层单点判定)</p>
  1102. </div>
  1103. <label class="search" style="max-width:280px"><svg class="ic ic-sm"><use href="#i-search"/></svg><input placeholder="搜索名称"></label>
  1104. </div>
  1105. <div style="max-width:1040px;margin:0 auto">
  1106. <div class="tabs">
  1107. <button class="tab on" data-panel="all">全部 7</button>
  1108. <button class="tab" data-panel="bots">AI 数字生命 4</button>
  1109. <button class="tab" data-panel="colleagues">同事 3</button>
  1110. </div>
  1111. <div data-panel-name="all" style="padding-top:var(--sp-5)">
  1112. <div class="sect-t">AI 数字生命</div>
  1113. <div class="card-grid" style="margin-bottom:var(--sp-6)">
  1114. <button class="wcard" style="text-align:left">
  1115. <div class="wcard-h"><span class="av av-bot av-on">数</span><span class="nm"><b>数据分析助手</b><span>运行中 · 已接入 FMODE</span></span></div>
  1116. <div class="wcard-foot"><span style="flex:1">最近成果 Q2 季度报表</span><svg class="ic ic-sm"><use href="#i-chev-r"/></svg></div>
  1117. </button>
  1118. <button class="wcard" style="text-align:left">
  1119. <div class="wcard-h"><span class="av av-bot av-off av-danger">运</span><span class="nm"><b>运维值守</b><span>异常 · 已接入 FMODE</span></span></div>
  1120. <div class="wcard-foot"><span style="flex:1">任务失败:依赖服务不可用</span><svg class="ic ic-sm"><use href="#i-chev-r"/></svg></div>
  1121. </button>
  1122. <button class="wcard" style="text-align:left">
  1123. <div class="wcard-h"><span class="av av-bot av-idle">文</span><span class="nm"><b>文案生成器</b><span>空闲 · 已接入 FMODE</span></span></div>
  1124. <div class="wcard-foot"><span style="flex:1">等待新任务</span><svg class="ic ic-sm"><use href="#i-chev-r"/></svg></div>
  1125. </button>
  1126. <button class="wcard" style="text-align:left">
  1127. <div class="wcard-h"><span class="av av-file av-sm" style="width:40px;height:40px"><svg class="ic"><use href="#i-user"/></svg></span><span class="nm"><b>行业研究员</b><span>未接入 FMODE</span></span><span class="badge badge-n">详情</span></div>
  1128. <div class="wcard-foot"><span style="flex:1">点行进「数字生命详情」</span><svg class="ic ic-sm"><use href="#i-chev-r"/></svg></div>
  1129. </button>
  1130. </div>
  1131. <div class="sect-t">同事</div>
  1132. <div style="background:var(--surface);border:1px solid var(--edge);border-radius:var(--r-md);overflow:hidden">
  1133. <button class="row" data-expand="ex-li" aria-expanded="false" style="border-bottom:1px solid var(--edge)">
  1134. <span class="av av-user av-on">李</span>
  1135. <span class="row-meta"><span class="row-name"><span class="nm">李工</span><span class="badge badge-n">2 个数字生命</span></span><span class="row-prev">产品经理 · 在线</span></span>
  1136. <svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-chev-r"/></svg>
  1137. </button>
  1138. <div id="ex-li" hidden style="background:var(--surface-2);border-bottom:1px solid var(--edge)">
  1139. <button class="row" style="padding-left:64px"><span class="av av-bot av-on av-sm">数</span><span class="row-meta"><span class="row-name"><span class="nm">数据分析助手</span></span><span class="row-prev">运行中</span></span></button>
  1140. <button class="row" style="padding-left:64px"><span class="av av-bot av-idle av-sm">文</span><span class="row-meta"><span class="row-name"><span class="nm">文案生成器</span></span><span class="row-prev">空闲</span></span></button>
  1141. </div>
  1142. <button class="row" data-expand="ex-zhang" aria-expanded="false" style="border-bottom:1px solid var(--edge)">
  1143. <span class="av av-user">张</span>
  1144. <span class="row-meta"><span class="row-name"><span class="nm">张敏</span><span class="badge badge-n">1 个数字生命</span></span><span class="row-prev">运营 · 离线</span></span>
  1145. <svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-chev-r"/></svg>
  1146. </button>
  1147. <div id="ex-zhang" hidden style="background:var(--surface-2)">
  1148. <button class="row" style="padding-left:64px"><span class="av av-bot av-off av-sm">运</span><span class="row-meta"><span class="row-name"><span class="nm">运维值守</span></span><span class="row-prev">异常</span></span></button>
  1149. </div>
  1150. </div>
  1151. </div>
  1152. <div data-panel-name="bots" hidden style="padding-top:var(--sp-5)">
  1153. <div class="empty"><svg class="ic" style="width:40px;height:40px"><use href="#i-users"/></svg><b>AI 数字生命(4)</b><p>此为 Tab 切换后的独立面板,与「全部」Tab 互斥显示(AC-03.1)。同一份网格卡在此完整呈现。</p></div>
  1154. </div>
  1155. <div data-panel-name="colleagues" hidden style="padding-top:var(--sp-5)">
  1156. <div class="empty"><svg class="ic" style="width:40px;height:40px"><use href="#i-users"/></svg><b>同事(3)</b><p>同事行可展开其名下数字生命(AC-03.1)。展开态见「全部」 Tab 中的李工一行。</p></div>
  1157. </div>
  1158. </div>
  1159. </div>
  1160. </div>
  1161. <!-- ===== 未覆盖模块占位(导航 7 项齐全,不砍功能) ===== -->
  1162. <div class="view" data-view="stub" hidden>
  1163. <div class="page" style="display:grid;place-items:center">
  1164. <div class="empty">
  1165. <svg class="ic" style="width:44px;height:44px;color:var(--faint)"><use href="#i-folder"/></svg>
  1166. <b id="stubTitle">该模块</b>
  1167. <p>本 MVP demo 聚焦 11 个关键屏;此模块 SPEC 中已有 FR 定义(非砍功能),将在阶段②B 组件规范细化时补齐。</p>
  1168. </div>
  1169. </div>
  1170. </div>
  1171. </div>
  1172. </div>
  1173. </div>
  1174. <div class="notes">
  1175. <div class="notes-h"><svg class="ic"><use href="#i-eye"/></svg>桌面端设计依据</div>
  1176. <div class="note">
  1177. <span class="ref">FR-04</span><span class="ref">AC-04.1</span><span class="ref">AC-04.6</span><span class="ref">约束 C5</span><br>
  1178. <b>会话页四栏</b> 56 / 300 / flex(≥480) / 340。<b>「正在处理…」状态条是全页面唯一渲染「在干嘛」的地方</b> —— 存量在标题栏、TaskStrip、LiveConsole 三处重复渲染,这里收敛成一条(AC-04.6)。
  1179. </div>
  1180. <div class="note ok">
  1181. <span class="ref">规则 2</span><b>不造假进度</b>:AI 任务耗时不可预测,用步骤清单(✓/▸/○)+ 已用时;进度条只用于分母已知的场景(CPU / 内存 / 上传)。
  1182. </div>
  1183. <div class="note warn">
  1184. <span class="ref">FR-07</span><span class="ref">AC-07.3</span><b>工作台</b>:加载失败必须显示错误态,禁止伪装成空态或"权限问题"(存量痛点)。双端同一实况服务,口径一致含 token 用量(FR-07 AC-07.2 / FR-17)。
  1185. </div>
  1186. <div class="note">
  1187. <span class="ref">FR-07b</span><span class="ref">AC-07b.3</span><b>任务看板</b>:阻塞 / 失败用<b>左色条 3px + 图标 + 文字 + 阻塞时长</b>,不用整行红底。列表内直接显示「已阻塞 436h」—— 这是 P2 的核心价值。一期只读。
  1188. </div>
  1189. <div class="note">
  1190. <span class="ref">FR-08</span><span class="ref">AC-08.1</span><b>成果</b>:按项目 / 按时间线<b>双视图分段器</b>;条目可展开看文件,图片支持全屏预览。成果独立于消息气泡,P1 核心场景。
  1191. </div>
  1192. <div class="note ok">
  1193. <span class="ref">规范</span><b>布局校验(本轮修正)</b>:① 四栏宽度全部令牌化(56 / 300 / flex ≥480 / 340),修复视图包裹层缺失导致的「各栏上下堆叠」;② 三栏头统一 <code>--hd-h</code> 64px,分隔线实测对齐;③ 任务看板列表栏由硬编码 340 归位 <code>--dk-list-w</code> 300;④ 右栏「当前任务」与 L2 状态条重复渲染已移除(约束 C5)。
  1194. </div>
  1195. </div>
  1196. </section>
  1197. <!-- ==================== 移动端(5 屏) ==================== -->
  1198. <section class="screen" data-end="mb">
  1199. <div class="device device-mb">
  1200. <div class="device-cap">
  1201. <b>移动端 · 390 × 844</b>
  1202. <span>单列高可达 · 触达区 ≥44 · 输入 ≥16px</span>
  1203. </div>
  1204. <div class="mb" id="mbShell">
  1205. <div class="mb-status"><span>9:41</span><span style="display:flex;align-items:center;gap:var(--sp-1)">中国移动 5G<svg class="ic ic-sm"><use href="#i-zap"/></svg></span></div>
  1206. <!-- ===== 移动屏 7 · 聚合首页 ===== -->
  1207. <div class="view" data-view="home">
  1208. <header class="mb-nav">
  1209. <span class="mb-nav-t">首页</span>
  1210. <button class="mb-icon-btn" title="搜索"><svg class="ic"><use href="#i-search"/></svg></button>
  1211. </header>
  1212. <div class="mb-body">
  1213. <button class="mb-banner">
  1214. <svg class="ic"><use href="#i-alert"/></svg>
  1215. <span style="flex:1"><b>有 2 项需要关注</b><span>1 个任务阻塞 · 1 个执行失败</span></span>
  1216. <svg class="ic ic-sm" style="color:var(--warning)"><use href="#i-chev-r"/></svg>
  1217. </button>
  1218. <div class="mb-sect"><b>最近会话</b><a>全部 3 ›</a></div>
  1219. <button class="mb-cell">
  1220. <span class="av av-bot av-on">数</span>
  1221. <span class="mb-cb"><span class="mb-ct"><span class="nm">数据分析助手</span></span><span class="mb-cd">已完成 3 份报表,正在生成第 4 份</span></span>
  1222. <span class="mb-cr">10:24</span>
  1223. </button>
  1224. <button class="mb-cell warn">
  1225. <span class="av av-bot av-off av-danger">运</span>
  1226. <span class="mb-cb"><span class="mb-ct"><span class="nm">运维值守</span></span><span class="mb-cd" style="color:var(--danger)">任务失败:依赖服务不可用</span></span>
  1227. <span class="mb-cr">09:51</span>
  1228. </button>
  1229. <button class="mb-cell">
  1230. <span class="av av-bot av-idle">文</span>
  1231. <span class="mb-cb"><span class="mb-ct"><span class="nm">文案生成器</span></span><span class="mb-cd">空闲,等待新任务</span></span>
  1232. <span class="mb-cr">昨天</span>
  1233. </button>
  1234. <div class="mb-sect"><b>最新成果</b><a>全部 3 ›</a></div>
  1235. <button class="mb-cell">
  1236. <span class="av av-file"><svg class="ic"><use href="#i-file"/></svg></span>
  1237. <span class="mb-cb"><span class="mb-ct"><span class="nm">Q2 季度报表.pdf</span></span><span class="mb-cd">数据分析助手 · 1.2 MB</span></span>
  1238. <span class="mb-cr">10:12</span>
  1239. </button>
  1240. <button class="mb-cell">
  1241. <span class="av av-file"><svg class="ic"><use href="#i-file"/></svg></span>
  1242. <span class="mb-cb"><span class="mb-ct"><span class="nm">销售趋势分析.xlsx</span></span><span class="mb-cd">数据分析助手 · 340 KB</span></span>
  1243. <span class="mb-cr">昨天</span>
  1244. </button>
  1245. <button class="mb-cell">
  1246. <span class="av av-file"><svg class="ic"><use href="#i-image"/></svg></span>
  1247. <span class="mb-cb"><span class="mb-ct"><span class="nm">渠道转化漏斗图(4 张)</span></span><span class="mb-cd">数据分析助手 · 图片组</span></span>
  1248. <span class="mb-cr">昨天</span>
  1249. </button>
  1250. <div style="height:var(--sp-4)"></div>
  1251. </div>
  1252. </div>
  1253. <!-- ===== 移动屏 8 · 会话 ===== -->
  1254. <div class="view" data-view="chat" hidden>
  1255. <header class="mb-nav">
  1256. <button class="mb-icon-btn" title="返回"><svg class="ic"><use href="#i-chev-l"/></svg></button>
  1257. <span class="av av-bot av-on av-sm">数</span>
  1258. <span style="flex:1;min-width:0">
  1259. <span style="display:block;font-size:var(--fs-15);font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">数据分析助手</span>
  1260. <span style="font-size:var(--fs-11);color:var(--muted);display:flex;align-items:center;gap:var(--sp-1)"><span class="dot dot-success"></span>运行中</span>
  1261. </span>
  1262. <button class="mb-icon-btn" data-drawer="open" title="查看成果"><svg class="ic"><use href="#i-box"/></svg></button>
  1263. </header>
  1264. <div class="statusbar is-static" style="margin:var(--sp-3) var(--sp-4) 0;padding:var(--sp-2) var(--sp-3)">
  1265. <span class="pulse"></span>
  1266. <span class="sb-text"><b style="font-size:var(--fs-12)">生成第 4 份报表</b></span>
  1267. <span style="font-size:var(--fs-12);color:var(--faint);display:inline-flex;align-items:center;gap:var(--sp-1)">3:12<svg class="ic ic-sm"><use href="#i-chev-d"/></svg></span>
  1268. </div>
  1269. <div class="mb-body" id="mbChatMsgs" style="display:flex;flex-direction:column;gap:var(--sp-3);padding:var(--sp-4)">
  1270. <div class="msg" style="max-width:88%"><span class="av av-bot av-sm">数</span><div class="bubble" style="font-size:var(--fs-15)">早上好,我已开始处理今天的报表任务。</div></div>
  1271. <div class="msg is-me" style="max-width:88%"><div class="bubble" style="font-size:var(--fs-15)">把第 3 季度的也一起做了</div><span class="av av-user av-sm">胡</span></div>
  1272. <div class="msg" style="max-width:88%"><span class="av av-bot av-sm">数</span><div class="bubble" style="font-size:var(--fs-15)">收到,已排入队列,正在处理第 4 份。</div></div>
  1273. <div class="msg is-me" style="max-width:88%"><div class="bubble" style="font-size:var(--fs-15)">完成后发我一份</div><span class="av av-user av-sm">胡</span></div>
  1274. </div>
  1275. <div class="mb-input">
  1276. <button class="mb-icon-btn" title="发送文件"><svg class="ic"><use href="#i-plus"/></svg></button>
  1277. <textarea class="mb-input-x" rows="1" placeholder="发消息…"></textarea>
  1278. <button class="mb-send" title="发送"><svg class="ic"><use href="#i-send"/></svg></button>
  1279. </div>
  1280. </div>
  1281. <!-- ===== 移动屏 9 · 工作台 ===== -->
  1282. <div class="view" data-view="workbench" hidden>
  1283. <header class="mb-nav">
  1284. <span class="mb-nav-t">工作台</span>
  1285. <button class="mb-icon-btn" title="刷新"><svg class="ic"><use href="#i-refresh"/></svg></button>
  1286. </header>
  1287. <div class="mb-body" style="padding-top:var(--sp-4)">
  1288. <div style="padding:0 var(--sp-4) var(--sp-4);display:grid;grid-template-columns:1fr 1fr 1fr;gap:var(--sp-3)">
  1289. <div class="mb-card" style="text-align:center;padding:var(--sp-3)"><div style="font-size:var(--fs-12);color:var(--muted)">运行中</div><div style="font-size:var(--fs-20);font-weight:750;color:var(--success)">2</div></div>
  1290. <div class="mb-card" style="text-align:center;padding:var(--sp-3)"><div style="font-size:var(--fs-12);color:var(--muted)">空闲</div><div style="font-size:var(--fs-20);font-weight:750;color:var(--warning)">1</div></div>
  1291. <div class="mb-card" style="text-align:center;padding:var(--sp-3)"><div style="font-size:var(--fs-12);color:var(--muted)">异常</div><div style="font-size:var(--fs-20);font-weight:750;color:var(--danger)">1</div></div>
  1292. </div>
  1293. <div style="padding:0 var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3)">
  1294. <article class="mb-card" style="border-left:3px solid var(--danger)">
  1295. <div style="display:flex;align-items:center;gap:var(--sp-3)">
  1296. <span class="av av-bot av-off av-danger">运</span>
  1297. <span style="flex:1;min-width:0"><b style="display:block;font-size:var(--fs-15)">运维值守</b><span style="font-size:var(--fs-12);color:var(--muted)">运维 · 通道异常</span></span>
  1298. <span class="badge badge-d">异常</span>
  1299. </div>
  1300. <div style="margin-top:var(--sp-3);font-size:var(--fs-13);color:var(--danger);display:flex;gap:var(--sp-2)"><svg class="ic ic-sm" style="color:var(--danger)"><use href="#i-alert"/></svg>任务失败:依赖服务不可用,连续失败 41 次</div>
  1301. <div style="margin-top:var(--sp-3);display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3)">
  1302. <div><div class="wcard-f"><span>CPU</span><b>88%</b></div><div class="bar warning"><i style="width:88%"></i></div></div>
  1303. <div><div class="wcard-f"><span>内存</span><b>71%</b></div><div class="bar"><i style="width:71%"></i></div></div>
  1304. </div>
  1305. </article>
  1306. <article class="mb-card">
  1307. <div style="display:flex;align-items:center;gap:var(--sp-3)">
  1308. <span class="av av-bot av-on">数</span>
  1309. <span style="flex:1;min-width:0"><b style="display:block;font-size:var(--fs-15)">数据分析助手</b><span style="font-size:var(--fs-12);color:var(--muted)">数据分析 · 通道正常</span></span>
  1310. <span class="badge badge-s">运行中</span>
  1311. </div>
  1312. <div style="margin-top:var(--sp-3);font-size:var(--fs-13);color:var(--ink-2);display:flex;gap:var(--sp-2)"><svg class="ic ic-sm" style="color:var(--info)"><use href="#i-play"/></svg>正在生成第 4 份季度报表(3:12)</div>
  1313. <div style="margin-top:var(--sp-3);display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3)">
  1314. <div><div class="wcard-f"><span>CPU</span><b>12%</b></div><div class="bar"><i style="width:12%"></i></div></div>
  1315. <div><div class="wcard-f"><span>内存</span><b>34%</b></div><div class="bar"><i style="width:34%"></i></div></div>
  1316. </div>
  1317. </article>
  1318. <article class="mb-card">
  1319. <div style="display:flex;align-items:center;gap:var(--sp-3)">
  1320. <span class="av av-bot av-on">研</span>
  1321. <span style="flex:1;min-width:0"><b style="display:block;font-size:var(--fs-15)">行业研究员</b><span style="font-size:var(--fs-12);color:var(--muted)">研究 · 通道正常</span></span>
  1322. <span class="badge badge-s">运行中</span>
  1323. </div>
  1324. <div style="margin-top:var(--sp-3);font-size:var(--fs-13);color:var(--ink-2);display:flex;gap:var(--sp-2)"><svg class="ic ic-sm" style="color:var(--info)"><use href="#i-play"/></svg>汇总本周行业动态(12:40)</div>
  1325. <div style="margin-top:var(--sp-3);display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3)">
  1326. <div><div class="wcard-f"><span>CPU</span><b>27%</b></div><div class="bar"><i style="width:27%"></i></div></div>
  1327. <div><div class="wcard-f"><span>内存</span><b>45%</b></div><div class="bar"><i style="width:45%"></i></div></div>
  1328. </div>
  1329. </article>
  1330. </div>
  1331. <div style="height:var(--sp-4)"></div>
  1332. </div>
  1333. </div>
  1334. <!-- ===== 移动屏 10 · 通讯录 ===== -->
  1335. <div class="view" data-view="contacts" hidden data-tab-scope>
  1336. <header class="mb-nav"><span class="mb-nav-t">通讯录</span></header>
  1337. <div style="padding:var(--sp-3) var(--sp-4);background:var(--surface)">
  1338. <label class="search" style="height:var(--touch-min)"><svg class="ic ic-sm"><use href="#i-search"/></svg><input placeholder="搜索数字生命 / 同事"></label>
  1339. </div>
  1340. <div class="mb-tabs">
  1341. <button class="chip on" data-panel="mall">全部 7</button>
  1342. <button class="chip" data-panel="mbots">数字生命 4</button>
  1343. <button class="chip" data-panel="mcol">同事 3</button>
  1344. </div>
  1345. <div class="mb-body" data-panel-name="mall">
  1346. <div class="mb-list-h">AI 数字生命</div>
  1347. <button class="mb-cell">
  1348. <span class="av av-bot av-on">数</span>
  1349. <span class="mb-cb"><span class="mb-ct"><span class="nm">数据分析助手</span></span><span class="mb-cd">运行中 · 已接入</span></span>
  1350. <svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-chev-r"/></svg>
  1351. </button>
  1352. <button class="mb-cell warn">
  1353. <span class="av av-bot av-off av-danger">运</span>
  1354. <span class="mb-cb"><span class="mb-ct"><span class="nm">运维值守</span></span><span class="mb-cd" style="color:var(--danger)">异常 · 已接入</span></span>
  1355. <svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-chev-r"/></svg>
  1356. </button>
  1357. <button class="mb-cell">
  1358. <span class="av av-bot av-idle">文</span>
  1359. <span class="mb-cb"><span class="mb-ct"><span class="nm">文案生成器</span></span><span class="mb-cd">空闲 · 已接入</span></span>
  1360. <svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-chev-r"/></svg>
  1361. </button>
  1362. <button class="mb-cell">
  1363. <span class="av av-file"><svg class="ic"><use href="#i-user"/></svg></span>
  1364. <span class="mb-cb"><span class="mb-ct"><span class="nm">行业研究员</span></span><span class="mb-cd">未接入 FMODE · 进详情页</span></span>
  1365. <span class="badge badge-n">详情</span>
  1366. </button>
  1367. <div class="mb-list-h">同事</div>
  1368. <button class="mb-cell" data-expand="mex-li" aria-expanded="false">
  1369. <span class="av av-user av-on">李</span>
  1370. <span class="mb-cb"><span class="mb-ct"><span class="nm">李工</span><span class="badge badge-n">2</span></span><span class="mb-cd">产品经理 · 在线</span></span>
  1371. <svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-chev-r"/></svg>
  1372. </button>
  1373. <div id="mex-li" hidden style="background:var(--surface-2)">
  1374. <button class="mb-cell" style="padding-left:64px"><span class="av av-bot av-on av-sm">数</span><span class="mb-cb"><span class="mb-ct"><span class="nm">数据分析助手</span></span><span class="mb-cd">运行中</span></span></button>
  1375. <button class="mb-cell" style="padding-left:64px"><span class="av av-bot av-idle av-sm">文</span><span class="mb-cb"><span class="mb-ct"><span class="nm">文案生成器</span></span><span class="mb-cd">空闲</span></span></button>
  1376. </div>
  1377. <div style="height:var(--sp-4)"></div>
  1378. </div>
  1379. <div class="mb-body" data-panel-name="mbots" hidden>
  1380. <div class="empty" style="padding:var(--sp-8) var(--sp-6)"><svg class="ic" style="width:40px;height:40px;color:var(--faint)"><use href="#i-users"/></svg><b>数字生命(4)</b><p>Tab 切换后的独立面板(AC-03.1)。同一份数据在此完整呈现。</p></div>
  1381. </div>
  1382. <div class="mb-body" data-panel-name="mcol" hidden>
  1383. <div class="empty" style="padding:var(--sp-8) var(--sp-6)"><svg class="ic" style="width:40px;height:40px;color:var(--faint)"><use href="#i-users"/></svg><b>同事(3)</b><p>同事行可展开其名下数字生命(AC-03.1)。展开态见「全部」Tab 的李工一行。</p></div>
  1384. </div>
  1385. </div>
  1386. <!-- ===== 移动屏 11 · 我的 ===== -->
  1387. <div class="view" data-view="mine" hidden>
  1388. <header class="mb-nav"><span class="mb-nav-t">我的</span></header>
  1389. <div class="mb-body">
  1390. <div class="mb-gutter">
  1391. <div class="mb-card" style="display:flex;align-items:center;gap:var(--sp-4)">
  1392. <span class="av av-user av-round av-on av-lg">胡</span>
  1393. <span style="flex:1;min-width:0">
  1394. <b style="font-size:var(--fs-17);display:block">胡文博</b>
  1395. <span style="font-size:var(--fs-13);color:var(--muted)">138 0000 0000</span><br>
  1396. <span class="badge badge-p" style="margin-top:var(--sp-1)"><svg class="ic ic-sm"><use href="#i-shield"/></svg>超管</span>
  1397. </span>
  1398. </div>
  1399. </div>
  1400. <div class="mb-list-h">账号</div>
  1401. <div class="mb-group">
  1402. <div class="mb-line"><svg class="ic" style="color:var(--muted)"><use href="#i-user"/></svg><span class="k">手机号</span><span class="v">138 0000 0000</span></div>
  1403. <div class="mb-line"><svg class="ic" style="color:var(--muted)"><use href="#i-db"/></svg><span class="k">归属公司</span><span class="v">Fmode 科技</span></div>
  1404. <div class="mb-line"><svg class="ic" style="color:var(--muted)"><use href="#i-key"/></svg><span class="k">修复 Token</span><svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-chev-r"/></svg></div>
  1405. </div>
  1406. <div class="mb-list-h">使用形态</div>
  1407. <div class="mb-group" style="padding:var(--sp-3) var(--sp-4)">
  1408. <div class="seg" style="width:100%">
  1409. <button class="seg-i on" style="flex:1;text-align:center">自动识别</button>
  1410. <button class="seg-i" style="flex:1;text-align:center">桌面端</button>
  1411. <button class="seg-i" style="flex:1;text-align:center">移动端</button>
  1412. </div>
  1413. <p style="font-size:var(--fs-12);color:var(--muted);margin-top:var(--sp-3);line-height:1.7">当前:移动端(UA + 视口判定)。端切换为一次性判定,缩放窗口不改变端(R1)。</p>
  1414. </div>
  1415. <div class="mb-gutter">
  1416. <button class="btn btn-danger btn-block"><svg class="ic ic-sm"><use href="#i-logout"/></svg>退出登录</button>
  1417. <p style="font-size:var(--fs-12);color:var(--faint);text-align:center;margin-top:var(--sp-3)">退出需二次确认(AC-10.2)</p>
  1418. </div>
  1419. <p style="font-size:var(--fs-12);color:var(--faint);text-align:center;padding-bottom:var(--sp-5)">版本 v2.0.0 · 构建注入(AC-10.3)</p>
  1420. </div>
  1421. </div>
  1422. <!-- 未覆盖模块占位 -->
  1423. <div class="view" data-view="stub" hidden>
  1424. <header class="mb-nav"><span class="mb-nav-t" id="mbStubTitle">该模块</span></header>
  1425. <div class="mb-body">
  1426. <div class="empty" style="padding:var(--sp-8) var(--sp-6)"><svg class="ic" style="width:44px;height:44px;color:var(--faint)"><use href="#i-folder"/></svg><b>本 demo 未覆盖</b><p>SPEC 中已有 FR 定义(非砍功能),阶段②B 补齐。</p></div>
  1427. </div>
  1428. </div>
  1429. <!-- 底部 TabBar(5 Tab · 老项目一致) -->
  1430. <nav class="mb-tabbar">
  1431. <button class="mb-tab on" data-mod="home"><svg class="ic"><use href="#i-grid"/></svg><span>首页</span></button>
  1432. <button class="mb-tab" data-mod="contacts"><svg class="ic"><use href="#i-users"/></svg><span>通讯录</span></button>
  1433. <button class="mb-tab" data-mod="projects"><svg class="ic"><use href="#i-folder"/></svg><span>项目</span></button>
  1434. <button class="mb-tab has-dot" data-mod="workbench"><svg class="ic"><use href="#i-grid"/></svg><span>工作台</span></button>
  1435. <button class="mb-tab" data-mod="mine"><svg class="ic"><use href="#i-user"/></svg><span>我的</span></button>
  1436. </nav>
  1437. <div class="mb-scrim" id="mbScrim" data-drawer="close"></div>
  1438. <!-- 成果抽屉(AC-04.1 移动全屏 + 成果抽屉) -->
  1439. <aside class="mb-drawer" id="mbDrawer" aria-label="会话成果">
  1440. <div class="mb-drawer-h">
  1441. <b>本会话成果</b>
  1442. <span class="badge badge-n">3</span>
  1443. <button class="mb-icon-btn" data-drawer="close" title="关闭"><svg class="ic"><use href="#i-chev-d"/></svg></button>
  1444. </div>
  1445. <div class="mb-drawer-b">
  1446. <button class="mb-cell" style="border-bottom:1px solid var(--edge)">
  1447. <span class="av av-file"><svg class="ic"><use href="#i-file"/></svg></span>
  1448. <span class="mb-cb"><span class="mb-ct"><span class="nm">Q2 季度报表.pdf</span></span><span class="mb-cd">1.2 MB · 10:12</span></span>
  1449. <svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-download"/></svg>
  1450. </button>
  1451. <button class="mb-cell" style="border-bottom:1px solid var(--edge)">
  1452. <span class="av av-file"><svg class="ic"><use href="#i-file"/></svg></span>
  1453. <span class="mb-cb"><span class="mb-ct"><span class="nm">销售趋势分析.xlsx</span></span><span class="mb-cd">340 KB · 昨天</span></span>
  1454. <svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-download"/></svg>
  1455. </button>
  1456. <button class="mb-cell">
  1457. <span class="av av-file"><svg class="ic"><use href="#i-image"/></svg></span>
  1458. <span class="mb-cb"><span class="mb-ct"><span class="nm">渠道转化漏斗图(4 张)</span></span><span class="mb-cd">图片组 · 昨天</span></span>
  1459. <svg class="ic ic-sm" style="color:var(--faint)"><use href="#i-eye"/></svg>
  1460. </button>
  1461. </div>
  1462. </aside>
  1463. </div>
  1464. </div>
  1465. <div class="notes">
  1466. <div class="notes-h"><svg class="ic"><use href="#i-eye"/></svg>移动端设计依据</div>
  1467. <div class="note">
  1468. <span class="ref">FR-19</span><span class="ref">AC-19.1</span><span class="ref">AC-19.3</span><b>聚合首页</b>:事件提醒条 + 最近会话 + 最新成果,各带「全部」入口;<b>区块计数与列表条数一致</b>(修正存量"显示 20 却只有 6 行");每个区块独立三态;搜索框字号 ≥16px(iOS 不缩放)。
  1469. </div>
  1470. <div class="note ok">
  1471. <span class="ref">FR-04</span><span class="ref">AC-04.1</span><b>会话</b>:全屏 + 成果抽屉(点右上角 📦 打开)。L2 状态条压缩成一行,仍是<b>唯一</b>渲染「在干嘛」的地方。
  1472. </div>
  1473. <div class="note">
  1474. <span class="ref">FR-13</span><span class="ref">AC-13.3</span><b>无障碍</b>:所有交互元素 ≥44×44(Tab / 图标按钮 / 列表行);列表行高 64px(双行)• 头像 44px;顶栏 56px + 底栏 56px + 安全区。
  1475. </div>
  1476. <div class="note ok">
  1477. <span class="ref">规范</span><b>移动端布局校验(本轮修正)</b>:修复设备外框被硬编码 844px 高、把底部 TabBar 连同内容一并裁掉的问题;列表头像按要求提到 44px;单行行高归位 56px(<code>--mb-list-h-1</code>);页面左右边距统一走 <code>--mb-gutter</code> 16px;会话为全屏详情,该视图激活时收起 TabBar。
  1478. </div>
  1479. <div class="note warn">
  1480. <span class="ref">FR-10</span><span class="ref">AC-10.2</span><span class="ref">AC-10.3</span><b>我的</b>:端切换(自动 / 桌面 / 移动)+ 修复 Token + 退出(需二次确认);版本号从构建注入,禁止硬编码。
  1481. </div>
  1482. </div>
  1483. </section>
  1484. </main>
  1485. </div>
  1486. <script>
  1487. (function(){
  1488. var root = document.documentElement;
  1489. var stage = document.getElementById('stage');
  1490. var DK_LABEL = { chat:'会话', contacts:'通讯录', projects:'项目', events:'事件',
  1491. workbench:'工作台', tasks:'任务看板', deliverables:'成果', mine:'我的' };
  1492. var MB_LABEL = { home:'首页', chat:'会话', contacts:'通讯录', projects:'项目',
  1493. workbench:'工作台', mine:'我的' };
  1494. var screens = {};
  1495. document.querySelectorAll('.screen').forEach(function(s){ screens[s.dataset.end] = s; });
  1496. /* 在指定壳内切换视图;模块未实现时落到占位屏 */
  1497. function showView(shellSel, mod, label){
  1498. var shell = document.querySelector(shellSel);
  1499. if(!shell) return;
  1500. var found = false;
  1501. shell.querySelectorAll('.view').forEach(function(v){
  1502. if(v.dataset.view === 'stub') return;
  1503. var hit = v.dataset.view === mod;
  1504. if(hit) found = true;
  1505. v.hidden = !hit;
  1506. });
  1507. var stub = shell.querySelector('.view[data-view="stub"]');
  1508. if(stub){
  1509. stub.hidden = found;
  1510. if(!found){
  1511. var t = stub.querySelector('b, .mb-nav-t, #stubTitle, #mbStubTitle');
  1512. if(t) t.textContent = label || '该模块';
  1513. }
  1514. }
  1515. }
  1516. /* 桌面端 1440×900 自适应装进舞台 */
  1517. function fitDk(){
  1518. var dev = document.getElementById('dkDevice');
  1519. var box = document.getElementById('dkBox');
  1520. var dk = document.getElementById('dkShell');
  1521. if(!dev || !box || !dk) return;
  1522. var availW = stage.clientWidth - 48;
  1523. var availH = stage.clientHeight - 48;
  1524. var scale = Math.max(.3, Math.min(1, availW / 1440, availH / 900));
  1525. dk.style.transform = 'scale(' + scale + ')';
  1526. box.style.width = (1440 * scale) + 'px';
  1527. box.style.height = (900 * scale) + 'px';
  1528. dev.style.width = (1440 * scale + 2) + 'px';
  1529. dev.style.maxWidth = 'none';
  1530. }
  1531. /* 总切换:端 + 模块 */
  1532. function activate(end, mod){
  1533. Object.keys(screens).forEach(function(k){
  1534. if(screens[k]) screens[k].classList.toggle('is-on', k === end);
  1535. });
  1536. document.querySelectorAll('.tool-i').forEach(function(b){ b.classList.toggle('on', b.dataset.end === end); });
  1537. document.querySelectorAll('.nav-i').forEach(function(b){
  1538. b.classList.toggle('on', b.dataset.end === end && (end === 'login' || b.dataset.mod === mod));
  1539. });
  1540. if(end === 'dk'){
  1541. showView('#dkShell', mod, DK_LABEL[mod]);
  1542. document.querySelectorAll('#dkShell .rail-i').forEach(function(b){
  1543. b.classList.toggle('on', b.dataset.mod === mod);
  1544. });
  1545. fitDk();
  1546. }
  1547. if(end === 'mb'){
  1548. showView('#mbShell', mod, MB_LABEL[mod]);
  1549. document.querySelectorAll('#mbShell .mb-tab').forEach(function(b){
  1550. b.classList.toggle('on', b.dataset.mod === mod);
  1551. });
  1552. }
  1553. stage.scrollTop = 0;
  1554. }
  1555. window.addEventListener('resize', fitDk);
  1556. /* 左侧清单 + 顶栏端点 */
  1557. document.querySelectorAll('.nav-i, .tool-i').forEach(function(b){
  1558. b.addEventListener('click', function(){ activate(b.dataset.end, b.dataset.mod || 'chat'); });
  1559. });
  1560. /* 桌面导航轨(7 模块 + 我的) */
  1561. document.querySelectorAll('#dkShell .rail-i').forEach(function(b){
  1562. b.addEventListener('click', function(){ activate('dk', b.dataset.mod); });
  1563. });
  1564. /* 移动 TabBar */
  1565. document.querySelectorAll('#mbShell .mb-tab').forEach(function(b){
  1566. b.addEventListener('click', function(){ activate('mb', b.dataset.mod); });
  1567. });
  1568. /* 列表行选中 */
  1569. document.querySelectorAll('.pane-list .row, .mb-cell').forEach(function(r){
  1570. r.addEventListener('click', function(){
  1571. if(r.hasAttribute('data-drawer')) return;
  1572. var box = r.parentNode;
  1573. box.querySelectorAll(':scope > .on').forEach(function(x){ x.classList.remove('on'); });
  1574. r.classList.add('on');
  1575. });
  1576. });
  1577. document.querySelectorAll('.task').forEach(function(t){
  1578. t.addEventListener('click', function(){
  1579. document.querySelectorAll('.task').forEach(function(x){ x.classList.remove('on'); });
  1580. t.classList.add('on');
  1581. });
  1582. });
  1583. /* 分段器 */
  1584. document.querySelectorAll('.seg').forEach(function(seg){
  1585. seg.querySelectorAll('.seg-i').forEach(function(i){
  1586. i.addEventListener('click', function(){
  1587. seg.querySelectorAll('.seg-i').forEach(function(x){ x.classList.remove('on'); });
  1588. i.classList.add('on');
  1589. if(!i.dataset.panel) return;
  1590. var scope = seg.closest('[data-seg-scope]') || document;
  1591. scope.querySelectorAll('[data-panel-name]').forEach(function(p){
  1592. p.hidden = p.dataset.panelName !== i.dataset.panel;
  1593. });
  1594. });
  1595. });
  1596. });
  1597. /* 标签页(桌面) */
  1598. document.querySelectorAll('.tabs').forEach(function(tb){
  1599. tb.querySelectorAll('.tab').forEach(function(t){
  1600. t.addEventListener('click', function(){
  1601. tb.querySelectorAll('.tab').forEach(function(x){ x.classList.remove('on'); });
  1602. t.classList.add('on');
  1603. if(!t.dataset.panel) return;
  1604. var scope = tb.closest('[data-tab-scope]') || document;
  1605. scope.querySelectorAll('[data-panel-name]').forEach(function(p){
  1606. p.hidden = p.dataset.panelName !== t.dataset.panel;
  1607. });
  1608. });
  1609. });
  1610. });
  1611. /* 移动筛选 Tab */
  1612. document.querySelectorAll('.mb-tabs').forEach(function(tb){
  1613. tb.querySelectorAll('.chip').forEach(function(c){
  1614. c.addEventListener('click', function(){
  1615. tb.querySelectorAll('.chip').forEach(function(x){ x.classList.remove('on'); });
  1616. c.classList.add('on');
  1617. var scope = tb.closest('[data-tab-scope]') || document;
  1618. scope.querySelectorAll('[data-panel-name]').forEach(function(p){
  1619. p.hidden = p.dataset.panelName !== c.dataset.panel;
  1620. });
  1621. });
  1622. });
  1623. });
  1624. /* 状态筛选胶囊(任务看板) */
  1625. document.querySelectorAll('.chips').forEach(function(box){
  1626. box.querySelectorAll('.chip').forEach(function(c){
  1627. c.addEventListener('click', function(){
  1628. box.querySelectorAll('.chip').forEach(function(x){ x.classList.remove('on'); });
  1629. c.classList.add('on');
  1630. var cat = c.dataset.cat;
  1631. var scope = box.closest('[data-filter-scope]') || document;
  1632. scope.querySelectorAll('[data-cat]').forEach(function(item){
  1633. if(item.classList.contains('chip')) return;
  1634. item.hidden = (cat !== 'all' && item.dataset.cat !== cat);
  1635. });
  1636. });
  1637. });
  1638. });
  1639. /* 发送消息(Enter 发送 / Shift+Enter 换行 / 真实自增高) */
  1640. function bindComposer(inSel, listSel, withAvatar){
  1641. document.querySelectorAll(inSel).forEach(function(input){
  1642. var list = document.querySelector(listSel);
  1643. if(!list) return;
  1644. input.addEventListener('keydown', function(e){
  1645. if(e.key !== 'Enter' || e.shiftKey) return;
  1646. e.preventDefault();
  1647. var v = input.value.trim(); if(!v) return;
  1648. var el = document.createElement('div');
  1649. el.className = 'msg is-me';
  1650. el.style.maxWidth = '88%';
  1651. el.innerHTML = '<div class="bubble" style="font-size:var(--fs-15)"></div>' + (withAvatar ? '<span class="av av-user av-sm">胡</span>' : '<span class="av av-user av-sm">胡</span>');
  1652. el.querySelector('.bubble').textContent = v;
  1653. list.appendChild(el);
  1654. input.value = ''; input.style.height = '';
  1655. list.scrollTop = list.scrollHeight;
  1656. });
  1657. input.addEventListener('input', function(){
  1658. input.style.height = 'auto';
  1659. input.style.height = Math.min(input.scrollHeight, 132) + 'px';
  1660. });
  1661. });
  1662. }
  1663. bindComposer('.composer-input', '#dkChatMsgs', false);
  1664. bindComposer('.mb-input-x', '#mbChatMsgs', true);
  1665. /* 移动成果抽屉 */
  1666. var drawer = document.getElementById('mbDrawer');
  1667. var scrim = document.getElementById('mbScrim');
  1668. function setDrawer(open){
  1669. if(!drawer) return;
  1670. drawer.classList.toggle('open', open);
  1671. if(scrim) scrim.classList.toggle('on', open);
  1672. }
  1673. document.querySelectorAll('[data-drawer="open"]').forEach(function(b){
  1674. b.addEventListener('click', function(e){ e.stopPropagation(); setDrawer(true); });
  1675. });
  1676. document.querySelectorAll('[data-drawer="close"]').forEach(function(b){
  1677. b.addEventListener('click', function(){ setDrawer(false); });
  1678. });
  1679. /* 同事展开名下数字生命 */
  1680. document.querySelectorAll('[data-expand]').forEach(function(b){
  1681. b.addEventListener('click', function(e){
  1682. e.stopPropagation();
  1683. var t = document.getElementById(b.dataset.expand);
  1684. if(!t) return;
  1685. var open = t.hidden;
  1686. t.hidden = !open;
  1687. b.setAttribute('aria-expanded', String(open));
  1688. var chev = b.querySelector('svg.ic');
  1689. if(chev && chev !== b.querySelector('span .ic')) chev.style.transform = open ? 'rotate(90deg)' : '';
  1690. });
  1691. });
  1692. /* 登录方式切换 */
  1693. document.querySelectorAll('.login-tabs .tab').forEach(function(t){
  1694. t.addEventListener('click', function(){
  1695. document.querySelectorAll('[data-login-method]').forEach(function(p){
  1696. p.hidden = p.dataset.loginMethod !== t.dataset.method;
  1697. });
  1698. });
  1699. });
  1700. /* 明暗 / 标注 */
  1701. document.getElementById('btnTheme').addEventListener('click', function(){
  1702. root.classList.toggle('dark');
  1703. this.querySelector('use').setAttribute('href', root.classList.contains('dark') ? '#i-sun' : '#i-moon');
  1704. });
  1705. document.getElementById('btnNotes').addEventListener('click', function(){
  1706. document.body.classList.toggle('show-notes');
  1707. this.classList.toggle('on');
  1708. });
  1709. /* 路由:支持 #<端>/<模块> 直达(评审截图与分享定位用) */
  1710. function fromHash(){
  1711. var h = (location.hash || '').replace(/^#/, '');
  1712. if(!h) return null;
  1713. var p = h.split('/');
  1714. if(!screens[p[0]]) return null;
  1715. return { end: p[0], mod: p[1] || (p[0] === 'login' ? 'login' : 'chat') };
  1716. }
  1717. window.addEventListener('hashchange', function(){
  1718. var t = fromHash(); if(t) activate(t.end, t.mod);
  1719. });
  1720. /* 启动 */
  1721. var t0 = fromHash();
  1722. activate(t0 ? t0.end : 'login', t0 ? t0.mod : 'login');
  1723. })();
  1724. </script>
  1725. </body>
  1726. </html>