byteplan-cli 1.0.2 → 1.2.0

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.
Files changed (65) hide show
  1. package/package.json +7 -3
  2. package/skills/byteplan-analysis/SKILL.md +1078 -0
  3. package/skills/byteplan-api/API_REFERENCE.md +249 -0
  4. package/skills/byteplan-api/SKILL.md +96 -0
  5. package/skills/byteplan-api/package.json +16 -0
  6. package/skills/byteplan-api/scripts/api.js +973 -0
  7. package/skills/byteplan-excel/SKILL.md +212 -0
  8. package/skills/byteplan-excel/examples/margin-analysis.json +40 -0
  9. package/skills/byteplan-excel/package.json +12 -0
  10. package/skills/byteplan-excel/pnpm-lock.yaml +68 -0
  11. package/skills/byteplan-excel/scripts/generate_excel.js +156 -0
  12. package/skills/byteplan-html/SKILL.md +490 -0
  13. package/skills/byteplan-html/examples/example-output.html +184 -0
  14. package/skills/byteplan-html/examples/generate-ppt-style-html.js +611 -0
  15. package/skills/byteplan-html/examples/margin-contribution-analysis.json +152 -0
  16. package/skills/byteplan-html/package.json +18 -0
  17. package/skills/byteplan-html/scripts/generate_html.js +517 -0
  18. package/skills/byteplan-ppt/SKILL.md +394 -0
  19. package/skills/byteplan-ppt/examples/margin-contribution-analysis.json +152 -0
  20. package/skills/byteplan-ppt/package.json +16 -0
  21. package/skills/byteplan-ppt/pnpm-lock.yaml +138 -0
  22. package/skills/byteplan-ppt/scripts/check_ppt_overlap.js +318 -0
  23. package/skills/byteplan-ppt/scripts/generate_ppt.js +680 -0
  24. package/skills/byteplan-video/SKILL.md +606 -0
  25. package/skills/byteplan-video/examples/sample-video-data.json +82 -0
  26. package/skills/byteplan-video/remotion-project/package.json +22 -0
  27. package/skills/byteplan-video/remotion-project/pnpm-lock.yaml +1646 -0
  28. package/skills/byteplan-video/remotion-project/remotion.config.ts +6 -0
  29. package/skills/byteplan-video/remotion-project/scene_durations.json +32 -0
  30. package/skills/byteplan-video/remotion-project/scripts/generate_audio.js +279 -0
  31. package/skills/byteplan-video/remotion-project/src/DynamicReport.tsx +172 -0
  32. package/skills/byteplan-video/remotion-project/src/Root.tsx +51 -0
  33. package/skills/byteplan-video/remotion-project/src/SalesReport.tsx +107 -0
  34. package/skills/byteplan-video/remotion-project/src/index.tsx +4 -0
  35. package/skills/byteplan-video/remotion-project/src/scenes/ChartSlide.tsx +201 -0
  36. package/skills/byteplan-video/remotion-project/src/scenes/CoverSlide.tsx +61 -0
  37. package/skills/byteplan-video/remotion-project/src/scenes/EndSlide.tsx +60 -0
  38. package/skills/byteplan-video/remotion-project/src/scenes/InsightSlide.tsx +101 -0
  39. package/skills/byteplan-video/remotion-project/src/scenes/KpiSlide.tsx +84 -0
  40. package/skills/byteplan-video/remotion-project/src/scenes/RecommendationSlide.tsx +100 -0
  41. package/skills/byteplan-video/remotion-project/tsconfig.json +13 -0
  42. package/skills/byteplan-video/remotion-project/video_data.json +76 -0
  43. package/skills/byteplan-video/scripts/generate_video.js +270 -0
  44. package/skills/byteplan-video/templates/package.json +31 -0
  45. package/skills/byteplan-video/templates/pnpm-lock.yaml +2200 -0
  46. package/skills/byteplan-video/templates/remotion.config.ts +9 -0
  47. package/skills/byteplan-video/templates/scripts/generate-audio.ts +55 -0
  48. package/skills/byteplan-video/templates/src/components/BarChartScene.tsx +153 -0
  49. package/skills/byteplan-video/templates/src/components/InsightScene.tsx +135 -0
  50. package/skills/byteplan-video/templates/src/components/LineChartScene.tsx +214 -0
  51. package/skills/byteplan-video/templates/src/components/SceneFactory.tsx +34 -0
  52. package/skills/byteplan-video/templates/src/components/SummaryScene.tsx +155 -0
  53. package/skills/byteplan-video/templates/src/components/TitleScene.tsx +130 -0
  54. package/skills/byteplan-video/templates/src/compositions/AnalysisVideo.tsx +39 -0
  55. package/skills/byteplan-video/templates/src/index.tsx +28 -0
  56. package/skills/byteplan-video/templates/src/register-root.tsx +4 -0
  57. package/skills/byteplan-video/templates/src/storyboard/types.ts +46 -0
  58. package/skills/byteplan-video/templates/tsconfig.json +17 -0
  59. package/skills/byteplan-video/templates/tsconfig.scripts.json +13 -0
  60. package/skills/byteplan-word/SKILL.md +233 -0
  61. package/skills/byteplan-word/package.json +12 -0
  62. package/skills/byteplan-word/pnpm-lock.yaml +120 -0
  63. package/skills/byteplan-word/scripts/generate_word.js +548 -0
  64. package/src/cli.js +4 -0
  65. package/src/commands/skills.js +279 -0
