返回 Skill 列表
extension
分类: 数据与分析无需 API Key

排列图制作

排列图(帕累托图)制作技能,支持用户提供数据或 AI 模拟数据,自动生成符合规范的 HTML 排列图文件

person作者: user_55f11d04hubcommunity

排列图(帕累托图)制作技能

触发条件

当用户提到以下关键词时激活:

  • 排列图、帕累托图、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. 分析结论模板

分析结论应包含:

  1. 识别关键少数因素(累计百分比接近 80% 的前几项)
  2. 逐项给出简短改进建议
  3. 标注"后几项为次要因素,暂不作为首要改进对象"
  4. 如果是模拟数据,标注"数据为模拟,实际使用替换为真实数据"

技术要点

  • 折线点位置问题:Chart.js 的 line 数据集默认将点放在 category 中心(柱体中间),不符合排列图规范。必须使用自定义 paretoLinePluginafterDatasetsDraw 钩子中手动计算每根柱子的右边缘坐标来画线。
  • 无间隙柱体:设置 categoryPercentage: 1.0barPercentage: 1.0 实现柱体紧密相连。
  • 左轴最大值:必须等于各项因素总和,不是取整的任意值。

完成后操作

  1. present_files 展示生成的 HTML 文件
  2. 在回复中附上数据表(Markdown 格式,兼容 WPS 复制)
  3. 如果是模拟数据,注明"数据为模拟编造"