排列图(帕累托图)制作技能
触发条件
当用户提到以下关键词时激活:
- 排列图、帕累托图、Pareto 图
- 质量分析图、出错分析图、缺陷分析图
- 二八法则图、关键少数分析
- "帮我画一个 XX 出错的排列图"
制作规范(用户确认,严格执行)
| 规范项 | 要求 | |--------|------| | 左轴最大值 | = 各项因素总和(不是任意整数) | | 80% 基准线 | 不需要 | | 累计百分比折线 | 从 0% 起始(左下角 → 第一柱右上角 → 依次连接) | | 折线点位置 | 每项柱体的右上角(右边缘顶部),非柱体中心 | | 柱体间隙 | 无间隙(紧密相连) | | 柱体颜色 | 每项不同颜色,色相阶梯:红 #D7263D → 橙 #F46036 → 黄 #F5A623 → 绿 #3CCB7F → 蓝 #2E86DE → 紫 #9B59B6 | | 折线点标注 | 每个点上方标注累计百分比(如 35.0%) | | 输出格式 | HTML 文件(Chart.js + 自定义 plugin) |
制作步骤
1. 数据准备
- 用户提供数据 → 直接使用
- 用户未提供数据 → 根据场景编造合理的模拟数据(项目名称和次数要符合实际业务场景)
- 将数据按出错次数从大到小排列
- 计算每项占比、累计次数、累计百分比
2. 生成 HTML 文件
使用以下模板生成完整的 HTML 文件。关键:必须使用自定义 paretoLinePlugin 来绘制折线,Chart.js 默认的 line 数据集点会落在柱体中心,必须通过 plugin 手动定位到右上角。
保存路径:用户指定目录(默认桌面 C:\Users\34630\Desktop\),文件名格式:{主题}出错类型排列图.html
3. HTML 模板
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{TITLE}}排列图</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: "Microsoft YaHei", "PingFang SC", sans-serif; background: #fff; padding: 40px; }
.container { max-width: 900px; margin: 0 auto; }
h1 { text-align: center; font-size: 20px; color: #2C2C2A; margin-bottom: 6px; }
.subtitle { text-align: center; font-size: 13px; color: #888780; margin-bottom: 24px; }
.chart-wrap { position: relative; height: 520px; background: #fff; }
table.data { width: 100%; border-collapse: collapse; margin-top: 24px; font-size: 13px; }
table.data th, table.data td { border: 1px solid #E5E4E0; padding: 8px 12px; text-align: center; }
table.data th { background: #F5F4F0; color: #2C2C2A; font-weight: 600; }
table.data td { color: #5F5E5A; }
.key { font-weight: 600; color: #D7263D; }
.analysis {
margin-top: 24px; padding: 16px 20px; background: #FBFAF6;
border-left: 4px solid #2E5EAA; font-size: 13px; color: #5F5E5A; line-height: 1.8;
}
.analysis strong { color: #2C2C2A; }
</style>
</head>
<body>
<div class="container">
<h1>{{TITLE}}</h1>
<div class="subtitle">{{SUBTITLE}}</div>
<div class="chart-wrap"><canvas id="chart"></canvas></div>
<table class="data">
<thead>
<tr><th>序号</th><th>出错类型</th><th>出错次数</th><th>占比</th><th>累计次数</th><th>累计百分比</th></tr>
</thead>
<tbody>
{{TABLE_ROWS}}
</tbody>
</table>
<div class="analysis">{{ANALYSIS}}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<script>
const labels = {{LABELS_ARRAY}};
const errors = {{ERRORS_ARRAY}};
const cumulative = {{CUMULATIVE_ARRAY}};
const barColors = ['#D7263D', '#F46036', '#F5A623', '#3CCB7F', '#2E86DE', '#9B59B6'];
const paretoLinePlugin = {
id: 'paretoLine',
afterDatasetsDraw(chart) {
const barMeta = chart.getDatasetMeta(0);
const y1 = chart.scales.y1;
const cumData = chart.data.datasets[1].data;
const c = chart.ctx;
if (!barMeta.data.length) return;
c.save();
const points = [];
const firstBar = barMeta.data[0];
const startX = firstBar.x - firstBar.width / 2;
const startY = y1.getPixelForValue(0);
points.push({ x: startX, y: startY, label: null });
barMeta.data.forEach((bar, i) => {
const barRight = bar.x + bar.width / 2;
const ptY = y1.getPixelForValue(cumData[i]);
points.push({ x: barRight, y: ptY, label: cumData[i] });
});
c.beginPath();
c.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length; i++) c.lineTo(points[i].x, points[i].y);
c.strokeStyle = '#2C2C2A';
c.lineWidth = 2;
c.stroke();
points.forEach((p) => {
c.beginPath();
c.arc(p.x, p.y, 5, 0, Math.PI * 2);
c.fillStyle = '#2C2C2A';
c.fill();
c.strokeStyle = '#fff';
c.lineWidth = 2;
c.stroke();
if (p.label !== null) {
c.fillStyle = '#2C2C2A';
c.font = 'bold 11px "Microsoft YaHei", sans-serif';
c.textAlign = 'center';
c.fillText(p.label.toFixed(1) + '%', p.x, p.y - 14);
}
});
c.restore();
}
};
new Chart(document.getElementById('chart'), {
type: 'bar',
data: {
labels: labels,
datasets: [
{
type: 'bar', label: '出错次数', data: errors,
backgroundColor: barColors, yAxisID: 'y',
categoryPercentage: 1.0, barPercentage: 1.0, borderWidth: 0
},
{
type: 'line', label: '累计百分比', data: cumulative, yAxisID: 'y1',
showLine: false, pointRadius: 0, pointHoverRadius: 0
}
]
},
options: {
responsive: true, maintainAspectRatio: false,
layout: { padding: { top: 20, right: 10, bottom: 0, left: 0 } },
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: (ctx) => {
if (ctx.datasetIndex === 0) {
const cum = ctx.chart.data.datasets[1].data[ctx.dataIndex];
return [' 次数:' + ctx.parsed.y, ' 累计:' + cum.toFixed(1) + '%'];
}
return null;
}
}
}
},
scales: {
x: { grid: { display: false }, ticks: { font: { size: 12 }, color: '#5F5E5A' } },
y: {
position: 'left', beginAtZero: true, max: {{TOTAL_SUM}},
title: { display: true, text: '次数', font: { size: 12 }, color: '#5F5E5A' },
ticks: { font: { size: 11 }, color: '#5F5E5A', stepSize: {{Y_STEP}} },
grid: { color: '#EFEEE8' }
},
y1: {
position: 'right', beginAtZero: true, max: 100,
title: { display: true, text: '累计百分比(%)', font: { size: 12 }, color: '#5F5E5A' },
ticks: { font: { size: 11 }, color: '#5F5E5A', stepSize: 20, callback: (v) => v + '%' },
grid: { display: false }
}
}
},
plugins: [paretoLinePlugin]
});
</script>
</body>
</html>
4. 模板变量说明
| 变量 | 说明 | 示例 |
|------|------|------|
| {{TITLE}} | 图表标题 | 送货出错类型排列图 |
| {{SUBTITLE}} | 副标题 | 样本周期:2026年7月 · 单位:次 |
| {{LABELS_ARRAY}} | JS 数组,项目名称 | ['地址/户号错误', '串户配送', ...] |
| {{ERRORS_ARRAY}} | JS 数组,出错次数 | [42, 31, 18, 14, 9, 6] |
| {{CUMULATIVE_ARRAY}} | JS 数组,累计百分比 | [35.0, 60.8, 75.8, 87.5, 95.0, 100.0] |
| {{TOTAL_SUM}} | 左轴最大值 = 各项总和 | 120 |
| {{Y_STEP}} | 左轴刻度间隔 | 20 |
| {{TABLE_ROWS}} | HTML 表格行 | <tr><td>1</td><td>...</td></tr> |
| {{ANALYSIS}} | 分析结论文字 | 包含关键少数因素识别和改进建议 |
5. 颜色扩展
如果项目超过 6 项,在 barColors 数组中追加颜色:
const barColors = ['#D7263D', '#F46036', '#F5A623', '#3CCB7F', '#2E86DE', '#9B59B6', '#1ABC9C', '#E67E22', '#34495E'];
6. 分析结论模板
分析结论应包含:
- 识别关键少数因素(累计百分比接近 80% 的前几项)
- 逐项给出简短改进建议
- 标注"后几项为次要因素,暂不作为首要改进对象"
- 如果是模拟数据,标注"数据为模拟,实际使用替换为真实数据"
技术要点
- 折线点位置问题:Chart.js 的 line 数据集默认将点放在 category 中心(柱体中间),不符合排列图规范。必须使用自定义
paretoLinePlugin在afterDatasetsDraw钩子中手动计算每根柱子的右边缘坐标来画线。 - 无间隙柱体:设置
categoryPercentage: 1.0和barPercentage: 1.0实现柱体紧密相连。 - 左轴最大值:必须等于各项因素总和,不是取整的任意值。
完成后操作
- 用
present_files展示生成的 HTML 文件 - 在回复中附上数据表(Markdown 格式,兼容 WPS 复制)
- 如果是模拟数据,注明"数据为模拟编造"
微信扫一扫