skills/ppt-station-skill/reference/layout-comparison-cards.md

265 lines
10 KiB
Markdown
Raw Normal View History

# Layout: `comparison_cards` — 对比卡片A vs B
> 幻灯片尺寸: 13.333 × 7.5 英寸 | 边距: 0.6 英寸 | 内容宽: 12.133 英寸
---
## 用途
两家公司对比、方案A vs 方案B、Before/After、两只基金横向比较。
---
## ASCII 线框图
```
┌─────────────────────────────────────────────────────────────┐
│ [标题] y=0.25 │
│ ▬▬ (accent 装饰线) y=0.90 │
│ │
│ ┌──────────────────────────┐VS┌──────────────────────────┐ │
│ │ [left_color 顶部色带] │ │ [right_color 顶部色带] │ │
│ │ │ │ │ │
│ │ [Left Title] │ │ [Right Title] │ │
│ │ [Left Subtitle] │ │ [Right Subtitle] │ │
│ │ ─────────────────────── │ │ ─────────────────────── │ │
│ │ 指标A 3,521点 │ │ 指标A 3,089点 │ │
│ │ 指标B +12.3% │ │ 指标B +8.7% │ │
│ │ 指标C 0.85 │ │ 指标C 0.62 │ │
│ │ 指标D 2.15% │ │ 指标D 3.40% │ │
│ │ 指标E AA │ │ 指标E A │ │
│ └──────────────────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## 精确坐标标注
### 固定元素
| 元素 | x | y | w | h | 字号 | 颜色 | 对齐 |
|------|---|---|---|---|------|------|------|
| 页面标题 | 0.60 | 0.25 | 12.133 | 0.60 | title_size-2 | text_dark | 左 |
| 装饰线 | 0.60 | 0.90 | 2.00 | 0.04 | — | accent | — |
### "VS" 中间分隔
| 元素 | x | y | w | h | 字号 | 颜色 | 对齐 |
|------|---|---|---|---|------|------|------|
| "VS" 文字 | 6.167 | 3.50 | 1.00 | 0.60 | subtitle_size+4 | text_muted | 中 |
| 中央竖线(可选) | 6.617 | 1.10 | 0.01 | 5.80 | — | border | — |
> VS 文字居中于两卡片之间间隙 (x≈6.167间隙约1.0英寸)
### 卡片尺寸
```
vs_gap = 1.00 # VS 区域宽度
card_w = (12.133 - vs_gap) / 2 = 5.567"
left_x = 0.60
right_x = 0.60 + 5.567 + 1.00 = 7.167
card_y = 1.10
card_h = 5.80 (到 y=6.90)
```
### 左/右卡片内部结构(以左卡片 x=0.60 为例)
| 元素 | x相对 card_x | y | w | h | 字号 | 颜色 | 对齐 |
|------|-----------------|---|---|---|------|------|------|
| 卡片背景(圆角矩形) | +0 | 1.10 | 5.567 | 5.80 | — | card_bg | — |
| 顶部色带 | +0 | 1.10 | 5.567 | 0.08 | — | left_color / right_color | — |
| 卡片标题 | +0.20 | 1.28 | 5.167 | 0.55 | subtitle_size+2 | text_dark | 左 |
| 卡片副标题 | +0.20 | 1.88 | 5.167 | 0.40 | body_size | text_muted | 左 |
| 分隔线 | +0.20 | 2.35 | 5.167 | 0.02 | — | border | — |
| 指标区域(起始 y | +0.20 | 2.50 | 5.167 | — | — | — | — |
**指标行(每行)**:
```
row_start_y = 2.50
row_h = 0.55 # 每行高度
max_rows = floor((6.90 - 2.50) / 0.55) = 8行最多
建议 5-7 行
```
| 元素 | x相对 card_x | y相对 row_y | w | h | 字号 | 颜色 | 对齐 |
|------|-----------------|----------------|---|---|------|------|------|
| 指标名称 | +0.20 | +0 | 2.80 | 0.40 | body_size | text_muted | 左 |
| 指标数值 | +3.00 | +0 | 2.37 | 0.40 | body_size | text_dark | 右 |
| 行间细线 | +0.20 | +0.47 | 5.167 | 0.01 | — | border | — |
**胜出方高亮规则**(当某指标某卡片更优时):
- 数值颜色改为 `positive`(绿),`bold=True`
- 或在数值前加 ▲ 符号
- 或行背景加极浅的 `positive` 色(透明度 10%
### 底部注释(可选)
| 元素 | x | y | w | h | 字号 | 颜色 |
|------|---|---|---|---|------|------|
| 来源说明 | 0.60 | 7.10 | 12.133 | 0.30 | caption_size | text_muted |
---
## 颜色约定
| 元素 | 默认值 | 说明 |
|------|--------|------|
| left_color | primary | 左卡片顶部色带 |
| right_color | secondary | 右卡片顶部色带(可自定义) |
| 胜出数值 | positive | 某指标上较优的一方 |
| 劣势数值 | negative | 某指标上较差的一方(可选显示) |
| "VS" 文字 | text_muted | 居中,降调处理 |
---
## 数据接口
```python
data = {
"title": str, # 页面标题
"subtitle": str, # 可选,如 "截至2024年12月31日"
"left": {
"title": str, # 左卡片主标题(如公司名/方案名)
"subtitle": str, # 可选,左卡片副标题
"color": str, # 可选,顶部色带颜色(主题变量名,默认 "primary"
"items": [
{
"label": str, # 指标名(如 "近一年收益"
"value": str, # 指标值(如 "+12.3%"
"better": bool, # 可选,是否为该指标的优胜方(高亮显示)
},
...
],
},
"right": {
# 同上
},
"source": str, # 可选,数据来源
}
```
---
## 实现伪代码
```python
def layout_comparison_cards(slide, data, theme):
m = theme["margin"]
sw, sh = 13.333, 7.5
cw = sw - 2 * m
set_slide_bg(slide, theme["bg_light"])
add_text(slide, data["title"],
x=m, y=0.25, w=cw, h=0.60,
font_size=theme["title_size"]-2, color=theme["text_dark"],
bold=True, font_name=theme["header_font"])
add_rect(slide, m, 0.90, 2.0, 0.04, fill=theme["accent"])
# "VS"
add_text(slide, "VS",
x=6.167, y=3.50, w=1.00, h=0.60,
font_size=theme["subtitle_size"]+4, color=theme["text_muted"],
bold=True, align="center", font_name=theme["header_font"])
vs_gap = 1.00
card_w = (cw - vs_gap) / 2 # 5.567"
card_y = 1.10
card_h = 5.80
for side, card_x in [("left", m), ("right", m + card_w + vs_gap)]:
card_data = data[side]
top_color = theme.get(card_data.get("color", "primary" if side=="left" else "secondary"))
# 卡片背景
add_rounded_rect(slide, card_x, card_y, card_w, card_h,
fill=theme["card_bg"], border=theme["border"], radius=0.10)
# 顶部色带
add_rect(slide, card_x, card_y, card_w, 0.08, fill=top_color)
# 标题
add_text(slide, card_data["title"],
x=card_x+0.20, y=1.28, w=card_w-0.40, h=0.55,
font_size=theme["subtitle_size"]+2, color=theme["text_dark"],
bold=True, font_name=theme["header_font"])
if card_data.get("subtitle"):
add_text(slide, card_data["subtitle"],
x=card_x+0.20, y=1.88, w=card_w-0.40, h=0.40,
font_size=theme["body_size"], color=theme["text_muted"],
font_name=theme["body_font"])
# 分隔线
add_rect(slide, card_x+0.20, 2.35, card_w-0.40, 0.02, fill=theme["border"])
# 指标行
row_y = 2.50
row_h = 0.55
for item in card_data["items"]:
val_color = theme["positive"] if item.get("better") else theme["text_dark"]
add_text(slide, item["label"],
x=card_x+0.20, y=row_y, w=3.00, h=0.40,
font_size=theme["body_size"], color=theme["text_muted"],
font_name=theme["body_font"])
add_text(slide, item["value"],
x=card_x+0.20, y=row_y, w=card_w-0.40, h=0.40,
font_size=theme["body_size"], color=val_color,
bold=item.get("better", False),
align="right", font_name=theme["body_font"])
add_rect(slide, card_x+0.20, row_y+0.47, card_w-0.40, 0.01, fill=theme["border"])
row_y += row_h
if data.get("source"):
add_text(slide, f"数据来源:{data['source']}",
x=m, y=7.10, w=cw, h=0.30,
font_size=theme["caption_size"], color=theme["text_muted"],
font_name=theme["body_font"])
```
---
## 设计要点
1. **"VS" 居中** 在两卡片之间间隙正中,字号略大,用 `text_muted` 降调,不抢主体
2. **顶部色带** 区分左右卡片身份,通常左用 `primary`,右用 `secondary``accent`
3. **胜出指标高亮**`positive` 绿色+粗体,对比一目了然
4. **指标行数** 最多 7 行(含卡片标题区后剩余 4.4 英寸 / 0.55 = 8 行预留间距后7行
5. **指标名长度** 建议 8 字以内;数值最长 10 个字符(含符号)
---
## 测试数据示例
```python
data = {
"title": "基金产品横向对比",
"subtitle": "截至2024年12月31日",
"left": {
"title": "国投养老稳健型 A",
"subtitle": "成立日期2015年3月",
"items": [
{"label": "近一年收益", "value": "+12.3%", "better": True},
{"label": "近三年年化", "value": "+8.7%", "better": True},
{"label": "最大回撤", "value": "-5.2%", "better": True},
{"label": "夏普比率", "value": "1.85", "better": True},
{"label": "管理费率", "value": "0.80%", "better": False},
{"label": "规模(亿元)", "value": "380"},
{"label": "评级", "value": "★★★★★"},
],
},
"right": {
"title": "同类产品均值",
"subtitle": "Wind 同类基金平均)",
"items": [
{"label": "近一年收益", "value": "+8.9%"},
{"label": "近三年年化", "value": "+6.2%"},
{"label": "最大回撤", "value": "-8.1%"},
{"label": "夏普比率", "value": "1.12"},
{"label": "管理费率", "value": "0.65%", "better": True},
{"label": "规模(亿元)", "value": "85"},
{"label": "评级", "value": "★★★"},
],
},
"source": "Wind 资讯,国投养老基金管理有限公司",
}
```