@liujitcn/kratos-admin-bi 0.0.0-stage → 0.1.6

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 (166) hide show
  1. package/README.md +43 -2
  2. package/dist/package/declarations/index.d.ts +1 -0
  3. package/dist/package/declarations/src/api/bi/admin/v1/bi_card.d.ts +18 -0
  4. package/dist/package/declarations/src/api/bi/admin/v1/bi_card.js +46 -0
  5. package/dist/package/declarations/src/api/bi/admin/v1/bi_dashboard.d.ts +17 -0
  6. package/dist/package/declarations/src/api/bi/admin/v1/bi_dashboard.js +30 -0
  7. package/dist/package/declarations/src/api/bi/admin/v1/bi_query.d.ts +11 -0
  8. package/dist/package/declarations/src/api/bi/admin/v1/bi_query.js +41 -0
  9. package/dist/package/declarations/src/api/bi/admin/v1/bi_query_record.d.ts +10 -0
  10. package/dist/package/declarations/src/api/bi/admin/v1/bi_query_record.js +14 -0
  11. package/dist/package/declarations/src/api/bi/admin/v1/bi_source.d.ts +26 -0
  12. package/dist/package/declarations/src/api/bi/admin/v1/bi_source.js +46 -0
  13. package/dist/package/declarations/src/bi-core.d.ts +17 -0
  14. package/dist/package/declarations/src/bi-core.js +22 -0
  15. package/dist/package/declarations/src/bi-dashboard-schema.d.ts +31 -0
  16. package/dist/package/declarations/src/bi-dashboard-schema.js +39 -0
  17. package/dist/package/declarations/src/bi-messages.d.ts +247 -0
  18. package/dist/package/declarations/src/bi-messages.js +247 -0
  19. package/dist/package/declarations/src/bi-response.d.ts +10 -0
  20. package/dist/package/declarations/src/bi-response.js +33 -0
  21. package/dist/package/declarations/src/bi-vue.d.ts +10 -0
  22. package/dist/package/declarations/src/bi-vue.js +10 -0
  23. package/dist/package/declarations/src/card-schema.d.ts +30 -0
  24. package/dist/package/declarations/src/card-schema.js +87 -0
  25. package/dist/package/declarations/src/components/BiCardChartEditor.vue.d.ts +17 -0
  26. package/dist/package/declarations/src/components/BiCardChartEditor.vue.js +527 -0
  27. package/dist/package/declarations/src/components/BiCardFormDialog.vue.d.ts +20 -0
  28. package/dist/package/declarations/src/components/BiCardFormDialog.vue.js +495 -0
  29. package/dist/package/declarations/src/components/BiCardLibrary.vue.d.ts +3 -0
  30. package/dist/package/declarations/src/components/BiCardLibrary.vue.js +807 -0
  31. package/dist/package/declarations/src/components/BiCardVisualization.vue.d.ts +10 -0
  32. package/dist/package/declarations/src/components/BiCardVisualization.vue.js +97 -0
  33. package/dist/package/declarations/src/components/BiChart.vue.d.ts +20 -0
  34. package/dist/package/declarations/src/components/BiChart.vue.js +112 -0
  35. package/dist/package/declarations/src/components/BiDashboard.vue.d.ts +9 -0
  36. package/dist/package/declarations/src/components/BiDashboard.vue.js +81 -0
  37. package/dist/package/declarations/src/components/BiDashboardMenuDialog.vue.d.ts +12 -0
  38. package/dist/package/declarations/src/components/BiDashboardMenuDialog.vue.js +252 -0
  39. package/dist/package/declarations/src/components/BiDataCard.vue.d.ts +36 -0
  40. package/dist/package/declarations/src/components/BiDataCard.vue.js +372 -0
  41. package/dist/package/declarations/src/components/BiDesigner.vue.d.ts +17 -0
  42. package/dist/package/declarations/src/components/BiDesigner.vue.js +666 -0
  43. package/dist/package/declarations/src/components/BiSourceModelDialog.vue.d.ts +17 -0
  44. package/dist/package/declarations/src/components/BiSourceModelDialog.vue.js +2118 -0
  45. package/dist/package/declarations/src/components/BiWorkbenchPreview.vue.d.ts +7 -0
  46. package/dist/package/declarations/src/components/BiWorkbenchPreview.vue.js +176 -0
  47. package/dist/package/declarations/src/components/source-model-layout.d.ts +3 -0
  48. package/dist/package/declarations/src/components/source-model-layout.js +66 -0
  49. package/dist/package/declarations/src/dataset/index.d.ts +7 -0
  50. package/dist/package/declarations/src/dataset/index.js +25 -0
  51. package/dist/package/declarations/src/dataset/labels.d.ts +2 -0
  52. package/dist/package/declarations/src/dataset/labels.js +21 -0
  53. package/dist/package/declarations/src/dataset/transform.d.ts +20 -0
  54. package/dist/package/declarations/src/dataset/transform.js +173 -0
  55. package/dist/package/declarations/src/engine/chart-types.d.ts +14 -0
  56. package/dist/package/declarations/src/engine/chart-types.js +37 -0
  57. package/dist/package/declarations/src/engine/echarts.d.ts +16 -0
  58. package/dist/package/declarations/src/engine/echarts.js +491 -0
  59. package/dist/package/declarations/src/engine/types.d.ts +36 -0
  60. package/dist/package/declarations/src/engine/types.js +20 -0
  61. package/dist/package/declarations/src/env.d.ts +17 -0
  62. package/dist/package/declarations/src/env.js +67 -0
  63. package/dist/package/declarations/src/index.d.ts +1 -0
  64. package/dist/package/declarations/src/index.js +1 -0
  65. package/dist/package/declarations/src/locales/en-US.json +254 -0
  66. package/dist/package/declarations/src/locales/generated.d.ts +1021 -0
  67. package/dist/package/declarations/src/locales/generated.js +13 -0
  68. package/dist/package/declarations/src/locales/ja-JP.json +254 -0
  69. package/dist/package/declarations/src/locales/zh-CN.json +254 -0
  70. package/dist/package/declarations/src/locales/zh-TW.json +254 -0
  71. package/dist/package/declarations/src/module.d.ts +2 -0
  72. package/dist/package/declarations/src/module.js +9 -0
  73. package/dist/package/declarations/src/rpc/bi/admin/v1/bi_card.d.ts +145 -0
  74. package/dist/package/declarations/src/rpc/bi/admin/v1/bi_card.js +6 -0
  75. package/dist/package/declarations/src/rpc/bi/admin/v1/bi_dashboard.d.ts +107 -0
  76. package/dist/package/declarations/src/rpc/bi/admin/v1/bi_dashboard.js +6 -0
  77. package/dist/package/declarations/src/rpc/bi/admin/v1/bi_query.d.ts +157 -0
  78. package/dist/package/declarations/src/rpc/bi/admin/v1/bi_query.js +6 -0
  79. package/dist/package/declarations/src/rpc/bi/admin/v1/bi_query_record.d.ts +83 -0
  80. package/dist/package/declarations/src/rpc/bi/admin/v1/bi_query_record.js +15 -0
  81. package/dist/package/declarations/src/rpc/bi/admin/v1/bi_source.d.ts +301 -0
  82. package/dist/package/declarations/src/rpc/bi/admin/v1/bi_source.js +6 -0
  83. package/dist/package/declarations/src/rpc/common/v1/types.d.ts +60 -0
  84. package/dist/package/declarations/src/rpc/common/v1/types.js +6 -0
  85. package/dist/package/declarations/src/schema/layout.d.ts +7 -0
  86. package/dist/package/declarations/src/schema/layout.js +52 -0
  87. package/dist/package/declarations/src/schema/types.d.ts +83 -0
  88. package/dist/package/declarations/src/schema/types.js +1 -0
  89. package/dist/package/declarations/src/theme/index.d.ts +29 -0
  90. package/dist/package/declarations/src/theme/index.js +83 -0
  91. package/dist/package/declarations/src/theme/palettes.d.ts +7 -0
  92. package/dist/package/declarations/src/theme/palettes.js +28 -0
  93. package/dist/package/declarations/src/theme/presets.d.ts +73 -0
  94. package/dist/package/declarations/src/theme/presets.js +235 -0
  95. package/dist/package/declarations/src/views/card/index.vue.d.ts +3 -0
  96. package/dist/package/declarations/src/views/card/index.vue.js +18 -0
  97. package/dist/package/declarations/src/views/dashboard/index.vue.d.ts +3 -0
  98. package/dist/package/declarations/src/views/dashboard/index.vue.js +107 -0
  99. package/dist/package/declarations/src/views/designer/index.vue.d.ts +3 -0
  100. package/dist/package/declarations/src/views/designer/index.vue.js +332 -0
  101. package/dist/package/declarations/src/views/manage/index.vue.d.ts +3 -0
  102. package/dist/package/declarations/src/views/manage/index.vue.js +325 -0
  103. package/dist/package/declarations/src/views/query/index.vue.d.ts +3 -0
  104. package/dist/package/declarations/src/views/query/index.vue.js +876 -0
  105. package/dist/package/declarations/src/views/source/index.vue.d.ts +3 -0
  106. package/dist/package/declarations/src/views/source/index.vue.js +343 -0
  107. package/dist/package/declarations/src/views/workbench/index.vue.d.ts +3 -0
  108. package/dist/package/declarations/src/views/workbench/index.vue.js +362 -0
  109. package/dist/package/src/api/bi/admin/v1/bi_card.ts +67 -0
  110. package/dist/package/src/api/bi/admin/v1/bi_dashboard.ts +51 -0
  111. package/dist/package/src/api/bi/admin/v1/bi_query.ts +55 -0
  112. package/dist/package/src/api/bi/admin/v1/bi_query_record.ts +25 -0
  113. package/dist/package/src/api/bi/admin/v1/bi_source.ts +80 -0
  114. package/dist/package/src/bi-core.ts +43 -0
  115. package/dist/package/src/bi-dashboard-schema.ts +69 -0
  116. package/dist/package/src/bi-messages.ts +249 -0
  117. package/dist/package/src/bi-response.ts +39 -0
  118. package/dist/package/src/bi-vue.ts +13 -0
  119. package/dist/package/src/card-schema.ts +111 -0
  120. package/dist/package/src/components/BiCardChartEditor.vue +139 -0
  121. package/dist/package/src/components/BiCardFormDialog.vue +289 -0
  122. package/dist/package/src/components/BiCardLibrary.vue +310 -0
  123. package/dist/package/src/components/BiCardVisualization.vue +95 -0
  124. package/dist/package/src/components/BiChart.vue +134 -0
  125. package/dist/package/src/components/BiDashboard.vue +53 -0
  126. package/dist/package/src/components/BiDashboardMenuDialog.vue +177 -0
  127. package/dist/package/src/components/BiDataCard.vue +231 -0
  128. package/dist/package/src/components/BiDesigner.vue +287 -0
  129. package/dist/package/src/components/BiSourceModelDialog.vue +846 -0
  130. package/dist/package/src/components/BiWorkbenchPreview.vue +131 -0
  131. package/dist/package/src/components/source-model-layout.ts +62 -0
  132. package/dist/package/src/dataset/index.ts +28 -0
  133. package/dist/package/src/dataset/labels.ts +19 -0
  134. package/dist/package/src/dataset/transform.ts +189 -0
  135. package/dist/package/src/engine/chart-types.ts +44 -0
  136. package/dist/package/src/engine/echarts.ts +568 -0
  137. package/dist/package/src/engine/types.ts +60 -0
  138. package/dist/package/src/env.ts +82 -0
  139. package/dist/package/src/index.ts +1 -0
  140. package/dist/package/src/locales/en-US.json +254 -0
  141. package/dist/package/src/locales/generated.ts +16 -0
  142. package/dist/package/src/locales/ja-JP.json +254 -0
  143. package/dist/package/src/locales/zh-CN.json +254 -0
  144. package/dist/package/src/locales/zh-TW.json +254 -0
  145. package/dist/package/src/module.ts +12 -0
  146. package/dist/package/src/rpc/bi/admin/v1/bi_card.ts +169 -0
  147. package/dist/package/src/rpc/bi/admin/v1/bi_dashboard.ts +126 -0
  148. package/dist/package/src/rpc/bi/admin/v1/bi_query.ts +187 -0
  149. package/dist/package/src/rpc/bi/admin/v1/bi_query_record.ts +98 -0
  150. package/dist/package/src/rpc/bi/admin/v1/bi_source.ts +338 -0
  151. package/dist/package/src/rpc/common/v1/types.ts +77 -0
  152. package/dist/package/src/schema/layout.ts +54 -0
  153. package/dist/package/src/schema/types.ts +103 -0
  154. package/dist/package/src/styles.scss +1 -0
  155. package/dist/package/src/theme/index.ts +108 -0
  156. package/dist/package/src/theme/palettes.ts +34 -0
  157. package/dist/package/src/theme/presets.ts +278 -0
  158. package/dist/package/src/views/card/index.vue +5 -0
  159. package/dist/package/src/views/dashboard/index.vue +58 -0
  160. package/dist/package/src/views/designer/index.vue +164 -0
  161. package/dist/package/src/views/manage/index.vue +276 -0
  162. package/dist/package/src/views/query/index.vue +593 -0
  163. package/dist/package/src/views/source/index.vue +214 -0
  164. package/dist/package/src/views/workbench/index.vue +208 -0
  165. package/package.json +67 -3
  166. package/tsconfig.json +12 -0