@@ -0,0 +1,201 @@
1
+ import {
2
+ AbsoluteFill,
3
+ useCurrentFrame,
4
+ interpolate,
5
+ } from "remotion";
6
+
7
+ interface ChartSlideProps {
8
+ title: string;
9
+ chartType: string;
10
+ data: Array<{ name: string; value: number }>;
11
+ }
12
+
13
+ const COLORS = ["#667eea", "#4ade80", "#f472b6", "#fbbf24", "#06B6D4"];
14
+
15
+ export const ChartSlide: React.FC<ChartSlideProps> = ({
16
+ title,
17
+ chartType,
18
+ data,
19
+ }) => {
20
+ const frame = useCurrentFrame();
21
+
22
+ const titleOpacity = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: "clamp" });
23
+
24
+ if (chartType === "pie") {
25
+ // 饼图
26
+ const total = data.reduce((sum, d) => sum + d.value, 0);
27
+ const radius = 180;
28
+ const svgSize = 400;
29
+ const cx = svgSize / 2;
30
+ const cy = svgSize / 2;
31
+
32
+ let cumulativeAngle = 0;
33
+ const slices = data.map((d, i) => {
34
+ const angle = (d.value / total) * 360;
35
+ const startAngle = cumulativeAngle;
36
+ cumulativeAngle += angle;
37
+ return { ...d, startAngle, angle, color: COLORS[i % COLORS.length] };
38
+ });
39
+
40
+ const pieOpacity = interpolate(frame, [20, 50], [0, 1], { extrapolateRight: "clamp" });
41
+
42
+ return (
43
+ <AbsoluteFill
44
+ style={{
45
+ background: "linear-gradient(135deg, #16213e 0%, #1a1a2e 100%)",
46
+ justifyContent: "center",
47
+ alignItems: "center",
48
+ flexDirection: "column",
49
+ }}
50
+ >
51
+ <div
52
+ style={{
53
+ fontSize: 44,
54
+ fontWeight: "bold",
55
+ color: "#fff",
56
+ marginBottom: 50,
57
+ opacity: titleOpacity,
58
+ }}
59
+ >
60
+ 📦 {title}
61
+ </div>
62
+
63
+ <div style={{ display: "flex", flexDirection: "row", gap: 80, alignItems: "center", opacity: pieOpacity }}>
64
+ {/* 饼图 SVG */}
65
+ <svg width={svgSize} height={svgSize}>
66
+ {slices.map((slice, i) => {
67
+ const startRad = (slice.startAngle - 90) * (Math.PI / 180);
68
+ const endRad = (slice.startAngle + slice.angle - 90) * (Math.PI / 180);
69
+ const x1 = cx + radius * Math.cos(startRad);
70
+ const y1 = cy + radius * Math.sin(startRad);
71
+ const x2 = cx + radius * Math.cos(endRad);
72
+ const y2 = cy + radius * Math.sin(endRad);
73
+ const largeArc = slice.angle > 180 ? 1 : 0;
74
+
75
+ return (
76
+ <path
77
+ key={i}
78
+ d={`M ${cx} ${cy} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} Z`}
79
+ fill={slice.color}
80
+ stroke="#fff"
81
+ strokeWidth={2}
82
+ />
83
+ );
84
+ })}
85
+ </svg>
86
+
87
+ {/* 图例 */}
88
+ <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
89
+ {data.map((d, i) => (
90
+ <div key={i} style={{ display: "flex", alignItems: "center", gap: 16 }}>
91
+ <div
92
+ style={{
93
+ width: 24,
94
+ height: 24,
95
+ borderRadius: 4,
96
+ background: COLORS[i % COLORS.length],
97
+ }}
98
+ />
99
+ <div style={{ fontSize: 20, color: "#fff", width: 100 }}>{d.name}</div>
100
+ <div style={{ fontSize: 18, color: "#4ade80" }}>
101
+ ¥{d.value.toLocaleString()}
102
+ </div>
103
+ </div>
104
+ ))}
105
+ </div>
106
+ </div>
107
+ </AbsoluteFill>
108
+ );
109
+ }
110
+
111
+ // 柱状图
112
+ const maxValue = Math.max(...data.map((d) => d.value));
113
+ const barWidth = 140;
114
+ const barGap = 40;
115
+ const chartHeight = 400;
116
+
117
+ return (
118
+ <AbsoluteFill
119
+ style={{
120
+ background: "linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)",
121
+ justifyContent: "center",
122
+ alignItems: "center",
123
+ flexDirection: "column",
124
+ }}
125
+ >
126
+ <div
127
+ style={{
128
+ fontSize: 44,
129
+ fontWeight: "bold",
130
+ color: "#fff",
131
+ marginBottom: 60,
132
+ opacity: titleOpacity,
133
+ }}
134
+ >
135
+ {title.includes("渠道") ? "🛒" : "🗺️"} {title}
136
+ </div>
137
+
138
+ {/* 图表容器 - 居中 */}
139
+ <div
140
+ style={{
141
+ display: "flex",
142
+ flexDirection: "row",
143
+ alignItems: "flex-end",
144
+ gap: barGap,
145
+ justifyContent: "center",
146
+ }}
147
+ >
148
+ {data.map((d, i) => {
149
+ const barHeight = (d.value / maxValue) * chartHeight;
150
+ const delay = 20 + i * 10;
151
+ const animatedHeight = interpolate(
152
+ frame,
153
+ [delay, delay + 30],
154
+ [0, barHeight],
155
+ { extrapolateRight: "clamp" }
156
+ );
157
+ const opacity = interpolate(frame, [delay, delay + 20], [0, 1], { extrapolateRight: "clamp" });
158
+
159
+ return (
160
+ <div
161
+ key={i}
162
+ style={{
163
+ display: "flex",
164
+ flexDirection: "column",
165
+ alignItems: "center",
166
+ opacity,
167
+ width: barWidth,
168
+ }}
169
+ >
170
+ {/* 数值 */}
171
+ <div
172
+ style={{
173
+ fontSize: 24,
174
+ fontWeight: "bold",
175
+ color: "#4ade80",
176
+ marginBottom: 10,
177
+ }}
178
+ >
179
+ {maxValue <= 100 ? `${d.value}%` : `¥${(d.value / 1000).toFixed(0)}k`}
180
+ </div>
181
+ {/* 柱状条 */}
182
+ <div
183
+ style={{
184
+ width: barWidth,
185
+ height: animatedHeight,
186
+ background: `linear-gradient(180deg, ${COLORS[i]} 0%, ${COLORS[i]}80 100%)`,
187
+ borderRadius: 8,
188
+ boxShadow: `0 4px 20px ${COLORS[i]}40`,
189
+ }}
190
+ />
191
+ {/* 标签 */}
192
+ <div style={{ fontSize: 22, color: "#fff", marginTop: 16 }}>
193
+ {d.name}
194
+ </div>
195
+ </div>
196
+ );
197
+ })}
198
+ </div>
199
+ </AbsoluteFill>
200
+ );
201
+ };
@@ -0,0 +1,61 @@
1
+ import {
2
+ AbsoluteFill,
3
+ useCurrentFrame,
4
+ interpolate,
5
+ } from "remotion";
6
+
7
+ interface CoverSlideProps {
8
+ title: string;
9
+ subtitle: string;
10
+ }
11
+
12
+ export const CoverSlide: React.FC<CoverSlideProps> = ({ title, subtitle }) => {
13
+ const frame = useCurrentFrame();
14
+
15
+ const titleOpacity = interpolate(frame, [0, 30], [0, 1], { extrapolateRight: "clamp" });
16
+ const subtitleOpacity = interpolate(frame, [30, 60], [0, 1], { extrapolateRight: "clamp" });
17
+
18
+ return (
19
+ <AbsoluteFill
20
+ style={{
21
+ background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
22
+ justifyContent: "center",
23
+ alignItems: "center",
24
+ flexDirection: "column",
25
+ }}
26
+ >
27
+ <div
28
+ style={{
29
+ fontSize: 64,
30
+ fontWeight: "bold",
31
+ color: "#fff",
32
+ textAlign: "center",
33
+ opacity: titleOpacity,
34
+ marginBottom: 40,
35
+ }}
36
+ >
37
+ 📊 {title}
38
+ </div>
39
+ <div
40
+ style={{
41
+ fontSize: 32,
42
+ color: "rgba(255,255,255,0.9)",
43
+ textAlign: "center",
44
+ opacity: subtitleOpacity,
45
+ }}
46
+ >
47
+ {subtitle}
48
+ </div>
49
+ <div
50
+ style={{
51
+ fontSize: 18,
52
+ color: "rgba(255,255,255,0.6)",
53
+ marginTop: 60,
54
+ opacity: subtitleOpacity,
55
+ }}
56
+ >
57
+ 2026年4月1日 · 跨境电商租户
58
+ </div>
59
+ </AbsoluteFill>
60
+ );
61
+ };
@@ -0,0 +1,60 @@
1
+ import {
2
+ AbsoluteFill,
3
+ useCurrentFrame,
4
+ interpolate,
5
+ } from "remotion";
6
+
7
+ interface EndSlideProps {
8
+ title: string;
9
+ subtitle: string;
10
+ }
11
+
12
+ export const EndSlide: React.FC<EndSlideProps> = ({ title, subtitle }) => {
13
+ const frame = useCurrentFrame();
14
+
15
+ const titleOpacity = interpolate(frame, [0, 30], [0, 1], { extrapolateRight: "clamp" });
16
+ const subtitleOpacity = interpolate(frame, [30, 60], [0, 1], { extrapolateRight: "clamp" });
17
+
18
+ return (
19
+ <AbsoluteFill
20
+ style={{
21
+ background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
22
+ justifyContent: "center",
23
+ alignItems: "center",
24
+ flexDirection: "column",
25
+ }}
26
+ >
27
+ <div
28
+ style={{
29
+ fontSize: 52,
30
+ fontWeight: "bold",
31
+ color: "#fff",
32
+ textAlign: "center",
33
+ opacity: titleOpacity,
34
+ }}
35
+ >
36
+ 📊 {title}
37
+ </div>
38
+ <div
39
+ style={{
40
+ fontSize: 28,
41
+ color: "rgba(255,255,255,0.8)",
42
+ marginTop: 30,
43
+ opacity: subtitleOpacity,
44
+ }}
45
+ >
46
+ {subtitle}
47
+ </div>
48
+ <div
49
+ style={{
50
+ fontSize: 18,
51
+ color: "rgba(255,255,255,0.6)",
52
+ marginTop: 50,
53
+ opacity: subtitleOpacity,
54
+ }}
55
+ >
56
+ BytePlan 数据平台
57
+ </div>
58
+ </AbsoluteFill>
59
+ );
60
+ };
@@ -0,0 +1,101 @@
1
+ import {
2
+ AbsoluteFill,
3
+ useCurrentFrame,
4
+ interpolate,
5
+ spring,
6
+ } from "remotion";
7
+
8
+ interface InsightSlideProps {
9
+ title: string;
10
+ insights: Array<{ title: string; content: string; warning?: boolean }>;
11
+ }
12
+
13
+ export const InsightSlide: React.FC<InsightSlideProps> = ({ title, insights }) => {
14
+ const frame = useCurrentFrame();
15
+
16
+ const titleOpacity = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: "clamp" });
17
+
18
+ // 根据 insights 数量决定布局
19
+ const gridCols = insights.length <= 3 ? 1 : 2;
20
+ const gridRows = Math.ceil(insights.length / gridCols);
21
+
22
+ return (
23
+ <AbsoluteFill
24
+ style={{
25
+ background: "linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)",
26
+ justifyContent: "center",
27
+ alignItems: "center",
28
+ flexDirection: "column",
29
+ padding: 60,
30
+ }}
31
+ >
32
+ <div
33
+ style={{
34
+ fontSize: 48,
35
+ fontWeight: "bold",
36
+ color: "#fff",
37
+ marginBottom: 50,
38
+ opacity: titleOpacity,
39
+ }}
40
+ >
41
+ 💡 {title}
42
+ </div>
43
+
44
+ <div
45
+ style={{
46
+ display: "grid",
47
+ gridTemplateColumns: gridCols === 1 ? "1fr" : "1fr 1fr",
48
+ gap: 25,
49
+ width: gridCols === 1 ? 800 : 1200,
50
+ }}
51
+ >
52
+ {insights.map((insight, index) => {
53
+ const delay = 20 + index * 12;
54
+ const scale = spring({
55
+ frame: frame - delay,
56
+ fps: 30,
57
+ config: { damping: 100, stiffness: 200 },
58
+ });
59
+ const opacity = interpolate(frame, [delay, delay + 20], [0, 1], { extrapolateRight: "clamp" });
60
+
61
+ return (
62
+ <div
63
+ key={index}
64
+ style={{
65
+ background: insight.warning
66
+ ? "rgba(251,191,36,0.15)"
67
+ : "rgba(255,255,255,0.1)",
68
+ borderRadius: 18,
69
+ padding: 30,
70
+ borderLeft: `5px solid ${insight.warning ? "#fbbf24" : "#667eea"}`,
71
+ opacity,
72
+ transform: `scale(${scale})`,
73
+ }}
74
+ >
75
+ {insight.title && (
76
+ <div
77
+ style={{
78
+ fontSize: 26,
79
+ fontWeight: "bold",
80
+ color: insight.warning ? "#fbbf24" : "#fff",
81
+ marginBottom: 12,
82
+ }}
83
+ >
84
+ {insight.title}
85
+ </div>
86
+ )}
87
+ <div style={{
88
+ fontSize: 20,
89
+ color: "#CBD5E1",
90
+ lineHeight: 1.6,
91
+ fontWeight: insight.title ? "normal" : "bold"
92
+ }}>
93
+ {insight.content}
94
+ </div>
95
+ </div>
96
+ );
97
+ })}
98
+ </div>
99
+ </AbsoluteFill>
100
+ );
101
+ };
@@ -0,0 +1,84 @@
1
+ import {
2
+ AbsoluteFill,
3
+ useCurrentFrame,
4
+ interpolate,
5
+ spring,
6
+ } from "remotion";
7
+
8
+ interface KpiSlideProps {
9
+ title: string;
10
+ kpis: Array<{ icon: string; label: string; value: string }>;
11
+ }
12
+
13
+ export const KpiSlide: React.FC<KpiSlideProps> = ({ title, kpis }) => {
14
+ const frame = useCurrentFrame();
15
+
16
+ const titleOpacity = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: "clamp" });
17
+
18
+ return (
19
+ <AbsoluteFill
20
+ style={{
21
+ background: "linear-gradient(135deg, #0f3460 0%, #16213e 100%)",
22
+ justifyContent: "center",
23
+ alignItems: "center",
24
+ flexDirection: "column",
25
+ padding: 60,
26
+ }}
27
+ >
28
+ <div
29
+ style={{
30
+ fontSize: 44,
31
+ fontWeight: "bold",
32
+ color: "#fff",
33
+ marginBottom: 60,
34
+ opacity: titleOpacity,
35
+ }}
36
+ >
37
+ 📈 {title}
38
+ </div>
39
+
40
+ <div
41
+ style={{
42
+ display: "flex",
43
+ flexDirection: "row",
44
+ gap: 40,
45
+ justifyContent: "center",
46
+ }}
47
+ >
48
+ {kpis.map((kpi, index) => {
49
+ const delay = 20 + index * 15;
50
+ const scale = spring({
51
+ frame: frame - delay,
52
+ fps: 30,
53
+ config: { damping: 100, stiffness: 200 },
54
+ });
55
+ const opacity = interpolate(frame, [delay, delay + 20], [0, 1], { extrapolateRight: "clamp" });
56
+
57
+ return (
58
+ <div
59
+ key={index}
60
+ style={{
61
+ background: "rgba(255,255,255,0.1)",
62
+ borderRadius: 20,
63
+ padding: 40,
64
+ textAlign: "center",
65
+ border: "2px solid rgba(255,255,255,0.2)",
66
+ width: 280,
67
+ opacity,
68
+ transform: `scale(${scale})`,
69
+ }}
70
+ >
71
+ <div style={{ fontSize: 48, marginBottom: 20 }}>{kpi.icon}</div>
72
+ <div style={{ fontSize: 18, color: "#94A3B8", marginBottom: 12 }}>
73
+ {kpi.label}
74
+ </div>
75
+ <div style={{ fontSize: 36, fontWeight: "bold", color: "#4ade80" }}>
76
+ {kpi.value}
77
+ </div>
78
+ </div>
79
+ );
80
+ })}
81
+ </div>
82
+ </AbsoluteFill>
83
+ );
84
+ };
@@ -0,0 +1,100 @@
1
+ import {
2
+ AbsoluteFill,
3
+ useCurrentFrame,
4
+ interpolate,
5
+ spring,
6
+ } from "remotion";
7
+
8
+ interface RecommendationSlideProps {
9
+ title: string;
10
+ recommendations: string[];
11
+ source?: string;
12
+ }
13
+
14
+ export const RecommendationSlide: React.FC<RecommendationSlideProps> = ({
15
+ title,
16
+ recommendations,
17
+ source,
18
+ }) => {
19
+ const frame = useCurrentFrame();
20
+
21
+ const titleOpacity = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: "clamp" });
22
+
23
+ // 根据建议数量决定布局
24
+ const gridCols = recommendations.length <= 2 ? 1 : Math.min(recommendations.length, 2);
25
+ const gridRows = Math.ceil(recommendations.length / gridCols);
26
+
27
+ return (
28
+ <AbsoluteFill
29
+ style={{
30
+ background: "linear-gradient(135deg, #0f3460 0%, #16213e 100%)",
31
+ justifyContent: "center",
32
+ alignItems: "center",
33
+ flexDirection: "column",
34
+ padding: 60,
35
+ }}
36
+ >
37
+ <div
38
+ style={{
39
+ fontSize: 48,
40
+ fontWeight: "bold",
41
+ color: "#fff",
42
+ marginBottom: 50,
43
+ opacity: titleOpacity,
44
+ }}
45
+ >
46
+ 🎯 {title}
47
+ </div>
48
+
49
+ <div
50
+ style={{
51
+ display: "grid",
52
+ gridTemplateColumns: gridCols === 1 ? "1fr" : "1fr 1fr",
53
+ gap: 25,
54
+ width: gridCols === 1 ? 800 : 1200,
55
+ }}
56
+ >
57
+ {recommendations.map((rec, index) => {
58
+ const delay = 20 + index * 12;
59
+ const scale = spring({
60
+ frame: frame - delay,
61
+ fps: 30,
62
+ config: { damping: 100, stiffness: 200 },
63
+ });
64
+ const opacity = interpolate(frame, [delay, delay + 20], [0, 1], { extrapolateRight: "clamp" });
65
+
66
+ return (
67
+ <div
68
+ key={index}
69
+ style={{
70
+ background: "rgba(74,222,128,0.15)",
71
+ borderRadius: 18,
72
+ padding: 30,
73
+ borderLeft: "5px solid #4ade80",
74
+ opacity,
75
+ transform: `scale(${scale})`,
76
+ }}
77
+ >
78
+ <div style={{ fontSize: 22, color: "#fff", lineHeight: 1.6 }}>
79
+ {rec}
80
+ </div>
81
+ </div>
82
+ );
83
+ })}
84
+ </div>
85
+
86
+ {source && (
87
+ <div
88
+ style={{
89
+ fontSize: 16,
90
+ color: "#94A3B8",
91
+ marginTop: 40,
92
+ opacity: interpolate(frame, [60, 80], [0, 1], { extrapolateRight: "clamp" }),
93
+ }}
94
+ >
95
+ 数据来源:{source}
96
+ </div>
97
+ )}
98
+ </AbsoluteFill>
99
+ );
100
+ };
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "react-jsx",
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "strict": true,
10
+ "resolveJsonModule": true
11
+ },
12
+ "include": ["src/**/*"]
13
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "title": "边际贡献分析报告",
3
+ "subtitle": "找出贡献最大的三个要素",
4
+ "period": "2026年4月2日",
5
+ "scenes": [
6
+ {
7
+ "id": "scene_1",
8
+ "type": "title",
9
+ "title": "边际贡献分析报告",
10
+ "subtitle": "找出贡献最大的三个要素",
11
+ "narration": "本视频将为您分析边际贡献情况,找出贡献最大的三个要素。",
12
+ "duration": 5
13
+ },
14
+ {
15
+ "id": "scene_2",
16
+ "type": "insight",
17
+ "title": "核心发现",
18
+ "narration": "经过分析,天猫渠道以1627万元边际贡献位居第一,CD120P产品贡献560万元排名第二,美国市场贡献498万元排名第三。",
19
+ "highlights": [
20
+ "🥇 天猫渠道:边际贡献 1,627万元,贡献率 68%",
21
+ "🥈 CD120P产品:边际贡献 563万元,贡献率 74%",
22
+ "🥉 美国市场:边际贡献 498万元,贡献率 65%"
23
+ ],
24
+ "duration": 10
25
+ },
26
+ {
27
+ "id": "scene_3",
28
+ "type": "bar_chart",
29
+ "title": "边际贡献对比(TOP5)",
30
+ "narration": "从边际贡献对比图可以看出,天猫渠道遥遥领先,是公司最核心的销售渠道。",
31
+ "duration": 10
32
+ },
33
+ {
34
+ "id": "scene_4",
35
+ "type": "insight",
36
+ "title": "关键洞察",
37
+ "narration": "天猫渠道贡献占比68%,是最大收入来源。CD120P产品贡献率高达74%,盈利能力最强。美国市场收入规模最大,但贡献率65%,仍有优化空间。天猫渠道变动成本偏高,需排查运营成本构成。",
38
+ "highlights": [
39
+ "天猫渠道:贡献占比68%,是最大收入来源",
40
+ "CD120P产品:贡献率74%,盈利效率最高",
41
+ "美国市场:收入最大,贡献率65%仍有优化空间",
42
+ "⚠️ 天猫变动成本偏高:需排查运营成本构成"
43
+ ],
44
+ "duration": 10
45
+ },
46
+ {
47
+ "id": "scene_5",
48
+ "type": "summary",
49
+ "title": "行动建议",
50
+ "narration": "建议加大CD120P产品产能,优化天猫运营成本,并建立月度边际贡献监控机制。",
51
+ "points": [
52
+ "加大CD120P产能,贡献率74%的产品应获得更多资源",
53
+ "优化天猫运营成本,降低变动成本提升贡献率",
54
+ "建立月度边际贡献监控机制,及时发现问题"
55
+ ],
56
+ "source": "BytePlan 数据平台 · 跨境电商租户",
57
+ "duration": 8
58
+ },
59
+ {
60
+ "id": "scene_6",
61
+ "type": "end",
62
+ "title": "边际贡献分析报告",
63
+ "narration": "谢谢观看。",
64
+ "duration": 3
65
+ }
66
+ ],
67
+ "chartData": {
68
+ "channelContribution": [
69
+ { "name": "天猫", "value": 1627 },
70
+ { "name": "CD120P", "value": 563 },
71
+ { "name": "美国", "value": 498 },
72
+ { "name": "Amazon", "value": 279 },
73
+ { "name": "京东", "value": 156 }
74
+ ]
75
+ }
76
+ }