【claude+weelinking产品经理系列16】数据可视化——用图表讲述产品数据的故事

「产品经理用 Claude 实现产品」系列 · 第16篇

产品经理最擅长用数据说话。以前你做周报时手动从 Excel 拉数据做图表,现在自己的产品里就有实时更新的数据看板。本篇用 Claude 接入 ECharts,实现需求状态分布饼图、趋势折线图、优先级分布柱状图,把「需求管理平台」变成真正的产品数据驾驶舱。


一、前言:产品经理最熟悉的工作就是做数据汇报

每周一的产品周会------你打开 PPT,翻出上周的数据,一项一项填进图表里。

总需求数多少、新增多少、完成率多少......你用 Excel 拖来拉去,复制粘贴,调颜色,花了1个小时做了一张本来5分钟就应该有答案的图。

如果你的工具自己会统计、会出图,会实时更新,你每周节省的不只是1小时。

这篇我们做的事,对产品经理来说太熟悉了:做数据看板。区别是:以前你用 Excel 手工做,现在让你的产品自动生成。

💡 本系列全程使用 weelinking 访问 Claude,国内可稳定使用


二、后端准备:统计数据接口

做数据看板,第一步是想清楚:我要看哪些数据?

作为产品经理,这个问题你最有发言权。

2.1 定义统计指标

我们的「需求管理平台」需要以下统计数据:

概览指标(数字卡片):

  • 需求总数
  • 本周新增数(本周一到今天)
  • 进行中的需求数
  • 整体完成率

分布数据(图表):

  • 各状态分布(待评审/开发中/测试中/已完成/已关闭)
  • 各优先级分布(P0/P1/P2/P3)
  • 过去 30 天每日新增趋势

2.2 实现统计接口

你: 帮我实现统计接口 GET /api/stats,数据库用 SQLite(better-sqlite3)。需要返回:

  1. overview:总需求数、本周新增数(从本周一 00:00 开始)、进行中数量(status 为 'developing' 或 'testing' 的)、完成率(status='completed' 的数量 / 总数量)

  2. statusDistribution:按状态分组,返回每种状态的数量,格式 [{ status: 'pending', count: 12, label: '待评审' }]

  3. priorityDistribution:按优先级分组,返回每种优先级的数量和其中已完成的数量(用于计算完成率),格式 [{ priority: 'P0', total: 5, completed: 3 }]

  4. dailyTrend:过去30天,每天新增的需求数量,格式 [{ date: '2024-03-01', count: 3 }],没有数据的日期也要包含(count: 0)

Claude 生成的接口核心代码:

javascript 复制代码
// src/controllers/statsController.js
const db = require('../models/db');

const getStats = (req, res) => {
  // 1. 总览数据
  const total = db.prepare('SELECT COUNT(*) as count FROM requirements WHERE deletedAt IS NULL').get();

  const thisMonday = new Date();
  thisMonday.setDate(thisMonday.getDate() - thisMonday.getDay() + 1);
  thisMonday.setHours(0, 0, 0, 0);

  const weekNew = db.prepare(
    'SELECT COUNT(*) as count FROM requirements WHERE createdAt >= ? AND deletedAt IS NULL'
  ).get(thisMonday.toISOString());

  const inProgress = db.prepare(
    "SELECT COUNT(*) as count FROM requirements WHERE status IN ('developing', 'testing') AND deletedAt IS NULL"
  ).get();

  const completed = db.prepare(
    "SELECT COUNT(*) as count FROM requirements WHERE status = 'completed' AND deletedAt IS NULL"
  ).get();

  const completionRate = total.count > 0
    ? Math.round((completed.count / total.count) * 100)
    : 0;

  // 2. 状态分布
  const statusLabels = {
    pending: '待评审',
    developing: '开发中',
    testing: '测试中',
    completed: '已完成',
    closed: '已关闭'
  };

  const statusDist = db.prepare(
    'SELECT status, COUNT(*) as count FROM requirements WHERE deletedAt IS NULL GROUP BY status'
  ).all();

  const statusDistribution = statusDist.map(item => ({
    ...item,
    label: statusLabels[item.status] || item.status
  }));

  // 3. 优先级分布
  const priorityDist = db.prepare(`
    SELECT priority,
           COUNT(*) as total,
           SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed
    FROM requirements
    WHERE deletedAt IS NULL
    GROUP BY priority
    ORDER BY priority
  `).all();

  // 4. 每日趋势(过去30天)
  const last30Days = [];
  for (let i = 29; i >= 0; i--) {
    const d = new Date();
    d.setDate(d.getDate() - i);
    last30Days.push(d.toISOString().split('T')[0]);
  }

  const dailyCounts = db.prepare(`
    SELECT DATE(createdAt) as date, COUNT(*) as count
    FROM requirements
    WHERE createdAt >= datetime('now', '-30 days')
      AND deletedAt IS NULL
    GROUP BY DATE(createdAt)
  `).all();

  const dailyMap = Object.fromEntries(dailyCounts.map(r => [r.date, r.count]));
  const dailyTrend = last30Days.map(date => ({
    date,
    count: dailyMap[date] || 0
  }));

  res.json({
    success: true,
    data: {
      overview: {
        total: total.count,
        weekNew: weekNew.count,
        inProgress: inProgress.count,
        completionRate
      },
      statusDistribution,
      priorityDistribution: priorityDist,
      dailyTrend
    }
  });
};