@@ -0,0 +1,491 @@
1
+ /**
2
+ * ECharts 引擎适配器(默认内置引擎)。
3
+ * 按需注册图表与组件(素材:go-wind-admin ele 端 plugins/echarts 注册表),
4
+ * 避免 import * as echarts from 'echarts' 的全量引入。
5
+ */
6
+ import * as echarts from "echarts/core";
7
+ import { BarChart, FunnelChart, GaugeChart, HeatmapChart, LineChart, PieChart, RadarChart, SankeyChart, ScatterChart, SunburstChart, TreemapChart, } from "echarts/charts";
8
+ import { DataZoomComponent, VisualMapComponent, DataZoomInsideComponent, DataZoomSliderComponent, DatasetComponent, GridComponent, LegendComponent, TitleComponent, TooltipComponent, TransformComponent, } from "echarts/components";
9
+ import { LabelLayout, UniversalTransition } from "echarts/features";
10
+ import { CanvasRenderer } from "echarts/renderers";
11
+ import { getColumns, toNumber, unique } from "../dataset";
12
+ import { createDimensionLabelFormatter } from "../dataset/labels";
13
+ import { formatNumber } from "../dataset/transform";
14
+ import { resolvePalette } from "../theme/palettes";
15
+ import { registerEngine } from "./types";
16
+ let registered = false;
17
+ function ensureRegistered() {
18
+ if (registered)
19
+ return;
20
+ registered = true;
21
+ echarts.use([
22
+ BarChart,
23
+ LineChart,
24
+ PieChart,
25
+ ScatterChart,
26
+ RadarChart,
27
+ GaugeChart,
28
+ FunnelChart,
29
+ TreemapChart,
30
+ SankeyChart,
31
+ HeatmapChart,
32
+ SunburstChart,
33
+ DatasetComponent,
34
+ GridComponent,
35
+ LegendComponent,
36
+ TitleComponent,
37
+ TooltipComponent,
38
+ TransformComponent,
39
+ VisualMapComponent,
40
+ DataZoomComponent,
41
+ DataZoomInsideComponent,
42
+ DataZoomSliderComponent,
43
+ LabelLayout,
44
+ UniversalTransition,
45
+ CanvasRenderer,
46
+ ]);
47
+ }
48
+ /** 轴最大值取整到「好看的刻度」(1/2/5×10^n) */
49
+ function niceMax(value) {
50
+ if (value <= 0)
51
+ return 100;
52
+ const exp = Math.floor(Math.log10(value));
53
+ const base = Math.pow(10, exp);
54
+ const scaled = value / base;
55
+ const nice = scaled <= 1 ? 1 : scaled <= 2 ? 2 : scaled <= 5 ? 5 : 10;
56
+ return nice * base;
57
+ }
58
+ function axisStyles(theme) {
59
+ return {
60
+ axisLabel: { color: theme.subText },
61
+ axisLine: { lineStyle: { color: theme.axisLine } },
62
+ axisTick: { lineStyle: { color: theme.axisLine } },
63
+ };
64
+ }
65
+ function tooltipStyles(theme) {
66
+ return {
67
+ backgroundColor: theme.tooltipBg,
68
+ borderColor: theme.axisLine,
69
+ textStyle: { color: theme.text },
70
+ };
71
+ }
72
+ /** schema + 行数据 + 主题模式 → ECharts option */
73
+ export function compileEcharts(chart, rows, theme) {
74
+ const enc = chart.encoding ?? {};
75
+ const style = chart.style ?? {};
76
+ const cols = getColumns(rows);
77
+ const dimensionLabel = createDimensionLabelFormatter(rows.map(row => row[enc.dimension ?? cols[0] ?? ""]), enc.dimension);
78
+ const seriesLabel = createDimensionLabelFormatter(rows.map(row => row[enc.series ?? cols[1] ?? ""]), enc.series);
79
+ const fmt = style.format;
80
+ const palette = resolvePalette(style.palette, theme);
81
+ const fmtTooltip = fmt ? { valueFormatter: (v) => formatNumber(toNumber(v), fmt) } : {};
82
+ const base = {
83
+ backgroundColor: "transparent",
84
+ color: palette,
85
+ textStyle: { color: theme.text },
86
+ tooltip: { ...tooltipStyles(theme), ...fmtTooltip },
87
+ };
88
+ // 图例显隐:显式 style.showLegend 优先,否则多系列自动显示
89
+ const multiSeries = !!(enc.series || (enc.measures?.length ?? 0) > 1);
90
+ const showLegend = style.showLegend ?? multiSeries;
91
+ // 引擎原生透传:浅合并每个顶层键(嵌套覆盖用整键替换语义)
92
+ const passthrough = (chart.options ?? {});
93
+ // y 轴数值格式化
94
+ const yAxisLabel = fmt ? { formatter: (v) => formatNumber(v, fmt) } : {};
95
+ const labelOption = style.showLabel ? { label: { show: true, color: theme.subText } } : {};
96
+ const cartesian = (seriesIn, categories, area) => {
97
+ const series = seriesIn.map((sr) => ({
98
+ ...labelOption,
99
+ ...(style.stack ? { stack: "total" } : {}),
100
+ ...sr,
101
+ }));
102
+ const hasLegend = showLegend && (multiSeries || seriesIn.length > 1 || enc.series);
103
+ return {
104
+ ...base,
105
+ legend: hasLegend
106
+ ? { top: 0, textStyle: { color: theme.subText }, itemWidth: 14, itemHeight: 8, formatter: (name) => name.split(" · ").map(seriesLabel).join(" · ") }
107
+ : undefined,
108
+ grid: { left: 8, right: 16, top: hasLegend ? 32 : 20, bottom: style.dataZoom ? 32 : 8, outerBoundsMode: "same", outerBoundsContain: "axisLabel" },
109
+ xAxis: {
110
+ ...(chart.type === "scatter" ? { type: "value" } : { type: "category", data: categories }),
111
+ ...axisStyles(theme),
112
+ ...(chart.type !== "scatter" ? { axisLabel: { color: theme.subText, formatter: dimensionLabel } } : {}),
113
+ splitLine: { show: false },
114
+ },
115
+ yAxis: {
116
+ type: "value",
117
+ ...axisStyles(theme),
118
+ axisLabel: { ...axisStyles(theme).axisLabel, ...yAxisLabel },
119
+ splitLine: { lineStyle: { color: theme.splitLine } },
120
+ },
121
+ ...(style.dataZoom
122
+ ? { dataZoom: [{ type: "inside" }, { type: "slider", height: 16, bottom: 4, ...(chart.type !== "scatter" ? { labelFormatter: (_value, text) => dimensionLabel(text) } : {}) }] }
123
+ : {}),
124
+ series: series.map((s) => ({
125
+ smooth: true,
126
+ ...(area ? { areaStyle: { opacity: 0.15 } } : {}),
127
+ ...s,
128
+ })),
129
+ ...passthrough,
130
+ };
131
+ };
132
+ switch (chart.type) {
133
+ case "bar":
134
+ case "line":
135
+ case "area":
136
+ case "scatter": {
137
+ const dim = enc.dimension ?? cols[0] ?? "";
138
+ const measure = enc.measure ?? cols.find((c) => c !== dim) ?? "";
139
+ // ECharts 无 area 系列:面积 = line + areaStyle(cartesian 已按 area 注入)
140
+ const seriesType = chart.type === "area" ? "line" : chart.type;
141
+ const categories = unique(rows.map((r) => String(r[dim] ?? "")));
142
+ if (["line", "area"].includes(chart.type) && !chart.sort && categories.every(value => /^\d{4}-\d{2}(?:-\d{2})?(?:[ T].*)?$/.test(value)))
143
+ categories.sort();
144
+ let series;
145
+ if (chart.type === "scatter") {
146
+ const group = (name, subset) => ({
147
+ name,
148
+ type: "scatter",
149
+ data: subset.map((r) => [toNumber(r[dim]), toNumber(r[measure])]),
150
+ symbolSize: 10,
151
+ });
152
+ series = enc.series
153
+ ? unique(rows.map((r) => String(r[enc.series]))).map((sn) => group(sn, rows.filter((r) => String(r[enc.series]) === sn)))
154
+ : [group(chart.title ?? measure, rows)];
155
+ }
156
+ else {
157
+ const seriesNames = enc.series ? unique(rows.map((r) => String(r[enc.series]))) : [""];
158
+ const measures = enc.measures?.length ? enc.measures : [measure];
159
+ series = seriesNames.flatMap((sn) => measures.map((m) => ({
160
+ name: enc.series ? (measures.length > 1 ? `${sn} · ${m}` : sn) : (enc.measures?.length ? m : chart.title ?? m),
161
+ type: seriesType,
162
+ data: categories.map((c) => {
163
+ const hit = rows.find((r) => String(r[dim]) === c && (!enc.series || String(r[enc.series]) === sn));
164
+ return hit ? toNumber(hit[m]) : null;
165
+ }),
166
+ })));
167
+ }
168
+ return {
169
+ ...cartesian(series, categories, chart.type === "area"),
170
+ ...passthrough,
171
+ };
172
+ }
173
+ case "pie":
174
+ case "donut": {
175
+ const dim = enc.dimension ?? cols[0] ?? "name";
176
+ const measure = enc.measure ?? cols.find((c) => c !== dim) ?? "value";
177
+ return {
178
+ ...base,
179
+ legend: style.showLegend === false
180
+ ? undefined
181
+ : { bottom: 0, textStyle: { color: theme.subText }, itemWidth: 14, itemHeight: 8, formatter: dimensionLabel },
182
+ series: [
183
+ {
184
+ type: "pie",
185
+ radius: chart.type === "donut" ? ["42%", "68%"] : "68%",
186
+ center: ["50%", "46%"],
187
+ itemStyle: { borderColor: "transparent", borderWidth: 2 },
188
+ label: {
189
+ show: style.showLabel ?? true,
190
+ color: theme.subText,
191
+ formatter: (p) => dimensionLabel(p.name) + (fmt ? ` ${formatNumber(toNumber(p.value), fmt)}` : ""),
192
+ },
193
+ data: rows.map((r) => ({
194
+ name: String(r[dim] ?? ""),
195
+ value: toNumber(r[measure]),
196
+ })),
197
+ },
198
+ ],
199
+ ...passthrough,
200
+ };
201
+ }
202
+ case "radar": {
203
+ const dim = enc.dimension ?? cols[0] ?? "name";
204
+ const measure = enc.measure ?? cols.find((c) => c !== dim) ?? "value";
205
+ const max = niceMax(Math.max(...rows.map((x) => toNumber(x[measure])), 1));
206
+ const categories = unique(rows.map((r) => String(r[dim] ?? "")));
207
+ const indicators = categories.map((name) => ({ name, max }));
208
+ let data;
209
+ if (enc.series) {
210
+ const seriesNames = unique(rows.map((r) => String(r[enc.series])));
211
+ data = seriesNames.map((sn) => ({
212
+ name: sn,
213
+ value: categories.map((category) => {
214
+ const hit = rows.find((r) => String(r[dim]) === category && String(r[enc.series]) === sn);
215
+ return hit ? toNumber(hit[measure]) : null;
216
+ }),
217
+ }));
218
+ }
219
+ else {
220
+ data = [{ name: chart.title ?? measure, value: rows.map((r) => toNumber(r[measure])) }];
221
+ }
222
+ return {
223
+ ...base,
224
+ legend: enc.series
225
+ ? { bottom: 0, textStyle: { color: theme.subText }, itemWidth: 14, itemHeight: 8, formatter: seriesLabel }
226
+ : undefined,
227
+ radar: {
228
+ indicator: indicators,
229
+ center: ["50%", "50%"],
230
+ radius: "62%",
231
+ axisName: { color: theme.subText, formatter: dimensionLabel },
232
+ axisLine: { lineStyle: { color: theme.axisLine } },
233
+ splitLine: { lineStyle: { color: theme.splitLine } },
234
+ splitArea: { show: false },
235
+ },
236
+ series: [{ type: "radar", data, areaStyle: { opacity: 0.12 } }],
237
+ ...passthrough,
238
+ };
239
+ }
240
+ case "gauge": {
241
+ const measure = enc.measure ?? cols[0] ?? "value";
242
+ const value = toNumber(rows[0]?.[measure]);
243
+ const max = niceMax(Math.max(value, 100));
244
+ return {
245
+ ...base,
246
+ series: [
247
+ {
248
+ type: "gauge",
249
+ min: 0,
250
+ max,
251
+ startAngle: 210,
252
+ endAngle: -30,
253
+ progress: { show: true, width: 12 },
254
+ axisLine: { lineStyle: { width: 12, color: [[1, theme.splitLine]] } },
255
+ axisTick: { show: false },
256
+ splitLine: { length: 6, lineStyle: { color: theme.axisLine } },
257
+ axisLabel: { color: theme.subText, distance: 16, fontSize: 10 },
258
+ pointer: { itemStyle: { color: "auto" } },
259
+ detail: {
260
+ valueAnimation: true,
261
+ formatter: fmt
262
+ ? (v) => formatNumber(v, fmt)
263
+ : "{value}",
264
+ color: theme.text,
265
+ fontSize: 22,
266
+ offsetCenter: [0, "62%"],
267
+ },
268
+ data: [{ value }],
269
+ },
270
+ ],
271
+ ...passthrough,
272
+ };
273
+ }
274
+ case "funnel": {
275
+ const dim = enc.dimension ?? cols[0] ?? "name";
276
+ const measure = enc.measure ?? cols.find((c) => c !== dim) ?? "value";
277
+ // 漏斗要求降序
278
+ const sorted = [...rows]
279
+ .map((r) => ({ name: String(r[dim] ?? ""), value: toNumber(r[measure]) }))
280
+ .sort((a, b) => b.value - a.value);
281
+ return {
282
+ ...base,
283
+ tooltip: { ...tooltipStyles(theme), trigger: "item", ...fmtTooltip },
284
+ series: [
285
+ {
286
+ type: "funnel",
287
+ left: "10%",
288
+ top: 12,
289
+ bottom: 12,
290
+ width: "80%",
291
+ sort: "descending",
292
+ gap: 2,
293
+ label: { show: true, position: "inside", color: "#fff", formatter: (p) => dimensionLabel(p.name) + (fmt ? ` ${formatNumber(toNumber(p.value), fmt)}` : "") },
294
+ data: sorted,
295
+ },
296
+ ],
297
+ ...passthrough,
298
+ };
299
+ }
300
+ case "treemap": {
301
+ const dim = enc.dimension ?? cols[0] ?? "name";
302
+ const measure = enc.measure ?? cols.find((c) => c !== dim) ?? "value";
303
+ return {
304
+ ...base,
305
+ series: [
306
+ {
307
+ type: "treemap",
308
+ left: 8,
309
+ right: 8,
310
+ top: 12,
311
+ bottom: 8,
312
+ roam: false,
313
+ nodeClick: false,
314
+ breadcrumb: { show: false },
315
+ label: { show: true, color: "#fff", formatter: (p) => dimensionLabel(p.name) + (fmt ? ` ${formatNumber(toNumber(p.value), fmt)}` : "") },
316
+ data: rows.map((r) => ({
317
+ name: String(r[dim] ?? ""),
318
+ value: toNumber(r[measure]),
319
+ })),
320
+ },
321
+ ],
322
+ ...passthrough,
323
+ };
324
+ }
325
+ case "sunburst": {
326
+ // 两层旭日:series 分组为内环,维度为外环叶子
327
+ const dim = enc.dimension ?? cols[0] ?? "name";
328
+ const measure = enc.measure ?? cols.find((c) => c !== dim) ?? "value";
329
+ let data;
330
+ if (enc.series) {
331
+ const seriesNames = unique(rows.map((r) => String(r[enc.series])));
332
+ data = seriesNames.map((sn) => {
333
+ const subset = rows.filter((r) => String(r[enc.series]) === sn);
334
+ return {
335
+ name: sn,
336
+ children: subset.map((r) => ({
337
+ name: String(r[dim] ?? ""),
338
+ value: toNumber(r[measure]),
339
+ })),
340
+ };
341
+ });
342
+ }
343
+ else {
344
+ data = rows.map((r) => ({
345
+ name: String(r[dim] ?? ""),
346
+ value: toNumber(r[measure]),
347
+ }));
348
+ }
349
+ return {
350
+ ...base,
351
+ series: [
352
+ {
353
+ type: "sunburst",
354
+ radius: [enc.series ? "18%" : "0%", "82%"],
355
+ label: { color: theme.subText, rotate: "radial", formatter: (p) => dimensionLabel(seriesLabel(p.name)) },
356
+ itemStyle: { borderColor: "transparent", borderWidth: 1 },
357
+ data,
358
+ },
359
+ ],
360
+ ...passthrough,
361
+ };
362
+ }
363
+ case "sankey": {
364
+ // 边列表:dimension=起点,series=终点,measure=流量
365
+ const sourceField = enc.dimension ?? cols[0] ?? "source";
366
+ const targetField = enc.series ?? cols[1] ?? "target";
367
+ const valueField = enc.measure ?? cols[2] ?? "value";
368
+ const nodeIds = new Set();
369
+ const links = [];
370
+ for (const row of rows) {
371
+ const source = String(row[sourceField] ?? "");
372
+ const target = String(row[targetField] ?? "");
373
+ if (!source || !target)
374
+ continue;
375
+ nodeIds.add(source);
376
+ nodeIds.add(target);
377
+ links.push({ source, target, value: toNumber(row[valueField]) });
378
+ }
379
+ return {
380
+ ...base,
381
+ series: [
382
+ {
383
+ type: "sankey",
384
+ left: 16,
385
+ right: 16,
386
+ top: 12,
387
+ bottom: 12,
388
+ nodeWidth: 14,
389
+ nodeGap: 10,
390
+ emphasis: { focus: "adjacency" },
391
+ lineStyle: { color: "gradient", curveness: 0.5 },
392
+ label: { color: theme.subText, formatter: (p) => dimensionLabel(seriesLabel(p.name)) },
393
+ data: [...nodeIds].map((id) => ({ name: id })),
394
+ links,
395
+ },
396
+ ],
397
+ ...passthrough,
398
+ };
399
+ }
400
+ case "heatmap": {
401
+ // 矩阵三元组:dimension=x 轴,series=y 轴,measure=值
402
+ const xField = enc.dimension ?? cols[0] ?? "x";
403
+ const yField = enc.series ?? cols[1] ?? "y";
404
+ const valueField = enc.measure ?? cols[2] ?? "value";
405
+ const xCats = unique(rows.map((r) => String(r[xField] ?? "")));
406
+ const yCats = unique(rows.map((r) => String(r[yField] ?? "")));
407
+ const data = rows.map((r) => [
408
+ xCats.indexOf(String(r[xField] ?? "")),
409
+ yCats.indexOf(String(r[yField] ?? "")),
410
+ toNumber(r[valueField]),
411
+ ]);
412
+ const max = Math.max(...rows.map((r) => toNumber(r[valueField])), 1);
413
+ return {
414
+ ...base,
415
+ // 为横向色阶和坐标标签分别留出空间,避免覆盖最后一行数据。
416
+ grid: { left: 8, right: 24, top: 12, bottom: 68, outerBoundsMode: "same", outerBoundsContain: "axisLabel" },
417
+ xAxis: { type: "category", data: xCats, ...axisStyles(theme), axisLabel: { color: theme.subText, formatter: dimensionLabel }, splitArea: { show: true } },
418
+ yAxis: { type: "category", data: yCats, ...axisStyles(theme), axisLabel: { color: theme.subText, formatter: seriesLabel }, splitArea: { show: true } },
419
+ visualMap: {
420
+ min: 0,
421
+ max,
422
+ calculable: true,
423
+ orient: "horizontal",
424
+ left: "center",
425
+ bottom: 0,
426
+ textStyle: { color: theme.subText },
427
+ inRange: { color: theme.palette.slice(0, 5) },
428
+ },
429
+ series: [
430
+ {
431
+ type: "heatmap",
432
+ data,
433
+ label: { show: chart.style?.showLabel ?? true, color: theme.text, fontSize: 10 },
434
+ },
435
+ ],
436
+ ...passthrough,
437
+ };
438
+ }
439
+ default:
440
+ throw new Error(`[bi] unsupported chart type: ${chart.type}`);
441
+ }
442
+ }
443
+ /**
444
+ * 创建 ECharts 引擎实例(幂等注册 echarts 模块)。
445
+ * handle 上带内部约定字段 __echartsInstance 供 bindEvents 取实例。
446
+ */
447
+ export function createEchartsEngine() {
448
+ ensureRegistered();
449
+ return {
450
+ mount(el, option) {
451
+ const instance = echarts.init(el);
452
+ instance.setOption(option);
453
+ const handle = {
454
+ __echartsInstance: instance,
455
+ update(next) {
456
+ instance.setOption(next, { notMerge: true });
457
+ },
458
+ resize() {
459
+ instance.resize();
460
+ },
461
+ dispose() {
462
+ instance.dispose();
463
+ },
464
+ getDataURL() {
465
+ return instance.getDataURL({ type: "png", pixelRatio: 2, backgroundColor: "transparent" });
466
+ },
467
+ };
468
+ return handle;
469
+ },
470
+ compile(chart, rows, theme) {
471
+ return compileEcharts(chart, rows, theme);
472
+ },
473
+ bindEvents(handle, cb) {
474
+ const inst = handle.__echartsInstance;
475
+ inst?.on("click", (params) => {
476
+ const e = {
477
+ chartId: "",
478
+ seriesName: params?.seriesName,
479
+ name: params?.name,
480
+ value: params?.value,
481
+ raw: params,
482
+ };
483
+ cb(e);
484
+ });
485
+ },
486
+ };
487
+ }
488
+ /** 默认注册(调用方引入本模块后执行一次;重复注册无害) */
489
+ export function installEchartsEngine() {
490
+ registerEngine("echarts", createEchartsEngine());
491
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * 图表引擎抽象:任何图表库实现这三个方法即可接入。
3
+ * 主题在编译期注入 option(引擎层不管主题切换,换主题 = 重新 compile + update)。
4
+ */
5
+ import type { BiChartSchema } from "../schema/types";
6
+ import type { BiRow } from "../schema/types";
7
+ import type { BiResolvedTheme } from "../theme";
8
+ /** 归一化图表事件(跨引擎联动的基础) */
9
+ export interface BiChartEvent {
10
+ chartId: string;
11
+ /** 命中的系列名(饼=系列即数据名;柱线=系列字段值) */
12
+ seriesName?: string;
13
+ /** 命中的类目名(dimension 值) */
14
+ name?: string;
15
+ value?: unknown;
16
+ /** 引擎原始事件参数(逃生口) */
17
+ raw?: unknown;
18
+ }
19
+ export interface ChartHandle {
20
+ update(option: unknown): void;
21
+ resize(): void;
22
+ dispose(): void;
23
+ /** 导出当前画布为 PNG data URL(引擎不支持时返回 undefined) */
24
+ getDataURL?(): string | undefined;
25
+ }
26
+ export interface ChartEngine {
27
+ /** 挂载容器并渲染首帧(option 由 compile 产出) */
28
+ mount(el: HTMLElement, option: unknown): ChartHandle;
29
+ /** schema + 行数据 + 主题 → 引擎原生 option */
30
+ compile(chart: BiChartSchema, rows: BiRow[], theme: BiResolvedTheme): unknown;
31
+ /** 订阅归一化点击事件 */
32
+ bindEvents(handle: ChartHandle, cb: (e: BiChartEvent) => void): void;
33
+ }
34
+ export declare function registerEngine(name: string, engine: ChartEngine): void;
35
+ export declare function getEngine(name: string | undefined): ChartEngine;
36
+ export declare function hasEngine(name: string | undefined): boolean;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 图表引擎抽象:任何图表库实现这三个方法即可接入。
3
+ * 主题在编译期注入 option(引擎层不管主题切换,换主题 = 重新 compile + update)。
4
+ */
5
+ // ============ 注册表 ============
6
+ const engines = new Map();
7
+ export function registerEngine(name, engine) {
8
+ engines.set(name, engine);
9
+ }
10
+ export function getEngine(name) {
11
+ const key = name || "echarts";
12
+ const engine = engines.get(key);
13
+ if (!engine) {
14
+ throw new Error(`[bi] chart engine "${key}" is not registered. Install and register the corresponding adapter package (e.g. registerEngine("${key}", create${key[0]?.toUpperCase() + key.slice(1)}Engine())).`);
15
+ }
16
+ return engine;
17
+ }
18
+ export function hasEngine(name) {
19
+ return engines.has(name || "echarts");
20
+ }
@@ -0,0 +1,17 @@
1
+ import type { ComputedRef } from "vue";
2
+ import type { BiResolvedTheme } from "./bi-core";
3
+ export interface BiEnv {
4
+ /** 宿主主题模式 getter(响应式);缺省自动跟随 html.dark + 系统偏好 */
5
+ themeMode?: () => "light" | "dark" | "auto";
6
+ }
7
+ export declare function provideBiEnv(env: BiEnv): void;
8
+ export declare function useBiEnv(): BiEnv;
9
+ /**
10
+ * 当前图表主题(编译期注入 option)。
11
+ * env.themeMode 缺省时跟随 html.dark + 系统偏好;提供时经 computed 追踪其响应式依赖。
12
+ */
13
+ export declare function useBiTheme(): ComputedRef<BiResolvedTheme>;
14
+ /** 使用管理端现有 Vue I18n,并以 BI 内置语言包作为缺省回退。 */
15
+ export declare function useBiI18n(): {
16
+ t(key: string, params?: Record<string, unknown>): string;
17
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * BI 运行环境注入:主题跟随 + 数据集 resolver + 语言。
3
+ * 缺省零配置:主题自动跟随 html.dark;static 数据集无需 resolver。
4
+ */
5
+ import { computed, inject, provide, ref } from "vue";
6
+ import { getLocaleText, useLocaleStore } from "@liujitcn/kratos-admin-core";
7
+ import { BI_MESSAGES_EN, BI_MESSAGES_ZH } from "./bi-messages";
8
+ import { resolveTheme, watchDark } from "./bi-core";
9
+ const BI_ENV_KEY = Symbol("BI_ENV");
10
+ const DEFAULT_ENV = {};
11
+ export function provideBiEnv(env) {
12
+ provide(BI_ENV_KEY, env);
13
+ }
14
+ export function useBiEnv() {
15
+ return inject(BI_ENV_KEY, DEFAULT_ENV);
16
+ }
17
+ // ============ 主题:模块级单一响应式源 ============
18
+ const darkRef = ref(false);
19
+ let bound = false;
20
+ function ensureDarkBinding() {
21
+ if (bound || typeof document === "undefined")
22
+ return;
23
+ bound = true;
24
+ watchDark(dark => {
25
+ darkRef.value = dark;
26
+ });
27
+ }
28
+ /**
29
+ * 当前图表主题(编译期注入 option)。
30
+ * env.themeMode 缺省时跟随 html.dark + 系统偏好;提供时经 computed 追踪其响应式依赖。
31
+ */
32
+ export function useBiTheme() {
33
+ const env = useBiEnv();
34
+ ensureDarkBinding();
35
+ return computed(() => {
36
+ const fallback = darkRef.value ? "dark" : "light";
37
+ if (!env.themeMode)
38
+ return resolveTheme(fallback);
39
+ const mode = env.themeMode();
40
+ return resolveTheme(mode === "auto" ? fallback : mode);
41
+ });
42
+ }
43
+ /** 从嵌套的 BI 内置语言包读取回退文案。 */
44
+ function getBuiltinMessage(messages, key) {
45
+ const value = key.split(".").reduce((current, segment) => {
46
+ if (!current || typeof current !== "object")
47
+ return undefined;
48
+ return current[segment];
49
+ }, messages);
50
+ return typeof value === "string" ? value : "";
51
+ }
52
+ /** 对 BI 组件内置文案执行管理端风格的插值。 */
53
+ function interpolate(message, params) {
54
+ return message.replace(/\{([A-Za-z0-9_]+)\}/g, (_, key) => String(params[key] ?? `{${key}}`));
55
+ }
56
+ /** 使用管理端现有 Vue I18n,并以 BI 内置语言包作为缺省回退。 */
57
+ export function useBiI18n() {
58
+ const { locale } = useLocaleStore();
59
+ return {
60
+ t(key, params = {}) {
61
+ const currentLocale = locale.value;
62
+ const projectMessage = getLocaleText(currentLocale, key);
63
+ const builtinMessages = currentLocale.toLowerCase().startsWith("zh") ? BI_MESSAGES_ZH : BI_MESSAGES_EN;
64
+ return interpolate(projectMessage || getBuiltinMessage(builtinMessages, key) || key, params);
65
+ }
66
+ };
67
+ }
@@ -0,0 +1 @@
1
+ export { biAdminModule } from "./module";
@@ -0,0 +1 @@
1
+ export { biAdminModule } from "./module";