pdd-skills 3.0.0

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.
Files changed (261) hide show
  1. package/README.md +1478 -0
  2. package/bin/pdd.js +354 -0
  3. package/config/bpmn-rules.yaml +166 -0
  4. package/config/checkstyle.xml +105 -0
  5. package/config/eslint.config.js +48 -0
  6. package/config/pmd.xml +91 -0
  7. package/config/prd-rules.yaml +113 -0
  8. package/config/ruff.toml +45 -0
  9. package/config/sqlfluff.cfg +82 -0
  10. package/hooks/hook-executor.js +332 -0
  11. package/index.js +43 -0
  12. package/lib/api-routes.js +750 -0
  13. package/lib/api-server.js +408 -0
  14. package/lib/cache/cache-config.js +209 -0
  15. package/lib/cache/system-cache.js +852 -0
  16. package/lib/config-manager.js +373 -0
  17. package/lib/generate.js +528 -0
  18. package/lib/grpc/grpc-routes.js +1134 -0
  19. package/lib/grpc/grpc-server.js +912 -0
  20. package/lib/grpc/proto-definitions.js +1033 -0
  21. package/lib/init.js +172 -0
  22. package/lib/iteration/auto-fixer.js +1025 -0
  23. package/lib/iteration/auto-reviewer.js +923 -0
  24. package/lib/iteration/controller.js +577 -0
  25. package/lib/list.js +130 -0
  26. package/lib/mcp-server.js +548 -0
  27. package/lib/openclaw/api-integration.js +535 -0
  28. package/lib/openclaw/cli-integration.js +567 -0
  29. package/lib/openclaw/data-sync.js +845 -0
  30. package/lib/openclaw/openclaw-adapter.js +783 -0
  31. package/lib/plugin/example-plugins/code-stats/index.js +332 -0
  32. package/lib/plugin/example-plugins/code-stats/plugin.json +1 -0
  33. package/lib/plugin/example-plugins/custom-linter/index.js +472 -0
  34. package/lib/plugin/example-plugins/custom-linter/plugin.json +1 -0
  35. package/lib/plugin/example-plugins/hello-world/index.js +86 -0
  36. package/lib/plugin/example-plugins/hello-world/plugin.json +1 -0
  37. package/lib/plugin/plugin-manager.js +655 -0
  38. package/lib/plugin/plugin-sdk.js +565 -0
  39. package/lib/plugin/sandbox.js +627 -0
  40. package/lib/quality/rules/maintainability.js +418 -0
  41. package/lib/quality/rules/performance.js +498 -0
  42. package/lib/quality/rules/readability.js +441 -0
  43. package/lib/quality/rules/robustness.js +504 -0
  44. package/lib/quality/rules/security.js +444 -0
  45. package/lib/quality/scorer.js +576 -0
  46. package/lib/report.js +669 -0
  47. package/lib/sdk-base.js +301 -0
  48. package/lib/sdk-js.js +446 -0
  49. package/lib/sdk-python/README.md +546 -0
  50. package/lib/sdk-python/examples/basic_usage.py +450 -0
  51. package/lib/sdk-python/pdd_sdk/__init__.py +180 -0
  52. package/lib/sdk-python/pdd_sdk/client.py +1170 -0
  53. package/lib/sdk-python/pdd_sdk/events.py +423 -0
  54. package/lib/sdk-python/pdd_sdk/exceptions.py +158 -0
  55. package/lib/sdk-python/pdd_sdk/models.py +518 -0
  56. package/lib/sdk-python/pdd_sdk/utils.py +759 -0
  57. package/lib/token/budget-alert.js +367 -0
  58. package/lib/token/budget-manager.js +485 -0
  59. package/lib/update.js +54 -0
  60. package/lib/utils/logger.js +88 -0
  61. package/lib/verify.js +741 -0
  62. package/lib/version.js +52 -0
  63. package/lib/vm/README.md +102 -0
  64. package/lib/vm/dashboard/api-routes.js +669 -0
  65. package/lib/vm/dashboard/server.js +391 -0
  66. package/lib/vm/dashboard/sse.js +358 -0
  67. package/lib/vm/dashboard/static/css/dashboard.css +1378 -0
  68. package/lib/vm/dashboard/static/index.html +118 -0
  69. package/lib/vm/dashboard/static/js/app.js +949 -0
  70. package/lib/vm/dashboard/static/js/charts.js +913 -0
  71. package/lib/vm/dashboard/static/js/kanban-view.js +1053 -0
  72. package/lib/vm/dashboard/static/js/pipeline-view.js +463 -0
  73. package/lib/vm/dashboard/static/js/quality-view.js +598 -0
  74. package/lib/vm/dashboard/static/js/system-view.js +1021 -0
  75. package/lib/vm/data-provider.js +1191 -0
  76. package/lib/vm/event-bus.js +402 -0
  77. package/lib/vm/hooks/extract-hook.js +307 -0
  78. package/lib/vm/hooks/generate-hook.js +374 -0
  79. package/lib/vm/hooks/hook-interface.js +458 -0
  80. package/lib/vm/hooks/report-hook.js +331 -0
  81. package/lib/vm/hooks/verify-hook.js +454 -0
  82. package/lib/vm/models.js +1003 -0
  83. package/lib/vm/reconciler.js +855 -0
  84. package/lib/vm/scanner.js +988 -0
  85. package/lib/vm/state-schema.js +955 -0
  86. package/lib/vm/state-store.js +733 -0
  87. package/lib/vm/tui/components/card.js +339 -0
  88. package/lib/vm/tui/components/progress-bar.js +368 -0
  89. package/lib/vm/tui/components/sparkline.js +327 -0
  90. package/lib/vm/tui/components/status-light.js +294 -0
  91. package/lib/vm/tui/components/table.js +370 -0
  92. package/lib/vm/tui/input.js +335 -0
  93. package/lib/vm/tui/renderer.js +548 -0
  94. package/lib/vm/tui/screens/kanban-screen.js +397 -0
  95. package/lib/vm/tui/screens/overview-screen.js +357 -0
  96. package/lib/vm/tui/screens/quality-screen.js +336 -0
  97. package/lib/vm/tui/screens/system-screen.js +379 -0
  98. package/lib/vm/tui/tui.js +805 -0
  99. package/package.json +1 -0
  100. package/scripts/cso-analyzer.js +198 -0
  101. package/scripts/eval-runner.js +359 -0
  102. package/scripts/i18n-checker.js +109 -0
  103. package/scripts/linter/activiti-linter.js +272 -0
  104. package/scripts/linter/prd-linter.js +162 -0
  105. package/scripts/linter/report-generator.js +207 -0
  106. package/scripts/linter/run-linters.js +285 -0
  107. package/scripts/linter/sql-linter.js +166 -0
  108. package/scripts/token-analyzer.js +162 -0
  109. package/scripts/vm-test.js +180 -0
  110. package/skills/core/official-doc-writer/LICENSE +21 -0
  111. package/skills/core/official-doc-writer/README.md +232 -0
  112. package/skills/core/official-doc-writer/SKILL.md +475 -0
  113. package/skills/core/official-doc-writer/_meta.json +1 -0
  114. package/skills/core/official-doc-writer/document_generator.py +580 -0
  115. package/skills/core/official-doc-writer/evals/default-evals.json +1 -0
  116. package/skills/core/official-doc-writer/examples.md +150 -0
  117. package/skills/core/official-doc-writer/fonts/FONTS_LIST.md +45 -0
  118. package/skills/core/official-doc-writer/fonts/README.md +141 -0
  119. package/skills/core/official-doc-writer/fonts/SIMFANG.TTF +0 -0
  120. package/skills/core/official-doc-writer/fonts/SIMHEI.TTF +0 -0
  121. package/skills/core/official-doc-writer/fonts/SIMKAI.TTF +0 -0
  122. package/skills/core/official-doc-writer/fonts/SIMSUN.TTC +0 -0
  123. package/skills/core/official-doc-writer/fonts//346/226/271/346/255/243/345/260/217/346/240/207/345/256/213GBK.TTF +0 -0
  124. package/skills/core/official-doc-writer/references/GBT_9704-2012_/345/205/232/346/224/277/346/234/272/345/205/263/345/205/254/346/226/207/346/240/274/345/274/217.md +422 -0
  125. package/skills/core/official-doc-writer/scripts/__pycache__/generate_official_doc.cpython-313.pyc +0 -0
  126. package/skills/core/official-doc-writer/scripts/dialog_manager.py +564 -0
  127. package/skills/core/official-doc-writer/scripts/generate_official_doc.py +252 -0
  128. package/skills/core/official-doc-writer/scripts/install_fonts.py +390 -0
  129. package/skills/core/official-doc-writer/scripts/smart_prompts.py +363 -0
  130. package/skills/core/pdd-ba/SKILL.md +305 -0
  131. package/skills/core/pdd-ba/_meta.json +1 -0
  132. package/skills/core/pdd-ba/evals/default-evals.json +1 -0
  133. package/skills/core/pdd-code-reviewer/SKILL.md +378 -0
  134. package/skills/core/pdd-code-reviewer/_meta.json +1 -0
  135. package/skills/core/pdd-code-reviewer/evals/default-evals.json +1 -0
  136. package/skills/core/pdd-doc-change/SKILL.md +350 -0
  137. package/skills/core/pdd-doc-change/_meta.json +1 -0
  138. package/skills/core/pdd-doc-change/evals/default-evals.json +1 -0
  139. package/skills/core/pdd-doc-gardener/SKILL.md +248 -0
  140. package/skills/core/pdd-doc-gardener/_meta.json +1 -0
  141. package/skills/core/pdd-doc-gardener/evals/default-evals.json +1 -0
  142. package/skills/core/pdd-entropy-reduction/SKILL.md +360 -0
  143. package/skills/core/pdd-entropy-reduction/_meta.json +1 -0
  144. package/skills/core/pdd-entropy-reduction/evals/default-evals.json +1 -0
  145. package/skills/core/pdd-entropy-reduction/references/entropy-report-template.md +287 -0
  146. package/skills/core/pdd-entropy-reduction/references/golden-principles.md +573 -0
  147. package/skills/core/pdd-entropy-reduction/scripts/entropy_scan.py +712 -0
  148. package/skills/core/pdd-extract-features/SKILL.md +320 -0
  149. package/skills/core/pdd-extract-features/_meta.json +1 -0
  150. package/skills/core/pdd-extract-features/evals/default-evals.json +1 -0
  151. package/skills/core/pdd-generate-spec/SKILL.md +418 -0
  152. package/skills/core/pdd-generate-spec/_meta.json +1 -0
  153. package/skills/core/pdd-generate-spec/evals/default-evals.json +1 -0
  154. package/skills/core/pdd-implement-feature/SKILL.md +332 -0
  155. package/skills/core/pdd-implement-feature/_meta.json +1 -0
  156. package/skills/core/pdd-implement-feature/evals/default-evals.json +1 -0
  157. package/skills/core/pdd-main/SKILL.md +540 -0
  158. package/skills/core/pdd-main/_meta.json +1 -0
  159. package/skills/core/pdd-main/evals/default-evals.json +1 -0
  160. package/skills/core/pdd-main/evals/evals.json +215 -0
  161. package/skills/core/pdd-verify-feature/SKILL.md +474 -0
  162. package/skills/core/pdd-verify-feature/_meta.json +1 -0
  163. package/skills/core/pdd-verify-feature/evals/default-evals.json +1 -0
  164. package/skills/core/pdd-vm/evals/default-evals.json +1 -0
  165. package/skills/core/traffic-accident-assessor/LICENSE +29 -0
  166. package/skills/core/traffic-accident-assessor/SKILL.md +439 -0
  167. package/skills/core/traffic-accident-assessor/evals/evals.json +1 -0
  168. package/skills/core/traffic-accident-assessor/references/accident-types.md +369 -0
  169. package/skills/core/traffic-accident-assessor/references/liability-rules.md +287 -0
  170. package/skills/core/traffic-accident-assessor/references/traffic-laws.md +226 -0
  171. package/skills/core/traffic-accident-assessor/references//351/253/230/345/260/224/345/244/253/350/257/264/346/230/216/344/271/246.pdf +32576 -106
  172. package/skills/core/traffic-accident-assessor/scripts/generate_official_statement.py +588 -0
  173. package/skills/core/traffic-accident-assessor/scripts/generate_report.py +495 -0
  174. package/skills/core/traffic-accident-assessor/scripts/generate_statement.py +528 -0
  175. package/skills/core/traffic-accident-assessor.zip +0 -0
  176. package/skills/entropy/expert-arch-enforcer/SKILL.md +292 -0
  177. package/skills/entropy/expert-arch-enforcer/_meta.json +1 -0
  178. package/skills/entropy/expert-arch-enforcer/evals/default-evals.json +1 -0
  179. package/skills/entropy/expert-auto-refactor/SKILL.md +327 -0
  180. package/skills/entropy/expert-auto-refactor/_meta.json +1 -0
  181. package/skills/entropy/expert-auto-refactor/evals/default-evals.json +1 -0
  182. package/skills/entropy/expert-code-quality/SKILL.md +468 -0
  183. package/skills/entropy/expert-code-quality/_meta.json +1 -0
  184. package/skills/entropy/expert-code-quality/evals/default-evals.json +1 -0
  185. package/skills/entropy/expert-code-quality/evals/evals.json +109 -0
  186. package/skills/entropy/expert-code-quality/references/code-smells.md +605 -0
  187. package/skills/entropy/expert-code-quality/references/design-patterns.md +1111 -0
  188. package/skills/entropy/expert-code-quality/references/refactoring-catalog.md +1281 -0
  189. package/skills/entropy/expert-code-quality/references/solid-principles.md +524 -0
  190. package/skills/entropy/expert-entropy-auditor/SKILL.md +276 -0
  191. package/skills/entropy/expert-entropy-auditor/_meta.json +1 -0
  192. package/skills/entropy/expert-entropy-auditor/evals/default-evals.json +1 -0
  193. package/skills/expert/expert-activiti/SKILL.md +497 -0
  194. package/skills/expert/expert-activiti/_meta.json +1 -0
  195. package/skills/expert/expert-mysql/SKILL.md +832 -0
  196. package/skills/expert/expert-mysql/_meta.json +1 -0
  197. package/skills/expert/expert-performance/SKILL.md +379 -0
  198. package/skills/expert/expert-performance/_meta.json +1 -0
  199. package/skills/expert/expert-performance/evals/default-evals.json +1 -0
  200. package/skills/expert/expert-ruoyi/SKILL.md +472 -0
  201. package/skills/expert/expert-ruoyi/_meta.json +1 -0
  202. package/skills/expert/expert-security/SKILL.md +1341 -0
  203. package/skills/expert/expert-security/_meta.json +1 -0
  204. package/skills/expert/expert-security/evals/default-evals.json +1 -0
  205. package/skills/expert/software-architect/SKILL.md +350 -0
  206. package/skills/expert/software-architect/_meta.json +1 -0
  207. package/skills/expert/software-engineer/SKILL.md +437 -0
  208. package/skills/expert/software-engineer/_meta.json +1 -0
  209. package/skills/expert/software-engineer/architecture.md +130 -0
  210. package/skills/expert/software-engineer/patterns.md +151 -0
  211. package/skills/expert/software-engineer/testing.md +135 -0
  212. package/skills/expert/system-architect/SKILL.md +628 -0
  213. package/skills/expert/system-architect/_meta.json +1 -0
  214. package/skills/expert/system-architect/assets/templates/ARCHITECTURE.md +25 -0
  215. package/skills/expert/system-architect/assets/templates/README.md +44 -0
  216. package/skills/expert/system-architect/references/js-ts-standards.md +18 -0
  217. package/skills/expert/system-architect/references/python-standards.md +19 -0
  218. package/skills/expert/system-architect/references/scaffolding.md +61 -0
  219. package/skills/expert/system-architect/references/security-checklist.md +21 -0
  220. package/skills/openspec/openspec-apply-change/SKILL.md +156 -0
  221. package/skills/openspec/openspec-apply-change/_meta.json +1 -0
  222. package/skills/openspec/openspec-archive-change/SKILL.md +114 -0
  223. package/skills/openspec/openspec-archive-change/_meta.json +1 -0
  224. package/skills/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
  225. package/skills/openspec/openspec-bulk-archive-change/_meta.json +1 -0
  226. package/skills/openspec/openspec-continue-change/SKILL.md +118 -0
  227. package/skills/openspec/openspec-continue-change/_meta.json +1 -0
  228. package/skills/openspec/openspec-explore/SKILL.md +288 -0
  229. package/skills/openspec/openspec-explore/_meta.json +1 -0
  230. package/skills/openspec/openspec-ff-change/SKILL.md +101 -0
  231. package/skills/openspec/openspec-ff-change/_meta.json +1 -0
  232. package/skills/openspec/openspec-new-change/SKILL.md +74 -0
  233. package/skills/openspec/openspec-new-change/_meta.json +1 -0
  234. package/skills/openspec/openspec-onboard/SKILL.md +554 -0
  235. package/skills/openspec/openspec-onboard/_meta.json +1 -0
  236. package/skills/openspec/openspec-sync-specs/SKILL.md +138 -0
  237. package/skills/openspec/openspec-sync-specs/_meta.json +1 -0
  238. package/skills/openspec/openspec-verify-change/SKILL.md +168 -0
  239. package/skills/openspec/openspec-verify-change/_meta.json +1 -0
  240. package/skills/pr/pdd-multi-review/SKILL.md +534 -0
  241. package/skills/pr/pdd-multi-review/_meta.json +1 -0
  242. package/skills/pr/pdd-pr-batch/SKILL.md +303 -0
  243. package/skills/pr/pdd-pr-batch/_meta.json +1 -0
  244. package/skills/pr/pdd-pr-create/SKILL.md +344 -0
  245. package/skills/pr/pdd-pr-create/_meta.json +1 -0
  246. package/skills/pr/pdd-pr-merge/SKILL.md +286 -0
  247. package/skills/pr/pdd-pr-merge/_meta.json +1 -0
  248. package/skills/pr/pdd-pr-review/SKILL.md +217 -0
  249. package/skills/pr/pdd-pr-review/_meta.json +1 -0
  250. package/skills/pr/pdd-task-manager/SKILL.md +636 -0
  251. package/skills/pr/pdd-task-manager/_meta.json +1 -0
  252. package/skills/pr/pdd-template-engine/SKILL.md +306 -0
  253. package/skills/pr/pdd-template-engine/_meta.json +1 -0
  254. package/templates/behavior-shaping/iron-law-template.md +87 -0
  255. package/templates/behavior-shaping/rationalization-template.md +62 -0
  256. package/templates/behavior-shaping/red-flags-template.md +70 -0
  257. package/templates/bilingual-template.md +139 -0
  258. package/templates/config/default.yaml +47 -0
  259. package/templates/project/default/README.md +31 -0
  260. package/templates/project/frontend/README.md +46 -0
  261. package/templates/project/java/README.md +48 -0