产品经理视角: 这段 SQL 查询,就是你以前在 Excel 里用 COUNTIF、SUMIF 做的事------只是现在换成了数据库语言,Claude 帮你写。


三、接入 ECharts

3.1 为什么选 ECharts

市面上的图表库很多,我们选 ECharts 的原因:

图表库 优势 适合场景
ECharts(百度) 功能最全,中文文档友好 数据看板、复杂图表
Ant Design Charts 和 Antd 集成最好 Antd 项目快速接入
Recharts 轻量,React 友好 简单图表
Chart.js 通用,文档丰富 基础图表

我们选 ECharts ------ 功能最丰富,产品经理做汇报最常见的图它都有。

3.2 安装配置

你: 帮我在 React 项目里接入 ECharts。安装 echartsecharts-for-react,创建一个通用的 Chart 组件,接收 option(ECharts 配置)和 style(宽高)作为 props,组件要支持图表自适应容器宽度(使用 echarts-for-reactonChartReady 回调注册 resize 监听)。

jsx 复制代码
// src/components/Chart.jsx
import ReactECharts from 'echarts-for-react';

const Chart = ({ option, style = { height: 300 }, loading = false }) => {
  return (
    <ReactECharts
      option={option}
      style={style}
      showLoading={loading}
      opts={{ renderer: 'canvas' }}
      notMerge={true}
      lazyUpdate={true}
    />
  );
};

export default Chart;

四、数据概览卡片

先做最重要的4个指标卡片------这是 dashboard 的"第一眼"。

你: 帮我做4个统计卡片组件,展示:总需求数、本周新增、进行中、完成率。

每个卡片样式:白色背景、圆角、阴影、内边距24px。分为两行:上行是标题(14px,灰色)和一个小图标(emoji);下行是数字(32px,加粗,黑色)和同比(可选,绿色↑/红色↓)。

4个卡片水平排列,适应不同屏幕宽度(移动端2列)。

jsx 复制代码
// src/components/StatsOverview.jsx
const StatsCard = ({ title, value, icon, suffix, trend }) => (
  <div className="stats-card">
    <div className="stats-card__header">
      <span className="stats-card__title">{title}</span>
      <span className="stats-card__icon">{icon}</span>
    </div>
    <div className="stats-card__body">
      <span className="stats-card__value">
        {value}
        {suffix && <span className="stats-card__suffix">{suffix}</span>}
      </span>
      {trend && (
        <span className={`stats-card__trend ${trend > 0 ? 'up' : 'down'}`}>
          {trend > 0 ? '↑' : '↓'} {Math.abs(trend)}%
        </span>
      )}
    </div>
  </div>
);

