ui-dashboard.html
10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" />
<title>DevLoop Dashboard · LemonBoss PRD</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,500;9..144,700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="assets/shell.css" />
</head>
<body data-page="ui-dashboard.html">
<div class="shell">
<aside id="sidebar"></aside>
<main class="page">
<header class="page-head">
<div class="crumb"><span class="pill">07 · UI</span><span class="sep">/</span><span>DevLoop Dashboard</span></div>
<h1>DevLoop Dashboard</h1>
<p class="deck">前端只有一个新页面:<code>DevLoopDashboard.vue</code>。复用 <code>PipelineEditor.vue</code> 的 DAG 渲染层,再套一个三层 Loop 状态条。</p>
</header>
<article class="body">
<section>
<h2>Wireframe</h2>
<div class="mock">
<div class="topbar">
<span class="dot" style="background:#FF5F57"></span>
<span class="dot" style="background:#FEBC2E"></span>
<span class="dot" style="background:#28C840"></span>
<span class="url">localhost:5173/devloop/p-731042</span>
</div>
<svg viewBox="0 0 1100 540" width="100%" role="img" aria-label="DevLoop Dashboard wireframe">
<defs>
<linearGradient id="card-g" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#fff"/>
<stop offset="100%" stop-color="#FAF7F1"/>
</linearGradient>
<marker id="arrU" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M0,0 L10,5 L0,10 Z" fill="#0E0E10"/>
</marker>
</defs>
<rect x="0" y="0" width="1100" height="60" fill="#0E0E10"/>
<text x="20" y="38" font-family="Fraunces" font-size="22" fill="#FAF7F1">Project p-731042 · Auto Auth System</text>
<rect x="900" y="18" width="180" height="28" rx="14" fill="#6E48C3"/>
<text x="990" y="37" font-family="JetBrains Mono" font-size="11" fill="#fff" text-anchor="middle">+ NEW REQUIREMENT</text>
<g font-family="JetBrains Mono" font-size="11">
<rect x="20" y="80" width="340" height="60" rx="6" fill="url(#card-g)" stroke="#0E8C5A"/>
<text x="34" y="100" fill="#0E8C5A" font-weight="700">L1 · UNIT</text>
<text x="34" y="120" fill="#0E0E10">8/12 passed · 1 testing · 3 pending</text>
<rect x="380" y="80" width="340" height="60" rx="6" fill="url(#card-g)" stroke="#C77C2C"/>
<text x="394" y="100" fill="#C77C2C" font-weight="700">L2 · INTEGRATION</text>
<text x="394" y="120" fill="#0E0E10">last run · failed at 06:21 · re-queued</text>
<rect x="740" y="80" width="340" height="60" rx="6" fill="url(#card-g)" stroke="#6E48C3"/>
<text x="754" y="100" fill="#6E48C3" font-weight="700">L3 · HUMAN</text>
<text x="754" y="120" fill="#0E0E10">rev 3 · awaiting next feedback</text>
</g>
<text x="20" y="170" font-family="JetBrains Mono" font-size="11" fill="#3B3B40">UNITS DAG</text>
<g font-family="JetBrains Mono" font-size="10">
<rect x="40" y="190" width="120" height="44" rx="4" fill="#E2F2EA" stroke="#0E8C5A"/><text x="100" y="208" text-anchor="middle">auth-be</text><text x="100" y="222" text-anchor="middle" fill="#0E8C5A">passed</text>
<rect x="220" y="190" width="120" height="44" rx="4" fill="#FFF6CC" stroke="#8a6b00"/><text x="280" y="208" text-anchor="middle">auth-fe</text><text x="280" y="222" text-anchor="middle" fill="#8a6b00">testing</text>
<rect x="400" y="190" width="120" height="44" rx="4" fill="#E2F2EA" stroke="#0E8C5A"/><text x="460" y="208" text-anchor="middle">profile-be</text><text x="460" y="222" text-anchor="middle" fill="#0E8C5A">passed</text>
<rect x="220" y="280" width="120" height="44" rx="4" fill="#fff" stroke="#0E0E10"/><text x="280" y="298" text-anchor="middle">oauth-be</text><text x="280" y="312" text-anchor="middle" fill="#3B3B40">pending</text>
<rect x="400" y="280" width="120" height="44" rx="4" fill="#fff" stroke="#0E0E10"/><text x="460" y="298" text-anchor="middle">oauth-fe</text><text x="460" y="312" text-anchor="middle" fill="#3B3B40">pending</text>
<rect x="580" y="190" width="120" height="44" rx="4" fill="#FCE7E2" stroke="#C7341A"/><text x="640" y="208" text-anchor="middle">session-mw</text><text x="640" y="222" text-anchor="middle" fill="#C7341A">failed × 2</text>
</g>
<g stroke="#0E0E10" stroke-width="1.2" fill="none" marker-end="url(#arrU)">
<line x1="160" y1="212" x2="215" y2="212"/>
<line x1="340" y1="212" x2="395" y2="212"/>
<line x1="520" y1="212" x2="575" y2="212"/>
<line x1="160" y1="220" x2="215" y2="290"/>
<line x1="340" y1="290" x2="395" y2="290"/>
</g>
<rect x="20" y="360" width="700" height="160" rx="6" fill="#0E0E10"/>
<text x="36" y="382" font-family="JetBrains Mono" font-size="11" fill="#86827a">LIVE · agent: tester · unit: auth-fe</text>
<g font-family="JetBrains Mono" font-size="11" fill="#F3EFE5">
<text x="36" y="406">› vitest run tests/login.spec.ts</text>
<text x="36" y="424"><tspan fill="#A0E8B9">PASS</tspan> tests/login.spec.ts > valid creds</text>
<text x="36" y="442"><tspan fill="#FF8A6E">FAIL</tspan> tests/login.spec.ts > invalid pwd</text>
<text x="36" y="460"> Expected: 401 Received: 500</text>
<text x="36" y="486" fill="#FFA94D">↻ retry budget 1/2 · re-dispatch coder…</text>
</g>
<rect x="740" y="360" width="340" height="160" rx="6" fill="#fff" stroke="#dcd5c4"/>
<text x="756" y="382" font-family="JetBrains Mono" font-size="11" fill="#3B3B40">PRD · rev 3 · diff vs rev 2</text>
<g font-family="JetBrains Mono" font-size="11">
<text x="756" y="406" fill="#0E8C5A">+ Section: OAuth login (GitHub)</text>
<text x="756" y="424" fill="#0E8C5A">+ NFR: 3rd-party token refresh ≤ 5s</text>
<text x="756" y="442" fill="#C7341A">– 旧: 仅支持邮箱密码登录</text>
<text x="756" y="468" fill="#3B3B40">[open full →]</text>
</g>
</svg>
</div>
</section>
<section>
<h2>四个核心区域</h2>
<div class="grid-2">
<div class="card l3">
<h4>① Header</h4>
<p>项目名 + 「+ NEW REQUIREMENT」按钮(L3 入口)。点开弹 textarea,提交后走 <code>POST .../requirement</code>。</p>
</div>
<div class="card">
<h4>② Loop 状态栏</h4>
<p>三张并排卡片显示 L1 / L2 / L3 当前状态。点击任意一张展开该 Loop 的详细历史时间线。</p>
</div>
<div class="card l1">
<h4>③ Units DAG</h4>
<p>复用 <code>PipelineEditor.vue</code> 的 SVG DAG 渲染。每个节点点击展开「该 Unit 的最近 3 次 run」抽屉。</p>
</div>
<div class="card l2">
<h4>④ Live 流 + PRD diff</h4>
<p>左:终端样式的 Agent stdout 流(SSE)。右:当前 PRD 与上一 rev 的 section 级 diff。</p>
</div>
</div>
</section>
<section>
<h2>组件树</h2>
<pre class="code"><span class="c">// web/src/views/DevLoopDashboard.vue</span>
<span class="k"><template></span>
<span class="k"><PageLayout></span>
<span class="k"><DashboardHeader</span> :project=<span class="s">"project"</span> @feedback=<span class="s">"onFeedback"</span> <span class="k">/></span>
<span class="k"><LoopStatusBar</span> :l1=<span class="s">"l1Stats"</span> :l2=<span class="s">"l2Stats"</span> :l3=<span class="s">"l3Stats"</span> <span class="k">/></span>
<span class="k"><UnitsDAG</span> :units=<span class="s">"units"</span> @select=<span class="s">"openUnitDrawer"</span> <span class="k">/></span>
<span class="k"><LiveAgentStream</span> :stream=<span class="s">"sse"</span> <span class="k">/></span>
<span class="k"><PrdDiffPanel</span> :prd=<span class="s">"prdHtml"</span> :history=<span class="s">"prdHistory"</span> <span class="k">/></span>
<span class="k"><UnitDrawer</span> v-if=<span class="s">"drawer"</span> :unit=<span class="s">"drawer"</span> @close=<span class="s">"drawer=null"</span> <span class="k">/></span>
<span class="k"><FeedbackModal</span> v-if=<span class="s">"modalOpen"</span> @submit=<span class="s">"sendRequirement"</span> <span class="k">/></span>
<span class="k"></PageLayout></span>
<span class="k"></template></span></pre>
<p class="sig">总行数预估 ~400,含模板 + script + 样式。其中 UnitsDAG 大约 120 行——这里有最高的复用率(参考 PipelineEditor)。</p>
</section>
<section>
<h2>路由 + i18n</h2>
<p><b>路由:</b><code>{ path: '/devloop/:pid', name: 'DevLoop', component: DevLoopDashboard }</code> 加进 <code>web/src/router/index.js</code>。</p>
<p><b>i18n:</b>新增 key 前缀 <code>devloop.*</code>。涉及的标签:<code>loop.l1</code> · <code>loop.l2</code> · <code>loop.l3</code> · <code>state.passed</code> · <code>state.testing</code> · <code>btn.newRequirement</code> 等 ~20 个。中英文必填,其他 4 种语言可走 fallback。</p>
</section>
<section>
<h2>性能考虑</h2>
<ul>
<li><b>SSE:</b>使用浏览器原生 <code>EventSource</code>,断线自动重连。</li>
<li><b>大 stream:</b>LiveAgentStream 保留最近 500 行,老的丢;用户想看完整日志走「open in new tab」按钮跳到 <code>/stream.log</code>。</li>
<li><b>PRD diff:</b>使用客户端 <code>diff-dom</code>,不在后端计算(项目 PRD 一般 < 50KB,浏览器算得动)。</li>
<li><b>DAG 大于 50 Unit:</b>切换到 d3-force 自动布局,否则手动栅格。</li>
</ul>
</section>
</article>
<nav class="prev-next"></nav>
<footer class="site"><div><b>Section</b>07 · UI</div><div><b>Page</b>ui-dashboard.html</div></footer>
</main>
</div>
<script src="assets/shell.js"></script>
</body>
</html>