speccore 8.3.137 → 8.3.148
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +24 -11
- package/dist/cli.js.map +1 -1
- package/dist/commands/about.d.ts.map +1 -1
- package/dist/commands/about.js +4 -0
- package/dist/commands/about.js.map +1 -1
- package/dist/commands/completion.js +2 -2
- package/dist/commands/completion.js.map +1 -1
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +23 -2
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/help.js +13 -13
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +9 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/review.d.ts +26 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +369 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/task/new.d.ts +1 -0
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +34 -24
- package/dist/commands/task/new.js.map +1 -1
- package/dist/commands/trace.js +1 -1
- package/dist/commands/trace.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +8 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +50 -4
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/intent-recognition.js +5 -5
- package/dist/core/intent-recognition.js.map +1 -1
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +79 -44
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/rag-engine.d.ts +14 -0
- package/dist/core/rag-engine.d.ts.map +1 -1
- package/dist/core/rag-engine.js +58 -5
- package/dist/core/rag-engine.js.map +1 -1
- package/package.json +3 -2
- package/templates/api-design-example.md +338 -0
- package/templates/ci/github-actions.yml +44 -0
- package/templates/dashboard.html +425 -0
- package/templates/database-example.md +223 -0
- package/templates/deploy-docker/README.md +284 -0
- package/templates/deploy-docker/deploy-remote.sh +79 -0
- package/templates/deploy-docker/docker-compose.yml +113 -0
- package/templates/deploy-docker/frontend.Dockerfile +43 -0
- package/templates/deploy-docker/nginx.conf +30 -0
- package/templates/deploy-examples.yaml +436 -0
- package/templates/deploy-java/Dockerfile +50 -0
- package/templates/deploy-java/README.md +212 -0
- package/templates/deploy-java/deploy.sh +120 -0
- package/templates/deploy-java/spring-boot.service +35 -0
- package/templates/html/speccore-ask-explain.html +10 -0
- package/templates/html/speccore-ask-guide.html +10 -0
- package/templates/html/speccore-ask-match.html +10 -0
- package/templates/html/speccore-ask-pipeline.html +1 -0
- package/templates/html/speccore-ask-result.html +10 -0
- package/templates/html/speccore-demo.html +449 -0
- package/templates/html/speccore-dev.html +202 -0
- package/templates/html/speccore-help.html +1 -0
- package/templates/html/speccore-knowledge-graph.html +656 -0
- package/templates/html/speccore-retro-T-001.html +1 -0
- package/templates/html/speccore-setup-guide.html +289 -0
- package/templates/html/speccore-welcome.html +1 -0
- package/templates/security-example.md +226 -0
- package/templates/verify-spec-example.yaml +138 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN" data-theme="ocean">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5
|
+
<title>SpecCore — 全量仪表盘</title>
|
|
6
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
7
|
+
<style>
|
|
8
|
+
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&family=JetBrains+Mono:wght@400;600;700&display=swap');
|
|
9
|
+
[data-theme="cyber"]{--cyan:#00f0ff;--blue:#3b82f6;--green:#00ff88;--purple:#a78bfa;--orange:#f59e0b;--bg:#060b14;--card:rgba(8,16,32,.85);--border:rgba(0,240,255,.12);--text:#c4d5e7;--muted:#4a5568;--surface:rgba(17,25,40,.7);--hover:rgba(0,240,255,.02)}
|
|
10
|
+
[data-theme="light"]{--cyan:#2563eb;--blue:#3b82f6;--green:#059669;--purple:#7c3aed;--orange:#d97706;--bg:#f8fafc;--card:rgba(255,255,255,.95);--border:rgba(0,0,0,.08);--text:#1e293b;--muted:#94a3b8;--surface:rgba(255,255,255,.95);--hover:rgba(37,99,235,.02)}
|
|
11
|
+
[data-theme="github"]{--cyan:#2da44e;--blue:#0969da;--green:#1a7f37;--purple:#8250df;--orange:#cf222e;--bg:#0d1117;--card:#161b22;--border:#30363d;--text:#c9d1d9;--muted:#8b949e;--surface:#161b22;--hover:rgba(45,164,78,.05)}
|
|
12
|
+
[data-theme="synth"]{--cyan:#f92aad;--blue:#ff7edb;--green:#36f9f6;--purple:#b381f9;--orange:#ffb86c;--bg:#1a0024;--card:rgba(30,0,50,.9);--border:rgba(249,42,173,.2);--text:#f8f8f2;--muted:#6b3e7a;--surface:rgba(30,0,50,.9);--hover:rgba(249,42,173,.05)}
|
|
13
|
+
[data-theme="ocean"]{--cyan:#0ea5e9;--blue:#0284c7;--green:#14b8a6;--purple:#6366f1;--orange:#f97316;--bg:#0b1929;--card:rgba(13,31,56,.9);--border:rgba(14,165,233,.15);--text:#bae6fd;--muted:#5b7fa5;--surface:rgba(13,31,56,.9);--hover:rgba(14,165,233,.05)}
|
|
14
|
+
[data-theme="sakura"]{--cyan:#f472b6;--blue:#c084fc;--green:#a3e635;--purple:#e879f9;--orange:#fb923c;--bg:#1a0a14;--card:rgba(30,10,20,.9);--border:rgba(244,114,182,.2);--text:#fce7f3;--muted:#9d4a6d;--surface:rgba(30,10,20,.9);--hover:rgba(244,114,182,.05)}
|
|
15
|
+
[data-theme="forest"]{--cyan:#059669;--blue:#0284c7;--green:#22c55e;--purple:#6366f1;--orange:#ca8a04;--bg:#0a140a;--card:rgba(12,24,12,.9);--border:rgba(5,150,105,.15);--text:#d1fae5;--muted:#3b6b4e;--surface:rgba(12,24,12,.9);--hover:rgba(5,150,105,.05)}
|
|
16
|
+
[data-theme="amber"]{--cyan:#ffb000;--blue:#ff8c00;--green:#32cd32;--purple:#da70d6;--orange:#ff6347;--bg:#0c0c0c;--card:#1a1a1a;--border:#333;--text:#ffb000;--muted:#666;--surface:#1a1a1a;--hover:rgba(255,176,0,.05)}
|
|
17
|
+
[data-theme="mono"]{--cyan:#64748b;--blue:#475569;--green:#334155;--purple:#1e293b;--orange:#94a3b8;--bg:#0f172a;--card:#1e293b;--border:#334155;--text:#e2e8f0;--muted:#64748b;--surface:#1e293b;--hover:rgba(100,116,139,.05)}
|
|
18
|
+
[data-theme="light"] table th{border-bottom-color:rgba(0,0,0,.15)!important}
|
|
19
|
+
[data-theme="light"] table td{border-bottom-color:rgba(0,0,0,.08)!important}
|
|
20
|
+
[data-theme="light"] .card,[data-theme="light"] .panel,[data-theme="light"] .header,[data-theme="light"] .stat-card,[data-theme="light"] .chart-card{border-color:rgba(0,0,0,.1)!important}
|
|
21
|
+
[data-theme="light"] .card:hover,[data-theme="light"] .panel:hover,[data-theme="light"] .stat-card:hover,[data-theme="light"] .chart-card:hover{border-color:rgba(37,99,235,.25)!important;box-shadow:0 0 24px rgba(37,99,235,.06),inset 0 0 24px rgba(37,99,235,.02)!important}
|
|
22
|
+
[data-theme="light"] .footer{border-top-color:rgba(0,0,0,.08)!important}
|
|
23
|
+
[data-theme="light"] .scanlines{opacity:.03!important}.stars{opacity:.02!important}
|
|
24
|
+
[data-theme="light"] .fs-btn{background:rgba(0,0,0,.03)!important;border-color:rgba(0,0,0,.08)!important}
|
|
25
|
+
[data-theme="light"] .data-stream span{color:rgba(0,0,0,.04)!important}
|
|
26
|
+
[data-theme="light"] .theme-sw,.lang-sw{border-color:rgba(0,0,0,.1)!important}
|
|
27
|
+
[data-theme="light"] tr:hover td{background:rgba(0,0,0,.02)!important}
|
|
28
|
+
[data-theme="light"] .header-stat{border-left-color:rgba(0,0,0,.1)!important}
|
|
29
|
+
[data-theme="light"] .panel-title::before{text-shadow:none!important}
|
|
30
|
+
[data-theme="light"] .grid-pattern{opacity:.3!important}
|
|
31
|
+
[data-fs="sm"]{font-size:13px}[data-fs="md"]{font-size:15px}[data-fs="lg"]{font-size:17px}[data-fs="xl"]{font-size:20px}
|
|
32
|
+
*,*::after,*::before{box-sizing:border-box;margin:0;padding:0}
|
|
33
|
+
.ctrl-panel{position:fixed;top:72px;right:16px;z-index:100;display:flex;flex-direction:column;align-items:flex-end;gap:6px}
|
|
34
|
+
.ctrl-toggle{width:56px;height:56px;border-radius:50%;border:1px solid var(--border);cursor:pointer;font-size:38px;display:flex;align-items:center;justify-content:center;line-height:0;padding:0;user-select:none;background:var(--surface);color:var(--muted);backdrop-filter:blur(10px);transition:all .3s}
|
|
35
|
+
.ctrl-toggle:hover{color:var(--cyan);border-color:var(--cyan);transform:rotate(90deg);transform-origin:center}
|
|
36
|
+
.ctrl-toggle.open{color:var(--cyan);border-color:var(--cyan);transform:rotate(90deg);transform-origin:center}
|
|
37
|
+
.ctrl-body{max-height:0;overflow:hidden;display:flex;flex-direction:column;gap:6px;align-items:flex-end;transition:max-height .4s ease,opacity .3s ease;opacity:0}
|
|
38
|
+
.ctrl-panel.open .ctrl-body{max-height:280px;opacity:1}
|
|
39
|
+
.theme-sw{display:flex;gap:6px;background:var(--surface);border:1px solid var(--border);border-radius:20px;padding:4px;backdrop-filter:blur(10px)}
|
|
40
|
+
.theme-sw button{width:32px;height:32px;border-radius:16px;border:none;cursor:pointer;transition:all .2s;font-size:14px;display:flex;align-items:center;justify-content:center;background:transparent}
|
|
41
|
+
.theme-sw button:hover{transform:scale(1.1)}
|
|
42
|
+
.theme-sw button.active{box-shadow:0 0 0 2px var(--cyan);background:rgba(0,240,255,.15)}
|
|
43
|
+
.lang-sw{display:flex;gap:4px;background:var(--surface);border:1px solid var(--border);border-radius:20px;padding:4px;backdrop-filter:blur(10px)}
|
|
44
|
+
.lang-sw button{padding:4px 10px;border-radius:12px;border:1px solid var(--border);cursor:pointer;font-size:11px;font-family:"JetBrains Mono",monospace;background:transparent;color:var(--muted)}
|
|
45
|
+
.lang-sw button:hover{color:var(--text)}
|
|
46
|
+
.lang-sw button.active{color:var(--cyan);border-color:var(--cyan);background:rgba(0,240,255,.1)}
|
|
47
|
+
html{font-size:18px}html.fs-sm{font-size:11px}html.fs-md{font-size:14px}html.fs-lg{font-size:18px}html.fs-xl{font-size:22px}body{font-family:'JetBrains Mono',monospace!important}.font-jetbrains h1,.font-jetbrains h3,.font-jetbrains .panel-title,.font-jetbrains .num-font,.font-jetbrains .big-num,.font-jetbrains .header-stat .num,.font-jetbrains .card-icon{font-family:'JetBrains Mono',monospace!important}
|
|
48
|
+
.font-hybrid,.font-hybrid html{font-size:18px}html.fs-sm{font-size:11px}html.fs-md{font-size:14px}html.fs-lg{font-size:18px}html.fs-xl{font-size:22px}body{font-family:'JetBrains Mono',monospace!important}.font-hybrid h1,.font-hybrid h3,.font-hybrid .panel-title,.font-hybrid .num-font,.font-hybrid .big-num,.font-hybrid .header-stat .num,.font-hybrid .card-icon{font-family:Orbitron,sans-serif!important}
|
|
49
|
+
.font-orbitron,.font-orbitron html{font-size:18px}html.fs-sm{font-size:11px}html.fs-md{font-size:14px}html.fs-lg{font-size:18px}html.fs-xl{font-size:22px}body{font-family:Orbitron,sans-serif!important}.font-orbitron h1,.font-orbitron h3,.font-orbitron .panel-title,.font-orbitron .num-font,.font-orbitron .big-num,.font-orbitron .header-stat .num,.font-orbitron .card-icon{font-family:Orbitron,sans-serif!important}
|
|
50
|
+
body{font-family:'JetBrains Mono',monospace;background:var(--bg);color:var(--text);min-height:100vh;overflow-x:hidden}
|
|
51
|
+
.scanlines{position:fixed;inset:0;background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(0,240,255,.015) 2px,rgba(0,240,255,.015) 4px);pointer-events:none;z-index:999}
|
|
52
|
+
.stars{position:fixed;inset:0;background:radial-gradient(1px 1px at 10% 20%,rgba(255,255,255,.4),transparent),radial-gradient(1px 1px at 25% 65%,rgba(255,255,255,.3),transparent),radial-gradient(1.5px 1.5px at 50% 30%,rgba(0,240,255,.5),transparent),radial-gradient(1px 1px at 70% 55%,rgba(255,255,255,.35),transparent),radial-gradient(1px 1px at 85% 15%,rgba(168,85,247,.4),transparent),radial-gradient(1.5px 1.5px at 15% 80%,rgba(0,240,255,.45),transparent),radial-gradient(1px 1px at 60% 85%,rgba(255,255,255,.3),transparent),radial-gradient(1px 1px at 90% 75%,rgba(0,255,136,.4),transparent);pointer-events:none;z-index:0}
|
|
53
|
+
.grid-pattern{position:fixed;inset:0;background-image:linear-gradient(rgba(0,240,255,.03) 1px,transparent 1px),linear-gradient(90deg,rgba(0,240,255,.03) 1px,transparent 1px);background-size:60px 60px;pointer-events:none;z-index:0}
|
|
54
|
+
main{position:relative;z-index:1;max-width:1400px;margin:0 auto;padding:40px 32px}
|
|
55
|
+
.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:36px;padding:24px 32px;background:var(--card);border:1px solid var(--border);border-radius:12px;backdrop-filter:blur(20px);position:relative;overflow:hidden}
|
|
56
|
+
.header::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--cyan),transparent);animation:scanX 3s linear infinite}
|
|
57
|
+
.header::after{content:'';position:absolute;bottom:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--cyan),transparent);animation:scanX-rev 3s linear infinite}
|
|
58
|
+
@keyframes scanX{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}
|
|
59
|
+
@keyframes scanX-rev{0%{transform:translateX(100%)}100%{transform:translateX(-100%)}}
|
|
60
|
+
@keyframes scanY{0%{transform:translateY(-100%)}100%{transform:translateY(100%)}}
|
|
61
|
+
@keyframes scanY-rev{0%{transform:translateY(100%)}100%{transform:translateY(-100%)}}
|
|
62
|
+
.header-left::before{content:'';position:absolute;top:0;left:0;width:1px;bottom:0;background:linear-gradient(180deg,transparent,var(--cyan),transparent);animation:scanY-rev 3s linear infinite;pointer-events:none}
|
|
63
|
+
.header-right::after{content:'';position:absolute;top:0;right:0;width:1px;bottom:0;background:linear-gradient(180deg,transparent,var(--cyan),transparent);animation:scanY 3s linear infinite;pointer-events:none}
|
|
64
|
+
.header-left h1{font-family:'Orbitron',sans-serif;font-size:26px;font-weight:900;background:linear-gradient(135deg,var(--cyan),var(--purple));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;letter-spacing:2px;text-shadow:0 0 40px rgba(0,240,255,.3)}
|
|
65
|
+
.header-left .subtitle{color:var(--muted);font-size:12px;margin-top:4px;letter-spacing:1px}
|
|
66
|
+
.header-right{display:flex;gap:16px;align-items:center}
|
|
67
|
+
.header-stat{text-align:center;padding:0 20px;border-left:1px solid rgba(0,240,255,.1)}
|
|
68
|
+
.header-stat .num{font-family:'Orbitron',sans-serif;font-size:22px;font-weight:700;color:var(--cyan);text-shadow:0 0 20px rgba(0,240,255,.4)}
|
|
69
|
+
.header-stat .label{font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:1px}
|
|
70
|
+
.phase-indicator{display:flex;align-items:center;gap:8px;padding:8px 20px;border:1px solid rgba(0,240,255,.2);border-radius:20px;background:rgba(0,240,255,.05)}
|
|
71
|
+
.phase-indicator .dot{width:8px;height:8px;border-radius:50%;background:var(--cyan);box-shadow:0 0 12px var(--cyan);animation:pulse 2s ease-in-out infinite}
|
|
72
|
+
@keyframes pulse{0%,100%{box-shadow:0 0 12px var(--cyan)}50%{box-shadow:0 0 24px var(--cyan),0 0 48px rgba(0,240,255,.3)}}
|
|
73
|
+
.phase-indicator span{font-size:12px;color:var(--cyan);letter-spacing:1px}
|
|
74
|
+
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-bottom:28px}
|
|
75
|
+
.stat-card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:24px;backdrop-filter:blur(20px);text-align:center;transition:all .3s;position:relative;overflow:hidden}
|
|
76
|
+
.stat-card:hover{border-color:rgba(0,240,255,.25);box-shadow:0 0 30px rgba(0,240,255,.08)}
|
|
77
|
+
.stat-card .label{font-size:12px;color:var(--muted);margin-bottom:12px;font-weight:500;text-transform:uppercase;letter-spacing:1px}
|
|
78
|
+
.stat-card .value{font-family:'Orbitron',sans-serif;font-size:38px;font-weight:900;letter-spacing:-1px}
|
|
79
|
+
.stat-card .value.c-cyan{color:var(--cyan);text-shadow:0 0 30px rgba(0,240,255,.3)}
|
|
80
|
+
.stat-card .value.c-green{color:var(--green);text-shadow:0 0 30px rgba(0,255,136,.3)}
|
|
81
|
+
.stat-card .value.c-orange{color:var(--orange);text-shadow:0 0 20px rgba(245,158,11,.3)}
|
|
82
|
+
.stat-card .value.c-muted{color:var(--muted)}
|
|
83
|
+
.stat-card .sub{font-size:11px;color:var(--muted);margin-top:6px}
|
|
84
|
+
.data-stream{position:absolute;bottom:6px;left:0;right:0;height:18px;overflow:hidden}
|
|
85
|
+
.data-stream span{position:absolute;top:3px;color:rgba(0,240,255,.08);font-family:'JetBrains Mono',monospace;font-size:9px;white-space:nowrap;animation:stream 25s linear infinite}
|
|
86
|
+
@keyframes stream{0%{transform:translateX(100%)}100%{transform:translateX(-100%)}}
|
|
87
|
+
.charts{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:28px}
|
|
88
|
+
@media(max-width:768px){.charts{grid-template-columns:1fr}.stats{grid-template-columns:1fr 1fr}}
|
|
89
|
+
.chart-card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:24px;backdrop-filter:blur(20px);position:relative;transition:all .3s}
|
|
90
|
+
.chart-card:hover{border-color:rgba(0,240,255,.25);box-shadow:0 0 30px rgba(0,240,255,.08)}
|
|
91
|
+
.chart-card:hover .fs-btn{opacity:1}
|
|
92
|
+
.chart-card h3{font-size:14px;font-weight:600;margin-bottom:16px;color:var(--muted);letter-spacing:.5px;text-transform:uppercase}
|
|
93
|
+
.chart-card canvas{max-height:300px}
|
|
94
|
+
.panel{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:28px;backdrop-filter:blur(20px);margin-bottom:28px;position:relative;transition:all .3s}
|
|
95
|
+
.panel:hover{border-color:rgba(0,240,255,.25);box-shadow:0 0 30px rgba(0,240,255,.08)}
|
|
96
|
+
.panel:hover .fs-btn{opacity:1}
|
|
97
|
+
.panel-title{font-family:'Orbitron',sans-serif;font-size:14px;font-weight:700;color:var(--cyan);letter-spacing:2px;margin-bottom:20px;display:flex;align-items:center;gap:10px}
|
|
98
|
+
.panel-title::before{content:'◆';font-size:10px;color:var(--cyan);text-shadow:0 0 8px var(--cyan)}
|
|
99
|
+
table{width:100%;border-collapse:collapse;font-size:13px}
|
|
100
|
+
th{padding:14px 16px;font-size:10px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:1.5px;border-bottom:1px solid rgba(0,240,255,.08);text-align:left}
|
|
101
|
+
td{padding:14px 16px;border-bottom:1px solid rgba(0,240,255,.03);color:var(--text);transition:all .2s}
|
|
102
|
+
tr:hover td{background:var(--hover)}
|
|
103
|
+
.badge{display:inline-block;padding:3px 12px;border-radius:12px;font-size:11px;font-weight:500;letter-spacing:.5px}
|
|
104
|
+
.badge-backend{background:rgba(59,130,246,.15);color:var(--blue)}
|
|
105
|
+
.badge-web{background:rgba(0,255,136,.1);color:var(--green)}
|
|
106
|
+
.badge-h5{background:rgba(245,158,11,.1);color:var(--orange)}
|
|
107
|
+
.badge-miniapp{background:rgba(168,85,247,.15);color:var(--purple)}
|
|
108
|
+
.badge-done{background:rgba(0,255,136,.1);color:var(--green)}
|
|
109
|
+
.badge-progress{background:rgba(59,130,246,.1);color:var(--blue)}
|
|
110
|
+
.badge-pending{background:rgba(255,255,255,.03);color:var(--muted)}
|
|
111
|
+
.footer{text-align:center;color:var(--muted);font-size:11px;margin-top:40px;padding:16px 0;border-top:1px solid rgba(0,240,255,.06);letter-spacing:1px}
|
|
112
|
+
.scroll-table{max-height:70vh;overflow-y:auto}.scroll-table thead{position:sticky;top:0;z-index:2;background:var(--card)}
|
|
113
|
+
.fs-btn{position:absolute;top:10px;right:10px;width:28px;height:28px;border-radius:6px;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.06);color:var(--muted);cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;transition:all .2s;z-index:10;opacity:0}
|
|
114
|
+
.stat-card:hover .fs-btn,.chart-card:hover .fs-btn,.panel:hover .fs-btn{opacity:1}
|
|
115
|
+
.fs-btn:hover{background:rgba(0,240,255,.1);border-color:rgba(0,240,255,.3);color:var(--cyan)}
|
|
116
|
+
.fs-fullscreen{position:fixed!important;inset:0!important;z-index:1000!important;border-radius:0!important;overflow-y:auto!important;background:var(--bg)!important;backdrop-filter:none!important;padding:40px!important;width:100vw!important;height:100vh!important;max-width:none!important}
|
|
117
|
+
.fs-fullscreen canvas{max-height:60vh!important}
|
|
118
|
+
.fs-fullscreen .scroll-table{max-height:none!important}
|
|
119
|
+
.fs-tip{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:rgba(0,240,255,.1);border:1px solid rgba(0,240,255,.2);padding:8px 20px;border-radius:20px;font-size:11px;color:var(--cyan);z-index:1001;letter-spacing:1px;animation:fadeIn .3s ease;pointer-events:none}
|
|
120
|
+
@keyframes fadeIn{from{opacity:0;transform:translateX(-50%) translateY(10px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}
|
|
121
|
+
@keyframes cardGlow{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.6)}}.card-bg{position:absolute;inset:0;pointer-events:none;z-index:0;background:radial-gradient(ellipse at 25% 10%,rgba(14,165,233,.25) 0%,transparent 70%);animation:cardGlow 3s ease-in-out infinite;transform-origin:top center}@keyframes titleGlow{0%,100%{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15)}50%{text-shadow:0 0 30px rgba(14,165,233,.7),0 0 80px rgba(14,165,233,.3)}}h1,h2{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15);animation:titleGlow 3s ease-in-out infinite}@keyframes cardGlow{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.6)}}.card-bg{position:absolute;inset:0;pointer-events:none;z-index:0;background:radial-gradient(ellipse at 25% 10%,rgba(14,165,233,.25) 0%,transparent 70%);animation:cardGlow 3s ease-in-out infinite;transform-origin:top center}</style>
|
|
122
|
+
</head>
|
|
123
|
+
<body>
|
|
124
|
+
<div class="ctrl-panel" id="ctrlPanel">
|
|
125
|
+
<button class="ctrl-toggle" onclick="toggleCtrl()" title="设置">⚙</button>
|
|
126
|
+
<div class="ctrl-body">
|
|
127
|
+
<div class="theme-sw">
|
|
128
|
+
<button onclick="setTheme('ocean')" title="Ocean Blue">🌊</button><button onclick="setTheme('cyber')" title="Cyber Dark">🌙</button>
|
|
129
|
+
<button onclick="setTheme('light')" title="Light Studio">☀️</button><button onclick="setTheme('mono')" title="Mono Tech">⬛</button>
|
|
130
|
+
<button onclick="setTheme('github')" title="GitHub Dark">🐙</button><button onclick="setTheme('synth')" title="SynthWave">💜</button>
|
|
131
|
+
<button onclick="setTheme('amber')" title="Amber Terminal">🟡</button><button onclick="setTheme('sakura')" title="Cherry Sakura">🌸</button>
|
|
132
|
+
<button onclick="setTheme('forest')" title="Midnight Forest">🌲</button>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="lang-sw">
|
|
135
|
+
<button data-lang="zh" class="active" onclick="setLang('zh')">中文</button>
|
|
136
|
+
<button data-lang="en" onclick="setLang('en')">EN</button>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="lang-sw">
|
|
139
|
+
<button onclick="setFont('hybrid')" class="active" data-font="hybrid">Hybrid</button>
|
|
140
|
+
<button onclick="setFont('orbitron')" data-font="orbitron">Orbit</button>
|
|
141
|
+
<button onclick="setFont('jetbrains')" data-font="jetbrains">Mono</button>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="lang-sw">
|
|
144
|
+
<button onclick="setSize('sm')" data-size="sm">S</button>
|
|
145
|
+
<button onclick="setSize('md')" data-size="md">M</button>
|
|
146
|
+
<button onclick="setSize('lg')" class="active" data-size="lg">L</button>
|
|
147
|
+
<button onclick="setSize('xl')" data-size="xl">XL</button>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
<div class="scanlines"></div><div class="stars"></div><div class="grid-pattern"></div>
|
|
152
|
+
<main>
|
|
153
|
+
<div class="header">
|
|
154
|
+
<div class="header-left">
|
|
155
|
+
<h1>SPECCORE</h1>
|
|
156
|
+
<div class="subtitle"><span data-i18n="globalDash">全局仪表盘</span> · 0 <span data-i18n="projects">项目</span> · 15 <span data-i18n="reqs">需求</span></div>
|
|
157
|
+
</div>
|
|
158
|
+
<div class="header-right">
|
|
159
|
+
<div class="header-stat"><div class="num">0</div><div class="label" data-i18n="projects">项目</div></div>
|
|
160
|
+
<div class="header-stat"><div class="num">15</div><div class="label" data-i18n="reqs">需求</div></div>
|
|
161
|
+
<div class="header-stat"><div class="num">33%</div><div class="label" data-i18n="done2">完成</div></div>
|
|
162
|
+
<div class="phase-indicator"><div class="dot"></div><span>LIVE</span></div>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="data-stream" style="position:absolute"><span>SPECCORE · DASHBOARD · 0 PROJECTS · 15 REQUIREMENTS · 33% COMPLETE · POWERED BY SPECCORE</span></div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div class="stats">
|
|
168
|
+
<div class="stat-card">
|
|
169
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
170
|
+
<div class="label">📋 <span data-i18n="total">总需求数</span></div>
|
|
171
|
+
<div class="value c-cyan">15</div>
|
|
172
|
+
<div class="sub">0 个项目</div>
|
|
173
|
+
<div class="data-stream"><span>SPECCORE · TOTAL REQUIREMENTS · 2026-08-31 · Vv1.2</span></div>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="stat-card">
|
|
176
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
177
|
+
<div class="label">✅ <span data-i18n="done">已完成</span></div>
|
|
178
|
+
<div class="value c-green">5</div>
|
|
179
|
+
<div class="sub"><span data-i18n="done2">完成</span>率 33%</div>
|
|
180
|
+
<div class="data-stream"><span>DONE · 33% COMPLETION · 5 IMPLEMENTED</span></div>
|
|
181
|
+
</div>
|
|
182
|
+
<div class="stat-card">
|
|
183
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
184
|
+
<div class="label">🔄 <span data-i18n="progress">进行中</span></div>
|
|
185
|
+
<div class="value c-orange">5</div>
|
|
186
|
+
<div class="sub">33%</div>
|
|
187
|
+
<div class="data-stream"><span>IN PROGRESS · 5 ACTIVE · ITERATIONS ACTIVE</span></div>
|
|
188
|
+
</div>
|
|
189
|
+
<div class="stat-card">
|
|
190
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
191
|
+
<div class="label">🔲 <span data-i18n="pending">待开发</span></div>
|
|
192
|
+
<div class="value c-muted">5</div>
|
|
193
|
+
<div class="sub">33%</div>
|
|
194
|
+
<div class="data-stream"><span>PENDING · 5 BACKLOG · AWAITING SPRINT</span></div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<div class="charts">
|
|
199
|
+
<div class="chart-card">
|
|
200
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
201
|
+
<h3>📊 <span data-i18n="statusDist">需求状态分布</span></h3>
|
|
202
|
+
<canvas id="statusChart"></canvas>
|
|
203
|
+
<div class="data-stream"><span>STATUS · 5 DONE · 5 ACTIVE · 5 BACKLOG</span></div>
|
|
204
|
+
</div>
|
|
205
|
+
<div class="chart-card">
|
|
206
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
207
|
+
<h3>📈 <span data-i18n="projDist">项目需求分布</span></h3>
|
|
208
|
+
<canvas id="projectChart"></canvas>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<div class="panel">
|
|
213
|
+
<div class="panel-title"><span data-i18n="projHealth">项目健康度</span></div>
|
|
214
|
+
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px">
|
|
215
|
+
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
<div class="charts">
|
|
220
|
+
<div class="chart-card">
|
|
221
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
222
|
+
<h3>⚡ <span data-i18n="createdVsResolved">需求创建 vs 完成</span></h3>
|
|
223
|
+
<canvas id="resolvedChart"></canvas>
|
|
224
|
+
</div>
|
|
225
|
+
<div class="chart-card">
|
|
226
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
227
|
+
<h3>📅 <span data-i18n="iterProgress">期次进度</span></h3>
|
|
228
|
+
<div style="display:flex;flex-direction:column;gap:10px;margin-top:8px;max-height:280px;overflow-y:auto">
|
|
229
|
+
|
|
230
|
+
<div style="color:var(--muted);text-align:center;padding:20px">暂无期次数据</div>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<div class="stats">
|
|
236
|
+
<div class="stat-card">
|
|
237
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
238
|
+
<div class="label">🏥 <span data-i18n="health">项目健康度</span></div>
|
|
239
|
+
<div class="value c-cyan">0/0</div>
|
|
240
|
+
<div class="sub">健康项目 / 全部</div>
|
|
241
|
+
<div class="data-stream"><span>HEALTH · 0 GREEN · 0 AT RISK</span></div>
|
|
242
|
+
</div>
|
|
243
|
+
<div class="stat-card">
|
|
244
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
245
|
+
<div class="label">📅 <span data-i18n="activeIter">活跃期次</span></div>
|
|
246
|
+
<div class="value c-green">0</div>
|
|
247
|
+
<div class="sub">共 0 个期次</div>
|
|
248
|
+
<div class="data-stream"><span>ITERATIONS · 0 ACTIVE · 0 TOTAL</span></div>
|
|
249
|
+
</div>
|
|
250
|
+
<div class="stat-card">
|
|
251
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
252
|
+
<div class="label">📈 <span data-i18n="velocity">交付速率</span></div>
|
|
253
|
+
<div class="value c-orange">33%</div>
|
|
254
|
+
<div class="sub">已完成 / 总需求</div>
|
|
255
|
+
<div class="data-stream"><span>VELOCITY · 5 DONE · 15 TOTAL · 33%</span></div>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<div class="panel">
|
|
260
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
261
|
+
<div class="panel-title"><span data-i18n="projList">项目列表</span></div>
|
|
262
|
+
<table>
|
|
263
|
+
<thead><tr><th><span data-i18n="projName">项目名称</span></th><th><span data-i18n="type">类型</span></th><th><span data-i18n="reqCount">需求数</span></th><th><span data-i18n="lastImport">最后导入</span></th></tr></thead>
|
|
264
|
+
<tbody><tr><td colspan="4">暂无项目</td></tr></tbody>
|
|
265
|
+
</table>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<div class="panel">
|
|
269
|
+
<button class="fs-btn" title="全屏 (F)" onclick="toggleFS(this.parentElement)">⛶</button>
|
|
270
|
+
<div class="panel-title"><span data-i18n="reqDetail">需求详情</span></div>
|
|
271
|
+
<div class="scroll-table">
|
|
272
|
+
<table>
|
|
273
|
+
<thead><tr><th><span data-i18n="reqId">需求 ID</span></th><th><span data-i18n="name">名称</span></th><th><span data-i18n="project">项目</span></th><th><span data-i18n="status">状态</span></th><th><span data-i18n="iteration">关联期次</span></th></tr></thead>
|
|
274
|
+
<tbody><tr><td>REQ-006</td><td>营销活动</td><td>电商平台</td><td><span class="badge badge-pending">🔲 待开发</span></td><td>Q3</td></tr><tr><td>REQ-007</td><td>数据看板</td><td>电商平台</td><td><span class="badge badge-pending">🔲 待开发</span></td><td>Q3</td></tr><tr><td>REQ-008</td><td>客服系统</td><td>电商平台</td><td><span class="badge badge-pending">🔲 待开发</span></td><td>Q3</td></tr><tr><td>REQ-012</td><td>处方管理</td><td>医疗 SaaS</td><td><span class="badge badge-pending">🔲 待开发</span></td><td>Q3</td></tr><tr><td>REQ-003</td><td>订单系统</td><td>电商平台</td><td><span class="badge badge-progress">🔄 进行中</span></td><td>Q2</td></tr><tr><td>REQ-004</td><td>支付集成</td><td>电商平台</td><td><span class="badge badge-progress">🔄 进行中</span></td><td>Q2</td></tr><tr><td>REQ-005</td><td>物流追踪</td><td>电商平台</td><td><span class="badge badge-progress">🔄 进行中</span></td><td>Q2</td></tr><tr><td>REQ-011</td><td>电子病历</td><td>医疗 SaaS</td><td><span class="badge badge-progress">🔄 进行中</span></td><td>Q2</td></tr><tr><td>REQ-014</td><td>文档协作</td><td>办公 OA</td><td><span class="badge badge-progress">🔄 进行中</span></td><td>Q2</td></tr><tr><td>REQ-015</td><td>日程管理</td><td>办公 OA</td><td><span class="badge badge-pending">🔲 待开发</span></td><td>Q2</td></tr><tr><td>REQ-001</td><td>用户登录注册</td><td>电商平台</td><td><span class="badge badge-done">✅ 已实现</span></td><td>Q1</td></tr><tr><td>REQ-002</td><td>商品管理</td><td>电商平台</td><td><span class="badge badge-done">✅ 已实现</span></td><td>Q1</td></tr><tr><td>REQ-009</td><td>患者管理</td><td>医疗 SaaS</td><td><span class="badge badge-done">✅ 已实现</span></td><td>Q1</td></tr><tr><td>REQ-010</td><td>预约挂号</td><td>医疗 SaaS</td><td><span class="badge badge-done">✅ 已实现</span></td><td>Q1</td></tr><tr><td>REQ-013</td><td>审批流引擎</td><td>办公 OA</td><td><span class="badge badge-done">✅ 已实现</span></td><td>Q1</td></tr></tbody>
|
|
275
|
+
</table>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
<div class="footer"><span data-i18n="powered">由 SpecCore 驱动</span> v1.2 | Generated 2026-08-31</div>
|
|
280
|
+
</main>
|
|
281
|
+
|
|
282
|
+
<script>
|
|
283
|
+
let fsEl = null;
|
|
284
|
+
|
|
285
|
+
// Fullscreen toggle
|
|
286
|
+
function toggleFS(el) {
|
|
287
|
+
if (fsEl === el) { el.classList.remove('fs-fullscreen'); fsEl = null; document.body.style.overflow = ''; return; }
|
|
288
|
+
if (fsEl) { fsEl.classList.remove('fs-fullscreen'); }
|
|
289
|
+
fsEl = el; el.classList.add('fs-fullscreen');
|
|
290
|
+
document.body.style.overflow = 'hidden';
|
|
291
|
+
const tip = document.createElement('div'); tip.className = 'fs-tip'; tip.textContent = '按 ESC 退出全屏 · 滚动查看详情';
|
|
292
|
+
document.body.appendChild(tip);
|
|
293
|
+
setTimeout(() => tip.remove(), 2500);
|
|
294
|
+
}
|
|
295
|
+
document.addEventListener('keydown',e=>{
|
|
296
|
+
if(e.key==='Escape'&&fsEl){fsEl.classList.remove('fs-fullscreen');document.body.style.overflow='';fsEl=null}
|
|
297
|
+
if(e.key==='f'||e.key==='F'){
|
|
298
|
+
if(document.activeElement&&document.activeElement.tagName==='INPUT')return;
|
|
299
|
+
const hovered=document.querySelector('.stat-card:hover,.chart-card:hover,.panel:hover');
|
|
300
|
+
if(hovered)toggleFS(hovered);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Export functions
|
|
305
|
+
function exportJSON() {
|
|
306
|
+
const data = { projects: [] };
|
|
307
|
+
download(JSON.stringify(data, null, 2), 'dashboard-data.json', 'application/json');
|
|
308
|
+
}
|
|
309
|
+
function exportCSV() {
|
|
310
|
+
const rows = [['需求ID','项目','名称','状态','版本','期次'].join(',')];
|
|
311
|
+
[{"id":"REQ-001","project":"电商平台","name":"用户登录注册","status":"✅ 已实现","version":"v1.2","iteration":"Q1","task":"Task-01","filePath":"PROJECTS/ecommerce/"},{"id":"REQ-002","project":"电商平台","name":"商品管理","status":"✅ 已实现","version":"v1.5","iteration":"Q1","task":"Task-02","filePath":"PROJECTS/ecommerce/"},{"id":"REQ-003","project":"电商平台","name":"订单系统","status":"🔄 进行中","version":"v2.0","iteration":"Q2","task":"Task-01","filePath":"PROJECTS/ecommerce/"},{"id":"REQ-004","project":"电商平台","name":"支付集成","status":"🔄 进行中","version":"v2.0","iteration":"Q2","task":"Task-03","filePath":"PROJECTS/ecommerce/"},{"id":"REQ-005","project":"电商平台","name":"物流追踪","status":"🔄 进行中","version":"v2.1","iteration":"Q2","task":"Task-04","filePath":"PROJECTS/ecommerce/"},{"id":"REQ-006","project":"电商平台","name":"营销活动","status":"🔲 待开发","version":"v0.1","iteration":"Q3","task":"","filePath":"PROJECTS/ecommerce/"},{"id":"REQ-007","project":"电商平台","name":"数据看板","status":"🔲 待开发","version":"v0.1","iteration":"Q3","task":"","filePath":"PROJECTS/ecommerce/"},{"id":"REQ-008","project":"电商平台","name":"客服系统","status":"🔲 待开发","version":"v0.1","iteration":"Q3","task":"","filePath":"PROJECTS/ecommerce/"},{"id":"REQ-009","project":"医疗 SaaS","name":"患者管理","status":"✅ 已实现","version":"v1.0","iteration":"Q1","task":"Task-01","filePath":"PROJECTS/medsaas/"},{"id":"REQ-010","project":"医疗 SaaS","name":"预约挂号","status":"✅ 已实现","version":"v1.1","iteration":"Q1","task":"Task-02","filePath":"PROJECTS/medsaas/"},{"id":"REQ-011","project":"医疗 SaaS","name":"电子病历","status":"🔄 进行中","version":"v2.0","iteration":"Q2","task":"Task-01","filePath":"PROJECTS/medsaas/"},{"id":"REQ-012","project":"医疗 SaaS","name":"处方管理","status":"🔲 待开发","version":"v0.1","iteration":"Q3","task":"","filePath":"PROJECTS/medsaas/"},{"id":"REQ-013","project":"办公 OA","name":"审批流引擎","status":"✅ 已实现","version":"v1.0","iteration":"Q1","task":"Task-01","filePath":"PROJECTS/oa/"},{"id":"REQ-014","project":"办公 OA","name":"文档协作","status":"🔄 进行中","version":"v1.5","iteration":"Q2","task":"Task-02","filePath":"PROJECTS/oa/"},{"id":"REQ-015","project":"办公 OA","name":"日程管理","status":"🔲 待开发","version":"v0.1","iteration":"Q2","task":"","filePath":"PROJECTS/oa/"}].forEach(r => rows.push([r.id,r.project,r.name,r.status,r.version,r.iteration||'-'].join(',')));
|
|
312
|
+
download(rows.join('\n'), 'dashboard-data.csv', 'text/csv');
|
|
313
|
+
}
|
|
314
|
+
function download(content, name, type) {
|
|
315
|
+
const b = new Blob([content], { type });
|
|
316
|
+
const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = name; a.click();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Settings toggle (ctrl-panel)
|
|
320
|
+
function toggleCtrl() {
|
|
321
|
+
document.getElementById('ctrlPanel').classList.toggle('open');
|
|
322
|
+
document.querySelector('.ctrl-toggle').classList.toggle('open');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Theme - index based (order: ocean,cyber,light,mono,github,synth,amber,sakura,forest)
|
|
326
|
+
const THEMES = ['ocean','cyber','light','mono','github','synth','amber','sakura','forest'];
|
|
327
|
+
function setTheme(t) {
|
|
328
|
+
document.documentElement.setAttribute('data-theme', t);
|
|
329
|
+
localStorage.setItem('speccore-theme', t);
|
|
330
|
+
document.querySelectorAll('.theme-sw button').forEach((b,i) => b.classList.toggle('active', THEMES[i]===t));
|
|
331
|
+
updateChartColors(t);
|
|
332
|
+
}
|
|
333
|
+
function updateChartColors(t) {
|
|
334
|
+
if(typeof statusChart === 'undefined') return;
|
|
335
|
+
const light = t === 'light';
|
|
336
|
+
const tc = light ? '#64748b' : '#4a5568';
|
|
337
|
+
const gc = light ? 'rgba(0,0,0,.06)' : 'rgba(255,255,255,.06)';
|
|
338
|
+
[statusChart, projectChart, resolvedChart].forEach(ch => {
|
|
339
|
+
if(!ch) return;
|
|
340
|
+
if(ch.config.type !== 'doughnut') {
|
|
341
|
+
ch.options.scales.x.ticks.color = tc; ch.options.scales.y.ticks.color = tc;
|
|
342
|
+
ch.options.scales.x.grid.color = gc; ch.options.scales.y.grid.color = gc;
|
|
343
|
+
}
|
|
344
|
+
if(ch.options.plugins.legend) ch.options.plugins.legend.labels.color = tc;
|
|
345
|
+
ch.update();
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Lang
|
|
350
|
+
var I18N={zh:{total:'总需求数',done:'已完成',progress:'进行中',pending:'待开发',health:'项目健康度',activeIter:'活跃期次',velocity:'交付速率',statusDist:'需求状态分布',projDist:'项目需求分布',createdVsResolved:'需求创建 vs 完成',iterProgress:'期次进度',projHealth:'项目健康度',projList:'项目列表',reqDetail:'需求详情',projName:'项目名称',type:'类型',reqCount:'需求数',lastImport:'最后导入',reqId:'需求 ID',name:'名称',project:'项目',status:'状态',iteration:'关联期次',projects:'项目',reqs:'需求',done2:'完成',live:'LIVE',globalDash:'全局仪表盘',powered:'由 SpecCore 驱动'},en:{total:'Total Reqs',done:'Done',progress:'In Progress',pending:'Backlog',health:'Project Health',activeIter:'Active Iterations',velocity:'Velocity',statusDist:'Status Distribution',projDist:'Project Distribution',createdVsResolved:'Created vs Resolved',iterProgress:'Iteration Progress',projHealth:'PROJECT HEALTH',projList:'Project List',reqDetail:'Requirement Details',projName:'Project Name',type:'Type',reqCount:'Reqs',lastImport:'Last Import',reqId:'Req ID',name:'Name',project:'Project',status:'Status',iteration:'Iteration',projects:'Projects',reqs:'Reqs',done2:'Done',live:'LIVE',globalDash:'GLOBAL DASHBOARD',powered:'Powered by SpecCore'}};
|
|
351
|
+
function setLang(l) {
|
|
352
|
+
document.querySelectorAll('.lang-sw button[data-lang]').forEach(b => b.classList.toggle('active', b.dataset.lang===l));
|
|
353
|
+
document.querySelectorAll('[data-i18n]').forEach(el => { var k=el.getAttribute('data-i18n'); if(I18N[l]&&I18N[l][k]) el.textContent=I18N[l][k] });
|
|
354
|
+
localStorage.setItem('speccore-lang', l);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Font family
|
|
358
|
+
function setFont(f) {
|
|
359
|
+
document.documentElement.className = document.documentElement.className.replace(/font-w+/g,'');
|
|
360
|
+
if(f!=='hybrid') document.documentElement.classList.add('font-'+f);
|
|
361
|
+
document.querySelectorAll('.lang-sw button[data-font]').forEach(b => b.classList.toggle('active', b.dataset.font===f));
|
|
362
|
+
localStorage.setItem('speccore-font', f);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Font size
|
|
366
|
+
function setSize(s) {
|
|
367
|
+
document.documentElement.className = document.documentElement.className.replace(/fs-w+/g,'');
|
|
368
|
+
document.documentElement.classList.add('fs-'+s);
|
|
369
|
+
document.querySelectorAll('.lang-sw button[data-size]').forEach(b => b.classList.toggle('active', b.dataset.size===s));
|
|
370
|
+
localStorage.setItem('speccore-size', s);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Init saved settings
|
|
374
|
+
(function(){
|
|
375
|
+
setTheme(localStorage.getItem('speccore-theme') || 'ocean');
|
|
376
|
+
setLang(localStorage.getItem('speccore-lang') || 'zh');
|
|
377
|
+
setFont(localStorage.getItem('speccore-font') || 'hybrid');
|
|
378
|
+
setSize(localStorage.getItem('speccore-size') || 'lg');
|
|
379
|
+
})();
|
|
380
|
+
|
|
381
|
+
// Charts
|
|
382
|
+
const isLight = document.documentElement.getAttribute('data-theme') === 'light';
|
|
383
|
+
const gridColor = isLight ? 'rgba(0,0,0,.06)' : 'rgba(255,255,255,.06)';
|
|
384
|
+
const textColor = isLight ? '#64748b' : '#4a5568';
|
|
385
|
+
|
|
386
|
+
const statusChart = new Chart(document.getElementById('statusChart'), {
|
|
387
|
+
type: 'doughnut',
|
|
388
|
+
data: {
|
|
389
|
+
labels: ['已完成', '进行中', '待开发'],
|
|
390
|
+
datasets: [{
|
|
391
|
+
data: [5, 5, 5],
|
|
392
|
+
backgroundColor: ['#00ff88', '#00f0ff', 'rgba(255,255,255,.05)'],
|
|
393
|
+
borderWidth: 2,
|
|
394
|
+
borderColor: 'var(--bg)'
|
|
395
|
+
}]
|
|
396
|
+
},
|
|
397
|
+
options: {
|
|
398
|
+
responsive: true,
|
|
399
|
+
cutout: '60%',
|
|
400
|
+
plugins: {
|
|
401
|
+
legend: { position: 'bottom', labels: { color: textColor, font: { family: 'JetBrains Mono', size: 11 }, padding: 16 } }
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
const projectChart = new Chart(document.getElementById('projectChart'), {
|
|
407
|
+
type: 'bar',
|
|
408
|
+
data: {
|
|
409
|
+
labels: [],
|
|
410
|
+
datasets: [{
|
|
411
|
+
label: '需求数',
|
|
412
|
+
data: [],
|
|
413
|
+
backgroundColor: '#00f0ff',
|
|
414
|
+
borderRadius: 6
|
|
415
|
+
}]
|
|
416
|
+
},
|
|
417
|
+
options: {
|
|
418
|
+
responsive: true,
|
|
419
|
+
plugins: { legend: { display: false } },
|
|
420
|
+
scales: {
|
|
421
|
+
x: { ticks: { color: textColor, font: { size: 10 } }, grid: { color: gridColor } },
|
|
422
|
+
y: { beginAtZero: true, ticks: { stepSize: 1, color: textColor, font: { size: 10 } }, grid: { color: gridColor } }
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const resolvedChart = new Chart(document.getElementById('resolvedChart'), {
|
|
428
|
+
type: 'bar',
|
|
429
|
+
data: {
|
|
430
|
+
labels: ['已实现', '进行中', '待开发'],
|
|
431
|
+
datasets: [{
|
|
432
|
+
data: [5, 5, 5],
|
|
433
|
+
backgroundColor: ['rgba(0,255,136,.6)', 'rgba(0,240,255,.6)', 'rgba(255,255,255,.06)'],
|
|
434
|
+
borderRadius: 4
|
|
435
|
+
}]
|
|
436
|
+
},
|
|
437
|
+
options: {
|
|
438
|
+
responsive: true, indexAxis: 'y',
|
|
439
|
+
plugins: { legend: { display: false } },
|
|
440
|
+
scales: {
|
|
441
|
+
x: { ticks: { color: textColor, font: { size: 10 } }, grid: { color: gridColor } },
|
|
442
|
+
y: { ticks: { color: textColor, font: { size: 10 } }, grid: { display: false } }
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
updateChartColors(document.documentElement.getAttribute('data-theme'));
|
|
447
|
+
</script>
|
|
448
|
+
</body>
|
|
449
|
+
</html>
|