@arronqzy/react-view 1.0.5 → 1.0.9

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 (36) hide show
  1. package/dist/styles.css +519 -9
  2. package/package.json +15 -7
  3. package/src/core/template.tsx +12 -2
  4. package/src/panel/ReactViewOnlinePreview.tsx +18 -8
  5. package/src/panel/ReactViewPanel.tsx +386 -170
  6. package/src/panel/ai/AssistantChatPanel.tsx +895 -0
  7. package/src/panel/ai/AssistantMarkdown.tsx +256 -0
  8. package/src/panel/ai/applyAssistantAction.ts +652 -0
  9. package/src/panel/ai/revealConfigField.ts +137 -0
  10. package/src/panel/components/ConfigHintIcon.tsx +6 -2
  11. package/src/panel/components/ElementsLayer.tsx +37 -18
  12. package/src/panel/components/MaterialSidebar.tsx +157 -109
  13. package/src/panel/components/MoveableLayer.tsx +7 -6
  14. package/src/panel/components/PanelConfigSidebar.tsx +434 -389
  15. package/src/panel/components/ViewElementScopePanel.tsx +9 -5
  16. package/src/panel/components/WorkspaceConfigSidebar.tsx +5 -2
  17. package/src/panel/components/WorkspaceProjectNav.tsx +25 -14
  18. package/src/panel/components/WorkspaceStageSplit.tsx +3 -1
  19. package/src/panel/components/scope-config/ScopeTemplatePreviewHost.tsx +11 -7
  20. package/src/panel/components/scope-config/ScopeTemplateUsageHint.tsx +6 -18
  21. package/src/panel/components/scope-config/ScopeTemplateWarningsPanel.tsx +5 -2
  22. package/src/panel/components/table/ConditionEditor.tsx +205 -0
  23. package/src/panel/components/table/JsonCodeEditor.tsx +115 -0
  24. package/src/panel/components/table/PanelConfigTableSection.tsx +2192 -0
  25. package/src/panel/components/table/TableNodeContent.tsx +544 -0
  26. package/src/panel/constants/messages.ts +76 -34
  27. package/src/panel/hooks/usePanelElements.ts +64 -52
  28. package/src/panel/hooks/useWorkspaceProjects.ts +11 -8
  29. package/src/panel/types.ts +4 -0
  30. package/src/panel/utils/panelElementDefaults.ts +69 -39
  31. package/src/panel/utils/scope-field-labels.ts +51 -38
  32. package/src/panel/utils/scope-template-preview.ts +10 -6
  33. package/src/panel/utils/scope-template-spread.ts +44 -21
  34. package/src/panel/utils/scope-template-warnings.ts +28 -16
  35. package/src/panel/utils/scope-template.ts +82 -2
  36. package/src/tailwind.css +17 -9
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useEffect, useMemo, useSyncExternalStore } from "react";
2
2
  import { store, type Node, type State } from "@arronqzy/rx-store";