const StatsOverview = ({ data }) => (
  <div className="stats-overview">
    <StatsCard title="需求总数" value={data.total} icon="📋" />
    <StatsCard title="本周新增" value={data.weekNew} icon="✨" />
    <StatsCard title="进行中" value={data.inProgress} icon="🚀" />
    <StatsCard title="完成率" value={data.completionRate} suffix="%" icon="✅" />
  </div>
);

五、需求状态分布------饼图

你: 用 ECharts 实现需求状态分布饼图:

  • 甜甜圈样式(环形饼图,内半径50%,外半径70%)
  • 中心显示总需求数
  • 颜色:待评审=橙色、开发中=蓝色、测试中=紫色、已完成=绿色、已关闭=灰色
  • 图例在右侧,每条图例显示名称和数量
  • 悬浮时显示 tooltip:名称、数量、占比
  • 动画:从底部顺时针展开(animationEasing: 'cubicInOut')
javascript 复制代码
// 状态分布饼图的 ECharts option
const getStatusChartOption = (data) => ({
  tooltip: {
    trigger: 'item',
    formatter: '{b}: {c} ({d}%)'
  },
  legend: {
    orient: 'vertical',
    right: 10,
    top: 'center',
    formatter: (name) => {
      const item = data.find(d => d.label === name);
      return `${name}  ${item?.count || 0}`;
    }
  },
  series: [{
    type: 'pie',
    radius: ['50%', '70%'],
    center: ['40%', '50%'],
    avoidLabelOverlap: false,
    label: {
      show: true,
      position: 'center',
      formatter: () => `总计\n${data.reduce((s, d) => s + d.count, 0)}`,
      fontSize: 16,
      fontWeight: 'bold'
    },
    emphasis: {
      label: { show: true, fontSize: 18 }
    },
    data: data.map(item => ({
      value: item.count,
      name: item.label,
      itemStyle: {
        color: {
          '待评审': '#F59E0B',
          '开发中': '#3B82F6',
          '测试中': '#8B5CF6',
          '已完成': '#10B981',
          '已关闭': '#9CA3AF'
        }[item.label]
      }
    })),
    animationType: 'scale',
    animationEasing: 'cubicInOut'
  }]
});

产品经理的感慨: "这不就是我每周截图给老板看的那个图吗?以前要手动拉数据,现在实时自动生成。"


六、需求趋势------折线图

你: 用 ECharts 实现过去30天需求新增趋势折线图:

  • X 轴:日期(只显示每5天一个刻度,格式 MM/DD)
  • Y 轴:数量(整数,从0开始)
  • 折线:蓝色,线宽2px,有数据点(直径4px)
  • 区域填充:蓝色渐变,从上到下透明度从0.3降到0
  • 悬浮 tooltip:显示日期和数量
  • 加一条7日移动平均线(橙色,虚线),帮助看趋势而不是看抖动
javascript 复制代码
// 趋势折线图的 ECharts option
const getTrendChartOption = (data) => {
  const dates = data.map(d => d.date.slice(5));  // 只取 MM-DD 部分
  const counts = data.map(d => d.count);

  // 计算7日移动平均
  const movingAvg = counts.map((_, i) => {
    if (i < 6) return null;
    const slice = counts.slice(i - 6, i + 1);
    return (slice.reduce((a, b) => a + b, 0) / 7).toFixed(1);
  });

  return {
    tooltip: {
      trigger: 'axis',
      formatter: (params) => {
        const date = params[0].axisValue;
        const count = params[0].value;
        const avg = params[1]?.value;
        return `${date}<br/>新增:${count} 个${avg ? `<br/>7日均值:${avg}` : ''}`;
      }
    },
    xAxis: {
      type: 'category',
      data: dates,
      axisLabel: {
        interval: 4,  // 每5天显示一个
        formatter: (val) => val
      }
    },
    yAxis: {
      type: 'value',
      minInterval: 1
    },
    series: [
      {
        name: '每日新增',
        type: 'line',
        data: counts,
        smooth: true,
        lineStyle: { color: '#3B82F6', width: 2 },
        itemStyle: { color: '#3B82F6' },
        areaStyle: {
          color: {
            type: 'linear', x: 0, y: 0, x2: 0, y2: 1,
            colorStops: [
              { offset: 0, color: 'rgba(59, 130, 246, 0.3)' },
              { offset: 1, color: 'rgba(59, 130, 246, 0)' }
            ]
          }
        }
      },
      {
        name: '7日均值',
        type: 'line',
        data: movingAvg,
        smooth: true,
        lineStyle: { color: '#F59E0B', width: 2, type: 'dashed' },
        itemStyle: { color: '#F59E0B' },
        symbol: 'none'
      }
    ]
  };
};

