@arronqzy/vue-view 0.1.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.
- package/README.md +50 -0
- package/package.json +49 -0
- package/src/env.d.ts +62 -0
- package/src/index.ts +4 -0
- package/src/panel/VueViewOnlinePreview.vue +276 -0
- package/src/panel/VueViewPanel.vue +871 -0
- package/src/panel/components/ConfigHintIcon.vue +34 -0
- package/src/panel/components/ElementsLayer.vue +165 -0
- package/src/panel/components/MaterialPreview.vue +135 -0
- package/src/panel/components/MaterialSidebar.vue +526 -0
- package/src/panel/components/MaterialSidebarTreeNode.vue +305 -0
- package/src/panel/components/MoveableLayer.vue +859 -0
- package/src/panel/components/PanelCanvas.vue +630 -0
- package/src/panel/components/PanelConfigSidebar.vue +397 -0
- package/src/panel/components/PanelRulers.vue +177 -0
- package/src/panel/components/SelectLayer.vue +115 -0
- package/src/panel/components/ViewElementScopePanel.vue +76 -0
- package/src/panel/components/WorkspaceConfigSidebar.vue +147 -0
- package/src/panel/components/WorkspaceProjectNav.vue +192 -0
- package/src/panel/components/WorkspaceStageSplit.vue +258 -0
- package/src/panel/components/config/ConfigColorField.vue +52 -0
- package/src/panel/components/config/ConfigFieldGroup.vue +20 -0
- package/src/panel/components/config/ConfigSection.vue +50 -0
- package/src/panel/components/config/PanelConfigAudioSection.vue +256 -0
- package/src/panel/components/config/PanelConfigChartSection.vue +650 -0
- package/src/panel/components/config/PanelConfigGeometrySection.vue +209 -0
- package/src/panel/components/config/PanelConfigGridChildSpan.vue +68 -0
- package/src/panel/components/config/PanelConfigGridSection.vue +103 -0
- package/src/panel/components/config/PanelConfigImageSection.vue +136 -0
- package/src/panel/components/config/PanelConfigMultiSelect.vue +434 -0
- package/src/panel/components/config/PanelConfigNodeInfo.vue +165 -0
- package/src/panel/components/config/PanelConfigReferenceSection.vue +77 -0
- package/src/panel/components/config/PanelConfigStyleSections.vue +208 -0
- package/src/panel/components/config/PanelConfigTextSection.vue +195 -0
- package/src/panel/components/config/PanelConfigVideoSection.vue +107 -0
- package/src/panel/components/config/shared.ts +74 -0
- package/src/panel/components/elementsLayerNodes.ts +830 -0
- package/src/panel/components/materialSidebarData.ts +85 -0
- package/src/panel/components/scope-config/ScopeConfigProvider.vue +153 -0
- package/src/panel/components/scope-config/ScopeTemplateAutocompleteHost.vue +234 -0
- package/src/panel/components/scope-config/ScopeTemplatePreviewHost.vue +192 -0
- package/src/panel/components/scope-config/ScopeTemplatePreviewPanel.vue +42 -0
- package/src/panel/components/scope-config/ScopeTemplateUsageHint.vue +20 -0
- package/src/panel/components/scope-config/ScopeTemplateWarningsPanel.vue +63 -0
- package/src/panel/components/scope-config/scopeConfigContext.ts +17 -0
- package/src/panel/components/scope-config/useScopeConfig.ts +11 -0
- package/src/panel/constants/messages.ts +34 -0
- package/src/panel/constants/zIndex.ts +6 -0
- package/src/panel/hooks/usePanelElements.ts +1075 -0
- package/src/panel/hooks/useRafThrottledScroll.ts +25 -0
- package/src/panel/hooks/useWorkspaceProjects.ts +240 -0
- package/src/panel/lib/panel-ruler-canvas.ts +139 -0
- package/src/panel/library/workspace-project-cache.ts +23 -0
- package/src/panel/library/workspace-project-db.ts +111 -0
- package/src/panel/library/workspace-project-sync.ts +41 -0
- package/src/panel/library/workspace-snapshot.ts +30 -0
- package/src/panel/parseOnlinePreviewSearchParams.ts +13 -0
- package/src/panel/scope/view-scope-store.ts +82 -0
- package/src/panel/types.ts +127 -0
- package/src/panel/utils/chartOptionBuilder.ts +327 -0
- package/src/panel/utils/gridPlacement.ts +189 -0
- package/src/panel/utils/mappingLayerOps.ts +142 -0
- package/src/panel/utils/panelElementDefaults.ts +161 -0
- package/src/panel/utils/panelElementNodes.ts +35 -0
- package/src/panel/utils/panelStateIO.ts +124 -0
- package/src/panel/utils/scope-autocomplete.ts +114 -0
- package/src/panel/utils/scope-field-labels.ts +46 -0
- package/src/panel/utils/scope-template-chart.ts +92 -0
- package/src/panel/utils/scope-template-preview.ts +124 -0
- package/src/panel/utils/scope-template-spread.ts +229 -0
- package/src/panel/utils/scope-template-warnings.ts +243 -0
- package/src/panel/utils/scope-template.ts +97 -0
- package/src/panel/utils/updateElementDraft.ts +221 -0
- package/src/panel/viewportZoom.ts +26 -0
- package/src/tailwind.css +43 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
onScopeDispose,
|
|
3
|
+
shallowRef,
|
|
4
|
+
toValue,
|
|
5
|
+
watchEffect,
|
|
6
|
+
type MaybeRefOrGetter,
|
|
7
|
+
type Ref,
|
|
8
|
+
type ShallowRef,
|
|
9
|
+
} from "vue";
|
|
10
|
+
|
|
11
|
+
const scopes = new Map<string, unknown>();
|
|
12
|
+
let version = 0;
|
|
13
|
+
const listeners = new Set<() => void>();
|
|
14
|
+
|
|
15
|
+
function emit() {
|
|
16
|
+
version += 1;
|
|
17
|
+
listeners.forEach((listener) => listener());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function subscribe(listener: () => void) {
|
|
21
|
+
listeners.add(listener);
|
|
22
|
+
return () => listeners.delete(listener);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function clearViewElementScopes() {
|
|
26
|
+
if (scopes.size === 0) return;
|
|
27
|
+
scopes.clear();
|
|
28
|
+
emit();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function setViewElementScope(elementId: string, scope: unknown) {
|
|
32
|
+
scopes.set(elementId, scope);
|
|
33
|
+
emit();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function setViewElementScopes(elementIds: string[], scope: unknown) {
|
|
37
|
+
if (elementIds.length === 0) return;
|
|
38
|
+
elementIds.forEach((id) => scopes.set(id, scope));
|
|
39
|
+
emit();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getViewElementScope(elementId: string): unknown | undefined {
|
|
43
|
+
return scopes.get(elementId);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function hasViewElementScope(elementId: string): boolean {
|
|
47
|
+
return scopes.has(elementId);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function useSyncExternalStore<T>(getSnapshot: () => T): ShallowRef<T> {
|
|
51
|
+
const state = shallowRef(getSnapshot()) as ShallowRef<T>;
|
|
52
|
+
const update = () => {
|
|
53
|
+
state.value = getSnapshot();
|
|
54
|
+
};
|
|
55
|
+
const unsub = subscribe(update);
|
|
56
|
+
onScopeDispose(unsub);
|
|
57
|
+
return state;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** 订阅单个视图元素的 scope 快照(Vue composable) */
|
|
61
|
+
export function useViewElementScope(
|
|
62
|
+
elementId: MaybeRefOrGetter<string | null | undefined>
|
|
63
|
+
): Ref<unknown | undefined> {
|
|
64
|
+
const scope = shallowRef<unknown>(undefined);
|
|
65
|
+
|
|
66
|
+
watchEffect((onCleanup) => {
|
|
67
|
+
const id = toValue(elementId);
|
|
68
|
+
const update = () => {
|
|
69
|
+
scope.value = id ? scopes.get(id) : undefined;
|
|
70
|
+
};
|
|
71
|
+
update();
|
|
72
|
+
const unsub = subscribe(update);
|
|
73
|
+
onCleanup(unsub);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return scope;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** 订阅 scope store 版本号,用于在任意 scope 变更时触发重渲染 */
|
|
80
|
+
export function useViewScopeStoreVersion(): Ref<number> {
|
|
81
|
+
return useSyncExternalStore(() => version);
|
|
82
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export type PanelChartConfig = {
|
|
2
|
+
title?: string;
|
|
3
|
+
color?: string;
|
|
4
|
+
colorMode?: "solid" | "gradient";
|
|
5
|
+
gradientFrom?: string;
|
|
6
|
+
gradientTo?: string;
|
|
7
|
+
gradientDirection?: "to-right" | "to-bottom" | "to-bottom-right" | "to-top-right";
|
|
8
|
+
renderMode?: "canvas" | "svg";
|
|
9
|
+
xAxisName?: string;
|
|
10
|
+
yAxisName?: string;
|
|
11
|
+
xAxisTickShow?: boolean;
|
|
12
|
+
yAxisTickShow?: boolean;
|
|
13
|
+
xAxisTickColor?: string;
|
|
14
|
+
yAxisTickColor?: string;
|
|
15
|
+
xAxisSplitLineShow?: boolean;
|
|
16
|
+
yAxisSplitLineShow?: boolean;
|
|
17
|
+
xAxisSplitLineColor?: string;
|
|
18
|
+
yAxisSplitLineColor?: string;
|
|
19
|
+
xAxisLabelColor?: string;
|
|
20
|
+
yAxisLabelColor?: string;
|
|
21
|
+
xAxisLabelFontSize?: number;
|
|
22
|
+
yAxisLabelFontSize?: number;
|
|
23
|
+
xAxisLabelAutoEllipsis?: boolean;
|
|
24
|
+
yAxisLabelAutoEllipsis?: boolean;
|
|
25
|
+
tooltipShow?: boolean;
|
|
26
|
+
tooltipTrigger?: "axis" | "item";
|
|
27
|
+
tooltipBackgroundColor?: string;
|
|
28
|
+
tooltipTextColor?: string;
|
|
29
|
+
tooltipFormatter?: string;
|
|
30
|
+
labels?: string[];
|
|
31
|
+
/** 逗号分隔的原始类目文本,支持 scope 模版;存在时优先于 labels */
|
|
32
|
+
labelsText?: string;
|
|
33
|
+
values?: number[];
|
|
34
|
+
/** 逗号分隔的原始数值文本,支持 scope 模版;存在时优先于 values */
|
|
35
|
+
valuesText?: string;
|
|
36
|
+
smooth?: boolean;
|
|
37
|
+
barWidth?: number;
|
|
38
|
+
pieInnerRadius?: number;
|
|
39
|
+
pieOuterRadius?: number;
|
|
40
|
+
option?: Record<string, unknown>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type PanelElementStyle = {
|
|
44
|
+
backgroundColor?: string;
|
|
45
|
+
backgroundImage?: string;
|
|
46
|
+
backgroundImageRemoteUrl?: string;
|
|
47
|
+
backgroundSize?: string;
|
|
48
|
+
backgroundPosition?: string;
|
|
49
|
+
borderWidth?: number;
|
|
50
|
+
borderStyle?: "none" | "solid" | "dashed" | "dotted" | "double";
|
|
51
|
+
borderColor?: string;
|
|
52
|
+
borderRadius?: number;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type ReferenceCopyMode = "shallow" | "deep";
|
|
56
|
+
|
|
57
|
+
export type PanelElement = {
|
|
58
|
+
id: string;
|
|
59
|
+
layerId: string;
|
|
60
|
+
zIndex?: number;
|
|
61
|
+
name?: string;
|
|
62
|
+
locked?: boolean;
|
|
63
|
+
materialType?: string;
|
|
64
|
+
refLayerId?: string;
|
|
65
|
+
refCopyMode?: ReferenceCopyMode;
|
|
66
|
+
refSnapshot?: PanelElement[];
|
|
67
|
+
chart?: PanelChartConfig;
|
|
68
|
+
textHtml?: string;
|
|
69
|
+
textAllowInput?: boolean;
|
|
70
|
+
textFontFamily?: string;
|
|
71
|
+
textFontSize?: number;
|
|
72
|
+
textFontWeight?: string;
|
|
73
|
+
textColor?: string;
|
|
74
|
+
textLineHeight?: number;
|
|
75
|
+
textAlign?: "left" | "center" | "right" | "justify";
|
|
76
|
+
gridRows?: number;
|
|
77
|
+
gridCols?: number;
|
|
78
|
+
gridGap?: number;
|
|
79
|
+
gridPadding?: number;
|
|
80
|
+
gridSnapThreshold?: number;
|
|
81
|
+
parentGridId?: string;
|
|
82
|
+
gridSlotIndex?: number;
|
|
83
|
+
gridColSpan?: number;
|
|
84
|
+
gridRowSpan?: number;
|
|
85
|
+
mappingSourceNodeId?: string;
|
|
86
|
+
mappingSourceLayerId?: string;
|
|
87
|
+
audioSrc?: string;
|
|
88
|
+
audioRemoteUrl?: string;
|
|
89
|
+
audioPosterImage?: string;
|
|
90
|
+
audioIconPreset?: "speaker" | "music" | "headphone" | "wave";
|
|
91
|
+
audioVisualEffect?: "none" | "pulse" | "ripple";
|
|
92
|
+
audioVisualSpeed?: "slow" | "normal" | "fast";
|
|
93
|
+
mediaAutoPauseOnEdit?: boolean;
|
|
94
|
+
videoSrc?: string;
|
|
95
|
+
videoRemoteUrl?: string;
|
|
96
|
+
geometryShape?: "rect" | "circle" | "triangle" | "diamond" | "hexagon" | "star" | "heart";
|
|
97
|
+
geometryColor?: string;
|
|
98
|
+
geometryScript?: string;
|
|
99
|
+
geometrySketchDataUrl?: string;
|
|
100
|
+
style?: PanelElementStyle;
|
|
101
|
+
x: number;
|
|
102
|
+
y: number;
|
|
103
|
+
width: number;
|
|
104
|
+
height: number;
|
|
105
|
+
rotate?: number;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type PanelLayer = {
|
|
109
|
+
id: string;
|
|
110
|
+
name: string;
|
|
111
|
+
locked: boolean;
|
|
112
|
+
editable: boolean;
|
|
113
|
+
isPrimary?: boolean;
|
|
114
|
+
isMapping?: boolean;
|
|
115
|
+
mappingBaseLayerId?: string;
|
|
116
|
+
mergeSelected?: boolean;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export type PanelHistoryItem = {
|
|
120
|
+
index: number;
|
|
121
|
+
timestamp: number;
|
|
122
|
+
label: string;
|
|
123
|
+
active: boolean;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export type PanelActionResult = { ok: true } | { ok: false; reason: string };
|
|
127
|
+
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import type { EChartsOption } from "echarts";
|
|
2
|
+
import type { PanelChartConfig, PanelElement } from "../types";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_CHART_VALUES = [12, 18, 9, 24];
|
|
5
|
+
|
|
6
|
+
export function getChartValuesDisplayText(chart?: PanelChartConfig): string {
|
|
7
|
+
if (chart?.valuesText !== undefined) return chart.valuesText;
|
|
8
|
+
return (chart?.values ?? []).join(",");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getChartLabelsDisplayText(chart?: PanelChartConfig): string {
|
|
12
|
+
if (chart?.labelsText !== undefined) return chart.labelsText;
|
|
13
|
+
return (chart?.labels ?? []).join(",");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function resolveChartLabels(element: PanelElement): string[] {
|
|
17
|
+
const chart = element.chart;
|
|
18
|
+
if (chart?.labels?.length) return chart.labels;
|
|
19
|
+
return ["A", "B", "C", "D"];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function parseChartValuesFromText(
|
|
23
|
+
text: string | undefined,
|
|
24
|
+
fallback: number[] = DEFAULT_CHART_VALUES
|
|
25
|
+
): number[] {
|
|
26
|
+
if (text === undefined) return fallback;
|
|
27
|
+
const trimmed = text.trim();
|
|
28
|
+
if (!trimmed) return fallback;
|
|
29
|
+
const parts = trimmed.split(",").map((s) => s.trim()).filter(Boolean);
|
|
30
|
+
if (!parts.length) return fallback;
|
|
31
|
+
return parts.map((s) => {
|
|
32
|
+
const n = Number(s);
|
|
33
|
+
return Number.isFinite(n) ? n : 0;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function resolveChartValues(element: PanelElement): number[] {
|
|
38
|
+
const chart = element.chart;
|
|
39
|
+
if (chart?.valuesText !== undefined) {
|
|
40
|
+
const parsed = parseChartValuesFromText(chart.valuesText, []);
|
|
41
|
+
return parsed.length ? parsed : DEFAULT_CHART_VALUES;
|
|
42
|
+
}
|
|
43
|
+
return chart?.values?.length ? chart.values : DEFAULT_CHART_VALUES;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const CHART_TYPES = new Set([
|
|
47
|
+
"bar",
|
|
48
|
+
"line",
|
|
49
|
+
"pie",
|
|
50
|
+
"area",
|
|
51
|
+
"scatter",
|
|
52
|
+
"radar",
|
|
53
|
+
"gauge",
|
|
54
|
+
"funnel",
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
type ChartType =
|
|
58
|
+
| "bar"
|
|
59
|
+
| "line"
|
|
60
|
+
| "pie"
|
|
61
|
+
| "area"
|
|
62
|
+
| "scatter"
|
|
63
|
+
| "radar"
|
|
64
|
+
| "gauge"
|
|
65
|
+
| "funnel";
|
|
66
|
+
|
|
67
|
+
function deepMerge<T extends Record<string, any>>(base: T, patch?: Record<string, unknown>): T {
|
|
68
|
+
if (!patch) return base;
|
|
69
|
+
const next: Record<string, any> = { ...base };
|
|
70
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
71
|
+
if (
|
|
72
|
+
value &&
|
|
73
|
+
typeof value === "object" &&
|
|
74
|
+
!Array.isArray(value) &&
|
|
75
|
+
next[key] &&
|
|
76
|
+
typeof next[key] === "object" &&
|
|
77
|
+
!Array.isArray(next[key])
|
|
78
|
+
) {
|
|
79
|
+
next[key] = deepMerge(next[key], value as Record<string, unknown>);
|
|
80
|
+
} else {
|
|
81
|
+
next[key] = value;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return next as T;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function buildChartOption(element: PanelElement): EChartsOption {
|
|
88
|
+
const chartType = (element.materialType ?? "") as ChartType;
|
|
89
|
+
const labels = resolveChartLabels(element);
|
|
90
|
+
const values = resolveChartValues(element);
|
|
91
|
+
const color = element.chart?.color || "#3b82f6";
|
|
92
|
+
const gradientFrom = element.chart?.gradientFrom || color;
|
|
93
|
+
const gradientTo = element.chart?.gradientTo || "#22d3ee";
|
|
94
|
+
const gradientDirection = element.chart?.gradientDirection ?? "to-right";
|
|
95
|
+
const useGradient = element.chart?.colorMode === "gradient";
|
|
96
|
+
const gradientMeta =
|
|
97
|
+
gradientDirection === "to-bottom"
|
|
98
|
+
? { x: 0, y: 0, x2: 0, y2: 1 }
|
|
99
|
+
: gradientDirection === "to-bottom-right"
|
|
100
|
+
? { x: 0, y: 0, x2: 1, y2: 1 }
|
|
101
|
+
: gradientDirection === "to-top-right"
|
|
102
|
+
? { x: 0, y: 1, x2: 1, y2: 0 }
|
|
103
|
+
: { x: 0, y: 0, x2: 1, y2: 0 };
|
|
104
|
+
const chartColor = useGradient
|
|
105
|
+
? ({
|
|
106
|
+
type: "linear",
|
|
107
|
+
...gradientMeta,
|
|
108
|
+
colorStops: [
|
|
109
|
+
{ offset: 0, color: gradientFrom },
|
|
110
|
+
{ offset: 1, color: gradientTo },
|
|
111
|
+
],
|
|
112
|
+
} as const)
|
|
113
|
+
: color;
|
|
114
|
+
const title = element.chart?.title ?? "";
|
|
115
|
+
const xAxisName = element.chart?.xAxisName ?? "";
|
|
116
|
+
const yAxisName = element.chart?.yAxisName ?? "";
|
|
117
|
+
const xAxisTickShow = element.chart?.xAxisTickShow ?? true;
|
|
118
|
+
const yAxisTickShow = element.chart?.yAxisTickShow ?? true;
|
|
119
|
+
const xAxisTickColor = element.chart?.xAxisTickColor ?? "#94a3b8";
|
|
120
|
+
const yAxisTickColor = element.chart?.yAxisTickColor ?? "#94a3b8";
|
|
121
|
+
const xAxisSplitLineShow = element.chart?.xAxisSplitLineShow ?? false;
|
|
122
|
+
const yAxisSplitLineShow = element.chart?.yAxisSplitLineShow ?? true;
|
|
123
|
+
const xAxisSplitLineColor = element.chart?.xAxisSplitLineColor ?? "#e2e8f0";
|
|
124
|
+
const yAxisSplitLineColor = element.chart?.yAxisSplitLineColor ?? "#e2e8f0";
|
|
125
|
+
const xAxisLabelColor = element.chart?.xAxisLabelColor ?? "#64748b";
|
|
126
|
+
const yAxisLabelColor = element.chart?.yAxisLabelColor ?? "#64748b";
|
|
127
|
+
const xAxisLabelFontSize = element.chart?.xAxisLabelFontSize ?? 10;
|
|
128
|
+
const yAxisLabelFontSize = element.chart?.yAxisLabelFontSize ?? 10;
|
|
129
|
+
const xAxisLabelAutoEllipsis = element.chart?.xAxisLabelAutoEllipsis ?? false;
|
|
130
|
+
const yAxisLabelAutoEllipsis = element.chart?.yAxisLabelAutoEllipsis ?? false;
|
|
131
|
+
const tooltipShow = element.chart?.tooltipShow ?? true;
|
|
132
|
+
const tooltipTrigger = element.chart?.tooltipTrigger ?? (chartType === "pie" || chartType === "funnel" || chartType === "scatter" ? "item" : "axis");
|
|
133
|
+
const tooltipBackgroundColor = element.chart?.tooltipBackgroundColor ?? "#0f172a";
|
|
134
|
+
const tooltipTextColor = element.chart?.tooltipTextColor ?? "#f8fafc";
|
|
135
|
+
const tooltipFormatter = element.chart?.tooltipFormatter;
|
|
136
|
+
const tooltipOption = {
|
|
137
|
+
show: tooltipShow,
|
|
138
|
+
trigger: tooltipTrigger,
|
|
139
|
+
backgroundColor: tooltipBackgroundColor,
|
|
140
|
+
textStyle: { color: tooltipTextColor },
|
|
141
|
+
formatter: tooltipFormatter || undefined,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
if (chartType === "pie") {
|
|
145
|
+
const inner = element.chart?.pieInnerRadius ?? 30;
|
|
146
|
+
const outer = element.chart?.pieOuterRadius ?? 65;
|
|
147
|
+
const baseOption: EChartsOption = {
|
|
148
|
+
animation: false,
|
|
149
|
+
title: { text: title, left: "center", top: 6, textStyle: { fontSize: 12 } },
|
|
150
|
+
color: [chartColor as any],
|
|
151
|
+
tooltip: tooltipOption,
|
|
152
|
+
series: [
|
|
153
|
+
{
|
|
154
|
+
type: "pie",
|
|
155
|
+
radius: [`${inner}%`, `${outer}%`],
|
|
156
|
+
center: ["50%", "58%"],
|
|
157
|
+
label: { fontSize: 10 },
|
|
158
|
+
data: labels.map((name, i) => ({ name, value: values[i] ?? 0 })),
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
};
|
|
162
|
+
return deepMerge(baseOption as Record<string, any>, element.chart?.option) as EChartsOption;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (chartType === "gauge") {
|
|
166
|
+
const gaugeValue = values[0] ?? 0;
|
|
167
|
+
const baseOption: EChartsOption = {
|
|
168
|
+
animation: false,
|
|
169
|
+
title: { text: title, left: "center", top: 6, textStyle: { fontSize: 12 } },
|
|
170
|
+
tooltip: tooltipOption,
|
|
171
|
+
series: [
|
|
172
|
+
{
|
|
173
|
+
type: "gauge",
|
|
174
|
+
min: 0,
|
|
175
|
+
max: 100,
|
|
176
|
+
progress: { show: true, width: 10 },
|
|
177
|
+
axisLine: { lineStyle: { width: 10 } },
|
|
178
|
+
detail: { valueAnimation: false, formatter: "{value}%" },
|
|
179
|
+
data: [{ value: gaugeValue, name: title }],
|
|
180
|
+
itemStyle: { color: chartColor as any },
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
return deepMerge(baseOption as Record<string, any>, element.chart?.option) as EChartsOption;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (chartType === "radar") {
|
|
188
|
+
const indicator = labels.map((name) => ({ name, max: 100 }));
|
|
189
|
+
const baseOption: EChartsOption = {
|
|
190
|
+
animation: false,
|
|
191
|
+
title: { text: title, left: "center", top: 6, textStyle: { fontSize: 12 } },
|
|
192
|
+
tooltip: tooltipOption,
|
|
193
|
+
radar: { indicator, radius: "60%", center: ["50%", "58%"] },
|
|
194
|
+
series: [
|
|
195
|
+
{
|
|
196
|
+
type: "radar",
|
|
197
|
+
data: [{ value: values, name: title }],
|
|
198
|
+
areaStyle: { opacity: 0.25 },
|
|
199
|
+
lineStyle: { color: chartColor as any },
|
|
200
|
+
itemStyle: { color: chartColor as any },
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
};
|
|
204
|
+
return deepMerge(baseOption as Record<string, any>, element.chart?.option) as EChartsOption;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (chartType === "funnel") {
|
|
208
|
+
const baseOption: EChartsOption = {
|
|
209
|
+
animation: false,
|
|
210
|
+
title: { text: title, left: "center", top: 6, textStyle: { fontSize: 12 } },
|
|
211
|
+
tooltip: tooltipOption,
|
|
212
|
+
series: [
|
|
213
|
+
{
|
|
214
|
+
type: "funnel",
|
|
215
|
+
top: 28,
|
|
216
|
+
left: "10%",
|
|
217
|
+
width: "80%",
|
|
218
|
+
height: "65%",
|
|
219
|
+
sort: "descending",
|
|
220
|
+
data: labels.map((name, i) => ({ name, value: values[i] ?? 0 })),
|
|
221
|
+
label: { fontSize: 10 },
|
|
222
|
+
itemStyle: { color: chartColor as any },
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
};
|
|
226
|
+
return deepMerge(baseOption as Record<string, any>, element.chart?.option) as EChartsOption;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (chartType === "scatter") {
|
|
230
|
+
const baseOption: EChartsOption = {
|
|
231
|
+
animation: false,
|
|
232
|
+
title: { text: title, left: 8, top: 6, textStyle: { fontSize: 12 } },
|
|
233
|
+
grid: { left: 28, right: 10, top: 30, bottom: 20 },
|
|
234
|
+
xAxis: {
|
|
235
|
+
type: "value",
|
|
236
|
+
name: xAxisName,
|
|
237
|
+
nameTextStyle: { fontSize: 10 },
|
|
238
|
+
axisLabel: {
|
|
239
|
+
fontSize: xAxisLabelFontSize,
|
|
240
|
+
color: xAxisLabelColor,
|
|
241
|
+
overflow: xAxisLabelAutoEllipsis ? "truncate" : undefined,
|
|
242
|
+
hideOverlap: xAxisLabelAutoEllipsis,
|
|
243
|
+
width: xAxisLabelAutoEllipsis ? 80 : undefined,
|
|
244
|
+
},
|
|
245
|
+
axisTick: { show: xAxisTickShow, lineStyle: { color: xAxisTickColor } },
|
|
246
|
+
splitLine: { show: xAxisSplitLineShow, lineStyle: { color: xAxisSplitLineColor } },
|
|
247
|
+
},
|
|
248
|
+
yAxis: {
|
|
249
|
+
type: "value",
|
|
250
|
+
name: yAxisName,
|
|
251
|
+
nameTextStyle: { fontSize: 10 },
|
|
252
|
+
axisLabel: {
|
|
253
|
+
fontSize: yAxisLabelFontSize,
|
|
254
|
+
color: yAxisLabelColor,
|
|
255
|
+
overflow: yAxisLabelAutoEllipsis ? "truncate" : undefined,
|
|
256
|
+
hideOverlap: yAxisLabelAutoEllipsis,
|
|
257
|
+
width: yAxisLabelAutoEllipsis ? 80 : undefined,
|
|
258
|
+
},
|
|
259
|
+
axisTick: { show: yAxisTickShow, lineStyle: { color: yAxisTickColor } },
|
|
260
|
+
splitLine: { show: yAxisSplitLineShow, lineStyle: { color: yAxisSplitLineColor } },
|
|
261
|
+
},
|
|
262
|
+
tooltip: tooltipOption,
|
|
263
|
+
series: [
|
|
264
|
+
{
|
|
265
|
+
type: "scatter",
|
|
266
|
+
data: values.map((v, i) => [i + 1, v]),
|
|
267
|
+
itemStyle: { color: chartColor as any },
|
|
268
|
+
symbolSize: 10,
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
};
|
|
272
|
+
return deepMerge(baseOption as Record<string, any>, element.chart?.option) as EChartsOption;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const seriesType = chartType === "area" ? "line" : chartType;
|
|
276
|
+
const baseOption: EChartsOption = {
|
|
277
|
+
animation: false,
|
|
278
|
+
title: { text: title, left: 8, top: 6, textStyle: { fontSize: 12 } },
|
|
279
|
+
grid: { left: 28, right: 10, top: 30, bottom: 20 },
|
|
280
|
+
xAxis: {
|
|
281
|
+
type: "category",
|
|
282
|
+
name: xAxisName,
|
|
283
|
+
nameTextStyle: { fontSize: 10 },
|
|
284
|
+
data: labels,
|
|
285
|
+
axisLabel: {
|
|
286
|
+
fontSize: xAxisLabelFontSize,
|
|
287
|
+
color: xAxisLabelColor,
|
|
288
|
+
overflow: xAxisLabelAutoEllipsis ? "truncate" : undefined,
|
|
289
|
+
hideOverlap: xAxisLabelAutoEllipsis,
|
|
290
|
+
width: xAxisLabelAutoEllipsis ? 80 : undefined,
|
|
291
|
+
},
|
|
292
|
+
axisTick: { show: xAxisTickShow, lineStyle: { color: xAxisTickColor } },
|
|
293
|
+
splitLine: { show: xAxisSplitLineShow, lineStyle: { color: xAxisSplitLineColor } },
|
|
294
|
+
},
|
|
295
|
+
yAxis: {
|
|
296
|
+
type: "value",
|
|
297
|
+
name: yAxisName,
|
|
298
|
+
nameTextStyle: { fontSize: 10 },
|
|
299
|
+
axisLabel: {
|
|
300
|
+
fontSize: yAxisLabelFontSize,
|
|
301
|
+
color: yAxisLabelColor,
|
|
302
|
+
overflow: yAxisLabelAutoEllipsis ? "truncate" : undefined,
|
|
303
|
+
hideOverlap: yAxisLabelAutoEllipsis,
|
|
304
|
+
width: yAxisLabelAutoEllipsis ? 80 : undefined,
|
|
305
|
+
},
|
|
306
|
+
axisTick: { show: yAxisTickShow, lineStyle: { color: yAxisTickColor } },
|
|
307
|
+
splitLine: { show: yAxisSplitLineShow, lineStyle: { color: yAxisSplitLineColor } },
|
|
308
|
+
},
|
|
309
|
+
tooltip: tooltipOption,
|
|
310
|
+
series: [
|
|
311
|
+
{
|
|
312
|
+
type: seriesType,
|
|
313
|
+
data: values,
|
|
314
|
+
smooth:
|
|
315
|
+
chartType === "line" || chartType === "area"
|
|
316
|
+
? (element.chart?.smooth ?? true)
|
|
317
|
+
: undefined,
|
|
318
|
+
barWidth: chartType === "bar" ? (element.chart?.barWidth ?? 24) : undefined,
|
|
319
|
+
areaStyle: chartType === "area" ? { opacity: 0.25 } : undefined,
|
|
320
|
+
itemStyle: { color: chartColor as any },
|
|
321
|
+
lineStyle: { color: chartColor as any },
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
};
|
|
325
|
+
return deepMerge(baseOption as Record<string, any>, element.chart?.option) as EChartsOption;
|
|
326
|
+
}
|
|
327
|
+
|