@topmindspace/tms-skills 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/README.md +9 -3
- package/bin/tms-skills.js +200 -160
- package/package.json +12 -9
- package/top-ppt-html/README.md +162 -254
- package/top-ppt-html/SKILL.md +126 -111
- package/top-ppt-html/assets/examples/2026-09-09-architecture-graphite-dark.html +3926 -3926
- package/top-ppt-html/assets/examples/2026-09-09-architecture-graphite-dark.model.json +167 -167
- package/top-ppt-html/assets/examples/2026-09-09-presentation-business-blue.html +4325 -4325
- package/top-ppt-html/assets/examples/2026-09-09-presentation-business-blue.model.json +320 -320
- package/top-ppt-html/assets/examples/2026-09-09-research-mckinsey.html +5527 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-mckinsey.model.json +913 -913
- package/top-ppt-html/assets/pptx-export.js +1944 -1944
- package/top-ppt-html/assets/style-gallery.html +589 -589
- package/top-ppt-html/assets/templates/architecture.html +3728 -3728
- package/top-ppt-html/assets/templates/engine.css +840 -840
- package/top-ppt-html/assets/templates/presentation.html +3738 -3738
- package/top-ppt-html/assets/templates/research.html +4017 -4017
- package/top-ppt-html/assets/templates/ui.js +520 -520
- package/top-ppt-html/assets/theme-overview-architecture.png +0 -0
- package/top-ppt-html/assets/theme-overview-research.png +0 -0
- package/top-ppt-html/assets/theme-overview.png +0 -0
- package/top-ppt-html/evals/prompts.csv +15 -15
- package/top-ppt-html/evals/rubric.schema.json +25 -25
- package/top-ppt-html/evals/run_evals.py +220 -220
- package/top-ppt-html/evals/trace.example.json +16 -16
- package/top-ppt-html/package-lock.json +186 -0
- package/top-ppt-html/package.json +11 -11
- package/top-ppt-html/references/charts-basic.md +624 -624
- package/top-ppt-html/references/charts-discipline.md +110 -108
- package/top-ppt-html/references/charts-extended.md +482 -482
- package/top-ppt-html/references/charts.md +37 -28
- package/top-ppt-html/references/components-atoms.md +624 -624
- package/top-ppt-html/references/components.md +30 -30
- package/top-ppt-html/references/content-rules.md +510 -490
- package/top-ppt-html/references/default-surface.md +61 -0
- package/top-ppt-html/references/design-system.md +478 -478
- package/top-ppt-html/references/failure-modes.md +227 -214
- package/top-ppt-html/references/high-fidelity.md +127 -127
- package/top-ppt-html/references/icons.md +176 -397
- package/top-ppt-html/references/infographics-stats.md +308 -308
- package/top-ppt-html/references/infographics-structure.md +226 -226
- package/top-ppt-html/references/infographics.md +43 -43
- package/top-ppt-html/references/layout-grammar.md +325 -315
- package/top-ppt-html/references/layouts-architecture.md +108 -108
- package/top-ppt-html/references/layouts-combo.md +600 -600
- package/top-ppt-html/references/layouts-research.md +160 -160
- package/top-ppt-html/references/modes.md +259 -254
- package/top-ppt-html/references/outline-design.md +275 -275
- package/top-ppt-html/references/playbook.md +284 -266
- package/top-ppt-html/references/pptx-export.md +227 -209
- package/top-ppt-html/references/presentation-craft.md +69 -0
- package/top-ppt-html/references/styles.md +370 -370
- package/top-ppt-html/references/tech-design.md +138 -138
- package/top-ppt-html/scripts/audit_css.py +109 -109
- package/top-ppt-html/scripts/audit_docs.py +176 -176
- package/top-ppt-html/scripts/audit_skill.py +231 -220
- package/top-ppt-html/scripts/audit_styles.py +351 -351
- package/top-ppt-html/scripts/build_examples.py +55 -2276
- package/top-ppt-html/scripts/build_pptx.js +2386 -2380
- package/top-ppt-html/scripts/capture_theme_overview.js +79 -78
- package/top-ppt-html/scripts/checks_html.py +166 -127
- package/top-ppt-html/scripts/cross_verify.py +298 -294
- package/top-ppt-html/scripts/env_probe.py +158 -158
- package/top-ppt-html/scripts/extract_model.py +210 -210
- package/top-ppt-html/scripts/extract_snippet.py +374 -374
- package/top-ppt-html/scripts/gen_channel_a.js +214 -214
- package/top-ppt-html/scripts/layout-constants.json +111 -18
- package/top-ppt-html/scripts/lib_layout_regions.js +410 -412
- package/top-ppt-html/scripts/measure_height.py +178 -178
- package/top-ppt-html/scripts/negative_tests.py +433 -307
- package/top-ppt-html/scripts/package_skill.py +294 -291
- package/top-ppt-html/scripts/prepare_images.py +341 -341
- package/top-ppt-html/scripts/probe_image_export.py +187 -188
- package/top-ppt-html/scripts/quality_gate.py +309 -301
- package/top-ppt-html/scripts/recommend_layout.py +384 -0
- package/top-ppt-html/scripts/regression.py +308 -308
- package/top-ppt-html/scripts/render_compare.py +275 -275
- package/top-ppt-html/scripts/render_from_model.py +698 -698
- package/top-ppt-html/scripts/scaffold_report.py +1054 -1054
- package/top-ppt-html/scripts/section-file-map.json +104 -104
- package/top-ppt-html/scripts/smoke_pptx.sh +30 -0
- package/top-ppt-html/scripts/sync_runtime.py +659 -662
- package/top-ppt-html/scripts/validate_pptx.py +1579 -1510
- package/top-ppt-html/scripts/validate_report.py +1755 -1456
- package/top-ppt-html/assets/examples/2026-09-09-architecture-spectrum.html +0 -3926
- package/top-ppt-html/assets/examples/2026-09-09-architecture-spectrum.model.json +0 -168
- package/top-ppt-html/assets/examples/2026-09-09-presentation-apple-mono.html +0 -4325
- package/top-ppt-html/assets/examples/2026-09-09-presentation-apple-mono.model.json +0 -321
- package/top-ppt-html/assets/examples/2026-09-09-presentation-brand-red.html +0 -4325
- package/top-ppt-html/assets/examples/2026-09-09-presentation-brand-red.model.json +0 -321
- package/top-ppt-html/assets/examples/2026-09-09-research-deep-teal.html +0 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-deep-teal.model.json +0 -914
- package/top-ppt-html/assets/examples/2026-09-09-research-indigo-violet.html +0 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-indigo-violet.model.json +0 -914
- package/top-ppt-html/assets/examples/2026-09-09-research-warm-sand.html +0 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-warm-sand.model.json +0 -914
- package/top-ppt-html/assets/theme-overview-presentation.png +0 -0
- package/top-ppt-html/references/design-system-engine.md +0 -235
- package/top-ppt-html/references/industry-benchmark.md +0 -105
- package/top-ppt-html/references/reform-plan.md +0 -252
- package/top-ppt-html/scripts/layout_slots.json +0 -830
|
@@ -1,4325 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<!--
|
|
3
|
-
TopPPT HTML· 演示汇报模式模板(presentation)
|
|
4
|
-
═══════════════════════════════════════════════════════════════════════
|
|
5
|
-
单文件 / 零外链 / 亮暗双主题 / 宽屏 16:9 / 9 风格实时切换。
|
|
6
|
-
形态定位:台上讲给人听——大字号、每屏一主张、图表大而克制。本模板即演示形态本体,
|
|
7
|
-
与 research / architecture 是三种独立输出体系(生成时确定,页面无模式切换)。
|
|
8
|
-
|
|
9
|
-
用法:复制本文件 → 选风格(data-style + header 下拉可实时预览 9 套)→ 按本骨架填内容
|
|
10
|
-
→ 同步填写 window.REPORT_MODEL(mode 固定为 presentation)→ 跑 validate_report.py 校验
|
|
11
|
-
→ 交付到 <输出目录>/YYYY-MM-DD-主题.html(输出目录由用户指定,默认当前工作目录)
|
|
12
|
-
模式规则:references/modes.md §A 版式组件:references/components.md
|
|
13
|
-
导出:header「预览 PPTX」+「?」帮助(精导通道指引);正式交付走智能体精导通道。
|
|
14
|
-
公共引擎/UI/运行时由 scripts/sync_runtime.py 注入(标记块内禁止手改)。
|
|
15
|
-
-->
|
|
16
|
-
<html lang="zh-CN" data-mode="presentation" data-style="brand-red" data-theme="light">
|
|
17
|
-
<head>
|
|
18
|
-
<meta charset="UTF-8">
|
|
19
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
20
|
-
<meta name="color-scheme" content="light dark">
|
|
21
|
-
<title>AI 智能体:从工具到同事 · TopPPT HTML 演示模式样例(品牌红)</title>
|
|
22
|
-
<style>
|
|
23
|
-
/* __TOPPPT_ENGINE_START__ */
|
|
24
|
-
/* ══ TopPPT HTML 公共样式引擎(assets/templates/engine.css 的内联副本 · 由 scripts/sync_runtime.py 注入,禁止手改) ══ */
|
|
25
|
-
/* ══ TopPPT HTML 公共样式引擎(assets/templates/engine.css)══
|
|
26
|
-
由 scripts/sync_runtime.py 注入到 templates/*.html 的 __TOPPPT_ENGINE__ 标记块 · 禁止在各模板手改。
|
|
27
|
-
含:结构 token / 9 套风格 token / 排版类 / 栅格(含密度版式扩展 g-hero--rev/g-side--rev/g-31/g-13、
|
|
28
|
-
拼贴栅格 g-bento + span 工具类、交错版式 .stagger、键值 .kv)/
|
|
29
|
-
强调带(band--accent / band--accent--solid / band--deep)/
|
|
30
|
-
顶栏+工具组(含全屏按钮与折叠态)/ 组件 / SVG 语义类 /
|
|
31
|
-
锁定版式组件(steps/heatmap/bullet/pyramid)/ 待核实标注(.tbd/.tbd-legend/.flagbar)/
|
|
32
|
-
素材图片族(.media 比例/裁切/蒙版 · --ph 配图占位 · .media-grid 多图网格 ·
|
|
33
|
-
.media-compare 双图对比 · .media-wall Logo 墙)/ Agenda / 页码指示 / PPTX 预览与帮助模态。
|
|
34
|
-
不含:模式密度层与模式专属组件(各模板自带,置于引擎块之后覆盖)。 */
|
|
35
|
-
|
|
36
|
-
/* ── 结构 token(版式/间距/字号,风格无关;presentation 基准值,模式模板在其后覆盖密度) ── */
|
|
37
|
-
:root{color-scheme:light;
|
|
38
|
-
--font-body:"Google Sans","Roboto",-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC",
|
|
39
|
-
"Hiragino Sans GB","Microsoft YaHei","Noto Sans SC",sans-serif;
|
|
40
|
-
--font-display:var(--font-body);
|
|
41
|
-
--fw-display:700;--fw-title:600;
|
|
42
|
-
--fs-display:clamp(2.75rem,4.6vw,4.25rem);--fs-h1:clamp(2rem,3.2vw,3rem);
|
|
43
|
-
--fs-h2:clamp(1.5rem,2.1vw,2.125rem);--fs-h3:clamp(1.125rem,1.35vw,1.375rem);
|
|
44
|
-
--fs-lead:clamp(1.0625rem,1.25vw,1.3125rem);--fs-body:clamp(.9375rem,1.05vw,1.0625rem);
|
|
45
|
-
--fs-sm:clamp(.8125rem,.92vw,.9375rem);--fs-xs:clamp(.75rem,.82vw,.8125rem);
|
|
46
|
-
--fs-metric:clamp(2.25rem,3.4vw,3.25rem);
|
|
47
|
-
/* 风格语汇基线(每套风格可覆盖:字距 / 行高 / 链接色 / 字重 / 字阶) */
|
|
48
|
-
--letter-display:-.03em;--letter-eyebrow:.11em;
|
|
49
|
-
--lh-body:1.68;--lh-lead:1.62;
|
|
50
|
-
--link:var(--accent-text);
|
|
51
|
-
--sp-1:4px;--sp-2:8px;--sp-3:12px;--sp-4:16px;--sp-5:24px;--sp-6:32px;--sp-7:48px;--sp-8:64px;
|
|
52
|
-
--radius:16px;--r-sm:8px;--r-md:12px;--r-lg:var(--radius);--r-xl:calc(var(--radius) + 8px);--r-full:999px;
|
|
53
|
-
/* 动效标度(MD3:short≤200 / medium 250–400 / long 450–600 / extra-long 700–1000)——
|
|
54
|
-
design-system.md §Motion 早有规定,此前散落成 8 个离散值,这里收敛成 4 档 + 2 条缓动 */
|
|
55
|
-
--dur-short:.18s;--dur-medium:.28s;--dur-long:.6s;--dur-xlong:.7s;
|
|
56
|
-
--ease-emphasized:cubic-bezier(.05,.7,.1,1);--ease-standard:cubic-bezier(.2,0,0,1);
|
|
57
|
-
--wrap:1400px;--gap:clamp(16px,1.6vw,28px);--bar-h:68px;
|
|
58
|
-
/* 页面高度模型(每页高度稳定):--band-pad 页内上下留白;--band-min 单页最小高度(一屏)。
|
|
59
|
-
模式模板只覆盖这两个 token,不重写 .band 规则。
|
|
60
|
-
--band-min 是唯一生效的页高开关:默认 100vh,支持 svh 的浏览器升级为 100svh(移动端工具栏友好)。 */
|
|
61
|
-
--band-pad:clamp(40px,6vh,88px);--band-min:calc(100vh - var(--bar-h));
|
|
62
|
-
--deep-bg:#1a1d21;--deep-fg:#e8eaed;--deep-fg-2:#b9c2cd;--deep-line:#2b3540;
|
|
63
|
-
--shadow-1:0 1px 2px rgba(60,64,67,.08),0 1px 3px 1px rgba(60,64,67,.06);
|
|
64
|
-
--shadow-2:0 1px 3px rgba(60,64,67,.10),0 4px 8px 3px rgba(60,64,67,.06)}
|
|
65
|
-
@supports (height:100svh){:root{--band-min:calc(100svh - var(--bar-h))}}
|
|
66
|
-
|
|
67
|
-
/* ── 风格 token · 9 套全量内嵌(header 风格下拉实时切换;生成时选定 data-style 默认值) ── */
|
|
68
|
-
:root{
|
|
69
|
-
--bg:#fff;--surface:#fff;--surface-1:#f8f9fa;--surface-2:#f1f3f4;--surface-3:#e8eaed;
|
|
70
|
-
--surface-inv:#202124;--text:#202124;--text-2:#5f6368;--text-3:#80868b;--text-inv:#fff;
|
|
71
|
-
--border:#dadce0;--border-soft:#e8eaed;
|
|
72
|
-
--accent:#1a73e8;--accent-soft:#e8f0fe;--accent-soft-2:#d2e3fc;--accent-text:#1b66c9;--accent-on:#fff}
|
|
73
|
-
/* business-blue 显式块(Google Material 语汇:中性灰阶 + 单一蓝 · 圆角 16 · 同字族层级靠字重字号) */
|
|
74
|
-
html[data-style="business-blue"]{
|
|
75
|
-
--font-body:"Google Sans","Roboto",-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei","Noto Sans SC",sans-serif;
|
|
76
|
-
--font-display:var(--font-body);
|
|
77
|
-
--radius:16px;--fw-display:700;--fw-title:600;
|
|
78
|
-
--letter-display:-.03em;--letter-eyebrow:.11em;--lh-body:1.68;--lh-lead:1.62;--link:#1b66c9;
|
|
79
|
-
--bg:#fff;--surface:#fff;--surface-1:#f8f9fa;--surface-2:#f1f3f4;--surface-3:#e8eaed;
|
|
80
|
-
--surface-inv:#202124;--text:#202124;--text-2:#5f6368;--text-3:#80868b;--text-inv:#fff;
|
|
81
|
-
--border:#dadce0;--border-soft:#e8eaed;
|
|
82
|
-
--accent:#1a73e8;--accent-soft:#e8f0fe;--accent-soft-2:#d2e3fc;--accent-text:#1b66c9;--accent-on:#fff}
|
|
83
|
-
html[data-style="business-blue"][data-theme="dark"]{
|
|
84
|
-
--bg:#0d0f13;--surface:#14171c;--surface-1:#191d23;--surface-2:#20252c;--surface-3:#2a3038;
|
|
85
|
-
--surface-inv:#e8eaed;--text:#e8eaed;--text-2:#adb4bd;--text-3:#868d96;--text-inv:#14171c;
|
|
86
|
-
--border:#2f353d;--border-soft:#23282f;
|
|
87
|
-
--accent:#8ab4f8;--accent-soft:#16243a;--accent-soft-2:#1d3557;--accent-text:#8ab4f8;--accent-on:#0d1b2e;--link:#8ab4f8}
|
|
88
|
-
html[data-theme="dark"]{color-scheme:dark;
|
|
89
|
-
--bg:#0d0f13;--surface:#14171c;--surface-1:#191d23;--surface-2:#20252c;--surface-3:#2a3038;
|
|
90
|
-
--surface-inv:#e8eaed;--text:#e8eaed;--text-2:#adb4bd;--text-3:#868d96;--text-inv:#14171c;
|
|
91
|
-
--border:#2f353d;--border-soft:#23282f;
|
|
92
|
-
--accent:#8ab4f8;--accent-soft:#16243a;--accent-soft-2:#1d3557;--accent-text:#8ab4f8;--accent-on:#0d1b2e;
|
|
93
|
-
--shadow-1:0 1px 2px rgba(0,0,0,.4),0 1px 3px 1px rgba(0,0,0,.24);
|
|
94
|
-
--shadow-2:0 1px 3px rgba(0,0,0,.5),0 4px 8px 3px rgba(0,0,0,.28)}
|
|
95
|
-
/* apple-mono 极简语汇:大字 · 大留白 · 近无阴影 · 圆角 20 · 系统蓝仅链接/CTA */
|
|
96
|
-
html[data-style="apple-mono"]{
|
|
97
|
-
--font-body:-apple-system,BlinkMacSystemFont,"SF Pro Text","Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
|
|
98
|
-
--font-display:-apple-system,BlinkMacSystemFont,"SF Pro Display","Helvetica Neue","PingFang SC",sans-serif;
|
|
99
|
-
--radius:20px;--fw-display:600;--fw-title:500;
|
|
100
|
-
--fs-display:clamp(3rem,5.4vw,4.75rem);--fs-h1:clamp(2.125rem,3.6vw,3.25rem);
|
|
101
|
-
--letter-display:-.04em;--letter-eyebrow:.12em;--lh-body:1.75;--lh-lead:1.7;
|
|
102
|
-
--gap:clamp(22px,2.2vw,36px);--link:#0071e3;
|
|
103
|
-
--bg:#fff;--surface:#fff;--surface-1:#f5f5f7;--surface-2:#f5f5f7;--surface-3:#e8e8ed;
|
|
104
|
-
--surface-inv:#1d1d1f;--text:#1d1d1f;--text-2:#6e6e73;--text-3:#86868b;--text-inv:#f5f5f7;
|
|
105
|
-
--border:#d2d2d7;--border-soft:#e8e8ed;
|
|
106
|
-
--accent:#1d1d1f;--accent-soft:#f5f5f7;--accent-soft-2:#e8e8ed;--accent-text:#1d1d1f;--accent-on:#fff;
|
|
107
|
-
--shadow-1:none;--shadow-2:0 4px 24px rgba(0,0,0,.06)}
|
|
108
|
-
html[data-style="apple-mono"][data-theme="dark"]{
|
|
109
|
-
--bg:#000;--surface:#000;--surface-1:#161617;--surface-2:#1d1d1f;--surface-3:#2a2a2d;
|
|
110
|
-
--surface-inv:#f5f5f7;--text:#f5f5f7;--text-2:#a1a1a6;--text-3:#86868b;--text-inv:#1d1d1f;
|
|
111
|
-
--border:#38383d;--border-soft:#2a2a2d;
|
|
112
|
-
--accent:#f5f5f7;--accent-soft:#1d1d1f;--accent-soft-2:#2a2a2d;--accent-text:#f5f5f7;--accent-on:#000;
|
|
113
|
-
--link:#2997ff}
|
|
114
|
-
/* mckinsey 咨询语汇:衬线标题 · 小圆角 6 · hairline · 无阴影 · eyebrow 拉开字距 · 密排 */
|
|
115
|
-
html[data-style="mckinsey"]{
|
|
116
|
-
--font-body:"Inter","IBM Plex Sans",-apple-system,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
|
|
117
|
-
--font-display:"Georgia","Times New Roman","Songti SC","STSong","SimSun",serif;
|
|
118
|
-
--radius:6px;--fw-display:600;--fw-title:600;
|
|
119
|
-
--letter-display:-.015em;--letter-eyebrow:.14em;--lh-body:1.55;--lh-lead:1.5;
|
|
120
|
-
--gap:clamp(14px,1.3vw,22px);
|
|
121
|
-
--bg:#fff;--surface:#fff;--surface-1:#f2f4f7;--surface-2:#f2f4f7;--surface-3:#e4e7ec;
|
|
122
|
-
--surface-inv:#101828;--text:#101828;--text-2:#475467;--text-3:#667085;--text-inv:#fff;
|
|
123
|
-
--border:#d0d5dd;--border-soft:#e4e7ec;
|
|
124
|
-
--accent:#003a70;--accent-soft:#eef4fb;--accent-soft-2:#d9e8f6;--accent-text:#003a70;--accent-on:#fff;
|
|
125
|
-
--shadow-1:none;--shadow-2:0 1px 2px rgba(16,24,40,.06)}
|
|
126
|
-
html[data-style="mckinsey"][data-theme="dark"]{
|
|
127
|
-
--bg:#0c1116;--surface:#0c1116;--surface-1:#12181f;--surface-2:#182030;--surface-3:#21293a;
|
|
128
|
-
--surface-inv:#e4e7ec;--text:#e4e7ec;--text-2:#aab3c2;--text-3:#8a94a6;--text-inv:#101828;
|
|
129
|
-
--border:#2a3342;--border-soft:#1f2733;
|
|
130
|
-
--accent:#7fb2e5;--accent-soft:#12233a;--accent-soft-2:#1b3358;--accent-text:#7fb2e5;--accent-on:#0c1a2e}
|
|
131
|
-
/* brand-red 正式品牌语汇:HarmonyOS/雅黑栈 · 圆角 14 · 品牌红强调 */
|
|
132
|
-
html[data-style="brand-red"]{
|
|
133
|
-
--font-body:"HarmonyOS Sans","PingFang SC","Microsoft YaHei","Noto Sans SC",-apple-system,"Segoe UI",sans-serif;
|
|
134
|
-
--font-display:var(--font-body);
|
|
135
|
-
--radius:14px;--fw-display:700;--fw-title:600;
|
|
136
|
-
--letter-display:-.025em;--letter-eyebrow:.12em;--lh-body:1.66;--lh-lead:1.6;
|
|
137
|
-
--bg:#fff;--surface:#fff;--surface-1:#faf7f7;--surface-2:#f5f0f0;--surface-3:#ece5e5;
|
|
138
|
-
--surface-inv:#1f1a1b;--text:#1f1a1b;--text-2:#5d5456;--text-3:#8a8083;--text-inv:#fff;
|
|
139
|
-
--border:#e2d9da;--border-soft:#ece5e5;
|
|
140
|
-
--accent:#d0021b;--accent-soft:#fbecec;--accent-soft-2:#f6d5d7;--accent-text:#b00217;--accent-on:#fff}
|
|
141
|
-
html[data-style="brand-red"][data-theme="dark"]{
|
|
142
|
-
--bg:#130d0e;--surface:#130d0e;--surface-1:#1a1213;--surface-2:#231718;--surface-3:#2e1e20;
|
|
143
|
-
--surface-inv:#f3ecec;--text:#f3ecec;--text-2:#c4b6b7;--text-3:#9a8d8e;--text-inv:#1f1a1b;
|
|
144
|
-
--border:#37272a;--border-soft:#2a1e20;
|
|
145
|
-
--accent:#ff5a66;--accent-soft:#331518;--accent-soft-2:#471c21;--accent-text:#ff7a83;--accent-on:#2a0d10}
|
|
146
|
-
/* warm-sand 编辑/年报语汇:暖米底 · 衬线标题 · 圆角 12 · 宽松行高 */
|
|
147
|
-
html[data-style="warm-sand"]{
|
|
148
|
-
--font-body:"Inter","Source Han Sans","PingFang SC","Microsoft YaHei",-apple-system,sans-serif;
|
|
149
|
-
--font-display:"Georgia","Playfair Display","Songti SC","STSong","SimSun",serif;
|
|
150
|
-
--radius:12px;--fw-display:600;--fw-title:600;
|
|
151
|
-
--letter-display:-.01em;--letter-eyebrow:.13em;--lh-body:1.72;--lh-lead:1.65;
|
|
152
|
-
--gap:clamp(18px,1.8vw,30px);
|
|
153
|
-
--bg:#fbf9f6;--surface:#fff;--surface-1:#f6f2ec;--surface-2:#efe9df;--surface-3:#e6ded2;
|
|
154
|
-
--surface-inv:#2a2521;--text:#2a2521;--text-2:#6b5f52;--text-3:#93836f;--text-inv:#f6f2ec;
|
|
155
|
-
--border:#e0d7c9;--border-soft:#eae3d6;
|
|
156
|
-
--accent:#96681f;--accent-soft:#f3ecdd;--accent-soft-2:#eaddc4;--accent-text:#8f6117;--accent-on:#fff;
|
|
157
|
-
--shadow-1:none;--shadow-2:0 1px 3px rgba(42,37,33,.08)}
|
|
158
|
-
html[data-style="warm-sand"][data-theme="dark"]{
|
|
159
|
-
--bg:#15120e;--surface:#15120e;--surface-1:#1d1913;--surface-2:#26201a;--surface-3:#312920;
|
|
160
|
-
--surface-inv:#ece5db;--text:#ece5db;--text-2:#c4b8a8;--text-3:#9b8d7a;--text-inv:#2a2521;
|
|
161
|
-
--border:#3a3128;--border-soft:#2c261e;
|
|
162
|
-
--accent:#d9b36a;--accent-soft:#2e2417;--accent-soft-2:#3e3019;--accent-text:#e0c184;--accent-on:#2a1f10}
|
|
163
|
-
/* deep-teal 沉稳语汇:冷灰底 · 深青强调 · 圆角 12 · 克制阴影 */
|
|
164
|
-
html[data-style="deep-teal"]{
|
|
165
|
-
--font-body:"Inter","Source Han Sans","PingFang SC","Microsoft YaHei",-apple-system,sans-serif;
|
|
166
|
-
--font-display:var(--font-body);
|
|
167
|
-
--radius:12px;--fw-display:700;--fw-title:600;
|
|
168
|
-
--letter-display:-.028em;--letter-eyebrow:.11em;--lh-body:1.66;--lh-lead:1.6;
|
|
169
|
-
--bg:#fff;--surface:#fff;--surface-1:#f2f6f5;--surface-2:#e8f0ef;--surface-3:#dce7e5;
|
|
170
|
-
--surface-inv:#14201e;--text:#14201e;--text-2:#4a5a57;--text-3:#73837f;--text-inv:#fff;
|
|
171
|
-
--border:#cfdedc;--border-soft:#e0eae9;
|
|
172
|
-
--accent:#0f6b5c;--accent-soft:#e4f1ee;--accent-soft-2:#cfe5e0;--accent-text:#0d5a4e;--accent-on:#fff}
|
|
173
|
-
html[data-style="deep-teal"][data-theme="dark"]{
|
|
174
|
-
--bg:#0d1312;--surface:#0d1312;--surface-1:#131b1a;--surface-2:#1a2523;--surface-3:#22302d;
|
|
175
|
-
--surface-inv:#e2ecea;--text:#e2ecea;--text-2:#b3c2bf;--text-3:#86948f;--text-inv:#14201e;
|
|
176
|
-
--border:#293834;--border-soft:#1e2a27;
|
|
177
|
-
--accent:#4fc3b0;--accent-soft:#12312c;--accent-soft-2:#1a423b;--accent-text:#66cfbd;--accent-on:#0c2420}
|
|
178
|
-
/* graphite-dark 石墨语汇:深色优先 · 亮蓝青 · 圆角 14 · 深色阴影更沉 */
|
|
179
|
-
html[data-style="graphite-dark"]{
|
|
180
|
-
--font-body:"Inter","SF Pro Text","PingFang SC","Microsoft YaHei",-apple-system,sans-serif;
|
|
181
|
-
--font-display:"Inter","SF Pro Display","PingFang SC",sans-serif;
|
|
182
|
-
--radius:14px;--fw-display:700;--fw-title:600;
|
|
183
|
-
--letter-display:-.03em;--letter-eyebrow:.1em;--lh-body:1.62;--lh-lead:1.55;
|
|
184
|
-
--gap:clamp(14px,1.4vw,24px);
|
|
185
|
-
--bg:#f4f5f7;--surface:#fff;--surface-1:#eceef1;--surface-2:#e3e6ea;--surface-3:#d8dbdf;
|
|
186
|
-
--surface-inv:#14171b;--text:#1a1d21;--text-2:#4b5158;--text-3:#767c84;--text-inv:#eef2f6;
|
|
187
|
-
--border:#ced1d6;--border-soft:#e3e6ea;
|
|
188
|
-
--accent:#0369a1;--accent-soft:#e2eff7;--accent-soft-2:#c8e1f2;--accent-text:#075985;--accent-on:#fff}
|
|
189
|
-
html[data-style="graphite-dark"][data-theme="dark"]{
|
|
190
|
-
--bg:#0b0e12;--surface:#12161b;--surface-1:#171d24;--surface-2:#1f2732;--surface-3:#28323f;
|
|
191
|
-
--surface-inv:#eef2f6;--text:#eef2f6;--text-2:#b9c2cd;--text-3:#85909d;--text-inv:#12161b;
|
|
192
|
-
--border:#2b3540;--border-soft:#212932;
|
|
193
|
-
--accent:#4da3ff;--accent-soft:#132841;--accent-soft-2:#1a3a5e;--accent-text:#6fb2ff;--accent-on:#07182b;
|
|
194
|
-
--shadow-1:0 1px 2px rgba(0,0,0,.5);--shadow-2:0 4px 12px rgba(0,0,0,.45)}
|
|
195
|
-
/* indigo-violet 创新语汇:冷白 + 靛紫 · 圆角 16 · 轻快字距 */
|
|
196
|
-
html[data-style="indigo-violet"]{
|
|
197
|
-
--font-body:"Inter","SF Pro Text","PingFang SC","Microsoft YaHei",-apple-system,sans-serif;
|
|
198
|
-
--font-display:var(--font-body);
|
|
199
|
-
--radius:16px;--fw-display:700;--fw-title:600;
|
|
200
|
-
--letter-display:-.032em;--letter-eyebrow:.12em;--lh-body:1.68;--lh-lead:1.62;
|
|
201
|
-
--bg:#fff;--surface:#fff;--surface-1:#f4f4fb;--surface-2:#ececf7;--surface-3:#e0e0f0;
|
|
202
|
-
--surface-inv:#1b1a2e;--text:#1b1a2e;--text-2:#52506b;--text-3:#7a779a;--text-inv:#fff;
|
|
203
|
-
--border:#d7d7e8;--border-soft:#e6e6f2;
|
|
204
|
-
--accent:#5b5bd6;--accent-soft:#ececf9;--accent-soft-2:#dcdcf3;--accent-text:#4848c0;--accent-on:#fff}
|
|
205
|
-
html[data-style="indigo-violet"][data-theme="dark"]{
|
|
206
|
-
--bg:#100f1c;--surface:#100f1c;--surface-1:#171629;--surface-2:#1f1e36;--surface-3:#292845;
|
|
207
|
-
--surface-inv:#e6e5f5;--text:#e6e5f5;--text-2:#b6b4d4;--text-3:#8b89b0;--text-inv:#1b1a2e;
|
|
208
|
-
--border:#322f52;--border-soft:#262441;
|
|
209
|
-
--accent:#a5a4f0;--accent-soft:#232146;--accent-soft-2:#2e2c58;--accent-text:#b6b5f5;--accent-on:#171631}
|
|
210
|
-
/* spectrum 数据语汇:中性骨架 + 彩色只进数据 · 圆角 14 · 紧凑栅格 */
|
|
211
|
-
html[data-style="spectrum"]{
|
|
212
|
-
--font-body:"Inter","SF Pro Text","PingFang SC","Microsoft YaHei",-apple-system,sans-serif;
|
|
213
|
-
--font-display:var(--font-body);
|
|
214
|
-
--radius:14px;--fw-display:700;--fw-title:600;
|
|
215
|
-
--letter-display:-.03em;--letter-eyebrow:.1em;--lh-body:1.6;--lh-lead:1.55;
|
|
216
|
-
--gap:clamp(12px,1.2vw,20px);
|
|
217
|
-
--bg:#fbfcfe;--surface:#fff;--surface-1:#f4f6fa;--surface-2:#eceff5;--surface-3:#dfe4ec;
|
|
218
|
-
--surface-inv:#171a21;--text:#171a21;--text-2:#4d5566;--text-3:#7b8494;--text-inv:#fff;
|
|
219
|
-
--border:#d6dce6;--border-soft:#e6eaf1;
|
|
220
|
-
--accent:#4563ef;--accent-soft:#eceeff;--accent-soft-2:#d8defb;--accent-text:#3d5bef;--accent-on:#fff}
|
|
221
|
-
html[data-style="spectrum"][data-theme="dark"]{
|
|
222
|
-
--bg:#0e1116;--surface:#131720;--surface-1:#171c26;--surface-2:#1d2330;--surface-3:#252c3b;
|
|
223
|
-
--surface-inv:#e8ebf2;--text:#e8ebf2;--text-2:#a7afbf;--text-3:#7e8798;--text-inv:#171a21;
|
|
224
|
-
--border:#2b3342;--border-soft:#212839;
|
|
225
|
-
--accent:#8ba0f8;--accent-soft:#1b2242;--accent-soft-2:#26305c;--accent-text:#93a7f9;--accent-on:#141a38}
|
|
226
|
-
|
|
227
|
-
*,*::before,*::after{box-sizing:border-box}
|
|
228
|
-
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;scroll-snap-type:y proximity}
|
|
229
|
-
body{margin:0;background:var(--bg);color:var(--text);
|
|
230
|
-
font-family:var(--font-body);
|
|
231
|
-
font-size:var(--fs-body);line-height:1.65;letter-spacing:.01em;-webkit-font-smoothing:antialiased;
|
|
232
|
-
transition:background-color var(--dur-medium) var(--ease-standard),color var(--dur-medium) var(--ease-standard)}
|
|
233
|
-
h1,h2,h3,h4,p{margin:0}
|
|
234
|
-
h1,h2,h3,h4{font-family:var(--font-display);font-weight:var(--fw-title);color:var(--text)}
|
|
235
|
-
.t-display{font-size:var(--fs-display);line-height:1.1;letter-spacing:var(--letter-display);font-weight:var(--fw-display)}
|
|
236
|
-
.t-h1{font-size:var(--fs-h1);line-height:1.18;letter-spacing:var(--letter-display);font-weight:var(--fw-title)}
|
|
237
|
-
.t-h2{font-size:var(--fs-h2);line-height:1.25;letter-spacing:var(--letter-display);font-weight:var(--fw-title)}
|
|
238
|
-
.t-h3{font-size:var(--fs-h3);line-height:1.35;letter-spacing:var(--letter-display);font-weight:var(--fw-title)}
|
|
239
|
-
.t-lead{font-size:var(--fs-lead);line-height:var(--lh-lead);color:var(--text-2)}
|
|
240
|
-
.t-body{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-2)}
|
|
241
|
-
.t-sm{font-size:var(--fs-sm);line-height:1.6;color:var(--text-3)}
|
|
242
|
-
.t-xs{font-size:var(--fs-xs);line-height:1.55;color:var(--text-3)}
|
|
243
|
-
.t-metric{font-size:var(--fs-metric);line-height:1;letter-spacing:var(--letter-display);font-weight:500;
|
|
244
|
-
font-variant-numeric:tabular-nums}
|
|
245
|
-
.t-eyebrow{font-size:var(--fs-xs);font-weight:600;letter-spacing:var(--letter-eyebrow);text-transform:uppercase;
|
|
246
|
-
color:var(--accent-text)}
|
|
247
|
-
.cite{font-size:.62em;vertical-align:super;color:var(--accent-text);font-weight:600;
|
|
248
|
-
margin-left:2px;text-decoration:none}
|
|
249
|
-
.cite:hover{text-decoration:underline}
|
|
250
|
-
.ref-link{color:var(--link);text-decoration:none;font-weight:500;
|
|
251
|
-
border-bottom:1px solid var(--accent-soft-2);padding-bottom:1px;
|
|
252
|
-
transition:border-color var(--dur-short),color var(--dur-short);white-space:nowrap}
|
|
253
|
-
.ref-link:hover{color:var(--accent);border-bottom-color:var(--accent)}
|
|
254
|
-
.ref-link svg{width:.82em;height:.82em;vertical-align:-.08em;margin-left:2px;opacity:.75}
|
|
255
|
-
.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:clamp(20px,3.2vw,56px)}
|
|
256
|
-
section{scroll-margin-top:calc(var(--bar-h) + 16px)}
|
|
257
|
-
section.band{scroll-snap-align:start}
|
|
258
|
-
/* ── 页面高度模型:默认「一页 = 一屏」——
|
|
259
|
-
所有 .band 至少一屏高并垂直居中,短内容页不再出现"高矮不齐";
|
|
260
|
-
内容超一屏时 min-height 不截断、自动退化为自然流(安全)。
|
|
261
|
-
svh 兼容移动端浏览器工具栏;旧浏览器落到 100vh。 */
|
|
262
|
-
.band{padding-block:var(--band-pad);min-height:var(--band-min);
|
|
263
|
-
display:flex;flex-direction:column;justify-content:center}
|
|
264
|
-
.band>.wrap{width:100%}
|
|
265
|
-
.band--top{justify-content:flex-start} /* 内容约六七成屏:贴顶 + 底部自然留白 */
|
|
266
|
-
.band--fit{justify-content:center} /* 内容接近一屏:垂直居中(默认行为,保留以兼容旧报告) */
|
|
267
|
-
.band--flow{min-height:0;justify-content:flex-start} /* 长内容页(附录/全量参考)显式退出固定页高 */
|
|
268
|
-
.band--tint{background:var(--surface-1)}
|
|
269
|
-
/* ── 强调带(主题一致):浅色/深色同向,不再出现"浅色模式末尾深色页" ──
|
|
270
|
-
band--accent accent-soft 软强调(收尾 / CTA / 小结)
|
|
271
|
-
band--accent--solid accent 实底强调(引言 / 金句 / 关键判断,文字走 --accent-on)
|
|
272
|
-
band--deep 固定深色反相页(**两主题一致**:始终深底浅字,不再随主题翻转)
|
|
273
|
-
——仅限显式选择,全文 ≤1 且不得作末页(校验器 emphasis 约束) */
|
|
274
|
-
.band--accent{background:var(--accent-soft)}
|
|
275
|
-
.band--accent .t-eyebrow{color:var(--accent-text)}
|
|
276
|
-
.band--accent .card{background:var(--surface);border-color:var(--border-soft)}
|
|
277
|
-
.band--accent--solid{background:var(--accent);color:var(--accent-on)}
|
|
278
|
-
.band--accent--solid .t-h1,.band--accent--solid .t-h2,.band--accent--solid .t-h3,
|
|
279
|
-
.band--accent--solid .t-display{color:var(--accent-on)}
|
|
280
|
-
.band--accent--solid .t-lead,.band--accent--solid .t-body{color:color-mix(in srgb,var(--accent-on) 80%,transparent)}
|
|
281
|
-
.band--accent--solid .t-eyebrow{color:color-mix(in srgb,var(--accent-on) 84%,transparent)}
|
|
282
|
-
.band--accent--solid .card{background:color-mix(in srgb,var(--accent-on) 12%,transparent);
|
|
283
|
-
border-color:color-mix(in srgb,var(--accent-on) 20%,transparent);color:var(--accent-on)}
|
|
284
|
-
.band--accent--solid .card .t-h3,.band--accent--solid .card .t-body{color:inherit}
|
|
285
|
-
.band--accent--solid .ul li{color:color-mix(in srgb,var(--accent-on) 84%,transparent)}
|
|
286
|
-
.band--accent--solid .ul li::before{background:var(--accent-on)}
|
|
287
|
-
.band--deep{background:var(--deep-bg);color:var(--deep-fg)}
|
|
288
|
-
.band--deep .t-lead,.band--deep .t-body{color:var(--deep-fg-2)}
|
|
289
|
-
.band--deep .t-eyebrow{color:color-mix(in srgb,var(--accent) 40%,#fff)}
|
|
290
|
-
.band--deep .card{background:color-mix(in srgb,var(--deep-fg) 6%,var(--deep-bg));border-color:var(--deep-line);color:var(--deep-fg)}
|
|
291
|
-
.band--deep .card .t-h3,.band--deep .card .t-body{color:inherit}
|
|
292
|
-
.band--deep .ul li{color:var(--deep-fg-2)}
|
|
293
|
-
.band--deep .ul li::before{background:color-mix(in srgb,var(--accent) 40%,#fff)}
|
|
294
|
-
.band--deep .tbl-wrap table thead th{color:var(--deep-fg)}
|
|
295
|
-
.band--deep .tbl-wrap table tbody td{color:var(--deep-fg-2)}
|
|
296
|
-
.a-c{align-items:center}.a-start{align-items:start}.a-end{align-items:end}
|
|
297
|
-
.j-c{justify-content:center}.j-between{justify-content:space-between}
|
|
298
|
-
.grow{flex:1;min-width:0}
|
|
299
|
-
.grid{display:grid;gap:var(--gap)}
|
|
300
|
-
.g-2{grid-template-columns:repeat(2,minmax(0,1fr))}
|
|
301
|
-
.g-3{grid-template-columns:repeat(3,minmax(0,1fr))}
|
|
302
|
-
.g-4{grid-template-columns:repeat(4,minmax(0,1fr))}
|
|
303
|
-
.g-5{grid-template-columns:repeat(5,minmax(0,1fr))}
|
|
304
|
-
.g-6{grid-template-columns:repeat(6,minmax(0,1fr))}
|
|
305
|
-
.g-auto{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
|
|
306
|
-
.g-hero{grid-template-columns:minmax(0,1.08fr) minmax(0,1fr);align-items:center}
|
|
307
|
-
.g-hero__visual{min-width:0}
|
|
308
|
-
.g-side{grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);align-items:start}
|
|
309
|
-
.stack{display:flex;flex-direction:column}
|
|
310
|
-
.gap-3{gap:var(--sp-3)}.gap-4{gap:var(--sp-4)}.gap-5{gap:var(--sp-5)}.gap-6{gap:var(--sp-6)}
|
|
311
|
-
.row{display:flex;align-items:center;gap:var(--sp-3)}.row-wrap{flex-wrap:wrap}
|
|
312
|
-
/* ── 密度版式扩展(内容丰富度分级选版式 · 见 content-rules.md 四-d / `components.md` §46)──
|
|
313
|
-
g-hero--rev:Hero 视觉居左(大图开场,文字收在右侧)
|
|
314
|
-
g-side--rev:图右文左(与 g-side 镜像,用于"结论先行、证据靠右")
|
|
315
|
-
g-31 / g-13:3:1 / 1:3 非对称双栏(主次分明的中等密度页)
|
|
316
|
-
g-41 / g-14:4:1 / 1:4 强主次双栏(主件 + 窄侧栏注解/指标条) */
|
|
317
|
-
.g-hero--rev{grid-template-columns:minmax(0,1fr) minmax(0,1.08fr);align-items:center}
|
|
318
|
-
.g-side--rev{grid-template-columns:minmax(0,1fr) minmax(0,1.35fr);align-items:start}
|
|
319
|
-
.g-31{grid-template-columns:minmax(0,3fr) minmax(0,1fr)}
|
|
320
|
-
.g-13{grid-template-columns:minmax(0,1fr) minmax(0,3fr)}
|
|
321
|
-
.g-41{grid-template-columns:minmax(0,4fr) minmax(0,1fr)}
|
|
322
|
-
.g-14{grid-template-columns:minmax(0,1fr) minmax(0,4fr)}
|
|
323
|
-
/* ── 版式 pattern 扩展(非对称多栏 / 象限 / 侧栏 / 马赛克 / 全幅主件)──
|
|
324
|
-
g-211 / g-121:三栏非对称(主栏 + 两个辅助栏;主次分明且不呆板)
|
|
325
|
-
g-quad:2×2 象限(四宫格,每格等权,适合四象限分析 / 四类并列)
|
|
326
|
-
g-aside:主内容 + 粘性侧栏(长文或大图 + 常驻注解/指标栏)
|
|
327
|
-
g-mosaic:命名区马赛克(hero 通栏 + 三卡,一屏一重心)
|
|
328
|
-
g-hero-full:全幅主件 + 底部三等分(大图/大表 + 支撑卡)
|
|
329
|
-
rows-2 / rows-3:等高行分区(纵向节奏,替代等高卡片堆叠) */
|
|
330
|
-
.g-211{grid-template-columns:minmax(0,2fr) minmax(0,1fr) minmax(0,1fr)}
|
|
331
|
-
.g-121{grid-template-columns:minmax(0,1fr) minmax(0,2fr) minmax(0,1fr)}
|
|
332
|
-
.g-quad{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:minmax(0,1fr);align-items:stretch}
|
|
333
|
-
.g-aside{grid-template-columns:minmax(0,1fr) minmax(200px,.36fr);align-items:start}
|
|
334
|
-
.g-aside__side{position:sticky;top:calc(var(--bar-h) + 16px)}
|
|
335
|
-
.g-mosaic{grid-template-columns:repeat(3,minmax(0,1fr));
|
|
336
|
-
grid-template-areas:"hero hero hero" "a b c";grid-auto-rows:auto}
|
|
337
|
-
.g-mosaic__hero{grid-area:hero}.g-mosaic__a{grid-area:a}
|
|
338
|
-
.g-mosaic__b{grid-area:b}.g-mosaic__c{grid-area:c}
|
|
339
|
-
.g-hero-full{grid-template-columns:repeat(3,minmax(0,1fr));
|
|
340
|
-
grid-template-areas:"main main main" "s1 s2 s3"}
|
|
341
|
-
.g-hero-full__main{grid-area:main}
|
|
342
|
-
.g-hero-full__s1{grid-area:s1}.g-hero-full__s2{grid-area:s2}.g-hero-full__s3{grid-area:s3}
|
|
343
|
-
.rows-2{grid-template-rows:repeat(2,minmax(0,1fr));grid-auto-rows:minmax(0,1fr)}
|
|
344
|
-
.rows-3{grid-template-rows:repeat(3,minmax(0,1fr));grid-auto-rows:minmax(0,1fr)}
|
|
345
|
-
.kv--2col{grid-template-columns:auto minmax(0,1fr) auto minmax(0,1fr)}
|
|
346
|
-
/* ── 拼贴栅格 g-bento(Bento / 马赛克):4 列基准 + 跨列/跨行工具类 ──
|
|
347
|
-
.sp-2/.sp-3/.sp-4 跨列(span),.rw-2 跨行;窄屏自动降列并收敛 span。 */
|
|
348
|
-
.g-bento{grid-template-columns:repeat(4,minmax(0,1fr));grid-auto-rows:minmax(118px,auto)}
|
|
349
|
-
.sp-2{grid-column:span 2}.sp-3{grid-column:span 3}.sp-4{grid-column:span 4}
|
|
350
|
-
.rw-2{grid-row:span 2}
|
|
351
|
-
@media (max-width:1180px){.g-bento{grid-template-columns:repeat(2,minmax(0,1fr))}
|
|
352
|
-
.sp-3,.sp-4{grid-column:span 2}}
|
|
353
|
-
@media (max-width:620px){.g-bento{grid-template-columns:1fr}
|
|
354
|
-
.sp-2,.sp-3,.sp-4{grid-column:span 1}.rw-2{grid-row:auto}}
|
|
355
|
-
/* ── 交错图文版式 .stagger(左右交替:一行图左文右、一行文左图右) ── */
|
|
356
|
-
.stagger{display:flex;flex-direction:column;gap:clamp(20px,2.8vh,40px)}
|
|
357
|
-
.stagger__row{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
|
|
358
|
-
gap:clamp(20px,3vw,52px);align-items:center}
|
|
359
|
-
.stagger__row--rev .stagger__media{order:2}
|
|
360
|
-
.stagger__row--rev .stagger__txt{order:1}
|
|
361
|
-
@media (max-width:960px){.stagger__row{grid-template-columns:1fr}
|
|
362
|
-
.stagger__row--rev .stagger__media,.stagger__row--rev .stagger__txt{order:0}}
|
|
363
|
-
/* ── 键值对照 .kv(定义列表:指标口径 / 参数 / 对照项) ── */
|
|
364
|
-
.kv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:var(--sp-3) var(--sp-5);
|
|
365
|
-
align-items:baseline;margin:0}
|
|
366
|
-
.kv dt{font-size:var(--fs-sm);font-weight:600;color:var(--text);white-space:nowrap}
|
|
367
|
-
.kv dd{margin:0;font-size:var(--fs-sm);color:var(--text-2);line-height:1.6}
|
|
368
|
-
@media (max-width:1180px){.g-4{grid-template-columns:repeat(2,minmax(0,1fr))}
|
|
369
|
-
.g-5{grid-template-columns:repeat(3,minmax(0,1fr))}
|
|
370
|
-
.g-6{grid-template-columns:repeat(3,minmax(0,1fr))}.g-side,.g-side--rev{grid-template-columns:1fr}}
|
|
371
|
-
@media (max-width:960px){.g-3,.g-hero,.g-hero--rev,.g-31,.g-13,.g-41,.g-14,.g-211,.g-121,.g-quad,.g-aside{grid-template-columns:1fr}
|
|
372
|
-
.g-aside__side{position:static}
|
|
373
|
-
.g-mosaic,.g-hero-full{grid-template-columns:1fr;grid-template-areas:none}
|
|
374
|
-
.g-hero__visual{order:-1;max-width:460px;margin-inline:auto}}
|
|
375
|
-
@media (max-width:620px){.g-2,.g-4,.g-5,.g-6{grid-template-columns:1fr}}
|
|
376
|
-
|
|
377
|
-
/* ── 顶栏 + header 工具组(主题/风格/预览/帮助/全屏/折叠;无模式切换) ── */
|
|
378
|
-
.bar{position:sticky;top:0;z-index:50;height:var(--bar-h);
|
|
379
|
-
background:color-mix(in srgb,var(--bg) 86%,transparent);
|
|
380
|
-
backdrop-filter:saturate(160%) blur(14px);-webkit-backdrop-filter:saturate(160%) blur(14px);
|
|
381
|
-
border-bottom:1px solid var(--border-soft);
|
|
382
|
-
transition:height var(--dur-medium) var(--ease-standard)}
|
|
383
|
-
.bar__in{height:100%;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-4)}
|
|
384
|
-
.brand{display:flex;align-items:center;gap:var(--sp-3);min-width:0}
|
|
385
|
-
.brand__mark{width:34px;height:34px;flex:none;border-radius:10px;background:var(--accent);
|
|
386
|
-
color:var(--accent-on);display:grid;place-items:center;font-size:13px;font-weight:700}
|
|
387
|
-
.brand__title{font-size:var(--fs-sm);font-weight:600;color:var(--text);white-space:nowrap;
|
|
388
|
-
overflow:hidden;text-overflow:ellipsis}
|
|
389
|
-
.brand__sub{font-size:var(--fs-xs);color:var(--text-3);white-space:nowrap;overflow:hidden;
|
|
390
|
-
text-overflow:ellipsis}
|
|
391
|
-
.nav{display:flex;align-items:center;gap:var(--sp-1)}
|
|
392
|
-
.nav a{padding:8px 14px;border-radius:var(--r-full);color:var(--text-2);text-decoration:none;
|
|
393
|
-
font-size:var(--fs-sm);font-weight:500;white-space:nowrap;transition:background-color var(--dur-short),color var(--dur-short)}
|
|
394
|
-
.nav a:hover{background:var(--surface-2);color:var(--text)}
|
|
395
|
-
@media (max-width:1180px){.nav{display:none}}
|
|
396
|
-
.iconbtn{width:40px;height:40px;flex:none;display:grid;place-items:center;
|
|
397
|
-
border:1px solid var(--border);border-radius:var(--r-full);background:transparent;
|
|
398
|
-
color:var(--text-2);cursor:pointer;transition:background-color var(--dur-short),color var(--dur-short)}
|
|
399
|
-
.iconbtn:hover{background:var(--surface-2);color:var(--text)}
|
|
400
|
-
.iconbtn svg{width:19px;height:19px}
|
|
401
|
-
.icon-sun,.icon-moon{display:none}
|
|
402
|
-
html[data-theme="light"] .icon-moon{display:block}
|
|
403
|
-
html[data-theme="dark"] .icon-sun{display:block}
|
|
404
|
-
.toolsbar{display:flex;align-items:center;gap:6px;flex:none}
|
|
405
|
-
@media (max-width:620px){.toolsbar .iconbtn{width:36px;height:36px}
|
|
406
|
-
.toolsbar .iconbtn svg{width:17px;height:17px}}
|
|
407
|
-
/* ── header 折叠态(B 键收起成迷你工具条:只留品牌标 + 展开钮;--bar-h 联动收紧) ── */
|
|
408
|
-
html.bar--fold{--bar-h:48px}
|
|
409
|
-
html.bar--fold .bar{border-bottom-color:transparent}
|
|
410
|
-
html.bar--fold .brand__txt,html.bar--fold .nav{display:none}
|
|
411
|
-
html.bar--fold .brand{gap:0}
|
|
412
|
-
html.bar--fold .brand__mark{width:28px;height:28px;border-radius:8px;font-size:11px}
|
|
413
|
-
html.bar--fold .toolsbar>*{display:none}
|
|
414
|
-
html.bar--fold .toolsbar #barFold{display:grid}
|
|
415
|
-
.icon-fold{display:block}.icon-unfold{display:none}
|
|
416
|
-
html.bar--fold .icon-fold{display:none}
|
|
417
|
-
html.bar--fold .icon-unfold{display:block}
|
|
418
|
-
/* ── 全屏按钮图标态(F 键同效) ── */
|
|
419
|
-
.icon-fsx{display:block}.icon-fsx-exit{display:none}
|
|
420
|
-
html.is-fs .icon-fsx{display:none}html.is-fs .icon-fsx-exit{display:block}
|
|
421
|
-
/* 风格下拉(header 内,克制弹出面板) */
|
|
422
|
-
.seld{position:relative}
|
|
423
|
-
.seld__pop{position:absolute;top:calc(100% + 10px);right:0;width:238px;background:var(--surface);
|
|
424
|
-
border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--shadow-2);
|
|
425
|
-
padding:var(--sp-3);display:none;z-index:80}
|
|
426
|
-
.seld.open .seld__pop{display:block}
|
|
427
|
-
.seld__lab{font-size:var(--fs-xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;
|
|
428
|
-
color:var(--text-3);padding:2px 6px 8px}
|
|
429
|
-
.seld__list{display:flex;flex-direction:column;gap:2px;max-height:330px;overflow:auto}
|
|
430
|
-
.sitem{display:flex;align-items:center;gap:10px;padding:7px 10px;border:none;background:transparent;
|
|
431
|
-
border-radius:var(--r-sm);color:var(--text-2);font-size:var(--fs-sm);font-weight:500;cursor:pointer;
|
|
432
|
-
text-align:left;font-family:inherit;transition:background-color var(--dur-short),color var(--dur-short)}
|
|
433
|
-
.sitem:hover{background:var(--surface-2);color:var(--text)}
|
|
434
|
-
.sitem.on{background:var(--accent-soft);color:var(--accent-text);font-weight:600}
|
|
435
|
-
.sitem .swd{width:12px;height:12px;border-radius:50%;flex:none;
|
|
436
|
-
box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--text) 12%,transparent)}
|
|
437
|
-
@media print{.toolsbar{display:none!important}.seld__pop{display:none!important}}
|
|
438
|
-
|
|
439
|
-
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:48px;
|
|
440
|
-
padding-inline:26px;border-radius:var(--r-full);font-size:var(--fs-sm);font-weight:500;
|
|
441
|
-
text-decoration:none;cursor:pointer;white-space:nowrap;border:1px solid transparent;
|
|
442
|
-
transition:background-color var(--dur-short),box-shadow var(--dur-short),transform var(--dur-short)}
|
|
443
|
-
.btn:hover{transform:translateY(-1px)}
|
|
444
|
-
.btn--primary{background:var(--accent);color:var(--accent-on)}
|
|
445
|
-
.btn--primary:hover{box-shadow:var(--shadow-2)}
|
|
446
|
-
.btn--soft{background:var(--accent-soft);color:var(--accent-text)}
|
|
447
|
-
.btn--ghost{background:transparent;color:var(--text);border-color:var(--border)}
|
|
448
|
-
.btn--onDark{background:var(--surface);color:var(--text)}
|
|
449
|
-
.card{background:var(--surface);border:1px solid var(--border-soft);border-radius:var(--r-lg);
|
|
450
|
-
padding:clamp(20px,2vw,30px);transition:border-color var(--dur-short),box-shadow var(--dur-short),background-color var(--dur-medium)}
|
|
451
|
-
.card:hover{border-color:var(--border);box-shadow:var(--shadow-1)}
|
|
452
|
-
.card--flat{background:var(--surface-1);border-color:transparent}
|
|
453
|
-
.card--flat:hover{background:var(--surface-2)}
|
|
454
|
-
.card--accent{background:var(--accent-soft);border-color:transparent}
|
|
455
|
-
.card__hd{display:flex;align-items:center;gap:var(--sp-3);margin-bottom:var(--sp-3)}
|
|
456
|
-
.card__ico{width:34px;height:34px;flex:none;border-radius:10px;background:var(--accent-soft);
|
|
457
|
-
color:var(--accent-text);display:grid;place-items:center;font-size:15px;font-weight:600}
|
|
458
|
-
.card__ico svg{width:18px;height:18px}
|
|
459
|
-
.chip{display:inline-flex;align-items:center;gap:6px;height:30px;padding-inline:13px;
|
|
460
|
-
border-radius:var(--r-full);background:var(--surface-2);color:var(--text-2);
|
|
461
|
-
font-size:var(--fs-xs);font-weight:500;white-space:nowrap}
|
|
462
|
-
.chip--accent{background:var(--accent-soft);color:var(--accent-text)}
|
|
463
|
-
.chip--line{background:transparent;border:1px solid var(--border);color:var(--text-2)}
|
|
464
|
-
.metric{position:relative;background:var(--surface);border:1px solid var(--border-soft);
|
|
465
|
-
border-radius:var(--r-lg);
|
|
466
|
-
padding:clamp(18px,1.7vw,26px);display:flex;flex-direction:column;gap:var(--sp-2)}
|
|
467
|
-
.metric:hover{border-color:var(--border);box-shadow:var(--shadow-1)}
|
|
468
|
-
.metric__v{display:flex;align-items:baseline;gap:4px;color:var(--text)}
|
|
469
|
-
.metric__v small{font-size:.42em;font-weight:500;letter-spacing:0;color:var(--text-3)}
|
|
470
|
-
.metric__k{font-size:var(--fs-sm);color:var(--text-2);font-weight:500}
|
|
471
|
-
.metric__n{font-size:var(--fs-xs);color:var(--text-3);line-height:1.5}
|
|
472
|
-
.metric--warn .metric__v{color:var(--accent)}
|
|
473
|
-
.metric__ico{position:absolute;top:clamp(14px,1.4vw,20px);right:clamp(14px,1.4vw,20px);
|
|
474
|
-
width:20px;height:20px;color:var(--text-3)}
|
|
475
|
-
.ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--sp-4)}
|
|
476
|
-
.ul li{position:relative;padding-left:30px;color:var(--text-2);font-size:var(--fs-body);
|
|
477
|
-
line-height:1.65}
|
|
478
|
-
.ul li::before{content:"";position:absolute;left:0;top:.52em;width:7px;height:7px;
|
|
479
|
-
border-radius:50%;background:var(--accent)}
|
|
480
|
-
.ul li strong{color:var(--text);font-weight:600}
|
|
481
|
-
.ul--flow li::before{content:"→";width:auto;height:auto;background:none;color:var(--accent);
|
|
482
|
-
font-weight:700;top:0;left:0}
|
|
483
|
-
.ul--check li::before{width:18px;height:18px;top:.18em;background:var(--accent-soft);
|
|
484
|
-
color:var(--accent-text);content:"✓";font-size:11px;font-weight:700;display:grid;place-items:center}
|
|
485
|
-
.ul--num{counter-reset:n}.ul--num li{padding-left:34px;counter-increment:n}
|
|
486
|
-
.ul--num li::before{content:counter(n);width:22px;height:22px;top:.06em;border-radius:50%;
|
|
487
|
-
background:var(--accent-soft);color:var(--accent-text);font-size:11px;font-weight:700;
|
|
488
|
-
display:grid;place-items:center}
|
|
489
|
-
.ul--ico li{padding-left:34px}
|
|
490
|
-
.ul--ico li>svg{position:absolute;left:0;top:.14em;width:17px;height:17px;color:var(--accent-text)}
|
|
491
|
-
.tbl-wrap{border:1px solid var(--border-soft);border-radius:var(--r-lg);overflow:hidden;
|
|
492
|
-
background:var(--surface);overflow-x:auto}
|
|
493
|
-
/* min-width 防列挤,但绝不能超过容器:半幅/双区版式里写死 720px 会把列裁掉(裁切比压缩更糟) */
|
|
494
|
-
table{width:100%;border-collapse:collapse;min-width:min(720px,100%)}
|
|
495
|
-
thead th{background:var(--surface-1);color:var(--text-2);font-size:var(--fs-xs);font-weight:600;
|
|
496
|
-
letter-spacing:.04em;text-transform:uppercase;text-align:left;padding:15px 20px;
|
|
497
|
-
border-bottom:1px solid var(--border);white-space:nowrap}
|
|
498
|
-
tbody td{padding:17px 20px;border-bottom:1px solid var(--border-soft);font-size:var(--fs-sm);
|
|
499
|
-
color:var(--text-2);vertical-align:top;line-height:1.6}
|
|
500
|
-
tbody tr:last-child td{border-bottom:none}
|
|
501
|
-
tbody tr:hover{background:var(--surface-1)}
|
|
502
|
-
td strong{color:var(--text);font-weight:600}
|
|
503
|
-
td.k{color:var(--text);font-weight:500;white-space:nowrap}
|
|
504
|
-
td.num{font-variant-numeric:tabular-nums}
|
|
505
|
-
.note{display:flex;gap:var(--sp-4);padding:clamp(20px,2vw,28px);border-radius:var(--r-lg);
|
|
506
|
-
background:var(--accent-soft);border-left:3px solid var(--accent)}
|
|
507
|
-
.note__ico{width:26px;height:26px;flex:none;border-radius:50%;background:var(--accent);
|
|
508
|
-
color:var(--accent-on);display:grid;place-items:center;font-size:13px;font-weight:700;font-style:normal}
|
|
509
|
-
.note__t{font-size:var(--fs-h3);font-weight:600;color:var(--text);margin-bottom:6px}
|
|
510
|
-
.tl{position:relative;padding-left:30px}
|
|
511
|
-
.tl::before{content:"";position:absolute;left:7px;top:10px;bottom:10px;width:2px;
|
|
512
|
-
background:var(--border);border-radius:2px}
|
|
513
|
-
.tl__i{position:relative;padding-bottom:var(--sp-6)}
|
|
514
|
-
.tl__i:last-child{padding-bottom:0}
|
|
515
|
-
.tl__d{position:absolute;left:-30px;top:5px;width:16px;height:16px;border-radius:50%;
|
|
516
|
-
background:var(--surface);border:3px solid var(--border)}
|
|
517
|
-
.tl__i--now .tl__d{border-color:var(--accent);background:var(--accent)}
|
|
518
|
-
.tl__i--done .tl__d{border-color:var(--accent)}
|
|
519
|
-
.tl__t{font-size:var(--fs-h3);font-weight:600;color:var(--text);margin-bottom:6px}
|
|
520
|
-
.tl__l{font-size:var(--fs-xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;
|
|
521
|
-
color:var(--accent-text);margin-bottom:2px}
|
|
522
|
-
.fig{background:var(--surface);border:1px solid var(--border-soft);border-radius:var(--r-xl);
|
|
523
|
-
padding:clamp(20px,2.2vw,36px);overflow:hidden}
|
|
524
|
-
|
|
525
|
-
/* ── 锁定版式组件(新页型 steps / heatmap / bullet / pyramid)──
|
|
526
|
-
几何与 PPTX 同源页型一致(layout-constants.json 的 pageTypes.steps/heatmap/bullet/pyramid)。 */
|
|
527
|
-
|
|
528
|
-
/* 步骤条:N 步横排(序号 + 标题 + 说明)+ 步间箭头;可分组(.steps__phase 阶段标签) */
|
|
529
|
-
.steps{display:flex;align-items:stretch}
|
|
530
|
-
.step{flex:1;min-width:0;position:relative;background:var(--surface);border:1px solid var(--border-soft);
|
|
531
|
-
border-radius:var(--r-md);padding:var(--sp-4) var(--sp-4) var(--sp-5)}
|
|
532
|
-
.step+.step{margin-left:calc(var(--sp-4) + 14px)}
|
|
533
|
-
.step+.step::before{content:"";position:absolute;left:calc(-1 * (var(--sp-4) + 14px));top:50%;
|
|
534
|
-
width:calc(var(--sp-4) + 14px);height:2px;background:var(--border);transform:translateY(-50%)}
|
|
535
|
-
.step+.step::after{content:"";position:absolute;left:-11px;top:50%;width:8px;height:8px;
|
|
536
|
-
border-top:2px solid var(--border);border-right:2px solid var(--border);
|
|
537
|
-
transform:translateY(-50%) rotate(45deg)}
|
|
538
|
-
.step__n{font-size:var(--fs-xs);font-weight:700;letter-spacing:.08em;color:var(--accent-text);
|
|
539
|
-
font-variant-numeric:tabular-nums;margin-bottom:6px}
|
|
540
|
-
.step__t{font-size:var(--fs-h3);font-weight:600;color:var(--text);margin-bottom:4px}
|
|
541
|
-
.step__d{font-size:var(--fs-sm);color:var(--text-3);line-height:1.55}
|
|
542
|
-
.step--a{background:var(--accent-soft);border-color:transparent}
|
|
543
|
-
.step--a .step__t{color:var(--accent-text)}
|
|
544
|
-
.steps__phase{display:flex;align-items:center;gap:var(--sp-3);margin-bottom:var(--sp-3);
|
|
545
|
-
font-size:var(--fs-xs);font-weight:700;letter-spacing:.06em;color:var(--text-3);text-transform:uppercase}
|
|
546
|
-
.steps__phase::after{content:"";flex:1;height:1px;background:var(--border-soft)}
|
|
547
|
-
@media (max-width:960px){.steps{flex-direction:column;gap:var(--sp-3)}
|
|
548
|
-
.step+.step{margin-left:0}.step+.step::before,.step+.step::after{display:none}}
|
|
549
|
-
|
|
550
|
-
/* 热力矩阵:行标签 × 列标签 + 强度色阶(accent 明度阶梯,不引入第二色相) */
|
|
551
|
-
.heat{border:1px solid var(--border-soft);border-radius:var(--r-md);background:var(--surface);
|
|
552
|
-
padding:clamp(14px,1.4vw,22px)}
|
|
553
|
-
.heat__grid{display:grid;grid-template-columns:var(--heat-label,150px) repeat(var(--heat-cols,4),minmax(0,1fr));
|
|
554
|
-
gap:6px;align-items:stretch}
|
|
555
|
-
.heat__h{font-size:var(--fs-xs);font-weight:700;letter-spacing:.05em;color:var(--text-2);
|
|
556
|
-
text-align:center;padding:6px 8px;border-bottom:2px solid var(--border)}
|
|
557
|
-
.heat__rh{font-size:var(--fs-xs);font-weight:700;color:var(--text-2);display:flex;align-items:center;
|
|
558
|
-
justify-content:flex-end;text-align:right;padding:6px 12px 6px 0;border-right:2px solid var(--border)}
|
|
559
|
-
.heat__c{background:var(--surface-1);border-radius:var(--r-sm);padding:10px 12px;text-align:center;
|
|
560
|
-
font-size:var(--fs-sm);font-weight:600;color:var(--text-2);font-variant-numeric:tabular-nums;
|
|
561
|
-
line-height:1.4;display:flex;flex-direction:column;justify-content:center;gap:2px}
|
|
562
|
-
.heat__c small{display:block;font-size:var(--fs-xs);font-weight:500;color:var(--text-3)}
|
|
563
|
-
.heat__c--1{background:var(--accent-soft);color:var(--accent-text)}
|
|
564
|
-
.heat__c--2{background:var(--accent-soft-2);color:var(--accent-text)}
|
|
565
|
-
.heat__c--3{background:var(--accent);color:var(--accent-on)}
|
|
566
|
-
.heat__c--3 small{color:color-mix(in srgb,var(--accent-on) 70%,transparent)}
|
|
567
|
-
.heat__legend{display:flex;align-items:center;gap:var(--sp-2);margin-top:var(--sp-3);
|
|
568
|
-
font-size:var(--fs-xs);color:var(--text-3)}
|
|
569
|
-
.heat__sw{width:18px;height:10px;border-radius:3px}
|
|
570
|
-
|
|
571
|
-
/* 达成对比:每行 = 标签 + 底槽 + 实际条 + 目标刻度 + 数值(实际/目标) */
|
|
572
|
-
.bul{display:flex;flex-direction:column;gap:var(--sp-4)}
|
|
573
|
-
.bul__row{display:grid;grid-template-columns:minmax(0,2.2fr) minmax(0,5fr) minmax(0,1.6fr);
|
|
574
|
-
align-items:center;gap:var(--sp-4)}
|
|
575
|
-
.bul__k{font-size:var(--fs-sm);color:var(--text);font-weight:600}
|
|
576
|
-
.bul__track{position:relative;height:10px;border-radius:999px;background:var(--surface-2)}
|
|
577
|
-
.bul__fill{position:absolute;top:0;bottom:0;left:0;border-radius:999px;background:var(--accent)}
|
|
578
|
-
.bul__tgt{position:absolute;top:-5px;bottom:-5px;width:2px;background:var(--text-3)}
|
|
579
|
-
.bul__tgt::after{content:"";position:absolute;top:-3px;left:-3px;width:8px;height:8px;
|
|
580
|
-
border-radius:2px;background:var(--text-3);transform:rotate(45deg)}
|
|
581
|
-
.bul__v{font-size:var(--fs-sm);color:var(--text-3);text-align:right;font-variant-numeric:tabular-nums}
|
|
582
|
-
.bul__v b{color:var(--text)}
|
|
583
|
-
.bul__row--warn .bul__fill{background:var(--text-3)}
|
|
584
|
-
|
|
585
|
-
/* 金字塔:自上而下逐层加宽(--w 控制该层宽度占比) */
|
|
586
|
-
.pyr{display:flex;flex-direction:column;gap:var(--sp-2);align-items:center}
|
|
587
|
-
.pyr__lvl{width:var(--w,100%);max-width:100%;background:var(--surface);border:1px solid var(--border-soft);
|
|
588
|
-
border-radius:var(--r-md);padding:var(--sp-3) var(--sp-5);display:grid;
|
|
589
|
-
grid-template-columns:minmax(0,auto) minmax(0,1fr);gap:var(--sp-4);align-items:center}
|
|
590
|
-
.pyr__lvl--a{background:var(--accent-soft);border-color:transparent}
|
|
591
|
-
.pyr__t{font-size:var(--fs-h3);font-weight:600;color:var(--text);white-space:nowrap}
|
|
592
|
-
.pyr__lvl--a .pyr__t{color:var(--accent-text)}
|
|
593
|
-
.pyr__d{font-size:var(--fs-sm);color:var(--text-3);line-height:1.5}
|
|
594
|
-
@media (max-width:960px){.pyr__lvl{width:100%!important;grid-template-columns:1fr;gap:var(--sp-2)}}
|
|
595
|
-
|
|
596
|
-
/* ── 结构图(分层 arch / 泳道 lane / 正交流)──
|
|
597
|
-
布局语法:节点等距、连接件正交带箭头、图例右下;三模式共用(v9 迁入引擎)。 */
|
|
598
|
-
.arch{display:flex;flex-direction:column;gap:0}
|
|
599
|
-
.arch__layer{display:grid;grid-template-columns:120px 1fr;gap:var(--sp-4);align-items:stretch}
|
|
600
|
-
.arch__lname{font-size:var(--fs-xs);font-weight:700;letter-spacing:.06em;color:var(--text-3);
|
|
601
|
-
display:flex;align-items:center;padding-left:12px;border-left:3px solid var(--border-soft)}
|
|
602
|
-
.arch__layer--focus .arch__lname{color:var(--accent-text);border-left-color:var(--accent)}
|
|
603
|
-
.arch__nodes{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--sp-3)}
|
|
604
|
-
.arch__node{background:var(--surface);border:1px solid var(--border-soft);border-radius:var(--r-md);
|
|
605
|
-
padding:var(--sp-3) var(--sp-4);transition:border-color .18s,box-shadow .18s}
|
|
606
|
-
.arch__node:hover{border-color:var(--border);box-shadow:var(--shadow-1)}
|
|
607
|
-
.arch__node--accent{background:var(--accent-soft);border-color:transparent}
|
|
608
|
-
.arch__nt{font-size:var(--fs-sm);font-weight:600;color:var(--text);margin-bottom:2px}
|
|
609
|
-
.arch__nd{font-size:var(--fs-xs);color:var(--text-3);line-height:1.45}
|
|
610
|
-
.arch__legend{display:flex;flex-wrap:wrap;gap:var(--sp-3);justify-content:flex-end;margin-top:var(--sp-4)}
|
|
611
|
-
.arch__conn{display:grid;place-items:center;height:28px;color:var(--text-3);margin-left:120px}
|
|
612
|
-
.arch__conn svg{display:block}
|
|
613
|
-
.lane{display:grid;grid-template-columns:110px 1fr;border:1px solid var(--border-soft);
|
|
614
|
-
border-radius:var(--r-md);overflow:hidden;background:var(--surface)}
|
|
615
|
-
.lane+.lane{margin-top:var(--sp-3)}
|
|
616
|
-
.lane__hd{background:var(--surface-1);padding:var(--sp-3) var(--sp-2);font-size:var(--fs-xs);
|
|
617
|
-
font-weight:700;letter-spacing:.04em;color:var(--text-2);display:flex;align-items:center;
|
|
618
|
-
justify-content:center;text-align:center;border-right:1px solid var(--border-soft)}
|
|
619
|
-
.lane__body{display:flex;align-items:center;gap:0;padding:var(--sp-3);flex-wrap:wrap;row-gap:var(--sp-2)}
|
|
620
|
-
.lane__step{background:var(--surface);border:1px solid var(--border-soft);border-radius:var(--r-sm);
|
|
621
|
-
padding:8px 14px;font-size:var(--fs-sm);font-weight:600;color:var(--text);white-space:nowrap}
|
|
622
|
-
.lane__step--a{background:var(--accent-soft);border-color:transparent;color:var(--accent-text)}
|
|
623
|
-
/* 步骤间正交箭头(CSS 线段+箭头,替代裸文字 →) */
|
|
624
|
-
.lane__arr{position:relative;flex:none;width:28px;height:12px;color:var(--text-3);margin:0 2px}
|
|
625
|
-
.lane__arr::before{content:"";position:absolute;left:2px;right:8px;top:50%;height:2px;
|
|
626
|
-
background:currentColor;transform:translateY(-50%)}
|
|
627
|
-
.lane__arr::after{content:"";position:absolute;right:2px;top:50%;width:7px;height:7px;
|
|
628
|
-
border-top:2px solid currentColor;border-right:2px solid currentColor;
|
|
629
|
-
transform:translateY(-50%) rotate(45deg)}
|
|
630
|
-
/* 正交流(简单流程骨架):动作盒 + 判断菱形占位 + 主线箭头 */
|
|
631
|
-
.flow-rail{display:flex;align-items:center;gap:0;flex-wrap:wrap;row-gap:var(--sp-3)}
|
|
632
|
-
.flow-box{background:var(--surface);border:1px solid var(--border-soft);border-radius:var(--r-md);
|
|
633
|
-
padding:12px 18px;font-size:var(--fs-sm);font-weight:600;color:var(--text);min-width:100px;text-align:center}
|
|
634
|
-
.flow-box--a{background:var(--accent);border-color:transparent;color:var(--accent-on)}
|
|
635
|
-
.flow-box--end{border-radius:999px}
|
|
636
|
-
.flow-box--ex{background:var(--surface-2);border-style:dashed;color:var(--text-2)}
|
|
637
|
-
.flow-dia{width:72px;height:72px;display:grid;place-items:center;flex:none;position:relative}
|
|
638
|
-
.flow-dia svg{position:absolute;inset:0;width:100%;height:100%}
|
|
639
|
-
.flow-dia span{position:relative;z-index:1;font-size:var(--fs-xs);font-weight:600;color:var(--accent-text);
|
|
640
|
-
text-align:center;line-height:1.25;padding:0 6px}
|
|
641
|
-
@media (max-width:960px){.arch__layer{grid-template-columns:1fr}
|
|
642
|
-
.arch__conn{margin-left:0}.lane{grid-template-columns:1fr}
|
|
643
|
-
.lane__hd{border-right:none;border-bottom:1px solid var(--border-soft)}}
|
|
644
|
-
|
|
645
|
-
/* ── 待核实标注(二次修改强调色体系)──
|
|
646
|
-
.tbd 内联标注:accent 色 + 虚线底(无法核实的数字 / xx% / 待补口径)
|
|
647
|
-
.tbd-legend 页级图例:说明"标色项待核实"(内联标注页必须配一处)
|
|
648
|
-
.flagbar 块级清单条:逐条列出待核实项(数据页/收尾页收口) */
|
|
649
|
-
.tbd{color:var(--accent-text);font-weight:600;border-bottom:1px dashed var(--accent);
|
|
650
|
-
padding-bottom:1px;white-space:nowrap}
|
|
651
|
-
.tbd-legend{display:flex;align-items:flex-start;gap:var(--sp-2);margin-top:var(--sp-4);
|
|
652
|
-
font-size:var(--fs-xs);color:var(--text-3);line-height:1.55}
|
|
653
|
-
.tbd-legend svg{width:14px;height:14px;flex:none;margin-top:.18em;color:var(--accent-text)}
|
|
654
|
-
.tbd-legend b{color:var(--accent-text);font-weight:600}
|
|
655
|
-
.flagbar{margin-top:var(--sp-4);border:1px solid var(--border-soft);border-left:3px solid var(--accent);
|
|
656
|
-
border-radius:var(--r-md);background:var(--accent-soft);padding:var(--sp-3) var(--sp-4)}
|
|
657
|
-
.flagbar__hd{display:flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-xs);
|
|
658
|
-
font-weight:700;letter-spacing:.04em;color:var(--accent-text);margin-bottom:var(--sp-2)}
|
|
659
|
-
.flagbar__hd svg{width:15px;height:15px;flex:none}
|
|
660
|
-
.flagbar ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}
|
|
661
|
-
.flagbar li{position:relative;padding-left:16px;font-size:var(--fs-xs);color:var(--text-2);line-height:1.55}
|
|
662
|
-
.flagbar li::before{content:"";position:absolute;left:0;top:.52em;width:6px;height:6px;
|
|
663
|
-
border-radius:2px;background:var(--accent)}
|
|
664
|
-
|
|
665
|
-
/* ── 素材图片 .media(效果与位置锁定:比例 / 裁切 / 圆角 / 蒙版图注 / 全幅)──
|
|
666
|
-
图片源只允许 data: 内联或相对路径(零外链铁律,禁 http(s) 外链图)。
|
|
667
|
-
比例:--r16-9 / --r4-3 / --r3-2 / --r1-1 / --r21-9;--contain 完整显示不裁切;
|
|
668
|
-
--plain 去圆角去边框;--mask 底部蒙版 + 图上白字图注;--full 通栏出血。 */
|
|
669
|
-
.media{position:relative;display:block;border-radius:var(--r-lg);overflow:hidden;
|
|
670
|
-
background:var(--surface-1);border:1px solid var(--border-soft)}
|
|
671
|
-
.media img{display:block;width:100%;height:100%;object-fit:cover}
|
|
672
|
-
.media--r16-9{aspect-ratio:16/9}.media--r4-3{aspect-ratio:4/3}.media--r3-2{aspect-ratio:3/2}
|
|
673
|
-
.media--r1-1{aspect-ratio:1/1}.media--r21-9{aspect-ratio:21/9}.media--r3-1{aspect-ratio:3/1}
|
|
674
|
-
.media--contain img{object-fit:contain}
|
|
675
|
-
.media--plain{border:none;border-radius:0}
|
|
676
|
-
.media--mask::after{content:"";position:absolute;inset:0;z-index:1;
|
|
677
|
-
background:linear-gradient(180deg,transparent 42%,color-mix(in srgb,var(--surface-inv) 64%,transparent))}
|
|
678
|
-
.media__cap{position:absolute;left:0;right:0;bottom:0;z-index:2;padding:var(--sp-4) var(--sp-5);
|
|
679
|
-
color:var(--text-inv);font-size:var(--fs-sm);line-height:1.5}
|
|
680
|
-
.media__cap--below{position:static;color:var(--text-3);padding:var(--sp-3) 0 0;font-size:var(--fs-xs)}
|
|
681
|
-
.media--full{margin-inline:calc(-1 * clamp(20px,3.2vw,56px));border-radius:0;
|
|
682
|
-
border-left:none;border-right:none}
|
|
683
|
-
/* ── 图片版式族(与 PPTX image.layout 一一对应:full/half/bleed/grid/compare/wall)──
|
|
684
|
-
full = 版心全宽(默认,配 --r16-9 / --r4-3 锁比例)
|
|
685
|
-
half = 左图右注(.grid.g-side + .media + 右栏 .stack,见 `components.md` §11c)
|
|
686
|
-
bleed = 通栏出血(--bleed,等价 --full;用于章节大图)
|
|
687
|
-
grid = 多图网格(.media-grid--2/3/4,图下小图注)
|
|
688
|
-
compare = 双图 A/B 对比(.media-compare + .media__tag 角标)
|
|
689
|
-
wall = Logo 墙(.media-wall,等距小图、灰阶弱化)
|
|
690
|
-
占位符 .media--ph = 无图时的原生占位(虚线框 + 图标 + 尺寸提示),零外链、随主题变色。 */
|
|
691
|
-
.media--bleed{margin-inline:calc(-1 * clamp(20px,3.2vw,56px));border-radius:0;
|
|
692
|
-
border-left:none;border-right:none}
|
|
693
|
-
.media--ph{border:1px dashed var(--border);background:var(--surface-1);
|
|
694
|
-
display:grid;place-items:center;text-align:center;gap:var(--sp-2)}
|
|
695
|
-
.media--ph::before{content:"";position:absolute;inset:0;opacity:.5;
|
|
696
|
-
background-image:repeating-linear-gradient(135deg,transparent 0 9px,var(--border-soft) 9px 10px)}
|
|
697
|
-
.media__ph{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;
|
|
698
|
-
gap:6px;padding:var(--sp-4)}
|
|
699
|
-
.media__ph svg{width:28px;height:28px;stroke:var(--accent);fill:none;stroke-width:1.6}
|
|
700
|
-
.media__ph b{font-size:var(--fs-sm);font-weight:600;color:var(--text-2)}
|
|
701
|
-
.media__ph span{font-size:var(--fs-xs);color:var(--text-3)}
|
|
702
|
-
.media-grid{display:grid;gap:clamp(12px,1.6vw,22px);align-items:start}
|
|
703
|
-
.media-grid--2{grid-template-columns:repeat(2,minmax(0,1fr))}
|
|
704
|
-
.media-grid--3{grid-template-columns:repeat(3,minmax(0,1fr))}
|
|
705
|
-
.media-grid--4{grid-template-columns:repeat(4,minmax(0,1fr))}
|
|
706
|
-
.media-grid .media__cap--below{text-align:center}
|
|
707
|
-
.media-compare{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
|
|
708
|
-
gap:clamp(16px,2.4vw,40px);align-items:start}
|
|
709
|
-
.media__tag{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:22px;
|
|
710
|
-
padding-inline:8px;border-radius:var(--r-full);background:var(--accent-soft);color:var(--accent-text);
|
|
711
|
-
font-size:var(--fs-xs);font-weight:700;letter-spacing:.04em}
|
|
712
|
-
.media-wall{display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));
|
|
713
|
-
gap:clamp(10px,1.4vw,20px);align-items:center}
|
|
714
|
-
.media-wall .media{background:var(--surface-2);filter:grayscale(1);opacity:.82;
|
|
715
|
-
transition:filter var(--dur-short),opacity var(--dur-short)}
|
|
716
|
-
.media-wall .media:hover{filter:none;opacity:1}
|
|
717
|
-
.media__src{margin-top:var(--sp-2);font-size:var(--fs-xs);color:var(--text-3)}
|
|
718
|
-
@media (max-width:1180px){.media-grid--4{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
719
|
-
@media (max-width:760px){.media-grid--3,.media-grid--4,.media-compare{grid-template-columns:1fr}
|
|
720
|
-
.media--bleed{margin-inline:calc(-1 * clamp(16px,4vw,28px))}}
|
|
721
|
-
.fig__cap{text-align:center;margin-bottom:var(--sp-4);font-size:var(--fs-sm);
|
|
722
|
-
color:var(--text-3);font-weight:500}
|
|
723
|
-
.fig svg{display:block;width:100%;height:auto}
|
|
724
|
-
.f-acc{fill:var(--accent)}.f-accs{fill:var(--accent-soft)}.f-accs2{fill:var(--accent-soft-2)}
|
|
725
|
-
.f-s1{fill:var(--surface-1)}.f-s2{fill:var(--surface-2)}.f-s3{fill:var(--surface-3)}
|
|
726
|
-
.f-inv{fill:var(--surface-inv)}
|
|
727
|
-
.f-txt{fill:var(--text)}.f-txt2{fill:var(--text-2)}.f-txt3{fill:var(--text-3)}.f-none{fill:none}
|
|
728
|
-
.s-bd{stroke:var(--border)}.s-bds{stroke:var(--border-soft)}
|
|
729
|
-
.s-acc{stroke:var(--accent)}.s-txt3{stroke:var(--text-3)}
|
|
730
|
-
.t-on-inv{fill:var(--text-inv)}
|
|
731
|
-
/* ── 编码色板(数据系列 / 图例色点 / 小段标签)──
|
|
732
|
-
与结构色纪律正交:结构色(标题/正文/边框/按钮/大面积底色)恒为中性 + 单一强调色;
|
|
733
|
-
编码色只出现在数据本体。**9 套风格各有自己的 c1–c5**(单源 = scripts/layout-constants.json
|
|
734
|
-
的 styleDataColors / styleDataColorsDark;改色值改 JSON,再跑 sync_runtime.py)。
|
|
735
|
-
无 data-style 时回落中性,不串色。 */
|
|
736
|
-
:root{--c1:var(--accent);--c2:var(--surface-3);--c3:var(--border);
|
|
737
|
-
--c4:var(--accent-soft-2);--c5:var(--text-3)}
|
|
738
|
-
html[data-style="business-blue"]{--c1:#1a73e8;--c2:#3526bb;--c3:#21a199;--c4:#b77148;--c5:#99983d}
|
|
739
|
-
html[data-style="business-blue"][data-theme="dark"]{--c1:#8ab4f8;--c2:#7966e0;--c3:#88e7e5;--c4:#d4ad91;--c5:#cbce83}
|
|
740
|
-
html[data-style="apple-mono"]{--c1:#1d1d1f;--c2:#0071e3;--c3:#0f7b6c;--c4:#c2410c;--c5:#7c3aed}
|
|
741
|
-
html[data-style="apple-mono"][data-theme="dark"]{--c1:#f5f5f7;--c2:#2997ff;--c3:#2dd4bf;--c4:#ff9f0a;--c5:#bf5af2}
|
|
742
|
-
html[data-style="mckinsey"]{--c1:#003a70;--c2:#2826bb;--c3:#21a18e;--c4:#b76848;--c5:#99903d}
|
|
743
|
-
html[data-style="mckinsey"][data-theme="dark"]{--c1:#7fb2e5;--c2:#6f6bdb;--c3:#8ce3d8;--c4:#d1a694;--c5:#cbc586}
|
|
744
|
-
html[data-style="brand-red"]{--c1:#d0021b;--c2:#bb6326;--c3:#d634ae;--c4:#40a182;--c5:#438aa8}
|
|
745
|
-
html[data-style="brand-red"][data-theme="dark"]{--c1:#ff5a66;--c2:#e09e66;--c3:#e788cb;--c4:#91d4c2;--c5:#83b4ce}
|
|
746
|
-
html[data-style="warm-sand"]{--c1:#96681f;--c2:#839324;--c3:#cf3b3e;--c4:#4d88b2;--c5:#4a47a4}
|
|
747
|
-
html[data-style="warm-sand"][data-theme="dark"]{--c1:#d9b36a;--c2:#c2d571;--c3:#df9391;--c4:#97b5ce;--c5:#8e89c7}
|
|
748
|
-
html[data-style="deep-teal"]{--c1:#0f6b5c;--c2:#2683bb;--c3:#22aa3e;--c4:#b74870;--c5:#a85c43}
|
|
749
|
-
html[data-style="deep-teal"][data-theme="dark"]{--c1:#4fc3b0;--c2:#7aaecd;--c3:#97d8a4;--c4:#c99cac;--c5:#c29c8f}
|
|
750
|
-
html[data-style="graphite-dark"]{--c1:#0369a1;--c2:#2637bb;--c3:#1f9977;--c4:#b75948;--c5:#a18a40}
|
|
751
|
-
html[data-style="graphite-dark"][data-theme="dark"]{--c1:#4da3ff;--c2:#6c66e0;--c3:#88e7dc;--c4:#d4a691;--c5:#cec983}
|
|
752
|
-
html[data-style="indigo-violet"]{--c1:#5b5bd6;--c2:#7432ae;--c3:#4197c8;--c4:#a08f4b;--c5:#7ca04b}
|
|
753
|
-
html[data-style="indigo-violet"][data-theme="dark"]{--c1:#a5a4f0;--c2:#a967e0;--c3:#89c3e7;--c4:#d3c792;--c5:#aece83}
|
|
754
|
-
html[data-style="spectrum"]{--c1:#4563ef;--c2:#5b26bb;--c3:#269ebb;--c4:#af8746;--c5:#83993d}
|
|
755
|
-
html[data-style="spectrum"][data-theme="dark"]{--c1:#8ba0f8;--c2:#9066e0;--c3:#88d6e7;--c4:#d4ba91;--c5:#bdce83}
|
|
756
|
-
.f-c1{fill:var(--c1)}.f-c2{fill:var(--c2)}.f-c3{fill:var(--c3)}.f-c4{fill:var(--c4)}.f-c5{fill:var(--c5)}
|
|
757
|
-
.s-c1{stroke:var(--c1)}.s-c2{stroke:var(--c2)}.s-c3{stroke:var(--c3)}.s-c4{stroke:var(--c4)}.s-c5{stroke:var(--c5)}
|
|
758
|
-
.shead{margin-bottom:clamp(28px,3.4vh,48px);max-width:940px}
|
|
759
|
-
.shead__title{margin-block:var(--sp-3) var(--sp-4)}
|
|
760
|
-
.shead__desc{max-width:900px}
|
|
761
|
-
.shead--center{margin-inline:auto;text-align:center}
|
|
762
|
-
.shead--center .shead__desc{margin-inline:auto}
|
|
763
|
-
.foot{padding-block:clamp(40px,5vh,64px);border-top:1px solid var(--border-soft);
|
|
764
|
-
background:var(--surface-1)}
|
|
765
|
-
.foot__in{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-5);flex-wrap:wrap}
|
|
766
|
-
@media (prefers-reduced-motion:no-preference){.rv{opacity:0;transform:translateY(14px)}
|
|
767
|
-
.rv.in{opacity:1;transform:none;transition:opacity var(--dur-long) var(--ease-emphasized),
|
|
768
|
-
transform var(--dur-long) var(--ease-emphasized)}}
|
|
769
|
-
@media (prefers-reduced-motion:reduce){.rv{opacity:1;transform:none}html{scroll-behavior:auto}}
|
|
770
|
-
/* ── 图表轻量动效(进入视口触发;JS 加 .in) ── */
|
|
771
|
-
[data-anim]{transform-box:fill-box}
|
|
772
|
-
.chart rect[data-anim="grow"]{transform:scaleY(0);transform-origin:center bottom;
|
|
773
|
-
transition:transform var(--dur-xlong) var(--ease-emphasized)}
|
|
774
|
-
.chart.in rect[data-anim="grow"]{transform:scaleY(1)}
|
|
775
|
-
.chart--x rect[data-anim="grow"]{transform:scaleX(0);transform-origin:left center}
|
|
776
|
-
.chart--x.in rect[data-anim="grow"]{transform:scaleX(1)}
|
|
777
|
-
.chart [data-anim="fade"]{opacity:0;transition:opacity var(--dur-long) ease var(--dur-short)}
|
|
778
|
-
.chart.in [data-anim="fade"]{opacity:1}
|
|
779
|
-
@media (prefers-reduced-motion:reduce){[data-anim]{transition:none!important;transform:none!important;opacity:1!important}}
|
|
780
|
-
/* 降级总闸:前面的 reduce 块只管入场动效,hover 的位移/缩放仍会触发前庭变换 */
|
|
781
|
-
@media (prefers-reduced-motion:reduce){
|
|
782
|
-
*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important}
|
|
783
|
-
.btn:hover,.pager__dot:hover,.card:hover,.metric:hover{transform:none!important}
|
|
784
|
-
}
|
|
785
|
-
/* ── Agenda 大纲页 ── */
|
|
786
|
-
.agenda{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
|
|
787
|
-
.agenda__i{border-top:1px solid var(--border-soft)}
|
|
788
|
-
.agenda__i:last-child{border-bottom:1px solid var(--border-soft)}
|
|
789
|
-
.agenda__a{display:grid;grid-template-columns:auto 1fr auto;align-items:center;
|
|
790
|
-
gap:clamp(16px,2.4vw,36px);padding:clamp(18px,2.2vh,26px) clamp(4px,.6vw,12px);
|
|
791
|
-
text-decoration:none;color:inherit;transition:background-color var(--dur-short)}
|
|
792
|
-
.agenda__a:hover{background:var(--surface-1)}
|
|
793
|
-
.agenda__n{font-size:clamp(1.4rem,2vw,1.9rem);font-weight:var(--fw-display);
|
|
794
|
-
color:var(--accent);font-variant-numeric:tabular-nums;line-height:1;min-width:2.2em}
|
|
795
|
-
.agenda__t{font-size:var(--fs-h3);font-weight:var(--fw-title);color:var(--text);margin-bottom:3px}
|
|
796
|
-
.agenda__d{font-size:var(--fs-sm);color:var(--text-3)}
|
|
797
|
-
.agenda__go{color:var(--text-3);transition:color var(--dur-short),transform var(--dur-short)}
|
|
798
|
-
.agenda__a:hover .agenda__go{color:var(--accent);transform:translateX(4px)}
|
|
799
|
-
.agenda__go svg{width:20px;height:20px;display:block}
|
|
800
|
-
@media (min-width:900px){
|
|
801
|
-
.agenda--2col{display:grid;grid-template-columns:1fr 1fr;column-gap:clamp(20px,3vw,48px)}
|
|
802
|
-
.agenda--2col .agenda__i{border-top:1px solid var(--border-soft)}}
|
|
803
|
-
/* ── 页码指示(翻页导航) ── */
|
|
804
|
-
.pager{position:fixed;right:clamp(10px,1.6vw,26px);top:50%;transform:translateY(-50%);
|
|
805
|
-
z-index:60;display:flex;flex-direction:column;align-items:center;gap:10px}
|
|
806
|
-
.pager__count{font-size:var(--fs-xs);font-weight:600;color:var(--text-3);
|
|
807
|
-
font-variant-numeric:tabular-nums;letter-spacing:.04em}
|
|
808
|
-
.pager__dots{display:flex;flex-direction:column;gap:8px}
|
|
809
|
-
.pager__dot{width:9px;height:9px;border-radius:50%;border:1px solid var(--border);
|
|
810
|
-
background:transparent;cursor:pointer;padding:0;transition:background-color var(--dur-short),transform var(--dur-short),border-color var(--dur-short)}
|
|
811
|
-
.pager__dot:hover{transform:scale(1.3);border-color:var(--accent)}
|
|
812
|
-
.pager__dot.on{background:var(--accent);border-color:var(--accent);transform:scale(1.25)}
|
|
813
|
-
@media (max-width:760px){.pager{right:6px;gap:6px}.pager__dot{width:7px;height:7px}}
|
|
814
|
-
@media (prefers-reduced-motion:reduce){.pager__dot{transition:none}}
|
|
815
|
-
/* ── PPTX 预览/导出/帮助 模态(预览与导出同一引擎,所见即所得;帮助为双通道指引) ── */
|
|
816
|
-
.pmodal{position:fixed;inset:0;z-index:90;display:none;align-items:center;justify-content:center;
|
|
817
|
-
background:color-mix(in srgb,var(--surface-inv) 55%,transparent);
|
|
818
|
-
backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
|
|
819
|
-
.pmodal.open{display:flex}
|
|
820
|
-
.pmodal__panel{width:min(1060px,94vw);max-height:90vh;display:flex;flex-direction:column;
|
|
821
|
-
background:var(--surface);border:1px solid var(--border);border-radius:var(--r-xl);
|
|
822
|
-
box-shadow:var(--shadow-2);overflow:hidden}
|
|
823
|
-
.pmodal__hd{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-4);
|
|
824
|
-
padding:var(--sp-4) var(--sp-5);border-bottom:1px solid var(--border-soft)}
|
|
825
|
-
.pmodal__t{font-size:var(--fs-h3);font-weight:600;color:var(--text)}
|
|
826
|
-
.pmodal__meta{font-size:var(--fs-xs);color:var(--text-3);margin-top:2px}
|
|
827
|
-
.pmodal__body{overflow:auto;padding:var(--sp-5);display:grid;
|
|
828
|
-
grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:var(--sp-4)}
|
|
829
|
-
.pslide{position:relative;width:100%;aspect-ratio:16/9;border:1px solid var(--border-soft);
|
|
830
|
-
border-radius:var(--r-sm);background:var(--bg);overflow:hidden;font-size:7px; /* JS 按宽度重设:1em=1pt;底色随主题(与导出的亮/暗版一致) */ }
|
|
831
|
-
.pslide__no{position:absolute;left:8px;bottom:5px;z-index:5;font-size:1.4em;font-weight:600;
|
|
832
|
-
color:var(--text-3);letter-spacing:.04em}
|
|
833
|
-
.pslide [data-el]{position:absolute;overflow:hidden}
|
|
834
|
-
.pslide [data-el] p{margin:0}
|
|
835
|
-
.pmodal__ft{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-4);
|
|
836
|
-
padding:var(--sp-4) var(--sp-5);border-top:1px solid var(--border-soft);flex-wrap:wrap}
|
|
837
|
-
.pmodal__ft .row{flex:none}
|
|
838
|
-
.pmodal__issues{flex:1;min-width:280px;font-size:var(--fs-xs);color:var(--text-2);line-height:1.6}
|
|
839
|
-
.pmodal__issues b{color:var(--accent-text)}
|
|
840
|
-
.pmodal__issues .btn{height:40px;padding-inline:18px}
|
|
841
|
-
.pmodal__prompt{width:100%;margin-top:var(--sp-3);padding:var(--sp-3);border:1px dashed var(--border);
|
|
842
|
-
border-radius:var(--r-md);background:var(--surface-1);
|
|
843
|
-
font-family:ui-monospace,Consolas,"Courier New",monospace;font-size:11px;line-height:1.65;
|
|
844
|
-
color:var(--text-2);white-space:pre-wrap;max-height:150px;overflow:auto}
|
|
845
|
-
/* 帮助模态(复用 pmodal 骨架,文档式单列) */
|
|
846
|
-
.pmodal__panel--doc{width:min(860px,94vw)}
|
|
847
|
-
.pmodal__panel--doc .pmodal__body{display:block}
|
|
848
|
-
.hdoc{font-size:var(--fs-sm);color:var(--text-2);line-height:1.75}
|
|
849
|
-
.hdoc h4{font-size:var(--fs-h3);color:var(--text);margin:var(--sp-5) 0 var(--sp-2)}
|
|
850
|
-
.hdoc h4:first-child{margin-top:0}
|
|
851
|
-
.hdoc p{margin-bottom:var(--sp-3)}
|
|
852
|
-
.hdoc b{color:var(--text)}
|
|
853
|
-
.hdoc .hch{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:var(--sp-4);
|
|
854
|
-
margin:var(--sp-4) 0}
|
|
855
|
-
.hdoc .hch>div{background:var(--surface-1);border:1px solid var(--border-soft);
|
|
856
|
-
border-radius:var(--r-md);padding:var(--sp-4)}
|
|
857
|
-
.hdoc .hch h5{font-size:var(--fs-sm);font-weight:700;color:var(--accent-text);margin:0 0 var(--sp-2)}
|
|
858
|
-
.hdoc .hch p{font-size:var(--fs-xs);margin:0}
|
|
859
|
-
.hdoc code{font-family:ui-monospace,Consolas,"Courier New",monospace;font-size:var(--fs-xs);
|
|
860
|
-
background:var(--surface-2);border-radius:6px;padding:2px 7px;color:var(--text)}
|
|
861
|
-
@media print{.pmodal{display:none!important}}
|
|
862
|
-
@media print{.rv{opacity:1!important;transform:none!important}
|
|
863
|
-
.band{padding-block:24px;break-inside:avoid}body{background:#fff;color:#000}
|
|
864
|
-
.pager{display:none!important}}
|
|
865
|
-
/* __TOPPPT_ENGINE_END__ */
|
|
866
|
-
|
|
867
|
-
/* ── 演示模式专属层(本模板即 presentation 形态:版心 1400 / 大字号 / 大留白;无模式切换) ── */
|
|
868
|
-
:root{--band-pad:clamp(56px,8.5vh,116px)}
|
|
869
|
-
</style>
|
|
870
|
-
</head>
|
|
871
|
-
<body>
|
|
872
|
-
|
|
873
|
-
<!-- ══ 顶栏 + header 工具组(风格 / 预览 / 帮助 / 全屏 / 主题 / 折叠) ══ -->
|
|
874
|
-
<header class="bar">
|
|
875
|
-
<div class="wrap bar__in">
|
|
876
|
-
<div class="brand">
|
|
877
|
-
<div class="brand__mark">R</div>
|
|
878
|
-
<div class="brand__txt">
|
|
879
|
-
<div class="brand__title">AI 智能体发展研究</div>
|
|
880
|
-
<div class="brand__sub">从工具到同事</div>
|
|
881
|
-
</div>
|
|
882
|
-
</div>
|
|
883
|
-
<nav class="nav">
|
|
884
|
-
<a href="#s1">范式跃迁</a>
|
|
885
|
-
<a href="#s2">形态边界</a>
|
|
886
|
-
<a href="#refs">参考资料</a>
|
|
887
|
-
</nav>
|
|
888
|
-
<div class="toolsbar">
|
|
889
|
-
<div class="seld" id="styleSel">
|
|
890
|
-
<button class="iconbtn" id="styleBtn" type="button" aria-label="切换风格" title="风格(9 套实时切换 · 纯视觉皮肤不动内容)">
|
|
891
|
-
<svg viewBox="0 0 24 24" fill="currentColor" stroke="none">
|
|
892
|
-
<circle cx="13.5" cy="6.5" r="1.2"/><circle cx="17.5" cy="10.5" r="1.2"/>
|
|
893
|
-
<circle cx="8.5" cy="7.5" r="1.2"/><circle cx="6.5" cy="12.5" r="1.2"/>
|
|
894
|
-
<path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.93 0 1.65-.75 1.65-1.69 0-.44-.18-.84-.44-1.13-.29-.29-.44-.65-.44-1.12a1.64 1.64 0 0 1 1.67-1.67h2c3.05 0 5.56-2.5 5.56-5.55C22 6.01 17.46 2 12 2z" fill="none" stroke="currentColor" stroke-width="1.6"/></svg>
|
|
895
|
-
</button>
|
|
896
|
-
<div class="seld__pop" id="stylePop" role="menu" aria-label="风格选择">
|
|
897
|
-
<div class="seld__lab">风格 · 9 套</div>
|
|
898
|
-
<div class="seld__list" id="styleList"></div>
|
|
899
|
-
</div>
|
|
900
|
-
</div>
|
|
901
|
-
<button class="iconbtn" id="pptPreviewBtn" type="button" aria-label="预览 PPTX" title="预览 PPTX(P)· 与智能体精导同一引擎,所见即所得">
|
|
902
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
903
|
-
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
|
904
|
-
</button>
|
|
905
|
-
<button class="iconbtn" id="helpBtn" type="button" aria-label="PPT 生成指引" title="PPT 生成指引(H)· 精导通道说明与提示词">
|
|
906
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
907
|
-
<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>
|
|
908
|
-
</button>
|
|
909
|
-
<button class="iconbtn" id="fsBtn" type="button" aria-label="全屏" title="全屏(F)">
|
|
910
|
-
<svg class="icon-fsx" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"
|
|
911
|
-
stroke-linecap="round" stroke-linejoin="round">
|
|
912
|
-
<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
|
|
913
|
-
<svg class="icon-fsx-exit" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"
|
|
914
|
-
stroke-linecap="round" stroke-linejoin="round">
|
|
915
|
-
<path d="M4 14h6v6M20 10h-6V4M14 10l7-7M3 21l7-7"/></svg>
|
|
916
|
-
</button>
|
|
917
|
-
<button class="iconbtn" id="themeBtn" type="button" aria-label="切换亮/暗主题" title="切换亮/暗主题(T)">
|
|
918
|
-
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"
|
|
919
|
-
stroke-linecap="round" stroke-linejoin="round">
|
|
920
|
-
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
|
|
921
|
-
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"
|
|
922
|
-
stroke-linecap="round" stroke-linejoin="round">
|
|
923
|
-
<circle cx="12" cy="12" r="4.2"/>
|
|
924
|
-
<path d="M12 2.4v2.2M12 19.4v2.2M4.2 12H2M22 12h-2.2M6.3 6.3 4.8 4.8
|
|
925
|
-
M19.2 19.2l-1.5-1.5M17.7 6.3l1.5-1.5M4.8 19.2l1.5-1.5"/></svg>
|
|
926
|
-
</button>
|
|
927
|
-
<button class="iconbtn" id="barFold" type="button" aria-label="收起/展开工具栏" title="收起/展开工具栏(B)">
|
|
928
|
-
<svg class="icon-fold" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"
|
|
929
|
-
stroke-linecap="round" stroke-linejoin="round">
|
|
930
|
-
<path d="M18 15l-6-6-6 6"/></svg>
|
|
931
|
-
<svg class="icon-unfold" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"
|
|
932
|
-
stroke-linecap="round" stroke-linejoin="round">
|
|
933
|
-
<path d="M6 9l6 6 6-6"/></svg>
|
|
934
|
-
</button>
|
|
935
|
-
</div>
|
|
936
|
-
</div>
|
|
937
|
-
</header>
|
|
938
|
-
|
|
939
|
-
<!-- __TOPPPT_CONTENT_START__ -->
|
|
940
|
-
<!-- ══ Hero(16:9 满屏页) ══ -->
|
|
941
|
-
<section class="band band--fit">
|
|
942
|
-
<div class="wrap">
|
|
943
|
-
<div class="grid g-hero" style="gap:clamp(32px,4vw,72px)">
|
|
944
|
-
<div class="stack gap-5 rv">
|
|
945
|
-
<div class="row row-wrap">
|
|
946
|
-
<span class="chip chip--accent">2026 · 演示汇报</span>
|
|
947
|
-
<span class="chip chip--line">年度研究</span>
|
|
948
|
-
</div>
|
|
949
|
-
<h1 class="t-display">AI 智能体<br>从工具到同事</h1>
|
|
950
|
-
<p class="t-lead" style="max-width:620px">2026 年,软件不再只是被使用,而是开始自主完成工作。</p>
|
|
951
|
-
<div class="row row-wrap" style="gap:var(--sp-3)">
|
|
952
|
-
<a class="btn btn--primary" href="#agenda">开始阅读</a>
|
|
953
|
-
<a class="btn btn--ghost" href="#refs">参考资料</a>
|
|
954
|
-
</div>
|
|
955
|
-
<div class="row row-wrap" style="gap:var(--sp-6);margin-top:var(--sp-3)">
|
|
956
|
-
<div>
|
|
957
|
-
<div class="t-metric" style="color:var(--accent)">61%</div>
|
|
958
|
-
<div class="t-xs" style="margin-top:4px">企业试点率<a class="cite" href="#ref-1">[1]</a></div>
|
|
959
|
-
</div>
|
|
960
|
-
<div>
|
|
961
|
-
<div class="t-metric">18<span style="font-size:.45em"> %</span></div>
|
|
962
|
-
<div class="t-xs" style="margin-top:4px">规模化运行占比<a class="cite" href="#ref-2">[2]</a></div>
|
|
963
|
-
</div>
|
|
964
|
-
</div>
|
|
965
|
-
</div>
|
|
966
|
-
<div class="g-hero__visual rv">
|
|
967
|
-
<div class="fig">
|
|
968
|
-
<div class="fig__cap">2025 企业智能体试点率</div>
|
|
969
|
-
<svg class="chart" data-chart="donut" viewBox="0 0 140 140" style="width:180px;margin-inline:auto">
|
|
970
|
-
<circle cx="70" cy="70" r="54" class="f-none s-bds" stroke-width="15"/>
|
|
971
|
-
<circle data-sweep data-circ="339.3" data-p="0.61" cx="70" cy="70" r="54" class="f-none s-acc"
|
|
972
|
-
stroke-width="15" stroke-dasharray="207 132.3" stroke-linecap="round"
|
|
973
|
-
transform="rotate(-90 70 70)"><title>试点率 61%</title></circle>
|
|
974
|
-
<text x="70" y="68" text-anchor="middle" class="f-txt" font-size="26" font-weight="600"
|
|
975
|
-
data-count="61" data-suffix="%">61%</text>
|
|
976
|
-
<text x="70" y="88" text-anchor="middle" class="f-txt3" font-size="11">已进入试点</text>
|
|
977
|
-
</svg>
|
|
978
|
-
<div class="row" style="justify-content:center;gap:var(--sp-4);margin-top:var(--sp-4)">
|
|
979
|
-
<span class="chip chip--accent">试点及以上 61%</span>
|
|
980
|
-
<span class="chip">观望 39%</span>
|
|
981
|
-
</div>
|
|
982
|
-
</div>
|
|
983
|
-
</div>
|
|
984
|
-
</div>
|
|
985
|
-
</div>
|
|
986
|
-
</section>
|
|
987
|
-
|
|
988
|
-
<!-- ══ Agenda 大纲页(第二页) ══ -->
|
|
989
|
-
<section class="band band--tint" id="agenda">
|
|
990
|
-
<div class="wrap">
|
|
991
|
-
<div class="shead rv">
|
|
992
|
-
<div class="t-eyebrow">Agenda</div>
|
|
993
|
-
<h2 class="t-h1 shead__title">报告大纲</h2>
|
|
994
|
-
<p class="t-lead shead__desc">三章加参考资料,从范式到落地。点击任意条目跳转。</p>
|
|
995
|
-
</div>
|
|
996
|
-
<ol class="agenda rv">
|
|
997
|
-
<li class="agenda__i"><a class="agenda__a" href="#s1">
|
|
998
|
-
<span class="agenda__n">01</span>
|
|
999
|
-
<span><span class="agenda__t">范式跃迁</span>
|
|
1000
|
-
<div class="agenda__d">从 Chatbot 到 Autonomous Agent</div></span>
|
|
1001
|
-
<span class="agenda__go"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
|
1002
|
-
</a></li>
|
|
1003
|
-
<li class="agenda__i"><a class="agenda__a" href="#s2">
|
|
1004
|
-
<span class="agenda__n">02</span>
|
|
1005
|
-
<span><span class="agenda__t">形态与边界</span>
|
|
1006
|
-
<div class="agenda__d">三类形态与能力边界</div></span>
|
|
1007
|
-
<span class="agenda__go"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
|
1008
|
-
</a></li>
|
|
1009
|
-
<li class="agenda__i"><a class="agenda__a" href="#s3">
|
|
1010
|
-
<span class="agenda__n">03</span>
|
|
1011
|
-
<span><span class="agenda__t">落地路径</span>
|
|
1012
|
-
<div class="agenda__d">采纳率四季度爬升</div></span>
|
|
1013
|
-
<span class="agenda__go"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
|
1014
|
-
</a></li>
|
|
1015
|
-
<li class="agenda__i"><a class="agenda__a" href="#refs">
|
|
1016
|
-
<span class="agenda__n">04</span>
|
|
1017
|
-
<span><span class="agenda__t">参考资料</span>
|
|
1018
|
-
<div class="agenda__d">数据来源与口径说明</div></span>
|
|
1019
|
-
<span class="agenda__go"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
|
1020
|
-
</a></li>
|
|
1021
|
-
</ol>
|
|
1022
|
-
</div>
|
|
1023
|
-
</section>
|
|
1024
|
-
|
|
1025
|
-
<!-- ══ 章节一(要点卡) ══ -->
|
|
1026
|
-
<section class="band" id="s1">
|
|
1027
|
-
<div class="wrap">
|
|
1028
|
-
<div class="shead rv">
|
|
1029
|
-
<div class="t-eyebrow">01 · 范式跃迁</div>
|
|
1030
|
-
<h2 class="t-h1 shead__title">范式跃迁:智能体的本质是目标驱动的自主执行</h2>
|
|
1031
|
-
<p class="t-lead shead__desc">范式为什么现在发生:成本、能力、生态三线交汇。</p>
|
|
1032
|
-
</div>
|
|
1033
|
-
<div class="grid g-3 rv">
|
|
1034
|
-
<div class="card">
|
|
1035
|
-
<div class="card__hd">
|
|
1036
|
-
<div class="card__ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M9 9h6v6H9z"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M1 9h3M1 15h3M20 9h3M20 15h3"/></svg></div>
|
|
1037
|
-
<h3 class="t-h3">从工具到同事</h3>
|
|
1038
|
-
</div>
|
|
1039
|
-
<ul class="ul">
|
|
1040
|
-
<li><strong>执行者换位。</strong>人从执行者变成目标设定者与审核者。</li>
|
|
1041
|
-
<li><strong>闭环标志。</strong>给目标即可闭环,过程自主。</li>
|
|
1042
|
-
</ul>
|
|
1043
|
-
</div>
|
|
1044
|
-
<div class="card">
|
|
1045
|
-
<div class="card__hd">
|
|
1046
|
-
<div class="card__ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M22 7l-8.5 8.5-5-5L2 17"/><path d="M16 7h6v6"/></svg></div>
|
|
1047
|
-
<h3 class="t-h3">试点率过六成</h3>
|
|
1048
|
-
</div>
|
|
1049
|
-
<ul class="ul">
|
|
1050
|
-
<li><strong>2025 基线。</strong>61% 企业已进入试点<a class="cite" href="#ref-1">[1]</a>。</li>
|
|
1051
|
-
<li><strong>规模化不足两成。</strong>分水岭在治理与集成<a class="cite" href="#ref-2">[2]</a>。</li>
|
|
1052
|
-
</ul>
|
|
1053
|
-
</div>
|
|
1054
|
-
<div class="card">
|
|
1055
|
-
<div class="card__hd">
|
|
1056
|
-
<div class="card__ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/></svg></div>
|
|
1057
|
-
<h3 class="t-h3">护栏先行</h3>
|
|
1058
|
-
</div>
|
|
1059
|
-
<ul class="ul">
|
|
1060
|
-
<li><strong>权限与审计。</strong>智能体操作全程留痕。</li>
|
|
1061
|
-
<li><strong>回退机制。</strong>高风险动作必须有人工确认。</li>
|
|
1062
|
-
</ul>
|
|
1063
|
-
</div>
|
|
1064
|
-
</div>
|
|
1065
|
-
</div>
|
|
1066
|
-
</section>
|
|
1067
|
-
|
|
1068
|
-
<!-- ══ 章节二(表格) ══ -->
|
|
1069
|
-
<section class="band" id="s2">
|
|
1070
|
-
<div class="wrap">
|
|
1071
|
-
<div class="shead rv">
|
|
1072
|
-
<div class="t-eyebrow">02 · 形态与边界</div>
|
|
1073
|
-
<h2 class="t-h1 shead__title">三类形态能力边界分明,同事级智能体尚在早期</h2>
|
|
1074
|
-
</div>
|
|
1075
|
-
<div class="tbl-wrap rv">
|
|
1076
|
-
<table>
|
|
1077
|
-
<thead><tr><th style="width:20%">能力</th><th style="width:40%">2026 现状</th><th>跃迁目标</th></tr></thead>
|
|
1078
|
-
<tbody>
|
|
1079
|
-
<tr><td class="k">任务自主性</td><td>单点工具为主,人逐步驱动</td>
|
|
1080
|
-
<td><strong>目标驱动,端到端</strong><a class="cite" href="#ref-1">[1]</a></td></tr>
|
|
1081
|
-
<tr><td class="k">工具生态</td><td>连接数少、缺乏统一编排</td>
|
|
1082
|
-
<td><strong>统一编排,千级工具</strong></td></tr>
|
|
1083
|
-
<tr><td class="k">记忆与规划</td><td>会话级记忆,无长期规划</td>
|
|
1084
|
-
<td><strong>任务级记忆,多步规划</strong></td></tr>
|
|
1085
|
-
<tr><td class="k">安全治理</td><td>事后审计、无回退</td>
|
|
1086
|
-
<td><strong>护栏内运行,全程可回退</strong><a class="cite" href="#ref-2">[2]</a></td></tr>
|
|
1087
|
-
</tbody>
|
|
1088
|
-
</table>
|
|
1089
|
-
</div>
|
|
1090
|
-
</div>
|
|
1091
|
-
</section>
|
|
1092
|
-
|
|
1093
|
-
<!-- ══ 章节三(图表 + 解读) ══ -->
|
|
1094
|
-
<section class="band" id="s3">
|
|
1095
|
-
<div class="wrap">
|
|
1096
|
-
<div class="shead rv">
|
|
1097
|
-
<div class="t-eyebrow">03 · 落地路径</div>
|
|
1098
|
-
<h2 class="t-h1 shead__title">企业智能体采纳率:四个季度四步走至 60%</h2>
|
|
1099
|
-
<p class="t-lead shead__desc">采纳率曲线的三个拐点分别由什么驱动。</p>
|
|
1100
|
-
</div>
|
|
1101
|
-
<div class="grid g-side rv" style="align-items:center">
|
|
1102
|
-
<div class="fig">
|
|
1103
|
-
<div class="fig__cap">企业智能体采纳率(试点及以上,%)</div>
|
|
1104
|
-
<svg class="chart" data-chart="bar" viewBox="0 0 560 220">
|
|
1105
|
-
<line x1="46" y1="170" x2="540" y2="170" class="s-bds" stroke-width="1"/>
|
|
1106
|
-
<rect data-anim="grow" x="60" y="131" width="58" height="39" rx="6" class="f-s3"/>
|
|
1107
|
-
<rect data-anim="grow" x="188" y="92" width="58" height="78" rx="6" class="f-s3"/>
|
|
1108
|
-
<rect data-anim="grow" x="316" y="60" width="58" height="110" rx="6" class="f-acc"/>
|
|
1109
|
-
<rect data-anim="grow" x="444" y="53" width="58" height="117" rx="6" class="f-acc"/>
|
|
1110
|
-
<text data-anim="fade" x="89" y="192" text-anchor="middle" class="f-txt3" font-size="11">2026Q3</text>
|
|
1111
|
-
<text data-anim="fade" x="217" y="192" text-anchor="middle" class="f-txt3" font-size="11">Q4</text>
|
|
1112
|
-
<text data-anim="fade" x="345" y="192" text-anchor="middle" class="f-txt3" font-size="11">2027Q1</text>
|
|
1113
|
-
<text data-anim="fade" x="473" y="192" text-anchor="middle" class="f-txt3" font-size="11">Q2</text>
|
|
1114
|
-
</svg>
|
|
1115
|
-
</div>
|
|
1116
|
-
<div class="stack gap-4">
|
|
1117
|
-
<h3 class="t-h3">怎么读这张图</h3>
|
|
1118
|
-
<ul class="ul">
|
|
1119
|
-
<li><strong>试点先行。</strong>六成企业已越过 PoC<a class="cite" href="#ref-1">[1]</a>。</li>
|
|
1120
|
-
<li><strong>规模化受阻。</strong>瓶颈在治理与集成,不在模型<a class="cite" href="#ref-2">[2]</a>。</li>
|
|
1121
|
-
</ul>
|
|
1122
|
-
</div>
|
|
1123
|
-
</div>
|
|
1124
|
-
</div>
|
|
1125
|
-
</section>
|
|
1126
|
-
|
|
1127
|
-
<!-- ══ 章节四(对比页 comparison) ══ -->
|
|
1128
|
-
<section class="band" id="s4">
|
|
1129
|
-
<div class="wrap">
|
|
1130
|
-
<div class="shead rv">
|
|
1131
|
-
<div class="t-eyebrow">03 · 落地路径</div>
|
|
1132
|
-
<h2 class="t-h1 shead__title">从试点到规模化:两条路径的分野</h2>
|
|
1133
|
-
</div>
|
|
1134
|
-
<div class="grid g-half rv">
|
|
1135
|
-
<div class="card">
|
|
1136
|
-
<h3 class="t-h3">堆场景路径 · 现状</h3>
|
|
1137
|
-
<ul class="ul">
|
|
1138
|
-
<li><strong>接入慢。</strong>每场景重复对接工具与权限。</li>
|
|
1139
|
-
<li><strong>治理弱。</strong>事后审计,回退靠人工。</li>
|
|
1140
|
-
<li><strong>易回退。</strong>23 家中 19 家半年内退回试点<a class="cite" href="#ref-2">[2]</a>。</li>
|
|
1141
|
-
</ul>
|
|
1142
|
-
</div>
|
|
1143
|
-
<div class="card" style="background:var(--accent-soft);border-color:transparent">
|
|
1144
|
-
<h3 class="t-h3" style="color:var(--accent)">平台化路径 · 目标</h3>
|
|
1145
|
-
<ul class="ul">
|
|
1146
|
-
<li><strong>接入快。</strong>统一编排,接入成本降一个量级。</li>
|
|
1147
|
-
<li><strong>治理内嵌。</strong>权限、审计、回退进平台层。</li>
|
|
1148
|
-
<li><strong>可放量。</strong>先行 9 家全部穿越分水岭<a class="cite" href="#ref-2">[2]</a>。</li>
|
|
1149
|
-
</ul>
|
|
1150
|
-
</div>
|
|
1151
|
-
</div>
|
|
1152
|
-
<div class="sowhat rv">
|
|
1153
|
-
<span class="sowhat__k">结论</span>
|
|
1154
|
-
<span class="sowhat__v">平台化不是可选项,而是规模化穿越分水岭的唯一共同路径。</span>
|
|
1155
|
-
</div>
|
|
1156
|
-
</div>
|
|
1157
|
-
</section>
|
|
1158
|
-
|
|
1159
|
-
<!-- ══ 章节五(大数指标页 kpi) ══ -->
|
|
1160
|
-
<section class="band" id="s5">
|
|
1161
|
-
<div class="wrap">
|
|
1162
|
-
<div class="shead rv">
|
|
1163
|
-
<div class="t-eyebrow">04 · 落地路径</div>
|
|
1164
|
-
<h2 class="t-h1 shead__title">六成企业已越过 PoC,规模化不足两成</h2>
|
|
1165
|
-
</div>
|
|
1166
|
-
<div class="grid g-side rv" style="align-items:center">
|
|
1167
|
-
<div class="stack gap-3">
|
|
1168
|
-
<div class="t-metric" style="color:var(--accent);font-size:clamp(56px,7vh,76px)">61%</div>
|
|
1169
|
-
<div class="t-h3">2025 企业智能体试点率</div>
|
|
1170
|
-
<div class="t-sm" style="color:var(--accent);font-weight:600">▲ +6pt vs 2024</div>
|
|
1171
|
-
</div>
|
|
1172
|
-
<div class="grid g-3">
|
|
1173
|
-
<div class="metric">
|
|
1174
|
-
<div class="metric__v t-metric">18<small>%</small></div>
|
|
1175
|
-
<div class="metric__k">规模化运行占比</div>
|
|
1176
|
-
<div class="metric__n">3 个以上场景稳定运行<a class="cite" href="#ref-2">[2]</a></div>
|
|
1177
|
-
</div>
|
|
1178
|
-
<div class="metric">
|
|
1179
|
-
<div class="metric__v t-metric">9<small> 家</small></div>
|
|
1180
|
-
<div class="metric__k">规模化成功企业</div>
|
|
1181
|
-
<div class="metric__n">全部先建平台层</div>
|
|
1182
|
-
</div>
|
|
1183
|
-
<div class="metric">
|
|
1184
|
-
<div class="metric__v t-metric">60<small>%</small></div>
|
|
1185
|
-
<div class="metric__k">2027Q2 采纳率目标</div>
|
|
1186
|
-
<div class="metric__n">四个季度四步走</div>
|
|
1187
|
-
</div>
|
|
1188
|
-
</div>
|
|
1189
|
-
</div>
|
|
1190
|
-
</div>
|
|
1191
|
-
</section>
|
|
1192
|
-
|
|
1193
|
-
<!-- ══ 章节六(环形图页 donut) ══ -->
|
|
1194
|
-
<section class="band" id="s6">
|
|
1195
|
-
<div class="wrap">
|
|
1196
|
-
<div class="shead rv">
|
|
1197
|
-
<div class="t-eyebrow">04 · 落地路径</div>
|
|
1198
|
-
<h2 class="t-h1 shead__title">试点企业分布:过六成跨越 PoC</h2>
|
|
1199
|
-
</div>
|
|
1200
|
-
<div class="grid g-side rv" style="align-items:center">
|
|
1201
|
-
<div class="fig">
|
|
1202
|
-
<div class="fig__cap">2025 企业智能体成熟度分布(%)</div>
|
|
1203
|
-
<svg class="chart" data-chart="donut" viewBox="0 0 150 150" style="width:200px;margin-inline:auto">
|
|
1204
|
-
<circle cx="75" cy="75" r="54" class="f-none s-bds" stroke-width="26"/>
|
|
1205
|
-
<circle cx="75" cy="75" r="54" class="f-acc" stroke-width="26"
|
|
1206
|
-
stroke-dasharray="61.1 278.2" transform="rotate(-90 75 75)"><title>已规模化 18%</title></circle>
|
|
1207
|
-
<circle cx="75" cy="75" r="54" class="f-accs" stroke-width="26"
|
|
1208
|
-
stroke-dasharray="145.9 193.4" transform="rotate(-25.2 75 75)"><title>试点中 43%</title></circle>
|
|
1209
|
-
<circle cx="75" cy="75" r="54" class="f-s2" stroke-width="26"
|
|
1210
|
-
stroke-dasharray="132.3 207" transform="rotate(129.6 75 75)"><title>观望 39%</title></circle>
|
|
1211
|
-
<text x="75" y="72" text-anchor="middle" class="f-txt" font-size="22" font-weight="600">100%</text>
|
|
1212
|
-
<text x="75" y="92" text-anchor="middle" class="f-txt3" font-size="10">企业占比</text>
|
|
1213
|
-
</svg>
|
|
1214
|
-
</div>
|
|
1215
|
-
<div class="stack gap-3">
|
|
1216
|
-
<div class="row" style="gap:var(--sp-4)">
|
|
1217
|
-
<span class="chip chip--accent">已规模化 18%</span>
|
|
1218
|
-
<span class="chip">试点中 43%</span>
|
|
1219
|
-
<span class="chip chip--line">观望 39%</span>
|
|
1220
|
-
</div>
|
|
1221
|
-
<ul class="ul">
|
|
1222
|
-
<li><strong>规模化仍是少数。</strong>18% 企业进入 3 场景以上稳定运行<a class="cite" href="#ref-2">[2]</a>。</li>
|
|
1223
|
-
<li><strong>观望盘最大。</strong>工具链与治理成熟后向试点迁移。</li>
|
|
1224
|
-
</ul>
|
|
1225
|
-
</div>
|
|
1226
|
-
</div>
|
|
1227
|
-
<div class="t-xs" style="margin-top:var(--sp-4);color:var(--text-3)">口径:试点及以上 = 61%;N=42 深度访谈 + 行业抽样<a class="cite" href="#ref-1">[1]</a></div>
|
|
1228
|
-
</div>
|
|
1229
|
-
</section>
|
|
1230
|
-
|
|
1231
|
-
<!-- ══ 章节七(步骤条页 steps) ══ -->
|
|
1232
|
-
<section class="band" id="s7">
|
|
1233
|
-
<div class="wrap">
|
|
1234
|
-
<div class="shead rv">
|
|
1235
|
-
<div class="t-eyebrow">05 · 落地节奏</div>
|
|
1236
|
-
<h2 class="t-h1 shead__title">四步走:从单场景试点到平台化放量</h2>
|
|
1237
|
-
</div>
|
|
1238
|
-
<div class="steps rv">
|
|
1239
|
-
<div class="step">
|
|
1240
|
-
<div class="step__n">01</div>
|
|
1241
|
-
<div class="step__t">选场景</div>
|
|
1242
|
-
<div class="step__d">客服 / 研发 / 数据三类高价值场景先跑通</div>
|
|
1243
|
-
</div>
|
|
1244
|
-
<div class="step">
|
|
1245
|
-
<div class="step__n">02</div>
|
|
1246
|
-
<div class="step__t">建平台</div>
|
|
1247
|
-
<div class="step__d">三层解耦 + 统一编排,接入成本降一个量级</div>
|
|
1248
|
-
</div>
|
|
1249
|
-
<div class="step step--a">
|
|
1250
|
-
<div class="step__n">03</div>
|
|
1251
|
-
<div class="step__t">立治理</div>
|
|
1252
|
-
<div class="step__d">权限、审计、回退进平台层,高风险动作人工确认</div>
|
|
1253
|
-
</div>
|
|
1254
|
-
<div class="step">
|
|
1255
|
-
<div class="step__n">04</div>
|
|
1256
|
-
<div class="step__t">放量复制</div>
|
|
1257
|
-
<div class="step__d">同一套护栏复制到新场景,无需重建</div>
|
|
1258
|
-
</div>
|
|
1259
|
-
</div>
|
|
1260
|
-
<div class="t-xs" style="margin-top:var(--sp-5);color:var(--text-3)">口径:先行 9 家规模化企业的共同路径<a class="cite" href="#ref-2">[2]</a></div>
|
|
1261
|
-
</div>
|
|
1262
|
-
</section>
|
|
1263
|
-
|
|
1264
|
-
<!-- ══ 章节八(达成对比页 bullet) ══ -->
|
|
1265
|
-
<section class="band" id="s8">
|
|
1266
|
-
<div class="wrap">
|
|
1267
|
-
<div class="shead rv">
|
|
1268
|
-
<div class="t-eyebrow">06 · 目标对照</div>
|
|
1269
|
-
<h2 class="t-h1 shead__title">四项能力对照目标:治理与集成缺口最大</h2>
|
|
1270
|
-
</div>
|
|
1271
|
-
<div class="bul rv" style="--bul-label:minmax(0,2.4fr)">
|
|
1272
|
-
<div class="bul__row">
|
|
1273
|
-
<div class="bul__k">场景覆盖</div>
|
|
1274
|
-
<div class="bul__track"><div class="bul__fill" style="width:61%"></div>
|
|
1275
|
-
<div class="bul__tgt" style="left:80%"></div></div>
|
|
1276
|
-
<div class="bul__v"><b>61%</b> / 80%</div>
|
|
1277
|
-
</div>
|
|
1278
|
-
<div class="bul__row bul__row--warn">
|
|
1279
|
-
<div class="bul__k">治理就绪</div>
|
|
1280
|
-
<div class="bul__track"><div class="bul__fill" style="width:34%"></div>
|
|
1281
|
-
<div class="bul__tgt" style="left:75%"></div></div>
|
|
1282
|
-
<div class="bul__v"><b>34%</b> / 75%</div>
|
|
1283
|
-
</div>
|
|
1284
|
-
<div class="bul__row bul__row--warn">
|
|
1285
|
-
<div class="bul__k">工具集成</div>
|
|
1286
|
-
<div class="bul__track"><div class="bul__fill" style="width:42%"></div>
|
|
1287
|
-
<div class="bul__tgt" style="left:70%"></div></div>
|
|
1288
|
-
<div class="bul__v"><b>42%</b> / 70%</div>
|
|
1289
|
-
</div>
|
|
1290
|
-
<div class="bul__row">
|
|
1291
|
-
<div class="bul__k">规模化运行</div>
|
|
1292
|
-
<div class="bul__track"><div class="bul__fill" style="width:18%"></div>
|
|
1293
|
-
<div class="bul__tgt" style="left:60%"></div></div>
|
|
1294
|
-
<div class="bul__v"><b>18%</b> / 60%</div>
|
|
1295
|
-
</div>
|
|
1296
|
-
</div>
|
|
1297
|
-
<div class="t-xs" style="margin-top:var(--sp-5);color:var(--text-3)">口径:能力就绪度 = 自评「已具备且稳定运行」的企业占比;目标取 2027Q2<a class="cite" href="#ref-2">[2]</a></div>
|
|
1298
|
-
</div>
|
|
1299
|
-
</section>
|
|
1300
|
-
|
|
1301
|
-
<!-- ══ 章节八b(卡片网格 cards · 路标图标) ══ -->
|
|
1302
|
-
<section class="band" id="s-cards">
|
|
1303
|
-
<div class="wrap">
|
|
1304
|
-
<div class="shead rv">
|
|
1305
|
-
<div class="t-eyebrow">06 · 能力地图</div>
|
|
1306
|
-
<h2 class="t-h1 shead__title">平台化三件套:编排、记忆、治理缺一不可</h2>
|
|
1307
|
-
</div>
|
|
1308
|
-
<div class="grid g-3 rv">
|
|
1309
|
-
<div class="card">
|
|
1310
|
-
<div class="card__hd">
|
|
1311
|
-
<div class="card__ico">01</div>
|
|
1312
|
-
<h3 class="t-h3">统一编排</h3>
|
|
1313
|
-
</div>
|
|
1314
|
-
<ul class="ul">
|
|
1315
|
-
<li><strong>工具连接。</strong>千级工具统一注册与调用。</li>
|
|
1316
|
-
<li><strong>任务拆解。</strong>多步规划与失败重试。</li>
|
|
1317
|
-
</ul>
|
|
1318
|
-
</div>
|
|
1319
|
-
<div class="card">
|
|
1320
|
-
<div class="card__hd">
|
|
1321
|
-
<div class="card__ico">02</div>
|
|
1322
|
-
<h3 class="t-h3">长期记忆</h3>
|
|
1323
|
-
</div>
|
|
1324
|
-
<ul class="ul">
|
|
1325
|
-
<li><strong>任务记忆。</strong>跨会话保留目标与进度。</li>
|
|
1326
|
-
<li><strong>偏好学习。</strong>组织口径与风格沉淀。</li>
|
|
1327
|
-
</ul>
|
|
1328
|
-
</div>
|
|
1329
|
-
<div class="card">
|
|
1330
|
-
<div class="card__hd">
|
|
1331
|
-
<div class="card__ico">03</div>
|
|
1332
|
-
<h3 class="t-h3">护栏治理</h3>
|
|
1333
|
-
</div>
|
|
1334
|
-
<ul class="ul">
|
|
1335
|
-
<li><strong>权限分级。</strong>高风险动作人工确认。</li>
|
|
1336
|
-
<li><strong>全程可回退。</strong>审计与回滚内嵌平台层。</li>
|
|
1337
|
-
</ul>
|
|
1338
|
-
</div>
|
|
1339
|
-
</div>
|
|
1340
|
-
<div class="t-xs" style="margin-top:var(--sp-4);color:var(--text-3)">口径:先行 9 家规模化企业的共同能力底座<a class="cite" href="#ref-2">[2]</a></div>
|
|
1341
|
-
</div>
|
|
1342
|
-
</section>
|
|
1343
|
-
|
|
1344
|
-
<!-- ══ 素材图片页(image 页型 · full 版心全宽 3:1 + 配图占位) ══ -->
|
|
1345
|
-
<section class="band" id="s-photo">
|
|
1346
|
-
<div class="wrap">
|
|
1347
|
-
<div class="shead rv">
|
|
1348
|
-
<div class="t-eyebrow">07 · 场景实拍</div>
|
|
1349
|
-
<h2 class="t-h1 shead__title">客服智能体工作台:会话、工具调用与人工接管同屏</h2>
|
|
1350
|
-
</div>
|
|
1351
|
-
<figure class="media media--r3-1 media--ph rv">
|
|
1352
|
-
<div class="media__ph">
|
|
1353
|
-
<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="9" cy="10" r="1.6"/><path d="m3 18 5-5 4 3.4 3-2.6 6 5.2"/></svg>
|
|
1354
|
-
<b>配图占位</b>
|
|
1355
|
-
<span>建议 2400×800px · 3:1 · 替换 image.src 即可</span>
|
|
1356
|
-
</div>
|
|
1357
|
-
</figure>
|
|
1358
|
-
<p class="media__src">图 1:客服智能体工作台(占位示例;交付前替换为真实截图,版式与比例已锁定)<a class="cite" href="#ref-2">[2]</a></p>
|
|
1359
|
-
</div>
|
|
1360
|
-
</section>
|
|
1361
|
-
|
|
1362
|
-
<!-- ══ 金句页(quote 页型 · 节奏休止 · 全文 1–2 处) ══ -->
|
|
1363
|
-
<section class="band band--accent band--fit" id="quote-1">
|
|
1364
|
-
<div class="wrap">
|
|
1365
|
-
<div class="shead shead--center rv">
|
|
1366
|
-
<div class="t-eyebrow">03 · 关键判断</div>
|
|
1367
|
-
<h2 class="t-h1 shead__title">规模化瓶颈不在模型,而在治理与集成</h2>
|
|
1368
|
-
<div style="font-size:clamp(40px,4.6vh,56px);font-weight:700;color:var(--accent);line-height:1;margin-top:var(--sp-4)">「</div>
|
|
1369
|
-
<p class="t-lead" style="max-width:860px;margin-inline:auto;color:var(--text);font-size:clamp(18px,2.2vh,22px);font-weight:600">穿越分水岭的钥匙是平台化,不是更大的模型。</p>
|
|
1370
|
-
<p class="t-sm" style="margin-top:var(--sp-4);color:var(--accent-text)">—— 企业智能体发展调研 · 42 家深度访谈</p>
|
|
1371
|
-
</div>
|
|
1372
|
-
</div>
|
|
1373
|
-
</section>
|
|
1374
|
-
|
|
1375
|
-
<!-- ══ 深色收尾章 ══ -->
|
|
1376
|
-
<section class="band band--accent band--fit" id="next">
|
|
1377
|
-
<div class="wrap">
|
|
1378
|
-
<div class="shead shead--center rv">
|
|
1379
|
-
<div class="t-eyebrow">下一步</div>
|
|
1380
|
-
<h2 class="t-h1 shead__title">让智能体真正干活起来</h2>
|
|
1381
|
-
<p class="t-lead shead__desc">三步走的行动清单。</p>
|
|
1382
|
-
</div>
|
|
1383
|
-
<div class="grid g-3 rv">
|
|
1384
|
-
<div class="card">
|
|
1385
|
-
<h3 class="t-h3" style="margin-bottom:var(--sp-2)">选场景</h3>
|
|
1386
|
-
<p class="t-body">
|
|
1387
|
-
客服 / 研发 / 数据三类高价值场景。</p>
|
|
1388
|
-
</div>
|
|
1389
|
-
<div class="card">
|
|
1390
|
-
<h3 class="t-h3" style="margin-bottom:var(--sp-2)">建平台</h3>
|
|
1391
|
-
<p class="t-body">
|
|
1392
|
-
三层解耦 + 统一编排 + 护栏。</p>
|
|
1393
|
-
</div>
|
|
1394
|
-
<div class="card">
|
|
1395
|
-
<h3 class="t-h3" style="margin-bottom:var(--sp-2)">立治理</h3>
|
|
1396
|
-
<p class="t-body">
|
|
1397
|
-
权限、审计、回退全闭环。</p>
|
|
1398
|
-
</div>
|
|
1399
|
-
</div>
|
|
1400
|
-
<div class="row" style="justify-content:center;gap:var(--sp-3);
|
|
1401
|
-
margin-top:clamp(28px,3.4vh,44px);flex-wrap:wrap">
|
|
1402
|
-
<a class="btn btn--primary" href="#s1">返回查看</a>
|
|
1403
|
-
<a class="btn btn--ghost" href="#refs"
|
|
1404
|
-
style="border-color:color-mix(in srgb,var(--text-inv) 30%,transparent)">
|
|
1405
|
-
参考资料</a>
|
|
1406
|
-
</div>
|
|
1407
|
-
</div>
|
|
1408
|
-
</section>
|
|
1409
|
-
|
|
1410
|
-
<!-- ══ 参考资料 ══ -->
|
|
1411
|
-
<section class="band band--tint" id="refs">
|
|
1412
|
-
<div class="wrap">
|
|
1413
|
-
<div class="shead rv">
|
|
1414
|
-
<div class="t-eyebrow">参考资料</div>
|
|
1415
|
-
<h2 class="t-h1 shead__title">数据来源与口径说明</h2>
|
|
1416
|
-
</div>
|
|
1417
|
-
<div class="tbl-wrap rv">
|
|
1418
|
-
<table>
|
|
1419
|
-
<thead><tr><th style="width:6%">编号</th><th style="width:26%">来源</th>
|
|
1420
|
-
<th style="width:14%">时间</th><th>口径说明</th></tr></thead>
|
|
1421
|
-
<tbody>
|
|
1422
|
-
<tr id="ref-1"><td class="k">[1]</td>
|
|
1423
|
-
<td><a class="ref-link" href="https://example.com/gartner-agentic-ai" target="_blank" rel="noopener">Gartner:Agentic AI 技术成熟度曲线
|
|
1424
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><path d="M15 3h6v6M10 14 21 3"/></svg></a></td>
|
|
1425
|
-
<td>2026-06</td><td>试点率 / 采纳率 / 形态成熟度判定</td></tr>
|
|
1426
|
-
<tr id="ref-2"><td class="k">[2]</td><td>企业智能体发展调研(内部,N=42)</td>
|
|
1427
|
-
<td>2026-08</td><td>规模化占比与访谈结论,2026-07 至 2026-08</td></tr>
|
|
1428
|
-
</tbody>
|
|
1429
|
-
</table>
|
|
1430
|
-
</div>
|
|
1431
|
-
</div>
|
|
1432
|
-
</section>
|
|
1433
|
-
|
|
1434
|
-
<!-- ══ 页脚 ══ -->
|
|
1435
|
-
<footer class="foot">
|
|
1436
|
-
<div class="wrap foot__in">
|
|
1437
|
-
<div class="brand">
|
|
1438
|
-
<div class="brand__mark">AI</div>
|
|
1439
|
-
<div class="brand__txt">
|
|
1440
|
-
<div class="brand__title">AI 智能体发展研究</div>
|
|
1441
|
-
<div class="brand__sub">2026 · 年度报告</div>
|
|
1442
|
-
</div>
|
|
1443
|
-
</div>
|
|
1444
|
-
<div class="t-xs">内部资料 · 请勿外传</div>
|
|
1445
|
-
</div>
|
|
1446
|
-
</footer>
|
|
1447
|
-
<!-- __TOPPPT_CONTENT_END__ -->
|
|
1448
|
-
|
|
1449
|
-
<!-- ══ 页码指示(翻页导航) ══ -->
|
|
1450
|
-
<aside class="pager" id="pager" aria-label="页码导航">
|
|
1451
|
-
<div class="pager__count" id="pagerCount">1 / 1</div>
|
|
1452
|
-
<div class="pager__dots" id="pagerDots"></div>
|
|
1453
|
-
</aside>
|
|
1454
|
-
|
|
1455
|
-
<!-- ══ PPTX 预览模态(与智能体精导同一引擎,所见即所得 + 可复制 AI 提示词) ══ -->
|
|
1456
|
-
<div class="pmodal" id="pptModal" role="dialog" aria-modal="true" aria-label="PPTX 预览">
|
|
1457
|
-
<div class="pmodal__panel">
|
|
1458
|
-
<div class="pmodal__hd">
|
|
1459
|
-
<div>
|
|
1460
|
-
<div class="pmodal__t">PPTX 预览</div>
|
|
1461
|
-
<div class="pmodal__meta" id="pptMeta">—</div>
|
|
1462
|
-
</div>
|
|
1463
|
-
<button class="iconbtn" id="pptClose" type="button" aria-label="关闭预览" title="关闭(Esc)">
|
|
1464
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
1465
|
-
stroke-linecap="round" stroke-linejoin="round">
|
|
1466
|
-
<path d="M18 6 6 18M6 6l12 12"/></svg>
|
|
1467
|
-
</button>
|
|
1468
|
-
</div>
|
|
1469
|
-
<div class="pmodal__body" id="pptBody"><!-- 页序列缩略(JS 渲染,同导出引擎) --></div>
|
|
1470
|
-
<div class="pmodal__ft">
|
|
1471
|
-
<div class="pmodal__issues" id="pptIssues">—</div>
|
|
1472
|
-
<div class="row" style="gap:var(--sp-3)">
|
|
1473
|
-
<button class="btn btn--primary" id="pptCopy" type="button" style="height:40px;padding-inline:18px">复制 AI 提示词</button>
|
|
1474
|
-
</div>
|
|
1475
|
-
</div>
|
|
1476
|
-
</div>
|
|
1477
|
-
</div>
|
|
1478
|
-
|
|
1479
|
-
<!-- ══ PPT 生成指引(帮助模态:精导通道说明 + 可复制提示词) ══ -->
|
|
1480
|
-
<div class="pmodal" id="helpModal" role="dialog" aria-modal="true" aria-label="PPT 生成指引">
|
|
1481
|
-
<div class="pmodal__panel pmodal__panel--doc">
|
|
1482
|
-
<div class="pmodal__hd">
|
|
1483
|
-
<div>
|
|
1484
|
-
<div class="pmodal__t">PPT 生成指引</div>
|
|
1485
|
-
<div class="pmodal__meta">智能体精导 · 与页面同源 · 16:9 可编辑原生形状</div>
|
|
1486
|
-
</div>
|
|
1487
|
-
<button class="iconbtn" id="helpClose" type="button" aria-label="关闭指引" title="关闭(Esc)">
|
|
1488
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
1489
|
-
stroke-linecap="round" stroke-linejoin="round">
|
|
1490
|
-
<path d="M18 6 6 18M6 6l12 12"/></svg>
|
|
1491
|
-
</button>
|
|
1492
|
-
</div>
|
|
1493
|
-
<div class="pmodal__body">
|
|
1494
|
-
<div class="hdoc">
|
|
1495
|
-
<h4>HTML 与 PPTX 的分工</h4>
|
|
1496
|
-
<p>HTML 是阅读与演示界面,PPTX 是协作与修改格式——两者来自同一内容模型(window.REPORT_MODEL),PPTX 内全部是原生形状、文本框、表格(<code>pictures=0</code>),可在 PowerPoint / WPS 中继续编辑;<b>带数据的图表为原生数据图表</b>(chart 对象 + 内嵌 Excel,双击即可"编辑数据");口径、来源与 so-what 自动写入<b>演讲者备注</b>,不占版面。</p>
|
|
1497
|
-
<h4>怎么获得 PPTX</h4>
|
|
1498
|
-
<div class="hch">
|
|
1499
|
-
<div>
|
|
1500
|
-
<h5>页面预览(P)· 所见即所得</h5>
|
|
1501
|
-
<p>点 header 的「预览」按钮查看 WYSIWYG 页序列——预览与智能体精导使用同一序列化引擎,看到的即交付所得。页面不直接导出文件(浏览器端达不到 strict 质量硬门禁)。</p>
|
|
1502
|
-
</div>
|
|
1503
|
-
<div>
|
|
1504
|
-
<h5>智能体精导 · 唯一交付通道</h5>
|
|
1505
|
-
<p>复制下方提示词回到 AI 对话,由本地智能体执行 <code>extract_model.py → build_pptx.js --model → validate_pptx.py --strict</code>,0 错误 0 警告才交付(含模型往返保真检查)。</p>
|
|
1506
|
-
</div>
|
|
1507
|
-
</div>
|
|
1508
|
-
<h4>命令序列(智能体精导)</h4>
|
|
1509
|
-
<p><code>python scripts/extract_model.py 报告.html</code><br>
|
|
1510
|
-
<code>node scripts/build_pptx.js 报告.pptx --model=报告.model.json</code><br>
|
|
1511
|
-
<code>python scripts/validate_pptx.py 报告.pptx --strict --model=报告.model.json</code></p>
|
|
1512
|
-
<h4>可复制提示词(交给 AI 生成 PPTX)</h4>
|
|
1513
|
-
<div class="pmodal__prompt" id="helpPrompt"></div>
|
|
1514
|
-
</div>
|
|
1515
|
-
</div>
|
|
1516
|
-
<div class="pmodal__ft">
|
|
1517
|
-
<div class="pmodal__issues">预览(P)与智能体精导使用同一序列化引擎——所见即所得;交付的 PPTX 为全原生形状,可在 PowerPoint / WPS 中继续编辑。</div>
|
|
1518
|
-
<div class="row" style="gap:var(--sp-3)">
|
|
1519
|
-
<button class="btn btn--primary" id="helpCopy" type="button" style="height:40px;padding-inline:18px">复制 AI 提示词</button>
|
|
1520
|
-
</div>
|
|
1521
|
-
</div>
|
|
1522
|
-
</div>
|
|
1523
|
-
</div>
|
|
1524
|
-
|
|
1525
|
-
<script>
|
|
1526
|
-
/* __TOPPPT_UI_START__ */
|
|
1527
|
-
/* ══ TopPPT HTML 公共 UI 脚本(assets/templates/ui.js 的内联副本 · 由 scripts/sync_runtime.py 注入,禁止手改) ══ */
|
|
1528
|
-
/* ══ TopPPT HTML 公共 UI 脚本(assets/templates/ui.js)══
|
|
1529
|
-
由 scripts/sync_runtime.py 注入到 templates/*.html 的 __TOPPPT_UI__ 标记块 · 禁止在各模板手改。
|
|
1530
|
-
含:主题切换 / header 工具组(风格下拉·预览·帮助·全屏·折叠)/ 滚动显现 / PPT 翻页页码 /
|
|
1531
|
-
图表轻量动效 / PPTX 预览模态(WYSIWYG + 可复制 AI 提示词)/
|
|
1532
|
-
快捷键(T 主题 · P 预览 · H 帮助 · F 全屏 · B 收起/展开工具栏)。
|
|
1533
|
-
注:三模式为生成时确定的独立形态——此处无模式切换;风格为纯视觉皮肤可实时切换。
|
|
1534
|
-
页面不提供 PPTX 导出(浏览器端达不到 strict 硬门禁质量);正式 PPTX 由
|
|
1535
|
-
本地智能体走精导通道生成,预览即"交付所见"(同一序列化引擎)+ 提示词引导后续补生成。
|
|
1536
|
-
主题记忆按文件隔离(report-theme:<pathname>)且系统偏好不覆盖模板出厂
|
|
1537
|
-
data-theme;
|
|
1538
|
-
翻页引擎:飞行期禁 scroll-snap / 连按直跳 / 滚轮打断 / 模态打开不翻页;
|
|
1539
|
-
header 全屏按钮(F)与折叠(B,收起成迷你工具条)。 */
|
|
1540
|
-
|
|
1541
|
-
/* ── 主题切换(记忆按文件隔离;出厂 data-theme 优先于系统偏好) ── */
|
|
1542
|
-
(function(){var r=document.documentElement,b=document.getElementById('themeBtn'),
|
|
1543
|
-
K='report-theme:'+(location.pathname||'index');
|
|
1544
|
-
if(!b)return;
|
|
1545
|
-
function a(t){r.setAttribute('data-theme',t);try{localStorage.setItem(K,t)}catch(e){}
|
|
1546
|
-
/* 主题切换实时同步模型(预览与导出按当前主题出亮/暗版) */
|
|
1547
|
-
if(window.REPORT_MODEL)window.REPORT_MODEL.theme=t;}
|
|
1548
|
-
var s=null;try{s=localStorage.getItem(K)}catch(e){}
|
|
1549
|
-
if(s==='light'||s==='dark'){a(s)}
|
|
1550
|
-
else if(!r.getAttribute('data-theme')&&window.matchMedia&&
|
|
1551
|
-
window.matchMedia('(prefers-color-scheme: dark)').matches){a('dark')}
|
|
1552
|
-
b.addEventListener('click',function(){a(r.getAttribute('data-theme')==='dark'?'light':'dark')})})();
|
|
1553
|
-
|
|
1554
|
-
/* ── header 全屏按钮(F 键同效) ── */
|
|
1555
|
-
(function(){
|
|
1556
|
-
var b=document.getElementById('fsBtn');if(!b)return;
|
|
1557
|
-
function sync(){var on=!!(document.fullscreenElement||document.webkitFullscreenElement);
|
|
1558
|
-
document.documentElement.classList.toggle('is-fs',on);
|
|
1559
|
-
b.title=(on?'退出全屏':'全屏(F)')}
|
|
1560
|
-
b.addEventListener('click',function(){
|
|
1561
|
-
var d=document,el=d.documentElement;
|
|
1562
|
-
if(d.fullscreenElement||d.webkitFullscreenElement){
|
|
1563
|
-
var ex=d.exitFullscreen||d.webkitExitFullscreen;
|
|
1564
|
-
if(ex)ex.call(d)}
|
|
1565
|
-
else{var fn=el.requestFullscreen||el.webkitRequestFullscreen;
|
|
1566
|
-
if(fn){var p=fn.call(el);if(p&&p.catch)p.catch(function(){})}}
|
|
1567
|
-
});
|
|
1568
|
-
['fullscreenchange','webkitfullscreenchange'].forEach(function(ev){
|
|
1569
|
-
document.addEventListener(ev,sync)});
|
|
1570
|
-
sync();
|
|
1571
|
-
})();
|
|
1572
|
-
|
|
1573
|
-
/* ── header 折叠/展开(收起成迷你工具条 · B 键同效 · 记忆按文件隔离) ── */
|
|
1574
|
-
(function(){
|
|
1575
|
-
var b=document.getElementById('barFold');if(!b)return;
|
|
1576
|
-
var K='report-bar-fold:'+(location.pathname||'index');
|
|
1577
|
-
function folded(){return document.documentElement.classList.contains('bar--fold')}
|
|
1578
|
-
function set(v){
|
|
1579
|
-
document.documentElement.classList.toggle('bar--fold',v);
|
|
1580
|
-
try{localStorage.setItem(K,v?'1':'0')}catch(e){}
|
|
1581
|
-
/* --bar-h 收紧会改变 band--fit 高度与锚点,动画结束后重算当前页 */
|
|
1582
|
-
setTimeout(function(){try{window.dispatchEvent(new Event('resize'))}catch(e){}},320);
|
|
1583
|
-
}
|
|
1584
|
-
b.addEventListener('click',function(){set(!folded())});
|
|
1585
|
-
try{if(localStorage.getItem(K)==='1')set(true)}catch(e){}
|
|
1586
|
-
})();
|
|
1587
|
-
|
|
1588
|
-
/* ── header 工具组:风格下拉 + 帮助模态 ── */
|
|
1589
|
-
(function(){
|
|
1590
|
-
var STYLES=[
|
|
1591
|
-
['business-blue','商务蓝','#1a73e8'],['apple-mono','优雅黑白','#1d1d1f'],
|
|
1592
|
-
['mckinsey','麦肯锡','#003a70'],['brand-red','品牌红','#d0021b'],
|
|
1593
|
-
['warm-sand','暖沙金','#96681f'],['deep-teal','墨绿','#0f6b5c'],
|
|
1594
|
-
['graphite-dark','石墨','#0369a1'],['indigo-violet','靛紫','#5b5bd6'],
|
|
1595
|
-
['spectrum','彩色','#4563ef']];
|
|
1596
|
-
var r=document.documentElement;
|
|
1597
|
-
function syncModel(){if(window.REPORT_MODEL){
|
|
1598
|
-
window.REPORT_MODEL.style=r.getAttribute('data-style')}}
|
|
1599
|
-
var seld=document.getElementById('styleSel');
|
|
1600
|
-
if(seld){
|
|
1601
|
-
var btn=document.getElementById('styleBtn'),list=document.getElementById('styleList');
|
|
1602
|
-
STYLES.forEach(function(s){
|
|
1603
|
-
var b=document.createElement('button');b.type='button';b.className='sitem';
|
|
1604
|
-
b.setAttribute('data-style',s[0]);b.title=s[1];
|
|
1605
|
-
b.innerHTML='<span class="swd" style="background:'+s[2]+'"></span>'+s[1];
|
|
1606
|
-
if(s[0]===r.getAttribute('data-style'))b.classList.add('on');
|
|
1607
|
-
b.addEventListener('click',function(){
|
|
1608
|
-
r.setAttribute('data-style',s[0]);
|
|
1609
|
-
list.querySelectorAll('.sitem').forEach(function(x){x.classList.remove('on')});
|
|
1610
|
-
b.classList.add('on');syncModel();
|
|
1611
|
-
seld.classList.remove('open')}); /* MD3 menu 惯例:选中即收起 */
|
|
1612
|
-
list.appendChild(b)});
|
|
1613
|
-
btn.addEventListener('click',function(e){e.stopPropagation();
|
|
1614
|
-
seld.classList.toggle('open')});
|
|
1615
|
-
document.addEventListener('click',function(e){
|
|
1616
|
-
if(!seld.contains(e.target))seld.classList.remove('open')});
|
|
1617
|
-
}
|
|
1618
|
-
/* 帮助模态(PPT 生成指引:双通道说明 + 可复制提示词) */
|
|
1619
|
-
var hmodal=document.getElementById('helpModal');
|
|
1620
|
-
if(hmodal){
|
|
1621
|
-
var hb=document.getElementById('helpBtn');
|
|
1622
|
-
var hclose=document.getElementById('helpClose');
|
|
1623
|
-
var hcopy=document.getElementById('helpCopy');
|
|
1624
|
-
if(hb)hb.addEventListener('click',function(){hmodal.classList.add('open')});
|
|
1625
|
-
if(hclose)hclose.addEventListener('click',function(){hmodal.classList.remove('open')});
|
|
1626
|
-
hmodal.addEventListener('click',function(e){if(e.target===hmodal)hmodal.classList.remove('open')});
|
|
1627
|
-
if(hcopy)hcopy.addEventListener('click',function(){
|
|
1628
|
-
var tx=document.getElementById('helpPrompt');
|
|
1629
|
-
var text=tx?tx.textContent:'';
|
|
1630
|
-
function done(){hcopy.textContent='已复制 ✓';
|
|
1631
|
-
setTimeout(function(){hcopy.textContent='复制 AI 提示词'},1600)}
|
|
1632
|
-
function fallback(){
|
|
1633
|
-
var ta=document.createElement('textarea');ta.value=text;
|
|
1634
|
-
document.body.appendChild(ta);ta.select();
|
|
1635
|
-
try{document.execCommand('copy');done()}catch(e){}ta.remove();}
|
|
1636
|
-
if(navigator.clipboard&&navigator.clipboard.writeText){
|
|
1637
|
-
navigator.clipboard.writeText(text).then(done,fallback)}
|
|
1638
|
-
else fallback();
|
|
1639
|
-
});
|
|
1640
|
-
}
|
|
1641
|
-
})();
|
|
1642
|
-
|
|
1643
|
-
/* ── 滚动显现 ── */
|
|
1644
|
-
(function(){var i=document.querySelectorAll('.rv');
|
|
1645
|
-
if(!('IntersectionObserver' in window)){i.forEach(function(e){e.classList.add('in')});return}
|
|
1646
|
-
var o=new IntersectionObserver(function(es){es.forEach(function(e){
|
|
1647
|
-
if(e.isIntersecting){e.target.classList.add('in');o.unobserve(e.target)}})},
|
|
1648
|
-
{threshold:.08,rootMargin:'0px 0px -48px 0px'});i.forEach(function(e){o.observe(e)})})();
|
|
1649
|
-
|
|
1650
|
-
/* ── PPT 翻页:方向键整屏翻页 + 页码指示
|
|
1651
|
-
翻页要点:① 飞行期临时禁用 scroll-snap(平滑滚动与吸附互相拉扯是卡顿/回弹根因);
|
|
1652
|
-
② 450ms 内连按直跳落位,不反复重启平滑滚动;③ 滚轮/触摸即打断飞行并恢复吸附;
|
|
1653
|
-
④ 预览/帮助模态打开时不翻页;⑤ 空格不劫持聚焦中的按钮/链接。 ── */
|
|
1654
|
-
(function(){
|
|
1655
|
-
var pages=[].slice.call(document.querySelectorAll('section.band'));
|
|
1656
|
-
var dots=document.getElementById('pagerDots');
|
|
1657
|
-
var count=document.getElementById('pagerCount');
|
|
1658
|
-
if(!pages.length||!dots)return;
|
|
1659
|
-
var flying=false,pending=-1,lastGo=0,restoreTO=null;
|
|
1660
|
-
var dotEls=pages.map(function(sec,i){
|
|
1661
|
-
var d=document.createElement('button');
|
|
1662
|
-
d.className='pager__dot';d.type='button';
|
|
1663
|
-
d.setAttribute('aria-label','第 '+(i+1)+' 页');
|
|
1664
|
-
d.addEventListener('click',function(){go(i)});
|
|
1665
|
-
dots.appendChild(d);return d;
|
|
1666
|
-
});
|
|
1667
|
-
function modalOpen(){return !!document.querySelector('.pmodal.open')}
|
|
1668
|
-
function pageTop(i){return pages[i].getBoundingClientRect().top+window.scrollY}
|
|
1669
|
-
function idx(){
|
|
1670
|
-
var y=window.scrollY+window.innerHeight*0.4,best=0,bd=1e9;
|
|
1671
|
-
pages.forEach(function(sec,i){
|
|
1672
|
-
var d=Math.abs(pageTop(i)-y);
|
|
1673
|
-
if(d<bd){bd=d;best=i}
|
|
1674
|
-
});
|
|
1675
|
-
return best;
|
|
1676
|
-
}
|
|
1677
|
-
function mark(c){
|
|
1678
|
-
dotEls.forEach(function(d,i){d.classList.toggle('on',i===c)});
|
|
1679
|
-
if(count)count.textContent=(c+1)+' / '+pages.length;
|
|
1680
|
-
}
|
|
1681
|
-
function snap(on){document.documentElement.style.scrollSnapType=on?'':'none'}
|
|
1682
|
-
function land(){flying=false;pending=-1;snap(true);mark(idx())}
|
|
1683
|
-
function interrupt(){if(flying){flying=false;pending=-1;clearTimeout(restoreTO);snap(true)}}
|
|
1684
|
-
function go(i){
|
|
1685
|
-
i=Math.max(0,Math.min(pages.length-1,i));
|
|
1686
|
-
if(flying&&i===pending)return;
|
|
1687
|
-
var now=Date.now(),instant=(now-lastGo)<450; /* 连按直跳:防平滑滚动反复重启打架 */
|
|
1688
|
-
lastGo=now;pending=i;flying=true;
|
|
1689
|
-
mark(i);snap(false);clearTimeout(restoreTO);
|
|
1690
|
-
try{pages[i].scrollIntoView({behavior:instant?'auto':'smooth',block:'start'})}
|
|
1691
|
-
catch(e){pages[i].scrollIntoView()}
|
|
1692
|
-
restoreTO=setTimeout(land,instant?150:900);
|
|
1693
|
-
}
|
|
1694
|
-
function paint(){if(flying)return;mark(idx())}
|
|
1695
|
-
var KEY_NEXT=['ArrowRight','ArrowDown','PageDown',' '];
|
|
1696
|
-
var KEY_PREV=['ArrowLeft','ArrowUp','PageUp'];
|
|
1697
|
-
document.addEventListener('keydown',function(e){
|
|
1698
|
-
var tag=(e.target&&e.target.tagName)||'';
|
|
1699
|
-
if(/INPUT|TEXTAREA|SELECT/.test(tag))return;
|
|
1700
|
-
if(e.key===' '&&tag!=='BODY')return; /* 空格留给聚焦中的按钮/链接 */
|
|
1701
|
-
if(modalOpen())return; /* 预览/帮助模态打开时不翻页 */
|
|
1702
|
-
var base=(flying&&pending>-1)?pending:idx(); /* 飞行中按基准页继续翻,不回跳 */
|
|
1703
|
-
if(KEY_NEXT.indexOf(e.key)>-1){e.preventDefault();go(base+1)}
|
|
1704
|
-
else if(KEY_PREV.indexOf(e.key)>-1){e.preventDefault();go(base-1)}
|
|
1705
|
-
else if(e.key==='Home'){e.preventDefault();go(0)}
|
|
1706
|
-
else if(e.key==='End'){e.preventDefault();go(pages.length-1)}
|
|
1707
|
-
});
|
|
1708
|
-
/* 用户主动滚动(滚轮/触摸)→ 立即打断飞行并恢复吸附 */
|
|
1709
|
-
['wheel','touchstart'].forEach(function(ev){
|
|
1710
|
-
window.addEventListener(ev,function(){interrupt();paint()},{passive:true});
|
|
1711
|
-
});
|
|
1712
|
-
if('onscrollend' in window)window.addEventListener('scrollend',function(){
|
|
1713
|
-
if(flying)land()});
|
|
1714
|
-
var raf=null;
|
|
1715
|
-
window.addEventListener('scroll',function(){
|
|
1716
|
-
if(raf)return;
|
|
1717
|
-
raf=requestAnimationFrame(function(){paint();raf=null});
|
|
1718
|
-
},{passive:true});
|
|
1719
|
-
window.addEventListener('resize',paint);
|
|
1720
|
-
paint();
|
|
1721
|
-
})();
|
|
1722
|
-
|
|
1723
|
-
/* ── 图表轻量动效:进入视口触发 生长/描边/扫入/计数 ── */
|
|
1724
|
-
(function(){
|
|
1725
|
-
if(!('IntersectionObserver' in window))return;
|
|
1726
|
-
function countUp(el){var t=parseFloat(el.dataset.count),s=el.dataset.suffix||'',
|
|
1727
|
-
d=(el.dataset.count.split('.')[1]||'').length,t0=null;
|
|
1728
|
-
(function st(ts){if(!t0)t0=ts;var p=Math.min(1,(ts-t0)/900);
|
|
1729
|
-
el.textContent=(t*p).toFixed(d)+s;if(p<1)requestAnimationFrame(st)})(performance.now());}
|
|
1730
|
-
var EASE='stroke-dashoffset 1s cubic-bezier(.05,.7,.1,1)';
|
|
1731
|
-
function all(g,sel){return [].slice.call(g.querySelectorAll(sel))}
|
|
1732
|
-
var io=new IntersectionObserver(function(es){
|
|
1733
|
-
var hit=[],draws=[],sweeps=[],counts=[];
|
|
1734
|
-
es.forEach(function(e){if(!e.isIntersecting)return;
|
|
1735
|
-
var g=e.target;io.unobserve(g);hit.push(g);
|
|
1736
|
-
draws=draws.concat(all(g,'[data-draw]'));
|
|
1737
|
-
sweeps=sweeps.concat(all(g,'[data-sweep]'));
|
|
1738
|
-
counts=counts.concat(all(g,'[data-count]'));});
|
|
1739
|
-
if(!hit.length)return;
|
|
1740
|
-
/* 先集中量(读),再集中写:逐条 getTotalLength 与改样式交替会反复触发强制回流 */
|
|
1741
|
-
var lens=draws.map(function(p){return p.getTotalLength()});
|
|
1742
|
-
hit.forEach(function(g){g.classList.add('in')});
|
|
1743
|
-
draws.forEach(function(p,i){p.style.willChange='stroke-dashoffset';
|
|
1744
|
-
p.style.strokeDasharray=lens[i];p.style.strokeDashoffset=lens[i]});
|
|
1745
|
-
sweeps.forEach(function(c){var L=+c.dataset.circ;c.style.willChange='stroke-dashoffset';
|
|
1746
|
-
c.style.strokeDasharray=L;c.style.strokeDashoffset=L});
|
|
1747
|
-
counts.forEach(countUp);
|
|
1748
|
-
requestAnimationFrame(function(){
|
|
1749
|
-
draws.forEach(function(p){p.style.transition=EASE;p.style.strokeDashoffset=0});
|
|
1750
|
-
sweeps.forEach(function(c){c.style.transition=EASE;
|
|
1751
|
-
c.style.strokeDashoffset=(+c.dataset.circ)*(1-(+c.dataset.p))});
|
|
1752
|
-
});
|
|
1753
|
-
/* will-change 长期驻留会占用合成层内存,动画结束即撤下 */
|
|
1754
|
-
setTimeout(function(){draws.concat(sweeps).forEach(function(el){el.style.willChange=''})},1200);
|
|
1755
|
-
},{threshold:.3});
|
|
1756
|
-
document.querySelectorAll('.chart,[data-chart]').forEach(function(el){io.observe(el)});
|
|
1757
|
-
})();
|
|
1758
|
-
|
|
1759
|
-
/* ── research 导航轨高亮(滚动跟随;rail 默认注释不启用,启用后自动生效) ── */
|
|
1760
|
-
(function(){
|
|
1761
|
-
var rail=document.getElementById('rail');if(!rail)return;
|
|
1762
|
-
var as=[].slice.call(rail.querySelectorAll('.rail__a'));
|
|
1763
|
-
if(!as.length)return;
|
|
1764
|
-
var raf=null,tops=[];
|
|
1765
|
-
/* 章节位置缓存:长报告里每帧对每个锚点做 querySelector + 读 offsetTop 会反复触发同步布局 */
|
|
1766
|
-
function measure(){tops=as.map(function(a){
|
|
1767
|
-
var s=document.querySelector(a.getAttribute('href'));return s?s.offsetTop:null})}
|
|
1768
|
-
function paint(){var y=window.scrollY+window.innerHeight*0.4,best=0,bd=1e9;
|
|
1769
|
-
tops.forEach(function(t,i){if(t==null)return;
|
|
1770
|
-
var d=Math.abs(t-y);if(d<bd){bd=d;best=i}});
|
|
1771
|
-
as.forEach(function(a,i){a.classList.toggle('on',i===best)});raf=null}
|
|
1772
|
-
window.addEventListener('scroll',function(){if(raf)return;
|
|
1773
|
-
raf=requestAnimationFrame(paint)},{passive:true});
|
|
1774
|
-
window.addEventListener('resize',function(){measure();paint()});
|
|
1775
|
-
measure();paint();
|
|
1776
|
-
})();
|
|
1777
|
-
|
|
1778
|
-
/* ══ PPTX 预览 / 提示词复制 / 快捷键(预览通道 UI 层;与智能体精导同一序列化引擎)
|
|
1779
|
-
版式/文本所见即所得;带数据的原生图表在交付 PPTX 中为真 chart part(双击可编辑数据),
|
|
1780
|
-
预览侧为形状近似——形态以精导产物为准。 ══ */
|
|
1781
|
-
(function(){
|
|
1782
|
-
var PW=13.333,PH=7.5,EMU=914400;
|
|
1783
|
-
var modal=document.getElementById('pptModal');
|
|
1784
|
-
var body=document.getElementById('pptBody'),meta=document.getElementById('pptMeta'),
|
|
1785
|
-
issues=document.getElementById('pptIssues'),
|
|
1786
|
-
copy=document.getElementById('pptCopy'),closeBtn=document.getElementById('pptClose');
|
|
1787
|
-
var helpModal=document.getElementById('helpModal');
|
|
1788
|
-
|
|
1789
|
-
var PROMPT=[
|
|
1790
|
-
'请为这份 HTML 报告生成可编辑 PPTX 并交付:',
|
|
1791
|
-
'1) 阅读 references/pptx-export.md 与 references/modes.md(三模式独立规则);',
|
|
1792
|
-
'2) 核对/补全 window.REPORT_MODEL 为与正文严格一致的内容(含 mode/style/theme/title/agenda/sections/closing;theme 为 light 或 dark、与页面 data-theme 一致——PPTX 按此导出亮色版或深色版;29 种页型字段与 30 类 chart.type 见 scripts/model-schema.json;research 关键图表页用 type:"exhibit" 并填 exhibitNo/soWhat/footnote);',
|
|
1793
|
-
'3) 运行 python scripts/validate_report.py <报告.html> 直到全 PASS(含模型一致性检查);',
|
|
1794
|
-
'4) 智能体精导并质检(0/0 才交付):python scripts/extract_model.py <报告.html> → NODE_PATH=<pptxgenjs 所在 node_modules> node scripts/build_pptx.js <报告.pptx> --model=<报告.model.json>(如需覆盖模型主题可加 --theme=light|dark) → python scripts/validate_pptx.py <报告.pptx> --strict --model=<报告.model.json>。',
|
|
1795
|
-
'报告文件:<请填写报告.html 的路径>'
|
|
1796
|
-
].join('\n');
|
|
1797
|
-
var promptEl=document.getElementById('helpPrompt');
|
|
1798
|
-
if(promptEl)promptEl.textContent=PROMPT;
|
|
1799
|
-
|
|
1800
|
-
function escHtml(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')}
|
|
1801
|
-
|
|
1802
|
-
/* ── slide XML → DOM 缩略(与导出同一序列化输出,所见即所得) ── */
|
|
1803
|
-
function renderSlide(xml,i,model){
|
|
1804
|
-
var doc=new DOMParser().parseFromString(xml,'application/xml');
|
|
1805
|
-
var slide=document.createElement('div');slide.className='pslide';
|
|
1806
|
-
var no=document.createElement('span');no.className='pslide__no';no.textContent=(i+1);
|
|
1807
|
-
slide.appendChild(no);
|
|
1808
|
-
var tree=doc.getElementsByTagName('p:spTree')[0];
|
|
1809
|
-
if(tree){[].forEach.call(tree.children||tree.childNodes,function(n){
|
|
1810
|
-
if(n.nodeType!==1)return;
|
|
1811
|
-
if(n.tagName==='p:sp')renderSp(slide,n);
|
|
1812
|
-
else if(n.tagName==='p:graphicFrame')renderTable(slide,n);});}
|
|
1813
|
-
paintImages(slide,i,model);
|
|
1814
|
-
return slide;
|
|
1815
|
-
}
|
|
1816
|
-
/* 把模型里的素材图片填进图位块(预览保真)。
|
|
1817
|
-
页序 = 封面 + 大纲(可选) + sections + 收尾 —— 与 slidesOf 同规则。 */
|
|
1818
|
-
function imageSrcsFor(model,idx){
|
|
1819
|
-
try{
|
|
1820
|
-
var secs=(model&&model.sections)||[];
|
|
1821
|
-
var off=1+((model&&model.agenda&&model.agenda.length)?1:0);
|
|
1822
|
-
var si=idx-off;
|
|
1823
|
-
if(si<0||si>=secs.length)return null;
|
|
1824
|
-
var sec=secs[si]||{},srcs=[],ph=false;
|
|
1825
|
-
var holders=[sec.image,sec.right&&sec.right.image];
|
|
1826
|
-
holders.forEach(function(img){
|
|
1827
|
-
if(!img||typeof img!=='object')return;
|
|
1828
|
-
if(img.placeholder){ph=true;return}
|
|
1829
|
-
if(typeof img.src==='string'&&img.src)srcs.push(img.src);
|
|
1830
|
-
(img.items||[]).forEach(function(it){
|
|
1831
|
-
if(typeof it==='string'&&it)srcs.push(it);
|
|
1832
|
-
else if(it&&typeof it.src==='string'&&it.src)srcs.push(it.src);});
|
|
1833
|
-
});
|
|
1834
|
-
return {ph:ph,srcs:srcs};
|
|
1835
|
-
}catch(e){return null}
|
|
1836
|
-
}
|
|
1837
|
-
function paintImages(slide,idx,model){
|
|
1838
|
-
var info=imageSrcsFor(model,idx);if(!info)return;
|
|
1839
|
-
var slots=[].slice.call(slide.querySelectorAll('[data-el="img"],[data-el="imgph"]'));
|
|
1840
|
-
if(info.ph&&!info.srcs.length)return; /* 纯占位:保留虚线占位块与标签 */
|
|
1841
|
-
slots.forEach(function(el,k){
|
|
1842
|
-
var src=info.srcs[k]||info.srcs[0];if(!src)return;
|
|
1843
|
-
el.style.backgroundImage='url("'+String(src).replace(/"/g,'%22')+'")';
|
|
1844
|
-
el.style.backgroundSize='cover';el.style.backgroundPosition='center';
|
|
1845
|
-
el.style.border='none';el.style.borderRadius='0.14em';
|
|
1846
|
-
});
|
|
1847
|
-
}
|
|
1848
|
-
function geomOf(scope){
|
|
1849
|
-
var xf=scope.getElementsByTagName('a:xfrm')[0];if(!xf)return null;
|
|
1850
|
-
var off=xf.getElementsByTagName('a:off')[0],ext=xf.getElementsByTagName('a:ext')[0];
|
|
1851
|
-
if(!off||!ext)return null;
|
|
1852
|
-
return{x:+off.getAttribute('x')/EMU,y:+off.getAttribute('y')/EMU,
|
|
1853
|
-
w:+ext.getAttribute('cx')/EMU,h:+ext.getAttribute('cy')/EMU};
|
|
1854
|
-
}
|
|
1855
|
-
function place(el,g){
|
|
1856
|
-
el.style.left=(g.x/PW*100)+'%';el.style.top=(g.y/PH*100)+'%';
|
|
1857
|
-
el.style.width=(g.w/PW*100)+'%';el.style.height=(g.h/PH*100)+'%';
|
|
1858
|
-
}
|
|
1859
|
-
function fillOf(node){
|
|
1860
|
-
if(!node)return null;
|
|
1861
|
-
var f=node.getElementsByTagName('a:solidFill')[0];if(!f)return null;
|
|
1862
|
-
var c=f.getElementsByTagName('a:srgbClr')[0];
|
|
1863
|
-
return c?'#'+c.getAttribute('val'):null;
|
|
1864
|
-
}
|
|
1865
|
-
function renderSp(slide,sp){
|
|
1866
|
-
var spPr=sp.getElementsByTagName('p:spPr')[0];if(!spPr)return;
|
|
1867
|
-
var g=geomOf(spPr);if(!g)return;
|
|
1868
|
-
var el=document.createElement('div');
|
|
1869
|
-
/* 图位块标记(img 真实图片 / imgph 配图占位)——预览时用真实图片填充图位,
|
|
1870
|
-
让"有配图的页面"在预览里就是交付所见(占位块保持虚线 + 标签)。 */
|
|
1871
|
-
var nv=sp.getElementsByTagName('p:cNvPr')[0];
|
|
1872
|
-
var nm=nv?nv.getAttribute('name'):'';
|
|
1873
|
-
el.setAttribute('data-el',(nm==='img'||nm==='imgph')?nm:'');
|
|
1874
|
-
place(el,g);
|
|
1875
|
-
var fill=fillOf(spPr);
|
|
1876
|
-
if(fill)el.style.background=fill;
|
|
1877
|
-
var prst=spPr.getElementsByTagName('a:prstGeom')[0];
|
|
1878
|
-
if(prst){var t=prst.getAttribute('prst');
|
|
1879
|
-
if(t==='roundRect')el.style.borderRadius='0.14em';
|
|
1880
|
-
else if(t==='ellipse')el.style.borderRadius='50%';}
|
|
1881
|
-
var ln=spPr.getElementsByTagName('a:ln')[0];
|
|
1882
|
-
if(ln&&!fill){var lc=fillOf(ln);if(lc)el.style.border='1px solid '+lc;}
|
|
1883
|
-
var tx=sp.getElementsByTagName('p:txBody')[0];
|
|
1884
|
-
if(tx){
|
|
1885
|
-
var bodyPr=tx.getElementsByTagName('a:bodyPr')[0];
|
|
1886
|
-
var anchor=(bodyPr&&bodyPr.getAttribute('anchor'))||'t';
|
|
1887
|
-
el.style.display='flex';el.style.flexDirection='column';
|
|
1888
|
-
el.style.justifyContent=anchor==='ctr'?'center':(anchor==='b'?'flex-end':'flex-start');
|
|
1889
|
-
el.style.padding='0.06em 0.08em';el.style.boxSizing='border-box';
|
|
1890
|
-
[].forEach.call(tx.getElementsByTagName('a:p'),function(p){
|
|
1891
|
-
var pe=document.createElement('p');
|
|
1892
|
-
var pPr=p.getElementsByTagName('a:pPr')[0];
|
|
1893
|
-
var algn=pPr?pPr.getAttribute('algn'):null;
|
|
1894
|
-
if(algn==='ctr')pe.style.textAlign='center';
|
|
1895
|
-
else if(algn==='r'||algn==='just')pe.style.textAlign='right';
|
|
1896
|
-
[].forEach.call(p.getElementsByTagName('a:r'),function(r){
|
|
1897
|
-
var rPr=r.getElementsByTagName('a:rPr')[0];
|
|
1898
|
-
var run=document.createElement('span');
|
|
1899
|
-
run.style.fontSize=(((rPr&&rPr.getAttribute('sz'))||1000)/100)+'em';
|
|
1900
|
-
if(rPr&&rPr.getAttribute('b'))run.style.fontWeight='700';
|
|
1901
|
-
var col=rPr?fillOf(rPr):null;if(col)run.style.color=col;
|
|
1902
|
-
var lat=r.getElementsByTagName('a:latin')[0];
|
|
1903
|
-
if(lat)run.style.fontFamily=lat.getAttribute('typeface');
|
|
1904
|
-
var tNode=r.getElementsByTagName('a:t')[0];
|
|
1905
|
-
run.textContent=tNode?tNode.textContent:'';
|
|
1906
|
-
pe.appendChild(run);
|
|
1907
|
-
});
|
|
1908
|
-
el.appendChild(pe);
|
|
1909
|
-
});
|
|
1910
|
-
}
|
|
1911
|
-
slide.appendChild(el);
|
|
1912
|
-
}
|
|
1913
|
-
function renderTable(slide,gf){
|
|
1914
|
-
var g=geomOf(gf);if(!g)return;
|
|
1915
|
-
var el=document.createElement('div');el.setAttribute('data-el','');
|
|
1916
|
-
place(el,g);
|
|
1917
|
-
var tbl=gf.getElementsByTagName('a:tbl')[0];
|
|
1918
|
-
if(tbl){
|
|
1919
|
-
var cols=[].map.call(tbl.getElementsByTagName('a:gridCol'),function(c){return +c.getAttribute('w')});
|
|
1920
|
-
var tot=cols.reduce(function(a,b){return a+b},0)||1;
|
|
1921
|
-
var t=document.createElement('table');
|
|
1922
|
-
t.style.width='100%';t.style.height='100%';t.style.borderCollapse='collapse';
|
|
1923
|
-
[].forEach.call(tbl.getElementsByTagName('a:tr'),function(tr){
|
|
1924
|
-
var re=document.createElement('tr');
|
|
1925
|
-
var tcs=tr.getElementsByTagName('a:tc');
|
|
1926
|
-
for(var ci=0;ci<cols.length;ci++){
|
|
1927
|
-
var td=document.createElement('td');
|
|
1928
|
-
td.style.width=(cols[ci]/tot*100)+'%';td.style.padding='0.08em 0.1em';
|
|
1929
|
-
var tc=tcs[ci];
|
|
1930
|
-
if(tc){
|
|
1931
|
-
var f=fillOf(tc);if(f)td.style.background=f;
|
|
1932
|
-
var r=tc.getElementsByTagName('a:r')[0];
|
|
1933
|
-
if(r){
|
|
1934
|
-
var rPr=r.getElementsByTagName('a:rPr')[0];
|
|
1935
|
-
var run=document.createElement('span');
|
|
1936
|
-
run.style.fontSize=(((rPr&&rPr.getAttribute('sz'))||1000)/100)+'em';
|
|
1937
|
-
if(rPr&&rPr.getAttribute('b'))run.style.fontWeight='700';
|
|
1938
|
-
var col=rPr?fillOf(rPr):null;if(col)run.style.color=col;
|
|
1939
|
-
var tNode=r.getElementsByTagName('a:t')[0];
|
|
1940
|
-
run.textContent=tNode?tNode.textContent:'';
|
|
1941
|
-
td.appendChild(run);
|
|
1942
|
-
}
|
|
1943
|
-
}
|
|
1944
|
-
re.appendChild(td);
|
|
1945
|
-
}
|
|
1946
|
-
t.appendChild(re);
|
|
1947
|
-
});
|
|
1948
|
-
el.appendChild(t);
|
|
1949
|
-
}
|
|
1950
|
-
slide.appendChild(el);
|
|
1951
|
-
}
|
|
1952
|
-
function fitSlides(){
|
|
1953
|
-
[].forEach.call(body.querySelectorAll('.pslide'),function(s){
|
|
1954
|
-
s.style.fontSize=(s.clientWidth/PW/72)+'px'; /* 1em = 1pt(按当前缩略宽度) */
|
|
1955
|
-
});
|
|
1956
|
-
}
|
|
1957
|
-
function currentModel(){
|
|
1958
|
-
var m=window.REPORT_MODEL;
|
|
1959
|
-
if(m){var r=document.documentElement;
|
|
1960
|
-
m.style=r.getAttribute('data-style')||m.style;
|
|
1961
|
-
m.theme=r.getAttribute('data-theme')||m.theme||'light';}
|
|
1962
|
-
return m;
|
|
1963
|
-
}
|
|
1964
|
-
|
|
1965
|
-
/* ── 打开模态:WYSIWYG 预览(与通道 B 同一序列化输出)+ 自检结果 + 常显可复制提示词 ── */
|
|
1966
|
-
function open(){
|
|
1967
|
-
if(!modal||!body||!meta||!issues)return; /* 预览骨架缺失时优雅降级(不影响其它快捷键) */
|
|
1968
|
-
var m=currentModel();
|
|
1969
|
-
var chk=(window.TopPptHtml&&window.TopPptHtml.validateModel)
|
|
1970
|
-
?window.TopPptHtml.validateModel(m)
|
|
1971
|
-
:{ok:false,missing:['预览运行时缺失'],warnings:[],pages:0};
|
|
1972
|
-
meta.textContent=(m&&m.title?m.title:'未命名报告')+' · '+(m&&m.style||'business-blue')
|
|
1973
|
-
+' · '+((m&&m.theme)==='dark'?'深色':(m&&m.theme)==='light'?'浅色':'浅色')
|
|
1974
|
-
+' · '+(m&&m.mode||'presentation')+' · '+(chk.pages||0)+' 页';
|
|
1975
|
-
body.innerHTML='';
|
|
1976
|
-
if(window.TopPptHtml&&m&&m.sections){
|
|
1977
|
-
try{
|
|
1978
|
-
window.TopPptHtml.slidesXml(m).forEach(function(x,i){body.appendChild(renderSlide(x,i,m))});
|
|
1979
|
-
}catch(e){body.innerHTML='<div class="t-sm">预览渲染失败:'+escHtml(e.message)+'</div>'}
|
|
1980
|
-
}else if(!m){
|
|
1981
|
-
body.innerHTML='<div class="t-sm">报告未内嵌 window.REPORT_MODEL 内容模型,无法预览。请按下方提示词回到 AI 对话补全。</div>';
|
|
1982
|
-
}
|
|
1983
|
-
if(chk.ok){
|
|
1984
|
-
issues.innerHTML='<b>模型自检通过</b>:内容完整('+chk.pages+' 页)。版式与文本与精导一致;'
|
|
1985
|
-
+'带数据的原生图表在交付 PPTX 中为可编辑数据图表(chart part),预览为形状近似。'
|
|
1986
|
-
+(chk.warnings.length?'<br>· '+chk.warnings.map(escHtml).join('<br>· '):'')
|
|
1987
|
-
+'<br>本页面不直接导出 PPTX——复制下方提示词交给 AI,由智能体精导通道生成并过 strict 校验(0/0)后交付。'
|
|
1988
|
-
+'<div class="pmodal__prompt">'+escHtml(PROMPT)+'</div>';
|
|
1989
|
-
}else{
|
|
1990
|
-
issues.innerHTML='<b>模型不完整</b>:'
|
|
1991
|
-
+chk.missing.map(escHtml).join('、')
|
|
1992
|
-
+'<br>预览为尽力渲染;复制下方提示词回到 AI 对话补全模型并由智能体精导交付。'
|
|
1993
|
-
+'<div class="pmodal__prompt">'+escHtml(PROMPT)+'</div>';
|
|
1994
|
-
}
|
|
1995
|
-
if(copy)copy.style.display='';
|
|
1996
|
-
modal.classList.add('open');
|
|
1997
|
-
requestAnimationFrame(fitSlides);
|
|
1998
|
-
}
|
|
1999
|
-
function close(){if(modal)modal.classList.remove('open')}
|
|
2000
|
-
|
|
2001
|
-
/* 按钮 wiring(header 预览 / 帮助) */
|
|
2002
|
-
var previewBtn=document.getElementById('pptPreviewBtn');
|
|
2003
|
-
if(previewBtn)previewBtn.addEventListener('click',open);
|
|
2004
|
-
if(closeBtn)closeBtn.addEventListener('click',close);
|
|
2005
|
-
if(modal)modal.addEventListener('click',function(e){if(e.target===modal)close()});
|
|
2006
|
-
if(copy)copy.addEventListener('click',function(){
|
|
2007
|
-
function done(){copy.textContent='已复制 ✓';
|
|
2008
|
-
setTimeout(function(){copy.textContent='复制 AI 提示词'},1600)}
|
|
2009
|
-
function fallback(){
|
|
2010
|
-
var ta=document.createElement('textarea');ta.value=PROMPT;
|
|
2011
|
-
document.body.appendChild(ta);ta.select();
|
|
2012
|
-
try{document.execCommand('copy');done()}catch(e){}ta.remove();}
|
|
2013
|
-
if(navigator.clipboard&&navigator.clipboard.writeText){
|
|
2014
|
-
navigator.clipboard.writeText(PROMPT).then(done,fallback)}
|
|
2015
|
-
else fallback();
|
|
2016
|
-
});
|
|
2017
|
-
|
|
2018
|
-
/* 快捷键:T 主题 / F 全屏 / B 收起工具栏 / P 预览 / H 帮助 / Esc 关闭 */
|
|
2019
|
-
document.addEventListener('keydown',function(e){
|
|
2020
|
-
var tag=(e.target&&e.target.tagName)||'';
|
|
2021
|
-
if(/INPUT|TEXTAREA|SELECT/.test(tag))return;
|
|
2022
|
-
if(e.key==='Escape'){
|
|
2023
|
-
close();
|
|
2024
|
-
if(helpModal)helpModal.classList.remove('open');
|
|
2025
|
-
var seld=document.getElementById('styleSel');
|
|
2026
|
-
if(seld)seld.classList.remove('open');
|
|
2027
|
-
return;
|
|
2028
|
-
}
|
|
2029
|
-
if(e.altKey||e.ctrlKey||e.metaKey)return;
|
|
2030
|
-
/* 模态打开时屏蔽内容快捷键:预览态整页锁定;帮助态仅 Esc(上方已处理)可用 */
|
|
2031
|
-
if((modal&&modal.classList.contains('open'))||
|
|
2032
|
-
(helpModal&&helpModal.classList.contains('open')))return;
|
|
2033
|
-
var k=e.key.toLowerCase();
|
|
2034
|
-
if(k==='t'){e.preventDefault();
|
|
2035
|
-
var tb=document.getElementById('themeBtn');if(tb)tb.click()}
|
|
2036
|
-
else if(k==='f'){e.preventDefault(); /* 全屏 */
|
|
2037
|
-
var fb=document.getElementById('fsBtn');if(fb)fb.click()}
|
|
2038
|
-
else if(k==='b'){e.preventDefault(); /* 收起/展开工具栏 */
|
|
2039
|
-
var bb=document.getElementById('barFold');if(bb)bb.click()}
|
|
2040
|
-
else if(k==='p'){e.preventDefault();open()}
|
|
2041
|
-
else if(k==='h'){e.preventDefault();
|
|
2042
|
-
if(helpModal)helpModal.classList.add('open')}
|
|
2043
|
-
});
|
|
2044
|
-
|
|
2045
|
-
window.addEventListener('resize',function(){
|
|
2046
|
-
if(modal&&modal.classList.contains('open'))fitSlides()},{passive:true});
|
|
2047
|
-
})();
|
|
2048
|
-
/* __TOPPPT_UI_END__ */
|
|
2049
|
-
|
|
2050
|
-
/* ══ PPTX 导出内容模型(与页面正文同源,生成报告时同步填写;填法见 references/pptx-export.md) ══
|
|
2051
|
-
两通道共用:① header"预览/导出 PPTX"(页面直导,导出前自检)② extract_model.py → build_pptx.js --model(智能体精导)
|
|
2052
|
-
必须是严格 JSON(双引号、无尾逗号、无注释)。mode 固定为 presentation(与 data-mode 一致,生成时锁定)。
|
|
2053
|
-
页型:points / metrics / table / timeline / bar / cards / split / diagram。 */
|
|
2054
|
-
window.REPORT_MODEL = {
|
|
2055
|
-
"mode": "presentation",
|
|
2056
|
-
"style": "brand-red",
|
|
2057
|
-
"title": "AI 智能体:从工具到同事",
|
|
2058
|
-
"subtitle": "从工具到同事 · 台上宣讲版",
|
|
2059
|
-
"meta": "宣讲材料 · 2026-09-09",
|
|
2060
|
-
"agenda": [
|
|
2061
|
-
[
|
|
2062
|
-
"01",
|
|
2063
|
-
"范式跃迁",
|
|
2064
|
-
"从 Chatbot 到 Autonomous Agent"
|
|
2065
|
-
],
|
|
2066
|
-
[
|
|
2067
|
-
"02",
|
|
2068
|
-
"形态与边界",
|
|
2069
|
-
"三类形态与能力边界"
|
|
2070
|
-
],
|
|
2071
|
-
[
|
|
2072
|
-
"03",
|
|
2073
|
-
"落地路径",
|
|
2074
|
-
"采纳率四季度爬升"
|
|
2075
|
-
],
|
|
2076
|
-
[
|
|
2077
|
-
"04",
|
|
2078
|
-
"参考资料",
|
|
2079
|
-
"数据来源与口径说明"
|
|
2080
|
-
]
|
|
2081
|
-
],
|
|
2082
|
-
"sections": [
|
|
2083
|
-
{
|
|
2084
|
-
"type": "points",
|
|
2085
|
-
"eyebrow": "01 · 范式跃迁",
|
|
2086
|
-
"title": "范式跃迁:智能体的本质是目标驱动的自主执行",
|
|
2087
|
-
"points": [
|
|
2088
|
-
[
|
|
2089
|
-
"从工具到同事",
|
|
2090
|
-
"人从执行者变成目标设定者与审核者"
|
|
2091
|
-
],
|
|
2092
|
-
[
|
|
2093
|
-
"试点率过六成",
|
|
2094
|
-
"61% 企业已进入试点,规模化不足两成"
|
|
2095
|
-
],
|
|
2096
|
-
[
|
|
2097
|
-
"护栏先行",
|
|
2098
|
-
"权限与审计内嵌,高风险动作人工确认"
|
|
2099
|
-
]
|
|
2100
|
-
],
|
|
2101
|
-
"metrics": [
|
|
2102
|
-
[
|
|
2103
|
-
"61%",
|
|
2104
|
-
"2025 企业试点率[1]"
|
|
2105
|
-
],
|
|
2106
|
-
[
|
|
2107
|
-
"18%",
|
|
2108
|
-
"规模化运行占比[2]"
|
|
2109
|
-
]
|
|
2110
|
-
]
|
|
2111
|
-
},
|
|
2112
|
-
{
|
|
2113
|
-
"type": "table",
|
|
2114
|
-
"eyebrow": "02 · 形态与边界",
|
|
2115
|
-
"title": "三类形态能力边界分明,同事级智能体尚在早期",
|
|
2116
|
-
"table": {
|
|
2117
|
-
"head": [
|
|
2118
|
-
"能力",
|
|
2119
|
-
"2026 现状",
|
|
2120
|
-
"跃迁目标"
|
|
2121
|
-
],
|
|
2122
|
-
"rows": [
|
|
2123
|
-
[
|
|
2124
|
-
"任务自主性",
|
|
2125
|
-
"单点工具为主,人逐步驱动",
|
|
2126
|
-
"目标驱动,端到端"
|
|
2127
|
-
],
|
|
2128
|
-
[
|
|
2129
|
-
"工具生态",
|
|
2130
|
-
"连接数少、缺乏统一编排",
|
|
2131
|
-
"统一编排,千级工具"
|
|
2132
|
-
],
|
|
2133
|
-
[
|
|
2134
|
-
"记忆与规划",
|
|
2135
|
-
"会话级记忆,无长期规划",
|
|
2136
|
-
"任务级记忆,多步规划"
|
|
2137
|
-
],
|
|
2138
|
-
[
|
|
2139
|
-
"安全治理",
|
|
2140
|
-
"事后审计、无回退",
|
|
2141
|
-
"护栏内运行,全程可回退"
|
|
2142
|
-
]
|
|
2143
|
-
]
|
|
2144
|
-
}
|
|
2145
|
-
},
|
|
2146
|
-
{
|
|
2147
|
-
"type": "bar",
|
|
2148
|
-
"eyebrow": "03 · 落地路径",
|
|
2149
|
-
"title": "企业智能体采纳率:四个季度四步走至 60%",
|
|
2150
|
-
"chart": {
|
|
2151
|
-
"labels": [
|
|
2152
|
-
"2026Q3",
|
|
2153
|
-
"Q4",
|
|
2154
|
-
"2027Q1",
|
|
2155
|
-
"Q2"
|
|
2156
|
-
],
|
|
2157
|
-
"values": [
|
|
2158
|
-
30,
|
|
2159
|
-
45,
|
|
2160
|
-
55,
|
|
2161
|
-
60
|
|
2162
|
-
],
|
|
2163
|
-
"max": 100,
|
|
2164
|
-
"unit": "%"
|
|
2165
|
-
},
|
|
2166
|
-
"note": "口径:试点及以上的企业占比[1]"
|
|
2167
|
-
},
|
|
2168
|
-
{
|
|
2169
|
-
"type": "comparison",
|
|
2170
|
-
"eyebrow": "03 · 落地路径",
|
|
2171
|
-
"title": "从试点到规模化:两条路径的分野",
|
|
2172
|
-
"left": {
|
|
2173
|
-
"title": "堆场景路径 · 现状",
|
|
2174
|
-
"points": [
|
|
2175
|
-
[
|
|
2176
|
-
"接入慢",
|
|
2177
|
-
"每场景重复对接工具与权限"
|
|
2178
|
-
],
|
|
2179
|
-
[
|
|
2180
|
-
"治理弱",
|
|
2181
|
-
"事后审计,回退靠人工"
|
|
2182
|
-
],
|
|
2183
|
-
[
|
|
2184
|
-
"易回退",
|
|
2185
|
-
"23 家中 19 家半年内退回试点"
|
|
2186
|
-
]
|
|
2187
|
-
]
|
|
2188
|
-
},
|
|
2189
|
-
"right": {
|
|
2190
|
-
"title": "平台化路径 · 目标",
|
|
2191
|
-
"points": [
|
|
2192
|
-
[
|
|
2193
|
-
"接入快",
|
|
2194
|
-
"统一编排,接入成本降一个量级"
|
|
2195
|
-
],
|
|
2196
|
-
[
|
|
2197
|
-
"治理内嵌",
|
|
2198
|
-
"权限、审计、回退进平台层"
|
|
2199
|
-
],
|
|
2200
|
-
[
|
|
2201
|
-
"可放量",
|
|
2202
|
-
"先行 9 家全部穿越分水岭"
|
|
2203
|
-
]
|
|
2204
|
-
]
|
|
2205
|
-
},
|
|
2206
|
-
"verdict": "平台化不是可选项,而是规模化穿越分水岭的唯一共同路径。"
|
|
2207
|
-
},
|
|
2208
|
-
{
|
|
2209
|
-
"type": "kpi",
|
|
2210
|
-
"eyebrow": "04 · 落地路径",
|
|
2211
|
-
"title": "六成企业已越过 PoC,规模化不足两成",
|
|
2212
|
-
"hero": [
|
|
2213
|
-
"61%",
|
|
2214
|
-
"2025 企业智能体试点率",
|
|
2215
|
-
"+6pt vs 2024"
|
|
2216
|
-
],
|
|
2217
|
-
"metrics": [
|
|
2218
|
-
[
|
|
2219
|
-
"18%",
|
|
2220
|
-
"规模化运行占比,3 场景以上"
|
|
2221
|
-
],
|
|
2222
|
-
[
|
|
2223
|
-
"9 家",
|
|
2224
|
-
"规模化成功企业,全部先建平台层"
|
|
2225
|
-
],
|
|
2226
|
-
[
|
|
2227
|
-
"60%",
|
|
2228
|
-
"2027Q2 采纳率目标"
|
|
2229
|
-
]
|
|
2230
|
-
]
|
|
2231
|
-
},
|
|
2232
|
-
{
|
|
2233
|
-
"type": "donut",
|
|
2234
|
-
"eyebrow": "04 · 落地路径",
|
|
2235
|
-
"title": "试点企业分布:过六成跨越 PoC",
|
|
2236
|
-
"chart": {
|
|
2237
|
-
"labels": [
|
|
2238
|
-
"已规模化",
|
|
2239
|
-
"试点中",
|
|
2240
|
-
"观望"
|
|
2241
|
-
],
|
|
2242
|
-
"values": [
|
|
2243
|
-
18,
|
|
2244
|
-
43,
|
|
2245
|
-
39
|
|
2246
|
-
],
|
|
2247
|
-
"unit": "%",
|
|
2248
|
-
"centerLabel": "企业占比"
|
|
2249
|
-
},
|
|
2250
|
-
"note": "口径:试点及以上 = 61%;N=42 深度访谈 + 行业抽样[1]"
|
|
2251
|
-
},
|
|
2252
|
-
{
|
|
2253
|
-
"type": "quote",
|
|
2254
|
-
"eyebrow": "03 · 关键判断",
|
|
2255
|
-
"title": "规模化瓶颈不在模型,而在治理与集成",
|
|
2256
|
-
"quote": "穿越分水岭的钥匙是平台化,不是更大的模型。",
|
|
2257
|
-
"author": "企业智能体发展调研 · 42 家深度访谈"
|
|
2258
|
-
},
|
|
2259
|
-
{
|
|
2260
|
-
"type": "steps",
|
|
2261
|
-
"eyebrow": "05 · 落地节奏",
|
|
2262
|
-
"title": "四步走:从单场景试点到平台化放量",
|
|
2263
|
-
"steps": [
|
|
2264
|
-
[
|
|
2265
|
-
"选场景",
|
|
2266
|
-
"客服 / 研发 / 数据三类高价值场景先跑通"
|
|
2267
|
-
],
|
|
2268
|
-
[
|
|
2269
|
-
"建平台",
|
|
2270
|
-
"三层解耦 + 统一编排,接入成本降一个量级"
|
|
2271
|
-
],
|
|
2272
|
-
{
|
|
2273
|
-
"t": "立治理",
|
|
2274
|
-
"d": "权限、审计、回退进平台层,高风险动作人工确认",
|
|
2275
|
-
"accent": true
|
|
2276
|
-
},
|
|
2277
|
-
[
|
|
2278
|
-
"放量复制",
|
|
2279
|
-
"同一套护栏复制到新场景,无需重建"
|
|
2280
|
-
]
|
|
2281
|
-
],
|
|
2282
|
-
"footnote": "口径:先行 9 家规模化企业的共同路径[2]"
|
|
2283
|
-
},
|
|
2284
|
-
{
|
|
2285
|
-
"type": "bullet",
|
|
2286
|
-
"eyebrow": "06 · 目标对照",
|
|
2287
|
-
"title": "四项能力对照目标:治理与集成缺口最大",
|
|
2288
|
-
"items": [
|
|
2289
|
-
[
|
|
2290
|
-
"场景覆盖",
|
|
2291
|
-
61,
|
|
2292
|
-
80
|
|
2293
|
-
],
|
|
2294
|
-
[
|
|
2295
|
-
"治理就绪",
|
|
2296
|
-
34,
|
|
2297
|
-
75
|
|
2298
|
-
],
|
|
2299
|
-
[
|
|
2300
|
-
"工具集成",
|
|
2301
|
-
42,
|
|
2302
|
-
70
|
|
2303
|
-
],
|
|
2304
|
-
[
|
|
2305
|
-
"规模化运行",
|
|
2306
|
-
18,
|
|
2307
|
-
60
|
|
2308
|
-
]
|
|
2309
|
-
],
|
|
2310
|
-
"unit": "%",
|
|
2311
|
-
"max": 100,
|
|
2312
|
-
"footnote": "口径:能力就绪度 = 自评「已具备且稳定运行」的企业占比;目标取 2027Q2[2]"
|
|
2313
|
-
},
|
|
2314
|
-
{
|
|
2315
|
-
"type": "cards",
|
|
2316
|
-
"eyebrow": "06 · 能力地图",
|
|
2317
|
-
"title": "平台化三件套:编排、记忆、治理缺一不可",
|
|
2318
|
-
"cards": [
|
|
2319
|
-
{
|
|
2320
|
-
"title": "统一编排",
|
|
2321
|
-
"points": [
|
|
2322
|
-
"工具连接:千级工具统一注册与调用",
|
|
2323
|
-
"任务拆解:多步规划与失败重试"
|
|
2324
|
-
]
|
|
2325
|
-
},
|
|
2326
|
-
{
|
|
2327
|
-
"title": "长期记忆",
|
|
2328
|
-
"points": [
|
|
2329
|
-
"任务记忆:跨会话保留目标与进度",
|
|
2330
|
-
"偏好学习:组织口径与风格沉淀"
|
|
2331
|
-
]
|
|
2332
|
-
},
|
|
2333
|
-
{
|
|
2334
|
-
"title": "护栏治理",
|
|
2335
|
-
"points": [
|
|
2336
|
-
"权限分级:高风险动作人工确认",
|
|
2337
|
-
"全程可回退:审计与回滚内嵌平台层"
|
|
2338
|
-
]
|
|
2339
|
-
}
|
|
2340
|
-
],
|
|
2341
|
-
"columns": 3,
|
|
2342
|
-
"footnote": "口径:先行 9 家规模化企业的共同能力底座[2]"
|
|
2343
|
-
},
|
|
2344
|
-
{
|
|
2345
|
-
"type": "image",
|
|
2346
|
-
"eyebrow": "07 · 场景实拍",
|
|
2347
|
-
"title": "客服智能体工作台:会话、工具调用与人工接管同屏",
|
|
2348
|
-
"image": {
|
|
2349
|
-
"placeholder": true,
|
|
2350
|
-
"layout": "full",
|
|
2351
|
-
"caption": "图 1:客服智能体工作台(占位示例;交付前替换为真实截图)",
|
|
2352
|
-
"hint": "建议 2400×800px(3:1)真实截图;把 image.placeholder 改为 image.src 即可"
|
|
2353
|
-
}
|
|
2354
|
-
}
|
|
2355
|
-
],
|
|
2356
|
-
"closing": {
|
|
2357
|
-
"title": "让智能体真正干活起来",
|
|
2358
|
-
"points": [
|
|
2359
|
-
[
|
|
2360
|
-
"选场景",
|
|
2361
|
-
"客服 / 研发 / 数据三类高价值场景"
|
|
2362
|
-
],
|
|
2363
|
-
[
|
|
2364
|
-
"建平台",
|
|
2365
|
-
"三层解耦 + 统一编排 + 护栏"
|
|
2366
|
-
],
|
|
2367
|
-
[
|
|
2368
|
-
"立治理",
|
|
2369
|
-
"权限、审计、回退全闭环"
|
|
2370
|
-
]
|
|
2371
|
-
]
|
|
2372
|
-
},
|
|
2373
|
-
"theme": "light"
|
|
2374
|
-
};
|
|
2375
|
-
|
|
2376
|
-
/* __TOPPPT_RUNTIME_START__ */
|
|
2377
|
-
/* ══ PPTX 导出运行时(assets/pptx-export.js 的内联副本 · 由 scripts/sync_runtime.py 注入,禁止手改) ══ */
|
|
2378
|
-
/* ═══════════════════════════════════════════════════════════════════════════
|
|
2379
|
-
* TopPPT HTML· 浏览器端 PPTX 预览运行时
|
|
2380
|
-
* 零依赖、随报告单文件分发;OOXML 序列化器(仅供预览,页面不导出文件)。
|
|
2381
|
-
*
|
|
2382
|
-
* 定位:HTML 是阅读与演示界面,PPTX 是协作与修改格式——两者来自同一内容模型
|
|
2383
|
-
* (window.REPORT_MODEL)。页面只提供「预览 PPTX」(WYSIWYG 模态,与智能体精导通道
|
|
2384
|
-
* 同一序列化引擎)与可复制 AI 提示词;正式 PPTX 一律由本地智能体走精导通道
|
|
2385
|
-
* (build_pptx.js)生成并过 strict 硬门禁,浏览器端导出达不到该质量标准。
|
|
2386
|
-
*
|
|
2387
|
-
* API:
|
|
2388
|
-
* TopPptHtml.slidesXml(model) → string[](每页 slide XML,预览模态渲染用)
|
|
2389
|
-
* TopPptHtml.validateModel(model) → {ok, missing[], warnings[], pages}(schema 单源驱动)
|
|
2390
|
-
* TopPptHtml.slidesOf / slideXml / relsXml / NS / PRESETS
|
|
2391
|
-
* → 供 scripts/gen_channel_a.js 在 Node 侧组装完整
|
|
2392
|
-
* PPTX 包(回归双裁判内部质量路径,页面不使用)
|
|
2393
|
-
*
|
|
2394
|
-
* model = window.REPORT_MODEL(JSON,与页面正文同源):
|
|
2395
|
-
* { mode, style, theme(light|dark:PPTX 按此选亮/暗 token,默认 light),
|
|
2396
|
-
* title, subtitle, meta, agenda:[[num,标题,说明]…](architecture 可省),
|
|
2397
|
-
* sections:[{type: points|metrics|kpi|table|timeline|steps|bar|donut|heatmap|bullet|pyramid|
|
|
2398
|
-
* image|cards|split|comparison|quote|diagram|exhibit|twocol|threecol|halftable|
|
|
2399
|
-
* matrix|lane, soWhat?, footnote?, flags?, image?, …}],
|
|
2400
|
-
* closing:{title,points} }
|
|
2401
|
-
* 通用可选字段:soWhat(结论条)/ footnote(来源行)/ flags(待核实清单 → accent 强调色条)
|
|
2402
|
-
* / image(素材图片或配图占位,{src|items|placeholder, layout, fit, caption, alt, hint},
|
|
2403
|
-
* src 仅 data: 或相对路径;layout ∈ full/half/bleed/grid/compare/wall,见 layout-constants.json imageSpec)。
|
|
2404
|
-
* 页型字段定义(29 种)与 chart.type 白名单(30 类)的唯一事实源:scripts/model-schema.json。
|
|
2405
|
-
*
|
|
2406
|
-
* ★ 单源机制(scripts/sync_runtime.py 注入,标记块内禁止手改):
|
|
2407
|
-
* - 版式常量:scripts/layout-constants.json → __TOPPPT_CONSTANTS__ 块;
|
|
2408
|
-
* 通道 B(build_pptx.js)require 同一份 JSON,两通道产出一致。
|
|
2409
|
-
* - DSL schema:scripts/model-schema.json → __TOPPPT_SCHEMA__ 块;
|
|
2410
|
-
* scripts/extract_model.py 读同一份文件,双端校验同源。
|
|
2411
|
-
* ★ 三模式独立排版比例尺(MTS)——research 咨询密排 / architecture 全幅图页型;
|
|
2412
|
-
* modeSize() 把调用点的 presentation 基准 pt 映射到当前模式比例尺。
|
|
2413
|
-
* ★ 本运行时只做「预览与回归裁判」:图表为形状近似渲染(交付通道 B 用原生数据图表,
|
|
2414
|
-
* 可在 PowerPoint 中"编辑数据");文本与版式几何两通道同源,cross_verify 逐页比对。
|
|
2415
|
-
* ═══════════════════════════════════════════════════════════════════════════ */
|
|
2416
|
-
(function (g) {
|
|
2417
|
-
'use strict';
|
|
2418
|
-
/* 双形态载荷判定:[[t,d]…] 数组 vs [{t,d,accent}…] 记录。typeof [] === 'object'。 */
|
|
2419
|
-
function isRec(v) { return !!v && typeof v === 'object' && !Array.isArray(v); }
|
|
2420
|
-
|
|
2421
|
-
/* __TOPPPT_CONSTANTS_START__ */
|
|
2422
|
-
/* ── 由 scripts/sync_runtime.py 从 scripts/layout-constants.json 注入 · 禁止手改 ── */
|
|
2423
|
-
/* MTS = 三模式独立排版比例尺(按模式取 pt;基准 TS_BASE = presentation 档) */
|
|
2424
|
-
/* PRESETS_DARK = 风格 dark token(REPORT_MODEL.theme="dark" 时导出深色版)*/
|
|
2425
|
-
/* GRID = 12 列网格;TYPO = 语义字阶 C0–T14;CONT = 容器内边距;REG = 图表登记四元组;DEEP = 深度模式配置 */
|
|
2426
|
-
/* IMG = 素材图片规格与配图占位约定(版式/裁切/建议尺寸/占位标签,双引擎同源) */
|
|
2427
|
-
/* STYLE_DATA_COLORS(_DARK) = 9 套风格各自的编码色板 c1–c5(数据系列;结构色纪律不变) */
|
|
2428
|
-
/* LAYOUT_SLOTS + regionOf = 页型布局区域 IR(与 scripts/lib_layout_regions.js 同源) */
|
|
2429
|
-
var PW = 13.333, PH = 7.5, MX = 0.6, CW = PW - 2 * MX, EMU = 914400;
|
|
2430
|
-
var TS_BASE = {"coverTitle":44,"coverSub":18,"h1":30,"h2":17,"lead":14,"body":13,"caption":11,"micro":10};
|
|
2431
|
-
var MTS = {"presentation":{"coverTitle":44,"coverSub":18,"h1":30,"h2":17,"lead":14,"body":13,"caption":11,"micro":10},"research":{"coverTitle":36,"coverSub":14,"h1":22,"h2":13.5,"lead":11.5,"body":10.5,"caption":9,"micro":8.5},"architecture":{"coverTitle":44,"coverSub":19,"h1":30,"h2":17,"lead":14,"body":13,"caption":11,"micro":10}};
|
|
2432
|
-
var PT = {"layout":{"contentTop":2.35,"contentBottom":6.9,"contentBottomWithNote":6.4,"pageNumY":7.0},"common":{"headEyebrowY":0.48,"headTitleY":0.82,"headRuleY":1.72,"leadY":1.88,"bodyY":2.35,"bodyYWithLead":2.55,"pagerY":7.0},"cover":{"metaY":0.7,"titleY":2.4,"titleH":2.0,"subtitleY":4.5,"subtitleH":0.8},"closing":{"eyebrowY":0.7,"titleY":1.2,"titleH":1.2,"pointsY":3.0,"pointTitleH":0.6,"pointBodyH":1.2},"points":{"listY":2.5,"rowH":0.72,"rowHMin":0.5,"markSize":0.14,"metricY":4.7,"metricW":2.6,"metricValH":0.9,"metricCapY":5.6},"metrics":{"bandY":3.1,"cardH":2.3,"valY":3.45,"valH":1.1,"capY":4.55,"capH":0.7,"gap":0.3,"maxW":2.9},"table":{"y":2.5,"rowH":0.55,"rowHMin":0.32,"headRowH":0.5},"bar":{"hbarY0":2.6,"hbarY1":6.25,"chartX":0.6,"chartY":2.95,"chartW":1.2,"chartH":3.35,"noteY":6.55},"note":{"y":6.55,"h":0.4},"exhibit":{"badgeY":2.35,"chartY":2.75,"chartH":3.25,"soWhatY":6.05,"footnoteY":6.72},"twocol":{"colGap":0.5,"bodyH":3.6,"rowHMin":0.5},"hbar":{"labelW":1.9,"valW":1.0,"rowH":0.55,"barRatio":0.5},"diagram":{"layerBarW":1.35,"layerGap":0.15,"nodeGap":0.15,"bodyStartY":2.9,"maxLayerH":1.15,"nodeMaxW":2.2},"split":{"leftW":0.55,"rightW":0.415,"rightX":0.585,"pointsY":2.7,"rowH":0.78,"capY":2.55,"chartY":3.0,"chartH":3.7,"tableY":2.7,"tableRowH":0.5},"cards":{"startY":3.0,"gap":0.25,"maxH":2.0,"titleH":0.4},"timeline":{"labelY":2.95,"axisY":3.58,"dotY":3.51,"dotR":0.18,"nameY":3.9,"descY":4.45,"descH":1.2},"research":{"col3Gap":0.38,"matrixLabelW":1.5,"matrixCellH":0.85,"matrixY":2.2,"denseTableY":2.2,"denseRowH":0.42,"halfTableW":0.52,"halfChartX":0.57,"halfChartW":0.43},"arch":{"fullStartY":2.1,"fullEndY":6.9,"laneH":0.72,"laneHMin":0.5,"laneGap":0.14,"laneHeadW":1.3,"stepGap":0.12,"stepMaxW":1.9},"kpi":{"heroY":2.9,"heroW":0.52,"heroH":1.6,"dividerX":0.56,"metricY0":2.95,"metricRowH":0.85},"comparison":{"panelGap":0.4,"panelY":2.55,"panelH":3.4,"titleH":0.45,"rowH":0.62},"quote":{"markY":1.75,"textY":2.7,"textH":2.3,"authorY":5.25,"contextY":5.8},"donut":{"centerX":3.05,"centerY":4.6,"r":1.6,"holeR":0.95,"legendX":5.6,"legendRowH":0.55,"legendLabelW":3.4,"legendValW":1.3,"legendPctX":5.3,"legendPctW":1.3},"steps":{"y":3.05,"barH":1.35,"gap":0.12,"arrowW":0.3,"numH":0.3,"titleH":0.34,"pad":0.08,"maxPerRow":6},"heatmap":{"y":2.6,"labelW":1.7,"headH":0.42,"cellH":0.6,"cellGap":0.06,"legendW":1.6},"pyramid":{"y":2.35,"rowH":0.72,"gap":0.08,"minW":0.34,"labelW":3.2},"bullet":{"y":2.6,"rowH":0.8,"labelW":2.4,"valW":1.7,"barH":0.26},"image":{"y":2.3,"h":4.2,"halfW":0.56,"halfGap":0.06,"capY":6.6,"capH":0.3,"radius":0.09,"noteRowH":0.62,"gridGap":0.16,"gridCols":{"2":2,"3":3,"4":4,"6":3},"gridCapH":0.28,"compareGap":0.3,"compareTagH":0.26,"wallCellH":0.92,"wallGap":0.18,"wallMaxRows":2},"flagbar":{"hdH":0.26,"rowH":0.24,"maxRows":3,"gap":0.12},"sankey":{"y":2.45,"h":4.1,"nodeW":0.32,"nodeGap":0.14,"labelW":1.55,"minBandH":0.22,"maxNodes":12,"maxFlows":24},"treemap":{"y":2.45,"h":4.1,"gap":0.05,"labelMinH":0.34,"maxLeaves":16,"maxDepth":3},"boxplot":{"y":2.7,"h":3.7,"labelW":1.5,"groupGap":0.3,"boxMaxW":0.9,"whiskerCapW":0.34,"maxGroups":8},"network":{"y":2.4,"h":4.2,"nodeR":0.28,"nodeRMin":0.18,"labelMaxW":1.4,"maxNodes":18,"maxEdges":30},"marimekko":{"y":2.6,"h":3.75,"labelW":1.55,"colGap":0.06,"legendH":0.42,"maxCols":6,"maxSegs":4},"streamgraph":{"y":2.6,"h":3.75,"labelW":1.4,"bandMinH":0.12,"maxSeries":6}};
|
|
2433
|
-
var PRESETS = {
|
|
2434
|
-
'business-blue': { accent:'1A73E8', ink:'202124', body:'5F6368', faint:'80868B', bg:'FFFFFF', surface:'F8F9FA', line:'DADCE0', soft:'E8F0FE', onAccent:'FFFFFF', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2435
|
-
'apple-mono': { accent:'1D1D1F', ink:'1D1D1F', body:'6E6E73', faint:'86868B', bg:'FFFFFF', surface:'F5F5F7', line:'D2D2D7', soft:'F5F5F7', onAccent:'FFFFFF', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2436
|
-
'mckinsey': { accent:'003A70', ink:'101828', body:'475467', faint:'667085', bg:'FFFFFF', surface:'F2F4F7', line:'D0D5DD', soft:'EEF4FB', onAccent:'FFFFFF', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2437
|
-
'brand-red': { accent:'D0021B', ink:'1F1A1B', body:'5D5456', faint:'8A8083', bg:'FFFFFF', surface:'FAF7F7', line:'E2D9DA', soft:'FBECEC', onAccent:'FFFFFF', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2438
|
-
'warm-sand': { accent:'96681F', ink:'2A2521', body:'6B5F52', faint:'93836F', bg:'FBF9F6', surface:'F6F2EC', line:'E0D7C9', soft:'F3ECDD', onAccent:'FFFFFF', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2439
|
-
'deep-teal': { accent:'0F6B5C', ink:'14201E', body:'4A5A57', faint:'73837F', bg:'FFFFFF', surface:'F2F6F5', line:'CFDEDC', soft:'E4F1EE', onAccent:'FFFFFF', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2440
|
-
'graphite-dark': { accent:'0369A1', ink:'1A1D21', body:'4B5158', faint:'767C84', bg:'F4F5F7', surface:'ECEEF1', line:'CED1D6', soft:'E2EFF7', onAccent:'FFFFFF', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2441
|
-
'indigo-violet': { accent:'5B5BD6', ink:'1B1A2E', body:'52506B', faint:'7A779A', bg:'FFFFFF', surface:'F4F4FB', line:'D7D7E8', soft:'ECECF9', onAccent:'FFFFFF', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2442
|
-
'spectrum': { accent:'4563EF', ink:'171A21', body:'4D5566', faint:'7B8494', bg:'FBFCFE', surface:'F4F6FA', line:'D6DCE6', soft:'ECEEFF', onAccent:'FFFFFF', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' }
|
|
2443
|
-
};
|
|
2444
|
-
var PRESETS_DARK = {
|
|
2445
|
-
'business-blue': { accent:'8AB4F8', ink:'E8EAED', body:'ADB4BD', faint:'868D96', bg:'0D0F13', surface:'191D23', line:'2F353D', soft:'16243A', onAccent:'0D1B2E', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2446
|
-
'apple-mono': { accent:'F5F5F7', ink:'F5F5F7', body:'A1A1A6', faint:'86868B', bg:'000000', surface:'161617', line:'38383D', soft:'1D1D1F', onAccent:'000000', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2447
|
-
'mckinsey': { accent:'7FB2E5', ink:'E4E7EC', body:'AAB3C2', faint:'8A94A6', bg:'0C1116', surface:'12181F', line:'2A3342', soft:'12233A', onAccent:'0C1A2E', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2448
|
-
'brand-red': { accent:'FF5A66', ink:'F3ECEC', body:'C4B6B7', faint:'9A8D8E', bg:'130D0E', surface:'1A1213', line:'37272A', soft:'331518', onAccent:'2A0D10', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2449
|
-
'warm-sand': { accent:'D9B36A', ink:'ECE5DB', body:'C4B8A8', faint:'9B8D7A', bg:'15120E', surface:'1D1913', line:'3A3128', soft:'2E2417', onAccent:'2A1F10', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2450
|
-
'deep-teal': { accent:'4FC3B0', ink:'E2ECEA', body:'B3C2BF', faint:'86948F', bg:'0D1312', surface:'131B1A', line:'293834', soft:'12312C', onAccent:'0C2420', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2451
|
-
'graphite-dark': { accent:'4DA3FF', ink:'EEF2F6', body:'B9C2CD', faint:'85909D', bg:'0B0E12', surface:'171D24', line:'2B3540', soft:'132841', onAccent:'07182B', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2452
|
-
'indigo-violet': { accent:'A5A4F0', ink:'E6E5F5', body:'B6B4D4', faint:'8B89B0', bg:'100F1C', surface:'171629', line:'322F52', soft:'232146', onAccent:'171631', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' },
|
|
2453
|
-
'spectrum': { accent:'8BA0F8', ink:'E8EBF2', body:'A7AFBF', faint:'7E8798', bg:'0E1116', surface:'171C26', line:'2B3342', soft:'1B2242', onAccent:'141A38', font:'Microsoft YaHei', fontDisplay:'Microsoft YaHei' }
|
|
2454
|
-
};
|
|
2455
|
-
var STYLE_DATA_COLORS = {"business-blue":["1a73e8","3526bb","21a199","b77148","99983d"],"apple-mono":["1d1d1f","0071e3","0f7b6c","c2410c","7c3aed"],"mckinsey":["003a70","2826bb","21a18e","b76848","99903d"],"brand-red":["d0021b","bb6326","d634ae","40a182","438aa8"],"warm-sand":["96681f","839324","cf3b3e","4d88b2","4a47a4"],"deep-teal":["0f6b5c","2683bb","22aa3e","b74870","a85c43"],"graphite-dark":["0369a1","2637bb","1f9977","b75948","a18a40"],"indigo-violet":["5b5bd6","7432ae","4197c8","a08f4b","7ca04b"],"spectrum":["4563ef","5b26bb","269ebb","af8746","83993d"]};
|
|
2456
|
-
var STYLE_DATA_COLORS_DARK = {"business-blue":["8ab4f8","7966e0","88e7e5","d4ad91","cbce83"],"apple-mono":["f5f5f7","2997ff","2dd4bf","ff9f0a","bf5af2"],"mckinsey":["7fb2e5","6f6bdb","8ce3d8","d1a694","cbc586"],"brand-red":["ff5a66","e09e66","e788cb","91d4c2","83b4ce"],"warm-sand":["d9b36a","c2d571","df9391","97b5ce","8e89c7"],"deep-teal":["4fc3b0","7aaecd","97d8a4","c99cac","c29c8f"],"graphite-dark":["4da3ff","6c66e0","88e7dc","d4a691","cec983"],"indigo-violet":["a5a4f0","a967e0","89c3e7","d3c792","aece83"],"spectrum":["8ba0f8","9066e0","88d6e7","d4ba91","bdce83"]};
|
|
2457
|
-
var GRID = {"columns":12,"gutter":0.16,"colW":0.8644,"x":[0.6,1.6244,2.6488,3.6732,4.6976,5.722,6.7464,7.7708,8.7952,9.8196,10.844,11.8684,12.733],"y":[2.35,3.27,4.19,5.11,6.03,6.9],"safe":{"top":0.4,"bottom":0.4,"left":0.6,"right":0.6}};
|
|
2458
|
-
var TYPO = {"levels":[{"id":"C0","name":"封面/章节幕标题","role":"coverTitle","usage":"封面主标题、章节幕标题"},{"id":"T1","name":"页码/章节徽章","role":"caption","usage":"页码徽章、章节编号"},{"id":"T2","name":"页面主标题/结论标题","role":"h1","usage":"每页顶部结论句(research 行动标题)"},{"id":"T3","name":"副标题/语境说明","role":"lead","usage":"主标题下方导语"},{"id":"T4","name":"模块标题/图表标题","role":"h2","usage":"卡片标题、图表标题、面板标题"},{"id":"T5","name":"证据编号/轻量标签","role":"micro","usage":"Exhibit 编号、状态标签"},{"id":"T6","name":"证据块标题/小节标题","role":"h2","usage":"分栏小标题、证据块标题"},{"id":"T7","name":"正文解释段落","role":"body","usage":"证据解释、管理解读正文、表格正文"},{"id":"T8","name":"结论条文字","role":"body","usage":"so-what 结论条、核心结论框"},{"id":"T9","name":"SO WHAT 标签","role":"caption","usage":"so-what 标签行"},{"id":"T10","name":"SO WHAT 正文/业务含义","role":"body","usage":"so-what 正文、行动含义、表格行动项"},{"id":"T11","name":"图表轴/图例/刻度/微标签","role":"micro","usage":"坐标轴、图例、刻度、单位(禁用于表格正文与完整短句)"},{"id":"T12","name":"图表数据标签","role":"caption","usage":"折线点值、柱形标签、百分比标注"},{"id":"T13","name":"关键 KPI 大数字","role":"h1","usage":"KPI 大数字、hero 数值"},{"id":"T14","name":"注释/口径/来源/页脚","role":"micro","usage":"footnote、来源行、页脚、小页码"}],"tableSemanticRule":{"body":["T7","T10"],"microAllowed":["T11"],"microForbiddenFor":["表格正文","行动项","风险项","解释句","建议句","长项目符号","完整短句"]}};
|
|
2459
|
-
var CONT = {"pad":{"card":0.18,"panel":0.2,"cell":0.08,"band":0.16,"soWhat":0.16,"kpi":0.18,"chart":0.1,"head":0.05,"footer":0.05,"node":0.06},"minPad":0.06,"textMetrics":{"lineFactor":1.45,"emAsciiRatio":0.52},"overflowRule":"溢出处置优先级:① 优化内容(列表化/精炼/合并/拆页/换组合版式)→ ② 调整容器尺寸与行宽 → ③ 有限度缩小字号(见 fontShrink)→ ④ 仍装不下则拆页。禁止无限缩字号硬塞,也禁止把「精简」当第一手段砍掉决策必需信息。","fontShrink":{"ladder":[15,14,13.5,13,12.5,12,11.5,11,10.5,10,9.5,9,8.5],"maxShrinkSteps":4,"floorPt":{"presentation":10,"research":9,"architecture":10},"preferContentFirst":true,"warnBelow":9.5},"continuousTextRule":"语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。"};
|
|
2460
|
-
var REG = {"bar":{"html":"svg-bars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160}},"hbar":{"html":"svg-hbars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":100}},"stack":{"html":"svg-stack","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160},"nativeOpt":{"barGrouping":"stacked"}},"stackline":{"html":"svg-stackline","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":40},"nativeOpt":{"barGrouping":"percentStacked"}},"line":{"html":"svg-line","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"dualline":{"html":"svg-dualline","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"area":{"html":"svg-area","pptx":"native","nativeType":"area","dataTable":"notes","min":{"vbHeightMin":160}},"donut":{"html":"svg-donut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"multidonut":{"html":"svg-mdonut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"pie":{"html":"svg-pie","pptx":"native","nativeType":"pie","dataTable":"notes","min":{"pxWidthMin":140}},"radar":{"html":"svg-radar","pptx":"native","nativeType":"radar","dataTable":"notes","min":{"pxWidthMin":220}},"scatter":{"html":"svg-scatter","pptx":"native","nativeType":"scatter","dataTable":"notes","min":{"vbHeightMin":180}},"bubble":{"html":"svg-bubble","pptx":"native","nativeType":"bubble","dataTable":"notes","min":{"vbHeightMin":180}},"waterfall":{"html":"svg-wf","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":170},"nativeTrick":"stacked+hiddenBase+connector"},"gauge":{"html":"svg-gauge","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":180},"nativeTrick":"doughnut+firstSliceAng+hiddenRemainder"},"pareto":{"html":"svg-pareto","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":180},"nativeTrick":"multiType+bar+line"},"gantt":{"html":"svg-gantt","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":130},"path":{"forbidPresetShapes":true}},"vsbar":{"html":"svg-vsbar","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"progress":{"html":"svg-progress","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"sparkline":{"html":"svg-spark","pptx":"shape","dataTable":"off","min":{"vbHeightMin":20},"path":{"forbidPresetShapes":true}},"funnel":{"html":"svg-funnel","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"minSamplePoints":8,"forbidPresetShapes":true}},"treemap":{"html":"svg-treemap","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":170},"path":{"forbidPresetShapes":true}},"marimekko":{"html":"svg-marimekko","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"boxplot":{"html":"svg-boxplot","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"network":{"html":"svg-network","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"forbidPresetShapes":true}},"sankey":{"html":"svg-sankey","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"minSamplePoints":16,"trackBothEdges":true,"forbidPresetShapes":true}},"streamgraph":{"html":"svg-stream","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"minSamplePoints":24,"trackBothEdges":true,"forbidPresetShapes":true}},"slope":{"html":"svg-slope","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"dumbbell":{"html":"svg-dumbbell","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"lollipop":{"html":"svg-lollipop","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"dotplot":{"html":"svg-dotplot","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":120},"path":{"forbidPresetShapes":true}},"bulletchart":{"html":"svg-bullet","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"waffle":{"html":"svg-waffle","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"radialbar":{"html":"svg-radialbar","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":200},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"rose":{"html":"svg-rose","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":180},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"candlestick":{"html":"svg-candle","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}}};
|
|
2461
|
-
var DEEP = {"triggers":["高保真","1:1","一比一","精确还原","按图还原","正式交付","不能偏移","像素级","严格对照","逐页验收"],"complexCharts":["sankey","streamgraph","treemap","marimekko","boxplot","network","rose","radialbar","candlestick","funnel"],"checks":{"anchors":true,"containerOverflow":true,"renderCompare":true,"manifest":true},"renderCompare":{"engine":"soffice","fallback":"powerpoint-com","skipIfMissing":true,"dpi":96}};
|
|
2462
|
-
var IMG = {"layouts":["full","half","bleed","grid","compare","wall"],"multiLayouts":["grid","compare","wall"],"fitEnum":["cover","contain"],"fitDefault":"cover","formats":["png","jpeg","jpg","webp","svg"],"ratioDefault":{"full":"3:1","half":"4:3","bleed":"21:9","grid":"4:3","compare":"4:3","wall":"1:1"},"ratioCssClass":{"full":"media--r3-1","half":"media--r4-3","bleed":"media--r21-9","grid":"media--r4-3","compare":"media--r4-3","wall":"media--r1-1"},"recommendedSizePx":{"full":"2400×800","half":"1400×1050","bleed":"2560×1100","grid":"900×675","compare":"1400×1050","wall":"480×480"},"maxInlineBytes":1572864,"maxTotalInlineBytes":8388608,"maxPerPage":6,"placeholderLabel":"配图占位","placeholderHintPrefix":"建议 ","placeholderHintSuffix":"px","zeroExternal":true,"qualityRule":"位图优先提供 ≥2× 显示尺寸;截图用 16:9 或 4:3 锁定比例;数据图不要用截图(应用内联 SVG 图表,可随主题变色)。"};
|
|
2463
|
-
var LAYOUT_SLOTS = {"version":"9.1","pageTypes":{"points":{"mode":["presentation","research","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".grid.g-2 / .card / .ul","pptx":"points columns","minUnits":1},{"id":"annotation","role":"annotation","required":false,"html":".sowhat / .note","pptx":"soWhatBar / footnoteLine"}]},"metrics":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".metric","pptx":"metric row","minUnits":4},{"id":"annotation","role":"annotation","required":false,"html":"口径行 / .sowhat","pptx":"footnoteLine / soWhatBar"}]},"kpi":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"hero","role":"primary","required":true,"html":".t-metric hero","pptx":"hero number"},{"id":"metrics","role":"secondary","required":false,"html":".metric","pptx":"support metrics"},{"id":"annotation","role":"annotation","required":false,"html":".sowhat / .note","pptx":"soWhatBar"}]},"exhibit":{"mode":["research","presentation"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead / .exhibit__hd","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"svg[data-chart] / .exhibit","pptx":"nativeChart | shapeChart","channel":"registry"},{"id":"secondary","role":"secondary","required":false,"html":"要点列表","pptx":"side bullets"},{"id":"annotation","role":"annotation","required":true,"html":".exhibit__src / .sowhat","pptx":"footnoteLine / soWhatBar / notes"}]},"split":{"mode":["presentation","research","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"left","role":"primary","required":true,"html":"左区(points/table/image/chart)","pptx":"left zone"},{"id":"right","role":"secondary","required":true,"html":"右区(points/table/image/chart)","pptx":"right zone"},{"id":"annotation","role":"annotation","required":false,"html":".sowhat / .note","pptx":"soWhatBar"}]},"table":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".tbl-wrap table","pptx":"addTable","maxRows":{"presentation":8,"research":16}},{"id":"annotation","role":"annotation","required":false,"html":".sowhat / .note","pptx":"soWhatBar / footnoteLine"}]},"bar":{"mode":["presentation","research","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"svg[data-chart]","pptx":"nativeChart | shapeChart","channel":"registry"},{"id":"annotation","role":"annotation","required":true,"html":"note / dataTable","pptx":"footnoteLine / notes / inline table"}]},"donut":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"svg[data-chart=donut]","pptx":"addChart doughnut"},{"id":"annotation","role":"annotation","required":false,"html":"中心合计 / 图例 / .note","pptx":"center label / legend / notes"}]},"twocol":{"mode":["research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".cols-2","pptx":"two text columns"},{"id":"annotation","role":"annotation","required":false,"html":".sowhat","pptx":"soWhatBar"}]},"threecol":{"mode":["research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".cols-3","pptx":"three text columns"},{"id":"annotation","role":"annotation","required":false,"html":".sowhat","pptx":"soWhatBar"}]},"halftable":{"mode":["research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"左表","pptx":"addTable left"},{"id":"secondary","role":"secondary","required":true,"html":"右图","pptx":"chart right"},{"id":"annotation","role":"annotation","required":false,"html":".sowhat","pptx":"soWhatBar"}]},"matrix":{"mode":["research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".matrix","pptx":"matrix cells"},{"id":"annotation","role":"annotation","required":false,"html":".sowhat","pptx":"soWhatBar"}]},"comparison":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"left","role":"primary","required":true,"html":"左面板","pptx":"left panel"},{"id":"right","role":"secondary","required":true,"html":"右面板","pptx":"right panel"},{"id":"annotation","role":"annotation","required":false,"html":"verdict / .sowhat","pptx":"verdict bar"}]},"quote":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"金句正文","pptx":"quote text"}]},"timeline":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".tl","pptx":"timeline axis"}]},"steps":{"mode":["presentation","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".steps","pptx":"step row"}]},"cards":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".card grid","pptx":"card grid"}]},"heatmap":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".heat","pptx":"heat cells"}]},"bullet":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".bul","pptx":"bullet rows"}]},"pyramid":{"mode":["presentation","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".pyr","pptx":"pyramid layers"}]},"image":{"mode":["presentation","research","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".media*","pptx":"imageLayoutShapes"}]},"diagram":{"mode":["architecture","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".arch / layers","pptx":"layered nodes + edges","maxNodes":24,"maxLayers":4},{"id":"annotation","role":"annotation","required":false,"html":".arch__legend / .note","pptx":"legend chips / notes"}]},"lane":{"mode":["architecture","research"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":".lane","pptx":"lanes + steps"}]},"sankey":{"mode":["research","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"svg[data-chart=sankey]","pptx":"infoSankey","maxNodes":12},{"id":"annotation","role":"annotation","required":true,"html":"dataTable / .note","pptx":"notes"}]},"treemap":{"mode":["research","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"svg[data-chart=treemap]","pptx":"infoTreemap"},{"id":"annotation","role":"annotation","required":true,"html":"dataTable / .note","pptx":"notes"}]},"boxplot":{"mode":["research","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"svg[data-chart=boxplot]","pptx":"infoBoxplot"},{"id":"annotation","role":"annotation","required":true,"html":"dataTable / .note","pptx":"notes"}]},"network":{"mode":["research","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"svg[data-chart=network]","pptx":"infoNetwork"},{"id":"annotation","role":"annotation","required":true,"html":"dataTable / .note","pptx":"notes"}]},"marimekko":{"mode":["research","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"svg[data-chart=marimekko]","pptx":"infoMarimekko"},{"id":"annotation","role":"annotation","required":true,"html":"dataTable / .note","pptx":"notes"}]},"streamgraph":{"mode":["research","architecture"],"slots":[{"id":"head","role":"chrome","required":true,"html":".shead","pptx":"head()"},{"id":"primary","role":"primary","required":true,"html":"svg[data-chart=streamgraph]","pptx":"infoStreamgraph"},{"id":"annotation","role":"annotation","required":true,"html":"dataTable / .note","pptx":"notes"}]}}};
|
|
2464
|
-
|
|
2465
|
-
/* regionOf(pageType, slotId, opts) — 页型布局区域 IR(与 scripts/lib_layout_regions.js 同源) */
|
|
2466
|
-
function regionOf(pageType, slotId, opts) {
|
|
2467
|
-
var type = String(pageType || 'points'), slot = String(slotId || '');
|
|
2468
|
-
opts = opts || {};
|
|
2469
|
-
var L = PT.layout || {}, C = PT.common || {};
|
|
2470
|
-
var topDefault = C.bodyY || 2.5;
|
|
2471
|
-
var bottomDefault = opts.withNote ? (L.contentBottomWithNote || 6.4) : (L.contentBottom || 6.9);
|
|
2472
|
-
if (slot === 'head' || slot === 'chrome') {
|
|
2473
|
-
return { x: MX, y: C.headEyebrowY || 0.55, w: CW,
|
|
2474
|
-
h: Math.max(0.4, (C.headRuleY || 1.95) - (C.headEyebrowY || 0.55)),
|
|
2475
|
-
titleY: C.headTitleY, ruleY: C.headRuleY, leadY: C.leadY, bodyY: C.bodyY };
|
|
2476
|
-
}
|
|
2477
|
-
if (slot === 'annotation' || slot === 'soWhat') {
|
|
2478
|
-
var sy = (PT.exhibit && PT.exhibit.soWhatY != null) ? PT.exhibit.soWhatY : 6.05;
|
|
2479
|
-
return { x: MX, y: sy, w: CW, h: 0.62 };
|
|
2480
|
-
}
|
|
2481
|
-
if (slot === 'footnote') {
|
|
2482
|
-
var fy = (PT.exhibit && PT.exhibit.footnoteY != null) ? PT.exhibit.footnoteY : ((PT.note && PT.note.y) || 6.55);
|
|
2483
|
-
return { x: MX, y: fy, w: CW - 1.2, h: (PT.note && PT.note.h) || 0.32 };
|
|
2484
|
-
}
|
|
2485
|
-
var P = PT[type] || {};
|
|
2486
|
-
if (type === 'exhibit' && slot === 'primary') {
|
|
2487
|
-
var ey = P.chartY || 2.95;
|
|
2488
|
-
return { x: MX, y: ey, w: CW, h: Math.max(1.2, (L.contentBottomWithNote || 6.4) - ey), badgeY: P.badgeY };
|
|
2489
|
-
}
|
|
2490
|
-
if (type === 'metrics' && slot === 'primary') {
|
|
2491
|
-
return { x: MX, y: P.bandY || 3.1, w: CW, h: P.cardH || 2.3, gap: P.gap, maxW: P.maxW,
|
|
2492
|
-
valY: P.valY, valH: P.valH, capY: P.capY, capH: P.capH };
|
|
2493
|
-
}
|
|
2494
|
-
if (type === 'table' && slot === 'primary') {
|
|
2495
|
-
var ty = P.y || topDefault;
|
|
2496
|
-
return { x: MX, y: ty, w: CW, h: Math.max(1, (opts.bottom != null ? opts.bottom : bottomDefault) - ty) };
|
|
2497
|
-
}
|
|
2498
|
-
if (type === 'bar' && slot === 'primary') {
|
|
2499
|
-
var by = P.chartY || 2.95;
|
|
2500
|
-
var bb = opts.bottom != null ? opts.bottom : bottomDefault;
|
|
2501
|
-
return { x: MX + (P.chartX || 0), y: by, w: CW - (P.chartW || 0), h: Math.max(1.2, bb - by) };
|
|
2502
|
-
}
|
|
2503
|
-
if (type === 'bar' && slot === 'hbar') {
|
|
2504
|
-
var y0 = P.hbarY0 || 2.6;
|
|
2505
|
-
var y1 = opts.bottom != null ? opts.bottom : (P.hbarY1 || 6.25);
|
|
2506
|
-
return { x: MX, y: y0, w: CW, h: Math.max(1.2, y1 - y0) };
|
|
2507
|
-
}
|
|
2508
|
-
if (type === 'points' && (slot === 'primary' || slot === 'list')) {
|
|
2509
|
-
var ly = P.listY || topDefault;
|
|
2510
|
-
return { x: MX, y: ly, w: CW, h: Math.max(0.8, (opts.bottom != null ? opts.bottom : bottomDefault) - ly) };
|
|
2511
|
-
}
|
|
2512
|
-
if (type === 'split' && (slot === 'left' || slot === 'right')) {
|
|
2513
|
-
var st = P.chartY || topDefault;
|
|
2514
|
-
var sb = opts.bottom != null ? opts.bottom : bottomDefault;
|
|
2515
|
-
if (slot === 'left') return { x: MX, y: st, w: CW * (P.leftW || 0.55), h: Math.max(1.2, sb - st) };
|
|
2516
|
-
return { x: MX + CW * (P.rightX != null ? P.rightX : 0.585), y: st, w: CW * (P.rightW || 0.415), h: Math.max(1.2, sb - st) };
|
|
2517
|
-
}
|
|
2518
|
-
if (type === 'twocol' && (slot === 'primary' || slot === 'left' || slot === 'right')) {
|
|
2519
|
-
var gap = P.colGap || 0.5, colW = (CW - gap) / 2;
|
|
2520
|
-
var tt = opts.top != null ? opts.top : topDefault;
|
|
2521
|
-
var tb = opts.bottom != null ? opts.bottom : bottomDefault;
|
|
2522
|
-
if (slot === 'right') return { x: MX + colW + gap, y: tt, w: colW, h: Math.max(1, tb - tt) };
|
|
2523
|
-
return { x: MX, y: tt, w: colW, h: Math.max(1, tb - tt) };
|
|
2524
|
-
}
|
|
2525
|
-
if ((type === 'diagram' || type === 'lane') && slot === 'primary') {
|
|
2526
|
-
var arch = PT.arch || {};
|
|
2527
|
-
var dy = (opts.mode === 'architecture' && arch.fullStartY != null) ? arch.fullStartY : (P.bodyStartY || 2.9);
|
|
2528
|
-
var db = (opts.mode === 'architecture' && arch.fullEndY != null) ? arch.fullEndY
|
|
2529
|
-
: (opts.bottom != null ? opts.bottom : bottomDefault);
|
|
2530
|
-
return { x: MX, y: dy, w: CW, h: Math.max(1.2, db - dy) };
|
|
2531
|
-
}
|
|
2532
|
-
if (type === 'kpi' && (slot === 'hero' || slot === 'primary')) {
|
|
2533
|
-
return { x: MX, y: P.heroY || 2.9, w: CW * (P.heroW || 0.52), h: P.heroH || 1.6,
|
|
2534
|
-
dividerX: P.dividerX, metricY0: P.metricY0, metricRowH: P.metricRowH };
|
|
2535
|
-
}
|
|
2536
|
-
if (type === 'comparison' && (slot === 'left' || slot === 'primary' || slot === 'right')) {
|
|
2537
|
-
var cg = P.panelGap || 0.4, pw = (CW - cg) / 2;
|
|
2538
|
-
if (slot === 'right') return { x: MX + pw + cg, y: P.panelY || 2.55, w: pw, h: P.panelH || 3.4 };
|
|
2539
|
-
return { x: MX, y: P.panelY || 2.55, w: pw, h: P.panelH || 3.4 };
|
|
2540
|
-
}
|
|
2541
|
-
if (type === 'quote' && slot === 'primary') {
|
|
2542
|
-
return { x: MX + 1.05, y: P.textY || 2.7, w: CW - 2.1, h: P.textH || 2.3 };
|
|
2543
|
-
}
|
|
2544
|
-
if (type === 'heatmap' && slot === 'primary') {
|
|
2545
|
-
var hy = P.y || 2.6;
|
|
2546
|
-
return { x: MX, y: hy, w: CW, h: Math.max(1, (opts.bottom != null ? opts.bottom : bottomDefault) - hy) };
|
|
2547
|
-
}
|
|
2548
|
-
if (type === 'bullet' && slot === 'primary') {
|
|
2549
|
-
var uy = P.y || 2.6;
|
|
2550
|
-
return { x: MX, y: uy, w: CW, h: Math.max(1, (opts.bottom != null ? opts.bottom : bottomDefault) - uy) };
|
|
2551
|
-
}
|
|
2552
|
-
if (type === 'pyramid' && slot === 'primary') {
|
|
2553
|
-
var py2 = P.y || 2.35;
|
|
2554
|
-
return { x: MX, y: py2, w: CW, h: Math.max(1, (opts.bottom != null ? opts.bottom : bottomDefault) - py2) };
|
|
2555
|
-
}
|
|
2556
|
-
if (type === 'steps' && slot === 'primary') {
|
|
2557
|
-
return { x: MX, y: P.y || 3.05, w: CW, h: P.barH || 1.35 };
|
|
2558
|
-
}
|
|
2559
|
-
if (type === 'cards' && slot === 'primary') {
|
|
2560
|
-
var cy2 = P.startY || 3.0;
|
|
2561
|
-
return { x: MX, y: cy2, w: CW, h: Math.max(1, (opts.bottom != null ? opts.bottom : bottomDefault) - cy2) };
|
|
2562
|
-
}
|
|
2563
|
-
if (type === 'timeline' && slot === 'primary') {
|
|
2564
|
-
return { x: MX, y: P.labelY || 2.95, w: CW, h: Math.max(1, bottomDefault - (P.labelY || 2.95)) };
|
|
2565
|
-
}
|
|
2566
|
-
if (type === 'image' && slot === 'primary') {
|
|
2567
|
-
var iy2 = Math.max(P.y || 2.3, opts.top || 0);
|
|
2568
|
-
return { x: MX, y: iy2, w: CW, h: Math.max(1.2, Math.min(P.h || 4.2, (opts.bottom != null ? opts.bottom : bottomDefault) - iy2)) };
|
|
2569
|
-
}
|
|
2570
|
-
if ((type === 'halftable') && (slot === 'left' || slot === 'primary')) {
|
|
2571
|
-
var rs = PT.research || {};
|
|
2572
|
-
return { x: MX, y: topDefault, w: CW * (rs.halfTableW || 0.52), h: Math.max(1, bottomDefault - topDefault) };
|
|
2573
|
-
}
|
|
2574
|
-
if ((type === 'halftable') && (slot === 'right' || slot === 'secondary')) {
|
|
2575
|
-
var rs2 = PT.research || {};
|
|
2576
|
-
return { x: MX + CW * (rs2.halfChartX || 0.57), y: topDefault, w: CW * (rs2.halfChartW || 0.43), h: Math.max(1, bottomDefault - topDefault) };
|
|
2577
|
-
}
|
|
2578
|
-
if (type === 'matrix' && slot === 'primary') {
|
|
2579
|
-
var rm = PT.research || {};
|
|
2580
|
-
var my = rm.matrixY || 2.3;
|
|
2581
|
-
return { x: MX, y: my, w: CW, h: Math.max(1, (opts.bottom != null ? opts.bottom : bottomDefault) - my) };
|
|
2582
|
-
}
|
|
2583
|
-
var INFO = { sankey:1, treemap:1, boxplot:1, network:1, marimekko:1, streamgraph:1 };
|
|
2584
|
-
if (INFO[type] && slot === 'primary') {
|
|
2585
|
-
var iy3 = P.y || topDefault;
|
|
2586
|
-
var ih3 = Math.max(1.2, Math.min(P.h || 4.1, (opts.bottom != null ? opts.bottom : bottomDefault) - iy3));
|
|
2587
|
-
return { x: MX, y: iy3, w: CW, h: ih3 };
|
|
2588
|
-
}
|
|
2589
|
-
return { x: MX, y: topDefault, w: CW, h: Math.max(0.8, bottomDefault - topDefault) };
|
|
2590
|
-
}
|
|
2591
|
-
|
|
2592
|
-
/* __TOPPPT_CONSTANTS_END__ */
|
|
2593
|
-
|
|
2594
|
-
/* __TOPPPT_SCHEMA_START__ */
|
|
2595
|
-
/* ── 由 scripts/sync_runtime.py 从 scripts/model-schema.json 注入 · 禁止手改 ── */
|
|
2596
|
-
/* DSL schema 单源:浏览器端 validateModel 与 scripts/extract_model.py 消费同一份定义 */
|
|
2597
|
-
var MODEL_SCHEMA = {"version":"9.1","modes":["presentation","research","architecture"],"model":{"required":["title","sections:array","closing.title","closing.points:array"],"optional":["mode","style","theme","subtitle","meta","agenda:array"],"agendaMin":{"presentation":3,"research":3,"architecture":0},"sectionsMin":1,"sectionsRecommended":3,"agendaComfortMax":16},"commonSectionFields":["eyebrow","title","lead","soWhat","footnote","flags","image","exhibitNo","layoutPreset"],"flagsHint":"flags = 待核实/待二次修改条目字符串数组(如 '2027 增速 xx%(口径未定)');渲染为 accent 强调色标注行,提示用户核对。","imageHint":"image = 素材图片/配图占位对象。三选一必填:src(用户图,data: 内联或相对路径,零外链铁律禁 http(s))|items(多图版式:grid/compare/wall,元素 {src?, alt?, caption?, placeholder?})|placeholder:true(无图时出配图占位,原生形状渲染、pictures 不增)。其余可选:layout(full/half/bleed/grid/compare/wall,见 layout-constants.json imageSpec.layouts)、fit(cover 默认 / contain)、caption、alt、hint、ratio。","imageLayoutHint":"layout 与 HTML 版式对应:full=版心全宽图|half=左图右注(points 为右栏注解)|bleed=通栏出血|grid=多图网格(items 2/3/4/6 张)|compare=双图 A/B 对比(items 2 张)|wall=Logo 墙(items 3–12 张小图)。占位符标签文本由双引擎按 imageSpec 拼同一串,保证 A/B 通道逐页文本一致。","chartTypes":["bar","hbar","stack","stackline","line","dualline","area","donut","multidonut","pie","radar","scatter","bubble","waterfall","gauge","pareto","funnel","gantt","vsbar","progress","sparkline","slope","dumbbell","lollipop","dotplot","bulletchart","waffle","radialbar","rose","candlestick"],"chartTypesHint":"chart.type 可取值 = 本表(30 类:16 原生 + 14 形状还原)。其余 6 类复杂信息图(sankey/treemap/boxplot/network/marimekko/streamgraph)是**专属页型**而非 chart.type——它们要按列/段/节点等结构化载荷表达,故走 sections[].type。不变量:charts.registry.types == chartTypes ∪ 上述 6 类页型(sync_runtime.py 校验)。","chartDataTable":{"enum":["notes","inline","appendix","off"],"default":"notes","rule":"非原生图表(registry.pptx=shape)的 dataTable 不得为 off;缺失时按 registry 默认值补齐。"},"pageTypes":{"points":{"label":"要点列表页","modes":["presentation","research","architecture"],"required":["points:array"],"optional":["metrics:array"]},"metrics":{"label":"指标带页(4–6 个核心数字)","modes":["presentation","research"],"required":["metrics:array"]},"kpi":{"label":"大数指标页(hero 大数字 + 支撑指标行)","modes":["presentation","research"],"required":["hero:array"],"optional":["metrics:array"]},"table":{"label":"对比表页","modes":["presentation","research","architecture"],"required":["table.head:array","table.rows:array"],"optional":["table.colW:array"]},"timeline":{"label":"时间线 / 路线页","modes":["presentation","research"],"required":["phases:array"]},"steps":{"label":"步骤条页(N 步横排 + 箭头,可分组)","modes":["presentation","research","architecture"],"required":["steps:array"],"optional":["groups:array","note"]},"bar":{"label":"图表页(chart.type 见 chartTypes;native 通道走原生数据图表,shape 通道走高保真形状还原 + 数据表)","modes":["presentation","research"],"required":["chart.labels:array","chart.values:array"],"optional":["chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.start:array","chart.target:array","chart.dataTable","note"]},"donut":{"label":"环形图页(原生 pie 楔形 + 图例)","modes":["presentation","research"],"required":["chart.labels:array","chart.values:array"],"optional":["chart.unit","chart.centerLabel","chart.colors:array","chart.dataTable","note"]},"heatmap":{"label":"热力矩阵页(行 × 列 + 强度色阶)","modes":["presentation","research"],"required":["rowHeads:array","colHeads:array","cells:array"],"optional":["unit","scaleLabel:array","note"]},"bullet":{"label":"达成对比页(实际 vs 目标条)","modes":["presentation","research"],"required":["items:array"],"optional":["unit","max","note"]},"pyramid":{"label":"金字塔页(层级递进,顶层最窄)","modes":["presentation","research","architecture"],"required":["levels:array"],"optional":["note"]},"image":{"label":"素材图片页(full/half/bleed/grid/compare/wall,效果与位置锁定;支持配图占位)","modes":["presentation","research","architecture"],"required":["anyof:image.src:str|image.items:array|image.placeholder"],"optional":["image.caption","image.alt","image.layout","image.fit","image.ratio","image.hint","points:array","note"]},"cards":{"label":"卡片网格页(cards 元素须为 {title, points:[[k,v]…]},不接受元组)","modes":["presentation","research"],"required":["cards:array"],"optional":["columns"]},"split":{"label":"双区组合页(left / right 各可为 要点|图表|表格|图片:type = points(默认)| table | image | 图表类型名;图表/表格字段直接挂在对应侧)","modes":["presentation","research"],"required":["anyof:left.points:array|left.type:str"],"optional":["left.cap","left.image","left.labels:array","left.values:array","left.series:array","left.points:array","left.max","left.unit","left.colors:array","left.dataTable","left.head:array","left.rows:array","left.colW:array","right.type","right.cap","right.image","right.labels:array","right.values:array","right.series:array","right.points:array","right.max","right.unit","right.colors:array","right.dataTable","right.head:array","right.rows:array","right.colW:array"]},"comparison":{"label":"对比页(左右双栏 + 可选结论条)","modes":["presentation","research"],"required":["left.title","left.points:array","right.title","right.points:array"],"optional":["verdict"]},"quote":{"label":"引用 / 金句页(深色全幅)","modes":["presentation","research"],"required":["quote","author"],"optional":["context"]},"diagram":{"label":"分层架构页(architecture 模式走全幅几何)","modes":["presentation","research","architecture"],"required":["layers:array"],"optional":["legend:array"]},"exhibit":{"label":"Exhibit 编号图表页(research R2)","modes":["research"],"required":["chart.labels:array","chart.values:array"],"optional":["exhibitNo","chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.dataTable","note"]},"twocol":{"label":"双栏论证页(research R1)","modes":["research"],"required":["paragraphs:array"]},"threecol":{"label":"三栏证据页(research R6)","modes":["research"],"required":["paragraphs:array"]},"halftable":{"label":"半表半图页(research R7)","modes":["research"],"required":["table.head:array","table.rows:array","chart.labels:array","chart.values:array"],"optional":["table.colW:array","chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.dataTable","note"]},"matrix":{"label":"矩阵图页(research R8)","modes":["research"],"required":["rowHeads:array","colHeads:array","cells:array"]},"lane":{"label":"泳道页(architecture A2)","modes":["architecture"],"required":["lanes:array"]},"sankey":{"label":"桑基图页(节点-流带,流向与流量)","modes":["research","architecture"],"required":["flows:array"],"optional":["unit","chart.dataTable","note"]},"treemap":{"label":"树图页(面积编码的层级构成)","modes":["research","architecture"],"required":["items:array"],"optional":["unit","chart.dataTable","note"]},"boxplot":{"label":"箱线图页(分布对比:min/q1/median/q3/max)","modes":["research","architecture"],"required":["groups:array"],"optional":["unit","chart.dataTable","note"]},"network":{"label":"关系网络页(节点-边拓扑)","modes":["research","architecture"],"required":["nodes:array","edges:array"],"optional":["chart.dataTable","note"]},"marimekko":{"label":"马赛克图页(列宽 × 列高双重编码)","modes":["research","architecture"],"required":["cols:array","cells:array"],"optional":["unit","chart.dataTable","note"]},"streamgraph":{"label":"流带图页(时间上的构成演变)","modes":["research","architecture"],"required":["series:array"],"optional":["labels:array","chart.dataTable","note"]}}};
|
|
2598
|
-
/* __TOPPPT_SCHEMA_END__ */
|
|
2599
|
-
|
|
2600
|
-
var _mode = 'presentation'; /* 当前模式(slidesOf 里按 model.mode 设置) */
|
|
2601
|
-
/* 三模式独立排版比例尺:调用点给 presentation 基准 pt → 取最接近的 TS_BASE 角色 →
|
|
2602
|
-
查 MTS[mode][role]。research 是咨询密排比例尺(正文 10.5pt),不再是简单 ×0.8。
|
|
2603
|
-
未知模式回落 presentation 档(MTS 与 mode 取值同源于 model-schema.json 的 modes)。 */
|
|
2604
|
-
function modeSize(base) {
|
|
2605
|
-
var mts = (typeof MTS !== 'undefined' && MTS) ? (MTS[_mode] || MTS.presentation) : null;
|
|
2606
|
-
if (!mts) return base;
|
|
2607
|
-
var best = null, bd = 1e9;
|
|
2608
|
-
for (var k in TS_BASE) {
|
|
2609
|
-
if (!TS_BASE.hasOwnProperty(k)) continue;
|
|
2610
|
-
var d = Math.abs(TS_BASE[k] - base);
|
|
2611
|
-
if (d < bd) { bd = d; best = mts[k]; }
|
|
2612
|
-
}
|
|
2613
|
-
/* 钳制与取整必须与 build_pptx.js 的 sz() 逐字一致——同一张表配不同后处理 = 两通道字号漂移 */
|
|
2614
|
-
return Math.max(8.5, Math.round((best == null ? base : best) * 2) / 2);
|
|
2615
|
-
}
|
|
2616
|
-
/* 编码色板:9 套风格**各有自己的** c1–c5(单源 styleDataColors / styleDataColorsDark)。
|
|
2617
|
-
与 HTML 侧的 .f-c1~c5 语义类同源;结构色纪律不受影响——本表只用于数据系列。 */
|
|
2618
|
-
function dataColors(style, theme) {
|
|
2619
|
-
var dark = (theme === 'dark');
|
|
2620
|
-
var map = (dark && typeof STYLE_DATA_COLORS_DARK !== 'undefined' && STYLE_DATA_COLORS_DARK)
|
|
2621
|
-
? STYLE_DATA_COLORS_DARK
|
|
2622
|
-
: (typeof STYLE_DATA_COLORS !== 'undefined' ? STYLE_DATA_COLORS : null);
|
|
2623
|
-
if (!map) return null;
|
|
2624
|
-
var c = map[style];
|
|
2625
|
-
return (c && c.length) ? c : null; // null = 用 S 的中性色(在调用处映射)
|
|
2626
|
-
}
|
|
2627
|
-
|
|
2628
|
-
/* ── 版式安全边界(与 build_pptx.js 同一常量单源) ── */
|
|
2629
|
-
var LAY = PT.layout || {};
|
|
2630
|
-
var CONTENT_TOP = (LAY.contentTop != null) ? LAY.contentTop : 2.5;
|
|
2631
|
-
var CONTENT_BOTTOM = (LAY.contentBottom != null) ? LAY.contentBottom : 6.9;
|
|
2632
|
-
|
|
2633
|
-
/* ── 自适应排版工具(与 build_pptx.js 同算法,保证两通道字号/换行一致) ── */
|
|
2634
|
-
function estLines(text, widthIn, fontPt) {
|
|
2635
|
-
var usable = Math.max(24, widthIn * 72 - 8), w = 0, lines = 1;
|
|
2636
|
-
var s = String(text == null ? '' : text);
|
|
2637
|
-
for (var i = 0; i < s.length; i++) {
|
|
2638
|
-
if (s[i] === '\n') { lines++; w = 0; continue; }
|
|
2639
|
-
var cw = /[\x00-\xff]/.test(s[i]) ? fontPt * 0.52 : fontPt;
|
|
2640
|
-
if (w + cw > usable) { lines++; w = cw; } else { w += cw; }
|
|
2641
|
-
}
|
|
2642
|
-
return lines;
|
|
2643
|
-
}
|
|
2644
|
-
function estTextH(text, widthIn, fontPt, lineFactor) {
|
|
2645
|
-
return estLines(text, widthIn, fontPt) * fontPt * (lineFactor || 1.45) / 72;
|
|
2646
|
-
}
|
|
2647
|
-
var FONT_LADDER = [15, 14, 13.5, 13, 12.5, 12, 11.5, 11, 10.5, 10, 9.5, 9, 8.5];
|
|
2648
|
-
/* 有限缩字号(v8.2,与 build_pptx.js 同策略):先优化内容/组合/拆页,最后才缩字号。
|
|
2649
|
-
maxShrinkSteps 限制下探步数;floorPt 按模式绝对下限。 */
|
|
2650
|
-
function modeFloorPtA(mode) {
|
|
2651
|
-
var floors = (typeof CONT !== 'undefined' && CONT && CONT.fontShrink && CONT.fontShrink.floorPt) ||
|
|
2652
|
-
{ presentation: 10, research: 9, architecture: 10 };
|
|
2653
|
-
var m = String(mode || 'presentation');
|
|
2654
|
-
return (floors[m] != null) ? floors[m] : 10;
|
|
2655
|
-
}
|
|
2656
|
-
function fitFont(items, widthIn, availIn, opts) {
|
|
2657
|
-
opts = opts || {};
|
|
2658
|
-
var max = (opts.max == null) ? 99 : opts.max, lf = opts.lineFactor || 1.45;
|
|
2659
|
-
var gapF = (opts.gapFactor == null) ? 0.5 : opts.gapFactor;
|
|
2660
|
-
var mode = (typeof CONTENT !== 'undefined' && CONTENT && CONTENT.mode) ||
|
|
2661
|
-
(typeof TOPPPT_MODE !== 'undefined' ? TOPPPT_MODE : 'presentation');
|
|
2662
|
-
var floor = (opts.floor != null) ? opts.floor : modeFloorPtA(mode);
|
|
2663
|
-
var maxSteps = (opts.maxShrinkSteps != null) ? opts.maxShrinkSteps
|
|
2664
|
-
: ((typeof CONT !== 'undefined' && CONT && CONT.fontShrink && CONT.fontShrink.maxShrinkSteps != null)
|
|
2665
|
-
? CONT.fontShrink.maxShrinkSteps : 4);
|
|
2666
|
-
var baseLadder = (typeof CONT !== 'undefined' && CONT && CONT.fontShrink && CONT.fontShrink.ladder) || FONT_LADDER;
|
|
2667
|
-
var raw = baseLadder.filter(function (v) { return v <= max && v >= floor; });
|
|
2668
|
-
var needH = function (f) {
|
|
2669
|
-
var h = 0;
|
|
2670
|
-
for (var i = 0; i < items.length; i++) h += estTextH(items[i], widthIn, f, lf) + f * gapF / 72;
|
|
2671
|
-
return h + (opts.pad || 0);
|
|
2672
|
-
};
|
|
2673
|
-
for (var li = 0; li < raw.length; li++) {
|
|
2674
|
-
if (needH(raw[li]) <= availIn) return raw[li];
|
|
2675
|
-
}
|
|
2676
|
-
var preferred = raw.length ? raw[0] : floor;
|
|
2677
|
-
var prefIdx = baseLadder.indexOf(preferred);
|
|
2678
|
-
var limited = baseLadder.slice(prefIdx, prefIdx + 1 + maxSteps)
|
|
2679
|
-
.filter(function (v) { return v >= floor && v <= max; });
|
|
2680
|
-
for (var lj = 0; lj < limited.length; lj++) {
|
|
2681
|
-
if (needH(limited[lj]) <= availIn) return limited[lj];
|
|
2682
|
-
}
|
|
2683
|
-
if (limited.length) return limited[limited.length - 1];
|
|
2684
|
-
return floor;
|
|
2685
|
-
}
|
|
2686
|
-
function cols(total, n, gap) {
|
|
2687
|
-
var w = (total - (n - 1) * (gap || 0)) / Math.max(1, n);
|
|
2688
|
-
return { w: w, step: w + (gap || 0) };
|
|
2689
|
-
}
|
|
2690
|
-
/* 行高收敛:与 build_pptx.js 同算法(宁可行高更小也不越界) */
|
|
2691
|
-
function fitRowH(avail, n, pref, floor) {
|
|
2692
|
-
var lo = (floor == null) ? 0.22 : floor;
|
|
2693
|
-
return Math.max(lo, Math.min(pref, avail / Math.max(1, n)));
|
|
2694
|
-
}
|
|
2695
|
-
function chartBottom(hasSoWhat, hasFootnote) {
|
|
2696
|
-
if (hasFootnote) return PT.exhibit.footnoteY - 0.12;
|
|
2697
|
-
if (hasSoWhat) return PT.exhibit.soWhatY - 0.12;
|
|
2698
|
-
return CONTENT_BOTTOM;
|
|
2699
|
-
}
|
|
2700
|
-
|
|
2701
|
-
/* ── XML 工具 ── */
|
|
2702
|
-
function esc(s) {
|
|
2703
|
-
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
2704
|
-
.replace(/"/g, '"').replace(/'/g, ''');
|
|
2705
|
-
}
|
|
2706
|
-
function E(v) { return Math.round(v * EMU); }
|
|
2707
|
-
function solid(hex) { return '<a:solidFill><a:srgbClr val="' + hex + '"/></a:solidFill>'; }
|
|
2708
|
-
|
|
2709
|
-
var _id = 10;
|
|
2710
|
-
function nid() { return ++_id; }
|
|
2711
|
-
|
|
2712
|
-
/* 文本框:paras = [[{t,b,sz,col,font,spc}…]…](每个内层数组=一段,可混排多 run)
|
|
2713
|
-
opts.lineSpacing / opts.spaceBefore 为基准 pt(与 build_pptx.js 的 lineSpacing/paraSpaceBefore 同源),
|
|
2714
|
-
序列化时统一过 modeSize 映射,保证两通道行距一致。 */
|
|
2715
|
-
function txSp(x, y, w, h, paras, opts) {
|
|
2716
|
-
opts = opts || {};
|
|
2717
|
-
var pPr = '';
|
|
2718
|
-
if (opts.lineSpacing) pPr += '<a:lnSpc><a:spcPts val="' + Math.round(modeSize(opts.lineSpacing) * 100) + '"/></a:lnSpc>';
|
|
2719
|
-
if (opts.spaceBefore) pPr += '<a:spcBef><a:spcPts val="' + Math.round(modeSize(opts.spaceBefore) * 100) + '"/></a:spcBef>';
|
|
2720
|
-
var pPrXml = (opts.align || pPr)
|
|
2721
|
-
? '<a:pPr' + (opts.align ? ' algn="' + opts.align + '"' : '') + '>' + pPr + '</a:pPr>'
|
|
2722
|
-
: '';
|
|
2723
|
-
var body = paras.map(function (runs) {
|
|
2724
|
-
var rs = runs.map(function (r) {
|
|
2725
|
-
var pr = '<a:rPr lang="zh-CN" sz="' + Math.round(modeSize(r.sz) * 100) + '"' +
|
|
2726
|
-
(r.b ? ' b="1"' : '') + (r.spc ? ' spc="' + r.spc + '"' : '') + '>' +
|
|
2727
|
-
solid(r.col) +
|
|
2728
|
-
'<a:latin typeface="' + esc(r.font) + '"/><a:ea typeface="' + esc(r.font) + '"/></a:rPr>';
|
|
2729
|
-
return '<a:r>' + pr + '<a:t>' + esc(r.t) + '</a:t></a:r>';
|
|
2730
|
-
}).join('');
|
|
2731
|
-
return '<a:p>' + pPrXml + rs + '</a:p>';
|
|
2732
|
-
}).join('');
|
|
2733
|
-
var fill = opts.fill ? solid(opts.fill) : '<a:noFill/>';
|
|
2734
|
-
return '<p:sp><p:nvSpPr><p:cNvPr id="' + nid() + '" name="tx"/><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr>' +
|
|
2735
|
-
'<p:spPr><a:xfrm><a:off x="' + E(x) + '" y="' + E(y) + '"/><a:ext cx="' + E(w) + '" cy="' + E(h) + '"/></a:xfrm>' +
|
|
2736
|
-
'<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>' + fill + '<a:ln><a:noFill/></a:ln></p:spPr>' +
|
|
2737
|
-
'<p:txBody><a:bodyPr wrap="square" anchor="' + (opts.anchor || 't') + '"><a:normAutofit/></a:bodyPr><a:lstStyle/>' +
|
|
2738
|
-
body + '</p:txBody></p:sp>';
|
|
2739
|
-
}
|
|
2740
|
-
|
|
2741
|
-
/* 形状:rect / roundRect / ellipse / pie(opts.av = avLst 原始内联 XML,供 donut 楔形角度) */
|
|
2742
|
-
function shape(geom, x, y, w, h, fillHex, opts) {
|
|
2743
|
-
opts = opts || {};
|
|
2744
|
-
var av;
|
|
2745
|
-
if (opts.av) av = '<a:avLst>' + opts.av + '</a:avLst>';
|
|
2746
|
-
else if (geom === 'roundRect') av = '<a:avLst><a:gd name="adj" fmla="val ' + (opts.adj || 8000) + '"/></a:avLst>';
|
|
2747
|
-
else av = '<a:avLst/>';
|
|
2748
|
-
var ln = opts.line
|
|
2749
|
-
? '<a:ln w="9525">' + (opts.dash ? '<a:prstDash val="dash"/>' : '') + solid(opts.line) + '</a:ln>'
|
|
2750
|
-
: '<a:ln><a:noFill/></a:ln>';
|
|
2751
|
-
return '<p:sp><p:nvSpPr><p:cNvPr id="' + nid() + '" name="' + (opts.name || 'sh') + '"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr>' +
|
|
2752
|
-
'<p:spPr><a:xfrm><a:off x="' + E(x) + '" y="' + E(y) + '"/><a:ext cx="' + E(Math.max(w, 0.01)) + '" cy="' + E(Math.max(h, 0.01)) + '"/></a:xfrm>' +
|
|
2753
|
-
'<a:prstGeom prst="' + geom + '">' + av + '</a:prstGeom>' + solid(fillHex) + ln + '</p:spPr>' +
|
|
2754
|
-
'<p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:endParaRPr lang="zh-CN"/></a:p></p:txBody></p:sp>';
|
|
2755
|
-
}
|
|
2756
|
-
/* pie 楔形 avLst:角度(度)× 60000;donut 页型两通道共用同一角度拆分规则 */
|
|
2757
|
-
function pieAv(a1, a2) {
|
|
2758
|
-
return '<a:gd name="adj1" fmla="val ' + Math.round(a1 * 60000) + '"/>' +
|
|
2759
|
-
'<a:gd name="adj2" fmla="val ' + Math.round(a2 * 60000) + '"/>';
|
|
2760
|
-
}
|
|
2761
|
-
/* 环形图(donut 页型):原生 pie 楔形 + 背景色圆(挖孔)+ 中心文字 + 右侧图例。
|
|
2762
|
-
角度约定:12 点钟(270°)起顺时针;跨 360° 的楔形拆成两段(≤359.9 封顶)。 */
|
|
2763
|
-
function donutWedges(labels, values) {
|
|
2764
|
-
var total = 0;
|
|
2765
|
-
(values || []).forEach(function (v) { total += Math.max(0, v); });
|
|
2766
|
-
var segs = [], a = 270;
|
|
2767
|
-
(values || []).forEach(function (v, i) {
|
|
2768
|
-
var frac = total > 0 ? Math.max(0, v) / total : 0;
|
|
2769
|
-
var b = a + frac * 360;
|
|
2770
|
-
var s = a % 360, e = b % 360;
|
|
2771
|
-
if (frac <= 0) { a = b; return; }
|
|
2772
|
-
if (b - a >= 359.9) { segs.push([0, 359.9, i]); }
|
|
2773
|
-
else if (b <= 360) { segs.push([a, Math.min(b, 359.9), i]); }
|
|
2774
|
-
else { segs.push([a, 359.9, i]); if (e > 0.1) segs.push([0, e, i]); }
|
|
2775
|
-
a = b;
|
|
2776
|
-
});
|
|
2777
|
-
return segs;
|
|
2778
|
-
}
|
|
2779
|
-
function donutChart(sh, ec, S, dcols) {
|
|
2780
|
-
var d = PT.donut, labels = ec.labels || [], values = ec.values || [];
|
|
2781
|
-
var segs = donutWedges(labels, values);
|
|
2782
|
-
var bx = d.centerX - d.r, by = d.centerY - d.r, bw = d.r * 2;
|
|
2783
|
-
segs.forEach(function (sg) {
|
|
2784
|
-
sh.push(shape('pie', bx, by, bw, bw, dcols[sg[2] % dcols.length], { av: pieAv(sg[0], sg[1]) }));
|
|
2785
|
-
});
|
|
2786
|
-
var hb = d.holeR * 2;
|
|
2787
|
-
sh.push(shape('ellipse', d.centerX - d.holeR, d.centerY - d.holeR, hb, hb, S.bg));
|
|
2788
|
-
var total = 0; values.forEach(function (v) { total += Math.max(0, v); });
|
|
2789
|
-
var totalStr = (Math.round(total * 10) / 10) + (ec.unit || '');
|
|
2790
|
-
sh.push(txSp(d.centerX - d.holeR, d.centerY - 0.42, hb, 0.5,
|
|
2791
|
-
[[{ t: totalStr, sz: 20, b: 1, col: S.ink, font: S.fontDisplay }]], { align: 'ctr', anchor: 'ctr' }));
|
|
2792
|
-
sh.push(txSp(d.centerX - d.holeR, d.centerY + 0.08, hb, 0.35,
|
|
2793
|
-
[[{ t: ec.centerLabel || '合计', sz: 9.5, col: S.faint, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
|
|
2794
|
-
var n = Math.max(1, labels.length);
|
|
2795
|
-
var rowH = Math.min(d.legendRowH, 3.4 / n);
|
|
2796
|
-
var ly0 = d.centerY - (n * rowH) / 2 + 0.1;
|
|
2797
|
-
var sum2 = total || 1;
|
|
2798
|
-
labels.forEach(function (lb, i) {
|
|
2799
|
-
var v = values[i] || 0;
|
|
2800
|
-
var y = ly0 + i * rowH;
|
|
2801
|
-
sh.push(shape('ellipse', d.legendX, y + 0.08, 0.16, 0.16, dcols[i % dcols.length]));
|
|
2802
|
-
sh.push(txSp(d.legendX + 0.3, y, d.legendLabelW, rowH,
|
|
2803
|
-
[[{ t: lb, sz: 12, b: 1, col: S.ink, font: S.font }]], { anchor: 'ctr' }));
|
|
2804
|
-
sh.push(txSp(d.legendX + 3.8, y, d.legendValW, rowH,
|
|
2805
|
-
[[{ t: String(v) + (ec.unit || ''), sz: 12, b: 1, col: S.body, font: S.font }]], { align: 'r', anchor: 'ctr' }));
|
|
2806
|
-
sh.push(txSp(d.legendPctX, y, d.legendPctW, rowH,
|
|
2807
|
-
[[{ t: (Math.round(v / sum2 * 1000) / 10) + '%', sz: 10.5, col: S.faint, font: S.font }]], { align: 'r', anchor: 'ctr' }));
|
|
2808
|
-
});
|
|
2809
|
-
}
|
|
2810
|
-
function donutColors(style, S, theme) {
|
|
2811
|
-
var d = dataColors(style, theme);
|
|
2812
|
-
return d || [S.accent, S.faint, S.body, S.line];
|
|
2813
|
-
}
|
|
2814
|
-
|
|
2815
|
-
/* 表格(graphicFrame + a:tbl);rows[0]=表头;行高按可用高度自适应(与 B 通道同规则) */
|
|
2816
|
-
function table(x, y, w, colW, rows, S, opts) {
|
|
2817
|
-
opts = opts || {};
|
|
2818
|
-
var n = rows.length;
|
|
2819
|
-
var maxRowH = (opts.maxRowH == null) ? PT.table.rowH : opts.maxRowH;
|
|
2820
|
-
var minRowH = (opts.minRowH == null) ? PT.table.rowHMin : opts.minRowH;
|
|
2821
|
-
var availH = (opts.availH == null) ? (CONTENT_BOTTOM - y) : opts.availH;
|
|
2822
|
-
var rowH = Math.max(minRowH, Math.min(maxRowH, availH / Math.max(1, n)));
|
|
2823
|
-
var dense = rowH < 0.42;
|
|
2824
|
-
var total = colW.reduce(function (a, b) { return a + b; }, 0);
|
|
2825
|
-
var grid = colW.map(function (cw) { return '<a:gridCol w="' + E(cw) + '"/>'; }).join('');
|
|
2826
|
-
var trs = rows.map(function (row, ri) {
|
|
2827
|
-
var tcs = row.map(function (cell) {
|
|
2828
|
-
var isHead = ri === 0;
|
|
2829
|
-
var fz = isHead ? (dense ? 11 : 13) : (dense ? 10 : 12.5);
|
|
2830
|
-
return '<a:tc><a:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r>' +
|
|
2831
|
-
'<a:rPr lang="zh-CN" sz="' + Math.round(modeSize(fz) * 100) + '"' + (isHead ? ' b="1"' : '') + '>' +
|
|
2832
|
-
solid(isHead ? S.onAccent : S.body) +
|
|
2833
|
-
'<a:latin typeface="' + S.font + '"/><a:ea typeface="' + S.font + '"/></a:rPr>' +
|
|
2834
|
-
'<a:t>' + esc(cell) + '</a:t></a:r></a:p></a:txBody>' +
|
|
2835
|
-
'<a:tcPr marL="91440" marR="91440" marT="45720" marB="45720" anchor="ctr">' +
|
|
2836
|
-
solid(isHead ? S.accent : S.bg) + '</a:tcPr></a:tc>';
|
|
2837
|
-
}).join('');
|
|
2838
|
-
return '<a:tr h="' + E(rowH) + '">' + tcs + '</a:tr>';
|
|
2839
|
-
}).join('');
|
|
2840
|
-
/* OOXML 规范:p:graphicFrame 的 graphic 子元素属 DrawingML 命名空间(a:graphic,与 pptxgenjs 一致) */
|
|
2841
|
-
return '<p:graphicFrame><p:nvGraphicFramePr><p:cNvPr id="' + nid() + '" name="tbl"/>' +
|
|
2842
|
-
'<p:cNvGraphicFramePr><a:graphicFrameLocks noGrp="1"/></p:cNvGraphicFramePr><p:nvPr/></p:nvGraphicFramePr>' +
|
|
2843
|
-
'<p:xfrm><a:off x="' + E(x) + '" y="' + E(y) + '"/><a:ext cx="' + E(w) + '" cy="' + E(rowH * rows.length) + '"/></p:xfrm>' +
|
|
2844
|
-
'<a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/table">' +
|
|
2845
|
-
'<a:tbl><a:tblPr firstRow="1" bandRow="0"/><a:tblGrid>' + grid + '</a:tblGrid>' + trs + '</a:tbl>' +
|
|
2846
|
-
'</a:graphicData></a:graphic></p:graphicFrame>';
|
|
2847
|
-
}
|
|
2848
|
-
|
|
2849
|
-
/* so-what 结论条 / 页脚来源行(research 页通用可选件,与 HTML 同源版式;区域走布局 IR) */
|
|
2850
|
-
function soWhatBar(sh, text, S) {
|
|
2851
|
-
var R = (typeof regionOf === 'function') ? regionOf('exhibit', 'annotation')
|
|
2852
|
-
: { x: MX, y: PT.exhibit.soWhatY, w: CW, h: 0.62 };
|
|
2853
|
-
/* 与 B 通道同算法:长结论条有限缩字号,优先保容器不溢出 */
|
|
2854
|
-
var bodyW = Math.max(2.5, R.w - 0.44 - 1.35);
|
|
2855
|
-
var fzB = fitFont([String(text || '')], bodyW, R.h - 0.14, { max: 12.5, gapFactor: 0.2, maxShrinkSteps: 3 });
|
|
2856
|
-
sh.push(shape('rect', R.x, R.y, R.w, R.h, S.soft));
|
|
2857
|
-
sh.push(shape('rect', R.x, R.y, 0.06, R.h, S.accent));
|
|
2858
|
-
sh.push(txSp(R.x + 0.22, R.y + 0.06, R.w - 0.44, R.h - 0.12,
|
|
2859
|
-
[[{ t: 'SO WHAT ', sz: 11, b: 1, col: S.accent, font: S.font, spc: 150 },
|
|
2860
|
-
{ t: text, sz: fzB, col: S.ink, font: S.font }]], { anchor: 'ctr' }));
|
|
2861
|
-
}
|
|
2862
|
-
function footnoteLine(sh, text, S) {
|
|
2863
|
-
var R = (typeof regionOf === 'function') ? regionOf('exhibit', 'footnote')
|
|
2864
|
-
: { x: MX, y: PT.exhibit.footnoteY, w: CW - 1.2, h: 0.32 };
|
|
2865
|
-
sh.push(txSp(R.x, R.y, R.w, R.h,
|
|
2866
|
-
[[{ t: text, sz: 9.5, col: S.faint, font: S.font }]]));
|
|
2867
|
-
}
|
|
2868
|
-
/* 待核实清单条(accent 强调色标注 · 与 build_pptx.js / HTML .flagbar 同源) */
|
|
2869
|
-
function flagBar(sh, items, S, yTop) {
|
|
2870
|
-
var F = PT.flagbar || { hdH: 0.26, rowH: 0.24, maxRows: 3, gap: 0.12 };
|
|
2871
|
-
var list = (items || []).slice(0, F.maxRows).map(String);
|
|
2872
|
-
if (!list.length) return 0;
|
|
2873
|
-
var h = F.hdH + list.length * F.rowH + 0.06;
|
|
2874
|
-
var y = (yTop != null) ? yTop : (CONTENT_BOTTOM - h);
|
|
2875
|
-
sh.push(shape('rect', MX, y, CW, h, S.soft));
|
|
2876
|
-
sh.push(shape('rect', MX, y, 0.055, h, S.accent));
|
|
2877
|
-
sh.push(txSp(MX + 0.22, y + 0.02, CW - 0.44, F.hdH,
|
|
2878
|
-
[[{ t: '待核实 · 需二次确认', sz: 9.5, b: 1, col: S.accent, font: S.font, spc: 100 }]]));
|
|
2879
|
-
list.forEach(function (t, i) {
|
|
2880
|
-
sh.push(txSp(MX + 0.24, y + F.hdH + i * F.rowH, CW - 0.5, F.rowH,
|
|
2881
|
-
[[{ t: '· ' + t, sz: 9.5, col: S.body, font: S.font }]], { anchor: 'ctr' }));
|
|
2882
|
-
});
|
|
2883
|
-
return h;
|
|
2884
|
-
}
|
|
2885
|
-
/* 素材图片:预览引擎只产出 slide XML(无 media part)→ 以中性图位块表示;
|
|
2886
|
-
配图占位(placeholder)额外发射标签文本——标签串由 imageSpec 常量拼接,与 B 通道**同串**
|
|
2887
|
-
(cross_verify 逐页比对)。真实图片不发射任何文本,保证 A/B 文本集合一致。 */
|
|
2888
|
-
function imgPlaceholderLabel(layout, multi) {
|
|
2889
|
-
var IS = (typeof IMG !== 'undefined' && IMG) || {};
|
|
2890
|
-
var label = IS.placeholderLabel || '配图占位';
|
|
2891
|
-
if (multi) return label;
|
|
2892
|
-
var size = (IS.recommendedSizePx || {})[layout];
|
|
2893
|
-
if (!size) return label;
|
|
2894
|
-
return label + ' · ' + (IS.placeholderHintPrefix || '建议 ') + size + (IS.placeholderHintSuffix || 'px');
|
|
2895
|
-
}
|
|
2896
|
-
function addImageEl(sh, src, S, x, y, w, h, opts) {
|
|
2897
|
-
opts = opts || {};
|
|
2898
|
-
var ph = !!opts.placeholder || !src;
|
|
2899
|
-
/* name="img"/"imgph":预览模态据此把图位替换为真实图片(ui.js renderSlide),
|
|
2900
|
-
不影响文本集合(cross_verify 只比文本)。 */
|
|
2901
|
-
sh.push(shape('roundRect', x, y, w, h, S.surface,
|
|
2902
|
-
{ line: S.line, dash: ph, adj: 4000, name: ph ? 'imgph' : 'img' }));
|
|
2903
|
-
if (ph) {
|
|
2904
|
-
sh.push(txSp(x + 0.08, y + h / 2 - 0.22, Math.max(0.4, w - 0.16), 0.44,
|
|
2905
|
-
[[{ t: imgPlaceholderLabel(opts.layout || 'full', !!opts.multi), sz: 11, b: 1, col: S.faint, font: S.font }]],
|
|
2906
|
-
{ align: 'ctr', anchor: 'ctr' }));
|
|
2907
|
-
}
|
|
2908
|
-
}
|
|
2909
|
-
/* 版式比例(imageSpec.ratioDefault 或 image.ratio,形如 "3:1")→ 数值宽高比。
|
|
2910
|
-
双引擎同源:HTML 用同比例的 .media--r* 锁定类,PPTX 用本函数算高度,版式才一致。 */
|
|
2911
|
-
function imgRatio(layout, img) {
|
|
2912
|
-
var IS = (typeof IMG !== 'undefined' && IMG) || {};
|
|
2913
|
-
var raw = String((img && img.ratio) || (IS.ratioDefault || {})[layout] || '3:1');
|
|
2914
|
-
var m = raw.match(/^\s*(\d+(?:\.\d+)?)\s*[:/×x]\s*(\d+(?:\.\d+)?)/);
|
|
2915
|
-
if (!m) return 3;
|
|
2916
|
-
var a = parseFloat(m[1]), b = parseFloat(m[2]);
|
|
2917
|
-
return (a > 0 && b > 0) ? a / b : 3;
|
|
2918
|
-
}
|
|
2919
|
-
/* 图片版式族(与 HTML .media / .media-grid / .media-compare / .media-wall 同源):
|
|
2920
|
-
full 版心全宽|half 左图右注|bleed 通栏出血|grid 多图网格|compare 双图对比|wall Logo 墙。
|
|
2921
|
-
每版式按 imageSpec.ratioDefault 锁定比例;空间不足时按可用高度收敛并**垂直居中**
|
|
2922
|
-
(避免"图贴顶、下方大片留白")。返回图注基准 Y。占位符走原生形状,pictures 不增。 */
|
|
2923
|
-
function imageLayoutShapes(sh, S, layout, img, items, points, isPh, fit, y0, y1) {
|
|
2924
|
-
var IM = PT.image || {};
|
|
2925
|
-
var gap = IM.gridGap || 0.16;
|
|
2926
|
-
var iy = y0;
|
|
2927
|
-
var ih = Math.max(1.0, y1 - iy);
|
|
2928
|
-
var capY = iy + ih;
|
|
2929
|
-
var ratio = imgRatio(layout, img);
|
|
2930
|
-
function one(x, y, w, h, it, multi) {
|
|
2931
|
-
var ph = isPh || !(it && it.src);
|
|
2932
|
-
addImageEl(sh, (it && it.src) || '', S, x, y, w, h,
|
|
2933
|
-
{ placeholder: ph, layout: layout, multi: multi, fit: fit });
|
|
2934
|
-
}
|
|
2935
|
-
if (layout === 'grid' && items.length) {
|
|
2936
|
-
var n = Math.min(items.length, (IMG && IMG.maxPerPage) || 6);
|
|
2937
|
-
var colsN = (IM.gridCols || {})[String(n)] || Math.min(3, n);
|
|
2938
|
-
var rowsN = Math.max(1, Math.ceil(n / colsN));
|
|
2939
|
-
var capH = IM.gridCapH || 0.28;
|
|
2940
|
-
var cellW = (CW - (colsN - 1) * gap) / colsN;
|
|
2941
|
-
var capRows = rowsN * (capH + 0.04);
|
|
2942
|
-
var cellH = Math.min(cellW / ratio, Math.max(0.5, (ih - (rowsN - 1) * gap - capRows) / rowsN));
|
|
2943
|
-
var blockH = rowsN * cellH + (rowsN - 1) * gap + capRows;
|
|
2944
|
-
var gy = iy + Math.max(0, (ih - blockH) / 2);
|
|
2945
|
-
items.slice(0, n).forEach(function (it, i) {
|
|
2946
|
-
var cx = MX + (i % colsN) * (cellW + gap);
|
|
2947
|
-
var cy = gy + Math.floor(i / colsN) * (cellH + gap + capH + 0.04);
|
|
2948
|
-
var cap = (it && it.caption) || '';
|
|
2949
|
-
one(cx, cy, cellW, cellH, it, true);
|
|
2950
|
-
if (cap) sh.push(txSp(cx, cy + cellH + 0.02, cellW, capH,
|
|
2951
|
-
[[{ t: cap, sz: 10, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
2952
|
-
});
|
|
2953
|
-
capY = gy + blockH;
|
|
2954
|
-
} else if (layout === 'compare' && items.length >= 2) {
|
|
2955
|
-
var cgap = IM.compareGap || 0.3;
|
|
2956
|
-
var cw2 = (CW - cgap) / 2;
|
|
2957
|
-
var capH2 = 0.34;
|
|
2958
|
-
var ih2 = Math.min(cw2 / ratio, Math.max(0.5, ih - capH2));
|
|
2959
|
-
var cy2 = iy + Math.max(0, (ih - ih2 - capH2) / 2);
|
|
2960
|
-
[0, 1].forEach(function (i) {
|
|
2961
|
-
var it = items[i] || {};
|
|
2962
|
-
var cx2 = MX + i * (cw2 + cgap);
|
|
2963
|
-
one(cx2, cy2, cw2, ih2, it, true);
|
|
2964
|
-
var cap2 = it.caption || '';
|
|
2965
|
-
if (cap2) sh.push(txSp(cx2, cy2 + ih2 + 0.02, cw2, 0.3,
|
|
2966
|
-
[[{ t: cap2, sz: 10, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
2967
|
-
});
|
|
2968
|
-
capY = cy2 + ih2 + capH2;
|
|
2969
|
-
} else if (layout === 'wall' && items.length) {
|
|
2970
|
-
var wgap = IM.wallGap || 0.18;
|
|
2971
|
-
var wn = Math.min(items.length, 12);
|
|
2972
|
-
var rowsW = Math.max(1, Math.min(IM.wallMaxRows || 2, Math.ceil(wn / 6)));
|
|
2973
|
-
var perRow = Math.max(1, Math.ceil(wn / rowsW));
|
|
2974
|
-
var cellW2 = (CW - (perRow - 1) * wgap) / perRow;
|
|
2975
|
-
var cellH2 = Math.min(IM.wallCellH || 0.92, cellW2 / ratio,
|
|
2976
|
-
Math.max(0.4, (ih - (rowsW - 1) * wgap) / rowsW));
|
|
2977
|
-
var blockH2 = rowsW * cellH2 + (rowsW - 1) * wgap;
|
|
2978
|
-
var wy = iy + Math.max(0, (ih - blockH2) / 2);
|
|
2979
|
-
items.slice(0, wn).forEach(function (it, i) {
|
|
2980
|
-
var cx3 = MX + (i % perRow) * (cellW2 + wgap);
|
|
2981
|
-
var cy3 = wy + Math.floor(i / perRow) * (cellH2 + wgap);
|
|
2982
|
-
one(cx3, cy3, cellW2, cellH2, it, true);
|
|
2983
|
-
});
|
|
2984
|
-
capY = wy + blockH2;
|
|
2985
|
-
} else if (layout === 'half') {
|
|
2986
|
-
var iw = CW * (IM.halfW || 0.56);
|
|
2987
|
-
var ihh = Math.min(iw / ratio, ih);
|
|
2988
|
-
one(MX, iy, iw, ihh, img, false);
|
|
2989
|
-
var rxI = MX + iw + CW * (IM.halfGap || 0.06);
|
|
2990
|
-
var rwI = CW - iw - CW * (IM.halfGap || 0.06);
|
|
2991
|
-
var ptsI = points || [];
|
|
2992
|
-
var rowHI = fitRowH(ih - 0.1, ptsI.length, IM.noteRowH || 0.62, 0.3);
|
|
2993
|
-
var fzI = fitFont(ptsI.map(function (p) { return (p[0] || '') + (p[1] || ''); }), rwI - 0.3, ih,
|
|
2994
|
-
{ max: 14, gapFactor: 0.6 });
|
|
2995
|
-
ptsI.forEach(function (pt, i) {
|
|
2996
|
-
var lyI = iy + i * rowHI;
|
|
2997
|
-
sh.push(shape('rect', rxI, lyI + rowHI / 2 - 0.06, 0.12, 0.12, S.accent));
|
|
2998
|
-
sh.push(txSp(rxI + 0.26, lyI, rwI - 0.3, rowHI - 0.04,
|
|
2999
|
-
[[{ t: (pt[0] || '') + ' ', sz: fzI, b: 1, col: S.ink, font: S.font },
|
|
3000
|
-
{ t: pt[1] || '', sz: fzI - 1, col: S.body, font: S.font }]], { anchor: 't' }));
|
|
3001
|
-
});
|
|
3002
|
-
capY = iy + Math.max(ihh, ih * 0.5);
|
|
3003
|
-
} else if (layout === 'bleed') {
|
|
3004
|
-
var bh = Math.min(ih, PW / ratio);
|
|
3005
|
-
var by = iy + Math.max(0, (ih - bh) / 2);
|
|
3006
|
-
one(0, by, PW, bh, img, false);
|
|
3007
|
-
capY = by + bh;
|
|
3008
|
-
} else {
|
|
3009
|
-
var fh = Math.min(ih, CW / ratio);
|
|
3010
|
-
var fy = iy + Math.max(0, (ih - fh) / 2);
|
|
3011
|
-
one(MX, fy, CW, fh, img, false);
|
|
3012
|
-
capY = fy + fh;
|
|
3013
|
-
}
|
|
3014
|
-
return capY;
|
|
3015
|
-
}
|
|
3016
|
-
|
|
3017
|
-
/* ══ 图表形状渲染(预览/回归用近似版;交付通道 B 用原生可编辑数据图表) ══
|
|
3018
|
-
A/B 两通道的「文本集合」必须一致(cross_verify 逐页比对):
|
|
3019
|
-
类别标签 + 数值标签都渲染为文本,数值标签由 cross_verify 的数值 token 规则过滤。 */
|
|
3020
|
-
function valText(v, unit) { return String(v) + (unit || ''); }
|
|
3021
|
-
/* 横向条形(hbar / halftable / exhibit hbar) */
|
|
3022
|
-
function hbarRows(sh, ec, S, y0, y1, dcols, x0, w) {
|
|
3023
|
-
x0 = (x0 == null) ? MX : x0;
|
|
3024
|
-
w = (w == null) ? CW : w;
|
|
3025
|
-
var labels = ec.labels || [], values = ec.values || [];
|
|
3026
|
-
var n = Math.max(1, labels.length);
|
|
3027
|
-
var rowH = Math.min(PT.hbar.rowH, (y1 - y0) / n);
|
|
3028
|
-
var barH = Math.max(0.16, rowH * (PT.hbar.barRatio || 0.5));
|
|
3029
|
-
var labelW = PT.hbar.labelW, valW = PT.hbar.valW;
|
|
3030
|
-
var trackX = x0 + labelW + 0.1, trackW = w - labelW - valW - 0.2;
|
|
3031
|
-
var vmax = ec.max || Math.max.apply(null, values.concat([1]));
|
|
3032
|
-
var top = values.length ? Math.max.apply(null, values) : 0;
|
|
3033
|
-
labels.forEach(function (lb, i) {
|
|
3034
|
-
var v = values[i] || 0;
|
|
3035
|
-
var y = y0 + i * rowH + (rowH - barH) / 2;
|
|
3036
|
-
var hot = v === top;
|
|
3037
|
-
var dc = dcols ? dcols[i % 5] : null;
|
|
3038
|
-
sh.push(txSp(x0, y - 0.06, labelW - 0.05, barH + 0.12,
|
|
3039
|
-
[[{ t: lb, sz: 11, col: S.body, font: S.font }]], { anchor: 'ctr' }));
|
|
3040
|
-
sh.push(shape('rect', trackX, y, trackW, barH, S.surface, { line: S.line }));
|
|
3041
|
-
var bw2 = Math.max(0.02, v / vmax * trackW);
|
|
3042
|
-
sh.push(shape('rect', trackX, y, bw2, barH, dc || (hot ? S.accent : S.faint)));
|
|
3043
|
-
sh.push(txSp(trackX + trackW + 0.08, y - 0.06, valW, barH + 0.12,
|
|
3044
|
-
[[{ t: valText(v, ec.unit), sz: 11.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { anchor: 'ctr' }));
|
|
3045
|
-
});
|
|
3046
|
-
}
|
|
3047
|
-
/* 竖柱 / 堆叠柱 */
|
|
3048
|
-
function vbarShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
3049
|
-
var labels = ec.labels || [], values = ec.values || [];
|
|
3050
|
-
var nb = Math.max(1, labels.length);
|
|
3051
|
-
var baseY = y + h, plotW = w, step = plotW / nb;
|
|
3052
|
-
var bw = Math.min(1.1, step * 0.55);
|
|
3053
|
-
var vmax = ec.max || Math.max.apply(null, values.concat([1]));
|
|
3054
|
-
var top = values.length ? Math.max.apply(null, values) : 0;
|
|
3055
|
-
sh.push(shape('rect', x, baseY, plotW, 0.02, S.line));
|
|
3056
|
-
values.forEach(function (v, i) {
|
|
3057
|
-
var hh = Math.max(0.05, v / vmax * (h - 0.5));
|
|
3058
|
-
var bx = x + i * step + (step - bw) / 2;
|
|
3059
|
-
var hot = v === top;
|
|
3060
|
-
var dc = dcols ? dcols[i % 5] : null;
|
|
3061
|
-
sh.push(shape('roundRect', bx, baseY - hh, bw, hh, dc || (hot ? S.accent : S.faint), { adj: 6000 }));
|
|
3062
|
-
sh.push(txSp(bx - 0.3, baseY - hh - 0.4, bw + 0.6, 0.32,
|
|
3063
|
-
[[{ t: valText(v, ec.unit), sz: 12.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { align: 'ctr' }));
|
|
3064
|
-
sh.push(txSp(bx - 0.3, baseY + 0.08, bw + 0.6, 0.32,
|
|
3065
|
-
[[{ t: labels[i], sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
3066
|
-
});
|
|
3067
|
-
}
|
|
3068
|
-
/* 折线 / 双折线 / 面积 */
|
|
3069
|
-
function lineShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
3070
|
-
var labels = ec.labels || [];
|
|
3071
|
-
var series = (ec.series && ec.series.length) ? ec.series : [{ name: '数值', values: ec.values || [] }];
|
|
3072
|
-
var n = Math.max(1, labels.length);
|
|
3073
|
-
var all = [];
|
|
3074
|
-
series.forEach(function (se) { (se.values || []).forEach(function (v) { all.push(Number(v) || 0); }); });
|
|
3075
|
-
var vmax = ec.max || Math.max.apply(null, all.concat([1]));
|
|
3076
|
-
var plotH = h - 0.5, baseY = y + h;
|
|
3077
|
-
sh.push(shape('rect', x, baseY, w, 0.02, S.line));
|
|
3078
|
-
var stepX = n > 1 ? w / (n - 1) : 0;
|
|
3079
|
-
series.forEach(function (se, si) {
|
|
3080
|
-
var col = dcols ? dcols[si % 5] : (si === 0 ? S.accent : S.faint);
|
|
3081
|
-
var pts = (se.values || []).map(function (v, i) {
|
|
3082
|
-
var px = x + (n > 1 ? i * stepX : w / 2);
|
|
3083
|
-
var py = baseY - Math.max(0.02, (Number(v) || 0) / vmax * plotH);
|
|
3084
|
-
return [px, py];
|
|
3085
|
-
});
|
|
3086
|
-
if (!pts.length) return;
|
|
3087
|
-
for (var i = 0; i < pts.length - 1; i++) {
|
|
3088
|
-
sh.push(lineSeg(pts[i][0], pts[i][1], pts[i + 1][0], pts[i + 1][1], col, 2.5));
|
|
3089
|
-
}
|
|
3090
|
-
pts.forEach(function (p) {
|
|
3091
|
-
sh.push(shape('ellipse', p[0] - 0.055, p[1] - 0.055, 0.11, 0.11, col));
|
|
3092
|
-
});
|
|
3093
|
-
});
|
|
3094
|
-
labels.forEach(function (lb, i) {
|
|
3095
|
-
var px = x + (n > 1 ? i * stepX : w / 2);
|
|
3096
|
-
sh.push(txSp(px - 0.5, baseY + 0.08, 1.0, 0.32,
|
|
3097
|
-
[[{ t: lb, sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
3098
|
-
});
|
|
3099
|
-
}
|
|
3100
|
-
/* 直线段(用细长矩形近似,避免自定义几何——两通道渲染一致) */
|
|
3101
|
-
function lineSeg(x1, y1, x2, y2, hex, thick) {
|
|
3102
|
-
var dx = x2 - x1, dy = y2 - y1;
|
|
3103
|
-
var len = Math.sqrt(dx * dx + dy * dy);
|
|
3104
|
-
var ang = Math.atan2(dy, dx) * 180 / Math.PI;
|
|
3105
|
-
var cx = (x1 + x2) / 2, cy = (y1 + y2) / 2;
|
|
3106
|
-
var t = thick / 72;
|
|
3107
|
-
var rot = '<a:xfrm rot="' + Math.round(ang * 60000) + '">';
|
|
3108
|
-
return '<p:sp><p:nvSpPr><p:cNvPr id="' + nid() + '" name="ln"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr>' +
|
|
3109
|
-
'<p:spPr>' + rot + '<a:off x="' + E(cx - len / 2) + '" y="' + E(cy - t / 2) + '"/>' +
|
|
3110
|
-
'<a:ext cx="' + E(len) + '" cy="' + E(t) + '"/></a:xfrm>' +
|
|
3111
|
-
'<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>' + solid(hex) + '<a:ln><a:noFill/></a:ln></p:spPr>' +
|
|
3112
|
-
'<p:txBody><a:bodyPr/><a:lstStyle/><a:p/></p:txBody></p:sp>';
|
|
3113
|
-
}
|
|
3114
|
-
/* 散点 / 气泡(A 通道近似):只画点、不产文本——与 B 通道 XY 图表(类别为数值,过滤后为空)一致 */
|
|
3115
|
-
function xyDots(sh, ec, S, x, y, w, h) {
|
|
3116
|
-
var values = (ec.values || []).map(Number);
|
|
3117
|
-
var pts = (ec.points && ec.points.length) ? ec.points : null;
|
|
3118
|
-
var n = pts ? pts.length : Math.max(1, values.length);
|
|
3119
|
-
var vmax = ec.max || Math.max.apply(null, values.concat([1]));
|
|
3120
|
-
var baseY = y + h, plotH = Math.max(0.3, h - 0.3);
|
|
3121
|
-
var stepX = n > 1 ? w / (n - 1) : 0;
|
|
3122
|
-
sh.push(shape('rect', x, baseY, w, 0.02, S.line));
|
|
3123
|
-
for (var i = 0; i < n; i++) {
|
|
3124
|
-
var px = pts ? x + (Number(pts[i][0]) || 0) / vmax * w : x + i * stepX;
|
|
3125
|
-
var py = pts ? baseY - (Number(pts[i][1]) || 0) / vmax * plotH : baseY - (values[i] || 0) / vmax * plotH;
|
|
3126
|
-
sh.push(shape('ellipse', px - 0.07, py - 0.07, 0.14, 0.14, S.accent));
|
|
3127
|
-
}
|
|
3128
|
-
}
|
|
3129
|
-
/* 仪表盘(A 通道近似):270° 弧段 + 数值(B 通道 gauge 的类别为数值,过滤后两端一致,故此处不产标签文本) */
|
|
3130
|
-
function gaugeApprox(sh, ec, S, x, y, w, h) {
|
|
3131
|
-
var v = Number((ec.values || [0])[0]) || 0;
|
|
3132
|
-
var max = Number(ec.max) || 100;
|
|
3133
|
-
var frac = Math.max(0, Math.min(1, v / max));
|
|
3134
|
-
var r = Math.min(w, h) / 2 - 0.12;
|
|
3135
|
-
var cx = x + w / 2, cy = y + h / 2;
|
|
3136
|
-
var steps = 40, startA = Math.PI * 0.75, sweep = Math.PI * 1.5;
|
|
3137
|
-
var px = null, py = null;
|
|
3138
|
-
for (var i = 0; i <= steps; i++) {
|
|
3139
|
-
var a = startA + sweep * (i / steps);
|
|
3140
|
-
var qx = cx + r * Math.cos(a), qy = cy + r * Math.sin(a);
|
|
3141
|
-
if (px !== null) sh.push(lineSeg(px, py, qx, qy, (i / steps) <= frac ? S.accent : S.line, 5));
|
|
3142
|
-
px = qx; py = qy;
|
|
3143
|
-
}
|
|
3144
|
-
sh.push(txSp(x, cy - 0.2, w, 0.42,
|
|
3145
|
-
[[{ t: String(v) + (ec.unit || ''), sz: 18, b: 1, col: S.accent, font: S.fontDisplay }]], { align: 'ctr', anchor: 'ctr' }));
|
|
3146
|
-
}
|
|
3147
|
-
/* 饼 / 环(A 通道近似):横向占比条 + 图例(类别标签为文本,数值由 cross_verify 过滤) */
|
|
3148
|
-
function pieApprox(sh, ec, S, x, y, w, h, dcols) {
|
|
3149
|
-
var labels = ec.labels || [], values = (ec.values || []).map(Number);
|
|
3150
|
-
var total = values.reduce(function (a, b) { return a + b; }, 0) || 1;
|
|
3151
|
-
var barH = Math.max(0.32, Math.min(0.62, h * 0.22));
|
|
3152
|
-
var barY = y + h / 2 - barH / 2 - 0.2;
|
|
3153
|
-
var cx0 = x;
|
|
3154
|
-
labels.forEach(function (lb, i) {
|
|
3155
|
-
var bw = (values[i] || 0) / total * w;
|
|
3156
|
-
sh.push(shape('rect', cx0, barY, bw, barH,
|
|
3157
|
-
dcols ? dcols[i % dcols.length] : (i === 0 ? S.accent : S.faint)));
|
|
3158
|
-
cx0 += bw;
|
|
3159
|
-
});
|
|
3160
|
-
var lx = x;
|
|
3161
|
-
labels.forEach(function (lb, i) {
|
|
3162
|
-
sh.push(shape('ellipse', lx, barY + barH + 0.16, 0.1, 0.1,
|
|
3163
|
-
dcols ? dcols[i % dcols.length] : (i === 0 ? S.accent : S.faint)));
|
|
3164
|
-
sh.push(txSp(lx + 0.14, barY + barH + 0.12, 1.2, 0.24, [[{ t: lb, sz: 10.5, col: S.body, font: S.font }]]));
|
|
3165
|
-
sh.push(txSp(lx + 1.36, barY + barH + 0.12, 0.8, 0.24,
|
|
3166
|
-
[[{ t: String(values[i] != null ? values[i] : '') + (ec.unit || ''), sz: 10.5, b: 1, col: S.body, font: S.font }]]));
|
|
3167
|
-
lx += 2.3;
|
|
3168
|
-
});
|
|
3169
|
-
}
|
|
3170
|
-
/* 迷你折线(A 近似):无轴无标签,仅趋势线 + 末值(与 B 的 shapeSpark 同文本集) */
|
|
3171
|
-
function sparkApprox(sh, ec, S, x, y, w, h) {
|
|
3172
|
-
var vals = (ec.values || []).map(Number);
|
|
3173
|
-
var vmax = ec.max || Math.max.apply(null, vals.concat([1]));
|
|
3174
|
-
var n = vals.length;
|
|
3175
|
-
var stepX = n > 1 ? w / (n - 1) : 0;
|
|
3176
|
-
var baseY = y + h - 0.06, plotH = Math.max(0.2, h - 0.18);
|
|
3177
|
-
var px = 0, py = 0;
|
|
3178
|
-
vals.forEach(function (v, i) {
|
|
3179
|
-
var cx = x + i * stepX, cy = baseY - Math.max(0.02, (v / vmax) * plotH);
|
|
3180
|
-
if (i > 0) sh.push(lineSeg(px, py, cx, cy, S.accent, 2));
|
|
3181
|
-
px = cx; py = cy;
|
|
3182
|
-
});
|
|
3183
|
-
sh.push(txSp(x, y, w, 0.3, [[{ t: String(vals[n - 1] != null ? vals[n - 1] : '') + (ec.unit || ''),
|
|
3184
|
-
sz: 11.5, b: 1, col: S.accent, font: S.font }]], { align: 'r' }));
|
|
3185
|
-
}
|
|
3186
|
-
/* K 线(A 近似):实体 + 须,标签 + 收盘值(与 B 的 shapeCandlestick 同文本集) */
|
|
3187
|
-
function candleApprox(sh, ec, S, x, y, w, h) {
|
|
3188
|
-
var labels = ec.labels || [];
|
|
3189
|
-
var rows = (ec.values || []).filter(function (r) { return r && r.length >= 4; })
|
|
3190
|
-
.map(function (r) { return r.slice(0, 4).map(Number); });
|
|
3191
|
-
var n = Math.max(1, rows.length);
|
|
3192
|
-
var flat = [];
|
|
3193
|
-
rows.forEach(function (r) { r.forEach(function (v) { if (!isNaN(v)) flat.push(v); }); });
|
|
3194
|
-
var lo = flat.length ? Math.min.apply(null, flat) : 0;
|
|
3195
|
-
var hi = flat.length ? Math.max.apply(null, flat) : 1;
|
|
3196
|
-
var span = (hi - lo) || 1;
|
|
3197
|
-
var axisH = 0.26, plotH = Math.max(0.5, h - axisH - 0.1);
|
|
3198
|
-
var yFor = function (v) { return y + 0.06 + (1 - (v - lo) / span) * plotH; };
|
|
3199
|
-
var step = w / n, bw = Math.min(0.42, step * 0.5);
|
|
3200
|
-
sh.push(shape('rect', x, y + 0.06 + plotH, w, 0.015, S.line));
|
|
3201
|
-
rows.forEach(function (r, i) {
|
|
3202
|
-
var o = r[0], hiP = r[1], loP = r[2], cl = r[3];
|
|
3203
|
-
var cxp = x + step * i + step / 2;
|
|
3204
|
-
var up = cl >= o;
|
|
3205
|
-
sh.push(shape('rect', cxp - 0.011, yFor(hiP), 0.022, Math.max(0.02, yFor(loP) - yFor(hiP)), S.faint));
|
|
3206
|
-
sh.push(shape('rect', cxp - bw / 2, yFor(Math.max(o, cl)), bw,
|
|
3207
|
-
Math.max(0.04, Math.abs(yFor(cl) - yFor(o))), up ? S.accent : S.line));
|
|
3208
|
-
if (labels[i]) {
|
|
3209
|
-
sh.push(txSp(cxp - step / 2, y + plotH + axisH - 0.06, step, 0.24,
|
|
3210
|
-
[[{ t: String(labels[i]), sz: 10, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
3211
|
-
}
|
|
3212
|
-
sh.push(txSp(cxp - step / 2, yFor(hiP) - 0.26, step, 0.24,
|
|
3213
|
-
[[{ t: String(cl) + (ec.unit || ''), sz: 11.5, b: 1, col: up ? S.accent : S.body, font: S.font }]],
|
|
3214
|
-
{ align: 'r' }));
|
|
3215
|
-
});
|
|
3216
|
-
}
|
|
3217
|
-
/* 通用形状近似(与 build_pptx.js 的 shapeProportion 同文本:标签 + 数值) */
|
|
3218
|
-
function shapeProportion(sh, ec, S, x, y, w, h, dcols) {
|
|
3219
|
-
var labels = ec.labels || [], values = (ec.values || []).map(Number);
|
|
3220
|
-
var n = Math.max(1, labels.length);
|
|
3221
|
-
var labelW = Math.min(2.4, w * 0.24), valW = 0.9;
|
|
3222
|
-
var vmax = ec.max || Math.max.apply(null, values.concat([1]));
|
|
3223
|
-
var top = values.length ? Math.max.apply(null, values) : 0;
|
|
3224
|
-
var rowH = Math.max(0.22, Math.min(0.52, h / n));
|
|
3225
|
-
var trackX = x + labelW + 0.08, trackW = w - labelW - valW - 0.2;
|
|
3226
|
-
var barH = Math.max(0.12, rowH * 0.52);
|
|
3227
|
-
labels.forEach(function (lb, i) {
|
|
3228
|
-
var v = values[i] || 0, ry = y + i * rowH, hot = v === top;
|
|
3229
|
-
sh.push(txSp(x, ry, labelW - 0.06, rowH, [[{ t: lb, sz: 11, col: S.body, font: S.font }]], { anchor: 'ctr' }));
|
|
3230
|
-
sh.push(shape('rect', trackX, ry + (rowH - barH) / 2, trackW, barH, S.surface, { line: S.line }));
|
|
3231
|
-
sh.push(shape('rect', trackX, ry + (rowH - barH) / 2, Math.max(0.02, Math.abs(v) / vmax * trackW), barH,
|
|
3232
|
-
dcols ? dcols[i % dcols.length] : (hot ? S.accent : S.faint)));
|
|
3233
|
-
sh.push(txSp(trackX + trackW + 0.06, ry, valW, rowH,
|
|
3234
|
-
[[{ t: String(v) + (ec.unit || ''), sz: 11.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]],
|
|
3235
|
-
{ align: 'right', anchor: 'ctr' }));
|
|
3236
|
-
});
|
|
3237
|
-
}
|
|
3238
|
-
/* 数据表行(与 build_pptx.js 的 dataTableRows 同规则) */
|
|
3239
|
-
function dataTableRows(ec) {
|
|
3240
|
-
var labels = (ec.labels || []).map(String);
|
|
3241
|
-
var unit = ec.unit ? '(' + ec.unit + ')' : '';
|
|
3242
|
-
if (ec.series && ec.series.length) {
|
|
3243
|
-
var out = [['项目'].concat(ec.series.map(function (se, i) { return se.name || ('系列' + (i + 1)); }))];
|
|
3244
|
-
labels.forEach(function (lb, i) {
|
|
3245
|
-
out.push([lb].concat(ec.series.map(function (se) {
|
|
3246
|
-
var v = (se.values || [])[i]; return v == null ? '' : String(v);
|
|
3247
|
-
})));
|
|
3248
|
-
});
|
|
3249
|
-
return out;
|
|
3250
|
-
}
|
|
3251
|
-
var out2 = [['项目', '数值' + unit]];
|
|
3252
|
-
labels.forEach(function (lb, i) {
|
|
3253
|
-
var v = (ec.values || [])[i];
|
|
3254
|
-
out2.push([lb, v == null ? '' : String(v)]);
|
|
3255
|
-
});
|
|
3256
|
-
return out2;
|
|
3257
|
-
}
|
|
3258
|
-
/* 数据表策略(与 build_pptx.js 的 dataTableMode 同规则) */
|
|
3259
|
-
function dataTableModeOf(ec) {
|
|
3260
|
-
var t = (ec.type || 'bar').toLowerCase();
|
|
3261
|
-
var reg = (typeof REG !== 'undefined' && REG && REG[t]) ? REG[t] : null;
|
|
3262
|
-
var m = ec.dataTable || (reg && reg.dataTable) || 'notes';
|
|
3263
|
-
return m === 'appendix' ? 'inline' : m;
|
|
3264
|
-
}
|
|
3265
|
-
/* 图表 + 数据表组合(与 build_pptx.js 的 chartBlock 同策略:inline 在图表下方附原生表格) */
|
|
3266
|
-
function chartBlockShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
3267
|
-
var dm = dataTableModeOf(ec);
|
|
3268
|
-
var chH = h;
|
|
3269
|
-
if (dm === 'inline' && (ec.labels || []).length) {
|
|
3270
|
-
var rowsData = dataTableRows(ec);
|
|
3271
|
-
var n = rowsData.length;
|
|
3272
|
-
var tH = Math.min(h * 0.44, Math.max(0.44, n * 0.22 + 0.06));
|
|
3273
|
-
chH = Math.max(0.85, h - tH - 0.1);
|
|
3274
|
-
var colW = rowsData[0].map(function () { return w / rowsData[0].length; });
|
|
3275
|
-
sh.push(table(x, y + chH + 0.1, w, colW, rowsData, S, { availH: tH, maxRowH: 0.24, minRowH: 0.14 }));
|
|
3276
|
-
}
|
|
3277
|
-
chartShapes(sh, ec, S, x, y, w, chH, dcols);
|
|
3278
|
-
}
|
|
3279
|
-
/* ══ 复杂信息图页型(A 通道形状近似:文本集合与 B 通道严格一致,cross_verify 逐页比对)══ */
|
|
3280
|
-
function infoTableRows(sec) {
|
|
3281
|
-
var t = sec.type || '';
|
|
3282
|
-
var u = sec.unit ? '(' + sec.unit + ')' : '';
|
|
3283
|
-
if (t === 'sankey') return [['源', '汇', '流量' + u]].concat((sec.flows || []).map(function (f) { return [String(f[0]), String(f[1]), String(f[2])]; }));
|
|
3284
|
-
if (t === 'treemap') return [['项目', '数值' + u]].concat((sec.items || []).map(function (it) { return [String(it[0]), String(it[1])]; }));
|
|
3285
|
-
if (t === 'boxplot') return [['分组', '最小', 'Q1', '中位', 'Q3', '最大']].concat((sec.groups || []).map(function (g) { return g.map(String); }));
|
|
3286
|
-
if (t === 'network') return [['节点', '名称']].concat((sec.nodes || []).map(function (n) { return [String(n[0]), String(n[1])]; }));
|
|
3287
|
-
if (t === 'marimekko') {
|
|
3288
|
-
var head = ['列', '总量' + u].concat((sec.legend || []).map(String));
|
|
3289
|
-
return [head].concat((sec.cols || []).map(function (c, i) {
|
|
3290
|
-
return [String(c[0]), String(c[1])].concat(((sec.cells || [])[i] || []).map(String));
|
|
3291
|
-
}));
|
|
3292
|
-
}
|
|
3293
|
-
if (t === 'streamgraph') {
|
|
3294
|
-
return [['系列'].concat((sec.labels || []).map(String))]
|
|
3295
|
-
.concat((sec.series || []).map(function (se) {
|
|
3296
|
-
return [String(se.name || '')].concat((se.values || []).map(String));
|
|
3297
|
-
}));
|
|
3298
|
-
}
|
|
3299
|
-
return null;
|
|
3300
|
-
}
|
|
3301
|
-
function infoDataTableMode(sec) {
|
|
3302
|
-
var m = String(((sec.chart || {}).dataTable) || 'notes').toLowerCase();
|
|
3303
|
-
return m === 'appendix' ? 'inline' : m;
|
|
3304
|
-
}
|
|
3305
|
-
function infoPlotA(sec, y0, y1) {
|
|
3306
|
-
var rows = infoTableRows(sec);
|
|
3307
|
-
var inline = (infoDataTableMode(sec) === 'inline') && rows && rows.length;
|
|
3308
|
-
if (!inline) return { y0: y0, y1: y1, rows: rows };
|
|
3309
|
-
var tH = Math.min((y1 - y0) * 0.4, Math.max(0.44, rows.length * 0.22 + 0.06));
|
|
3310
|
-
return { y0: y0, y1: y1 - tH - 0.1, rows: rows, table: { y: y1 - tH, h: tH } };
|
|
3311
|
-
}
|
|
3312
|
-
function tmSplitA(items, x, y, w, h, out) {
|
|
3313
|
-
if (!items.length) return;
|
|
3314
|
-
if (items.length === 1) { out.push({ it: items[0], x: x, y: y, w: w, h: h }); return; }
|
|
3315
|
-
var total = items.reduce(function (a, i) { return a + i.v; }, 0) || 1;
|
|
3316
|
-
var acc = 0, k = 0;
|
|
3317
|
-
for (; k < items.length - 1; k++) { acc += items[k].v; if (acc >= total / 2) break; }
|
|
3318
|
-
var first = items.slice(0, k + 1), second = items.slice(k + 1);
|
|
3319
|
-
var r = first.reduce(function (a, i) { return a + i.v; }, 0) / total;
|
|
3320
|
-
if (w >= h) {
|
|
3321
|
-
tmSplitA(first, x, y, w * r, h, out);
|
|
3322
|
-
tmSplitA(second, x + w * r, y, w * (1 - r), h, out);
|
|
3323
|
-
} else {
|
|
3324
|
-
tmSplitA(first, x, y, w, h * r, out);
|
|
3325
|
-
tmSplitA(second, x, y + h * r, w, h * (1 - r), out);
|
|
3326
|
-
}
|
|
3327
|
-
}
|
|
3328
|
-
/* 信息图近似渲染:按页型画形状 + 落同样的标签文本 */
|
|
3329
|
-
function infoApprox(sh, sec, S, y0, y1) {
|
|
3330
|
-
var t = sec.type || '';
|
|
3331
|
-
var P = infoPlotA(sec, y0, y1);
|
|
3332
|
-
if (P.table) {
|
|
3333
|
-
var colW = P.rows[0].map(function () { return CW / P.rows[0].length; });
|
|
3334
|
-
sh.push(table(MX, P.table.y, CW, colW, P.rows, S, { availH: P.table.h, maxRowH: 0.24, minRowH: 0.15 }));
|
|
3335
|
-
}
|
|
3336
|
-
var palette = [S.accent, S.faint, S.body, S.line];
|
|
3337
|
-
if (t === 'sankey') {
|
|
3338
|
-
var flows = (sec.flows || []).filter(function (f) { return f && f.length >= 3; });
|
|
3339
|
-
var nodes = [], idx = {};
|
|
3340
|
-
flows.forEach(function (f) {
|
|
3341
|
-
[f[0], f[1]].forEach(function (n) { if (idx[n] == null) { idx[n] = nodes.length; nodes.push(String(n)); } });
|
|
3342
|
-
});
|
|
3343
|
-
var level = nodes.map(function () { return 0; });
|
|
3344
|
-
for (var it2 = 0; it2 < nodes.length + 1; it2++) {
|
|
3345
|
-
var ch = false;
|
|
3346
|
-
flows.forEach(function (f) {
|
|
3347
|
-
var a = idx[f[0]], b = idx[f[1]];
|
|
3348
|
-
if (level[b] < level[a] + 1) { level[b] = level[a] + 1; ch = true; }
|
|
3349
|
-
});
|
|
3350
|
-
if (!ch) break;
|
|
3351
|
-
}
|
|
3352
|
-
var maxLv = Math.max.apply(null, level.concat([0]));
|
|
3353
|
-
var colW2 = CW / (maxLv + 1);
|
|
3354
|
-
var geo = {};
|
|
3355
|
-
for (var l = 0; l <= maxLv; l++) {
|
|
3356
|
-
var lv = nodes.filter(function (n, i) { return level[i] === l; });
|
|
3357
|
-
var rowH2 = Math.max(0.24, (P.y1 - P.y0) / Math.max(1, lv.length));
|
|
3358
|
-
lv.forEach(function (n, k2) { geo[n] = { x: MX + l * colW2 + colW2 * 0.5 - 0.16, y: P.y0 + k2 * rowH2, h: rowH2 * 0.7 }; });
|
|
3359
|
-
}
|
|
3360
|
-
flows.forEach(function (f) {
|
|
3361
|
-
var a = geo[f[0]], b = geo[f[1]];
|
|
3362
|
-
if (!a || !b) return;
|
|
3363
|
-
sh.push(lineSeg(a.x + 0.32, a.y + a.h / 2, b.x, b.y + b.h / 2, S.soft, 8));
|
|
3364
|
-
/* 流量标签:与 B 通道同文本(数值 + 单位,由 cross_verify 过滤) */
|
|
3365
|
-
sh.push(txSp((a.x + b.x) / 2 - 0.5, (a.y + b.y) / 2 - 0.14, 1.0, 0.28,
|
|
3366
|
-
[[{ t: String(f[2]) + (sec.unit || ''), sz: 9.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
3367
|
-
});
|
|
3368
|
-
nodes.forEach(function (n) {
|
|
3369
|
-
var g = geo[n];
|
|
3370
|
-
sh.push(shape('rect', g.x, g.y, 0.32, g.h, S.accent));
|
|
3371
|
-
sh.push(txSp(g.x + 0.38, g.y, Math.min(1.4, colW2 * 0.5), g.h,
|
|
3372
|
-
[[{ t: n, sz: 10.5, b: 1, col: S.body, font: S.font }]], { anchor: 'ctr' }));
|
|
3373
|
-
});
|
|
3374
|
-
return;
|
|
3375
|
-
}
|
|
3376
|
-
if (t === 'treemap') {
|
|
3377
|
-
var items = (sec.items || []).map(function (it) { return { label: String(it[0]), v: Math.max(0, Number(it[1]) || 0) }; })
|
|
3378
|
-
.filter(function (i) { return i.v > 0; }).sort(function (a, b) { return b.v - a.v; });
|
|
3379
|
-
var out = [];
|
|
3380
|
-
tmSplitA(items, MX, P.y0, CW, P.y1 - P.y0, out);
|
|
3381
|
-
out.forEach(function (o) {
|
|
3382
|
-
sh.push(shape('rect', o.x + 0.03, o.y + 0.03, Math.max(0.05, o.w - 0.06), Math.max(0.05, o.h - 0.06), S.surface, { line: S.line }));
|
|
3383
|
-
sh.push(txSp(o.x + 0.1, o.y + 0.06, Math.max(0.2, o.w - 0.2), 0.28, [[{ t: o.it.label, sz: 10.5, b: 1, col: S.ink, font: S.font }]]));
|
|
3384
|
-
sh.push(txSp(o.x + 0.1, o.y + 0.32, Math.max(0.2, o.w - 0.2), 0.26,
|
|
3385
|
-
[[{ t: String(o.it.v) + (sec.unit || ''), sz: 10, col: S.body, font: S.font }]]));
|
|
3386
|
-
});
|
|
3387
|
-
return;
|
|
3388
|
-
}
|
|
3389
|
-
if (t === 'boxplot') {
|
|
3390
|
-
var groups = (sec.groups || []).filter(function (g) { return g && g.length >= 6; });
|
|
3391
|
-
var step = CW / Math.max(1, groups.length);
|
|
3392
|
-
var plotH = (P.y1 - P.y0) - 0.4;
|
|
3393
|
-
groups.forEach(function (g, i) {
|
|
3394
|
-
var cx = MX + step * i + step / 2;
|
|
3395
|
-
var bw = Math.min(0.9, step * 0.5);
|
|
3396
|
-
sh.push(shape('rect', cx - 0.012, P.y0, 0.024, plotH, S.faint));
|
|
3397
|
-
sh.push(shape('rect', cx - bw / 2, P.y0 + plotH * 0.2, bw, plotH * 0.6, S.soft, { line: S.accent }));
|
|
3398
|
-
sh.push(shape('rect', cx - bw / 2, P.y0 + plotH * 0.48, bw, 0.03, S.accent));
|
|
3399
|
-
sh.push(txSp(cx - step / 2, P.y1 - 0.34, step, 0.28,
|
|
3400
|
-
[[{ t: String(g[0]), sz: 10.5, col: S.body, font: S.font }]], { align: 'ctr' }));
|
|
3401
|
-
});
|
|
3402
|
-
return;
|
|
3403
|
-
}
|
|
3404
|
-
if (t === 'network') {
|
|
3405
|
-
var nds = sec.nodes || [];
|
|
3406
|
-
var n2 = Math.max(1, nds.length);
|
|
3407
|
-
var cx0 = MX + CW / 2, cy0 = (P.y0 + P.y1) / 2;
|
|
3408
|
-
var r = Math.min(CW / 2, (P.y1 - P.y0) / 2) - 1.0;
|
|
3409
|
-
var pos = {};
|
|
3410
|
-
nds.forEach(function (nd, i) {
|
|
3411
|
-
var a = -Math.PI / 2 + i * 2 * Math.PI / n2;
|
|
3412
|
-
pos[String(nd[0])] = { x: cx0 + r * Math.cos(a), y: cy0 + r * Math.sin(a) };
|
|
3413
|
-
});
|
|
3414
|
-
(sec.edges || []).forEach(function (e) {
|
|
3415
|
-
var a = pos[String(e[0])], b = pos[String(e[1])];
|
|
3416
|
-
if (!a || !b) return;
|
|
3417
|
-
sh.push(lineSeg(a.x, a.y, b.x, b.y, S.line, 1.5));
|
|
3418
|
-
});
|
|
3419
|
-
nds.forEach(function (nd) {
|
|
3420
|
-
var p = pos[String(nd[0])];
|
|
3421
|
-
sh.push(shape('ellipse', p.x - 0.26, p.y - 0.26, 0.52, 0.52, S.accent));
|
|
3422
|
-
var outward = p.x >= cx0;
|
|
3423
|
-
sh.push(txSp(outward ? p.x + 0.32 : p.x - 0.32 - 1.4, p.y - 0.14, 1.4, 0.28,
|
|
3424
|
-
[[{ t: String(nd[1] != null ? nd[1] : nd[0]), sz: 10, col: S.body, font: S.font }]],
|
|
3425
|
-
{ align: outward ? 'l' : 'r', anchor: 'ctr' }));
|
|
3426
|
-
});
|
|
3427
|
-
return;
|
|
3428
|
-
}
|
|
3429
|
-
if (t === 'marimekko') {
|
|
3430
|
-
var cols = sec.cols || [];
|
|
3431
|
-
var legend = (sec.legend || []).map(String);
|
|
3432
|
-
var total = cols.reduce(function (a, c) { return a + (Number(c[1]) || 0); }, 0) || 1;
|
|
3433
|
-
var plotH2 = (P.y1 - P.y0) - 0.6;
|
|
3434
|
-
var availW = CW - 0.06 * Math.max(0, cols.length - 1);
|
|
3435
|
-
var cx1 = MX;
|
|
3436
|
-
cols.forEach(function (c, i) {
|
|
3437
|
-
var cw = Math.max(0.2, (Number(c[1]) || 0) / total * availW);
|
|
3438
|
-
var shares = ((sec.cells || [])[i] || []).map(Number);
|
|
3439
|
-
var sum = shares.reduce(function (a, b) { return a + b; }, 0) || 1;
|
|
3440
|
-
var cy = P.y0;
|
|
3441
|
-
shares.forEach(function (v, k) {
|
|
3442
|
-
var shh = Math.max(0.02, v / sum * plotH2);
|
|
3443
|
-
sh.push(shape('rect', cx1, cy, cw, shh, palette[k % palette.length]));
|
|
3444
|
-
cy += shh;
|
|
3445
|
-
});
|
|
3446
|
-
sh.push(txSp(cx1, P.y0 + plotH2 + 0.06, cw, 0.28,
|
|
3447
|
-
[[{ t: String(c[0]), sz: 10.5, b: 1, col: S.body, font: S.font }]], { align: 'ctr' }));
|
|
3448
|
-
cx1 += cw + 0.06;
|
|
3449
|
-
});
|
|
3450
|
-
var lx = MX;
|
|
3451
|
-
legend.forEach(function (lg, k) {
|
|
3452
|
-
sh.push(shape('rect', lx, P.y1 - 0.2, 0.12, 0.12, palette[k % palette.length]));
|
|
3453
|
-
sh.push(txSp(lx + 0.16, P.y1 - 0.26, 1.5, 0.26, [[{ t: lg, sz: 10, col: S.body, font: S.font }]], { anchor: 'ctr' }));
|
|
3454
|
-
lx += 1.75;
|
|
3455
|
-
});
|
|
3456
|
-
return;
|
|
3457
|
-
}
|
|
3458
|
-
if (t === 'streamgraph') {
|
|
3459
|
-
var series = sec.series || [];
|
|
3460
|
-
var labels = sec.labels || [];
|
|
3461
|
-
var nPts = labels.length || ((series[0] && series[0].values || []).length);
|
|
3462
|
-
if (nPts >= 2) {
|
|
3463
|
-
var values = series.map(function (se) {
|
|
3464
|
-
return (se.values || []).map(function (v) { return Math.max(0, Number(v) || 0); });
|
|
3465
|
-
});
|
|
3466
|
-
var totals = [];
|
|
3467
|
-
for (var i2 = 0; i2 < nPts; i2++) {
|
|
3468
|
-
var tt = 0;
|
|
3469
|
-
values.forEach(function (v) { tt += (v[i2] || 0); });
|
|
3470
|
-
totals.push(tt);
|
|
3471
|
-
}
|
|
3472
|
-
var maxTot = Math.max.apply(null, totals.concat([1]));
|
|
3473
|
-
var plotH3 = P.y1 - P.y0;
|
|
3474
|
-
var cyMid = (P.y0 + P.y1) / 2;
|
|
3475
|
-
var base = totals.map(function (tv) { return cyMid - tv / maxTot * plotH3 / 2; });
|
|
3476
|
-
series.forEach(function (se, si) {
|
|
3477
|
-
var upper = base.map(function (bv, i3) { return bv + (values[si][i3] || 0) / maxTot * plotH3 / 2; });
|
|
3478
|
-
var stepX = CW / (nPts - 1);
|
|
3479
|
-
for (var i4 = 0; i4 < nPts - 1; i4++) {
|
|
3480
|
-
var hgt = Math.max(0.04, ((upper[i4] - base[i4]) + (upper[i4 + 1] - base[i4 + 1])) / 2);
|
|
3481
|
-
sh.push(shape('rect', MX + i4 * stepX, (base[i4] + base[i4 + 1]) / 2, stepX, hgt, palette[si % palette.length]));
|
|
3482
|
-
}
|
|
3483
|
-
base = upper;
|
|
3484
|
-
sh.push(shape('rect', MX, P.y1 + 0.06 + si * 0.26, 0.12, 0.12, palette[si % palette.length]));
|
|
3485
|
-
sh.push(txSp(MX + 0.16, P.y1 + si * 0.26, 1.5, 0.26,
|
|
3486
|
-
[[{ t: String(se.name || ('系列' + (si + 1))), sz: 10, col: S.body, font: S.font }]], { anchor: 'ctr' }));
|
|
3487
|
-
});
|
|
3488
|
-
labels.forEach(function (lb, i5) {
|
|
3489
|
-
sh.push(txSp(MX + (nPts > 1 ? i5 / (nPts - 1) : 0.5) * CW - 0.4, P.y1 - 0.02, 0.8, 0.24,
|
|
3490
|
-
[[{ t: String(lb), sz: 9.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
3491
|
-
});
|
|
3492
|
-
}
|
|
3493
|
-
return;
|
|
3494
|
-
}
|
|
3495
|
-
}
|
|
3496
|
-
/* 统一图表分发(与 scripts/layout-constants.json 的 charts.registry 同分派规则)
|
|
3497
|
-
* A 通道为形状近似:类别标签与数值都落为文本,数值 token 由 cross_verify 过滤;
|
|
3498
|
-
* 复杂信息图(sankey / treemap / boxplot / network / marimekko / streamgraph)请用专属页型承载。 */
|
|
3499
|
-
function chartShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
3500
|
-
var t = (ec.type || 'bar').toLowerCase();
|
|
3501
|
-
var reg = (typeof REG !== 'undefined' && REG && REG[t]) ? REG[t] : null;
|
|
3502
|
-
if (t === 'hbar') { hbarRows(sh, ec, S, y, y + h, dcols, x, w); return; }
|
|
3503
|
-
if (t === 'line' || t === 'dualline' || t === 'area') { lineShapes(sh, ec, S, x, y, w, h, dcols); return; }
|
|
3504
|
-
if (t === 'scatter' || t === 'bubble') { xyDots(sh, ec, S, x, y, w, h); return; }
|
|
3505
|
-
if (t === 'gauge') { gaugeApprox(sh, ec, S, x, y, w, h); return; }
|
|
3506
|
-
if (t === 'pie' || t === 'donut' || t === 'multidonut') { pieApprox(sh, ec, S, x, y, w, h, dcols); return; }
|
|
3507
|
-
/* sparkline / candlestick 的文本集须与 B 通道专属渲染器一致(否则 cross_verify 误报) */
|
|
3508
|
-
if (t === 'sparkline') { sparkApprox(sh, ec, S, x, y, w, h); return; }
|
|
3509
|
-
if (t === 'candlestick') { candleApprox(sh, ec, S, x, y, w, h); return; }
|
|
3510
|
-
if (reg && reg.pptx === 'shape') { shapeProportion(sh, ec, S, x, y, w, h, dcols); return; }
|
|
3511
|
-
vbarShapes(sh, ec, S, x, y, w, h, dcols);
|
|
3512
|
-
}
|
|
3513
|
-
|
|
3514
|
-
/* 区域 IR 取用(与 scripts/lib_layout_regions.js 同源;未注入时回落 null) */
|
|
3515
|
-
function regOf(type, slot, opts) {
|
|
3516
|
-
return (typeof regionOf === 'function') ? regionOf(type, slot, opts) : null;
|
|
3517
|
-
}
|
|
3518
|
-
|
|
3519
|
-
/* ── 幻灯片生成(版式常量同 build_pptx.js · 同一 layout-constants.json) ── */
|
|
3520
|
-
function slidesOf(model, S) {
|
|
3521
|
-
var slides = [];
|
|
3522
|
-
_mode = model.mode || 'presentation';
|
|
3523
|
-
function head(sh, eyebrow, title, lead) {
|
|
3524
|
-
var H = (typeof regionOf === 'function') ? regionOf('head', 'head') : {
|
|
3525
|
-
x: MX, y: PT.common.headEyebrowY, w: CW,
|
|
3526
|
-
titleY: PT.common.headTitleY, ruleY: PT.common.headRuleY, leadY: PT.common.leadY,
|
|
3527
|
-
};
|
|
3528
|
-
if (eyebrow) sh.push(txSp(H.x, H.y, H.w, 0.4, [[{ t: eyebrow, sz: 13, b: 1, col: S.accent, font: S.font, spc: 200 }]]));
|
|
3529
|
-
var titleH = eyebrow ? (H.ruleY - H.titleY - 0.06) : 1.0;
|
|
3530
|
-
var tSize = estTextH(title, CW, 30, 1.25) > titleH ? 24 : 30;
|
|
3531
|
-
sh.push(txSp(H.x, eyebrow ? H.titleY : 0.6, H.w, Math.max(0.6, titleH), [[{ t: title, sz: tSize, b: 1, col: S.ink, font: S.fontDisplay }]]));
|
|
3532
|
-
sh.push(shape('rect', H.x, eyebrow ? H.ruleY : 1.6, 0.9, 0.045, S.accent));
|
|
3533
|
-
if (lead) sh.push(txSp(H.x, H.leadY || PT.common.leadY, H.w, 0.5, [[{ t: lead, sz: 14, col: S.body, font: S.font }]]));
|
|
3534
|
-
}
|
|
3535
|
-
|
|
3536
|
-
/* 封面 */
|
|
3537
|
-
var CV = PT.cover;
|
|
3538
|
-
var c = [shape('rect', 0, 0, PW, PH, S.accent)];
|
|
3539
|
-
c.push(txSp(MX, CV.metaY, CW, 0.4, [[{ t: model.meta || '', sz: 12, col: S.soft, font: S.font }]]));
|
|
3540
|
-
var cTSize = estTextH(model.title, CW - 1, 44, 1.25) > CV.titleH ? 36 : 44;
|
|
3541
|
-
c.push(txSp(MX, CV.titleY, CW - 1, CV.titleH, [[{ t: model.title, sz: cTSize, b: 1, col: S.onAccent, font: S.fontDisplay }]]));
|
|
3542
|
-
c.push(txSp(MX, CV.subtitleY, CW - 1, CV.subtitleH, [[{ t: model.subtitle || '', sz: 18, col: S.soft, font: S.font }]]));
|
|
3543
|
-
slides.push(c);
|
|
3544
|
-
|
|
3545
|
-
/* Agenda(>8 条自动双列,行高自适应防溢出;architecture 极简形态可省略 agenda) */
|
|
3546
|
-
var items = model.agenda || [];
|
|
3547
|
-
if (items.length) {
|
|
3548
|
-
var ag = [];
|
|
3549
|
-
head(ag, 'AGENDA', '报告大纲');
|
|
3550
|
-
var colN = items.length > 8 ? 2 : 1;
|
|
3551
|
-
var perCol = Math.ceil(items.length / colN) || 1;
|
|
3552
|
-
var rowH = Math.min(1.05, (CONTENT_BOTTOM - CONTENT_TOP) / perCol);
|
|
3553
|
-
var numSz = rowH >= 0.85 ? 30 : (rowH >= 0.65 ? 22 : 18);
|
|
3554
|
-
var tSz = rowH >= 0.85 ? 16 : 14, dSz = rowH >= 0.85 ? 12 : 11;
|
|
3555
|
-
items.forEach(function (it, i) {
|
|
3556
|
-
var col = Math.floor(i / perCol), row = i % perCol;
|
|
3557
|
-
var x = MX + col * (CW / colN), y = CONTENT_TOP + row * rowH;
|
|
3558
|
-
ag.push(txSp(x, y, 0.9, Math.min(0.9, rowH), [[{ t: it[0], sz: numSz, b: 1, col: S.accent, font: S.font }]]));
|
|
3559
|
-
ag.push(txSp(x + 1.0, y + 0.03, CW / colN - 1.1, Math.min(0.95, rowH),
|
|
3560
|
-
[[{ t: it[1], sz: tSz, b: 1, col: S.ink, font: S.font }],
|
|
3561
|
-
[{ t: it[2] || '', sz: dSz, col: S.body, font: S.font }]], { lineSpacing: tSz + 5 }));
|
|
3562
|
-
});
|
|
3563
|
-
slides.push(ag);
|
|
3564
|
-
}
|
|
3565
|
-
|
|
3566
|
-
/* 章节页 */
|
|
3567
|
-
(model.sections || []).forEach(function (sec) {
|
|
3568
|
-
var type = sec.type || 'points';
|
|
3569
|
-
var sh = [];
|
|
3570
|
-
if (type !== 'quote') head(sh, sec.eyebrow, sec.title, sec.lead);
|
|
3571
|
-
/* 非 exhibit 页型也可带 Exhibit 编号(帧头徽标;正文区下移让位,与 B 通道同规则) */
|
|
3572
|
-
var exBadge = (type !== 'exhibit' && sec.exhibitNo) ? String(sec.exhibitNo).toUpperCase() : null;
|
|
3573
|
-
var bodyY0 = sec.lead ? PT.common.bodyYWithLead : CONTENT_TOP;
|
|
3574
|
-
var bodyY = exBadge ? bodyY0 + 0.42 : bodyY0;
|
|
3575
|
-
if (exBadge) {
|
|
3576
|
-
sh.push(txSp(MX, CONTENT_TOP - 0.06, CW, 0.3,
|
|
3577
|
-
[[{ t: exBadge, sz: 11.5, b: 1, col: S.accent, font: S.font, spc: 200 }]]));
|
|
3578
|
-
}
|
|
3579
|
-
var flagItems = Array.isArray(sec.flags) ? sec.flags.filter(Boolean) : [];
|
|
3580
|
-
var _F = PT.flagbar || { hdH: 0.26, rowH: 0.24, maxRows: 3, gap: 0.12 };
|
|
3581
|
-
var flagH = flagItems.length
|
|
3582
|
-
? (_F.hdH + Math.min(_F.maxRows, flagItems.length) * _F.rowH + 0.06) : 0;
|
|
3583
|
-
var bodyBottom = chartBottom(!!sec.soWhat, !!sec.footnote);
|
|
3584
|
-
var flagY = 0;
|
|
3585
|
-
if (flagH) {
|
|
3586
|
-
flagY = CONTENT_BOTTOM - flagH;
|
|
3587
|
-
if (sec.footnote) flagY = Math.min(flagY, PT.exhibit.footnoteY - flagH - 0.06);
|
|
3588
|
-
if (sec.soWhat) flagY = Math.min(flagY, PT.exhibit.soWhatY - flagH - 0.06);
|
|
3589
|
-
bodyBottom = Math.min(bodyBottom, flagY - (_F.gap || 0.12));
|
|
3590
|
-
}
|
|
3591
|
-
|
|
3592
|
-
if (type === 'metrics') {
|
|
3593
|
-
var M = PT.metrics, ms = sec.metrics || [], n = Math.max(1, ms.length);
|
|
3594
|
-
var mReg = regOf('metrics', 'primary') || { x: MX, y: M.bandY, w: CW, h: M.cardH };
|
|
3595
|
-
var g = cols(mReg.w, n, M.gap);
|
|
3596
|
-
var mw = Math.min(M.maxW, g.w);
|
|
3597
|
-
var gx = mReg.x + (mReg.w - n * mw - (n - 1) * M.gap) / 2;
|
|
3598
|
-
var cardH = Math.min(mReg.h, CONTENT_BOTTOM - mReg.y);
|
|
3599
|
-
ms.forEach(function (m, i) {
|
|
3600
|
-
var x = gx + i * (mw + M.gap);
|
|
3601
|
-
sh.push(shape('roundRect', x, mReg.y, mw, cardH, S.surface, { line: S.line, adj: 4000 }));
|
|
3602
|
-
var vSize = estTextH(m[0], mw - 0.2, 40, 1.1) > cardH * 0.5 ? 30 : 40;
|
|
3603
|
-
sh.push(txSp(x, M.valY, mw, M.valH, [[{ t: m[0], sz: vSize, b: 1, col: S.accent, font: S.fontDisplay }]], { align: 'ctr' }));
|
|
3604
|
-
sh.push(txSp(x + 0.15, M.capY, mw - 0.3, M.capH, [[{ t: m[1], sz: 11.5, col: S.body, font: S.font }]], { align: 'ctr' }));
|
|
3605
|
-
});
|
|
3606
|
-
} else if (type === 'kpi') {
|
|
3607
|
-
var K = PT.kpi, hro = sec.hero || [];
|
|
3608
|
-
var hReg = regOf('kpi', 'hero') || { x: MX, y: K.heroY, w: CW * K.heroW, h: K.heroH };
|
|
3609
|
-
sh.push(txSp(hReg.x, hReg.y, hReg.w, hReg.h, [[{ t: String(hro[0] || ''), sz: 64, b: 1, col: S.accent, font: S.fontDisplay }]]));
|
|
3610
|
-
sh.push(txSp(hReg.x + 0.05, hReg.y + 1.5, hReg.w - 0.4, 0.55, [[{ t: String(hro[1] || ''), sz: 15, b: 1, col: S.ink, font: S.font }]]));
|
|
3611
|
-
if (hro[2]) sh.push(txSp(hReg.x + 0.05, hReg.y + 2.1, hReg.w - 0.4, 0.4, [[{ t: '▲ ' + hro[2], sz: 12, b: 1, col: S.accent, font: S.font }]]));
|
|
3612
|
-
var kdivX = MX + CW * K.dividerX;
|
|
3613
|
-
sh.push(shape('rect', kdivX, K.heroY + 0.15, 0.025, 3.1, S.line));
|
|
3614
|
-
var mRegK = regOf('kpi', 'metrics') || { x: kdivX + 0.35, y: K.metricY0, w: CW * (1 - K.dividerX) - 0.5 };
|
|
3615
|
-
var kmets = sec.metrics || [];
|
|
3616
|
-
var kCap = Math.max(1, Math.floor((CONTENT_BOTTOM - mRegK.y) / K.metricRowH));
|
|
3617
|
-
var kRows = Math.min(kmets.length, kCap);
|
|
3618
|
-
var kRowH = fitRowH(CONTENT_BOTTOM - mRegK.y, kRows, K.metricRowH, 0.42);
|
|
3619
|
-
var kTopH = Math.min(0.55, kRowH * 0.62);
|
|
3620
|
-
kmets.slice(0, kRows).forEach(function (m, i) {
|
|
3621
|
-
var ky = mRegK.y + i * kRowH;
|
|
3622
|
-
sh.push(txSp(mRegK.x, ky, mRegK.w, kTopH, [[{ t: m[0], sz: 22, b: 1, col: S.accent, font: S.fontDisplay }]]));
|
|
3623
|
-
sh.push(txSp(mRegK.x, ky + kTopH, mRegK.w, Math.max(0.24, kRowH - kTopH), [[{ t: m[1], sz: 10.5, col: S.faint, font: S.font }]]));
|
|
3624
|
-
});
|
|
3625
|
-
} else if (type === 'comparison') {
|
|
3626
|
-
var C2 = PT.comparison;
|
|
3627
|
-
var lR = regOf('comparison', 'left') || { x: MX, y: C2.panelY, w: (CW - C2.panelGap) / 2, h: C2.panelH };
|
|
3628
|
-
var rR = regOf('comparison', 'right') || { x: MX + (CW + C2.panelGap) / 2, y: C2.panelY, w: (CW - C2.panelGap) / 2, h: C2.panelH };
|
|
3629
|
-
var cw2 = lR.w, cpy = lR.y, cph = lR.h, rx2 = rR.x;
|
|
3630
|
-
sh.push(shape('roundRect', lR.x, cpy, cw2, cph, S.surface, { adj: 4000, line: S.line }));
|
|
3631
|
-
sh.push(shape('roundRect', rx2, cpy, cw2, cph, S.soft, { adj: 4000 }));
|
|
3632
|
-
[{ x: lR.x, d: (sec.left || {}) }, { x: rx2, d: (sec.right || {}) }].forEach(function (sd) {
|
|
3633
|
-
var isRight = sd.x === rx2;
|
|
3634
|
-
var pts = sd.d.points || [];
|
|
3635
|
-
var avail = cph - C2.titleH - 0.34;
|
|
3636
|
-
var rowH2 = fitRowH(avail, pts.length, C2.rowH, 0.34);
|
|
3637
|
-
var fz = rowH2 < 0.5 ? 11.5 : 13;
|
|
3638
|
-
sh.push(txSp(sd.x + 0.22, cpy + 0.18, cw2 - 0.44, C2.titleH,
|
|
3639
|
-
[[{ t: sd.d.title || '', sz: 15, b: 1, col: isRight ? S.accent : S.ink, font: S.font }]]));
|
|
3640
|
-
pts.forEach(function (p, pi) {
|
|
3641
|
-
var py = cpy + 0.18 + C2.titleH + 0.16 + pi * rowH2;
|
|
3642
|
-
sh.push(shape('rect', sd.x + 0.24, py + rowH2 / 2 - 0.06, 0.12, 0.12, isRight ? S.accent : S.faint));
|
|
3643
|
-
sh.push(txSp(sd.x + 0.5, py, cw2 - 0.75, rowH2 - 0.04,
|
|
3644
|
-
[[{ t: p[0] + ' ', sz: fz, b: 1, col: S.ink, font: S.font },
|
|
3645
|
-
{ t: p[1], sz: fz - 1, col: S.body, font: S.font }]]));
|
|
3646
|
-
});
|
|
3647
|
-
});
|
|
3648
|
-
if (sec.verdict) {
|
|
3649
|
-
var vR = regOf('exhibit', 'annotation') || { x: MX, y: PT.exhibit.soWhatY, w: CW, h: 0.62 };
|
|
3650
|
-
sh.push(shape('rect', vR.x, vR.y, vR.w, vR.h, S.accent));
|
|
3651
|
-
sh.push(txSp(vR.x + 0.22, vR.y + 0.06, vR.w - 0.44, vR.h - 0.12,
|
|
3652
|
-
[[{ t: '结论 ', sz: 11, b: 1, col: S.onAccent, font: S.font, spc: 150 },
|
|
3653
|
-
{ t: sec.verdict, sz: 12.5, b: 1, col: S.onAccent, font: S.font }]], { anchor: 'ctr' }));
|
|
3654
|
-
}
|
|
3655
|
-
} else if (type === 'quote') {
|
|
3656
|
-
var Q = PT.quote;
|
|
3657
|
-
var qR = regOf('quote', 'primary') || { x: MX + 1.05, y: Q.textY, w: CW - 2.1, h: Q.textH };
|
|
3658
|
-
/* 金句页用主题一致强调带(accent-soft 底)——浅色主题下不再出现深色页 */
|
|
3659
|
-
sh.push(shape('rect', 0, 0, PW, PH, S.soft));
|
|
3660
|
-
if (sec.eyebrow) sh.push(txSp(MX, PT.common.headEyebrowY, CW, 0.4, [[{ t: sec.eyebrow, sz: 13, b: 1, col: S.accent, font: S.font, spc: 200 }]]));
|
|
3661
|
-
sh.push(txSp(MX, PT.common.headTitleY, CW, 1.0, [[{ t: sec.title, sz: 30, b: 1, col: S.ink, font: S.fontDisplay }]]));
|
|
3662
|
-
sh.push(shape('rect', MX, PT.common.headRuleY, 0.9, 0.045, S.accent));
|
|
3663
|
-
sh.push(txSp(MX + 0.05, Q.markY, 1.2, 1.1, [[{ t: '「', sz: 60, b: 1, col: S.accent, font: S.fontDisplay }]]));
|
|
3664
|
-
var qSize = estTextH(sec.quote, qR.w, 20, 1.4) > qR.h ? 16 : 20;
|
|
3665
|
-
sh.push(txSp(qR.x, qR.y, qR.w, qR.h, [[{ t: sec.quote, sz: qSize, b: 1, col: S.ink, font: S.fontDisplay }]], { anchor: 'ctr' }));
|
|
3666
|
-
sh.push(txSp(qR.x, Q.authorY, qR.w, 0.45, [[{ t: '—— ' + sec.author, sz: 13, b: 1, col: S.accent, font: S.font }]], { align: 'r' }));
|
|
3667
|
-
if (sec.context) sh.push(txSp(qR.x, Q.contextY, qR.w, 0.4, [[{ t: sec.context, sz: 11, col: S.faint, font: S.font }]], { align: 'r' }));
|
|
3668
|
-
} else if (type === 'image') {
|
|
3669
|
-
/* 素材图片页:full 版心全宽 / half 左图右注 / bleed 通栏出血 / grid 多图网格 /
|
|
3670
|
-
compare 双图对比 / wall Logo 墙;无图(image.placeholder)走原生占位块 + 标签文本 */
|
|
3671
|
-
var IM = PT.image, img = sec.image || {};
|
|
3672
|
-
var items = Array.isArray(img.items) ? img.items.filter(Boolean) : [];
|
|
3673
|
-
var layout = String(img.layout || (items.length > 1 ? 'grid' : 'full')).toLowerCase();
|
|
3674
|
-
var fit = String(img.fit || ((typeof IMG !== 'undefined' && IMG && IMG.fitDefault) || 'cover')).toLowerCase();
|
|
3675
|
-
var isPh = !!img.placeholder;
|
|
3676
|
-
var iR = regOf('image', 'primary', { top: bodyY, caption: !!img.caption }) ||
|
|
3677
|
-
{ x: MX, y: Math.max(IM.y, bodyY), w: CW, h: IM.h };
|
|
3678
|
-
var iy = iR.y;
|
|
3679
|
-
var capH2 = img.caption ? 0.32 : 0;
|
|
3680
|
-
var ih = Math.max(1.2, iR.h);
|
|
3681
|
-
imageLayoutShapes(sh, S, layout, img, items, sec.points || [], isPh, fit, iy, iy + ih);
|
|
3682
|
-
if (img.caption) sh.push(txSp(MX, iy + ih + 0.04, CW, capH2, [[{ t: img.caption, sz: 10.5, col: S.faint, font: S.font }]]));
|
|
3683
|
-
} else if (type === 'donut') {
|
|
3684
|
-
var dcn = sec.chart || {};
|
|
3685
|
-
if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme));
|
|
3686
|
-
if (sec.note) sh.push(txSp(MX, PT.note.y, CW, PT.note.h, [[{ t: sec.note, sz: 11, col: S.faint, font: S.font }]]));
|
|
3687
|
-
} else if (type === 'heatmap') {
|
|
3688
|
-
var H = PT.heatmap;
|
|
3689
|
-
var hR = regOf('heatmap', 'primary', { bottom: bodyBottom }) || { x: MX, y: H.y, w: CW, h: bodyBottom - H.y };
|
|
3690
|
-
var rowsH = sec.rowHeads || [], colsH = sec.colHeads || [], cells = sec.cells || [];
|
|
3691
|
-
var nR = Math.max(1, rowsH.length), nC = Math.max(1, colsH.length);
|
|
3692
|
-
var availH = hR.h;
|
|
3693
|
-
var rowH3 = fitRowH(availH - H.headH, nR, H.cellH, 0.24);
|
|
3694
|
-
var gridW = hR.w - H.labelW;
|
|
3695
|
-
var cw3 = (gridW - (nC - 1) * H.cellGap) / nC;
|
|
3696
|
-
var flat = [], k2;
|
|
3697
|
-
for (k2 = 0; k2 < cells.length; k2++) for (var j2 = 0; j2 < (cells[k2] || []).length; j2++) { var nv = Number(cells[k2][j2]); if (!isNaN(nv)) flat.push(nv); }
|
|
3698
|
-
var vmax3 = flat.length ? Math.max.apply(null, flat) : 1;
|
|
3699
|
-
var vmin3 = flat.length ? Math.min.apply(null, flat) : 0;
|
|
3700
|
-
var levels = [S.surface, S.soft, S.line, S.accent];
|
|
3701
|
-
colsH.forEach(function (ch2, ci) {
|
|
3702
|
-
sh.push(txSp(hR.x + H.labelW + ci * (cw3 + H.cellGap), hR.y, cw3, H.headH,
|
|
3703
|
-
[[{ t: ch2, sz: 11, b: 1, col: S.body, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
|
|
3704
|
-
});
|
|
3705
|
-
var rowsUse = Math.min(nR, cells.length);
|
|
3706
|
-
for (var ri = 0; ri < rowsUse; ri++) {
|
|
3707
|
-
var yy = hR.y + H.headH + 0.08 + ri * (rowH3 + H.cellGap);
|
|
3708
|
-
sh.push(txSp(hR.x, yy, H.labelW - 0.15, rowH3, [[{ t: rowsH[ri] || '', sz: 11, b: 1, col: S.body, font: S.font }]], { align: 'r', anchor: 'ctr' }));
|
|
3709
|
-
for (var ci2 = 0; ci2 < nC; ci2++) {
|
|
3710
|
-
var v2 = Number((cells[ri] || [])[ci2]);
|
|
3711
|
-
var has2 = !isNaN(v2);
|
|
3712
|
-
var p2 = (has2 && vmax3 > vmin3) ? (v2 - vmin3) / (vmax3 - vmin3) : 0.5;
|
|
3713
|
-
var lvl = p2 >= 0.86 ? 3 : (p2 >= 0.55 ? 2 : (p2 >= 0.25 ? 1 : 0));
|
|
3714
|
-
var cx2 = hR.x + H.labelW + ci2 * (cw3 + H.cellGap);
|
|
3715
|
-
sh.push(shape('roundRect', cx2, yy, cw3, rowH3, levels[lvl],
|
|
3716
|
-
{ adj: 3000, line: lvl === 0 ? S.line : undefined }));
|
|
3717
|
-
sh.push(txSp(cx2, yy, cw3, rowH3,
|
|
3718
|
-
[[{ t: has2 ? valText(v2, sec.unit) : '—', sz: 11.5, b: lvl >= 2 ? 1 : 0,
|
|
3719
|
-
col: lvl >= 3 ? S.onAccent : S.body, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
|
|
3720
|
-
}
|
|
3721
|
-
}
|
|
3722
|
-
var lg = sec.scaleLabel || ['低', '高'];
|
|
3723
|
-
var lgy = hR.y + H.headH + 0.08 + rowsUse * (rowH3 + H.cellGap) + 0.06;
|
|
3724
|
-
sh.push(txSp(hR.x, lgy, 0.6, 0.3, [[{ t: lg[0] || '低', sz: 9.5, col: S.faint, font: S.font }]]));
|
|
3725
|
-
for (var li2 = 0; li2 < 4; li2++) {
|
|
3726
|
-
sh.push(shape('rect', hR.x + 0.45 + li2 * 0.3, lgy + 0.04, 0.28, 0.16, levels[li2],
|
|
3727
|
-
{ line: li2 === 0 ? S.line : undefined }));
|
|
3728
|
-
}
|
|
3729
|
-
sh.push(txSp(hR.x + 1.75, lgy, 0.6, 0.3, [[{ t: lg[1] || '高', sz: 9.5, col: S.faint, font: S.font }]]));
|
|
3730
|
-
} else if (type === 'bullet') {
|
|
3731
|
-
var B = PT.bullet, its = sec.items || [], nB = Math.max(1, its.length);
|
|
3732
|
-
var bR = regOf('bullet', 'primary', { bottom: bodyBottom }) || { x: MX, y: B.y, w: CW, h: bodyBottom - B.y };
|
|
3733
|
-
var availB = bR.h;
|
|
3734
|
-
var rowHB = fitRowH(availB, nB, B.rowH, 0.26);
|
|
3735
|
-
var trackX = bR.x + B.labelW + 0.15;
|
|
3736
|
-
var trackW = bR.w - B.labelW - B.valW - 0.3;
|
|
3737
|
-
var nums = [], q2;
|
|
3738
|
-
for (q2 = 0; q2 < its.length; q2++) { var nq = Number(its[q2][1]); if (!isNaN(nq)) nums.push(nq); }
|
|
3739
|
-
var vmaxB = sec.max || (nums.length ? Math.max.apply(null, nums) : 1);
|
|
3740
|
-
its.forEach(function (it, i) {
|
|
3741
|
-
var yy2 = bR.y + i * rowHB + rowHB / 2 - B.barH / 2;
|
|
3742
|
-
var v = Number(it[1]) || 0, tgt = Number(it[2]);
|
|
3743
|
-
var hit = !isNaN(tgt) && v >= tgt;
|
|
3744
|
-
sh.push(txSp(bR.x, bR.y + i * rowHB, B.labelW, rowHB, [[{ t: String(it[0]), sz: 12, b: 1, col: S.ink, font: S.font }]], { anchor: 'ctr' }));
|
|
3745
|
-
sh.push(shape('roundRect', trackX, yy2, trackW, B.barH, S.surface, { adj: 50000, line: S.line }));
|
|
3746
|
-
var fw = Math.max(0.02, Math.min(1, v / (vmaxB || 1)) * trackW);
|
|
3747
|
-
sh.push(shape('roundRect', trackX, yy2, fw, B.barH, hit ? S.accent : S.faint, { adj: 50000 }));
|
|
3748
|
-
if (!isNaN(tgt)) {
|
|
3749
|
-
var tx2 = trackX + Math.min(1, tgt / (vmaxB || 1)) * trackW;
|
|
3750
|
-
sh.push(shape('rect', tx2 - 0.01, yy2 - 0.07, 0.02, B.barH + 0.14, S.body));
|
|
3751
|
-
}
|
|
3752
|
-
sh.push(txSp(trackX + trackW + 0.12, bR.y + i * rowHB, B.valW, rowHB,
|
|
3753
|
-
[[{ t: String(it[1]) + (sec.unit || ''), sz: 12, b: 1, col: hit ? S.accent : S.body, font: S.font },
|
|
3754
|
-
{ t: isNaN(tgt) ? '' : ' / ' + tgt + (sec.unit || ''), sz: 10, col: S.faint, font: S.font }]],
|
|
3755
|
-
{ align: 'r', anchor: 'ctr' }));
|
|
3756
|
-
});
|
|
3757
|
-
} else if (type === 'pyramid') {
|
|
3758
|
-
var P = PT.pyramid, lv = sec.levels || [], nP = Math.max(1, lv.length);
|
|
3759
|
-
var pR = regOf('pyramid', 'primary', { bottom: bodyBottom }) || { x: MX, y: P.y, w: CW, h: bodyBottom - P.y };
|
|
3760
|
-
var availP = pR.h;
|
|
3761
|
-
var rowHP = fitRowH(availP - (nP - 1) * P.gap, nP, P.rowH, 0.26);
|
|
3762
|
-
lv.forEach(function (l, i) {
|
|
3763
|
-
var frac = P.minW + (1 - P.minW) * (nP === 1 ? 1 : i / (nP - 1));
|
|
3764
|
-
var w2 = pR.w * frac, x2 = pR.x + (pR.w - w2) / 2, y2 = pR.y + i * (rowHP + P.gap);
|
|
3765
|
-
var acc = isRec(l) && l.accent;
|
|
3766
|
-
var lt = isRec(l) ? (l.t || '') : String(l[0] || '');
|
|
3767
|
-
var ld = isRec(l) ? (l.d || '') : String(l[1] || '');
|
|
3768
|
-
sh.push(shape('roundRect', x2, y2, w2, rowHP, acc ? S.soft : S.surface, { adj: 5000, line: acc ? undefined : S.line }));
|
|
3769
|
-
var lwP = Math.min(P.labelW, w2 - 0.44);
|
|
3770
|
-
sh.push(txSp(x2 + 0.22, y2, lwP, rowHP, [[{ t: lt, sz: 13.5, b: 1, col: acc ? S.accent : S.ink, font: S.font }]], { anchor: 'ctr' }));
|
|
3771
|
-
if (ld) sh.push(txSp(x2 + 0.22 + lwP, y2, w2 - 0.44 - lwP, rowHP, [[{ t: ld, sz: 11, col: S.faint, font: S.font }]], { anchor: 'ctr' }));
|
|
3772
|
-
});
|
|
3773
|
-
} else if (type === 'steps') {
|
|
3774
|
-
var S2 = PT.steps, st = sec.steps || [], maxPer = S2.maxPerRow || 6, groups = sec.groups || [];
|
|
3775
|
-
var sR = regOf('steps', 'primary') || { x: MX, y: S2.y, w: CW, h: S2.barH };
|
|
3776
|
-
var chunks = [];
|
|
3777
|
-
if (groups.length) {
|
|
3778
|
-
var kk = 0;
|
|
3779
|
-
groups.forEach(function (gp) { var cnt = Number(gp[1]) || 0; chunks.push({ label: gp[0], items: st.slice(kk, kk + cnt) }); kk += cnt; });
|
|
3780
|
-
if (kk < st.length) chunks.push({ label: '', items: st.slice(kk) });
|
|
3781
|
-
} else {
|
|
3782
|
-
for (var i3 = 0; i3 < st.length; i3 += maxPer) chunks.push({ label: '', items: st.slice(i3, i3 + maxPer) });
|
|
3783
|
-
}
|
|
3784
|
-
var rowsN = Math.max(1, chunks.length);
|
|
3785
|
-
var availS = bodyBottom - sR.y;
|
|
3786
|
-
var rowHS = fitRowH(availS - (rowsN - 1) * 0.3, rowsN, sR.h || S2.barH, 0.3);
|
|
3787
|
-
var cyS = sR.y;
|
|
3788
|
-
chunks.forEach(function (ck) {
|
|
3789
|
-
if (ck.label) {
|
|
3790
|
-
sh.push(txSp(sR.x, cyS - 0.02, sR.w, 0.26, [[{ t: ck.label, sz: 10, b: 1, col: S.faint, font: S.font, spc: 100 }]]));
|
|
3791
|
-
cyS += 0.28;
|
|
3792
|
-
}
|
|
3793
|
-
var n4 = Math.max(1, ck.items.length);
|
|
3794
|
-
var g4 = cols(sR.w, n4, S2.gap + S2.arrowW);
|
|
3795
|
-
ck.items.forEach(function (it, i) {
|
|
3796
|
-
var acc = isRec(it) && it.accent;
|
|
3797
|
-
var t4 = isRec(it) ? (it.t || '') : String(it[0] || '');
|
|
3798
|
-
var d4 = isRec(it) ? (it.d || '') : String(it[1] || '');
|
|
3799
|
-
var x4 = sR.x + i * g4.step;
|
|
3800
|
-
sh.push(shape('roundRect', x4, cyS, g4.w, rowHS, acc ? S.soft : S.surface, { adj: 5000, line: acc ? undefined : S.line }));
|
|
3801
|
-
var num = String(i + 1); if (num.length < 2) num = '0' + num;
|
|
3802
|
-
var pd = S2.pad, numH = S2.numH, ttlH = S2.titleH;
|
|
3803
|
-
sh.push(txSp(x4 + 0.14, cyS + pd, g4.w - 0.28, numH, [[{ t: num, sz: 10, b: 1, col: S.accent, font: S.font }]]));
|
|
3804
|
-
sh.push(txSp(x4 + 0.14, cyS + pd + numH, g4.w - 0.28, ttlH, [[{ t: t4, sz: 13, b: 1, col: acc ? S.accent : S.ink, font: S.font }]]));
|
|
3805
|
-
if (d4) sh.push(txSp(x4 + 0.14, cyS + pd + numH + ttlH, g4.w - 0.28, Math.max(0.2, rowHS - pd * 2 - numH - ttlH), [[{ t: d4, sz: 10, col: S.faint, font: S.font }]]));
|
|
3806
|
-
if (i < n4 - 1) {
|
|
3807
|
-
sh.push(txSp(x4 + g4.w, cyS, S2.arrowW + S2.gap, rowHS, [[{ t: '→', sz: 12, col: S.faint, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
|
|
3808
|
-
}
|
|
3809
|
-
});
|
|
3810
|
-
cyS += rowHS + 0.3;
|
|
3811
|
-
});
|
|
3812
|
-
} else if (type === 'table') {
|
|
3813
|
-
var tR = regOf('table', 'primary', { bottom: bodyBottom }) || { x: MX, y: CONTENT_TOP, w: CW, h: bodyBottom - CONTENT_TOP };
|
|
3814
|
-
var headRow = sec.table.head;
|
|
3815
|
-
var colW = sec.table.colW || headRow.map(function () { return tR.w / headRow.length; });
|
|
3816
|
-
sh.push(table(tR.x, tR.y, tR.w, colW, [headRow].concat(sec.table.rows), S, { availH: tR.h }));
|
|
3817
|
-
} else if (type === 'timeline') {
|
|
3818
|
-
var T = PT.timeline, ps = sec.phases || [], np = Math.max(1, ps.length);
|
|
3819
|
-
var tLR = regOf('timeline', 'primary') || { x: MX, y: T.labelY, w: CW };
|
|
3820
|
-
var stepW = (tLR.w - 1.0) / np;
|
|
3821
|
-
sh.push(shape('rect', tLR.x + 0.2, T.axisY, tLR.w - 0.4, 0.02, S.line));
|
|
3822
|
-
ps.forEach(function (p, i) {
|
|
3823
|
-
var x5 = tLR.x + 0.2 + i * stepW;
|
|
3824
|
-
var on = p[3] === 'done' || p[3] === 'now';
|
|
3825
|
-
sh.push(shape('ellipse', x5 - T.dotR / 2, T.dotY, T.dotR, T.dotR, on ? S.accent : S.line));
|
|
3826
|
-
sh.push(txSp(x5 - 0.1, T.labelY, stepW - 0.2, 0.4, [[{ t: p[0], sz: 12, b: 1, col: S.accent, font: S.font, spc: 100 }]]));
|
|
3827
|
-
sh.push(txSp(x5 - 0.1, T.nameY, stepW - 0.2, 0.5, [[{ t: p[1], sz: 17, b: 1, col: p[3] === 'now' ? S.accent : S.ink, font: S.fontDisplay }]]));
|
|
3828
|
-
sh.push(txSp(x5 - 0.1, T.descY, stepW - 0.25, T.descH, [[{ t: p[2] || '', sz: 12, col: S.body, font: S.font }]], { lineSpacing: 18 }));
|
|
3829
|
-
});
|
|
3830
|
-
} else if (type === 'bar') {
|
|
3831
|
-
var ch = sec.chart || {};
|
|
3832
|
-
var dcols2 = (ch.colors || dataColors(model.style, model.theme));
|
|
3833
|
-
var botY = chartBottom(!!sec.soWhat, !!sec.footnote) - (sec.note ? 0.42 : 0.05);
|
|
3834
|
-
var isHBar = ch.type === 'hbar';
|
|
3835
|
-
var bRR = regOf('bar', isHBar ? 'hbar' : 'primary', { bottom: botY }) ||
|
|
3836
|
-
{ x: isHBar ? MX : MX + PT.bar.chartX, y: isHBar ? PT.bar.hbarY0 : PT.bar.chartY,
|
|
3837
|
-
w: isHBar ? CW : CW - PT.bar.chartW, h: 3 };
|
|
3838
|
-
if (isHBar) {
|
|
3839
|
-
chartBlockShapes(sh, ch, S, bRR.x, bRR.y, bRR.w, bRR.h, dcols2);
|
|
3840
|
-
} else {
|
|
3841
|
-
chartBlockShapes(sh, ch, S, bRR.x, bRR.y, bRR.w, Math.max(1.5, bRR.h), dcols2);
|
|
3842
|
-
}
|
|
3843
|
-
if (sec.note) sh.push(txSp(MX, PT.note.y, CW, PT.note.h, [[{ t: sec.note, sz: 11, col: S.faint, font: S.font }]]));
|
|
3844
|
-
} else if (type === 'twocol' || type === 'threecol') {
|
|
3845
|
-
var ps2 = sec.paragraphs || [];
|
|
3846
|
-
var nCol = (type === 'threecol') ? 3 : 2;
|
|
3847
|
-
var gapC = (type === 'threecol') ? PT.research.col3Gap : PT.twocol.colGap;
|
|
3848
|
-
var tcR = regOf('twocol', 'primary', { top: bodyY, bottom: bodyBottom }) ||
|
|
3849
|
-
{ x: MX, y: bodyY, w: CW, h: bodyBottom - bodyY };
|
|
3850
|
-
var gC = cols(tcR.w, nCol, gapC);
|
|
3851
|
-
var perC = Math.ceil(ps2.length / nCol);
|
|
3852
|
-
var availC = tcR.h;
|
|
3853
|
-
/* 探针与 build_pptx.js 同算法:按「各栏字数之和」的最大值估算(保证两通道字号一致) */
|
|
3854
|
-
var longest = 0, gi, pi2;
|
|
3855
|
-
for (gi = 0; gi < nCol; gi++) {
|
|
3856
|
-
var colG = ps2.slice(gi * perC, (gi + 1) * perC), ssum = 0;
|
|
3857
|
-
for (pi2 = 0; pi2 < colG.length; pi2++) ssum += String(colG[pi2][1] || '').length;
|
|
3858
|
-
if (ssum > longest) longest = ssum;
|
|
3859
|
-
}
|
|
3860
|
-
var probe = [], w4;
|
|
3861
|
-
for (w4 = 0; w4 < longest / 120; w4++) probe.push(new Array(121).join('x'));
|
|
3862
|
-
if (!probe.length) probe.push(new Array(1 + longest).join('x'));
|
|
3863
|
-
var fzC = fitFont(probe, gC.w, availC, { max: 13.5, gapFactor: 1.2 });
|
|
3864
|
-
for (var ci3 = 0; ci3 < nCol; ci3++) {
|
|
3865
|
-
var colC = ps2.slice(ci3 * perC, (ci3 + 1) * perC);
|
|
3866
|
-
if (!colC.length) continue;
|
|
3867
|
-
var runsC = [];
|
|
3868
|
-
colC.forEach(function (p) {
|
|
3869
|
-
runsC.push([{ t: p[0] + ' ', sz: fzC, b: 1, col: S.ink, font: S.font },
|
|
3870
|
-
{ t: p[1], sz: fzC - 1, col: S.body, font: S.font }]);
|
|
3871
|
-
});
|
|
3872
|
-
sh.push(txSp(tcR.x + ci3 * gC.step, tcR.y, gC.w, availC, runsC,
|
|
3873
|
-
{ lineSpacing: fzC * 1.5, spaceBefore: 7 }));
|
|
3874
|
-
}
|
|
3875
|
-
} else if (type === 'halftable') {
|
|
3876
|
-
var yH = sec.lead ? PT.common.bodyYWithLead : PT.research.denseTableY;
|
|
3877
|
-
var hLR = regOf('halftable', 'left', { top: yH, bottom: bodyBottom }) ||
|
|
3878
|
-
{ x: MX, y: yH, w: CW * PT.research.halfTableW, h: bodyBottom - yH };
|
|
3879
|
-
var hRR = regOf('halftable', 'right', { top: yH, bottom: bodyBottom }) ||
|
|
3880
|
-
{ x: MX + CW * PT.research.halfChartX, y: yH, w: CW * PT.research.halfChartW, h: bodyBottom - yH };
|
|
3881
|
-
var lwH = hLR.w;
|
|
3882
|
-
if (sec.table && sec.table.head) {
|
|
3883
|
-
var colWH = sec.table.colW || sec.table.head.map(function () { return lwH / sec.table.head.length; });
|
|
3884
|
-
sh.push(table(hLR.x, hLR.y, lwH, colWH, [sec.table.head].concat(sec.table.rows || []), S,
|
|
3885
|
-
{ availH: hLR.h, maxRowH: PT.research.denseRowH + 0.1, minRowH: 0.28 }));
|
|
3886
|
-
}
|
|
3887
|
-
var ecH = sec.chart || {};
|
|
3888
|
-
if (ecH.labels && ecH.values) {
|
|
3889
|
-
chartBlockShapes(sh, ecH, S, hRR.x, hRR.y, hRR.w, Math.max(1.2, hRR.h),
|
|
3890
|
-
(ecH.colors || dataColors(model.style, model.theme)));
|
|
3891
|
-
}
|
|
3892
|
-
} else if (type === 'matrix') {
|
|
3893
|
-
var rhH = sec.rowHeads || [], chH = sec.colHeads || [];
|
|
3894
|
-
var mxR = regOf('matrix', 'primary', { bottom: bodyBottom }) ||
|
|
3895
|
-
{ x: MX, y: PT.research.matrixY, w: CW, h: bodyBottom - PT.research.matrixY };
|
|
3896
|
-
var mY = mxR.y, mLW = PT.research.matrixLabelW;
|
|
3897
|
-
var rowsC = sec.cells || [], nR2 = Math.max(1, rowsC.length);
|
|
3898
|
-
var availM = mxR.h - 0.42;
|
|
3899
|
-
var mCH = fitRowH(availM, nR2, PT.research.matrixCellH, 0.3);
|
|
3900
|
-
var mW = (mxR.w - mLW) / Math.max(1, chH.length);
|
|
3901
|
-
chH.forEach(function (ch3, ci) {
|
|
3902
|
-
sh.push(txSp(mxR.x + mLW + ci * mW, mY, mW, 0.34, [[{ t: ch3, sz: 11, b: 1, col: S.body, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
|
|
3903
|
-
});
|
|
3904
|
-
rowsC.forEach(function (rowC, ri) {
|
|
3905
|
-
var y6 = mY + 0.42 + ri * mCH;
|
|
3906
|
-
sh.push(txSp(mxR.x, y6 + (mCH - 0.5) / 2, mLW - 0.15, 0.5, [[{ t: rhH[ri] || '', sz: 11, b: 1, col: S.body, font: S.font }]], { align: 'r', anchor: 'ctr' }));
|
|
3907
|
-
rowC.forEach(function (cell, ci) {
|
|
3908
|
-
var acc = isRec(cell) && cell.accent;
|
|
3909
|
-
var txt = isRec(cell) ? (cell.t || '') : String(cell);
|
|
3910
|
-
var cx4 = mxR.x + mLW + ci * mW;
|
|
3911
|
-
sh.push(shape('roundRect', cx4 + 0.04, y6, mW - 0.08, mCH - 0.1, acc ? S.soft : S.surface,
|
|
3912
|
-
{ adj: 3000, line: acc ? undefined : S.line }));
|
|
3913
|
-
sh.push(txSp(cx4 + 0.14, y6 + 0.08, mW - 0.28, mCH - 0.26, [[{ t: txt, sz: 11, col: acc ? S.accent : S.body, font: S.font }]], { anchor: 'ctr' }));
|
|
3914
|
-
});
|
|
3915
|
-
});
|
|
3916
|
-
} else if (type === 'lane') {
|
|
3917
|
-
var lnS = sec.lanes || [], nL = Math.max(1, lnS.length);
|
|
3918
|
-
var startL = sec.lead ? PT.common.bodyYWithLead : PT.arch.fullStartY;
|
|
3919
|
-
var lnR = regOf('lane', 'primary', { mode: model.mode, bottom: bodyBottom }) ||
|
|
3920
|
-
{ x: MX, y: startL, w: CW, h: bodyBottom - startL };
|
|
3921
|
-
var availL = lnR.h;
|
|
3922
|
-
var lnH = fitRowH(availL - (nL - 1) * PT.arch.laneGap, nL, PT.arch.laneH, 0.3);
|
|
3923
|
-
var lnGap = PT.arch.laneGap, lnHW = PT.arch.laneHeadW;
|
|
3924
|
-
lnS.forEach(function (ln, li) {
|
|
3925
|
-
var y7 = lnR.y + li * (lnH + lnGap);
|
|
3926
|
-
sh.push(shape('roundRect', lnR.x, y7, lnR.w, lnH, S.surface, { adj: 2500, line: S.line }));
|
|
3927
|
-
sh.push(shape('rect', lnR.x, y7, lnHW, lnH, S.surface, { line: S.line }));
|
|
3928
|
-
sh.push(txSp(lnR.x + 0.08, y7, lnHW - 0.16, lnH, [[{ t: ln[0], sz: 11, b: 1, col: S.body, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
|
|
3929
|
-
var steps = ln[1] || [];
|
|
3930
|
-
var stepW2 = Math.min(PT.arch.stepMaxW, (lnR.w - lnHW - 0.4 - (steps.length - 1) * PT.arch.stepGap) / Math.max(1, steps.length));
|
|
3931
|
-
steps.forEach(function (st, si) {
|
|
3932
|
-
var stAcc = isRec(st) && st.accent;
|
|
3933
|
-
var stT = isRec(st) ? st.t : st;
|
|
3934
|
-
var sx2 = lnR.x + lnHW + 0.2 + si * (stepW2 + PT.arch.stepGap);
|
|
3935
|
-
sh.push(shape('roundRect', sx2, y7 + 0.13, stepW2, lnH - 0.26, stAcc ? S.soft : S.bg,
|
|
3936
|
-
{ adj: 4000, line: stAcc ? undefined : S.line }));
|
|
3937
|
-
sh.push(txSp(sx2 + 0.06, y7 + 0.13, stepW2 - 0.12, lnH - 0.26,
|
|
3938
|
-
[[{ t: stT, sz: 11, b: stAcc ? 1 : 0, col: stAcc ? S.accent : S.ink, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
|
|
3939
|
-
if (si < steps.length - 1) {
|
|
3940
|
-
sh.push(txSp(sx2 + stepW2, y7 + 0.13, PT.arch.stepGap, lnH - 0.26, [[{ t: '→', sz: 11, col: S.faint, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
|
|
3941
|
-
}
|
|
3942
|
-
});
|
|
3943
|
-
});
|
|
3944
|
-
} else if (type === 'cards') {
|
|
3945
|
-
var cdC = PT.cards, cds = sec.cards || [];
|
|
3946
|
-
var cR = regOf('cards', 'primary', { bottom: bodyBottom }) ||
|
|
3947
|
-
{ x: MX, y: cdC.startY, w: CW, h: bodyBottom - cdC.startY };
|
|
3948
|
-
var cN = Math.min(sec.columns || 3, cds.length || 1);
|
|
3949
|
-
var rN = Math.ceil((cds.length || 1) / cN);
|
|
3950
|
-
var gw2 = (cR.w - (cN - 1) * cdC.gap) / cN;
|
|
3951
|
-
var gh2 = fitRowH(cR.h - (rN - 1) * cdC.gap, rN, cdC.maxH, 0.6);
|
|
3952
|
-
cds.forEach(function (cd, i) {
|
|
3953
|
-
var col2 = i % cN, row2 = Math.floor(i / cN);
|
|
3954
|
-
var x8 = cR.x + col2 * (gw2 + cdC.gap), y8 = cR.y + row2 * (gh2 + cdC.gap);
|
|
3955
|
-
sh.push(shape('roundRect', x8, y8, gw2, gh2, S.surface, { line: S.line, adj: 5000 }));
|
|
3956
|
-
/* 卡片头路标(与 B 通道同源:accent 小方块 = PPTX 侧图标等价物) */
|
|
3957
|
-
var ico2 = 0.18;
|
|
3958
|
-
sh.push(shape('roundRect', x8 + 0.18, y8 + 0.2, ico2, ico2, S.accent, { adj: 2500 }));
|
|
3959
|
-
sh.push(txSp(x8 + 0.18 + ico2 + 0.1, y8 + 0.14, gw2 - 0.36 - ico2 - 0.1, cdC.titleH,
|
|
3960
|
-
[[{ t: cd.title, sz: 15, b: 1, col: S.ink, font: S.font }]]));
|
|
3961
|
-
var paras2 = (cd.points || []).map(function (pt) {
|
|
3962
|
-
var line;
|
|
3963
|
-
if (pt && pt.length != null && typeof pt !== 'string') {
|
|
3964
|
-
var k = pt[0] == null ? '' : String(pt[0]);
|
|
3965
|
-
var v = pt[1] == null ? '' : String(pt[1]);
|
|
3966
|
-
line = (k && v) ? ('· ' + k + ' ' + v) : ('· ' + (k || v));
|
|
3967
|
-
} else if (pt && typeof pt === 'object') {
|
|
3968
|
-
line = '· ' + String(pt.t || '') + (pt.d ? ' ' + String(pt.d) : '');
|
|
3969
|
-
} else {
|
|
3970
|
-
line = '· ' + String(pt == null ? '' : pt);
|
|
3971
|
-
}
|
|
3972
|
-
return [{ t: line, sz: 12, col: S.body, font: S.font }];
|
|
3973
|
-
});
|
|
3974
|
-
sh.push(txSp(x8 + 0.18, y8 + 0.14 + cdC.titleH + 0.06, gw2 - 0.36, Math.max(0.4, gh2 - cdC.titleH - 0.34), paras2,
|
|
3975
|
-
{ lineSpacing: 18 }));
|
|
3976
|
-
});
|
|
3977
|
-
} else if (type === 'split') {
|
|
3978
|
-
/* 双区自由组合页:左区与右区各可为 要点 / 图表 / 表格 / 图片(与 B 通道同语义)。 */
|
|
3979
|
-
var SP = PT.split;
|
|
3980
|
-
var lRs = regOf('split', 'left', { bottom: bodyBottom }) ||
|
|
3981
|
-
{ x: MX, y: SP.chartY, w: CW * SP.leftW, h: bodyBottom - SP.chartY };
|
|
3982
|
-
var rRs = regOf('split', 'right', { bottom: bodyBottom }) ||
|
|
3983
|
-
{ x: MX + CW * SP.rightX, y: SP.chartY, w: CW * SP.rightW, h: bodyBottom - SP.chartY };
|
|
3984
|
-
var lw2 = lRs.w, rw2 = rRs.w, rxL = rRs.x;
|
|
3985
|
-
var lt2 = (sec.left && sec.left.type) || 'points';
|
|
3986
|
-
/* 要点区渲染(左右两区共用;与 B 通道同算法同文本) */
|
|
3987
|
-
var drawPointsL = function (el, x, w) {
|
|
3988
|
-
var ptsP = (el && el.points) || [];
|
|
3989
|
-
var lyP = bodyY, availP = bodyBottom - lyP;
|
|
3990
|
-
var rowHP = fitRowH(availP, ptsP.length, SP.rowH, 0.3);
|
|
3991
|
-
var probeP = [], q5;
|
|
3992
|
-
for (q5 = 0; q5 < ptsP.length; q5++) probeP.push(String(ptsP[q5][0] || '') + String(ptsP[q5][1] || ''));
|
|
3993
|
-
var fzP = fitFont(probeP, w - 0.4, availP, { max: 14, gapFactor: 0.6 });
|
|
3994
|
-
ptsP.forEach(function (pt, i) {
|
|
3995
|
-
var ly2 = lyP + i * rowHP;
|
|
3996
|
-
sh.push(shape('rect', x, ly2 + rowHP / 2 - 0.06, 0.12, 0.12, S.accent));
|
|
3997
|
-
sh.push(txSp(x + 0.28, ly2, w - 0.4, rowHP - 0.04,
|
|
3998
|
-
[[{ t: pt[0] + ' ', sz: fzP, b: 1, col: S.ink, font: S.font },
|
|
3999
|
-
{ t: pt[1], sz: fzP - 1, col: S.body, font: S.font }]]));
|
|
4000
|
-
});
|
|
4001
|
-
};
|
|
4002
|
-
if (lt2 === 'table') {
|
|
4003
|
-
var lhL = [sec.left.head].concat(sec.left.rows);
|
|
4004
|
-
var lcwL = sec.left.colW || sec.left.head.map(function () { return lw2 / sec.left.head.length; });
|
|
4005
|
-
sh.push(table(lRs.x, SP.tableY, lw2, lcwL, lhL, S, { availH: bodyBottom - SP.tableY, maxRowH: SP.tableRowH, minRowH: 0.3 }));
|
|
4006
|
-
} else if (lt2 === 'image') {
|
|
4007
|
-
var imL = (sec.left && sec.left.image) || {};
|
|
4008
|
-
addImageEl(sh, imL.src, S, lRs.x, SP.chartY, lw2, Math.max(1.4, bodyBottom - SP.chartY),
|
|
4009
|
-
{ placeholder: !!imL.placeholder || !imL.src, layout: 'half', multi: false, fit: imL.fit });
|
|
4010
|
-
if (imL.caption) sh.push(txSp(lRs.x, SP.capY, lw2, 0.35, [[{ t: imL.caption, sz: 11, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
4011
|
-
} else if (lt2 !== 'points') {
|
|
4012
|
-
var lc2 = sec.left;
|
|
4013
|
-
chartBlockShapes(sh, lc2, S, lRs.x, SP.chartY, lw2,
|
|
4014
|
-
Math.max(1.4, bodyBottom - SP.chartY - (lc2.cap ? 0.1 : 0)),
|
|
4015
|
-
lc2.colors || dataColors(model.style, model.theme));
|
|
4016
|
-
if (lc2.cap) sh.push(txSp(lRs.x, SP.capY, lw2, 0.35, [[{ t: lc2.cap, sz: 12, col: S.body, font: S.font }]], { align: 'ctr' }));
|
|
4017
|
-
} else {
|
|
4018
|
-
drawPointsL(sec.left, lRs.x, lw2);
|
|
4019
|
-
}
|
|
4020
|
-
var rt = (sec.right && sec.right.type) || 'bar';
|
|
4021
|
-
if (rt === 'table') {
|
|
4022
|
-
var rrL = [sec.right.head].concat(sec.right.rows);
|
|
4023
|
-
var rcwL = sec.right.colW || sec.right.head.map(function () { return rw2 / sec.right.head.length; });
|
|
4024
|
-
sh.push(table(rxL, SP.tableY, rw2, rcwL, rrL, S, { availH: bodyBottom - SP.tableY, maxRowH: SP.tableRowH, minRowH: 0.3 }));
|
|
4025
|
-
} else if (rt === 'image') {
|
|
4026
|
-
var im2 = (sec.right && sec.right.image) || {};
|
|
4027
|
-
addImageEl(sh, im2.src, S, rxL, SP.chartY, rw2, Math.max(1.4, bodyBottom - SP.chartY),
|
|
4028
|
-
{ placeholder: !!im2.placeholder || !im2.src, layout: 'half', multi: false });
|
|
4029
|
-
if (im2.caption) sh.push(txSp(rxL, SP.capY, rw2, 0.35, [[{ t: im2.caption, sz: 11, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
4030
|
-
} else if (rt === 'points') {
|
|
4031
|
-
drawPointsL(sec.right, rxL, rw2);
|
|
4032
|
-
} else {
|
|
4033
|
-
var bc2 = sec.right;
|
|
4034
|
-
var dcols3 = bc2.colors || dataColors(model.style, model.theme);
|
|
4035
|
-
chartBlockShapes(sh, bc2, S, rxL, SP.chartY, rw2, Math.max(1.4, bodyBottom - SP.chartY - (bc2.cap ? 0.1 : 0)), dcols3);
|
|
4036
|
-
if (bc2.cap) sh.push(txSp(rxL, SP.capY, rw2, 0.35, [[{ t: bc2.cap, sz: 12, col: S.body, font: S.font }]], { align: 'ctr' }));
|
|
4037
|
-
}
|
|
4038
|
-
} else if (type === 'diagram') {
|
|
4039
|
-
var D = PT.diagram, lys = sec.layers || [];
|
|
4040
|
-
var dgR = regOf('diagram', 'primary', { mode: _mode, bottom: bodyBottom }) ||
|
|
4041
|
-
{ x: MX, y: D.bodyStartY, w: CW, h: bodyBottom - D.bodyStartY };
|
|
4042
|
-
var dStart = dgR.y;
|
|
4043
|
-
var dEnd = dgR.y + dgR.h;
|
|
4044
|
-
var nLay = Math.max(1, lys.length);
|
|
4045
|
-
var lh2 = Math.max(0.6, Math.min(D.maxLayerH, (dEnd - dStart) / nLay - D.layerGap));
|
|
4046
|
-
lys.forEach(function (lay, li) {
|
|
4047
|
-
var y9 = dStart + li * (lh2 + D.layerGap);
|
|
4048
|
-
var focus = lay[2] === 'focus';
|
|
4049
|
-
sh.push(shape('roundRect', dgR.x, y9, D.layerBarW, lh2, focus ? S.accent : S.surface,
|
|
4050
|
-
{ adj: 3000, line: focus ? undefined : S.line }));
|
|
4051
|
-
sh.push(txSp(dgR.x + 0.1, y9 + 0.1, D.layerBarW - 0.2, lh2 - 0.2, [[{ t: lay[0], sz: 12, b: 1, col: focus ? S.onAccent : S.body, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
|
|
4052
|
-
var nodes = lay[1] || [], nn2 = Math.max(1, nodes.length);
|
|
4053
|
-
var availW = dgR.w - D.layerBarW - 0.2;
|
|
4054
|
-
var nw2 = Math.min(D.nodeMaxW, (availW - (nn2 - 1) * D.nodeGap) / nn2);
|
|
4055
|
-
nodes.forEach(function (nd, ni) {
|
|
4056
|
-
var x10 = dgR.x + D.layerBarW + 0.2 + ni * (nw2 + D.nodeGap);
|
|
4057
|
-
var ndacc = isRec(nd) ? nd.accent : false;
|
|
4058
|
-
var ntt = isRec(nd) ? nd.t : nd;
|
|
4059
|
-
var nds = isRec(nd) ? (nd.d || '') : '';
|
|
4060
|
-
sh.push(shape('roundRect', x10, y9, nw2, lh2, ndacc ? S.soft : S.surface, { adj: 4000, line: S.line }));
|
|
4061
|
-
sh.push(txSp(x10 + 0.12, y9 + (nds ? 0.12 : lh2 / 2 - 0.2), nw2 - 0.24, 0.38,
|
|
4062
|
-
[[{ t: ntt, sz: 12.5, b: 1, col: ndacc ? S.accent : S.ink, font: S.font }]], { anchor: nds ? 't' : 'ctr' }));
|
|
4063
|
-
if (nds) sh.push(txSp(x10 + 0.12, y9 + lh2 - 0.5, nw2 - 0.24, 0.4, [[{ t: nds, sz: 10, col: S.faint, font: S.font }]]));
|
|
4064
|
-
});
|
|
4065
|
-
if (li < lys.length - 1) {
|
|
4066
|
-
var cy2 = y9 + lh2 + D.layerGap / 2;
|
|
4067
|
-
var cxm2 = dgR.x + D.layerBarW / 2;
|
|
4068
|
-
sh.push(shape('rect', cxm2 - 0.01, cy2 - 0.1, 0.02, 0.14, focus ? S.accent : S.line));
|
|
4069
|
-
sh.push(shape('rect', cxm2 - 0.06, cy2 + 0.04, 0.12, 0.02, focus ? S.accent : S.line));
|
|
4070
|
-
}
|
|
4071
|
-
});
|
|
4072
|
-
if (sec.legend) {
|
|
4073
|
-
var lx2 = MX;
|
|
4074
|
-
sec.legend.forEach(function (lg2) {
|
|
4075
|
-
sh.push(txSp(lx2, PH - 0.62, 3.2, 0.3, [[{ t: '● ' + lg2, sz: 10, col: S.faint, font: S.font }]]));
|
|
4076
|
-
lx2 += 2.0;
|
|
4077
|
-
});
|
|
4078
|
-
}
|
|
4079
|
-
} else if (type === 'exhibit') {
|
|
4080
|
-
var eReg = regOf('exhibit', 'primary')
|
|
4081
|
-
|| { x: MX, y: PT.exhibit.chartY, w: CW, badgeY: PT.exhibit.badgeY };
|
|
4082
|
-
if (sec.exhibitNo) {
|
|
4083
|
-
sh.push(txSp(MX, eReg.badgeY != null ? eReg.badgeY : PT.exhibit.badgeY, CW, 0.35, [[{ t: String(sec.exhibitNo).toUpperCase(), sz: 11.5, b: 1, col: S.accent, font: S.font, spc: 200 }]]));
|
|
4084
|
-
}
|
|
4085
|
-
var ec2 = sec.chart || {};
|
|
4086
|
-
if (ec2.labels && ec2.values) {
|
|
4087
|
-
var edcols2 = (ec2.colors || dataColors(model.style, model.theme));
|
|
4088
|
-
var hh2 = Math.max(1.2, chartBottom(!!sec.soWhat, !!sec.footnote) - eReg.y - 0.05);
|
|
4089
|
-
if (ec2.type === 'hbar') {
|
|
4090
|
-
chartBlockShapes(sh, ec2, S, eReg.x, eReg.y, eReg.w, hh2, edcols2);
|
|
4091
|
-
} else {
|
|
4092
|
-
chartBlockShapes(sh, ec2, S, eReg.x + 0.4, eReg.y - 0.1, eReg.w - 0.8, hh2 + 0.15, edcols2);
|
|
4093
|
-
}
|
|
4094
|
-
}
|
|
4095
|
-
} else if (type === 'sankey' || type === 'treemap' || type === 'boxplot'
|
|
4096
|
-
|| type === 'network' || type === 'marimekko' || type === 'streamgraph') {
|
|
4097
|
-
var infoR = regOf(type, 'primary', { bottom: bodyBottom }) ||
|
|
4098
|
-
{ x: MX, y: bodyY, w: CW, h: bodyBottom - bodyY };
|
|
4099
|
-
infoApprox(sh, sec, S, infoR.y, infoR.y + infoR.h);
|
|
4100
|
-
} else {
|
|
4101
|
-
/* points(默认):要点列表 + 可选右侧指标列 */
|
|
4102
|
-
var P2 = PT.points, pts3 = sec.points || [];
|
|
4103
|
-
var hasM = !!(sec.metrics && sec.metrics.length);
|
|
4104
|
-
var ptsR = regOf('points', 'primary', { top: bodyY, bottom: bodyBottom }) ||
|
|
4105
|
-
{ x: MX, y: bodyY, w: CW, h: bodyBottom - bodyY };
|
|
4106
|
-
/* 指标列最多 3 个 + 文本区严格分区(与 build_pptx.js 同算法) */
|
|
4107
|
-
var nm2 = hasM ? Math.min(sec.metrics.length, 3) : 0;
|
|
4108
|
-
var mw2 = hasM ? Math.min(P2.metricW, Math.max(1.6, (CW - 4.8) / nm2)) : 0;
|
|
4109
|
-
var textW = hasM ? Math.max(4.2, (PW - MX - nm2 * mw2) - MX - 0.35) : CW - 0.4;
|
|
4110
|
-
var availPt = ptsR.h;
|
|
4111
|
-
var rowHPt = fitRowH(availPt, pts3.length, P2.rowH, 0.3);
|
|
4112
|
-
var probeP = [], q5;
|
|
4113
|
-
for (q5 = 0; q5 < pts3.length; q5++) probeP.push(String(pts3[q5][0] || '') + String(pts3[q5][1] || ''));
|
|
4114
|
-
var fzP = fitFont(probeP, textW - 0.32, availPt, { max: 15, gapFactor: 0.55 });
|
|
4115
|
-
pts3.forEach(function (p, i) {
|
|
4116
|
-
var y11 = ptsR.y + i * rowHPt;
|
|
4117
|
-
sh.push(shape('rect', ptsR.x, y11 + rowHPt / 2 - P2.markSize / 2, P2.markSize, P2.markSize, S.accent));
|
|
4118
|
-
sh.push(txSp(ptsR.x + 0.32, y11, textW, rowHPt - 0.04,
|
|
4119
|
-
[[{ t: p[0] + ' ', sz: fzP, b: 1, col: S.ink, font: S.font },
|
|
4120
|
-
{ t: p[1], sz: fzP - 1, col: S.body, font: S.font }]]));
|
|
4121
|
-
});
|
|
4122
|
-
if (hasM) {
|
|
4123
|
-
var gx2 = PW - MX - nm2 * mw2;
|
|
4124
|
-
var vSz = nm2 >= 3 ? 28 : 34;
|
|
4125
|
-
sec.metrics.slice(0, nm2).forEach(function (m, i) {
|
|
4126
|
-
var x12 = gx2 + i * mw2;
|
|
4127
|
-
sh.push(txSp(x12, P2.metricY, mw2 - 0.2, P2.metricValH, [[{ t: m[0], sz: vSz, b: 1, col: S.accent, font: S.fontDisplay }]]));
|
|
4128
|
-
sh.push(txSp(x12, P2.metricCapY, mw2 - 0.2, 0.9, [[{ t: m[1], sz: 11, col: S.faint, font: S.font }]]));
|
|
4129
|
-
});
|
|
4130
|
-
}
|
|
4131
|
-
}
|
|
4132
|
-
/* research 通用可选件:so-what 结论条 + 页脚来源行 + 待核实条(共用同一槽位) */
|
|
4133
|
-
if (sec.soWhat) soWhatBar(sh, sec.soWhat, S);
|
|
4134
|
-
if (sec.footnote) footnoteLine(sh, sec.footnote, S);
|
|
4135
|
-
if (flagH) flagBar(sh, flagItems, S, flagY);
|
|
4136
|
-
slides.push(sh);
|
|
4137
|
-
});
|
|
4138
|
-
|
|
4139
|
-
/* 收尾(主题一致强调带——accent-soft 底 + 常规文字,浅色模式不再出深色页) */
|
|
4140
|
-
var CL = PT.closing;
|
|
4141
|
-
var cl = [shape('rect', 0, 0, PW, PH, S.soft)];
|
|
4142
|
-
cl.push(shape('rect', MX, CL.eyebrowY, 0.9, 0.045, S.accent));
|
|
4143
|
-
cl.push(txSp(MX, CL.eyebrowY + 0.16, CW, 0.4, [[{ t: '下一步', sz: 13, b: 1, col: S.accent, font: S.font, spc: 200 }]]));
|
|
4144
|
-
cl.push(txSp(MX, CL.titleY, CW - 1, CL.titleH, [[{ t: model.closing.title, sz: 30, b: 1, col: S.ink, font: S.fontDisplay }]]));
|
|
4145
|
-
var clPts = model.closing.points || [], clN = Math.max(1, clPts.length);
|
|
4146
|
-
var clW = (CW - (clN - 1) * 0.3) / clN;
|
|
4147
|
-
clPts.forEach(function (p, i) {
|
|
4148
|
-
var x13 = MX + i * (clW + 0.3);
|
|
4149
|
-
cl.push(shape('roundRect', x13, CL.pointsY - 0.2, clW, CL.pointTitleH + CL.pointBodyH + 0.32, S.surface, { line: S.line, adj: 4000 }));
|
|
4150
|
-
cl.push(txSp(x13 + 0.18, CL.pointsY, clW - 0.36, CL.pointTitleH, [[{ t: p[0], sz: 17, b: 1, col: S.accent, font: S.font }]]));
|
|
4151
|
-
cl.push(txSp(x13 + 0.18, CL.pointsY + CL.pointTitleH, clW - 0.36, CL.pointBodyH, [[{ t: p[1], sz: 12.5, col: S.body, font: S.font }]]));
|
|
4152
|
-
});
|
|
4153
|
-
slides.push(cl);
|
|
4154
|
-
|
|
4155
|
-
/* 页码 */
|
|
4156
|
-
var total = slides.length;
|
|
4157
|
-
slides.forEach(function (sh, i) {
|
|
4158
|
-
sh.push(txSp(PW - 1.4, (LAY.pageNumY != null ? LAY.pageNumY : PH - 0.5), 0.9, 0.3,
|
|
4159
|
-
[[{ t: (i + 1) + ' / ' + total, sz: 10, col: S.faint, font: S.font }]], { align: 'r' }));
|
|
4160
|
-
});
|
|
4161
|
-
return slides;
|
|
4162
|
-
}
|
|
4163
|
-
|
|
4164
|
-
/* ── 打包骨架 ── */
|
|
4165
|
-
var NS = {
|
|
4166
|
-
a: 'http://schemas.openxmlformats.org/drawingml/2006/main',
|
|
4167
|
-
p: 'http://schemas.openxmlformats.org/presentationml/2006/main',
|
|
4168
|
-
r: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
|
|
4169
|
-
};
|
|
4170
|
-
function slideXml(shapes) {
|
|
4171
|
-
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
|
4172
|
-
'<p:sld xmlns:a="' + NS.a + '" xmlns:p="' + NS.p + '" xmlns:r="' + NS.r + '">' +
|
|
4173
|
-
'<p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>' +
|
|
4174
|
-
'<p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr>' +
|
|
4175
|
-
shapes.join('') + '</p:spTree></p:cSld><p:clrMapOvr><a:overrideClrMapping bg1="lt1" tx1="dk1" bg2="lt2" tx2="dk2" accent1="accent1" accent2="accent2" accent3="accent3" accent4="accent4" accent5="accent5" accent6="accent6" hlink="hlink" folHlink="folHlink"/></p:clrMapOvr></p:sld>';
|
|
4176
|
-
}
|
|
4177
|
-
function relsXml(pairs) {
|
|
4178
|
-
var rs = pairs.map(function (p) {
|
|
4179
|
-
return '<Relationship Id="' + p[0] + '" Type="' + p[1] + '" Target="' + p[2] + '"/>';
|
|
4180
|
-
}).join('');
|
|
4181
|
-
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
|
4182
|
-
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' + rs + '</Relationships>';
|
|
4183
|
-
}
|
|
4184
|
-
var RT = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/';
|
|
4185
|
-
|
|
4186
|
-
/* 每页 slide XML(预览模态用:与导出同一引擎,所见即所得)
|
|
4187
|
-
theme="dark" 时用 PRESETS_DARK(与页面深色主题同源),默认 light */
|
|
4188
|
-
function themePresets(theme) {
|
|
4189
|
-
return (theme === 'dark' && typeof PRESETS_DARK !== 'undefined' &&
|
|
4190
|
-
PRESETS_DARK && Object.keys(PRESETS_DARK).length) ? PRESETS_DARK : PRESETS;
|
|
4191
|
-
}
|
|
4192
|
-
function slidesXml(model) {
|
|
4193
|
-
var S = themePresets(model && model.theme)[model.style] || PRESETS['business-blue'];
|
|
4194
|
-
return slidesOf(model, S).map(slideXml);
|
|
4195
|
-
}
|
|
4196
|
-
|
|
4197
|
-
/* ── 模型自检(schema 单源驱动:scripts/model-schema.json 经 sync_runtime 注入的 MODEL_SCHEMA;
|
|
4198
|
-
与 scripts/extract_model.py 本地校验同一份定义,双端不漂移) ── */
|
|
4199
|
-
function schemaGet(obj, path) {
|
|
4200
|
-
var cur = obj, ks = path.split('.');
|
|
4201
|
-
for (var i = 0; i < ks.length; i++) {
|
|
4202
|
-
if (cur == null || typeof cur !== 'object') return undefined;
|
|
4203
|
-
cur = cur[ks[i]];
|
|
4204
|
-
}
|
|
4205
|
-
return cur;
|
|
4206
|
-
}
|
|
4207
|
-
function schemaFieldOk(sec, spec) {
|
|
4208
|
-
/* 'anyof:a|b:c'(任一满足,用于 image 页型 src/items/placeholder 三选一);
|
|
4209
|
-
'a.b:array' / 'a.b:str' / 'a.b'(真值检查)——与 scripts/extract_model.py 同语义。 */
|
|
4210
|
-
if (spec.indexOf('anyof:') === 0) {
|
|
4211
|
-
return spec.slice(6).split('|').some(function (alt) { return alt && schemaFieldOk(sec, alt); });
|
|
4212
|
-
}
|
|
4213
|
-
var m = spec.match(/^(.*?)(?::(array|str))?$/), path = m[1], kind = m[2];
|
|
4214
|
-
var v = schemaGet(sec, path);
|
|
4215
|
-
if (kind === 'array') return Array.isArray(v) && v.length > 0;
|
|
4216
|
-
if (kind === 'str') return typeof v === 'string' && v.trim().length > 0;
|
|
4217
|
-
return !!v;
|
|
4218
|
-
}
|
|
4219
|
-
/* 图片模型软校验:版式 / 裁切策略 / 多图数量 / 外链 src——与 validate_pptx.py 硬门禁同口径,
|
|
4220
|
-
浏览器预览与 extract_model.py 都能提前看到问题。 */
|
|
4221
|
-
function imageModelWarnings(sec, where, warnings) {
|
|
4222
|
-
var IS = (typeof IMG !== 'undefined' && IMG) || {};
|
|
4223
|
-
var holders = [{ img: sec.image, at: where + '.image' }];
|
|
4224
|
-
if (sec.right && sec.right.image) holders.push({ img: sec.right.image, at: where + '.right.image' });
|
|
4225
|
-
holders.forEach(function (h) {
|
|
4226
|
-
var img = h.img;
|
|
4227
|
-
if (!img || typeof img !== 'object') return;
|
|
4228
|
-
var layouts = IS.layouts || ['full', 'half', 'bleed', 'grid', 'compare', 'wall'];
|
|
4229
|
-
var fitEnum = IS.fitEnum || ['cover', 'contain'];
|
|
4230
|
-
var items = Array.isArray(img.items) ? img.items.filter(Boolean) : [];
|
|
4231
|
-
var layout = String(img.layout || (items.length > 1 ? 'grid' : 'full')).toLowerCase();
|
|
4232
|
-
if (layouts.indexOf(layout) < 0) {
|
|
4233
|
-
warnings.push(h.at + '.layout="' + layout + '" 未知(应为 ' + layouts.join('/') + ')');
|
|
4234
|
-
}
|
|
4235
|
-
if (img.fit && fitEnum.indexOf(String(img.fit).toLowerCase()) < 0) {
|
|
4236
|
-
warnings.push(h.at + '.fit="' + img.fit + '" 未知(应为 ' + fitEnum.join('/') + ')');
|
|
4237
|
-
}
|
|
4238
|
-
if (img.placeholder && (img.src || items.length)) {
|
|
4239
|
-
warnings.push(h.at + ' 同时声明 placeholder 与图片源(placeholder 优先,图片将被忽略)');
|
|
4240
|
-
}
|
|
4241
|
-
if (items.length > (IS.maxPerPage || 6)) {
|
|
4242
|
-
warnings.push(h.at + '.items 共 ' + items.length + ' 张,超出单页上限 ' + (IS.maxPerPage || 6));
|
|
4243
|
-
}
|
|
4244
|
-
if (layout === 'compare' && items.length && items.length !== 2) {
|
|
4245
|
-
warnings.push(h.at + '.items 为 compare 版式时应为 2 张(当前 ' + items.length + ')');
|
|
4246
|
-
}
|
|
4247
|
-
[img].concat(items).forEach(function (it) {
|
|
4248
|
-
if (it && typeof it === 'object' && typeof it.src === 'string' &&
|
|
4249
|
-
/^\s*(?:https?:)?\/\//.test(it.src)) {
|
|
4250
|
-
warnings.push(h.at + ' 含外链 src(只允许 data: 内联或相对路径)');
|
|
4251
|
-
}
|
|
4252
|
-
});
|
|
4253
|
-
});
|
|
4254
|
-
}
|
|
4255
|
-
|
|
4256
|
-
function validateModel(model) {
|
|
4257
|
-
var missing = [], warnings = [];
|
|
4258
|
-
var M = (typeof MODEL_SCHEMA !== 'undefined' && MODEL_SCHEMA) || {};
|
|
4259
|
-
var pageTypes = M.pageTypes || {};
|
|
4260
|
-
if (!model || typeof model !== 'object') {
|
|
4261
|
-
return { ok: false, missing: ['window.REPORT_MODEL(内容模型缺失)'], warnings: warnings, pages: 0 };
|
|
4262
|
-
}
|
|
4263
|
-
(M.model && M.model.required || ['title', 'sections:array', 'closing.title', 'closing.points:array'])
|
|
4264
|
-
.forEach(function (spec) {
|
|
4265
|
-
if (!schemaFieldOk(model, spec)) missing.push(spec.split(':')[0] + '(顶层必填)');
|
|
4266
|
-
});
|
|
4267
|
-
var mode = model.mode || 'presentation';
|
|
4268
|
-
var hasAgenda = Array.isArray(model.agenda) && model.agenda.length > 0;
|
|
4269
|
-
var agMin = (M.model && M.model.agendaMin && M.model.agendaMin[mode]) || 0;
|
|
4270
|
-
if (agMin > 0 && (!hasAgenda || model.agenda.length < agMin)) {
|
|
4271
|
-
missing.push('agenda(大纲 ≥' + agMin + ' 条)');
|
|
4272
|
-
}
|
|
4273
|
-
var secs = model.sections || [];
|
|
4274
|
-
if (!Array.isArray(secs) || secs.length < ((M.model && M.model.sectionsMin) || 1)) {
|
|
4275
|
-
missing.push('sections(章节页 ≥1)');
|
|
4276
|
-
}
|
|
4277
|
-
if (!model.closing || !model.closing.title) missing.push('closing(收尾页)');
|
|
4278
|
-
secs.forEach(function (sec, i) {
|
|
4279
|
-
var t = sec.type || 'points';
|
|
4280
|
-
var def = pageTypes[t];
|
|
4281
|
-
var where = 'sections[' + i + ']';
|
|
4282
|
-
if (!def) { missing.push(where + '.type="' + t + '"(未知页型)'); return; }
|
|
4283
|
-
if (!sec.title) missing.push(where + '.title(第 ' + (i + 1) + ' 章标题)');
|
|
4284
|
-
(def.required || []).forEach(function (spec) {
|
|
4285
|
-
if (!schemaFieldOk(sec, spec)) missing.push(where + '.' + spec + '(' + (def.label || t) + '必填)');
|
|
4286
|
-
});
|
|
4287
|
-
if (def.modes && def.modes.indexOf(mode) < 0) {
|
|
4288
|
-
warnings.push('sections[' + i + '] 页型 "' + t + '" 文档口径适用于 ' + def.modes.join('/') + ',当前 mode="' + mode + '"');
|
|
4289
|
-
}
|
|
4290
|
-
imageModelWarnings(sec, where, warnings);
|
|
4291
|
-
});
|
|
4292
|
-
if (M.modes && model.mode && M.modes.indexOf(model.mode) < 0) {
|
|
4293
|
-
warnings.push('mode="' + model.mode + '" 未知,按 presentation 处理');
|
|
4294
|
-
}
|
|
4295
|
-
if (model.style && !PRESETS[model.style]) warnings.push('style="' + model.style + '" 未知,回落商务蓝');
|
|
4296
|
-
if (model.theme && model.theme !== 'light' && model.theme !== 'dark') {
|
|
4297
|
-
warnings.push('theme="' + model.theme + '" 未知(应为 light/dark),按浅色导出');
|
|
4298
|
-
}
|
|
4299
|
-
if (secs.length < ((M.model && M.model.sectionsRecommended) || 3)) {
|
|
4300
|
-
warnings.push('章节页仅 ' + secs.length + ' 页,正式报告建议 ≥3');
|
|
4301
|
-
}
|
|
4302
|
-
var agMax = (M.model && M.model.agendaComfortMax) || 16;
|
|
4303
|
-
if ((model.agenda || []).length > agMax) {
|
|
4304
|
-
warnings.push('agenda ' + model.agenda.length + ' 条超出单页舒适上限 ' + agMax + ',建议拆分');
|
|
4305
|
-
}
|
|
4306
|
-
var pages = 1 + (hasAgenda ? 1 : 0) + secs.length + 1; /* 封面+大纲(可选)+章节+收尾 */
|
|
4307
|
-
return { ok: missing.length === 0, missing: missing, warnings: warnings, pages: pages };
|
|
4308
|
-
}
|
|
4309
|
-
|
|
4310
|
-
var api = {
|
|
4311
|
-
slidesXml: slidesXml,
|
|
4312
|
-
validateModel: validateModel,
|
|
4313
|
-
/* 以下供 scripts/gen_channel_a.js 在 Node 侧组装完整 PPTX 包(回归双裁判内部质量路径) */
|
|
4314
|
-
slidesOf: slidesOf, slideXml: slideXml, relsXml: relsXml, NS: NS, RT: RT, PRESETS: PRESETS,
|
|
4315
|
-
PRESETS_DARK: (typeof PRESETS_DARK !== 'undefined' ? PRESETS_DARK : null),
|
|
4316
|
-
dataColors: dataColors,
|
|
4317
|
-
esc: esc, solid: solid, E: E
|
|
4318
|
-
};
|
|
4319
|
-
if (typeof module !== 'undefined' && module.exports) module.exports = api; /* Node:gen_channel_a.js 用 */
|
|
4320
|
-
g.TopPptHtml = api;
|
|
4321
|
-
})(typeof window !== 'undefined' ? window : globalThis);
|
|
4322
|
-
/* __TOPPPT_RUNTIME_END__ */
|
|
4323
|
-
</script>
|
|
4324
|
-
</body>
|
|
4325
|
-
</html>
|