siluzan-tso-cli 1.1.64-beta.2 → 1.1.64-beta.3
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/README.md +1 -1
- package/dist/index.js +689 -561
- package/dist/skill/SKILL.md +3 -2
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/README.md +1 -0
- package/dist/skill/references/analytics/account-analytics.md +2 -0
- package/dist/skill/references/core/intent-routing.md +5 -2
- package/dist/skill/references/core/playbooks.md +17 -0
- package/dist/skill/references/google-ads/demand-gen-api.md +1 -1
- package/dist/skill/references/report-templates/README.md +2 -0
- package/dist/skill/references/report-templates/REPORT-WORKFLOW.md +5 -2
- package/dist/skill/references/report-templates/demand-gen-analysis-report-excel.md +122 -0
- package/dist/skill/references/report-templates/demand-gen-analysis-report.md +166 -0
- package/dist/skill/report-templates/README.md +2 -0
- package/dist/skill/report-templates/REPORT-WORKFLOW.md +5 -2
- package/dist/skill/report-templates/demand-gen-analysis-report-excel.md +122 -0
- package/dist/skill/report-templates/demand-gen-analysis-report.html +402 -0
- package/dist/skill/report-templates/demand-gen-analysis-report.md +166 -0
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Demand Gen 投放数据分析汇总报告 — Excel
|
|
2
|
+
|
|
3
|
+
> **终稿语言**:Sheet 名和表头跟 `meta.lang`。英文用下面的英文表名和表头,不要另起结构。
|
|
4
|
+
> **适用场景**:用户明确要求 **Demand Gen 汇总报告 + Excel/xlsx**。工作流 **P4-DG**。
|
|
5
|
+
> **工作流**:与 `demand-gen-analysis-report.md` 共用拉数和 JSON。交付形态不同。
|
|
6
|
+
> **禁止**:调用 `google-analysis demand-gen-render` 代替 xlsx;加载宿主「xlsx / Excel」第三方 Skill;把 HTML 样例里的演示数字写入单元格。
|
|
7
|
+
|
|
8
|
+
用户没要 Excel 时,只走 HTML 纲要。两样都要时,先写同一份 `demand-gen-analysis-report.json`,再渲染 HTML,并用本文把这份 JSON 写成 xlsx。
|
|
9
|
+
|
|
10
|
+
样式按 `excel-style-guide.md`,脚本可 `import` 同目录 `excel-style-kit.mjs`。账户 ID、系列 ID、广告组 ID 用文本(`numFmt: '@'`)。
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 执行门禁
|
|
15
|
+
|
|
16
|
+
| # | 门禁 | 动作 |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| 1 | Read | `agent-conventions.md`、`demand-gen-analysis-report.md`、本文 |
|
|
19
|
+
| 2 | 账户 | `list-accounts -m Google -k <id> --json-out ./snap-dg` |
|
|
20
|
+
| 3 | 币种 | `items[].ma.currencyCode` |
|
|
21
|
+
| 4 | 拉数 | 与 HTML 纲要相同。Demand Gen 过滤 `channelTypeV2 = DEMAND_GEN`。版位用 `ad demand-gen report` |
|
|
22
|
+
| 5 | outline | 每个 `google-analysis` section 先 Read `*.outline.txt` |
|
|
23
|
+
| 6 | JSON | 脚本写入 `demand-gen-analysis-report.json`。缺 CRM 的格子写 `未提供` |
|
|
24
|
+
| 7 | 写簿 | 脚本只读这份 JSON 和落盘快照。9 张表都要有,空表也保留表头并写 `未提供` |
|
|
25
|
+
| 8 | 审阅 | `agent-conventions.md` §七。xlsx 无法 Read 时贴自检表和脚本 stdout |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 9 张表
|
|
30
|
+
|
|
31
|
+
中文表名固定如下。英文终稿换成括号里的名字。
|
|
32
|
+
|
|
33
|
+
| 表 | 内容来源 |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| `01_管理层总览`(`01_Overview`) | `meta`、`headline`、`summaryParagraph`、`headlineKpis`、`summaryPoints` |
|
|
36
|
+
| `02_分析范围`(`02_Scope`) | `scopeRows`、`conversionLevels` |
|
|
37
|
+
| `03_漏斗`(`03_Funnel`) | `funnel.stages`、`funnel.rates`、两条建议 |
|
|
38
|
+
| `04_核心指标`(`04_KPIs`) | `kpiGroups` |
|
|
39
|
+
| `05_趋势异常`(`05_Trend`) | `narrative.trend`。HTML 没有这一章 |
|
|
40
|
+
| `06_各维度数据`(`06_Dimensions`) | `dimensions`,每个维度一块,块标题用 `title` |
|
|
41
|
+
| `07_线索质量`(`07_Lead_Quality`) | `quality` |
|
|
42
|
+
| `08_行动建议`(`08_Actions`) | `actions` |
|
|
43
|
+
| `09_测试计划`(`09_Tests`) | `tests` |
|
|
44
|
+
|
|
45
|
+
### 01_管理层总览
|
|
46
|
+
|
|
47
|
+
- 第 1 行标题:`Demand Gen 投放数据分析汇总报告`(英文 `Demand Gen performance summary`)。
|
|
48
|
+
- 第 2 行:账户名、账户 ID、本期、对比期、币种、时区、业务目标、主转化。
|
|
49
|
+
- 第 3 行:可信度说明。
|
|
50
|
+
- 接着一块「一句话结论」。
|
|
51
|
+
- KPI 表头:`指标 | 本期 | 对比期变化 | 备注`。英文 `Metric | Current | Change | Note`。
|
|
52
|
+
- 要点表头:`标签 | 标题 | 说明 | 参见`。英文 `Tag | Title | Detail | See`。
|
|
53
|
+
|
|
54
|
+
### 02_分析范围
|
|
55
|
+
|
|
56
|
+
两块,不要合成一张没有表头的说明。
|
|
57
|
+
|
|
58
|
+
- 范围:`项目 | 内容`(`Item | Definition`),来自 `scopeRows`。
|
|
59
|
+
- 转化层级:`层级 | 定义 | 来源 | 报告用途`(`Level | Definition | Source | Use`)。
|
|
60
|
+
|
|
61
|
+
### 03_漏斗
|
|
62
|
+
|
|
63
|
+
`阶段 | 数量 | 逐级转化率 | 主要影响因素`(`Stage | Volume | Step rate | Driver`)。
|
|
64
|
+
|
|
65
|
+
阶段名和数量来自 `stages`,转化率和影响因素来自 `rates`。对不上的行分开写,不要为了对齐而编造。表下两行写 `adsAdvice`、`salesAdvice`。
|
|
66
|
+
|
|
67
|
+
### 04_核心指标
|
|
68
|
+
|
|
69
|
+
`层级 | 指标 | 本期 | 对比期变化`(`Layer | Metric | Current | Change`)。`层级` 用 `kpiGroups[].title`。
|
|
70
|
+
|
|
71
|
+
### 05_趋势异常
|
|
72
|
+
|
|
73
|
+
日表:`日期 | 花费 | 平台转化 | 平台CPA | 有效线索 | 备注`(`Date | Spend | Platform conversions | Platform CPA | Qualified leads | Note`)。
|
|
74
|
+
|
|
75
|
+
异常表:`现象 | 证据 | 原因 | 动作`(`What happened | Evidence | Cause | Action`)。
|
|
76
|
+
|
|
77
|
+
日花费和平台转化来自 `daily-metrics` 里 Demand Gen 能拆出来的部分。拆不出来就用账户日花费,并在备注写「账户合计,含非 Demand Gen」。有效线索没有 CRM 就写 `未提供`。
|
|
78
|
+
|
|
79
|
+
### 06_各维度数据
|
|
80
|
+
|
|
81
|
+
每个 `dimensions[]` 一块。表头用该块的 `columns`,不要改成另一套列。建议列(有数据才出现):
|
|
82
|
+
|
|
83
|
+
| 块 | 中文列 | 英文列 |
|
|
84
|
+
| --- | --- | --- |
|
|
85
|
+
| Campaign | 投放组合、花费、有效线索、有效成本、有效率、商机率、状态、修改建议 | Mix, Spend, Qualified leads, Cost per qualified, Qualified rate, Opportunity rate, Status, Change |
|
|
86
|
+
| 素材 | 素材角度 + 同上,可无商机率 | Angle |
|
|
87
|
+
| 受众 | 受众 + Campaign 列 | Audience |
|
|
88
|
+
| 地域 | 地域 + Campaign 列 | Location |
|
|
89
|
+
| 设备 | 设备 + Campaign 列 | Device |
|
|
90
|
+
| 版位 | 版位 + Campaign 列 | Channel |
|
|
91
|
+
| 来源 | 来源 + Campaign 列 | Source |
|
|
92
|
+
|
|
93
|
+
没有 CRM 时删掉有效线索及之后的业务列,改留「花费、平台转化、平台 CPA、修改建议」,并在块下注明。不要留一列全是 `未提供` 假装算过。
|
|
94
|
+
|
|
95
|
+
版位块只来自 `ad demand-gen report`。地域、设备、受众若仍是账户合计,块下写明,不要把它们说成 Demand Gen 已对账。
|
|
96
|
+
|
|
97
|
+
### 07_线索质量
|
|
98
|
+
|
|
99
|
+
`线索批次 | 表单 | 有效线索 | 商机 | 成交 | 成交收入 | 成熟度 | 建议`(`Cohort | Forms | Qualified | Opportunities | Wins | Revenue | Maturity | Note`)。
|
|
100
|
+
|
|
101
|
+
没有批次资料时只留一行 `未提供`。表下写 `businessConclusion` 和 `splitFactors`。
|
|
102
|
+
|
|
103
|
+
### 08_行动建议
|
|
104
|
+
|
|
105
|
+
`优先级 | 动作 | 证据 | 具体修改 | 验证阈值 | 负责人 | 时限 | 风险`(`Priority | Action | Evidence | Change | Check | Owner | Due | Risk`)。
|
|
106
|
+
|
|
107
|
+
优先级只用 `P0` / `P1` / `P2`。
|
|
108
|
+
|
|
109
|
+
### 09_测试计划
|
|
110
|
+
|
|
111
|
+
`测试项 | 假设 | 对照 / 实验组 | 主指标 | 样本量 | 周期 | 停止规则`(`Test | Hypothesis | Control / variant | Metric | Sample | Window | Stop rule`)。
|
|
112
|
+
|
|
113
|
+
一个测试只改一个主要变量。样本还没达到时,停止规则写「样本不足则不判胜负」。
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 不要做的事
|
|
118
|
+
|
|
119
|
+
- 不要把 9 张表收成 1 张「汇总」。
|
|
120
|
+
- 不要把 Search / PMax 系列写进 Campaign 块。
|
|
121
|
+
- 不要用模板演示账户的 29 天、$42,680、286、74、19、ROAS 2.84。
|
|
122
|
+
- 对比期没拉时,变化列写 `未拉对比期`,不要写 0% 或 `—` 假装持平。
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
5
|
+
<title>Demand Gen 投放数据分析汇总报告|客户与运营共用版</title>
|
|
6
|
+
<style>
|
|
7
|
+
:root{--ink:#172033;--muted:#687386;--line:#e3e8f0;--bg:#f5f7fb;--card:#fff;--blue:#2f6fed;--blue2:#eaf1ff;--green:#16845b;--green2:#e8f7ef;--orange:#b76713;--orange2:#fff4df;--red:#b84242;--red2:#ffeded;--purple:#7146c9;--shadow:0 7px 24px rgba(30,49,83,.07)}
|
|
8
|
+
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.65 -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans CJK SC","Microsoft YaHei",Arial,sans-serif}.wrap{max-width:1440px;margin:auto;padding:0 28px}.top{background:#101a30;color:#fff;font-size:12px}.top .wrap{display:flex;justify-content:space-between;padding-top:11px;padding-bottom:11px}.badge{display:inline-block;border-radius:999px;padding:3px 9px;font-size:11px;font-weight:750;white-space:nowrap}.blue{background:var(--blue2);color:var(--blue)}.green{background:var(--green2);color:var(--green)}.orange{background:var(--orange2);color:var(--orange)}.red{background:var(--red2);color:var(--red)}.purple{background:#f0eaff;color:var(--purple)}header{background:linear-gradient(135deg,#18284b,#2c5bb0);color:#fff;padding:38px 0 31px}h1{font-size:32px;letter-spacing:-.7px;margin:0 0 7px}header p{margin:0;color:#dbe6fb}.meta{display:flex;flex-wrap:wrap;gap:9px;margin-top:20px}.meta span{padding:7px 10px;border:1px solid #ffffff3b;background:#ffffff12;border-radius:7px;font-size:12px}.note{margin-top:17px;background:#fff8df;color:#594600;border:1px solid #eed486;border-radius:8px;padding:10px 13px;font-size:12px}nav{position:sticky;top:0;z-index:5;background:#fffffff2;border-bottom:1px solid var(--line);box-shadow:0 2px 10px #2034510a}nav .wrap{display:flex;gap:20px;overflow:auto;white-space:nowrap}nav a{color:#536177;text-decoration:none;font-size:12px;font-weight:700;padding:11px 0}main{padding:24px 0 60px}section{margin-bottom:25px}.head{display:flex;justify-content:space-between;align-items:end;gap:15px;margin-bottom:12px}.head h2{font-size:20px;margin:0;letter-spacing:-.2px}.head p{font-size:12px;color:var(--muted);margin:0}.grid{display:grid;gap:14px}.g2{grid-template-columns:repeat(2,minmax(0,1fr))}.g4{grid-template-columns:repeat(4,minmax(0,1fr))}.card{background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow);padding:17px}.card h3{font-size:15px;margin:0 0 11px}.call{border-left:4px solid var(--blue);background:#f2f6ff;padding:11px 14px;border-radius:0 8px 8px 0;margin-top:10px}.call.warn{border-color:#d88a1c;background:#fff8eb}.big{font-size:18px;line-height:1.55;font-weight:700}.kpi{min-height:96px}.kpi .label{color:var(--muted);font-size:12px}.kpi .value{font-size:28px;font-weight:800;letter-spacing:-.5px;margin:8px 0 2px}.up{color:var(--green)}.down{color:var(--red)}.neutral{color:var(--muted)}.small{font-size:12px;color:var(--muted)}.tiny{font-size:11px;color:var(--muted)}table{width:100%;border-collapse:collapse;font-size:12px}th{text-align:left;color:#637087;background:#f8faff;white-space:nowrap}th,td{padding:9px 8px;border-bottom:1px solid var(--line);vertical-align:middle}tr:last-child td{border-bottom:0}.num{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}.scroll{overflow:auto}.barrow{display:grid;grid-template-columns:150px 1fr 90px;gap:9px;align-items:center;margin:10px 0;font-size:12px}.bar{height:9px;background:#e9eef6;border-radius:20px;overflow:hidden}.bar i{display:block;height:100%;border-radius:20px;background:var(--blue)}.bar.green i{background:var(--green)}.bar.orange i{background:#e39a27}.bar.purple i{background:var(--purple)}.action{display:grid;grid-template-columns:45px 1fr 135px;gap:12px;padding:12px 0;border-bottom:1px solid var(--line)}.action:last-child{border-bottom:0}.priority{font-size:11px;font-weight:800;border-radius:5px;padding:4px 7px;text-align:center;height:max-content}.p0{background:var(--red2);color:var(--red)}.p1{background:var(--orange2);color:var(--orange)}.p2{background:var(--blue2);color:var(--blue)}.action strong{display:block}.action small{display:block;color:var(--muted);margin-top:2px}.owner{text-align:right;color:var(--muted);font-size:11px}.section-note{margin-top:10px;border-top:1px solid var(--line);padding-top:10px;font-size:12px;color:#4c596c}.row-good td{background:#f2fbf6}.row-warn td{background:#fffaf0}.row-bad td{background:#fff3f3}.row-neutral td{background:#fbfcfe}.foot{border-top:1px solid var(--line);padding:21px 0 45px;color:var(--muted);font-size:12px}.missing{color:var(--muted)}@media(max-width:900px){.g4{grid-template-columns:repeat(2,minmax(0,1fr))}.g2{grid-template-columns:1fr}.wrap{padding-left:16px;padding-right:16px}h1{font-size:27px}}@media(max-width:600px){.g4{grid-template-columns:1fr}.action{grid-template-columns:42px 1fr}.owner{grid-column:2;text-align:left}}@media print{nav{display:none}.top{background:#fff;color:#111;border-bottom:1px solid #ddd}header{background:#fff;color:#111;border-bottom:2px solid #18284b}header p,.meta span{color:#333}.meta span{border-color:#aaa;background:#fff}.card{box-shadow:none;break-inside:avoid}section{break-inside:avoid}body{background:#fff}}
|
|
9
|
+
</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div class="top"><div class="wrap"><span id="top-title">Demand Gen 投放数据分析汇总报告|客户与运营共用版</span><span class="badge blue" id="top-badge"></span></div></div>
|
|
13
|
+
<header><div class="wrap" id="hero"></div></header>
|
|
14
|
+
<nav><div class="wrap" id="nav"></div></nav>
|
|
15
|
+
<main><div class="wrap" id="main"></div></main>
|
|
16
|
+
<footer class="foot"><div class="wrap" id="foot"></div></footer>
|
|
17
|
+
<script>
|
|
18
|
+
window.__DEMAND_GEN_REPORT__ = window.__DEMAND_GEN_REPORT__ || null;
|
|
19
|
+
(function () {
|
|
20
|
+
var data = window.__DEMAND_GEN_REPORT__;
|
|
21
|
+
var main = document.getElementById("main");
|
|
22
|
+
if (!data || !data.meta || !data.narrative) {
|
|
23
|
+
main.textContent = "报告数据未注入。请用 google-analysis demand-gen-render 生成终稿。";
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
var en = data.meta.lang === "en";
|
|
27
|
+
var copy = en
|
|
28
|
+
? {
|
|
29
|
+
title: "Demand Gen performance summary",
|
|
30
|
+
sub: "One report for the client readout and the operator's next decisions.",
|
|
31
|
+
nav: ["Summary", "Scope", "KPIs", "Funnel", "Breakdowns", "Lead quality", "Actions", "Tests"],
|
|
32
|
+
sections: [
|
|
33
|
+
["1. Management summary", "Is the spend working, and should budget increase?"],
|
|
34
|
+
["2. Scope and conversion definitions", "Every breakdown uses the same definitions."],
|
|
35
|
+
["3. Core metrics", "Front end, platform, and business stay separate."],
|
|
36
|
+
["4. Funnel", "Each rate uses the previous stage as the denominator."],
|
|
37
|
+
["5. Breakdowns and changes", "Data, reading, and the change to make."],
|
|
38
|
+
["6. Lead quality", "Judge by lead-created cohort, not by unfinished days."],
|
|
39
|
+
["7. Actions", "One ordered list."],
|
|
40
|
+
["8. Next-cycle tests", "One test changes one variable."]
|
|
41
|
+
],
|
|
42
|
+
credibility: "Credibility: ",
|
|
43
|
+
customer: "Client reading: ",
|
|
44
|
+
ads: "Ads: ",
|
|
45
|
+
sales: "Sales: ",
|
|
46
|
+
evidence: "Evidence: ",
|
|
47
|
+
action: "Change: ",
|
|
48
|
+
verify: "Check: ",
|
|
49
|
+
owner: "Owner: ",
|
|
50
|
+
due: "Due: ",
|
|
51
|
+
risk: "Risk: ",
|
|
52
|
+
missing: "Not provided",
|
|
53
|
+
foot: "Shared client and operator summary. Numbers come from the injected report JSON."
|
|
54
|
+
}
|
|
55
|
+
: {
|
|
56
|
+
title: "Demand Gen 投放数据分析汇总报告",
|
|
57
|
+
sub: "一份报告同时完成:客户效果讲解、运营维度核实和下一步优化决策。",
|
|
58
|
+
nav: ["结论", "口径", "核心指标", "漏斗", "各维度数据", "线索质量", "行动建议", "测试计划"],
|
|
59
|
+
sections: [
|
|
60
|
+
["1. 管理层结论", "先回答客户:投入是否有效,下一步是否应该加预算"],
|
|
61
|
+
["2. 分析范围与转化口径", "所有维度数据都在同一套口径下比较"],
|
|
62
|
+
["3. 核心指标", "前端、平台、业务三层分开,避免只看平台 CPA"],
|
|
63
|
+
["4. 转化漏斗", "每一级转化率的分母固定为上一级"],
|
|
64
|
+
["5. 各维度数据与分析建议", "每个维度都展示数据、业务解读和具体修改动作"],
|
|
65
|
+
["6. 线索质量与业务结果", "按线索创建批次观察成熟度,避免把未成熟数据当成最终效果"],
|
|
66
|
+
["7. 统一行动建议", "把各维度结论合并成一套有先后顺序的执行计划"],
|
|
67
|
+
["8. 下一周期测试计划", "每个测试只改变一个主要变量,并在同一人群内比较"]
|
|
68
|
+
],
|
|
69
|
+
credibility: "可信度说明:",
|
|
70
|
+
customer: "客户解读:",
|
|
71
|
+
ads: "广告侧:",
|
|
72
|
+
sales: "销售侧:",
|
|
73
|
+
evidence: "依据:",
|
|
74
|
+
action: "动作:",
|
|
75
|
+
verify: "验证:",
|
|
76
|
+
owner: "负责人:",
|
|
77
|
+
due: "时限:",
|
|
78
|
+
risk: "风险:",
|
|
79
|
+
missing: "未提供",
|
|
80
|
+
foot: "客户汇报与运营核实共用的汇总报告。正文按「结论—指标—漏斗—各维度—质量—行动—测试」展开。趋势异常只在 Excel 第 5 张表。"
|
|
81
|
+
};
|
|
82
|
+
var ids = ["summary", "scope", "kpi", "funnel", "dimensions", "quality", "actions", "tests"];
|
|
83
|
+
var meta = data.meta;
|
|
84
|
+
var narrative = data.narrative;
|
|
85
|
+
|
|
86
|
+
function el(tag, className, text) {
|
|
87
|
+
var node = document.createElement(tag);
|
|
88
|
+
if (className) node.className = className;
|
|
89
|
+
if (text != null && text !== "") node.textContent = String(text);
|
|
90
|
+
return node;
|
|
91
|
+
}
|
|
92
|
+
function show(value) {
|
|
93
|
+
var text = value == null ? "" : String(value).trim();
|
|
94
|
+
return text || copy.missing;
|
|
95
|
+
}
|
|
96
|
+
function section(index) {
|
|
97
|
+
var box = el("section");
|
|
98
|
+
box.id = ids[index];
|
|
99
|
+
var head = el("div", "head");
|
|
100
|
+
var titles = el("div");
|
|
101
|
+
titles.appendChild(el("h2", "", copy.sections[index][0]));
|
|
102
|
+
titles.appendChild(el("p", "", copy.sections[index][1]));
|
|
103
|
+
head.appendChild(titles);
|
|
104
|
+
box.appendChild(head);
|
|
105
|
+
return box;
|
|
106
|
+
}
|
|
107
|
+
function kpiCard(item) {
|
|
108
|
+
var card = el("div", "card kpi");
|
|
109
|
+
card.appendChild(el("div", "label", item.label));
|
|
110
|
+
card.appendChild(el("div", "value", show(item.value)));
|
|
111
|
+
if (item.delta) card.appendChild(el("div", item.tone || "neutral", item.delta));
|
|
112
|
+
return card;
|
|
113
|
+
}
|
|
114
|
+
function table(columns, rows) {
|
|
115
|
+
var wrap = el("div", "scroll");
|
|
116
|
+
var node = el("table");
|
|
117
|
+
var head = el("thead");
|
|
118
|
+
var hr = el("tr");
|
|
119
|
+
(columns || []).forEach(function (name, index) {
|
|
120
|
+
hr.appendChild(el("th", index > 0 ? "num" : "", name));
|
|
121
|
+
});
|
|
122
|
+
head.appendChild(hr);
|
|
123
|
+
node.appendChild(head);
|
|
124
|
+
var body = el("tbody");
|
|
125
|
+
(rows || []).forEach(function (row) {
|
|
126
|
+
var tr = el("tr", row.tone ? "row-" + row.tone : "");
|
|
127
|
+
(row.cells || []).forEach(function (cell, index) {
|
|
128
|
+
tr.appendChild(el("td", index > 0 ? "num" : "", show(cell)));
|
|
129
|
+
});
|
|
130
|
+
body.appendChild(tr);
|
|
131
|
+
});
|
|
132
|
+
if (!(rows || []).length) {
|
|
133
|
+
var empty = el("tr");
|
|
134
|
+
var td = el("td", "missing", copy.missing);
|
|
135
|
+
td.colSpan = Math.max((columns || []).length, 1);
|
|
136
|
+
empty.appendChild(td);
|
|
137
|
+
body.appendChild(empty);
|
|
138
|
+
}
|
|
139
|
+
node.appendChild(body);
|
|
140
|
+
wrap.appendChild(node);
|
|
141
|
+
return wrap;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
document.title = copy.title;
|
|
145
|
+
document.getElementById("top-title").textContent = copy.title + (en ? " | shared" : "|客户与运营共用版");
|
|
146
|
+
document.getElementById("top-badge").textContent = show(meta.confidence || meta.accountId);
|
|
147
|
+
|
|
148
|
+
var nav = document.getElementById("nav");
|
|
149
|
+
copy.nav.forEach(function (label, index) {
|
|
150
|
+
var link = el("a", "", label);
|
|
151
|
+
link.href = "#" + ids[index];
|
|
152
|
+
nav.appendChild(link);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
var hero = document.getElementById("hero");
|
|
156
|
+
hero.appendChild(el("h1", "", copy.title));
|
|
157
|
+
hero.appendChild(el("p", "", copy.sub));
|
|
158
|
+
var chips = el("div", "meta");
|
|
159
|
+
var period = (en ? "Period: " : "分析周期:") + show(meta.startDate) + "–" + show(meta.endDate);
|
|
160
|
+
if (meta.periodDays) period += en ? " (" + meta.periodDays + " days)" : "(" + meta.periodDays + " 天)";
|
|
161
|
+
chips.appendChild(el("span", "", period));
|
|
162
|
+
if (meta.compareStartDate && meta.compareEndDate) {
|
|
163
|
+
var compare = (en ? "Compare: " : "对比周期:") + meta.compareStartDate + "–" + meta.compareEndDate;
|
|
164
|
+
if (meta.compareDays) compare += en ? " (" + meta.compareDays + " days)" : "(" + meta.compareDays + " 天)";
|
|
165
|
+
chips.appendChild(el("span", "", compare));
|
|
166
|
+
}
|
|
167
|
+
chips.appendChild(el("span", "", (en ? "Currency: " : "币种:") + show(meta.currency)));
|
|
168
|
+
if (meta.timezone) chips.appendChild(el("span", "", (en ? "Timezone: " : "时区:") + meta.timezone));
|
|
169
|
+
chips.appendChild(el("span", "", (en ? "Account: " : "账户:") + show(meta.accountName || meta.accountId)));
|
|
170
|
+
if (meta.objective) chips.appendChild(el("span", "", (en ? "Goal: " : "业务目标:") + meta.objective));
|
|
171
|
+
if (meta.primaryConversion) chips.appendChild(el("span", "", (en ? "Primary conversion: " : "主转化:") + meta.primaryConversion));
|
|
172
|
+
hero.appendChild(chips);
|
|
173
|
+
if (meta.credibilityNote) {
|
|
174
|
+
var note = el("div", "note");
|
|
175
|
+
note.appendChild(el("strong", "", copy.credibility));
|
|
176
|
+
note.appendChild(document.createTextNode(meta.credibilityNote));
|
|
177
|
+
hero.appendChild(note);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
var summary = section(0);
|
|
181
|
+
var summaryGrid = el("div", "grid g2");
|
|
182
|
+
var left = el("div", "card");
|
|
183
|
+
var call = el("div", "call");
|
|
184
|
+
call.appendChild(el("div", "big", narrative.headline));
|
|
185
|
+
if (narrative.summaryParagraph) call.appendChild(el("p", "", narrative.summaryParagraph));
|
|
186
|
+
left.appendChild(call);
|
|
187
|
+
(narrative.summaryPoints || []).forEach(function (point) {
|
|
188
|
+
var row = el("div", "action");
|
|
189
|
+
row.appendChild(el("span", "priority p1", point.tag || "•"));
|
|
190
|
+
var body = el("div");
|
|
191
|
+
body.appendChild(el("strong", "", point.title));
|
|
192
|
+
body.appendChild(el("small", "", point.detail));
|
|
193
|
+
row.appendChild(body);
|
|
194
|
+
row.appendChild(el("span", "owner", point.owner || ""));
|
|
195
|
+
left.appendChild(row);
|
|
196
|
+
});
|
|
197
|
+
var right = el("div", "card");
|
|
198
|
+
right.appendChild(el("h3", "", en ? "Headline numbers" : "本期最重要的数字"));
|
|
199
|
+
var kpis = el("div", "grid g2");
|
|
200
|
+
(narrative.headlineKpis || []).forEach(function (item) { kpis.appendChild(kpiCard(item)); });
|
|
201
|
+
right.appendChild(kpis);
|
|
202
|
+
if (narrative.customerRead) {
|
|
203
|
+
var read = el("div", "call warn");
|
|
204
|
+
read.appendChild(el("strong", "", copy.customer));
|
|
205
|
+
read.appendChild(document.createTextNode(narrative.customerRead));
|
|
206
|
+
right.appendChild(read);
|
|
207
|
+
}
|
|
208
|
+
summaryGrid.appendChild(left);
|
|
209
|
+
summaryGrid.appendChild(right);
|
|
210
|
+
summary.appendChild(summaryGrid);
|
|
211
|
+
main.appendChild(summary);
|
|
212
|
+
|
|
213
|
+
var scope = section(1);
|
|
214
|
+
var scopeGrid = el("div", "grid g2");
|
|
215
|
+
var scopeCard = el("div", "card");
|
|
216
|
+
scopeCard.appendChild(el("h3", "", en ? "What this report covers" : "本报告分析什么"));
|
|
217
|
+
scopeCard.appendChild(table(
|
|
218
|
+
en ? ["Item", "Definition"] : ["项目", "内容"],
|
|
219
|
+
(narrative.scopeRows || []).map(function (row) { return { cells: [row.item, row.value] }; })
|
|
220
|
+
));
|
|
221
|
+
var levelCard = el("div", "card");
|
|
222
|
+
levelCard.appendChild(el("h3", "", en ? "Conversion levels" : "转化层级"));
|
|
223
|
+
levelCard.appendChild(table(
|
|
224
|
+
en ? ["Level", "Definition", "Source", "Use"] : ["层级", "定义", "来源", "报告用途"],
|
|
225
|
+
(narrative.conversionLevels || []).map(function (row) {
|
|
226
|
+
return { cells: [row.level, row.definition, row.source, row.use] };
|
|
227
|
+
})
|
|
228
|
+
));
|
|
229
|
+
scopeGrid.appendChild(scopeCard);
|
|
230
|
+
scopeGrid.appendChild(levelCard);
|
|
231
|
+
scope.appendChild(scopeGrid);
|
|
232
|
+
main.appendChild(scope);
|
|
233
|
+
|
|
234
|
+
var kpi = section(2);
|
|
235
|
+
(narrative.kpiGroups || []).forEach(function (group) {
|
|
236
|
+
kpi.appendChild(el("h3", "", group.title));
|
|
237
|
+
var grid = el("div", "grid g4");
|
|
238
|
+
(group.items || []).forEach(function (item) { grid.appendChild(kpiCard(item)); });
|
|
239
|
+
kpi.appendChild(grid);
|
|
240
|
+
});
|
|
241
|
+
main.appendChild(kpi);
|
|
242
|
+
|
|
243
|
+
var funnel = section(3);
|
|
244
|
+
if (narrative.funnel && narrative.funnel.bottleneck) {
|
|
245
|
+
funnel.querySelector(".head").appendChild(el("span", "badge orange", narrative.funnel.bottleneck));
|
|
246
|
+
}
|
|
247
|
+
var funnelCard = el("div", "card");
|
|
248
|
+
var funnelGrid = el("div", "grid g2");
|
|
249
|
+
var bars = el("div");
|
|
250
|
+
bars.appendChild(el("h3", "", en ? "Volume" : "漏斗数量"));
|
|
251
|
+
(narrative.funnel && narrative.funnel.stages || []).forEach(function (stage) {
|
|
252
|
+
var row = el("div", "barrow");
|
|
253
|
+
row.appendChild(el("span", "", stage.name));
|
|
254
|
+
var bar = el("div", "bar " + (stage.tone || ""));
|
|
255
|
+
var fill = document.createElement("i");
|
|
256
|
+
var width = Number(stage.width);
|
|
257
|
+
fill.style.width = (Number.isFinite(width) ? Math.max(0, Math.min(100, width)) : 0) + "%";
|
|
258
|
+
bar.appendChild(fill);
|
|
259
|
+
row.appendChild(bar);
|
|
260
|
+
row.appendChild(el("b", "", show(stage.value)));
|
|
261
|
+
bars.appendChild(row);
|
|
262
|
+
});
|
|
263
|
+
var rates = el("div");
|
|
264
|
+
rates.appendChild(el("h3", "", en ? "Step rates" : "逐级转化率"));
|
|
265
|
+
rates.appendChild(table(
|
|
266
|
+
en ? ["Step", "Rate", "Driver"] : ["阶段", "转化率", "主要影响因素"],
|
|
267
|
+
(narrative.funnel && narrative.funnel.rates || []).map(function (row) {
|
|
268
|
+
return { cells: [row.stage, row.rate, row.factor] };
|
|
269
|
+
})
|
|
270
|
+
));
|
|
271
|
+
funnelGrid.appendChild(bars);
|
|
272
|
+
funnelGrid.appendChild(rates);
|
|
273
|
+
funnelCard.appendChild(funnelGrid);
|
|
274
|
+
if (narrative.funnel && narrative.funnel.adsAdvice) {
|
|
275
|
+
var ads = el("div", "call warn");
|
|
276
|
+
ads.appendChild(el("strong", "", copy.ads));
|
|
277
|
+
ads.appendChild(document.createTextNode(narrative.funnel.adsAdvice));
|
|
278
|
+
funnelCard.appendChild(ads);
|
|
279
|
+
}
|
|
280
|
+
if (narrative.funnel && narrative.funnel.salesAdvice) {
|
|
281
|
+
var sales = el("div", "call");
|
|
282
|
+
sales.appendChild(el("strong", "", copy.sales));
|
|
283
|
+
sales.appendChild(document.createTextNode(narrative.funnel.salesAdvice));
|
|
284
|
+
funnelCard.appendChild(sales);
|
|
285
|
+
}
|
|
286
|
+
funnel.appendChild(funnelCard);
|
|
287
|
+
main.appendChild(funnel);
|
|
288
|
+
|
|
289
|
+
var dimensions = section(4);
|
|
290
|
+
var pendingHalf = null;
|
|
291
|
+
function flushHalf() {
|
|
292
|
+
if (!pendingHalf) return;
|
|
293
|
+
var grid = el("div", "grid g2");
|
|
294
|
+
grid.style.marginTop = "14px";
|
|
295
|
+
grid.appendChild(pendingHalf);
|
|
296
|
+
dimensions.appendChild(grid);
|
|
297
|
+
pendingHalf = null;
|
|
298
|
+
}
|
|
299
|
+
(narrative.dimensions || []).forEach(function (dimension, index) {
|
|
300
|
+
var card = el("div", "card");
|
|
301
|
+
card.appendChild(el("h3", "", dimension.title));
|
|
302
|
+
card.appendChild(table(dimension.columns, dimension.rows));
|
|
303
|
+
if (dimension.note) card.appendChild(el("div", "section-note", dimension.note));
|
|
304
|
+
var width = dimension.width || (index === 0 ? "full" : "half");
|
|
305
|
+
if (width === "full") {
|
|
306
|
+
flushHalf();
|
|
307
|
+
card.style.marginTop = index ? "14px" : "0";
|
|
308
|
+
dimensions.appendChild(card);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (!pendingHalf) {
|
|
312
|
+
pendingHalf = card;
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
var grid = el("div", "grid g2");
|
|
316
|
+
grid.style.marginTop = "14px";
|
|
317
|
+
grid.appendChild(pendingHalf);
|
|
318
|
+
grid.appendChild(card);
|
|
319
|
+
dimensions.appendChild(grid);
|
|
320
|
+
pendingHalf = null;
|
|
321
|
+
});
|
|
322
|
+
flushHalf();
|
|
323
|
+
main.appendChild(dimensions);
|
|
324
|
+
|
|
325
|
+
var quality = section(5);
|
|
326
|
+
if (narrative.quality && narrative.quality.badge) {
|
|
327
|
+
quality.querySelector(".head").appendChild(el("span", "badge orange", narrative.quality.badge));
|
|
328
|
+
}
|
|
329
|
+
var qualityCard = el("div", "card");
|
|
330
|
+
var qualityData = narrative.quality || {};
|
|
331
|
+
qualityCard.appendChild(table(qualityData.columns || [], qualityData.rows || []));
|
|
332
|
+
if (qualityData.businessConclusion) {
|
|
333
|
+
var biz = el("div", "call warn");
|
|
334
|
+
biz.appendChild(el("strong", "", en ? "Business: " : "业务结论:"));
|
|
335
|
+
biz.appendChild(document.createTextNode(qualityData.businessConclusion));
|
|
336
|
+
qualityCard.appendChild(biz);
|
|
337
|
+
}
|
|
338
|
+
if (qualityData.splitFactors) {
|
|
339
|
+
var split = el("div", "call");
|
|
340
|
+
split.appendChild(el("strong", "", en ? "Separate from ads: " : "需要分开看的因素:"));
|
|
341
|
+
split.appendChild(document.createTextNode(qualityData.splitFactors));
|
|
342
|
+
qualityCard.appendChild(split);
|
|
343
|
+
}
|
|
344
|
+
quality.appendChild(qualityCard);
|
|
345
|
+
main.appendChild(quality);
|
|
346
|
+
|
|
347
|
+
var actions = section(6);
|
|
348
|
+
if (narrative.actionBadge) actions.querySelector(".head").appendChild(el("span", "badge red", narrative.actionBadge));
|
|
349
|
+
var actionCard = el("div", "card");
|
|
350
|
+
(narrative.actions || []).forEach(function (item) {
|
|
351
|
+
var row = el("div", "action");
|
|
352
|
+
var level = String(item.priority || "P2").toUpperCase();
|
|
353
|
+
row.appendChild(el("span", "priority " + (level === "P0" ? "p0" : level === "P1" ? "p1" : "p2"), level));
|
|
354
|
+
var body = el("div");
|
|
355
|
+
body.appendChild(el("strong", "", item.title));
|
|
356
|
+
var detail = el("small");
|
|
357
|
+
detail.appendChild(el("b", "", copy.evidence));
|
|
358
|
+
detail.appendChild(document.createTextNode(show(item.evidence)));
|
|
359
|
+
detail.appendChild(el("br"));
|
|
360
|
+
detail.appendChild(el("b", "", copy.action));
|
|
361
|
+
detail.appendChild(document.createTextNode(show(item.action)));
|
|
362
|
+
detail.appendChild(el("br"));
|
|
363
|
+
detail.appendChild(el("b", "", copy.verify));
|
|
364
|
+
detail.appendChild(document.createTextNode(show(item.verify)));
|
|
365
|
+
if (item.risk) {
|
|
366
|
+
detail.appendChild(el("br"));
|
|
367
|
+
detail.appendChild(el("b", "", copy.risk));
|
|
368
|
+
detail.appendChild(document.createTextNode(item.risk));
|
|
369
|
+
}
|
|
370
|
+
body.appendChild(detail);
|
|
371
|
+
row.appendChild(body);
|
|
372
|
+
row.appendChild(el("span", "owner", [item.owner, item.deadline].filter(Boolean).join(en ? " / " : "|")));
|
|
373
|
+
actionCard.appendChild(row);
|
|
374
|
+
});
|
|
375
|
+
actions.appendChild(actionCard);
|
|
376
|
+
main.appendChild(actions);
|
|
377
|
+
|
|
378
|
+
var tests = section(7);
|
|
379
|
+
var testCard = el("div", "card scroll");
|
|
380
|
+
testCard.appendChild(table(
|
|
381
|
+
en
|
|
382
|
+
? ["Test", "Hypothesis", "Control / variant", "Metric", "Sample", "Window", "Stop rule"]
|
|
383
|
+
: ["测试项", "假设", "对照 / 实验组", "主指标", "样本量", "周期", "停止规则"],
|
|
384
|
+
(narrative.tests || []).map(function (item) {
|
|
385
|
+
return { cells: [item.name, item.hypothesis || "", item.groups, item.metric, item.sample, item.period, item.stop] };
|
|
386
|
+
})
|
|
387
|
+
));
|
|
388
|
+
tests.appendChild(testCard);
|
|
389
|
+
main.appendChild(tests);
|
|
390
|
+
|
|
391
|
+
var foot = document.getElementById("foot");
|
|
392
|
+
foot.appendChild(el("strong", "", en ? "Report: " : "报告定位:"));
|
|
393
|
+
foot.appendChild(document.createTextNode(copy.foot));
|
|
394
|
+
foot.appendChild(el("br"));
|
|
395
|
+
foot.appendChild(document.createTextNode(
|
|
396
|
+
(en ? "Generated: " : "生成日期:") + show(meta.generatedAt) +
|
|
397
|
+
(en ? " | Account " : "|账户 ") + show(meta.accountId)
|
|
398
|
+
));
|
|
399
|
+
})();
|
|
400
|
+
</script>
|
|
401
|
+
</body>
|
|
402
|
+
</html>
|