@hecom/codearts 0.4.1 → 0.4.2

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.
@@ -8,10 +8,10 @@ const bug_by_fix_duration_1 = require("./modules/bug-by-fix-duration");
8
8
  const bug_by_developer_hours_1 = require("./modules/bug-by-developer-hours");
9
9
  const bug_open_priority_heatmap_1 = require("./modules/bug-open-priority-heatmap");
10
10
  exports.allCharts = [
11
- bug_by_defect_analysis_1.bugByDefectAnalysisChart,
12
- bug_by_assignee_1.bugByAssigneeChart,
13
11
  bug_by_module_1.bugByModuleChart,
12
+ bug_by_defect_analysis_1.bugByDefectAnalysisChart,
14
13
  bug_by_fix_duration_1.bugByFixDurationChart,
15
- bug_by_developer_hours_1.bugByDeveloperHoursChart,
16
14
  bug_open_priority_heatmap_1.bugOpenPriorityHeatmapChart,
15
+ bug_by_assignee_1.bugByAssigneeChart,
16
+ bug_by_developer_hours_1.bugByDeveloperHoursChart,
17
17
  ];
@@ -14,15 +14,26 @@ exports.bugByAssigneeChart = {
14
14
  const values = sorted.map(([, value]) => value);
15
15
  return {
16
16
  tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
17
- grid: { left: '3%', right: '8%', bottom: '3%', containLabel: true },
18
- xAxis: { type: 'value' },
19
- yAxis: { type: 'category', data: names, inverse: true },
17
+ grid: { left: '3%', right: '6%', bottom: '3%', containLabel: true },
18
+ xAxis: {
19
+ type: 'value',
20
+ axisLine: { show: false },
21
+ splitLine: { lineStyle: { color: '#f0f3f7' } },
22
+ },
23
+ yAxis: {
24
+ type: 'category',
25
+ data: names,
26
+ inverse: true,
27
+ axisLine: { show: false },
28
+ axisTick: { show: false },
29
+ },
20
30
  series: [
21
31
  {
22
32
  type: 'bar',
23
33
  data: values,
24
- label: { show: true, position: 'right' },
25
- itemStyle: { color: '#5470c6' },
34
+ label: { show: true, position: 'right', color: '#111827', fontWeight: 600 },
35
+ itemStyle: { color: '#4A6CF7', borderRadius: 6 },
36
+ barCategoryGap: '40%',
26
37
  },
27
38
  ],
28
39
  };
@@ -15,13 +15,19 @@ exports.bugByDefectAnalysisChart = {
15
15
  .map(([name, value]) => ({ name, value }));
16
16
  return {
17
17
  tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
18
- legend: { orient: 'vertical', left: 'left', type: 'scroll' },
18
+ legend: { orient: 'vertical', left: 'left', type: 'scroll', textStyle: { color: '#666' } },
19
19
  series: [
20
20
  {
21
21
  type: 'pie',
22
- radius: ['40%', '70%'],
22
+ radius: ['42%', '68%'],
23
23
  avoidLabelOverlap: true,
24
- label: { show: true, formatter: '{b}: {d}%' },
24
+ label: {
25
+ show: true,
26
+ position: 'outside',
27
+ formatter: '{b}: {d}%',
28
+ color: '#111827',
29
+ },
30
+ labelLine: { length: 18, length2: 6, lineStyle: { width: 1 } },
25
31
  data,
26
32
  },
27
33
  ],
@@ -16,15 +16,33 @@ exports.bugByDeveloperHoursChart = {
16
16
  const values = sorted.map(([, hours]) => hours);
17
17
  return {
18
18
  tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
19
- grid: { left: '3%', right: '8%', bottom: '3%', containLabel: true },
20
- xAxis: { type: 'value', name: '小时' },
21
- yAxis: { type: 'category', data: names, inverse: true },
19
+ grid: { left: '3%', right: '6%', bottom: '3%', containLabel: true },
20
+ xAxis: {
21
+ type: 'value',
22
+ name: '小时',
23
+ axisLine: { show: false },
24
+ splitLine: { lineStyle: { color: '#f0f3f7' } },
25
+ },
26
+ yAxis: {
27
+ type: 'category',
28
+ data: names,
29
+ inverse: true,
30
+ axisLine: { show: false },
31
+ axisTick: { show: false },
32
+ },
22
33
  series: [
23
34
  {
24
35
  type: 'bar',
25
36
  data: values,
26
- label: { show: true, position: 'right', formatter: '{c}h' },
27
- itemStyle: { color: '#ee6666' },
37
+ label: {
38
+ show: true,
39
+ position: 'right',
40
+ formatter: '{c}h',
41
+ color: '#111827',
42
+ fontWeight: 600,
43
+ },
44
+ itemStyle: { color: '#6AD3FF', borderRadius: 6 },
45
+ barCategoryGap: '40%',
28
46
  },
29
47
  ],
30
48
  };
@@ -27,18 +27,21 @@ exports.bugByFixDurationChart = {
27
27
  const values = BUCKETS.map((b) => countMap.get(b) ?? 0);
28
28
  return {
29
29
  tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
30
- grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
30
+ grid: { left: '6%', right: '6%', bottom: '8%', containLabel: true },
31
31
  xAxis: {
32
32
  type: 'category',
33
33
  data: [...BUCKETS],
34
+ axisLine: { show: false },
35
+ axisTick: { show: false },
34
36
  },
35
- yAxis: { type: 'value', minInterval: 1 },
37
+ yAxis: { type: 'value', minInterval: 1, splitLine: { lineStyle: { color: '#f0f3f7' } } },
36
38
  series: [
37
39
  {
38
40
  type: 'bar',
39
41
  data: values,
40
- label: { show: true, position: 'top' },
41
- itemStyle: { color: '#fac858' },
42
+ label: { show: true, position: 'top', color: '#111827' },
43
+ itemStyle: { color: '#FFD66B', borderRadius: [6, 6, 0, 0] },
44
+ barWidth: '46%',
42
45
  },
43
46
  ],
44
47
  };
@@ -14,19 +14,21 @@ exports.bugByModuleChart = {
14
14
  const values = sorted.map(([, value]) => value);
15
15
  return {
16
16
  tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
17
- grid: { left: '3%', right: '4%', bottom: '15%', containLabel: true },
17
+ grid: { left: '4%', right: '4%', bottom: '18%', containLabel: true },
18
18
  xAxis: {
19
19
  type: 'category',
20
20
  data: names,
21
- axisLabel: { rotate: 30, overflow: 'truncate', width: 80 },
21
+ axisLabel: { rotate: 30, overflow: 'truncate', width: 90, color: '#666' },
22
+ axisLine: { show: false },
22
23
  },
23
- yAxis: { type: 'value' },
24
+ yAxis: { type: 'value', splitLine: { lineStyle: { color: '#f0f3f7' } } },
24
25
  series: [
25
26
  {
26
27
  type: 'bar',
27
28
  data: values,
28
- label: { show: true, position: 'top' },
29
- itemStyle: { color: '#91cc75' },
29
+ label: { show: true, position: 'top', color: '#111827' },
30
+ itemStyle: { color: '#6BCB9B', borderRadius: 6 },
31
+ barWidth: '48%',
30
32
  },
31
33
  ],
32
34
  };
@@ -38,16 +38,18 @@ exports.bugOpenPriorityHeatmapChart = {
38
38
  position: 'top',
39
39
  formatter: (params) => `${yCategories[params.data[1]]} / ${xCategories[params.data[0]]}: ${params.data[2]} 个`,
40
40
  },
41
- grid: { left: '3%', right: '4%', bottom: '10%', containLabel: true },
41
+ grid: { left: '4%', right: '4%', bottom: '12%', containLabel: true },
42
42
  xAxis: {
43
43
  type: 'category',
44
44
  data: xCategories,
45
45
  splitArea: { show: true },
46
+ axisLabel: { color: '#666' },
46
47
  },
47
48
  yAxis: {
48
49
  type: 'category',
49
50
  data: yCategories,
50
51
  splitArea: { show: true },
52
+ axisLabel: { color: '#666' },
51
53
  },
52
54
  visualMap: {
53
55
  min: 0,
@@ -56,7 +58,7 @@ exports.bugOpenPriorityHeatmapChart = {
56
58
  orient: 'horizontal',
57
59
  left: 'center',
58
60
  bottom: '0%',
59
- inRange: { color: ['#ffffff', '#ee6666'] },
61
+ inRange: { color: ['#ffffff', '#FF9A76'] },
60
62
  },
61
63
  series: [
62
64
  {
@@ -65,6 +67,7 @@ exports.bugOpenPriorityHeatmapChart = {
65
67
  label: {
66
68
  show: true,
67
69
  formatter: (params) => params.data[2] > 0 ? String(params.data[2]) : '',
70
+ color: '#111827',
68
71
  },
69
72
  emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0, 0, 0, 0.5)' } },
70
73
  },
@@ -90,15 +90,53 @@ function buildHtml(bugs, charts, meta) {
90
90
  const chartContainers = charts
91
91
  .map((chart, i) => `<div class="chart-card">
92
92
  <div class="chart-title">${chart.title}</div>
93
- <div id="chart-${i}" style="height:400px;"></div>
93
+ <div id="chart-${i}" class="chart-canvas"></div>
94
94
  </div>`)
95
95
  .join('\n');
96
96
  const chartScripts = charts
97
97
  .map((chart, i) => {
98
98
  const option = JSON.stringify(chart.buildOption(bugs));
99
- return `echarts.init(document.getElementById('chart-${i}')).setOption(${option});`;
99
+ return `(() => {
100
+ const el = document.getElementById('chart-${i}');
101
+ if (!el) return;
102
+ const myChart = echarts.init(el);
103
+ const defaultTheme = {
104
+ color: ['#4A6CF7', '#6AD3FF', '#6BCB9B', '#FFD66B', '#FF9A76', '#7E63FF', '#FFA2EC'],
105
+ textStyle: { color: '#2b2b2b', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif' },
106
+ tooltip: { backgroundColor: 'rgba(0,0,0,0.75)', textStyle: { color: '#fff' } },
107
+ legend: { textStyle: { color: '#666' } },
108
+ axis: { axisLine: { lineStyle: { color: '#e9edf1' } }, axisLabel: { color: '#9aa0a6' }, splitLine: { lineStyle: { color: '#f0f3f7' } } }
109
+ };
110
+ // merge defaults shallowly; chart option will override defaults where provided
111
+ const userOption = ${option};
112
+ const merged = Object.assign({}, defaultTheme, userOption);
113
+ myChart.setOption(merged, true);
114
+ // store instance for global resize handling
115
+ window.__HECOM_CHARTS = window.__HECOM_CHARTS || [];
116
+ window.__HECOM_CHARTS.push(myChart);
117
+ // ensure initial render after layout
118
+ setTimeout(() => myChart.resize(), 60);
119
+ })();`;
100
120
  })
101
- .join('\n');
121
+ .join('\n') +
122
+ `
123
+ ;(function(){
124
+ if (window.__HECOM_CHARTS_RESIZE_ADDED) return;
125
+ window.__HECOM_CHARTS_RESIZE_ADDED = true;
126
+ let _rt;
127
+ function resizeAll(){
128
+ (window.__HECOM_CHARTS || []).forEach(c => { try { c.resize(); } catch(e){} });
129
+ }
130
+ window.addEventListener('resize', () => { clearTimeout(_rt); _rt = setTimeout(resizeAll, 120); });
131
+ window.addEventListener('load', () => setTimeout(resizeAll, 100));
132
+ // in case layout changes without window resize (e.g. CSS media query), observe container
133
+ try {
134
+ const ro = new ResizeObserver(() => { clearTimeout(_rt); _rt = setTimeout(resizeAll, 80); });
135
+ document.querySelectorAll('.chart-canvas').forEach(el => ro.observe(el));
136
+ } catch (e) {
137
+ // ResizeObserver not available -> fallback only to window resize
138
+ }
139
+ })();`;
102
140
  const iterationsText = meta.iterationNames.join(', ') || '全部';
103
141
  const terminalText = meta.terminalTypes.join(', ') || '全部';
104
142
  return `<!DOCTYPE html>
@@ -108,20 +146,36 @@ function buildHtml(bugs, charts, meta) {
108
146
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
109
147
  <title>Bug 分析报告</title>
110
148
  <style>
111
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 0; padding: 20px; background: #f5f5f5; color: #333; }
112
- .header { background: #fff; border-radius: 8px; padding: 24px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
113
- .header h1 { margin: 0 0 16px; font-size: 22px; color: #1a1a1a; }
114
- .meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
115
- .meta-item { background: #f8f9fa; border-radius: 6px; padding: 12px; }
116
- .meta-label { font-size: 12px; color: #888; margin-bottom: 4px; }
117
- .meta-value { font-size: 14px; font-weight: 600; color: #333; }
118
- .charts-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
119
- .chart-card { background: #fff; border-radius: 8px; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
120
- .chart-title { font-size: 15px; font-weight: 600; color: #1a1a1a; margin-bottom: 12px; }
121
- @media (max-width: 900px) { .charts-grid { grid-template-columns: 1fr; } }
149
+ :root{ --bg:#f5f7fa; --card:#ffffff; --muted:#9aa0a6; --text:#2b2b2b; --accent:#4A6CF7; }
150
+ html,body{height:100%;}
151
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 0; padding: 28px 28px 64px; background: var(--bg); color: var(--text); -webkit-font-smoothing:antialiased; }
152
+ .container{max-width:1200px;margin:0 auto}
153
+ .header { background: var(--card); border-radius: 10px; padding: 28px; margin-bottom: 22px; box-shadow: 0 6px 18px rgba(18,23,34,0.06); }
154
+ .header h1 { margin: 0 0 10px; font-size: 20px; color: #111827; }
155
+ .meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
156
+ .meta-item { background: #f8f9fb; border-radius: 8px; padding: 14px; }
157
+ .meta-label { font-size: 12px; color: #818a91; margin-bottom: 6px; }
158
+ .meta-value { font-size: 15px; font-weight: 700; color: #111827; }
159
+ .charts-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; align-items: start; }
160
+ .chart-card { background: var(--card); border-radius: 10px; padding: 18px; box-shadow: 0 6px 18px rgba(18,23,34,0.04); display:flex; flex-direction:column; }
161
+ .chart-title { font-size: 15px; font-weight: 600; color: #111827; margin-bottom: 12px; }
162
+ .chart-canvas { width:100%; height:400px; }
163
+ .page-footer-spacer{height:56px;flex:0 0 56px}
164
+ /* card footer or small notes */
165
+ .muted { color: var(--muted); font-size:12px }
166
+
167
+ @media (max-width: 1199px){ .container{max-width:980px} }
168
+ @media (max-width: 900px) {
169
+ body{padding:16px}
170
+ .charts-grid { grid-template-columns: 1fr; }
171
+ .chart-canvas{height:320px}
172
+ .header { padding:18px }
173
+ }
174
+ @media (max-width: 480px){ .chart-canvas{height:280px} }
122
175
  </style>
123
176
  </head>
124
177
  <body>
178
+ <div class="container">
125
179
  <div class="header">
126
180
  <h1>Bug 分析报告</h1>
127
181
  <div class="meta-grid">
@@ -134,6 +188,8 @@ function buildHtml(bugs, charts, meta) {
134
188
  <div class="charts-grid">
135
189
  ${chartContainers}
136
190
  </div>
191
+ <div class="page-footer-spacer" aria-hidden="true"></div>
192
+ </div>
137
193
  <script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
138
194
  <script>
139
195
  ${chartScripts}
@@ -52,9 +52,11 @@ async function fixCommand(cliOptions) {
52
52
  logger_1.logger.info('操作取消');
53
53
  return;
54
54
  }
55
- // Step 5: 填写缺陷分析信息
55
+ // Step 5: 检查是否为客户反馈 bug
56
+ const isCustomerFeedback = checkIsCustomerFeedback(selectedBug);
57
+ // Step 6: 填写缺陷分析信息
56
58
  const bugFixData = {};
57
- // 5.1: 选择缺陷技术分析(必填)
59
+ // 6.1: 选择缺陷技术分析(必填)
58
60
  const defectAnalysisOptions = customFieldOptions[types_1.CustomFieldId.DEFECT_TECHNICAL_ANALYSIS] || [];
59
61
  if (defectAnalysisOptions.length === 0) {
60
62
  logger_1.logger.error('无法获取缺陷技术分析选项');
@@ -67,24 +69,30 @@ async function fixCommand(cliOptions) {
67
69
  value: option,
68
70
  })),
69
71
  });
70
- // 5.2: 填写问题原因及解决办法(可选)
72
+ // 6.2: 填写问题原因及解决办法
71
73
  const problemReason = await (0, prompts_1.input)({
72
- message: '请输入问题原因及解决办法(可选,留空跳过)',
74
+ message: isCustomerFeedback
75
+ ? '请输入问题原因及解决办法(必填)'
76
+ : '请输入问题原因及解决办法(可选,留空跳过)',
77
+ validate: (value) => {
78
+ if (isCustomerFeedback && !value.trim()) {
79
+ return '问题原因及解决办法不能为空';
80
+ }
81
+ return true;
82
+ },
73
83
  });
74
84
  if (problemReason.trim()) {
75
85
  bugFixData.problemReason = problemReason;
76
86
  }
77
- // 5.3: 填写影响范围(可选)
87
+ // 6.3: 填写影响范围(可选)
78
88
  const impactScope = await (0, prompts_1.input)({
79
89
  message: '请输入影响范围(可选,留空跳过)',
80
90
  });
81
91
  if (impactScope.trim()) {
82
92
  bugFixData.impactScope = impactScope;
83
93
  }
84
- // Step 6: 检查是否为客户反馈 bug
85
- const isCustomerFeedback = checkIsCustomerFeedback(selectedBug);
86
94
  if (isCustomerFeedback) {
87
- // 6.1: 选择引入阶段(必填)
95
+ // 6.4: 选择引入阶段(必填)
88
96
  const introductionPhaseOptions = customFieldOptions[types_1.CustomFieldId.INTRODUCTION_PHASE] || [];
89
97
  if (introductionPhaseOptions.length === 0) {
90
98
  logger_1.logger.error('无法获取引入阶段选项');
@@ -100,7 +108,7 @@ async function fixCommand(cliOptions) {
100
108
  value: option,
101
109
  })),
102
110
  });
103
- // 6.2: 输入发布时间(必填)
111
+ // 6.5: 输入发布时间(必填)
104
112
  let releaseDate;
105
113
  let validDate = false;
106
114
  while (!validDate) {
@@ -205,7 +213,7 @@ async function showSummaryAndConfirm(bug, bugFixData, isCustomerFeedback) {
205
213
  logger_1.logger.info(`Bug ID: #${bug.id}`);
206
214
  logger_1.logger.info(`Bug 标题: ${bug.name}`);
207
215
  logger_1.logger.info(`缺陷技术分析: ${bugFixData.defectAnalysis || '未填写'}`);
208
- logger_1.logger.info(`问题原因: ${bugFixData.problemReason || '未填写'}`);
216
+ logger_1.logger.info(`问题原因及解决办法: ${bugFixData.problemReason || '未填写'}`);
209
217
  logger_1.logger.info(`影响范围: ${bugFixData.impactScope || '未填写'}`);
210
218
  if (isCustomerFeedback) {
211
219
  logger_1.logger.info(`引入阶段: ${bugFixData.introductionStage || '未填写'}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hecom/codearts",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "华为云 CodeArts 统计分析工具",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",