@jzg223/design-runtime 0.0.1 → 0.1.0-rc.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +52 -0
  3. package/dist/board/api.js +57 -0
  4. package/dist/board/board-chart.vue.js +7 -0
  5. package/dist/board/board-chart2.vue.js +93 -0
  6. package/dist/board/context.js +15 -0
  7. package/dist/board/datav-map.js +66 -0
  8. package/dist/board/design-board.vue.js +172 -0
  9. package/dist/board/design-board2.vue.js +331 -0
  10. package/dist/board/echarts.js +40 -0
  11. package/dist/board/part-node.vue.js +63 -0
  12. package/dist/board/part-node2.vue.js +125 -0
  13. package/dist/board/part-renderer.vue.js +561 -0
  14. package/dist/board/part-renderer2.vue.js +884 -0
  15. package/dist/board/theme-host.js +129 -0
  16. package/dist/board-engine/asset.js +32 -0
  17. package/dist/board-engine/chart-option.js +377 -0
  18. package/dist/board-engine/data-hub.js +67 -0
  19. package/dist/board-engine/datav-meta.js +51 -0
  20. package/dist/board-engine/i18n.js +130 -0
  21. package/dist/board-engine/param-catalog.js +137 -0
  22. package/dist/board-engine/params.js +61 -0
  23. package/dist/board-engine/render-model.js +65 -0
  24. package/dist/board-engine/schema.js +856 -0
  25. package/dist/board-engine/theme-manifest.js +19 -0
  26. package/dist/board-engine/version.js +4 -0
  27. package/dist/host.js +6 -1
  28. package/dist/index.js +172 -24
  29. package/dist/style.css +1067 -0
  30. package/dist/types/board/api.d.ts +45 -0
  31. package/dist/types/board/board-chart.vue.d.ts +15 -0
  32. package/dist/types/board/context.d.ts +29 -0
  33. package/dist/types/board/datav-map.d.ts +24 -0
  34. package/dist/types/board/design-board.vue.d.ts +1409 -0
  35. package/dist/types/board/echarts.d.ts +25 -0
  36. package/dist/types/board/part-node.vue.d.ts +1237 -0
  37. package/dist/types/board/part-renderer.vue.d.ts +1205 -0
  38. package/dist/types/board/theme-host.d.ts +41 -0
  39. package/dist/types/board-engine/asset.d.ts +37 -0
  40. package/dist/types/board-engine/chart-option.d.ts +737 -0
  41. package/dist/types/board-engine/data-hub.d.ts +48 -0
  42. package/dist/types/board-engine/datav-meta.d.ts +37 -0
  43. package/dist/types/board-engine/i18n.d.ts +43 -0
  44. package/dist/types/board-engine/index.d.ts +25 -0
  45. package/dist/types/board-engine/param-catalog.d.ts +82 -0
  46. package/dist/types/board-engine/params.d.ts +37 -0
  47. package/dist/types/board-engine/render-model.d.ts +87 -0
  48. package/dist/types/board-engine/schema.d.ts +513 -0
  49. package/dist/types/board-engine/theme-manifest.d.ts +23 -0
  50. package/dist/types/board-engine/types.d.ts +147 -0
  51. package/dist/types/board-engine/version.d.ts +10 -0
  52. package/dist/types/host.d.ts +21 -0
  53. package/dist/types/index.d.ts +21 -2
  54. package/package.json +21 -4
