skills/ppt-station-skill/reference/layout-chart-table.md
wangyitong a65adcc2e5 Initial commit: merged, deduplicated, and vetted skill collection
Sources: extracted from two upstream archives (skill-repo, skills-main),
merged with the following policy:

- 15 broken symlinks (pointing to /Users/jameslee/.cc-switch/skills or
  ../../.agents/skills on a foreign machine) discarded
- 3 real name collisions with identical content (ai-pair, ifind-http-api,
  zhipu-websearch) kept as one copy
- Functional overlaps deduped keeping the strongest variant:
  - docx family: kept docx (official, full toolchain) + docx-cn
    (GB/T 9704 Chinese official-document constants),
    dropped docx_writer (no scripts, name collided with docx)
  - humanizer family: kept humanizer-zh (6 zh reference docs),
    dropped humanizer (en, redundant for CN workflow)
- Skills that only ran in a foreign environment removed:
  ablemind-ops, app-publish, hlb-design-system, openclaw-adj-skill,
  claude-driver
- alphapai excluded from this public repo because its SKILL.md hard-coded
  live credentials

Result: 25 skills, 572 files, ~7.5 MB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:47:12 +08:00

305 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Layout: `chart_table` — 上图表+下表格
> 幻灯片尺寸: 13.333 × 7.5 英寸 | 边距: 0.6 英寸 | 内容宽: 12.133 英寸
---
## 用途
图表和明细数据一页展示:走势图 + 数据明细、收益图 + 各期收益表、指数图 + 成分股数据。
---
## ASCII 线框图
```
┌─────────────────────────────────────────────────────────────┐
│ [标题] y=0.25 │
│ ▬▬ (accent 装饰线) y=0.90 │
│ │
│ ╔══════════════════════════════════════════════════════╗ │
│ ║ ║ │
│ ║ [ 图 表 区 ] ║ │
│ ║ ║ │
│ ╚══════════════════════════════════════════════════════╝ │
│ │
│ ┌────────┬─────────┬─────────┬─────────┬─────────────┐ │
│ │ 指标名 │ 数值A │ 数值B │ 数值C │ 数值D │ │ ← 表头行accent背景
│ ├────────┼─────────┼─────────┼─────────┼─────────────┤ │
│ │ 行1 │ xx.x% │ xx.x% │ xx.x% │ xx.x% │ │
│ │ 行2 │ xx.x% │ xx.x% │ xx.x% │ xx.x% │ │
│ │ 行3 │ xx.x% │ xx.x% │ xx.x% │ xx.x% │ │
│ └────────┴─────────┴─────────┴─────────┴─────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## 分区比例与布局参数
### 高度分配
```
chart_table_ratio: 55/45默认、60/40、70/30
chart_start_y = 1.20
chart_end_y = 1.20 + 7.5 * ratio_chart = 5.325默认55%
table_start_y = chart_end_y + 0.15(间距)
table_end_y = 7.10
实际各值:
ratio 55/45: chart_h=3.525", table_area_h=1.60"
ratio 60/40: chart_h=3.900", table_area_h=1.225"
ratio 70/30: chart_h=4.550", table_area_h=0.575"(仅一行表头+1-2数据行
```
> **建议比例**: 55/45图表适中表格可放3-5行数据
---
## 精确坐标标注
### 固定元素
| 元素 | 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 | — |
### 图表区
| 参数 | 值55/45 比例) |
|------|----------------|
| chart_x | 0.60 |
| chart_y | 1.20 |
| chart_w | 12.133 |
| chart_h | 3.525 |
### 表格区55/45 比例,表格起始 y=4.875
**表头行**:
| 元素 | x | y | w | h | 字号 | 颜色 | 对齐 |
|------|---|---|---|---|------|------|------|
| 表头背景 | 0.60 | 4.875 | 12.133 | 0.38 | — | primary | — |
| 表头文字(每列) | col_x | 4.875 | col_w | 0.38 | body_size-1 | text_light | 中 |
**数据行(斑马条纹)**:
```
row_h = 0.38
最多行数 = floor((7.10 - 4.875 - 0.38) / 0.38) = floor(1.845 / 0.38) ≈ 4行
偶数行背景: secondary极淡奇数行: card_bg
```
| 元素 | x | y相对 row_y | w | h | 字号 | 颜色 | 对齐 |
|------|---|----------------|---|---|------|------|------|
| 行背景(偶数行) | 0.60 | +0 | 12.133 | 0.38 | — | secondary30%透明)| — |
| 第1列标签 | 0.60 | +0.03 | col_widths[0] | 0.32 | body_size-1 | text_dark | 左 |
| 数据列(对齐右) | col_x[j] | +0.03 | col_widths[j] | 0.32 | body_size-1 | text_dark | 右 |
| 行间细线 | 0.60 | +0.36 | 12.133 | 0.02 | — | border | — |
### 列宽分配(自动计算)
```python
def calc_col_widths(headers, table_w=12.133):
"""
第1列标签列分配 25%,其余等分剩余空间
或根据 headers 自定义
"""
n_cols = len(headers)
label_w = table_w * 0.25 # 3.033"
data_w = (table_w - label_w) / (n_cols - 1)
return [label_w] + [data_w] * (n_cols - 1)
# 示例5列:
# label_w = 3.033",每数据列 = (12.133-3.033)/4 = 2.275"
```
**正负值着色规则**(表格数据):
- 含 "+" 或正数: `positive` 绿色
- 含 "-" 或负数: `negative` 红色
- 中性/非数字: `text_dark`
---
## 数据接口
```python
data = {
"title": str, # 页面标题
"subtitle": str, # 可选
# 图表参数(与 layout_chart_full 相同)
"df": pd.DataFrame, # 图表数据
"categories_col": str, # 分类列名X轴
"series_config": list, # 系列配置
"style_config": StyleConfig | None, # 图表样式
"layout_config": ChartLayoutConfig | None,
# 表格参数
"headers": list[str], # 列头(含第一列标签名)
"rows": list[list[str]], # 数据行,每行一个列表
"col_widths": list[float] | None, # 可选,自定义各列宽(英寸),默认自动分配
"chart_ratio": float, # 可选图表占高比0.5-0.75),默认 0.55
"header_color": str, # 可选,表头背景色变量名,默认 "primary"
"zebra": bool, # 是否斑马条纹,默认 True
"highlight_last_row": bool, # 是否高亮最后一行(汇总行),默认 False
"source": str, # 可选,数据来源
}
```
---
## 实现伪代码
```python
def layout_chart_table(slide, data, theme):
from ppt_station.chart_builder.api import create_combo_chart
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"])
ratio = data.get("chart_ratio", 0.55)
chart_y = 1.20
chart_h = (sh - chart_y - 0.40) * ratio # 可用高度的 ratio 给图表
table_y = chart_y + chart_h + 0.15
# 图表
from pptx.util import Inches
create_combo_chart(
slide=slide,
df=data["df"],
categories_col=data["categories_col"],
series_config=data["series_config"],
position=(Inches(m), Inches(chart_y)),
size=(Inches(cw), Inches(chart_h)),
style_config=data.get("style_config"),
layout_config=data.get("layout_config"),
)
# 表格
headers = data["headers"]
rows = data["rows"]
n_cols = len(headers)
col_widths = data.get("col_widths")
if col_widths is None:
label_w = cw * 0.25
data_w = (cw - label_w) / (n_cols - 1)
col_widths = [label_w] + [data_w] * (n_cols - 1)
col_xs = [m]
for w in col_widths[:-1]:
col_xs.append(col_xs[-1] + w)
row_h = 0.38
header_color = theme.get(data.get("header_color", "primary"), theme["primary"])
# 表头
add_rect(slide, m, table_y, cw, row_h, fill=header_color)
for j, (header, cx, cw_j) in enumerate(zip(headers, col_xs, col_widths)):
align = "left" if j == 0 else "right"
px = cx + (0.10 if j == 0 else 0)
add_text(slide, header,
x=px, y=table_y+0.04, w=cw_j-0.10, h=row_h-0.08,
font_size=theme["body_size"]-1, color=theme["text_light"],
bold=True, align=align, font_name=theme["body_font"])
# 数据行
for k, row in enumerate(rows):
ry = table_y + row_h + k * row_h
if ry + row_h > sh - 0.30:
break # 超出页面则停止
# 斑马背景
if data.get("zebra", True) and k % 2 == 1:
add_rect(slide, m, ry, cw, row_h, fill=theme["secondary"], alpha=20)
# 汇总行高亮
is_last = (k == len(rows) - 1)
if is_last and data.get("highlight_last_row", False):
add_rect(slide, m, ry, cw, row_h, fill=theme["secondary"], alpha=40)
for j, (cell, cx, cw_j) in enumerate(zip(row, col_xs, col_widths)):
align = "left" if j == 0 else "right"
# 正负着色
cell_str = str(cell)
if j > 0:
try:
val = float(cell_str.replace("%", "").replace("+", "").replace(",", ""))
c = theme["positive"] if val > 0 else (theme["negative"] if val < 0 else theme["text_dark"])
except ValueError:
c = theme["text_dark"]
else:
c = theme["text_dark"]
bold = is_last and data.get("highlight_last_row", False)
px = cx + (0.10 if j == 0 else 0)
add_text(slide, cell_str,
x=px, y=ry+0.04, w=cw_j-0.10, h=row_h-0.08,
font_size=theme["body_size"]-1, color=c,
bold=bold, align=align, font_name=theme["body_font"])
# 行间线
add_rect(slide, m, ry+row_h-0.01, cw, 0.01, fill=theme["border"])
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. **55/45 比例** 是最常用配置:图表够大,表格放得下 3-4 行有效数据
2. **表头用 `primary` 背景 + `text_light` 白字**,与数据行形成强烈对比
3. **斑马条纹** 轻微区分奇偶行(透明度 20% 的 secondary不喧宾夺主
4. **正负值自动着色** 让数字表格直观传达方向
5. **70/30 比例** 适合图表是核心时表格仅作辅助参考仅1-2行数据
6. **表格列宽** 第1列标签/日期)固定 25%,其余等分
---
## 测试数据示例
```python
import pandas as pd
from ppt_station.chart_builder.models import SeriesConfig
df = pd.DataFrame({
"日期": ["2024-01", "2024-02", "2024-03", "2024-04",
"2024-05", "2024-06", "2024-07", "2024-08"],
"组合收益": [1.2, 2.5, -0.8, 3.1, 2.0, 1.5, 0.7, 2.3],
"基准收益": [0.8, 1.9, -1.2, 2.4, 1.6, 1.1, 0.5, 1.8],
})
data = {
"title": "2024年月度收益对比",
"df": df,
"categories_col": "日期",
"series_config": [
SeriesConfig(key="组合收益", name="本组合", type="bar", axis="primary"),
SeriesConfig(key="基准收益", name="基准", type="line", axis="primary"),
],
"chart_ratio": 0.55,
"headers": ["时间段", "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月"],
"rows": [
["本组合(%)", "+1.2", "+2.5", "-0.8", "+3.1", "+2.0", "+1.5", "+0.7", "+2.3"],
["基准(%)", "+0.8", "+1.9", "-1.2", "+2.4", "+1.6", "+1.1", "+0.5", "+1.8"],
["超额(%)", "+0.4", "+0.6", "+0.4", "+0.7", "+0.4", "+0.4", "+0.2", "+0.5"],
["累计超额(%)", "+0.4", "+1.0", "+1.4", "+2.1", "+2.5", "+2.9", "+3.1", "+3.6"],
],
"highlight_last_row": True,
"source": "Wind 资讯,内部测算",
}
```