@@ -0,0 +1,463 @@
1
+ /**
2
+ * PipelineView - 流水线总览视图 (Panel 1)
3
+ * 展示项目整体进度、六阶段流水线、功能点分布和最近活动
4
+ *
5
+ * @module PipelineView
6
+ * @version 1.0.0
7
+ * @requires App (全局应用对象)
8
+ */
9
+
10
+ const PipelineView = {
11
+ name: 'pipeline',
12
+
13
+ // 阶段配置
14
+ stages: [
15
+ { id: 'prd', name: 'PRD', icon: '📋', color: '#9b59b6', description: '需求分析' },
16
+ { id: 'extracted', name: 'EXTRACTED', icon: '🔍', color: '#3498db', description: '功能提取' },
17
+ { id: 'spec', name: 'SPEC', icon: '📝', color: '#2ecc71', description: '规格生成' },
18
+ { id: 'implementing', name: 'IMPLEMENTING', icon: '⚙️', color: '#f39c12', description: '代码实现' },
19
+ { id: 'verifying', name: 'VERIFYING', icon: '✅', color: '#e74c3c', description: '质量验证' },
20
+ { id: 'done', name: 'DONE', icon: '🎉', color: '#1abc9c', description: '已完成' }
21
+ ],
22
+
23
+ /**
24
+ * 初始化视图
25
+ */
26
+ init() {
27
+ App.registerView(this.name, this);
28
+ console.log('[PipelineView] Initialized');
29
+ },
30
+
31
+ /**
32
+ * 视图显示时调用
33
+ */
34
+ onShow() {
35
+ this.render(App.state.features || [], App.state.summary || {});
36
+ },
37
+
38
+ /**
39
+ * SSE事件处理
40
+ * @param {string} type - 事件类型
41
+ * @param {*} data - 事件数据
42
+ */
43
+ onEvent(type, data) {
44
+ if (type === 'stage_change' || type === 'data_refreshed') {
45
+ this.render(App.state.features || [], App.state.summary || {});
46
+ }
47
+ },
48
+
49
+ /**
50
+ * 主渲染方法
51
+ * @param {Array} features - 功能点列表
52
+ * @param {Object} summary - 项目汇总数据
53
+ */
54
+ render(features, summary) {
55
+ const container = document.getElementById('view-pipeline');
56
+ if (!container) return;
57
+
58
+ container.innerHTML = `
59
+ <div class="pipeline-view">
60
+ ${this._renderSummaryCards(summary)}
61
+ ${this._renderPipelineStages(features)}
62
+ ${this._renderFeatureDistribution(features)}
63
+ ${this._renderActivityTimeline(features)}
64
+ </div>
65
+ `;
66
+
67
+ // 绑定阶段点击事件
68
+ this._bindStageClickEvents();
69
+ },
70
+
71
+ /**
72
+ * 渲染三张汇总卡片
73
+ * @param {Object} summary - 汇总数据
74
+ * @returns {string} HTML字符串
75
+ */
76
+ _renderSummaryCards(summary) {
77
+ const totalFeatures = summary.totalFeatures || 0;
78
+ const doneCount = summary.doneCount || 0;
79
+ const progressPercent = totalFeatures > 0 ? Math.round((doneCount / totalFeatures) * 100) : 0;
80
+
81
+ const avgScore = summary.avgScore || 0;
82
+ const grade = Charts.getGrade(avgScore);
83
+
84
+ const tokensUsed = summary.tokensUsed || 0;
85
+ const tokensTotal = summary.tokensTotal || 100000;
86
+ const tokenPercent = tokensTotal > 0 ? Math.round((tokensUsed / tokensTotal) * 100) : 0;
87
+
88
+ return `
89
+ <div class="grid-3 summary-cards">
90
+ <!-- 卡1: 整体进度 -->
91
+ <div class="card summary-card">
92
+ <div class="card-header">
93
+ <h3>整体进度</h3>
94
+ <span class="badge badge-info">${doneCount}/${totalFeatures}</span>
95
+ </div>
96
+ <div class="card-body progress-ring-container">
97
+ ${this._createProgressRing(progressPercent, 90)}
98
+ <div class="progress-stats">
99
+ <div class="stat-item">
100
+ <span class="stat-label">已完成</span>
101
+ <span class="stat-value">${doneCount}</span>
102
+ </div>
103
+ <div class="stat-item">
104
+ <span class="stat-label">进行中</span>
105
+ <span class="stat-value">${totalFeatures - doneCount}</span>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+
111
+ <!-- 卡2: 质量概览 -->
112
+ <div class="card summary-card">
113
+ <div class="card-header">
114
+ <h3>质量概览</h3>
115
+ <span class="badge badge-${grade.toLowerCase()}">${grade}</span>
116
+ </div>
117
+ <div class="card-body quality-overview">
118
+ <div class="quality-score">
119
+ <span class="score-number">${avgScore.toFixed(1)}</span>
120
+ <span class="score-max">/100</span>
121
+ </div>
122
+ <div class="grade-distribution">
123
+ ${this._renderGradeBadges(summary.gradeDistribution || {})}
124
+ </div>
125
+ </div>
126
+ </div>
127
+
128
+ <!-- 卡3: Token消耗 -->
129
+ <div class="card summary-card">
130
+ <div class="card-header">
131
+ <h3>Token 消耗</h3>
132
+ <span class="badge badge-warning">${tokenPercent}%</span>
133
+ </div>
134
+ <div class="card-body token-overview">
135
+ <div class="token-usage">
136
+ <div class="token-number">
137
+ <span class="token-used">${this._formatNumber(tokensUsed)}</span>
138
+ <span class="token-divider">/</span>
139
+ <span class="token-total">${this._formatNumber(tokensTotal)}</span>
140
+ </div>
141
+ <div class="token-bar">
142
+ <div class="token-bar-fill" style="width: ${Math.min(tokenPercent, 100)}%; background-color: ${tokenPercent > 95 ? '#e74c3c' : tokenPercent > 80 ? '#f39c12' : '#27ae60'}"></div>
143
+ </div>
144
+ </div>
145
+ <div class="token-trend">
146
+ <i class="trend-icon ${summary.tokenTrend >= 0 ? 'trend-up' : 'trend-down'}">${summary.tokenTrend >= 0 ? '↑' : '↓'}</i>
147
+ <span>较上次 +${Math.abs(summary.tokenTrend || 0).toFixed(1)}%</span>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ </div>
152
+ `;
153
+ },
154
+
155
+ /**
156
+ * 创建SVG环形进度条
157
+ * @param {number} percent - 百分比
158
+ * @param {number} size - 尺寸
159
+ * @returns {string} SVG HTML
160
+ */
161
+ _createProgressRing(percent, size = 80) {
162
+ const radius = (size - 12) / 2;
163
+ const circumference = 2 * Math.PI * radius;
164
+ const offset = circumference - (percent / 100) * circumference;
165
+ const color = percent >= 80 ? '#27ae60' : percent >= 50 ? '#f39c12' : '#e74c3c';
166
+
167
+ return `
168
+ <svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" class="progress-ring-svg">
169
+ <circle cx="${size/2}" cy="${size/2}" r="${radius}" fill="none" stroke="#ecf0f1" stroke-width="10"/>
170
+ <circle cx="${size/2}" cy="${size/2}" r="${radius}" fill="none" stroke="${color}" stroke-width="10"
171
+ stroke-dasharray="${circumference}" stroke-dashoffset="${offset}"
172
+ stroke-linecap="round"
173
+ transform="rotate(-90 ${size/2} ${size/2})"
174
+ style="transition: stroke-dashoffset 0.8s ease-in-out"/>
175
+ <text x="${size/2}" y="${size/2 + 5}" text-anchor="middle" font-size="18" font-weight="bold" fill="#2c3e50">
176
+ ${percent}%
177
+ </text>
178
+ </svg>
179
+ `;
180
+ },
181
+
182
+ /**
183
+ * 渲染等级徽章
184
+ * @param {Object} distribution - 等级分布
185
+ * @returns {string} HTML
186
+ */
187
+ _renderGradeBadges(distribution) {
188
+ const grades = ['S', 'A', 'B', 'C', 'D', 'F'];
189
+ const colors = {
190
+ S: '#27ae60', A: '#2980b9', B: '#f39c12',
191
+ C: '#e67e22', D: '#e74c3c', F: '#c0392b'
192
+ };
193
+
194
+ return grades.map(grade => `
195
+ <span class="grade-badge" style="background-color: ${colors[grade]}20; color: ${colors[grade]}; border: 1px solid ${colors[grade]}40;">
196
+ ${grade}: ${distribution[grade] || 0}
197
+ </span>
198
+ `).join('');
199
+ },
200
+
201
+ /**
202
+ * 渲染六阶段流水线图
203
+ * @param {Array} features - 功能点列表
204
+ * @returns {string} HTML
205
+ */
206
+ _renderPipelineStages(features) {
207
+ // 计算每个阶段的统计
208
+ const stageStats = {};
209
+ const total = features.length || 0;
210
+
211
+ this.stages.forEach(stage => {
212
+ stageStats[stage.id] = features.filter(f => f.stage === stage.id);
213
+ });
214
+
215
+ return `
216
+ <div class="card pipeline-card">
217
+ <div class="card-header">
218
+ <h3>开发流水线</h3>
219
+ <span class="text-muted">共 ${total} 个功能点</span>
220
+ </div>
221
+ <div class="card-body">
222
+ <div class="pipeline-container">
223
+ ${this.stages.map((stage, index) => {
224
+ const count = stageStats[stage.id] ? stageStats[stage.id].length : 0;
225
+ const percent = total > 0 ? Math.round((count / total) * 100) : 0;
226
+
227
+ return `
228
+ <div class="pipeline-stage" data-stage="${stage.id}">
229
+ <div class="stage-icon" style="background-color: ${stage.color}20; color: ${stage.color};">
230
+ ${stage.icon}
231
+ </div>
232
+ <div class="stage-name" style="color: ${stage.color};">${stage.name}</div>
233
+ <div class="stage-count">${count}</div>
234
+ <div class="stage-percent">${percent}%</div>
235
+ <div class="stage-progress-bar">
236
+ <div class="stage-progress-fill" style="width: ${percent}%; background-color: ${stage.color};"></div>
237
+ </div>
238
+ <div class="stage-description">${stage.description}</div>
239
+ ${index < this.stages.length - 1 ? `<div class="stage-arrow">→</div>` : ''}
240
+ </div>
241
+ `;
242
+ }).join('')}
243
+ </div>
244
+ </div>
245
+ </div>
246
+ `;
247
+ },
248
+
249
+ /**
250
+ * 渲染功能点阶段分布表
251
+ * @param {Array} features - 功能点列表
252
+ * @returns {string} HTML
253
+ */
254
+ _renderFeatureDistribution(features) {
255
+ if (!features || features.length === 0) {
256
+ return '';
257
+ }
258
+
259
+ // 只显示前15个,避免过长
260
+ const displayFeatures = features.slice(0, 15);
261
+
262
+ return `
263
+ <div class="card distribution-card">
264
+ <div class="card-header">
265
+ <h3>功能点阶段分布</h3>
266
+ <span class="text-muted">显示前 ${displayFeatures.length} / ${features.length} 个</span>
267
+ </div>
268
+ <div class="card-body">
269
+ <table class="data-table feature-table">
270
+ <thead>
271
+ <tr>
272
+ <th>功能点名称</th>
273
+ <th>当前阶段</th>
274
+ <th>评分</th>
275
+ <th>状态</th>
276
+ <th>操作</th>
277
+ </tr>
278
+ </thead>
279
+ <tbody>
280
+ ${displayFeatures.map(feature => {
281
+ const stageConfig = this.stages.find(s => s.id === feature.stage) || this.stages[0];
282
+ const score = feature.quality?.score || 0;
283
+ const grade = Charts.getGrade(score);
284
+
285
+ return `
286
+ <tr>
287
+ <td class="feature-name-cell">
288
+ <span class="feature-name" title="${feature.name}">${this._truncate(feature.name, 30)}</span>
289
+ </td>
290
+ <td>
291
+ <span class="badge" style="background-color: ${stageConfig.color}20; color: ${stageConfig.color};">
292
+ ${stageConfig.icon} ${stageConfig.name}
293
+ </span>
294
+ </td>
295
+ <td>
296
+ <span class="score-text" style="color: ${Charts.getColor(score)};">${score.toFixed(1)} (${grade})</span>
297
+ </td>
298
+ <td>
299
+ <span class="status-dot status-${feature.status || 'active'}"></span>
300
+ ${feature.status || 'active'}
301
+ </td>
302
+ <td>
303
+ <button class="btn btn-sm btn-outline" onclick="KanbanView.showFeatureDetail('${feature.id}')">
304
+ 详情
305
+ </button>
306
+ </td>
307
+ </tr>
308
+ `;
309
+ }).join('')}
310
+ </tbody>
311
+ </table>
312
+ </div>
313
+ </div>
314
+ `;
315
+ },
316
+
317
+ /**
318
+ * 渲染最近活动时间线
319
+ * @param {Array} features - 功能点列表
320
+ * @returns {string} HTML
321
+ */
322
+ _renderActivityTimeline(features) {
323
+ // 从所有功能的timeline中收集最近的10条活动
324
+ let allActivities = [];
325
+
326
+ (features || []).forEach(feature => {
327
+ if (feature.timeline && Array.isArray(feature.timeline)) {
328
+ feature.timeline.forEach(entry => {
329
+ allActivities.push({
330
+ ...entry,
331
+ featureName: feature.name,
332
+ featureId: feature.id
333
+ });
334
+ });
335
+ }
336
+ });
337
+
338
+ // 按时间倒序排列,取前10条
339
+ allActivities.sort((a, b) => new Date(b.timestamp || b.time) - new Date(a.timestamp || a.time));
340
+ const recentActivities = allActivities.slice(0, 10);
341
+
342
+ if (recentActivities.length === 0) {
343
+ return `
344
+ <div class="card timeline-card">
345
+ <div class="card-header">
346
+ <h3>最近活动</h3>
347
+ </div>
348
+ <div class="card-body text-center text-muted py-4">
349
+ 暂无活动记录
350
+ </div>
351
+ </div>
352
+ `;
353
+ }
354
+
355
+ return `
356
+ <div class="card timeline-card">
357
+ <div class="card-header">
358
+ <h3>最近活动</h3>
359
+ <span class="text-muted">最近 10 条</span>
360
+ </div>
361
+ <div class="card-body">
362
+ <div class="timeline">
363
+ ${recentActivities.map((activity, index) => {
364
+ const timeStr = this._formatTime(activity.timestamp || activity.time);
365
+ const stageConfig = this.stages.find(s =>
366
+ activity.toStage === s.id || activity.stage === s.id
367
+ );
368
+ const dotColor = stageConfig ? stageConfig.color : '#95a5a6';
369
+
370
+ return `
371
+ <div class="timeline-item ${index === 0 ? 'latest' : ''}">
372
+ <div class="timeline-dot" style="background-color: ${dotColor};"></div>
373
+ <div class="timeline-content">
374
+ <div class="timeline-header">
375
+ <strong>${activity.featureName || '未知功能点'}</strong>
376
+ <span class="timeline-time">${timeStr}</span>
377
+ </div>
378
+ <div class="timeline-description">
379
+ ${activity.description || `${activity.type || '操作'}: ${activity.fromStage || ''} → ${activity.toStage || activity.stage || ''}`}
380
+ </div>
381
+ </div>
382
+ </div>
383
+ `;
384
+ }).join('')}
385
+ </div>
386
+ </div>
387
+ </div>
388
+ `;
389
+ },
390
+
391
+ /**
392
+ * 绑定阶段点击事件
393
+ */
394
+ _bindStageClickEvents() {
395
+ document.querySelectorAll('.pipeline-stage[data-stage]').forEach(stageEl => {
396
+ stageEl.addEventListener('click', () => {
397
+ const stageId = stageEl.dataset.stage;
398
+ // 切换到看板视图并筛选该阶段
399
+ window.location.hash = '#kanban';
400
+ // 延迟执行以确保视图已切换
401
+ setTimeout(() => {
402
+ if (typeof KanbanView !== 'undefined') {
403
+ KanbanView.setFilter('stage', stageId);
404
+ }
405
+ }, 100);
406
+ });
407
+
408
+ stageEl.style.cursor = 'pointer';
409
+ stageEl.title = '点击查看该阶段的功能点';
410
+ });
411
+ },
412
+
413
+ // ==================== 工具方法 ====================
414
+
415
+ /**
416
+ * 格式化数字(添加千位分隔符)
417
+ * @param {number} num
418
+ * @returns {string}
419
+ */
420
+ _formatNumber(num) {
421
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
422
+ },
423
+
424
+ /**
425
+ * 截断文本
426
+ * @param {string} text - 文本
427
+ * @param {number} maxLength - 最大长度
428
+ * @returns {string}
429
+ */
430
+ _truncate(text, maxLength = 50) {
431
+ if (!text) return '';
432
+ return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
433
+ },
434
+
435
+ /**
436
+ * 格式化时间
437
+ * @param {string|Date} time - 时间
438
+ * @returns {string}
439
+ */
440
+ _formatTime(time) {
441
+ if (!time) return '';
442
+
443
+ try {
444
+ const date = new Date(time);
445
+ const now = new Date();
446
+ const diff = now - date;
447
+
448
+ if (diff < 60000) return '刚刚';
449
+ if (diff < 3600000) return `${Math.floor(diff / 60000)} 分钟前`;
450
+ if (diff < 86400000) return `${Math.floor(diff / 3600000)} 小时前`;
451
+
452
+ return date.toLocaleDateString('zh-CN') + ' ' + date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
453
+ } catch (e) {
454
+ return String(time);
455
+ }
456
+ }
457
+ };
458
+
459
+ // 初始化
460
+ PipelineView.init();
461
+
462
+ // 导出全局使用
463
+ window.PipelineView = PipelineView;