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>
305 lines
12 KiB
Markdown
305 lines
12 KiB
Markdown
# 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 | — | secondary(30%透明)| — |
|
||
| 第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 资讯,内部测算",
|
||
}
|
||
```
|