七、优先级分布------柱状图

你: 用 ECharts 实现优先级分布柱状图:

  • X 轴:P0、P1、P2、P3
  • Y 轴:数量
  • 每个优先级显示两根柱子:总数(蓝色,半透明)和已完成(绿色,实心),叠放展示
  • 柱子上方显示完成率(绿色文字,百分比)
  • 颜色约定:P0=红色、P1=橙色、P2=蓝色、P3=灰色,柱子颜色与优先级对应
javascript 复制代码
const getPriorityChartOption = (data) => ({
  tooltip: {
    trigger: 'axis',
    axisPointer: { type: 'shadow' },
    formatter: (params) => {
      const priority = params[0].axisValue;
      const item = data.find(d => d.priority === priority);
      const rate = item ? Math.round(item.completed / item.total * 100) : 0;
      return `${priority}<br/>总数:${item?.total || 0}<br/>已完成:${item?.completed || 0}<br/>完成率:${rate}%`;
    }
  },
  xAxis: {
    type: 'category',
    data: ['P0', 'P1', 'P2', 'P3']
  },
  yAxis: { type: 'value', minInterval: 1 },
  series: [
    {
      name: '总数',
      type: 'bar',
      data: data.map(d => ({
        value: d.total,
        itemStyle: {
          color: { P0: '#FEE2E2', P1: '#FEF3C7', P2: '#DBEAFE', P3: '#F3F4F6' }[d.priority],
          borderColor: { P0: '#EF4444', P1: '#F59E0B', P2: '#3B82F6', P3: '#9CA3AF' }[d.priority],
          borderWidth: 1
        }
      }))
    },
    {
      name: '已完成',
      type: 'bar',
      barGap: '-100%',  // 叠加在总数柱子上
      data: data.map(d => ({
        value: d.completed,
        itemStyle: {
          color: { P0: '#EF4444', P1: '#F59E0B', P2: '#3B82F6', P3: '#9CA3AF' }[d.priority]
        }
      })),
      label: {
        show: true,
        position: 'top',
        formatter: (params) => {
          const item = data[params.dataIndex];
          const rate = item ? Math.round(item.completed / item.total * 100) : 0;
          return `${rate}%`;
        },
        color: '#10B981',
        fontWeight: 'bold'
      }
    }
  ]
});

八、让图表可以交互

8.1 时间范围选择

你: 在趋势折线图上方,加一个时间范围选择器(按钮组):7天、30天、90天。

点击后,向后端传递 ?days=7(或30/90)参数,后端重新查询对应天数的数据,前端图表更新。

选中的按钮有蓝色背景,默认选30天。

jsx 复制代码
const TrendChart = () => {
  const [days, setDays] = useState(30);
  const [data, setData] = useState([]);

  useEffect(() => {
    fetchTrend(days).then(setData);
  }, [days]);

  return (
    <div>
      <div className="chart-actions">
        {[7, 30, 90].map(d => (
          <button
            key={d}
            className={`btn-period ${days === d ? 'active' : ''}`}
            onClick={() => setDays(d)}
          >
            {d}天
          </button>
        ))}
      </div>
      <Chart option={getTrendChartOption(data)} />
    </div>
  );
};

8.2 数据导出

你: 在数据统计页面右上角,加一个"导出数据"按钮,点击后下载一个 CSV 文件,包含当前时间段内所有需求的关键字段:ID、标题、状态、优先级、创建人、创建时间、完成时间。

纯前端实现,不需要调接口,直接从已有的需求列表数据生成 CSV,用 Blob 和 a 标签下载。