@@ -0,0 +1,856 @@
1
+ const PART_TYPES = [
2
+ { type: "chart", label: "图表", icon: "bar-chart", group: "图表", w: 520, h: 320 },
3
+ { type: "kpi", label: "指标卡", icon: "number", group: "图表", w: 260, h: 140 },
4
+ { type: "table", label: "明细表格", icon: "table", group: "图表", w: 560, h: 300 },
5
+ { type: "crossTable", label: "交叉表", icon: "table", group: "图表", w: 620, h: 320 },
6
+ { type: "text", label: "文本", icon: "font-colors", group: "通用", w: 320, h: 60 },
7
+ { type: "image", label: "图片", icon: "picture", group: "通用", w: 300, h: 200 },
8
+ { type: "border", label: "静态边框", icon: "border", group: "通用", w: 480, h: 280 },
9
+ { type: "container", label: "自由容器", icon: "layout", group: "通用", w: 460, h: 260 },
10
+ { type: "tabContainer", label: "tab容器", icon: "table", group: "通用", w: 520, h: 300 },
11
+ { type: "iframe", label: "web元素", icon: "global", group: "通用", w: 480, h: 300 },
12
+ { type: "video", label: "视频", icon: "play-circle", group: "媒体", w: 420, h: 260 },
13
+ { type: "carousel", label: "轮播", icon: "picture", group: "媒体", w: 480, h: 280 },
14
+ { type: "marquee", label: "跑马灯", icon: "sound", group: "媒体", w: 460, h: 60 },
15
+ // 装饰:尺寸按"默认编号(5 阶梯折线)"的推荐尺寸给,见 engine/datav.js 的 DATAV_DECORATION_META
16
+ { type: "decoration", label: "装饰", icon: "thunderbolt", group: "媒体", w: 280, h: 60 },
17
+ { type: "progress", label: "进度条", icon: "dashboard", group: "交互", w: 320, h: 120 },
18
+ { type: "button", label: "按钮", icon: "thunderbolt", group: "交互", w: 160, h: 48 },
19
+ // 导航:一栏菜单项。点击**写看板参数**(主模型)或跳另一个看板;
20
+ // 其它部件用 view.visibleWhen 声明"哪个参数值下才显示" ⇒ 切页与切数据共用同一条参数链。
21
+ { type: "nav", label: "导航", icon: "menu", group: "交互", w: 760, h: 44 },
22
+ { type: "filter", label: "筛选方案", icon: "filter", group: "交互", w: 420, h: 60 }
23
+ ];
24
+ const CHART_TYPES = [
25
+ { value: "bar", label: "柱状图" },
26
+ { value: "hbar", label: "条形图" },
27
+ { value: "line", label: "折线图" },
28
+ { value: "area", label: "面积图" },
29
+ { value: "pie", label: "饼图" },
30
+ { value: "donut", label: "环形图" },
31
+ { value: "rose", label: "玫瑰图" },
32
+ { value: "funnel", label: "漏斗图" },
33
+ { value: "pyramid", label: "金字塔图" },
34
+ { value: "radar", label: "雷达图" },
35
+ { value: "scatter", label: "散点图" },
36
+ { value: "gauge", label: "仪表盘" },
37
+ { value: "ring", label: "进度环" }
38
+ ];
39
+ const DEFAULT_THEME = "tech-blue";
40
+ const CONTAINER_TYPES = ["container", "tabContainer", "border"];
41
+ const isContainer = (type) => CONTAINER_TYPES.indexOf(type) >= 0;
42
+ function partVisible(part, params) {
43
+ const w = part && part.view && part.view.visibleWhen;
44
+ if (!w || !w.param) return true;
45
+ const raw = params ? params[w.param] : void 0;
46
+ const cur = raw === void 0 || raw === null ? "" : String(raw);
47
+ if (Array.isArray(w.in) && w.in.length > 0 && w.in.map(String).indexOf(cur) < 0) return false;
48
+ if (Array.isArray(w.notIn) && w.notIn.length > 0 && w.notIn.map(String).indexOf(cur) >= 0) return false;
49
+ return true;
50
+ }
51
+ const partTypeBadge = (part) => {
52
+ if (!part || !part.type) return "";
53
+ if (part.type === "chart") {
54
+ const chartType = part.view && part.view.chart || CHART_TYPES[0] && CHART_TYPES[0].value || "bar";
55
+ return `chart·${chartType}`;
56
+ }
57
+ return String(part.type);
58
+ };
59
+ const AGG_LABELS = { sum: "求和", avg: "平均", count: "计数", max: "最大", min: "最小", countdistinct: "去重计数" };
60
+ function measureLabel(measure, fieldAlias) {
61
+ if (!measure) return fieldAlias || "";
62
+ if (measure.label) return measure.label;
63
+ const base = fieldAlias || measure.field;
64
+ const agg = AGG_LABELS[String(measure.agg || "").toLowerCase()] || measure.agg || "";
65
+ return agg ? `${base}(${agg})` : base;
66
+ }
67
+ const STAGE_BG = "#070c18";
68
+ const CARD_TYPES = ["kpi", "chart", "table", "crossTable", "progress", "filter"];
69
+ const CARD_STYLE = {
70
+ bg: "",
71
+ border: { width: 0, style: "solid", color: "rgba(86,168,255,0.32)", radius: 0 },
72
+ shadow: { enable: false, color: "rgba(2,8,20,0.55)", blur: 22, spread: 0, x: 0, y: 10 }
73
+ };
74
+ const PANEL_STYLE = {
75
+ bg: "",
76
+ border: { width: 0, style: "solid", color: "rgba(86,168,255,0.32)", radius: 0 },
77
+ shadow: { enable: false, color: "rgba(2,8,20,0.55)", blur: 22, spread: 0, x: 0, y: 10 }
78
+ };
79
+ const CONTAINER_DECOR = { corner: true, glass: true };
80
+ const BORDER_PRESETS = [
81
+ { value: "corner", label: "四角括号(轻)" },
82
+ { value: "cut", label: "切角描边(科技)" },
83
+ { value: "glow", label: "描边 + 流光" },
84
+ { value: "datav", label: "DataV 边框(13 种)" },
85
+ { value: "none", label: "无边框" }
86
+ ];
87
+ function borderPresetOf(part) {
88
+ const v = part && part.view ? part.view.borderPreset : void 0;
89
+ if (v === "none" || v === "cut" || v === "glow" || v === "datav" || v === "corner") return v;
90
+ return decorOf(part).corner ? "corner" : "none";
91
+ }
92
+ function decorOf(part) {
93
+ const d = part && part.view && part.view.decor || {};
94
+ return {
95
+ corner: d.corner === void 0 ? CONTAINER_DECOR.corner : !!d.corner,
96
+ glass: d.glass === void 0 ? CONTAINER_DECOR.glass : !!d.glass
97
+ };
98
+ }
99
+ const THEMES = {
100
+ "tech-blue": {
101
+ label: "科技蓝",
102
+ dark: true,
103
+ colors: ["#4d8dff", "#22d3ee", "#a78bfa", "#fbbf24", "#fb7185", "#34d399", "#38bdf8", "#f472b6"],
104
+ bg: STAGE_BG,
105
+ text: "#eaf2ff",
106
+ subText: "rgba(196,216,246,0.74)",
107
+ splitLine: "rgba(120,170,255,0.18)",
108
+ cardBg: "linear-gradient(180deg, rgba(30,66,128,0.55) 0%, rgba(8,18,38,0.78) 100%)",
109
+ cardBorder: "rgba(86,168,255,0.32)",
110
+ cardRadius: 0,
111
+ // 科技感三件套:主强调色、辉光、上沿高光
112
+ accent: "#4d8dff",
113
+ accent2: "#22d3ee",
114
+ // DataV 边框/装饰的配色(两个槽位给**同一个色**=单色,边框四条边才一致;
115
+ // 想还原 DataV 原版双色就把第二个写成 accent2。主题 CSS 里是 --brd-frame-1/2)
116
+ frameColors: ["#4d8dff", "#22d3ee"],
117
+ decoColors: ["#4d8dff", "#22d3ee"],
118
+ glow: "rgba(77,141,255,0.55)",
119
+ glowSoft: "rgba(77,141,255,0.14)",
120
+ topLight: "rgba(150,205,255,0.85)",
121
+ numGlow: "0 0 26px rgba(77,141,255,0.55), 0 0 2px rgba(255,255,255,0.35)",
122
+ // 舞台底纹:两团光晕 + 40px 细网格(画在画布底层,部件浮在上面)
123
+ decor: "radial-gradient(900px 420px at 50% -10%, rgba(56,110,220,0.22), transparent 70%),radial-gradient(700px 420px at 100% 110%, rgba(34,211,238,0.13), transparent 70%),linear-gradient(rgba(120,170,255,0.055) 1px, transparent 1px) 0 0/100% 44px repeat,linear-gradient(90deg, rgba(120,170,255,0.055) 1px, transparent 1px) 0 0/44px 100% repeat",
124
+ tooltipBg: "rgba(9,17,31,0.94)",
125
+ tooltipBorder: "rgba(96,150,232,0.32)"
126
+ },
127
+ "aurora-white": {
128
+ label: "极光白",
129
+ dark: false,
130
+ colors: ["#3b6fe0", "#0ea5a5", "#7c5cf5", "#e0a11b", "#e5566a", "#16a34a", "#0284c7", "#c026d3"],
131
+ bg: "#f4f6fa",
132
+ text: "#16233a",
133
+ subText: "rgba(52,72,102,0.72)",
134
+ splitLine: "rgba(30,60,120,0.12)",
135
+ cardBg: "linear-gradient(180deg, #ffffff 0%, #f4f8ff 100%)",
136
+ cardBorder: "rgba(30,70,140,0.14)",
137
+ cardRadius: 0,
138
+ accent: "#3b6fe0",
139
+ accent2: "#0ea5a5",
140
+ frameColors: ["#3b6fe0", "#0ea5a5"],
141
+ decoColors: ["#3b6fe0", "#0ea5a5"],
142
+ glow: "rgba(59,111,224,0.30)",
143
+ glowSoft: "rgba(59,111,224,0.07)",
144
+ topLight: "rgba(59,111,224,0.45)",
145
+ // 浅色主题不要发光文字(白底上会糊),用"无色"表示不加
146
+ numGlow: "none",
147
+ decor: "radial-gradient(900px 420px at 50% -10%, rgba(59,111,224,0.10), transparent 70%),radial-gradient(700px 420px at 100% 110%, rgba(14,165,165,0.07), transparent 70%),linear-gradient(rgba(30,70,140,0.05) 1px, transparent 1px) 0 0/100% 44px repeat,linear-gradient(90deg, rgba(30,70,140,0.05) 1px, transparent 1px) 0 0/44px 100% repeat",
148
+ tooltipBg: "rgba(255,255,255,0.98)",
149
+ tooltipBorder: "rgba(30,70,140,0.16)"
150
+ },
151
+ emerald: {
152
+ label: "翡翠绿",
153
+ dark: true,
154
+ colors: ["#22c55e", "#14b8a6", "#a3e635", "#fbbf24", "#f87171", "#38bdf8", "#c084fc", "#fb923c"],
155
+ bg: "#04140f",
156
+ text: "#e8fff7",
157
+ subText: "rgba(186,238,222,0.74)",
158
+ splitLine: "rgba(110,231,183,0.18)",
159
+ cardBg: "linear-gradient(180deg, rgba(16,74,58,0.58) 0%, rgba(4,20,16,0.80) 100%)",
160
+ cardBorder: "rgba(80,220,170,0.30)",
161
+ cardRadius: 0,
162
+ accent: "#22c55e",
163
+ accent2: "#14b8a6",
164
+ frameColors: ["#22c55e", "#14b8a6"],
165
+ decoColors: ["#22c55e", "#14b8a6"],
166
+ glow: "rgba(34,197,94,0.50)",
167
+ glowSoft: "rgba(34,197,94,0.13)",
168
+ topLight: "rgba(150,255,220,0.80)",
169
+ numGlow: "0 0 26px rgba(34,197,94,0.50), 0 0 2px rgba(255,255,255,0.30)",
170
+ decor: "radial-gradient(900px 420px at 50% -10%, rgba(16,120,90,0.26), transparent 70%),radial-gradient(700px 420px at 100% 110%, rgba(20,184,166,0.13), transparent 70%),linear-gradient(rgba(110,231,183,0.06) 1px, transparent 1px) 0 0/100% 44px repeat,linear-gradient(90deg, rgba(110,231,183,0.06) 1px, transparent 1px) 0 0/44px 100% repeat",
171
+ tooltipBg: "rgba(6,22,18,0.94)",
172
+ tooltipBorder: "rgba(80,220,170,0.30)"
173
+ }
174
+ };
175
+ const THEME_OVERRIDES = {};
176
+ function setThemeTokens(code, tokens) {
177
+ if (!code || !tokens) return;
178
+ const known = THEMES[DEFAULT_THEME];
179
+ const next = Object.assign({}, THEME_OVERRIDES[code]);
180
+ Object.keys(tokens).forEach((k) => {
181
+ if (tokens[k] === void 0 || tokens[k] === null) return;
182
+ if (!(k in known)) return;
183
+ next[k] = tokens[k];
184
+ });
185
+ THEME_OVERRIDES[code] = next;
186
+ }
187
+ function resetThemeOverrides() {
188
+ Object.keys(THEME_OVERRIDES).forEach((k) => delete THEME_OVERRIDES[k]);
189
+ }
190
+ function themeOf(code) {
191
+ const base = THEMES[code] || THEMES[DEFAULT_THEME];
192
+ const ov = THEME_OVERRIDES[code];
193
+ return ov ? Object.assign({}, base, ov) : base;
194
+ }
195
+ function defaultConfig(width = 1920, height = 1080) {
196
+ return {
197
+ version: 1,
198
+ page: {
199
+ width,
200
+ height,
201
+ // 布局模式:free=自由绝对定位(大屏,默认,与既有看板兼容);grid=24 栅格流式(门户/自适应)
202
+ layoutMode: "free",
203
+ // 移动端策略:auto(窄屏自动堆叠单列)/ stack(总是堆叠)/ scale(整页等比缩放)
204
+ mobileMode: "auto",
205
+ gridCols: 24,
206
+ gridRowHeight: 60,
207
+ gridGap: 12,
208
+ // 行高是否随内容自动增长:
209
+ // false(默认)= 固定行高,部件框就是"跨行 × 行高",内容超出在部件内部滚动,
210
+ // 整页高度可预测、同一带的邻居不会被某个长表格撑高(经典仪表盘);
211
+ // true = minmax(行高, auto),行随内容长高、整页纵向流式滚动(门户/长内容)。
212
+ gridAutoHeight: false,
213
+ scaleMode: "auto",
214
+ minScale: 0.4,
215
+ fontAdaptive: true,
216
+ minFont: 12,
217
+ theme: DEFAULT_THEME,
218
+ chartTheme: DEFAULT_THEME,
219
+ bgColor: themeOf(DEFAULT_THEME).bg,
220
+ bgImage: "",
221
+ opacity: 100,
222
+ grid: { show: true, size: 10, snap: true },
223
+ guides: true,
224
+ scroll: false,
225
+ watermark: ""
226
+ },
227
+ parts: []
228
+ };
229
+ }
230
+ let seq = 0;
231
+ function newPartId() {
232
+ seq += 1;
233
+ return "p" + Date.now().toString(36) + seq.toString(36);
234
+ }
235
+ function typeShape(type) {
236
+ const meta = PART_TYPES.find((t) => t.type === type);
237
+ const s = { name: meta ? meta.label : "", w: meta ? meta.w : 320, h: meta ? meta.h : 200, data: null, view: {} };
238
+ if (type === "chart") {
239
+ s.name = "图表";
240
+ s.data = { setId: "", dims: [], measures: [], filters: [], params: {}, orderField: "", orderDesc: false, limit: 1e3 };
241
+ s.view = { chart: "bar", showLegend: true, showLabel: false, smooth: false, horizontal: false };
242
+ } else if (type === "kpi") {
243
+ s.name = "指标卡";
244
+ s.data = { setId: "", dims: [], measures: [], filters: [], params: {}, limit: 1e3 };
245
+ s.view = { title: "指标名称", unit: "", precision: 0, thousand: true };
246
+ } else if (type === "table") {
247
+ s.name = "明细表格";
248
+ s.data = { setId: "", dims: [], measures: [], filters: [], params: {}, limit: 200 };
249
+ s.view = { stripe: true, bordered: false, headerBg: "" };
250
+ } else if (type === "text") {
251
+ s.name = "文本";
252
+ s.view = { content: "双击编辑文本", overflow: "wrap" };
253
+ } else if (type === "image") {
254
+ s.name = "图片";
255
+ s.view = { src: "", fit: "contain" };
256
+ } else if (type === "border") {
257
+ s.name = "静态边框";
258
+ s.view = { title: "标题", showTitle: true, titleStyle: "chip", padding: CONTAINER_PAD, childLayout: "grid", childRows: "fixed", decor: { ...CONTAINER_DECOR } };
259
+ } else if (type === "iframe") {
260
+ s.name = "web元素";
261
+ s.view = { src: "" };
262
+ } else if (type === "crossTable") {
263
+ s.name = "交叉表";
264
+ s.data = { setId: "", dims: [], measures: [], filters: [], params: {}, limit: 2e3 };
265
+ s.view = { rowDim: "", colDim: "", showTotal: true, stripe: true };
266
+ } else if (type === "container") {
267
+ s.name = "自由容器";
268
+ s.view = { title: "", showTitle: false, titleStyle: "chip", padding: CONTAINER_PAD, childLayout: "grid", childRows: "fixed", decor: { ...CONTAINER_DECOR } };
269
+ } else if (type === "tabContainer") {
270
+ s.name = "tab容器";
271
+ s.view = { tabs: ["页签一", "页签二", "页签三"], active: 0, carousel: false, interval: 5, padding: CONTAINER_PAD, childLayout: "grid", childRows: "fixed", decor: { ...CONTAINER_DECOR } };
272
+ } else if (type === "video") {
273
+ s.name = "视频";
274
+ s.view = { src: "", autoplay: false, loop: true, muted: true, controls: true };
275
+ } else if (type === "carousel") {
276
+ s.name = "轮播";
277
+ s.view = {
278
+ images: [
279
+ { src: "", title: "第一张" },
280
+ { src: "", title: "第二张" }
281
+ ],
282
+ interval: 3,
283
+ autoplay: true,
284
+ showTitle: true
285
+ };
286
+ } else if (type === "marquee") {
287
+ s.name = "跑马灯";
288
+ s.view = { content: "这是一条滚动播报的文字,可在右侧改内容与速度", speed: 12, color: "" };
289
+ } else if (type === "decoration") {
290
+ s.name = "装饰";
291
+ s.view = { datavDecoration: 5 };
292
+ } else if (type === "progress") {
293
+ s.name = "进度条";
294
+ s.data = { setId: "", dims: [], measures: [], filters: [], params: {}, limit: 1 };
295
+ s.view = { title: "完成率", unit: "%", percent: 60, showValue: true, useData: false };
296
+ } else if (type === "button") {
297
+ s.name = "按钮";
298
+ s.view = { text: "按钮", href: "", target: "_blank", type: "primary" };
299
+ } else if (type === "nav") {
300
+ s.name = "导航";
301
+ s.view = {
302
+ items: [
303
+ { label: "菜单一", value: "m1", code: "" },
304
+ { label: "菜单二", value: "m2", code: "" }
305
+ ],
306
+ mode: "param",
307
+ layout: "flow",
308
+ paramCode: "nav",
309
+ active: "m1",
310
+ gap: 8,
311
+ itemW: 120,
312
+ itemH: 34,
313
+ // 外观预设:只作为 class 挂点(nav-<style>),具体长相由主题 CSS 决定 ——
314
+ // 框架不写死视觉,主题换肤才能生效(B 档的教训)
315
+ style: "plate"
316
+ };
317
+ } else if (type === "filter") {
318
+ s.name = "筛选方案";
319
+ s.view = {
320
+ label: "筛选",
321
+ paramCode: "",
322
+ paramType: "select",
323
+ options: "选项一,选项二,选项三"
324
+ };
325
+ }
326
+ return s;
327
+ }
328
+ function basePartShape(type) {
329
+ const s = typeShape(type);
330
+ return {
331
+ type,
332
+ x: 40,
333
+ y: 40,
334
+ w: s.w,
335
+ h: s.h,
336
+ // 栅格模式用(layoutMode='grid'):起始列 / 跨列 / 跨行;free 模式下忽略
337
+ col: 0,
338
+ colSpan: 6,
339
+ rowSpan: 4,
340
+ // 容器嵌套:'' = 顶层。tabIndex 只对 tab容器的子级有意义
341
+ parentId: "",
342
+ tabIndex: 0,
343
+ z: 1,
344
+ hidden: false,
345
+ locked: false,
346
+ style: (() => {
347
+ const panel = CONTAINER_TYPES.indexOf(type) >= 0;
348
+ const card = CARD_TYPES.indexOf(type) >= 0;
349
+ const src = panel ? PANEL_STYLE : card ? CARD_STYLE : null;
350
+ return {
351
+ bg: src ? src.bg : "",
352
+ opacity: 100,
353
+ border: src ? Object.assign({}, src.border) : { width: 0, style: "solid", color: "#1b3a5c", radius: 0 },
354
+ shadow: src ? Object.assign({}, src.shadow) : { enable: false, color: "rgba(0,0,0,0.3)", blur: 12, spread: 0, x: 0, y: 4 },
355
+ rotate: 0
356
+ };
357
+ })(),
358
+ text: {
359
+ fontFamily: "微软雅黑",
360
+ fontSize: 14,
361
+ bold: false,
362
+ italic: false,
363
+ color: "",
364
+ align: "left"
365
+ },
366
+ data: s.data,
367
+ view: s.view
368
+ };
369
+ }
370
+ function fillPartDefaults(part) {
371
+ if (!part) return part;
372
+ const known = PART_TYPES.some((t) => t.type === part.type);
373
+ const base = basePartShape(part.type);
374
+ if (!known) {
375
+ base.data = part.data === void 0 ? null : part.data;
376
+ base.view = part.view && typeof part.view === "object" ? part.view : {};
377
+ }
378
+ return deepMerge(base, part);
379
+ }
380
+ function jsonEqual(a, b) {
381
+ if (a === b) return true;
382
+ if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
383
+ try {
384
+ return JSON.stringify(a) === JSON.stringify(b);
385
+ } catch (e) {
386
+ return false;
387
+ }
388
+ }
389
+ function stripPartDefaults(part) {
390
+ if (!part || !part.type) return part;
391
+ const def = basePartShape(part.type);
392
+ const out = { ...part };
393
+ ["style", "text", "view", "data"].forEach((key) => {
394
+ const cur = out[key];
395
+ const d = def[key];
396
+ if (cur === void 0 || cur === null || d === void 0 || d === null) return;
397
+ if (typeof cur !== "object" || typeof d !== "object") return;
398
+ const kept = stripEqual(cur, d);
399
+ if (kept === null) delete out[key];
400
+ else out[key] = kept;
401
+ });
402
+ if (out.style && !isContainer(part.type)) {
403
+ delete out.style.bg;
404
+ if (!Object.keys(out.style).length) delete out.style;
405
+ }
406
+ return out;
407
+ }
408
+ function stripEqual(cur, def) {
409
+ const out = {};
410
+ let any = false;
411
+ Object.keys(cur).forEach((k) => {
412
+ const cv = cur[k];
413
+ const dv = def[k];
414
+ if (dv !== void 0 && jsonEqual(cv, dv)) return;
415
+ if (dv !== void 0 && cv && typeof cv === "object" && !Array.isArray(cv) && typeof dv === "object" && !Array.isArray(dv)) {
416
+ const sub = stripEqual(cv, dv);
417
+ if (sub !== null) {
418
+ out[k] = sub;
419
+ any = true;
420
+ }
421
+ return;
422
+ }
423
+ out[k] = cv;
424
+ any = true;
425
+ });
426
+ return any ? out : null;
427
+ }
428
+ function stripConfigDefaults(cfg) {
429
+ if (!cfg || !Array.isArray(cfg.parts)) return cfg;
430
+ return { ...cfg, parts: cfg.parts.map(stripPartDefaults) };
431
+ }
432
+ function createPart(type, overrides = {}) {
433
+ const base = Object.assign({ id: newPartId(), type, name: typeShape(type).name }, basePartShape(type));
434
+ return deepMerge(base, overrides);
435
+ }
436
+ function deepMerge(target, source) {
437
+ if (!source) return target;
438
+ const out = Array.isArray(target) ? [...target] : { ...target };
439
+ Object.keys(source).forEach((k) => {
440
+ const sv = source[k];
441
+ if (sv && typeof sv === "object" && !Array.isArray(sv) && typeof out[k] === "object" && out[k] !== null) {
442
+ out[k] = deepMerge(out[k], sv);
443
+ } else {
444
+ out[k] = sv;
445
+ }
446
+ });
447
+ return out;
448
+ }
449
+ function parseConfig(raw, fallbackSize) {
450
+ let cfg = raw;
451
+ if (typeof raw === "string") {
452
+ try {
453
+ cfg = JSON.parse(raw);
454
+ } catch (e) {
455
+ cfg = null;
456
+ }
457
+ }
458
+ if (!cfg || typeof cfg !== "object") {
459
+ return defaultConfig(fallbackSize && fallbackSize.width, fallbackSize && fallbackSize.height);
460
+ }
461
+ const base = defaultConfig(cfg.page && cfg.page.width, cfg.page && cfg.page.height);
462
+ const merged = deepMerge(base, cfg);
463
+ if (!Array.isArray(merged.parts)) merged.parts = [];
464
+ else merged.parts = normalizeParts(merged.parts).parts;
465
+ return merged;
466
+ }
467
+ function sortedParts(parts) {
468
+ return [...parts || []].sort((a, b) => (a.z || 0) - (b.z || 0));
469
+ }
470
+ function childParts(parts, parentId, tabIndex) {
471
+ const list = (parts || []).filter((p) => (p.parentId || "") === (parentId || ""));
472
+ if (tabIndex === void 0 || tabIndex === null) return list;
473
+ return list.filter((p) => (p.tabIndex || 0) === tabIndex);
474
+ }
475
+ function descendantIds(parts, id) {
476
+ const out = [id];
477
+ const seen = {};
478
+ seen[id] = 1;
479
+ const walk = (pid) => {
480
+ (parts || []).forEach((p) => {
481
+ if ((p.parentId || "") === pid) {
482
+ if (seen[p.id]) return;
483
+ seen[p.id] = 1;
484
+ out.push(p.id);
485
+ walk(p.id);
486
+ }
487
+ });
488
+ };
489
+ walk(id);
490
+ return out;
491
+ }
492
+ function canHostChildren(part) {
493
+ return !!part && isContainer(part.type);
494
+ }
495
+ function childLayoutOf(container) {
496
+ return container && container.view && container.view.childLayout || "grid";
497
+ }
498
+ function layoutAuthority(part, parentPart, page) {
499
+ if (parentPart) return childLayoutOf(parentPart);
500
+ return (page && page.layoutMode) === "grid" ? "grid" : "free";
501
+ }
502
+ function containerGrid(page, innerWidth) {
503
+ const g = {
504
+ gridCols: page && page.gridCols || GRID_DEFAULTS.cols,
505
+ gridRowHeight: page && page.gridRowHeight || GRID_DEFAULTS.rowHeight,
506
+ gridGap: page && page.gridGap !== void 0 ? page.gridGap : GRID_DEFAULTS.gap,
507
+ width: innerWidth || page && page.width || 1920
508
+ };
509
+ return g;
510
+ }
511
+ const CONTAINER_PAD = 10;
512
+ const CONTAINER_PAD_MIN = 0;
513
+ const CONTAINER_PAD_MAX = 40;
514
+ function paddingOf(part) {
515
+ const v = part && part.view ? part.view.padding : void 0;
516
+ const n = Number(v);
517
+ if (!Number.isFinite(n)) return CONTAINER_PAD;
518
+ return Math.max(CONTAINER_PAD_MIN, Math.min(CONTAINER_PAD_MAX, Math.round(n)));
519
+ }
520
+ function titleStyleOf(part) {
521
+ const s = part && part.view ? part.view.titleStyle : void 0;
522
+ return s === "bar" ? "bar" : "chip";
523
+ }
524
+ function childRowsOf(part) {
525
+ const v = part && part.view ? part.view.childRows : void 0;
526
+ return v === "fill" ? "fill" : "fixed";
527
+ }
528
+ const TITLE_BAR_HEIGHT = 34;
529
+ const TITLE_CHIP_HEIGHT = 20;
530
+ const TITLE_OFFSET_DEFAULT = 0;
531
+ const TITLE_OFFSET_MIN = -20;
532
+ const TITLE_OFFSET_MAX = 40;
533
+ const TITLE_INSET_DEFAULT = 8;
534
+ const TITLE_INSET_MIN = -20;
535
+ const TITLE_INSET_MAX = 120;
536
+ function titleInsetOf(part) {
537
+ const v = part && part.view ? part.view.titleInset : void 0;
538
+ const n = Number(v);
539
+ if (!Number.isFinite(n)) return TITLE_INSET_DEFAULT;
540
+ return Math.max(TITLE_INSET_MIN, Math.min(TITLE_INSET_MAX, Math.round(n)));
541
+ }
542
+ function titleOffsetOf(part) {
543
+ const v = part && part.view ? part.view.titleOffset : void 0;
544
+ const n = Number(v);
545
+ if (!Number.isFinite(n)) return TITLE_OFFSET_DEFAULT;
546
+ return Math.max(TITLE_OFFSET_MIN, Math.min(TITLE_OFFSET_MAX, Math.round(n)));
547
+ }
548
+ function titleReserveOf(part) {
549
+ const hasTitle = part && part.view && part.view.showTitle && part.view.title !== void 0;
550
+ if (!hasTitle) return 0;
551
+ if (titleStyleOf(part) === "bar") return TITLE_BAR_HEIGHT;
552
+ return Math.max(0, TITLE_CHIP_HEIGHT - titleOffsetOf(part) - paddingOf(part));
553
+ }
554
+ function reorderDepthFirst(parts) {
555
+ const list = parts || [];
556
+ const out = [];
557
+ const seen = {};
558
+ const pushTop = (p) => {
559
+ if (seen[p.id]) return;
560
+ seen[p.id] = 1;
561
+ out.push(p);
562
+ list.forEach((c) => {
563
+ if ((c.parentId || "") === p.id) pushTop(c);
564
+ });
565
+ };
566
+ list.forEach((p) => {
567
+ if (!(p.parentId || "")) pushTop(p);
568
+ });
569
+ list.forEach((p) => {
570
+ if (!seen[p.id]) pushTop(p);
571
+ });
572
+ return out;
573
+ }
574
+ function normalizeParts(parts) {
575
+ const list = (parts || []).filter((p) => p && p.id && p.type);
576
+ const byId = {};
577
+ list.forEach((p) => {
578
+ byId[p.id] = p;
579
+ });
580
+ const fixed = [];
581
+ list.forEach((p) => {
582
+ const filled = fillPartDefaults(p);
583
+ Object.keys(filled).forEach((k) => {
584
+ if (k !== "id") p[k] = filled[k];
585
+ });
586
+ if (typeof p.parentId !== "string") p.parentId = "";
587
+ if (typeof p.tabIndex !== "number") p.tabIndex = 0;
588
+ const pid = p.parentId;
589
+ if (!pid) return;
590
+ const parent = byId[pid];
591
+ if (!parent) {
592
+ p.parentId = "";
593
+ fixed.push(`${p.name || p.id}: 父容器不存在,已提升为顶层`);
594
+ return;
595
+ }
596
+ if (!isContainer(parent.type)) {
597
+ p.parentId = "";
598
+ fixed.push(`${p.name || p.id}: 父级不是容器,已提升为顶层`);
599
+ return;
600
+ }
601
+ const tabs = parent.view && parent.view.tabs || [];
602
+ if (parent.type === "tabContainer" && tabs.length && (p.tabIndex || 0) >= tabs.length) {
603
+ p.tabIndex = 0;
604
+ fixed.push(`${p.name || p.id}: 页签序号越界,已归到第 1 个页签`);
605
+ }
606
+ });
607
+ list.forEach((p) => {
608
+ if (!p.parentId) return;
609
+ if (descendantIds(list, p.id).indexOf(p.parentId) >= 0) {
610
+ p.parentId = "";
611
+ fixed.push(`${p.name || p.id}: 存在循环嵌套,已提升为顶层`);
612
+ }
613
+ });
614
+ return { parts: reorderDepthFirst(list), fixed };
615
+ }
616
+ const GRID_DEFAULTS = { cols: 24, rowHeight: 60, gap: 12 };
617
+ function freeToGrid(part, page) {
618
+ const cols = page && page.gridCols || GRID_DEFAULTS.cols;
619
+ const rowH = page && page.gridRowHeight || GRID_DEFAULTS.rowHeight;
620
+ const gap = page && page.gridGap !== void 0 ? page.gridGap : GRID_DEFAULTS.gap;
621
+ const W = page && page.width || 1920;
622
+ const colW = (W - gap * (cols + 1)) / cols;
623
+ const unit = colW + gap;
624
+ const col = Math.max(0, Math.min(cols - 1, Math.round((part.x - gap) / unit)));
625
+ const span = Math.max(1, Math.min(cols - col, Math.round((part.w + gap) / unit)));
626
+ const rowSpan = Math.max(1, Math.round((part.h + gap) / (rowH + gap)));
627
+ return { col, colSpan: span, rowSpan };
628
+ }
629
+ function gridToFreeParts(parts, page) {
630
+ const cols = page && page.gridCols || GRID_DEFAULTS.cols;
631
+ const rowH = page && page.gridRowHeight || GRID_DEFAULTS.rowHeight;
632
+ const gap = page && page.gridGap !== void 0 ? page.gridGap : GRID_DEFAULTS.gap;
633
+ const W = page && page.width || 1920;
634
+ const colW = (W - gap * (cols + 1)) / cols;
635
+ const unit = colW + gap;
636
+ const rowUnit = rowH + gap;
637
+ let top = gap;
638
+ let cursor = 0;
639
+ let bandRows = 1;
640
+ parts.forEach((part) => {
641
+ const col = Math.max(0, Math.min(cols - 1, part.col || 0));
642
+ const span = Math.max(1, Math.min(cols - col, part.colSpan || 6));
643
+ const rows = Math.max(1, part.rowSpan || 4);
644
+ if (col < cursor) {
645
+ top += bandRows * rowUnit;
646
+ cursor = 0;
647
+ bandRows = 1;
648
+ }
649
+ part.x = Math.round(gap + col * unit);
650
+ part.y = Math.round(top);
651
+ part.w = Math.round(span * unit - gap);
652
+ part.h = Math.round(rows * rowUnit - gap);
653
+ cursor = col + span;
654
+ if (rows > bandRows) bandRows = rows;
655
+ });
656
+ return parts;
657
+ }
658
+ function gridAutoRows(page) {
659
+ const rowH = page && page.gridRowHeight || GRID_DEFAULTS.rowHeight;
660
+ return page && page.gridAutoHeight ? `minmax(${rowH}px, auto)` : `${rowH}px`;
661
+ }
662
+ function sortSiblingsByVisual(parts, parentId) {
663
+ const pid = parentId || "";
664
+ const sibs = (parts || []).filter((p) => (p.parentId || "") === pid);
665
+ if (sibs.length < 2) return parts;
666
+ const sorted = sibs.slice().sort((a, b) => {
667
+ const dy = (a.y || 0) - (b.y || 0);
668
+ if (Math.abs(dy) > 4) return dy;
669
+ return (a.x || 0) - (b.x || 0);
670
+ });
671
+ const slots = [];
672
+ parts.forEach((p, i) => {
673
+ if ((p.parentId || "") === pid) slots.push(i);
674
+ });
675
+ slots.forEach((slot, k) => {
676
+ parts[slot] = sorted[k];
677
+ });
678
+ return parts;
679
+ }
680
+ function gridSpanToSize(part, page) {
681
+ const cols = page && page.gridCols || GRID_DEFAULTS.cols;
682
+ const rowH = page && page.gridRowHeight || GRID_DEFAULTS.rowHeight;
683
+ const gap = page && page.gridGap !== void 0 ? page.gridGap : GRID_DEFAULTS.gap;
684
+ const W = page && page.width || 1920;
685
+ const colW = (W - gap * (cols + 1)) / cols;
686
+ const unit = colW + gap;
687
+ return {
688
+ w: Math.round(Math.max(1, Math.min(cols, part.colSpan || 6)) * unit - gap),
689
+ h: Math.round(Math.max(1, part.rowSpan || 4) * (rowH + gap) - gap)
690
+ };
691
+ }
692
+ function gridPartStyle(part, page) {
693
+ const st = partStyle(part);
694
+ const cols = page && page.gridCols || GRID_DEFAULTS.cols;
695
+ const col = Math.max(0, Math.min(cols - 1, part.col || 0));
696
+ const span = Math.max(1, Math.min(cols - col, part.colSpan || 6));
697
+ st.position = "relative";
698
+ st.left = "auto";
699
+ st.top = "auto";
700
+ st.width = "auto";
701
+ st.height = "auto";
702
+ st.gridColumn = `${col + 1} / span ${span}`;
703
+ st.gridRow = `span ${Math.max(1, part.rowSpan || 4)}`;
704
+ st.minWidth = "0";
705
+ return st;
706
+ }
707
+ function partStyle(part) {
708
+ const s = part.style || {};
709
+ const st = {
710
+ position: "absolute",
711
+ left: part.x + "px",
712
+ top: part.y + "px",
713
+ width: part.w + "px",
714
+ height: part.h + "px",
715
+ zIndex: part.z || 1,
716
+ opacity: (s.opacity === void 0 ? 100 : s.opacity) / 100,
717
+ boxSizing: "border-box"
718
+ };
719
+ if (s.rotate) {
720
+ st.transform = `rotate(${s.rotate}deg)`;
721
+ }
722
+ return st;
723
+ }
724
+ function partVisualStyle(part) {
725
+ const s = part.style || {};
726
+ const b = s.border || {};
727
+ const sh = s.shadow || {};
728
+ const st = {};
729
+ if (s.bg && isContainer(part.type)) st.background = s.bg;
730
+ if (b.radius) st.borderRadius = (b.radius || 0) + "px";
731
+ if (b.width) {
732
+ st.border = `${b.width}px ${b.style || "solid"} ${b.color || "transparent"}`;
733
+ }
734
+ if (sh.enable) {
735
+ st.boxShadow = `${sh.x || 0}px ${sh.y || 0}px ${sh.blur || 0}px ${sh.spread || 0}px ${sh.color || "rgba(0,0,0,.3)"}`;
736
+ }
737
+ return st;
738
+ }
739
+ function textStyle(part, theme) {
740
+ const t = part.text || {};
741
+ return {
742
+ fontFamily: t.fontFamily || "微软雅黑",
743
+ fontSize: (t.fontSize || 14) + "px",
744
+ fontWeight: t.bold ? "bold" : "normal",
745
+ fontStyle: t.italic ? "italic" : "normal",
746
+ color: t.color || (theme ? theme.text : "#fff"),
747
+ textAlign: t.align || "left"
748
+ };
749
+ }
750
+ const MOBILE_MAX_WIDTH = 768;
751
+ function mobileModeOf(page) {
752
+ const m = page && page.mobileMode || "auto";
753
+ return ["auto", "stack", "scale"].indexOf(m) >= 0 ? m : "auto";
754
+ }
755
+ function isMobileStack(page, viewportWidth) {
756
+ const mode = mobileModeOf(page);
757
+ if (mode === "scale") return false;
758
+ const w = Number(viewportWidth) || 0;
759
+ if (mode === "stack") return true;
760
+ return w > 0 && w < MOBILE_MAX_WIDTH;
761
+ }
762
+ const STACK_MIN_HEIGHT = { chart: 320, table: 320, crossTable: 320, kpi: 120, text: 56, progress: 120, filter: 80 };
763
+ function stackMinHeight(type) {
764
+ return STACK_MIN_HEIGHT[type] || 60;
765
+ }
766
+ function stackPartStyle(part, childCount) {
767
+ const st = partStyle(part);
768
+ st.position = "relative";
769
+ st.left = "auto";
770
+ st.top = "auto";
771
+ st.width = "100%";
772
+ st.maxWidth = "100%";
773
+ st.height = isContainer(part.type) ? childCount > 0 ? "auto" : Math.max(60, part.h || 120) + "px" : Math.max(stackMinHeight(part.type), part.h || 120) + "px";
774
+ return st;
775
+ }
776
+ function visualOrder(list) {
777
+ return [...list || []].sort((a, b) => {
778
+ const dy = (a.y || 0) - (b.y || 0);
779
+ if (Math.abs(dy) > 20) return dy;
780
+ return (a.x || 0) - (b.x || 0);
781
+ });
782
+ }
783
+ export {
784
+ AGG_LABELS,
785
+ BORDER_PRESETS,
786
+ CARD_STYLE,
787
+ CARD_TYPES,
788
+ CHART_TYPES,
789
+ CONTAINER_DECOR,
790
+ CONTAINER_PAD,
791
+ CONTAINER_PAD_MAX,
792
+ CONTAINER_PAD_MIN,
793
+ CONTAINER_TYPES,
794
+ DEFAULT_THEME,
795
+ GRID_DEFAULTS,
796
+ MOBILE_MAX_WIDTH,
797
+ PANEL_STYLE,
798
+ PART_TYPES,
799
+ STAGE_BG,
800
+ THEMES,
801
+ TITLE_BAR_HEIGHT,
802
+ TITLE_CHIP_HEIGHT,
803
+ TITLE_INSET_DEFAULT,
804
+ TITLE_INSET_MAX,
805
+ TITLE_INSET_MIN,
806
+ TITLE_OFFSET_DEFAULT,
807
+ TITLE_OFFSET_MAX,
808
+ TITLE_OFFSET_MIN,
809
+ basePartShape,
810
+ borderPresetOf,
811
+ canHostChildren,
812
+ childLayoutOf,
813
+ childParts,
814
+ childRowsOf,
815
+ containerGrid,
816
+ createPart,
817
+ decorOf,
818
+ deepMerge,
819
+ defaultConfig,
820
+ descendantIds,
821
+ fillPartDefaults,
822
+ freeToGrid,
823
+ gridAutoRows,
824
+ gridPartStyle,
825
+ gridSpanToSize,
826
+ gridToFreeParts,
827
+ isContainer,
828
+ isMobileStack,
829
+ layoutAuthority,
830
+ measureLabel,
831
+ mobileModeOf,
832
+ newPartId,
833
+ normalizeParts,
834
+ paddingOf,
835
+ parseConfig,
836
+ partStyle,
837
+ partTypeBadge,
838
+ partVisible,
839
+ partVisualStyle,
840
+ reorderDepthFirst,
841
+ resetThemeOverrides,
842
+ setThemeTokens,
843
+ sortSiblingsByVisual,
844
+ sortedParts,
845
+ stackMinHeight,
846
+ stackPartStyle,
847
+ stripConfigDefaults,
848
+ stripPartDefaults,
849
+ textStyle,
850
+ themeOf,
851
+ titleInsetOf,
852
+ titleOffsetOf,
853
+ titleReserveOf,
854
+ titleStyleOf,
855
+ visualOrder
856
+ };