保险多产品对照 · ECharts 交互折线图看板(v8 最终版)
把一张「产品 × 年龄 × 指标」的保险产品对比 Excel 表,转成与「保证20年:明三恒二泰享三金生有约_交互版_v8.html」完全同款的对照看板。
本技能自带文件
generate_dashboard.py— 核心生成器(Python 标准库 + zip/xml 解析 Excel,无需 openpyxl)assets_v8_reference.html— v8 最终成品(视觉与交互参考)
脚本路径:~/.workbuddy/skills/insurance-echarts-linechart/
Python 运行时:/Users/liqibin/.workbuddy/binaries/python/versions/3.13.12/bin/python3
触发条件
用户说类似以下任意一种:
- 「按 v8 模板做折线图」
- 「把这个表做成 ECharts 折线图」
- 「保证20年同款对照图」
- 用户提供新 Excel,希望沿用「保证20年」最终版的配色/气泡/交互
输入表格式要求
本生成器默认解析「多块宽表」布局,结构同「保证20年:明三恒二泰享三金生有约.xlsx」:
第1块表头行: 保单年度 | 年龄 | 指标名
1 | 40 | 产品A固定 | 产品A含分红 | 产品B固定 | 产品B含分红 | ...
2 | 41 | ...
第2块表头行: 保单年度 | 年龄 | 下一指标名
...(每指标一块)
必要条件:
- 必须有「保司」行:该行第 C 列(第 3 列)文本等于「保司」,右侧依次每两列为一个产品(固定列 + 含分红列),产品简称取单元格中「(简称)」括号内文字。
- 每个指标块必须有一行同时含「保单年度」「年龄」表头。
- 指标名优先识别并排序为:每年领取 / 累计领取 / 现价 / 总收益 / 身故金 / IRR / 单利。未列出的指标自动追加。
- 可选一行「保额」作为展示;倍数基准默认 100 万元,可通过
--base覆盖。
若新表布局不同(例如列号偏移、保司行关键字变化),必须调整
generate_dashboard.py中parse()的定位逻辑,而不是硬编数据。
输出规格清单(v8 最终版,必须完整复刻)
1. 整体布局
- 页面背景浅灰
#F8FAFC,居中卡片白底。 - 标题 20px 居中,副标题 13px 灰色居中。
- 右上角固定「全部 / 固定 / 分红」三个圆角按钮。
- 标题下方 8 个圆角 tab:
每年领取 / 累计领取 / 现价 / 总收益 / 身故金 / IRR / 单利 / 累计领取+身故金 - 图表区高度 560px,最大宽度 1280px。
- 图表下方为自定义图例栏(非 ECharts 自带图例),再下方为说明文字。
2. 数据系列
- 每个产品两条线:
- 含分红:实线,颜色为该产品主色(产品主色)
- 固定:虚线点(
[4, 4]),颜色为该产品主色 + 55% 亮化
- 主产品线的线宽 2.6,固定/身故金副线 1.4。
- 系列名格式:
简称(含分红)/简称(固定)/简称(累计领取·含分红)/简称(身故金·含分红)。
3. 产品排序与配色(核心规则,不能擅自改动)
排序依据:「每年领取·固定」第一年(最小年龄)的数值,从高到低。
配色:
| 排序 | 颜色名 | HEX |
|------|--------|-----|
| 1(最高) | 红 | #E5484D |
| 2 | 黄 | #F2C200 |
| 3 | 绿 | #2BB673 |
| 4(最低) | 灰 | #9AA0A6 |
- 颜色必须按这个顺序分配,不能因为产品名字变化而固定死。
- 黄、灰属于浅色,气泡/标签内的文字用深色
#1E293B;红、绿用白色#FFFFFF。
4. 右上角模式按钮
- 全部:显示所有线。
- 固定:只显示
(固定)系列。 - 分红:只显示
(含分红)系列。 - 组合图「累计领取+身故金」不受此过滤影响。
5. 图例交互
- 自定义图例栏展示当前 tab 下所有系列。
- 点击某个图例项:隐藏/浮现对应系列,图例项加
off样式(透明度 0.35 + 删除线)。 - 气泡(markPoint)必须跟随系列显隐同步消失/出现。
6. Tooltip 行为
trigger: 'axis'。- 同一 X 轴(年龄)上所有系列值按金额从大到小排序。
- Tooltip 首行固定显示
年龄 XX 岁。 - 每条记录带颜色方块和系列名,格式:
1. 明三(含分红) 58,000。
7. 累计领取图:倍数气泡(v8 定版样式)
- 1× ~ 7× 倍数气泡,基准金额由
--base指定(默认 100 万元)。 - 每个可见折线(每产品每条线)挂载自己的倍数达成点。
- 气泡位置:首次累计领取 ≥ N×BASE 的年龄点。
- 气泡样式:
- 实心圆形,
symbol: 'circle' - 填充色 = 该折线颜色(必须完全一致)
- 白色细边框
- 尺寸:1× 气泡 30px,其他 26px
- 内部居中显示
×N - 文字颜色:浅色折线(黄/灰)用深色字
#1E293B,深色折线(红/绿)用白字#FFFFFF
- 实心圆形,
- 防重叠:同 level 气泡按预设
OFFSET_CANDIDATES像素偏移上下错开。 - 橙色水平虚线标记
N×BASE(不超过 yMax 0.99)。 - 橙色垂直虚线标记每个 level 首次出现的年龄(保至 XX 岁)。
- 气泡交互:
- 悬停:显示小浮层(产品名、达成年龄、累计领取金额、提示点击查看)。
- 点击:显示可交互完整说明浮层,包含关闭按钮。
8. 累计领取+身故金组合图
- 4 条累计领取含分红实线(主 Y 轴)。
- 4 条身故金含分红虚线(次 Y 轴,颜色为产品主色 + 40% 亮化)。
- 不受全部/固定/分红过滤。
- 不显示倍数气泡,但显示橙色垂直参考线。
9. 坐标轴与格式化
- X 轴:年龄,最小刻度 5 岁。
- Y 轴金额:标签格式化为「万」为单位整数,如
12万。 - IRR/单利:Y 轴显示百分比。
执行流程
-
拿表:拿到用户给的
.xlsx文件路径。 -
检查表布局:确认有「保司」行和「保单年度/年龄」表头。若布局不同,调整
parse()中的定位逻辑。 -
生成:运行
/Users/liqibin/.workbuddy/binaries/python/versions/3.13.12/bin/python3 \ ~/.workbuddy/skills/insurance-echarts-linechart/generate_dashboard.py \ --input "<新表.xlsx>" \ --out "<输出目录>/<文件名>_交互版_v1.html" \ --title "标题" \ --subtitle "副标题"参数说明:
--input/-i:Excel 路径(必填)--out/-o:输出 HTML 路径(必填)--title:标题(缺省使用 Excel 文件名)--subtitle:副标题(缺省为「固定 vs 含分红 多指标交互看板(ECharts 版)」)--base:倍数基准,默认 1000000 元--sheet:工作表序号,默认 1
-
冒烟测试:
- 生成后确认控制台输出产品排序、指标 tabs、年龄区间、气泡信息无异常。
- 用 headless Chrome 截图或浏览器打开,确认:
- 8 个 tab 正常切换
- 全部/固定/分红过滤正常
- 图例点击隐藏/浮现正常
- 累计领取图气泡显示正常、颜色与折线一致
-
交付:
- 用
present_files展示生成的 HTML。 - 向用户说明版本号(v1 / v2…)。
- 强调:数据 100% 来自 Excel,未做任何外部补充。
- 用
关键约束(来自用户长期偏好)
- 数据不幻觉:所有图表数值必须 100% 来自用户提供的 Excel,不得用网络数据或自行估算填充。
- 来源可追溯:若用户质疑数据,必须能指出 Excel 中的具体单元格/行。
- 防缓存:每次交付必须生成新文件名(如
_v1.html、v2.html),禁止覆盖同名文件。预览器/浏览器会缓存旧版 HTML,导致「改了但没显示」。 - 先备份再改:如果后续迭代需要修改已交付的 HTML,先
cp一份.prev.html,再修改。
与旧 Plotly 版技能的关系
本技能是 ECharts 版,复刻 v8 最终版规格。旧技能 insurance-chart-generator 是 Plotly 版(生成 5MB 内联 Plotly 看板),两者技术栈和视觉规格不同。如果用户没有特别说明,默认使用本 ECharts 版。
常见调整速查
| 需求 | 修改位置 |
|------|----------|
| 表布局不同(列偏移、关键字变化) | generate_dashboard.py 中 parse() 的 cells.get(...) 索引或匹配文字 |
| 产品数不是 4 个 | parse() 中 products 循环的列范围;PALETTE 可扩展 |
| 倍数基准不是 100 万 | --base 800000 |
| 标题/副标题 | --title / --subtitle |
| 调整配色 | PALETTE 列表,但务必保持「红黄绿灰」顺序规则 |
| 增加/减少指标 | 指标块自动识别;block_order 控制顺序 |
版本记录
- v1:2026-08-25,沉淀「保证20年」v8 最终版完整规格,脚本泛化为 CLI,冒烟测试通过。
微信扫一扫