javascript 复制代码
const exportToCSV = (data, filename = 'requirements.csv') => {
  const headers = ['ID', '标题', '状态', '优先级', '创建人', '创建时间'];
  const rows = data.map(item => [
    item.id,
    `"${item.title.replace(/"/g, '""')}"`,  // 处理标题里可能有逗号
    item.status,
    item.priority,
    item.creatorName,
    new Date(item.createdAt).toLocaleDateString('zh-CN')
  ]);

  const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n');
  const blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8' });  // \uFEFF 解决中文乱码

  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
};

九、组装统计看板页面

你: 帮我组装统计看板页面 src/pages/Dashboard.jsx

布局:

  • 顶部:4个概览卡片(一排)
  • 中间左:需求状态饼图(占一半宽度)
  • 中间右:优先级分布柱状图(占一半宽度)
  • 底部:趋势折线图(全宽,含时间范围选择器)

页面打开时统一请求 /api/stats,用 Promise.all 并发获取所有数据,全部加载完成前显示骨架屏。

jsx 复制代码
// src/pages/Dashboard.jsx
const Dashboard = () => {
  const [stats, setStats] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchAll = async () => {
      try {
        const res = await api.get('/stats');
        setStats(res.data);
      } catch (e) {
        message.error('数据加载失败');
      } finally {
        setLoading(false);
      }
    };
    fetchAll();
  }, []);

  if (loading) return <DashboardSkeleton />;

  return (
    <div className="dashboard">
      <StatsOverview data={stats.overview} />
      <div className="chart-row">
        <div className="chart-half">
          <Chart option={getStatusChartOption(stats.statusDistribution)} style={{ height: 320 }} />
        </div>
        <div className="chart-half">
          <Chart option={getPriorityChartOption(stats.priorityDistribution)} style={{ height: 320 }} />
        </div>
      </div>
      <TrendChart />
    </div>
  );
};

十、这就是你的数据驾驶舱

做完这一切,打开统计看板页面的那一刻:

实时更新的总需求数、本周新增趋势、各优先级的完成情况------全部自动生成,不需要你手动拉数据,不需要打开 Excel,不需要做 PPT。

以前你花一小时准备的产品周报数据,现在刷新一下页面,5秒就全有了。

这才是技术型 PM 的价值:不只是会提需求,而是能让产品自己"说话"。


十一、总结与下期预告

🎯 本篇核心要点

1. 数据看板从"想清楚要看什么"开始。 作为产品经理,你比任何人都清楚需要哪些指标。Claude 负责实现,你负责定义。

2. 后端 SQL 聚合查询 = 你在 Excel 里的 COUNTIF/SUMIF。 换了工具,逻辑没变。

3. ECharts option 交给 Claude 生成就好。 图表样式太多参数,不用背,用自然语言描述你想要的效果,Claude 帮你转成配置。

4. 时间范围可切换 + 数据导出 = 一个完整的数据工具。 这两个功能让你的看板从"展示用"变成"分析用"。

5. 骨架屏 + 并发请求 = 更好的加载体验。 数据加载期间不让用户看白屏,是产品设计的基本功。

📌 记住这句话

以前花一小时手动做的产品周报,现在刷新一下,5秒就全有了。这就是让产品"自己说话"的意义。

📣 下期预告

第17篇:《部署上线------从 localhost 到全世界》

统计看板做完了,「需求管理平台」的全部功能也完成了!

下一篇是最激动人心的时刻------部署上线

从"在自己电脑上跑"到"发个链接给任何人都能访问",这一步对产品经理来说有特殊意义:你终于要亲手完成一次"上线"了。Vercel 部署前端、Railway 部署后端,全程免费,Claude 帮你准备配置文件。

💡 本系列全程使用 weelinking 访问 Claude,国内可稳定使用


📎 配套资源

📋 数据看板实现清单

复制代码
□ 后端统计接口
  □ GET /api/stats 接口实现
  □ 总览数据(总数/本周新增/进行中/完成率)
  □ 状态分布数据
  □ 优先级分布数据(含已完成数)
  □ 过去N天每日趋势数据(空日期补0)