3
+ import { useI18nOptional } from "@arronqzy/i18n/react";
3
4
  import type {
4
5
  PanelActionResult,
5
6
  PanelElement,
@@ -7,11 +8,14 @@ import type {
7
8
  PanelLayer,
8
9
  ReferenceCopyMode,
9
10
  } from "../types";
11
+ import { getPanelMessages } from "../constants/messages";
10
12
  import {
11
13
  DEFAULT_LAYER,
12
14
  DEFAULT_LAYER_ID,
13
15
  getDefaultChartConfig,
16
+ getDefaultTableConfig,
14
17
  getDefaultGridConfig,
18
+ getDefaultLayer,
15
19
  getDefaultNodeName,
16
20
  getDefaultSizeByMaterial,
17
21
  getDefaultTextContent,
@@ -44,6 +48,10 @@ import {
44
48
  export type { PanelActionResult, PanelHistoryItem, PanelLayer } from "../types";
45
49
 
46
50
  export function usePanelElements() {
51
+ const { t } = useI18nOptional();
52
+ const messages = useMemo(() => getPanelMessages(t), [t]);
53
+ const defaultLayer = useMemo(() => getDefaultLayer(t), [t]);
54
+
47
55
  const state = useSyncExternalStore(
48
56
  (cb) => {
49
57
  const sub = store.select().subscribe(cb);
@@ -52,7 +60,7 @@ export function usePanelElements() {
52
60
  () => store.getState(),
53
61
  () => store.getState()
54
62
  ) as State;
55
- const rawLayers = (state.variables?.layers as PanelLayer[] | undefined) ?? [DEFAULT_LAYER];
63
+ const rawLayers = (state.variables?.layers as PanelLayer[] | undefined) ?? [defaultLayer];
56
64
  const layers = useMemo(() => normalizePrimaryLayer(rawLayers), [rawLayers]);
57
65
  const activeLayerId =
58
66
  (state.variables?.activeLayerId as string | undefined) ?? DEFAULT_LAYER_ID;
@@ -70,8 +78,8 @@ export function usePanelElements() {
70
78
  store.update((draft) => {
71
79
  draft.variables = draft.variables ?? {};
72
80
  draft.variables.layers = hasLayers
73
- ? normalizePrimaryLayer((draft.variables.layers as PanelLayer[] | undefined) ?? [DEFAULT_LAYER])
74
- : [DEFAULT_LAYER];
81
+ ? normalizePrimaryLayer((draft.variables.layers as PanelLayer[] | undefined) ?? [defaultLayer])
82
+ : [defaultLayer];
75
83
  draft.variables.activeLayerId = hasActive
76
84
  ? draft.variables.activeLayerId
77
85
  : DEFAULT_LAYER_ID;
@@ -565,14 +573,14 @@ export function usePanelElements() {
565
573
  []
566
574
  );
567
575
 
568
- const addElementFromMaterial = useCallback((materialType: string, x: number, y: number) => {
576
+ const addElementFromMaterial = useCallback((materialType: string, x: number, y: number): string | null => {
569
577
  const current = store.getState();
570
578
  const currentLayers =
571
579
  (current.variables?.layers as PanelLayer[] | undefined) ?? [DEFAULT_LAYER];
572
580
  const currentLayerId =
573
581
  (current.variables?.activeLayerId as string | undefined) ?? DEFAULT_LAYER_ID;
574
582
  const layer = currentLayers.find((l) => l.id === currentLayerId);
575
- if (!layer || layer.locked) return;
583
+ if (!layer || layer.locked) return null;
576
584
 
577
585
  const size = getDefaultSizeByMaterial(materialType);
578
586
  const id = randomId("el");
@@ -581,11 +589,12 @@ export function usePanelElements() {
581
589
  layerId: currentLayerId,
582
590
  zIndex: 1,
583
591
  materialType,
584
- name: getDefaultNodeName(materialType),
585
- ...getDefaultTextContent(materialType),
592
+ name: getDefaultNodeName(materialType, t),
593
+ ...getDefaultTextContent(materialType, t),
586
594
  ...getDefaultGridConfig(materialType),
587
595
  refCopyMode: materialType === "reference" ? "shallow" : undefined,
588
- chart: getDefaultChartConfig(materialType),
596
+ chart: getDefaultChartConfig(materialType, t),
597
+ table: getDefaultTableConfig(materialType, t),
589
598
  geometryShape: materialType === "geometry" ? "rect" : undefined,
590
599
  geometryColor: materialType === "geometry" ? "#3b82f6" : undefined,
591
600
  geometryScript: undefined,
@@ -662,7 +671,7 @@ export function usePanelElements() {
662
671
  },
663
672
  { meta: { type: "node.add", materialType, id: sourceId } }
664
673
  );
665
- return;
674
+ return sourceId;
666
675
  }
667
676
  store.update(
668
677
  (draft) => {
@@ -676,7 +685,8 @@ export function usePanelElements() {
676
685
  },
677
686
  { meta: { type: "node.add", materialType, id } }
678
687
  );
679
- }, []);
688
+ return id;
689
+ }, [t]);
680
690
 
681
691
  const setReferenceCopyMode = useCallback(
682
692
  (id: string, mode: ReferenceCopyMode) => {
@@ -727,7 +737,7 @@ export function usePanelElements() {
727
737
  const nextId = randomId("layer");
728
738
  const next: PanelLayer = {
729
739
  id: nextId,
730
- name: `图层${list.length + 1}`,
740
+ name: t("panel.defaults.layerN", { n: list.length + 1 }),
731
741
  locked: false,
732
742
  editable: true,
733
743
  isPrimary: false,
@@ -740,11 +750,11 @@ export function usePanelElements() {
740
750
  },
741
751
  { meta: { type: "layer.add" } }
742
752
  );
743
- }, []);
753
+ }, [t]);
744
754
 
745
755
  const openElementsInNewLayer = useCallback((ids: string[]): PanelActionResult => {
746
756
  const filtered = ids.filter(Boolean);
747
- if (filtered.length === 0) return { ok: false, reason: "未选择节点" };
757
+ if (filtered.length === 0) return { ok: false, reason: messages.noNodesSelected };
748
758
  const current = store.getState();
749
759
  const currentLayers =
750
760
  (current.variables?.layers as PanelLayer[] | undefined) ?? [DEFAULT_LAYER];
@@ -757,14 +767,14 @@ export function usePanelElements() {
757
767
  const selected = expandedIds
758
768
  .map((id) => nodeById.get(id))
759
769
  .filter((el): el is PanelElement => !!el);
760
- if (selected.length === 0) return { ok: false, reason: "未找到可迁移节点" };
770
+ if (selected.length === 0) return { ok: false, reason: messages.noMigratableNodes };
761
771
  const anchorEl = nodeById.get(filtered[0]!) ?? selected[0];
762
772
  const hasLocked = selected.some((el) => {
763
773
  if (el.locked) return true;
764
774
  const layer = currentLayers.find((l) => l.id === el.layerId);
765
775
  return !!layer?.locked;
766
776
  });
767
- if (hasLocked) return { ok: false, reason: "选中节点包含锁定内容,无法在新图层打开" };
777
+ if (hasLocked) return { ok: false, reason: messages.lockedCannotOpenMapping };
768
778
 
769
779
  const selectedSet = new Set(selected.map((el) => el.id));
770
780
  const idMap = new Map<string, string>();
@@ -772,7 +782,7 @@ export function usePanelElements() {
772
782
  const nextLayerId = randomId("layer");
773
783
  const nextLayer: PanelLayer = {
774
784
  id: nextLayerId,
775
- name: `映射图层${currentLayers.length + 1}`,
785
+ name: t("panel.defaults.mappingLayerN", { n: currentLayers.length + 1 }),
776
786
  locked: false,
777
787
  editable: true,
778
788
  isPrimary: false,
@@ -826,7 +836,7 @@ export function usePanelElements() {
826
836
  }
827
837
  );
828
838
  return { ok: true };
829
- }, []);
839
+ }, [messages, t]);
830
840
 
831
841
  const renameLayer = useCallback((layerId: string, name: string) => {
832
842
  store.update(
@@ -862,16 +872,16 @@ export function usePanelElements() {
862
872
  const current = store.getState();
863
873
  const list = (current.variables?.layers as PanelLayer[] | undefined) ?? [];
864
874
  const target = list.find((l) => l.id === layerId);
865
- if (!target) return { ok: false, reason: "图层不存在" };
866
- if (!target.editable) return { ok: false, reason: "默认图层不可删除" };
867
- if (target.locked) return { ok: false, reason: "锁定图层不可删除" };
875
+ if (!target) return { ok: false, reason: messages.layerNotFound };
876
+ if (!target.editable) return { ok: false, reason: messages.defaultLayerCannotDelete };
877
+ if (target.locked) return { ok: false, reason: messages.lockedLayerCannotDelete };
868
878
  if (mode === "move" && !targetLayerId) {
869
- return { ok: false, reason: "请选择目标图层" };
879
+ return { ok: false, reason: messages.selectTargetLayer };
870
880
  }
871
881
  const remainingLayers = list.filter((l) => l.id !== layerId);
872
882
  const moveTarget = remainingLayers.find((l) => l.id === targetLayerId);
873
883
  if (mode === "move" && !moveTarget) {
874
- return { ok: false, reason: "目标图层不存在" };
884
+ return { ok: false, reason: messages.targetLayerNotFound };
875
885
  }
876
886
  const allElements = (current.root.children ?? [])
877
887
  .filter((n) => isPanelElementNode(n) && n.props)
@@ -885,7 +895,7 @@ export function usePanelElements() {
885
895
  return (el.refCopyMode ?? "shallow") !== "deep";
886
896
  });
887
897
  if (hasBlockingRef) {
888
- return { ok: false, reason: "该图层仍被浅拷贝引用,请先删除引用节点或改为深拷贝" };
898
+ return { ok: false, reason: messages.shallowRefBlockingDelete };
889
899
  }
890
900
 
891
901
  store.update(
@@ -924,7 +934,7 @@ export function usePanelElements() {
924
934
  );
925
935
  return { ok: true };
926
936
  },
927
- []
937
+ [messages, t]
928
938
  );
929
939
 
930
940
  const toggleLayerMergeSelected = useCallback((layerId: string) => {
@@ -951,7 +961,9 @@ export function usePanelElements() {
951
961
  const nextId = randomId("layer");
952
962
  const nextLayer: PanelLayer = {
953
963
  id: nextId,
954
- name: name?.trim() || `图层-${Math.random().toString(36).slice(2, 6)}`,
964
+ name: name?.trim() || t("panel.defaults.layerRandom", {
965
+ id: Math.random().toString(36).slice(2, 6),
966
+ }),
955
967
  locked: false,
956
968
  editable: true,
957
969
  isPrimary: false,
@@ -978,7 +990,7 @@ export function usePanelElements() {
978
990
  },
979
991
  { meta: { type: "layer.merge", name } }
980
992
  );
981
- }, []);
993
+ }, [t]);
982
994
 
983
995
  const setPrimaryLayer = useCallback((layerId: string) => {
984
996
  store.update(
@@ -1009,41 +1021,41 @@ export function usePanelElements() {
1009
1021
  const entries = store.getHistoryEntries();
1010
1022
  const cursor = store.getHistoryCursorIndex();
1011
1023
  const labelByType: Record<string, string> = {
1012
- initial: "初始化",
1013
- "node.update": "更新节点",
1014
- "node.group-drag": "批量移动节点",
1015
- "node.group-resize": "批量缩放节点",
1016
- "node.group-rotate": "批量旋转节点",
1017
- "node.delete": "删除节点",
1018
- "node.batch-delete": "批量删除节点",
1019
- "node.z.front": "节点置顶",
1020
- "node.z.back": "节点置底",
1021
- "node.z.up": "节点上移一层",
1022
- "node.z.down": "节点下移一层",
1023
- "node.duplicate": "复制节点",
1024
- "node.ref-copy-mode": "设置引用拷贝模式",
1025
- "node.add": "添加节点",
1026
- "layer.activate": "切换图层",
1027
- "layer.add": "新增图层",
1028
- "layer.rename": "重命名图层",
1029
- "layer.toggle-lock": "切换图层锁定",
1030
- "layer.open-selected-mapping": "映射图层打开选中节点",
1031
- "layer.delete": "删除图层",
1032
- "layer.toggle-merge": "勾选合并图层",
1033
- "layer.merge": "合并图层",
1034
- "layer.set-primary": "设置主图层",
1035
- "panel.import": "导入面板",
1024
+ initial: t("panel.history.initial"),
1025
+ "node.update": t("panel.history.nodeUpdate"),
1026
+ "node.group-drag": t("panel.history.nodeGroupDrag"),
1027
+ "node.group-resize": t("panel.history.nodeGroupResize"),
1028
+ "node.group-rotate": t("panel.history.nodeGroupRotate"),
1029
+ "node.delete": t("panel.history.nodeDelete"),
1030
+ "node.batch-delete": t("panel.history.nodeBatchDelete"),
1031
+ "node.z.front": t("panel.history.nodeZFront"),
1032
+ "node.z.back": t("panel.history.nodeZBack"),
1033
+ "node.z.up": t("panel.history.nodeZUp"),
1034
+ "node.z.down": t("panel.history.nodeZDown"),
1035
+ "node.duplicate": t("panel.history.nodeDuplicate"),
1036
+ "node.ref-copy-mode": t("panel.history.nodeRefCopyMode"),
1037
+ "node.add": t("panel.history.nodeAdd"),
1038
+ "layer.activate": t("panel.history.layerActivate"),
1039
+ "layer.add": t("panel.history.layerAdd"),
1040
+ "layer.rename": t("panel.history.layerRename"),
1041
+ "layer.toggle-lock": t("panel.history.layerToggleLock"),
1042
+ "layer.open-selected-mapping": t("panel.history.layerOpenSelectedMapping"),
1043
+ "layer.delete": t("panel.history.layerDelete"),
1044
+ "layer.toggle-merge": t("panel.history.layerToggleMerge"),
1045
+ "layer.merge": t("panel.history.layerMerge"),
1046
+ "layer.set-primary": t("panel.history.layerSetPrimary"),
1047
+ "panel.import": t("panel.history.panelImport"),
1036
1048
  };
1037
1049
  return entries.map((entry, index) => {
1038
1050
  const type = entry.meta?.type as string | undefined;
1039
1051
  return {
1040
1052
  index,
1041
1053
  timestamp: entry.timestamp,
1042
- label: (type && labelByType[type]) || "编辑",
1054
+ label: (type && labelByType[type]) || t("panel.history.fallback"),
1043
1055
  active: index === cursor,
1044
1056
  };
1045
1057
  });
1046
- }, [state]);
1058
+ }, [state, t]);
1047
1059
 
1048
1060
  const exportPanelData = useCallback(() => {
1049
1061
  const current = store.getState();
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { useI18nOptional } from "@arronqzy/i18n/react";
2
3
  import { BlueprintGraph } from "@arronqzy/react-blueprint";
3
4
  import type { BlueprintDocument, BlueprintMetaDraft } from "@arronqzy/react-blueprint";
4
5
  import type { State } from "@arronqzy/rx-store";
@@ -63,14 +64,15 @@ export function useWorkspaceProjects({
63
64
  panelRevision,
64
65
  onProjectApplied,
65
66
  }: UseWorkspaceProjectsOptions) {
67
+ const { t } = useI18nOptional();
66
68
  const [projects, setProjects] = useState<WorkspaceProjectListItem[]>([]);
67
69
  const [activeProjectId, setActiveProjectId] = useState<string | null>(null);
68
70
  const [activeProjectName, setActiveProjectName] = useState<string | null>(null);
69
71
  const syncedSnapshotRef = useRef<WorkspaceSnapshot | null>(null);
70
72
 
71
73
  const resolveProjectName = useCallback(() => {
72
- return productName.trim() || "未命名产物";
73
- }, [productName]);
74
+ return productName.trim() || t("panel.defaults.unnamedProduct");
75
+ }, [productName, t]);
74
76
 
75
77
  const buildCurrentSnapshot = useCallback((): WorkspaceSnapshot => {
76
78
  return {
@@ -115,7 +117,7 @@ export function useWorkspaceProjects({
115
117
  const blueprintDocument =
116
118
  record.blueprintDocument ?? BlueprintGraph.empty().document;
117
119
  const blueprintMeta: BlueprintMetaDraft = {
118
- name: record.blueprintMeta?.name ?? "未命名蓝图",
120
+ name: record.blueprintMeta?.name ?? t("panel.defaults.unnamedBlueprint"),
119
121
  remark: record.blueprintMeta?.remark ?? "",
120
122
  };
121
123
 
@@ -146,6 +148,7 @@ export function useWorkspaceProjects({
146
148
  setBlueprintMeta,
147
149
  setProductName,
148
150
  setTitleIconDataUrl,
151
+ t,
149
152
  ]
150
153
  );
151
154
 
@@ -206,16 +209,16 @@ export function useWorkspaceProjects({
206
209
  const record = await getWorkspaceProject(id);
207
210
  if (!record) {
208
211
  await refreshProjects();
209
- throw new Error("工作区不存在或已被删除");
212
+ throw new Error(t("panel.messages.workspaceNotFound"));
210
213
  }
211
214
  applyProjectRecord(record);
212
215
  },
213
- [applyProjectRecord, refreshProjects]
216
+ [applyProjectRecord, refreshProjects, t]
214
217
  );
215
218
 
216
219
  const handleSyncProject = useCallback(async () => {
217
220
  if (!activeProjectId) {
218
- throw new Error("请先保存工作区");
221
+ throw new Error(t("panel.messages.saveWorkspaceFirst"));
219
222
  }
220
223
  const existing = await getWorkspaceProject(activeProjectId);
221
224
  if (!existing) {
@@ -223,7 +226,7 @@ export function useWorkspaceProjects({
223
226
  setActiveProjectName(null);
224
227
  syncedSnapshotRef.current = null;
225
228
  await refreshProjects();
226
- throw new Error("工作区不存在或已被删除");
229
+ throw new Error(t("panel.messages.workspaceNotFound"));
227
230
  }
228
231
  const name = resolveProjectName() || existing.name;
229
232
  await persistProject({
@@ -232,7 +235,7 @@ export function useWorkspaceProjects({
232
235
  createdAt: existing.createdAt,
233
236
  });
234
237
  return name;
235
- }, [activeProjectId, persistProject, refreshProjects, resolveProjectName]);
238
+ }, [activeProjectId, persistProject, refreshProjects, resolveProjectName, t]);
236
239
 
237
240
  const handleDeleteProject = useCallback(
238
241
  async (id: string) => {
@@ -54,6 +54,9 @@ export type PanelElementStyle = {
54
54
 
55
55
  export type ReferenceCopyMode = "shallow" | "deep";
56
56
 
57
+ export type { PanelTableConfig } from "@arronqzy/view-table";
58
+ import type { PanelTableConfig } from "@arronqzy/view-table";
59
+
57
60
  export type PanelElement = {
58
61
  id: string;
59
62
  layerId: string;
@@ -65,6 +68,7 @@ export type PanelElement = {
65
68
  refCopyMode?: ReferenceCopyMode;
66
69
  refSnapshot?: PanelElement[];
67
70
  chart?: PanelChartConfig;
71
+ table?: PanelTableConfig;
68
72
  textHtml?: string;
69
73
  textAllowInput?: boolean;
70
74
  textFontFamily?: string;
@@ -1,41 +1,53 @@
1
+ import type { TranslateFn } from "@arronqzy/i18n";
2
+ import { tForLocale } from "@arronqzy/i18n";
1
3
  import type { PanelChartConfig, PanelLayer } from "../types";
4
+ import { createDefaultTableConfig, type PanelTableConfig } from "@arronqzy/view-table";
2
5
 
3
- const DEFAULT_NODE_NAME_MAP: Record<string, string> = {
4
- bar: "柱状图",
5
- line: "折线图",
6
- pie: "饼图",
7
- area: "面积图",
8
- scatter: "散点图",
9
- radar: "雷达图",
10
- gauge: "仪表盘",
11
- funnel: "漏斗图",
12
- text: "文本",
13
- grid: "网格布局",
14
- image: "图片",
15
- video: "视频",
16
- audio: "音频",
17
- reference: "引用组件",
18
- geometry: "几何",
6
+ const tFallback = tForLocale("zh-CN");
7
+
8
+ const DEFAULT_NODE_NAME_KEYS: Record<string, string> = {
9
+ bar: "panel.defaults.bar",
10
+ line: "panel.defaults.line",
11
+ pie: "panel.defaults.pie",
12
+ area: "panel.defaults.area",
13
+ scatter: "panel.defaults.scatter",
14
+ radar: "panel.defaults.radar",
15
+ gauge: "panel.defaults.gauge",
16
+ funnel: "panel.defaults.funnel",
17
+ text: "panel.defaults.text",
18
+ grid: "panel.defaults.grid",
19
+ image: "panel.defaults.image",
20
+ video: "panel.defaults.video",
21
+ audio: "panel.defaults.audio",
22
+ reference: "panel.defaults.reference",
23
+ geometry: "panel.defaults.geometry",
24
+ table: "panel.defaults.table",
19
25
  };
20
26
 
21
27
  export const DEFAULT_LAYER_ID = "layer-1";
22
28
 
23
- export function getDefaultNodeName(materialType: string): string {
24
- return DEFAULT_NODE_NAME_MAP[materialType] ?? materialType;
29
+ export function getDefaultNodeName(materialType: string, t: TranslateFn = tFallback): string {
30
+ const key = DEFAULT_NODE_NAME_KEYS[materialType];
31
+ return key ? t(key) : materialType;
25
32
  }
26
33
 
27
- export const DEFAULT_LAYER: PanelLayer = {
28
- id: DEFAULT_LAYER_ID,
29
- name: "图层1",
30
- locked: false,
31
- editable: false,
32
- isPrimary: true,
33
- isMapping: false,
34
- mappingBaseLayerId: undefined,
35
- };
34
+ export function getDefaultLayer(t: TranslateFn = tFallback): PanelLayer {
35
+ return {
36
+ id: DEFAULT_LAYER_ID,
37
+ name: t("panel.defaults.layer1"),
38
+ locked: false,
39
+ editable: false,
40
+ isPrimary: true,
41
+ isMapping: false,
42
+ mappingBaseLayerId: undefined,
43
+ };
44
+ }
45
+
46
+ /** @deprecated Prefer getDefaultLayer(t); kept for non-React call sites */
47
+ export const DEFAULT_LAYER: PanelLayer = getDefaultLayer();
36
48
 
37
49
  export function normalizePrimaryLayer(layers: PanelLayer[]): PanelLayer[] {
38
- if (layers.length === 0) return [DEFAULT_LAYER];
50
+ if (layers.length === 0) return [getDefaultLayer()];
39
51
  const explicitPrimary = layers.find((layer) => layer.isPrimary);
40
52
  const primaryId = explicitPrimary?.id ?? layers[0].id;
41
53
  return layers.map((layer) => {
@@ -68,15 +80,17 @@ export function getDefaultSizeByMaterial(materialType: string) {
68
80
  return { width: 280, height: 180 };
69
81
  case "geometry":
70
82
  return { width: 220, height: 220 };
83
+ case "table":
84
+ return { width: 480, height: 280 };
71
85
  default:
72
86
  return { width: 220, height: 130 };
73
87
  }
74
88
  }
75
89
 
76
- export function getDefaultTextContent(materialType: string) {
90
+ export function getDefaultTextContent(materialType: string, t: TranslateFn = tFallback) {
77
91
  if (materialType !== "text") return {};
78
92
  return {
79
- textHtml: "<p>双击输入文本</p>",
93
+ textHtml: `<p>${t("panel.defaults.doubleClickTextHtml")}</p>`,
80
94
  textAllowInput: true,
81
95
  textFontSize: 14,
82
96
  textFontWeight: "400",
@@ -96,7 +110,10 @@ export function getDefaultGridConfig(materialType: string) {
96
110
  } as const;
97
111
  }
98
112
 
99
- export function getDefaultChartConfig(materialType: string): PanelChartConfig | undefined {
113
+ export function getDefaultChartConfig(
114
+ materialType: string,
115
+ t: TranslateFn = tFallback
116
+ ): PanelChartConfig | undefined {
100
117
  if (!["bar", "line", "pie", "area", "scatter", "radar", "gauge", "funnel"].includes(materialType))
101
118
  return undefined;
102
119
  const common = {
@@ -107,40 +124,40 @@ export function getDefaultChartConfig(materialType: string): PanelChartConfig |
107
124
  };
108
125
  if (materialType === "bar") {
109
126
  return {
110
- title: "柱状图",
127
+ title: t("panel.defaults.bar"),
111
128
  ...common,
112
129
  barWidth: 24,
113
130
  };
114
131
  }
115
132
  if (materialType === "line") {
116
133
  return {
117
- title: "折线图",
134
+ title: t("panel.defaults.line"),
118
135
  ...common,
119
136
  smooth: true,
120
137
  };
121
138
  }
122
139
  if (materialType === "area") {
123
140
  return {
124
- title: "面积图",
141
+ title: t("panel.defaults.area"),
125
142
  ...common,
126
143
  smooth: true,
127
144
  };
128
145
  }
129
146
  if (materialType === "scatter") {
130
147
  return {
131
- title: "散点图",
148
+ title: t("panel.defaults.scatter"),
132
149
  ...common,
133
150
  };
134
151
  }
135
152
  if (materialType === "radar") {
136
153
  return {
137
- title: "雷达图",
154
+ title: t("panel.defaults.radar"),
138
155
  ...common,
139
156
  };
140
157
  }
141
158
  if (materialType === "gauge") {
142
159
  return {
143
- title: "仪表盘",
160
+ title: t("panel.defaults.gauge"),
144
161
  color: "#3b82f6",
145
162
  renderMode: "canvas",
146
163
  values: [68],
@@ -148,14 +165,27 @@ export function getDefaultChartConfig(materialType: string): PanelChartConfig |
148
165
  }
149
166
  if (materialType === "funnel") {
150
167
  return {
151
- title: "漏斗图",
168
+ title: t("panel.defaults.funnel"),
152
169
  ...common,
153
170
  };
154
171
  }
155
172
  return {
156
- title: "饼图",
173
+ title: t("panel.defaults.pie"),
157
174
  ...common,
158
175
  pieInnerRadius: 30,
159
176
  pieOuterRadius: 65,
160
177
  };
161
178
  }
179
+
180
+ export function getDefaultTableConfig(
181
+ materialType: string,
182
+ t: TranslateFn = tFallback
183
+ ): PanelTableConfig | undefined {
184
+ if (materialType !== "table") return undefined;
185
+ const cfg = createDefaultTableConfig();
186
+ cfg.emptyText = t("panel.config.tableEmpty");
187
+ if (cfg.columns?.[0]) cfg.columns[0].title = t("panel.config.tableColName");
188
+ if (cfg.columns?.[1]) cfg.columns[1].title = t("panel.config.tableColStatus");
189
+ if (cfg.columns?.[2]) cfg.columns[2].title = t("panel.config.tableColScore");
190
+ return cfg;
191
+ }