@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.
- package/dist/charts/index.js +3 -3
- package/dist/charts/modules/bug-by-assignee.js +16 -5
- package/dist/charts/modules/bug-by-defect-analysis.js +9 -3
- package/dist/charts/modules/bug-by-developer-hours.js +23 -5
- package/dist/charts/modules/bug-by-fix-duration.js +7 -4
- package/dist/charts/modules/bug-by-module.js +7 -5
- package/dist/charts/modules/bug-open-priority-heatmap.js +5 -2
- package/dist/charts/renderer.js +70 -14
- package/dist/commands/fix.command.js +18 -10
- package/package.json +1 -1
package/dist/charts/index.js
CHANGED
|
@@ -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: '
|
|
18
|
-
xAxis: {
|
|
19
|
-
|
|
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: '#
|
|
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: ['
|
|
22
|
+
radius: ['42%', '68%'],
|
|
23
23
|
avoidLabelOverlap: true,
|
|
24
|
-
label: {
|
|
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: '
|
|
20
|
-
xAxis: {
|
|
21
|
-
|
|
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: {
|
|
27
|
-
|
|
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: '
|
|
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: '#
|
|
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: '
|
|
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:
|
|
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: '#
|
|
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: '
|
|
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', '#
|
|
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
|
},
|
package/dist/charts/renderer.js
CHANGED
|
@@ -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}"
|
|
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 `
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
.
|
|
115
|
-
.
|
|
116
|
-
.
|
|
117
|
-
.meta-
|
|
118
|
-
.
|
|
119
|
-
.
|
|
120
|
-
.
|
|
121
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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.
|
|
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.
|
|
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(
|
|
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 || '未填写'}`);
|