□ 前端图表
  □ ECharts + echarts-for-react 安装完成
  □ 通用 Chart 组件(支持自适应宽度)
  □ 概览卡片(4个指标)
  □ 状态分布饼图(甜甜圈样式)
  □ 趋势折线图(含7日移动平均线)
  □ 优先级分布柱状图(含完成率)

□ 交互功能
  □ 时间范围切换(7/30/90天)
  □ 图表 tooltip 信息完整
  □ 数据导出 CSV 功能

□ 体验优化
  □ 加载时有骨架屏
  □ 数据获取失败有错误提示
  □ 移动端图表可正常显示

📋 ECharts 常用图表提示词模板

饼图:

复制代码
用 ECharts 实现[名称]饼图:
- 样式:[普通饼图/甜甜圈形](内半径X%,外半径Y%)
- 数据格式:[{ name: '标签', value: 数值 }]
- 颜色:[每个分类对应的颜色]
- 图例位置:[右侧/底部/顶部]
- tooltip:显示[名称和数量/名称和占比]
- 中心显示:[总数/空]

折线图:

复制代码
用 ECharts 实现[名称]折线图:
- X 轴:[日期/类别]数据
- Y 轴:[数量],从0开始,整数
- 折线颜色:[颜色],线宽[N]px
- 是否填充区域:[是/否],填充颜色[颜色]
- 是否平滑:[是/否]
- tooltip:显示[X值和Y值]

柱状图:

复制代码
用 ECharts 实现[名称]柱状图:
- X 轴分类:[类别列表]
- 系列:[系列1(颜色)、系列2(颜色)]
- 排列方式:[并排/堆叠]
- 柱子上是否显示数值/百分比标签
- tooltip:显示[X值和Y值]

数字卡片:

复制代码
帮我实现统计数字卡片组件:
- 显示:大数字(32px,加粗)+ 小标题(14px,灰色)+ 图标(emoji)
- 可选:趋势指示(↑N%或↓N%)
- 样式:白色背景、圆角12px、阴影、内边距24px
- 多个卡片水平排列,响应式(移动端2列)

🌟 如果这篇文章对你有帮助,请点赞👍 收藏⭐ 关注🔔

你的支持是我持续更新的最大动力!

💬 评论区聊聊:你在现在的工作中,有哪个数据是你每周都要手动整理的?这篇能帮你解放吗?


📌 系列导航: 产品经理用 Claude 实现产品 · 系列目录

上一篇: 第15篇:UI/UX 打磨------产品经理的审美终于能自己实现

下一篇: 第17篇:部署上线------从 localhost 到全世界
💡 本系列全程使用 weelinking 访问 Claude,国内可稳定使用

🚀 整个系列的核心理念:你不需要变成程序员,你只需要从"找人做"变成"自己能做"。

相关推荐
大模型真好玩2 小时前
LangChain DeepAgents 速通指南(二)—— Summarization中间件为Agent作记忆加减法
人工智能·langchain·agent
北辰alk2 小时前
大模型微调技术全景解析:从LoRA到RLHF的演进之路
人工智能
番茄去哪了2 小时前
Python基础入门(二)
linux·服务器·开发语言·python
未来之窗软件服务2 小时前
AI人工智能(二十一)pt模型转onnx sensvoice—东方仙盟练气期
人工智能·python·仙盟创梦ide·东方仙盟
2501_946490382 小时前
Hirender MTC时间码技术实操——PH®CLUB激光投影声光电精准同步实现方案
大数据·运维·人工智能·hirender·hecoos
诚思报告YH2 小时前
半导体石英制品市场洞察:2026-2032年复合增长率(CAGR)达9.2%
大数据·人工智能
yohalaser2 小时前
智测破局提质 武汉曜华激光助力钙钛矿产线规模化量产
大数据·人工智能·太阳能·光伏发电·曜华激光·光伏组件生产线
苡~2 小时前
【openclaw+claude】手机+OpenClaw+Claude实现远程AI编程系列大纲
java·前端·人工智能·智能手机·ai编程·claude api
生成论实验室2 小时前
即事经智能:一种基于生成易算的通用智能新范式(书)
人工智能·神经网络·算法·架构·信息与通信