@pisell/materials 1.8.54 → 1.8.56

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 (151) hide show
  1. package/build/lowcode/assets-daily.json +11 -11
  2. package/build/lowcode/assets-dev.json +2 -2
  3. package/build/lowcode/assets-prod.json +11 -11
  4. package/build/lowcode/async/view.js +1 -1
  5. package/build/lowcode/index.js +1 -1
  6. package/build/lowcode/meta.js +3 -3
  7. package/build/lowcode/preview.js +1 -1
  8. package/build/lowcode/render/default/async/view.js +1 -1
  9. package/build/lowcode/render/default/view.css +1 -1
  10. package/build/lowcode/render/default/view.js +37 -37
  11. package/build/lowcode/view.css +1 -1
  12. package/build/lowcode/view.js +37 -37
  13. package/es/components/buttonGroupPreview/index.js +8 -4
  14. package/es/components/pisellFloorMapLayout/PisellFloorMapLayout.js +207 -49
  15. package/es/components/pisellFloorMapLayout/PisellFloorMapLayout.less +33 -2
  16. package/es/components/pisellFloorMapLayout/appearance/floorMapAppearance.js +50 -2
  17. package/es/components/pisellFloorMapLayout/components/EdgeLayer.js +252 -0
  18. package/es/components/pisellFloorMapLayout/components/EdgeLayer.less +11 -0
  19. package/es/components/pisellFloorMapLayout/components/EditableItemLayer.js +179 -29
  20. package/es/components/pisellFloorMapLayout/components/EditableItemLayer.less +0 -28
  21. package/es/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.js +131 -0
  22. package/es/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.less +65 -0
  23. package/es/components/pisellFloorMapLayout/components/FloorMapEditPanel.js +5 -4
  24. package/es/components/pisellFloorMapLayout/components/FloorMapEditPanel.less +12 -1
  25. package/es/components/pisellFloorMapLayout/components/FloorMapEditQuickActions.js +8 -1
  26. package/es/components/pisellFloorMapLayout/components/FloorMapOverview.js +16 -8
  27. package/es/components/pisellFloorMapLayout/components/FloorMapOverview.less +69 -17
  28. package/es/components/pisellFloorMapLayout/components/FloorMapToolbar.js +290 -147
  29. package/es/components/pisellFloorMapLayout/components/FloorMapToolbar.less +210 -11
  30. package/es/components/pisellFloorMapLayout/components/FloorMapToolbarViewAllModal.js +81 -0
  31. package/es/components/pisellFloorMapLayout/components/FloorMapZoneElement.d.ts +8 -0
  32. package/es/components/pisellFloorMapLayout/components/FloorMapZoneElement.js +21 -0
  33. package/es/components/pisellFloorMapLayout/components/FloorMapZoneElement.less +74 -0
  34. package/es/components/pisellFloorMapLayout/components/ItemLayer.less +3 -1
  35. package/es/components/pisellFloorMapLayout/components/NodePortMarkers.js +70 -0
  36. package/es/components/pisellFloorMapLayout/components/NodePortMarkers.less +33 -0
  37. package/es/components/pisellFloorMapLayout/components/ViewControls.js +13 -5
  38. package/es/components/pisellFloorMapLayout/components/ViewControlsWithZoom.js +2 -0
  39. package/es/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +2 -1
  40. package/es/components/pisellFloorMapLayout/floorMapLayoutConstants.js +1 -1
  41. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEdgeEditing.js +340 -0
  42. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEdgeViewModel.js +102 -0
  43. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEditState.js +244 -84
  44. package/es/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.js +54 -0
  45. package/es/components/pisellFloorMapLayout/hooks/useShopFloorPlanSubscription.js +7 -4
  46. package/es/components/pisellFloorMapLayout/index.d.ts +5 -1
  47. package/es/components/pisellFloorMapLayout/locales-ja.js +14 -2
  48. package/es/components/pisellFloorMapLayout/locales-pt.js +14 -2
  49. package/es/components/pisellFloorMapLayout/locales.js +161 -8
  50. package/es/components/pisellFloorMapLayout/types.d.ts +86 -4
  51. package/es/components/pisellFloorMapLayout/types.js +5 -1
  52. package/es/components/pisellFloorMapLayout/utils/alignSnap.js +170 -0
  53. package/es/components/pisellFloorMapLayout/utils/batchScenePlacementLayout.js +86 -0
  54. package/es/components/pisellFloorMapLayout/utils/edgeRouting.js +942 -0
  55. package/es/components/pisellFloorMapLayout/utils/floorMapCanvasDisplay.d.ts +7 -0
  56. package/es/components/pisellFloorMapLayout/utils/floorMapCanvasStage.js +23 -1
  57. package/es/components/pisellFloorMapLayout/utils/floorMapConfigMerge.js +11 -2
  58. package/es/components/pisellFloorMapLayout/utils/floorMapElementKindUtils.js +20 -3
  59. package/es/components/pisellFloorMapLayout/utils/floorMapFallbackRender.js +11 -5
  60. package/es/components/pisellFloorMapLayout/utils/floorMapOverviewLayout.js +5 -3
  61. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.d.ts +20 -0
  62. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.js +62 -0
  63. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.d.ts +83 -0
  64. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.js +29 -0
  65. package/es/components/pisellFloorMapLayout/utils/floorMapToolbarPalette.js +51 -0
  66. package/es/components/productCard/cartSkuCard/components/BookingItem/index.js +14 -0
  67. package/es/components/productCard/cartSkuCard/components/BookingItem/index.less +32 -0
  68. package/es/components/productCard/cartSkuCard/components/resources/index.less +4 -0
  69. package/es/components/productCard/cartSkuCard/components/timeRange/index.js +1 -2
  70. package/es/components/productCard/cartSkuCard/index.js +17 -11
  71. package/es/components/productCard/lineItem/BookingLineItem.js +17 -6
  72. package/es/components/productCard/lineItem/components/Holders/index.js +51 -0
  73. package/es/components/productCard/lineItem/components/Holders/index.less +39 -0
  74. package/es/components/productCard/lineItem/components/Resources/index.js +45 -0
  75. package/es/components/productCard/lineItem/components/TimeRange/index.js +64 -0
  76. package/es/components/productCard/lineItem/index.js +5 -2
  77. package/es/components/productCard/lineItem/index.less +21 -10
  78. package/es/components/productCard/types.d.ts +1 -0
  79. package/es/index.d.ts +6 -2
  80. package/es/index.js +6 -2
  81. package/es/pisell-materials.tw.css +3 -0
  82. package/lib/components/buttonGroupPreview/index.js +8 -4
  83. package/lib/components/pisellFloorMapLayout/PisellFloorMapLayout.js +206 -48
  84. package/lib/components/pisellFloorMapLayout/PisellFloorMapLayout.less +33 -2
  85. package/lib/components/pisellFloorMapLayout/appearance/floorMapAppearance.js +50 -2
  86. package/lib/components/pisellFloorMapLayout/components/EdgeLayer.js +255 -0
  87. package/lib/components/pisellFloorMapLayout/components/EdgeLayer.less +11 -0
  88. package/lib/components/pisellFloorMapLayout/components/EditableItemLayer.js +179 -29
  89. package/lib/components/pisellFloorMapLayout/components/EditableItemLayer.less +0 -28
  90. package/lib/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.js +134 -0
  91. package/lib/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.less +65 -0
  92. package/lib/components/pisellFloorMapLayout/components/FloorMapEditPanel.js +4 -3
  93. package/lib/components/pisellFloorMapLayout/components/FloorMapEditPanel.less +12 -1
  94. package/lib/components/pisellFloorMapLayout/components/FloorMapEditQuickActions.js +8 -1
  95. package/lib/components/pisellFloorMapLayout/components/FloorMapOverview.js +16 -8
  96. package/lib/components/pisellFloorMapLayout/components/FloorMapOverview.less +69 -17
  97. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbar.js +287 -144
  98. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbar.less +210 -11
  99. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbarViewAllModal.js +84 -0
  100. package/lib/components/pisellFloorMapLayout/components/FloorMapZoneElement.d.ts +8 -0
  101. package/lib/components/pisellFloorMapLayout/components/FloorMapZoneElement.js +23 -0
  102. package/lib/components/pisellFloorMapLayout/components/FloorMapZoneElement.less +74 -0
  103. package/lib/components/pisellFloorMapLayout/components/ItemLayer.less +3 -1
  104. package/lib/components/pisellFloorMapLayout/components/NodePortMarkers.js +73 -0
  105. package/lib/components/pisellFloorMapLayout/components/NodePortMarkers.less +33 -0
  106. package/lib/components/pisellFloorMapLayout/components/ViewControls.js +12 -4
  107. package/lib/components/pisellFloorMapLayout/components/ViewControlsWithZoom.js +2 -0
  108. package/lib/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +2 -1
  109. package/lib/components/pisellFloorMapLayout/floorMapLayoutConstants.js +1 -1
  110. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEdgeEditing.js +341 -0
  111. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEdgeViewModel.js +103 -0
  112. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEditState.js +242 -82
  113. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.js +54 -0
  114. package/lib/components/pisellFloorMapLayout/hooks/useShopFloorPlanSubscription.js +7 -4
  115. package/lib/components/pisellFloorMapLayout/index.d.ts +5 -1
  116. package/lib/components/pisellFloorMapLayout/locales-ja.js +14 -2
  117. package/lib/components/pisellFloorMapLayout/locales-pt.js +14 -2
  118. package/lib/components/pisellFloorMapLayout/locales.js +161 -8
  119. package/lib/components/pisellFloorMapLayout/types.d.ts +86 -4
  120. package/lib/components/pisellFloorMapLayout/types.js +5 -0
  121. package/lib/components/pisellFloorMapLayout/utils/alignSnap.js +170 -0
  122. package/lib/components/pisellFloorMapLayout/utils/batchScenePlacementLayout.js +87 -0
  123. package/lib/components/pisellFloorMapLayout/utils/edgeRouting.js +956 -0
  124. package/lib/components/pisellFloorMapLayout/utils/floorMapCanvasDisplay.d.ts +7 -0
  125. package/lib/components/pisellFloorMapLayout/utils/floorMapCanvasStage.js +23 -0
  126. package/lib/components/pisellFloorMapLayout/utils/floorMapConfigMerge.js +11 -2
  127. package/lib/components/pisellFloorMapLayout/utils/floorMapElementKindUtils.js +21 -2
  128. package/lib/components/pisellFloorMapLayout/utils/floorMapFallbackRender.js +11 -5
  129. package/lib/components/pisellFloorMapLayout/utils/floorMapOverviewLayout.js +5 -3
  130. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.d.ts +20 -0
  131. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.js +64 -0
  132. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.d.ts +83 -0
  133. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.js +29 -0
  134. package/lib/components/pisellFloorMapLayout/utils/floorMapToolbarPalette.js +54 -0
  135. package/lib/components/productCard/cartSkuCard/components/BookingItem/index.js +17 -0
  136. package/lib/components/productCard/cartSkuCard/components/BookingItem/index.less +32 -0
  137. package/lib/components/productCard/cartSkuCard/components/resources/index.less +4 -0
  138. package/lib/components/productCard/cartSkuCard/components/timeRange/index.js +1 -2
  139. package/lib/components/productCard/cartSkuCard/index.js +17 -11
  140. package/lib/components/productCard/lineItem/BookingLineItem.js +17 -6
  141. package/lib/components/productCard/lineItem/components/Holders/index.js +53 -0
  142. package/lib/components/productCard/lineItem/components/Holders/index.less +39 -0
  143. package/lib/components/productCard/lineItem/components/Resources/index.js +47 -0
  144. package/lib/components/productCard/lineItem/components/TimeRange/index.js +67 -0
  145. package/lib/components/productCard/lineItem/index.js +5 -2
  146. package/lib/components/productCard/lineItem/index.less +21 -10
  147. package/lib/components/productCard/types.d.ts +1 -0
  148. package/lib/index.d.ts +6 -2
  149. package/lib/index.js +11 -0
  150. package/lib/pisell-materials.tw.css +3 -0
  151. package/package.json +6 -3
@@ -1,15 +1,18 @@
1
1
  import { _objectSpread2 } from "../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
2
2
  import { _objectWithoutProperties } from "../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectWithoutProperties.js";
3
- import { FLOOR_MAP_STAGE_ELEMENT_KIND } from "../types.js";
3
+ import { FLOOR_MAP_STAGE_ELEMENT_KIND, FLOOR_MAP_ZONE_ELEMENT_KIND } from "../types.js";
4
4
  import { STAGE_DECO_DEFAULT } from "../utils/floorMapStageDefaults.js";
5
5
  import { FLOOR_MAP_BUILTIN_SHAPES, getFloorMapBuiltinShapeDef } from "../utils/floorMapBuiltinShapes.js";
6
- import { getElementKindConfig, isFloorMapBuiltinShapeElementKind, isFloorMapImageElementKind, resolveDataBindingPolicy } from "../utils/floorMapElementKindUtils.js";
6
+ import { FLOOR_MAP_RESOURCE_PLACE_DEFAULT, getElementKindConfig, isFloorMapBuiltinShapeElementKind, isFloorMapImageElementKind, resolveDataBindingPolicy } from "../utils/floorMapElementKindUtils.js";
7
7
  import useEngineContext from "../../../hooks/useEngineContext.js";
8
8
  import { createTranslationText } from "../../translation/utils.js";
9
9
  import { initFloorMapLayoutLocales } from "../utils/floorMapCanvasDisplay.js";
10
+ import { gapToPx } from "../utils/layoutMeasurements.js";
10
11
  import { mergeCanvasItems } from "../utils/floorMapConfigMerge.js";
11
12
  import { useDraftConfigHistory } from "./useDraftConfigHistory.js";
12
13
  import { floorMapViewConfigsEqual, normalizeFloorMapViewConfigFromProp } from "../utils/floorMapViewConfigDiff.js";
14
+ import { computeFloorMapContentStage } from "../utils/floorMapCanvasStage.js";
15
+ import { computeBatchScenePlacementPositions, getPlacementBoundingBox } from "../utils/batchScenePlacementLayout.js";
13
16
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
14
17
  import { locales } from "@pisell/utils";
15
18
  import { message } from "antd";
@@ -34,6 +37,11 @@ const BUILTIN_SHAPE_PLACE_DEFAULT = {
34
37
  height: 96
35
38
  };
36
39
  const I18N_PLACEMENT_FALLBACK_IMAGE = "pisell-floor-map-layout.placement.fallback-image-name";
40
+ const I18N_PLACEMENT_ZONE_NAME = "pisell-floor-map-layout.zone.unnamed";
41
+ const ZONE_PLACE_DEFAULT = {
42
+ width: 240,
43
+ height: 160
44
+ };
37
45
  const GRID_VIS_STORAGE_PREFIX = "pisell-floor-map-layout.grid-visible";
38
46
  function getGridVisStorageKey(canvasId) {
39
47
  return `${GRID_VIS_STORAGE_PREFIX}:${canvasId}`;
@@ -54,8 +62,18 @@ function writeStoredGridVisible(canvasId, visible) {
54
62
  localStorage.setItem(getGridVisStorageKey(canvasId), visible ? "1" : "0");
55
63
  } catch (_unused2) {}
56
64
  }
57
- /** 批量落位时每条相对上一条的偏移(内容坐标) */
58
- const BATCH_PLACE_OFFSET = 16;
65
+ /** props 同步 / dirty 基线一致:合并 localStorage 中的网格显隐偏好 */
66
+ function normalizeFloorMapConfigWithStoredGrid(floorMapConfigProp) {
67
+ const baseConfig = normalizeFloorMapViewConfigFromProp(floorMapConfigProp);
68
+ return _objectSpread2(_objectSpread2({}, baseConfig), {}, { canvases: baseConfig.canvases.map((canvas) => {
69
+ var _canvas$mapLayer;
70
+ const stored = readStoredGridVisible(canvas.id);
71
+ if (stored == null) return canvas;
72
+ return _objectSpread2(_objectSpread2({}, canvas), {}, { mapLayer: _objectSpread2(_objectSpread2({}, (_canvas$mapLayer = canvas.mapLayer) !== null && _canvas$mapLayer !== void 0 ? _canvas$mapLayer : {}), {}, { showGrid: stored }) });
73
+ }) });
74
+ }
75
+ /** 复制单个图元时的错位偏移(内容坐标) */
76
+ const DUPLICATE_PLACE_OFFSET = 16;
59
77
  function newInstanceId() {
60
78
  return `elm_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
61
79
  }
@@ -65,9 +83,61 @@ function duplicateBinding(elements, binding, exceptInstanceId) {
65
83
  return e.instanceId !== exceptInstanceId && ((_e$dataBinding = e.dataBinding) === null || _e$dataBinding === void 0 ? void 0 : _e$dataBinding.dataSourceKey) === binding.dataSourceKey && ((_e$dataBinding2 = e.dataBinding) === null || _e$dataBinding2 === void 0 ? void 0 : _e$dataBinding2.recordId) === binding.recordId;
66
84
  });
67
85
  }
86
+ /** 解析落点图元的默认像素尺寸(与 applyScenePlacementAtCoords 一致) */
87
+ function resolveScenePlacementDimensions(config, scene) {
88
+ var _scene$builtinShapeKe, _FLOOR_MAP_BUILTIN_SH, _builtinDef$defaultSi, _builtinDef$defaultSi2;
89
+ const defaultPlacementWidth = FLOOR_MAP_RESOURCE_PLACE_DEFAULT.width;
90
+ const defaultPlacementHeight = FLOOR_MAP_RESOURCE_PLACE_DEFAULT.height;
91
+ const defaultDecoSize = 48;
92
+ const kindConfig = getElementKindConfig(config, scene.elementKind);
93
+ const policy = resolveDataBindingPolicy(kindConfig);
94
+ const isStage = scene.elementKind === FLOOR_MAP_STAGE_ELEMENT_KIND;
95
+ const isZone = scene.elementKind === FLOOR_MAP_ZONE_ELEMENT_KIND;
96
+ const isImage = isFloorMapImageElementKind(scene.elementKind);
97
+ const isBuiltinShape = isFloorMapBuiltinShapeElementKind(scene.elementKind);
98
+ const initialBuiltinShapeKey = isBuiltinShape ? (_scene$builtinShapeKe = scene.builtinShapeKey) !== null && _scene$builtinShapeKe !== void 0 ? _scene$builtinShapeKe : (_FLOOR_MAP_BUILTIN_SH = FLOOR_MAP_BUILTIN_SHAPES[0]) === null || _FLOOR_MAP_BUILTIN_SH === void 0 ? void 0 : _FLOOR_MAP_BUILTIN_SH.key : void 0;
99
+ const builtinDef = isBuiltinShape ? getFloorMapBuiltinShapeDef(initialBuiltinShapeKey) : void 0;
100
+ const width = isImage ? IMAGE_PLACE_DEFAULT.width : isZone ? ZONE_PLACE_DEFAULT.width : isStage ? STAGE_DECO_DEFAULT.width : isBuiltinShape ? (_builtinDef$defaultSi = builtinDef === null || builtinDef === void 0 ? void 0 : builtinDef.defaultSize.width) !== null && _builtinDef$defaultSi !== void 0 ? _builtinDef$defaultSi : BUILTIN_SHAPE_PLACE_DEFAULT.width : defaultPlacementWidth;
101
+ const height = isImage ? IMAGE_PLACE_DEFAULT.height : isZone ? ZONE_PLACE_DEFAULT.height : isStage ? STAGE_DECO_DEFAULT.height : isBuiltinShape ? (_builtinDef$defaultSi2 = builtinDef === null || builtinDef === void 0 ? void 0 : builtinDef.defaultSize.height) !== null && _builtinDef$defaultSi2 !== void 0 ? _builtinDef$defaultSi2 : BUILTIN_SHAPE_PLACE_DEFAULT.height : defaultPlacementHeight;
102
+ const useDecoSize = policy === "none" && !isStage && !isZone && !isImage && !isBuiltinShape;
103
+ return {
104
+ width: useDecoSize ? defaultDecoSize : width,
105
+ height: useDecoSize ? defaultDecoSize : height,
106
+ useDecoSize,
107
+ isStage,
108
+ isZone,
109
+ isImage,
110
+ isBuiltinShape,
111
+ initialBuiltinShapeKey,
112
+ kindConfig
113
+ };
114
+ }
115
+ function buildPlacedSceneElement(params, dims) {
116
+ const { canvasId, scene, x, y, name, dataBinding } = params;
117
+ const { width, height, isStage, isZone, isImage, isBuiltinShape, initialBuiltinShapeKey, kindConfig } = dims;
118
+ return _objectSpread2(_objectSpread2(_objectSpread2({
119
+ instanceId: newInstanceId(),
120
+ canvasId,
121
+ x: Math.round(x),
122
+ y: Math.round(y),
123
+ name,
124
+ zIndex: isZone ? -100 : isImage || isBuiltinShape ? -5 : 0,
125
+ width: Math.round(width),
126
+ height: Math.round(height),
127
+ elementKind: scene.elementKind,
128
+ shape: isStage || isZone || isImage || isBuiltinShape ? "rect" : kindConfig === null || kindConfig === void 0 ? void 0 : kindConfig.defaultShape,
129
+ dataBinding
130
+ }, isZone ? { isZone: true } : {}), isImage ? { imageUrl: "" } : {}), isBuiltinShape ? { builtinShapeKey: initialBuiltinShapeKey } : {});
131
+ }
68
132
  function useFloorMapEditState(params) {
69
133
  var _engineCtx$engine, _selectedItem$_sceneE;
70
- const { floorMapConfigProp, isFloorMapView, itemsProp, cellSizeProp, dataSources, onSave, onDataSourceRecordSave, dataSourceFormColumns, dataSourceRecordForm, dataSourceLabels, layoutPrefix, contentOffsetRef, showDataSourceRecordFormBlock } = params;
134
+ const { floorMapConfigProp, isFloorMapView, itemsProp, cellSizeProp, dataSources, onSave, onDataSourceRecordSave, dataSourceFormColumns, dataSourceRecordForm, dataSourceLabels, layoutPrefix, contentOffsetRef, showDataSourceRecordFormBlock, focusViewportOnBoundsRef } = params;
135
+ const scheduleFocusOnPlacements = useCallback((positions, sizes) => {
136
+ var _focusViewportOnBound;
137
+ const box = getPlacementBoundingBox(positions, sizes);
138
+ if (!box) return;
139
+ focusViewportOnBoundsRef === null || focusViewportOnBoundsRef === void 0 || (_focusViewportOnBound = focusViewportOnBoundsRef.current) === null || _focusViewportOnBound === void 0 || _focusViewportOnBound.call(focusViewportOnBoundsRef, box);
140
+ }, [focusViewportOnBoundsRef]);
71
141
  const engineCtx = useEngineContext();
72
142
  const engineLocale = (engineCtx === null || engineCtx === void 0 || (_engineCtx$engine = engineCtx.engine) === null || _engineCtx$engine === void 0 || (_engineCtx$engine = _engineCtx$engine.props) === null || _engineCtx$engine === void 0 ? void 0 : _engineCtx$engine.locale) || "zh-CN";
73
143
  /**
@@ -96,21 +166,32 @@ function useFloorMapEditState(params) {
96
166
  const config = isFloorMapView ? draftConfig !== null && draftConfig !== void 0 ? draftConfig : floorMapConfigProp : null;
97
167
  const configRef = useRef(config);
98
168
  configRef.current = config;
169
+ /** 上一次已从 props 写入草稿的配置;用于区分「接口刚下发」与「用户本地改过」 */
170
+ const lastSyncedPropConfigRef = useRef(null);
99
171
  useEffect(() => {
100
- if (!floorMapConfigProp) return;
101
- const baseConfig = normalizeFloorMapViewConfigFromProp(floorMapConfigProp);
102
- const nextConfig = _objectSpread2(_objectSpread2({}, baseConfig), {}, { canvases: baseConfig.canvases.map((canvas) => {
103
- var _canvas$mapLayer;
104
- const stored = readStoredGridVisible(canvas.id);
105
- if (stored == null) return canvas;
106
- return _objectSpread2(_objectSpread2({}, canvas), {}, { mapLayer: _objectSpread2(_objectSpread2({}, (_canvas$mapLayer = canvas.mapLayer) !== null && _canvas$mapLayer !== void 0 ? _canvas$mapLayer : {}), {}, { showGrid: stored }) });
107
- }) });
172
+ if (!floorMapConfigProp) {
173
+ lastSyncedPropConfigRef.current = null;
174
+ return;
175
+ }
176
+ const nextConfig = normalizeFloorMapConfigWithStoredGrid(floorMapConfigProp);
177
+ const lastSynced = lastSyncedPropConfigRef.current;
178
+ /**
179
+ * 仅当「自上次 props 同步后用户又改过草稿」时拒绝覆盖:
180
+ * - 接口/订阅首次带回 layout:草稿仍等于上次 props → 必须 replace,否则会空白;
181
+ * - 编辑中落了新图元:草稿已与 lastSynced 不同 → 拒绝旧 props 覆盖。
182
+ */
183
+ if (draftConfig && lastSynced != null) {
184
+ const propMatchesDraft = floorMapViewConfigsEqual(draftConfig, nextConfig);
185
+ const draftMatchesLastSynced = floorMapViewConfigsEqual(draftConfig, lastSynced);
186
+ if (!propMatchesDraft && !draftMatchesLastSynced) return;
187
+ }
108
188
  /**
109
189
  * 上游配置变更:直接 replace(不进入历史栈),并清空 undo/redo;
110
190
  * 同时清空本地临时保存基线,让 dirty/未保存提示与上游对齐。
111
191
  */
112
192
  replaceDraft(nextConfig);
113
- setSavedBaselineConfig(null);
193
+ lastSyncedPropConfigRef.current = nextConfig;
194
+ setSavedBaselineConfig((prev) => prev != null && floorMapViewConfigsEqual(prev, nextConfig) ? prev : null);
114
195
  const enabled = nextConfig.canvases.filter((c) => c.enabled).sort((a, b) => a.order - b.order);
115
196
  setCurrentCanvasId((prev) => {
116
197
  var _enabled$0$id, _enabled$;
@@ -120,7 +201,7 @@ function useFloorMapEditState(params) {
120
201
  /** 当前草稿与上游持久化 `floorMapConfig`(经同等规范化)是否不一致 */
121
202
  const isLayoutDirty = useMemo(() => {
122
203
  if (!isFloorMapView || !draftConfig || !floorMapConfigProp) return false;
123
- return !floorMapViewConfigsEqual(draftConfig, savedBaselineConfig !== null && savedBaselineConfig !== void 0 ? savedBaselineConfig : normalizeFloorMapViewConfigFromProp(floorMapConfigProp));
204
+ return !floorMapViewConfigsEqual(draftConfig, savedBaselineConfig !== null && savedBaselineConfig !== void 0 ? savedBaselineConfig : normalizeFloorMapConfigWithStoredGrid(floorMapConfigProp));
124
205
  }, [
125
206
  isFloorMapView,
126
207
  draftConfig,
@@ -215,6 +296,40 @@ function useFloorMapEditState(params) {
215
296
  cellSizeProp
216
297
  ]);
217
298
  const items = isFloorMapView ? mergedItems : itemsProp;
299
+ const getBatchPlacementLayoutContext = useCallback(() => {
300
+ var _config$sceneElements, _config$mapLayer, _canvas$mapLayer2;
301
+ const existingElements = ((_config$sceneElements = config === null || config === void 0 ? void 0 : config.sceneElements) !== null && _config$sceneElements !== void 0 ? _config$sceneElements : []).filter((e) => e.canvasId === currentCanvasId).map((e) => {
302
+ var _e$x, _e$y, _e$width, _e$height;
303
+ return {
304
+ x: Math.round((_e$x = e.x) !== null && _e$x !== void 0 ? _e$x : 0),
305
+ y: Math.round((_e$y = e.y) !== null && _e$y !== void 0 ? _e$y : 0),
306
+ width: Math.round((_e$width = e.width) !== null && _e$width !== void 0 ? _e$width : FLOOR_MAP_RESOURCE_PLACE_DEFAULT.width),
307
+ height: Math.round((_e$height = e.height) !== null && _e$height !== void 0 ? _e$height : FLOOR_MAP_RESOURCE_PLACE_DEFAULT.height)
308
+ };
309
+ });
310
+ const canvas = config === null || config === void 0 ? void 0 : config.canvases.find((c) => c.id === currentCanvasId);
311
+ const mapLayer = _objectSpread2(_objectSpread2({}, (_config$mapLayer = config === null || config === void 0 ? void 0 : config.mapLayer) !== null && _config$mapLayer !== void 0 ? _config$mapLayer : {}), (_canvas$mapLayer2 = canvas === null || canvas === void 0 ? void 0 : canvas.mapLayer) !== null && _canvas$mapLayer2 !== void 0 ? _canvas$mapLayer2 : {});
312
+ const padSingle = typeof mapLayer.boundingPadding === "number" ? mapLayer.boundingPadding : 56;
313
+ const stage = computeFloorMapContentStage({
314
+ items: mergedItems,
315
+ cellSize: cellSizeProp !== null && cellSizeProp !== void 0 ? cellSizeProp : 64,
316
+ itemGap: gapToPx(mapLayer.gap),
317
+ itemUnit: "pixel",
318
+ boundingPadding: padSingle,
319
+ preferPixelBoundingBox: true
320
+ });
321
+ return {
322
+ existingElements,
323
+ contentWidth: stage.contentWidth,
324
+ contentHeight: stage.contentHeight,
325
+ boundingPadding: padSingle
326
+ };
327
+ }, [
328
+ config,
329
+ currentCanvasId,
330
+ mergedItems,
331
+ cellSizeProp
332
+ ]);
218
333
  const updateDraftSceneElement = useCallback((instanceId, patch) => {
219
334
  commitDraft((prev) => {
220
335
  var _prev$sceneElements2, _prev$sceneElements3;
@@ -243,43 +358,15 @@ function useFloorMapEditState(params) {
243
358
  * @returns 是否已成功写入草稿(校验失败时为 false)
244
359
  */
245
360
  const applyScenePlacementAtCoords = useCallback((contentX, contentY, scene) => {
246
- var _kindConfig$allowedDa, _ref, _scene$dataSourceKey, _scene$builtinShapeKe, _FLOOR_MAP_BUILTIN_SH, _builtinDef$defaultSi, _builtinDef$defaultSi2;
361
+ var _kindConfig$allowedDa, _ref, _scene$dataSourceKey;
247
362
  if (!config || !currentCanvasId) return false;
248
- const defaultPlacementWidth = 180;
249
- const defaultPlacementHeight = 120;
250
- const defaultDecoSize = 48;
251
- const kindConfig = getElementKindConfig(config, scene.elementKind);
363
+ const dims = resolveScenePlacementDimensions(config, scene);
364
+ const kindConfig = dims.kindConfig;
252
365
  const policy = resolveDataBindingPolicy(kindConfig);
253
366
  const allKeys = Object.keys(dataSources !== null && dataSources !== void 0 ? dataSources : {});
254
367
  const allowedKeys = (kindConfig === null || kindConfig === void 0 || (_kindConfig$allowedDa = kindConfig.allowedDataSourceKeys) === null || _kindConfig$allowedDa === void 0 ? void 0 : _kindConfig$allowedDa.length) ? kindConfig.allowedDataSourceKeys.filter((k) => allKeys.includes(k)) : allKeys;
255
368
  const pickKey = (_ref = (_scene$dataSourceKey = scene.dataSourceKey) !== null && _scene$dataSourceKey !== void 0 ? _scene$dataSourceKey : allowedKeys[0]) !== null && _ref !== void 0 ? _ref : allKeys[0];
256
369
  const recordIdsToPlace = scene.recordIds && scene.recordIds.length > 0 ? scene.recordIds : scene.recordId ? [scene.recordId] : [];
257
- const isStage = scene.elementKind === FLOOR_MAP_STAGE_ELEMENT_KIND;
258
- const isImage = isFloorMapImageElementKind(scene.elementKind);
259
- const isBuiltinShape = isFloorMapBuiltinShapeElementKind(scene.elementKind);
260
- /**
261
- * 内置图形 kind 的初始 `builtinShapeKey`:优先用侧栏 Select 预选的 key
262
- * (`scene.builtinShapeKey`,由 toolbar 写入 placeMode / drag payload 透传),
263
- * 未预选时回退为枚举首项,避免空白占位。
264
- */
265
- const initialBuiltinShapeKey = isBuiltinShape ? (_scene$builtinShapeKe = scene.builtinShapeKey) !== null && _scene$builtinShapeKe !== void 0 ? _scene$builtinShapeKe : (_FLOOR_MAP_BUILTIN_SH = FLOOR_MAP_BUILTIN_SHAPES[0]) === null || _FLOOR_MAP_BUILTIN_SH === void 0 ? void 0 : _FLOOR_MAP_BUILTIN_SH.key : void 0;
266
- const builtinDef = isBuiltinShape ? getFloorMapBuiltinShapeDef(initialBuiltinShapeKey) : void 0;
267
- const w = isImage ? IMAGE_PLACE_DEFAULT.width : isStage ? STAGE_DECO_DEFAULT.width : isBuiltinShape ? (_builtinDef$defaultSi = builtinDef === null || builtinDef === void 0 ? void 0 : builtinDef.defaultSize.width) !== null && _builtinDef$defaultSi !== void 0 ? _builtinDef$defaultSi : BUILTIN_SHAPE_PLACE_DEFAULT.width : defaultPlacementWidth;
268
- const h = isImage ? IMAGE_PLACE_DEFAULT.height : isStage ? STAGE_DECO_DEFAULT.height : isBuiltinShape ? (_builtinDef$defaultSi2 = builtinDef === null || builtinDef === void 0 ? void 0 : builtinDef.defaultSize.height) !== null && _builtinDef$defaultSi2 !== void 0 ? _builtinDef$defaultSi2 : BUILTIN_SHAPE_PLACE_DEFAULT.height : defaultPlacementHeight;
269
- const useDecoSize = policy === "none" && scene.elementKind !== "stage" && !isImage && !isBuiltinShape;
270
- const buildOneElement = (dataBinding, x, y, name) => _objectSpread2(_objectSpread2({
271
- instanceId: newInstanceId(),
272
- canvasId: currentCanvasId,
273
- x,
274
- y,
275
- name,
276
- zIndex: isImage || isBuiltinShape ? -5 : 0,
277
- width: useDecoSize ? defaultDecoSize : w,
278
- height: useDecoSize ? defaultDecoSize : h,
279
- elementKind: scene.elementKind,
280
- shape: isStage || isImage || isBuiltinShape ? "rect" : kindConfig === null || kindConfig === void 0 ? void 0 : kindConfig.defaultShape,
281
- dataBinding
282
- }, isImage ? { imageUrl: "" } : {}), isBuiltinShape ? { builtinShapeKey: initialBuiltinShapeKey } : {});
283
370
  let dataBinding;
284
371
  if (policy !== "none" && pickKey) {
285
372
  var _dataSources$pickKey;
@@ -309,10 +396,9 @@ function useFloorMapEditState(params) {
309
396
  }
310
397
  dataBinding = cand;
311
398
  } else {
312
- const newEls = [];
399
+ const pending = [];
313
400
  let skippedDup = 0;
314
401
  let skippedMissing = 0;
315
- let placeIndex = 0;
316
402
  const seenInBatch = /* @__PURE__ */ new Set();
317
403
  recordIdsToPlace.forEach((rid) => {
318
404
  var _row$name;
@@ -331,46 +417,78 @@ function useFloorMapEditState(params) {
331
417
  }
332
418
  seenInBatch.add(rid);
333
419
  const row = record;
334
- const placementName = String((_row$name = row === null || row === void 0 ? void 0 : row.name) !== null && _row$name !== void 0 ? _row$name : rid);
335
- newEls.push(buildOneElement(cand, contentX + placeIndex * BATCH_PLACE_OFFSET, contentY + placeIndex * BATCH_PLACE_OFFSET, placementName));
336
- placeIndex += 1;
420
+ pending.push({
421
+ binding: cand,
422
+ name: String((_row$name = row === null || row === void 0 ? void 0 : row.name) !== null && _row$name !== void 0 ? _row$name : rid)
423
+ });
337
424
  });
338
425
  if (skippedDup > 0) message.warning(`已跳过 ${skippedDup} 条已在平面图中绑定的记录`);
339
426
  if (skippedMissing > 0) message.warning(`有 ${skippedMissing} 条记录不存在,已跳过`);
340
- if (newEls.length === 0) {
427
+ if (pending.length === 0) {
341
428
  message.warning("没有可落位的记录");
342
429
  return false;
343
430
  }
431
+ const sizes = pending.map(() => ({
432
+ width: dims.width,
433
+ height: dims.height
434
+ }));
435
+ const positions = computeBatchScenePlacementPositions(sizes, getBatchPlacementLayoutContext());
436
+ const newEls = pending.map((p, i) => buildPlacedSceneElement({
437
+ canvasId: currentCanvasId,
438
+ scene,
439
+ x: positions[i].x,
440
+ y: positions[i].y,
441
+ name: p.name,
442
+ dataBinding: p.binding
443
+ }, dims));
344
444
  commitDraft((prev) => {
345
445
  var _prev$sceneElements4;
346
446
  return prev ? _objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: [...(_prev$sceneElements4 = prev.sceneElements) !== null && _prev$sceneElements4 !== void 0 ? _prev$sceneElements4 : [], ...newEls] }) : prev;
347
447
  });
448
+ scheduleFocusOnPlacements(positions, sizes);
348
449
  return true;
349
450
  }
350
451
  }
351
- const placementName = (() => {
352
- var _kindConfig$defaultPr, _kindConfig$defaultPr2;
353
- if (isStage) return STAGE_DECO_DEFAULT.name;
354
- if (isImage) return locales.getText(I18N_PLACEMENT_FALLBACK_IMAGE);
355
- const db = dataBinding;
356
- const rid = recordIdsToPlace[0];
357
- if (db && rid) {
358
- var _dataSources$db$dataS, _row$name2;
359
- const row = dataSources === null || dataSources === void 0 || (_dataSources$db$dataS = dataSources[db.dataSourceKey]) === null || _dataSources$db$dataS === void 0 ? void 0 : _dataSources$db$dataS.find((r) => r.id === rid);
360
- return String((_row$name2 = row === null || row === void 0 ? void 0 : row.name) !== null && _row$name2 !== void 0 ? _row$name2 : rid);
361
- }
362
- return (_kindConfig$defaultPr = kindConfig === null || kindConfig === void 0 || (_kindConfig$defaultPr2 = kindConfig.defaultPresentation) === null || _kindConfig$defaultPr2 === void 0 ? void 0 : _kindConfig$defaultPr2.title) !== null && _kindConfig$defaultPr !== void 0 ? _kindConfig$defaultPr : "";
363
- })();
364
- const newEl = buildOneElement(dataBinding, contentX, contentY, placementName);
452
+ const newEl = buildPlacedSceneElement({
453
+ canvasId: currentCanvasId,
454
+ scene,
455
+ x: contentX,
456
+ y: contentY,
457
+ name: (() => {
458
+ var _kindConfig$defaultPr, _kindConfig$defaultPr2;
459
+ if (dims.isStage) return STAGE_DECO_DEFAULT.name;
460
+ if (dims.isZone) return locales.getText(I18N_PLACEMENT_ZONE_NAME);
461
+ if (dims.isImage) return locales.getText(I18N_PLACEMENT_FALLBACK_IMAGE);
462
+ const db = dataBinding;
463
+ const rid = recordIdsToPlace[0];
464
+ if (db && rid) {
465
+ var _dataSources$db$dataS, _row$name2;
466
+ const row = dataSources === null || dataSources === void 0 || (_dataSources$db$dataS = dataSources[db.dataSourceKey]) === null || _dataSources$db$dataS === void 0 ? void 0 : _dataSources$db$dataS.find((r) => r.id === rid);
467
+ return String((_row$name2 = row === null || row === void 0 ? void 0 : row.name) !== null && _row$name2 !== void 0 ? _row$name2 : rid);
468
+ }
469
+ return (_kindConfig$defaultPr = kindConfig === null || kindConfig === void 0 || (_kindConfig$defaultPr2 = kindConfig.defaultPresentation) === null || _kindConfig$defaultPr2 === void 0 ? void 0 : _kindConfig$defaultPr2.title) !== null && _kindConfig$defaultPr !== void 0 ? _kindConfig$defaultPr : "";
470
+ })(),
471
+ dataBinding
472
+ }, dims);
365
473
  commitDraft((prev) => {
366
474
  var _prev$sceneElements5;
367
475
  return prev ? _objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: [...(_prev$sceneElements5 = prev.sceneElements) !== null && _prev$sceneElements5 !== void 0 ? _prev$sceneElements5 : [], newEl] }) : prev;
368
476
  });
477
+ scheduleFocusOnPlacements([{
478
+ x: contentX,
479
+ y: contentY
480
+ }], [{
481
+ width: dims.width,
482
+ height: dims.height
483
+ }]);
369
484
  return true;
370
485
  }, [
371
486
  config,
372
487
  currentCanvasId,
373
- dataSources
488
+ dataSources,
489
+ getBatchPlacementLayoutContext,
490
+ commitDraft,
491
+ scheduleFocusOnPlacements
374
492
  ]);
375
493
  const handleCanvasPlaceClick = useCallback((e) => {
376
494
  if (!placeMode || placeMode.type !== "scene" || !config || !currentCanvasId) return;
@@ -386,8 +504,48 @@ function useFloorMapEditState(params) {
386
504
  ]);
387
505
  /** 从左侧图元栏拖拽到画布落位(与点击落点规则一致) */
388
506
  const placeSceneElementFromPaletteDrop = useCallback((contentX, contentY, scene) => {
389
- if (applyScenePlacementAtCoords(contentX, contentY, scene)) setPlaceMode(null);
507
+ const ok = applyScenePlacementAtCoords(contentX, contentY, scene);
508
+ if (ok) setPlaceMode(null);
509
+ return ok;
390
510
  }, [applyScenePlacementAtCoords]);
511
+ /**
512
+ * 批量落点(弹窗多选、侧栏多选数据行等):在画布右侧空白区按近方阵网格排列。
513
+ */
514
+ const bulkPlacePaletteItems = useCallback((items) => {
515
+ if (!items.length || !config || !currentCanvasId) return;
516
+ const scenes = items.map((item) => _objectSpread2({
517
+ type: "scene",
518
+ elementKind: item.elementKind
519
+ }, item.builtinShapeKey ? { builtinShapeKey: item.builtinShapeKey } : {}));
520
+ const dimsList = scenes.map((scene) => resolveScenePlacementDimensions(config, scene));
521
+ const sizes = dimsList.map((d) => ({
522
+ width: d.width,
523
+ height: d.height
524
+ }));
525
+ const positions = computeBatchScenePlacementPositions(sizes, getBatchPlacementLayoutContext());
526
+ const newEls = scenes.map((scene, i) => {
527
+ var _ref2, _dimsList$i$kindConfi, _dimsList$i$kindConfi2, _dimsList$i$kindConfi3;
528
+ return buildPlacedSceneElement({
529
+ canvasId: currentCanvasId,
530
+ scene,
531
+ x: positions[i].x,
532
+ y: positions[i].y,
533
+ name: (_ref2 = (_dimsList$i$kindConfi = (_dimsList$i$kindConfi2 = dimsList[i].kindConfig) === null || _dimsList$i$kindConfi2 === void 0 ? void 0 : _dimsList$i$kindConfi2.label) !== null && _dimsList$i$kindConfi !== void 0 ? _dimsList$i$kindConfi : (_dimsList$i$kindConfi3 = dimsList[i].kindConfig) === null || _dimsList$i$kindConfi3 === void 0 || (_dimsList$i$kindConfi3 = _dimsList$i$kindConfi3.defaultPresentation) === null || _dimsList$i$kindConfi3 === void 0 ? void 0 : _dimsList$i$kindConfi3.title) !== null && _ref2 !== void 0 ? _ref2 : scene.elementKind
534
+ }, dimsList[i]);
535
+ });
536
+ commitDraft((prev) => {
537
+ var _prev$sceneElements6;
538
+ return prev ? _objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: [...(_prev$sceneElements6 = prev.sceneElements) !== null && _prev$sceneElements6 !== void 0 ? _prev$sceneElements6 : [], ...newEls] }) : prev;
539
+ });
540
+ scheduleFocusOnPlacements(positions, sizes);
541
+ setPlaceMode(null);
542
+ }, [
543
+ config,
544
+ currentCanvasId,
545
+ getBatchPlacementLayoutContext,
546
+ commitDraft,
547
+ scheduleFocusOnPlacements
548
+ ]);
391
549
  const handleSave = useCallback(() => {
392
550
  if (!draftConfig || !onSave) return;
393
551
  setSaving(true);
@@ -414,16 +572,13 @@ function useFloorMapEditState(params) {
414
572
  const discardDraft = useCallback(() => {
415
573
  if (!floorMapConfigProp) {
416
574
  replaceDraft(null);
575
+ lastSyncedPropConfigRef.current = null;
417
576
  setSavedBaselineConfig(null);
418
577
  return;
419
578
  }
420
- const baseConfig = normalizeFloorMapViewConfigFromProp(floorMapConfigProp);
421
- replaceDraft(_objectSpread2(_objectSpread2({}, baseConfig), {}, { canvases: baseConfig.canvases.map((canvas) => {
422
- var _canvas$mapLayer2;
423
- const stored = readStoredGridVisible(canvas.id);
424
- if (stored == null) return canvas;
425
- return _objectSpread2(_objectSpread2({}, canvas), {}, { mapLayer: _objectSpread2(_objectSpread2({}, (_canvas$mapLayer2 = canvas.mapLayer) !== null && _canvas$mapLayer2 !== void 0 ? _canvas$mapLayer2 : {}), {}, { showGrid: stored }) });
426
- }) }));
579
+ const nextConfig = normalizeFloorMapConfigWithStoredGrid(floorMapConfigProp);
580
+ replaceDraft(nextConfig);
581
+ lastSyncedPropConfigRef.current = nextConfig;
427
582
  setSavedBaselineConfig(null);
428
583
  }, [floorMapConfigProp, replaceDraft]);
429
584
  /** 切换当前画布草稿中的 mapLayer.showGrid(随布局保存持久化) */
@@ -460,12 +615,15 @@ function useFloorMapEditState(params) {
460
615
  const handleEditPanelDelete = useCallback(() => {
461
616
  if (!selectedId) return;
462
617
  commitDraft((prev) => {
463
- var _prev$sceneElements6;
618
+ var _prev$sceneElements7, _prev$edges;
464
619
  if (!prev) return prev;
465
- return _objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: ((_prev$sceneElements6 = prev.sceneElements) !== null && _prev$sceneElements6 !== void 0 ? _prev$sceneElements6 : []).filter((e) => e.instanceId !== selectedId) });
620
+ return _objectSpread2(_objectSpread2({}, prev), {}, {
621
+ sceneElements: ((_prev$sceneElements7 = prev.sceneElements) !== null && _prev$sceneElements7 !== void 0 ? _prev$sceneElements7 : []).filter((e) => e.instanceId !== selectedId),
622
+ edges: ((_prev$edges = prev.edges) !== null && _prev$edges !== void 0 ? _prev$edges : []).filter((edge) => edge.sourceId !== selectedId && edge.targetId !== selectedId)
623
+ });
466
624
  });
467
625
  setSelectedId(null);
468
- }, [selectedId]);
626
+ }, [selectedId, commitDraft]);
469
627
  /** 与编辑侧栏锁定按钮同源:切换 `layoutTransformLocked` */
470
628
  const toggleSelectedLayoutLock = useCallback(() => {
471
629
  var _configRef$current2;
@@ -477,21 +635,21 @@ function useFloorMapEditState(params) {
477
635
  * 复制选中图元:新实例 + 位移,**清除 dataBinding**(避免与侧栏一致的「同一记录不可重复绑定」冲突)。
478
636
  */
479
637
  const duplicateSelectedElement = useCallback(() => {
480
- var _prev$sceneElements7, _el$x, _el$y, _prev$sceneElements8;
638
+ var _prev$sceneElements8, _el$x, _el$y, _prev$sceneElements9;
481
639
  if (!selectedId) return;
482
640
  const prev = configRef.current;
483
641
  if (!prev) return;
484
- const el = ((_prev$sceneElements7 = prev.sceneElements) !== null && _prev$sceneElements7 !== void 0 ? _prev$sceneElements7 : []).find((e) => e.instanceId === selectedId);
642
+ const el = ((_prev$sceneElements8 = prev.sceneElements) !== null && _prev$sceneElements8 !== void 0 ? _prev$sceneElements8 : []).find((e) => e.instanceId === selectedId);
485
643
  if (!el) return;
486
644
  const newId = newInstanceId();
487
645
  const copy = _objectSpread2(_objectSpread2({}, el), {}, {
488
646
  instanceId: newId,
489
- x: Math.round(((_el$x = el.x) !== null && _el$x !== void 0 ? _el$x : 0) + BATCH_PLACE_OFFSET),
490
- y: Math.round(((_el$y = el.y) !== null && _el$y !== void 0 ? _el$y : 0) + BATCH_PLACE_OFFSET),
647
+ x: Math.round(((_el$x = el.x) !== null && _el$x !== void 0 ? _el$x : 0) + DUPLICATE_PLACE_OFFSET),
648
+ y: Math.round(((_el$y = el.y) !== null && _el$y !== void 0 ? _el$y : 0) + DUPLICATE_PLACE_OFFSET),
491
649
  dataBinding: void 0,
492
650
  layoutTransformLocked: false
493
651
  });
494
- commitDraft(_objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: [...(_prev$sceneElements8 = prev.sceneElements) !== null && _prev$sceneElements8 !== void 0 ? _prev$sceneElements8 : [], copy] }));
652
+ commitDraft(_objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: [...(_prev$sceneElements9 = prev.sceneElements) !== null && _prev$sceneElements9 !== void 0 ? _prev$sceneElements9 : [], copy] }));
495
653
  setSelectedId(newId);
496
654
  }, [selectedId]);
497
655
  const selectedItem = selectedId ? mergedItems.find((m) => m.id === selectedId) : null;
@@ -515,6 +673,7 @@ function useFloorMapEditState(params) {
515
673
  handleSave,
516
674
  discardDraft,
517
675
  placeSceneElementFromPaletteDrop,
676
+ bulkPlacePaletteItems,
518
677
  editPanelParams: selectedItem && config && selectedId && !((_selectedItem$_sceneE = selectedItem._sceneElement) === null || _selectedItem$_sceneE === void 0 ? void 0 : _selectedItem$_sceneE.layoutTransformLocked) ? {
519
678
  itemId: selectedId,
520
679
  sceneElement: selectedItem._sceneElement,
@@ -537,7 +696,8 @@ function useFloorMapEditState(params) {
537
696
  undoDraft,
538
697
  redoDraft,
539
698
  canUndo,
540
- canRedo
699
+ canRedo,
700
+ commitDraft
541
701
  };
542
702
  }
543
703
  //#endregion
@@ -346,6 +346,59 @@ function useFloorMapTransformMetrics(params) {
346
346
  /** 让"进入伪全屏后自动 fit"的 effect 能拿到最新的 fitBounds 实现 */
347
347
  fitBoundsRef.current = fitBounds;
348
348
  /**
349
+ * 将视口平移/缩放至能看见指定内容区包围盒(场景坐标,与 sceneElement x/y 一致)。
350
+ * 批量落点后调用;视口锁定时跳过。
351
+ */
352
+ const focusContentBounds = useCallback((bounds) => {
353
+ var _ref, _cur$scale;
354
+ if (viewportLockedRef.current || !bounds) return false;
355
+ const ctx = transformRef.current;
356
+ const container = containerRef.current;
357
+ if (!ctx || !container) return false;
358
+ const wrapper = container.querySelector(".react-transform-wrapper");
359
+ if (!wrapper) return false;
360
+ const wEl = wrapper;
361
+ const wrapperRect = wrapper.getBoundingClientRect();
362
+ const layoutW = wEl.clientWidth > 0 ? wEl.clientWidth : wrapperRect.width;
363
+ const layoutH = wEl.clientHeight > 0 ? wEl.clientHeight : wrapperRect.height;
364
+ if (layoutW <= 0 || layoutH <= 0) return false;
365
+ const ox = contentOffsetRef.current.x;
366
+ const oy = contentOffsetRef.current.y;
367
+ const visMinL = bounds.minLeft + ox;
368
+ const visMinT = bounds.minTop + oy;
369
+ const visMaxR = bounds.maxRight + ox;
370
+ const visMaxB = bounds.maxBottom + oy;
371
+ const bw = Math.max(1, visMaxR - visMinL);
372
+ const bh = Math.max(1, visMaxB - visMinT);
373
+ const edgePx = 48;
374
+ const availW = Math.max(1, layoutW - 2 * edgePx);
375
+ const availH = Math.max(1, layoutH - 2 * edgePx);
376
+ const clampFitScale = (raw) => {
377
+ var _zoom$min3, _zoom$max3;
378
+ return Math.max((_zoom$min3 = zoom.min) !== null && _zoom$min3 !== void 0 ? _zoom$min3 : .5, Math.min(raw, (_zoom$max3 = zoom.max) !== null && _zoom$max3 !== void 0 ? _zoom$max3 : 2));
379
+ };
380
+ const cur = lastTransformRef.current;
381
+ const curScale = (_ref = (_cur$scale = cur === null || cur === void 0 ? void 0 : cur.scale) !== null && _cur$scale !== void 0 ? _cur$scale : zoomConfig.initialScale) !== null && _ref !== void 0 ? _ref : 1;
382
+ const rawFit = Math.min(availW / bw, availH / bh);
383
+ let nextScale = curScale;
384
+ if (bw * curScale > availW || bh * curScale > availH) nextScale = clampFitScale(rawFit);
385
+ const bboxCx = visMinL + bw / 2;
386
+ const bboxCy = visMinT + bh / 2;
387
+ const x = layoutW / 2 - bboxCx * nextScale;
388
+ const y = layoutH / 2 - bboxCy * nextScale;
389
+ clearFitBoundsTweenHold();
390
+ lastFitSnapshotRef.current = null;
391
+ setIsAtFitBounds(false);
392
+ ctx.setTransform(x, y, nextScale);
393
+ return true;
394
+ }, [
395
+ clearFitBoundsTweenHold,
396
+ contentOffsetRef,
397
+ transformRef,
398
+ zoom,
399
+ zoomConfig.initialScale
400
+ ]);
401
+ /**
349
402
  * autoFitOnMount:每个画布在「有图元且 content 尺寸已按包围盒算好」后执行一次 fitBounds。
350
403
  * 避免首帧 items 仍为空时用占位尺寸 fit 完并标记 done,导致异步图元加载后不再适配。
351
404
  */
@@ -693,6 +746,7 @@ function useFloorMapTransformMetrics(params) {
693
746
  isFullscreen,
694
747
  toggleFullscreen,
695
748
  fitBounds,
749
+ focusContentBounds,
696
750
  isAtFitBounds,
697
751
  notifyTransformed,
698
752
  persistCurrentViewport,
@@ -11,11 +11,15 @@ function useShopFloorPlanSubscription(params) {
11
11
  onDetailRef.current = onDetail;
12
12
  const onLoadErrorRef = useRef(onLoadError);
13
13
  onLoadErrorRef.current = onLoadError;
14
+ const ensureByCodeRef = useRef(ensureByCode);
15
+ ensureByCodeRef.current = ensureByCode;
16
+ const unsubscribeQueryRef = useRef(unsubscribeQuery);
17
+ unsubscribeQueryRef.current = unsubscribeQuery;
14
18
  useEffect(() => {
15
19
  if (!enabled) return;
16
20
  let cancelled = false;
17
21
  const subId = subscriberId;
18
- ensureByCode(code, {
22
+ ensureByCodeRef.current(code, {
19
23
  osServer,
20
24
  subscriberId: subId,
21
25
  callback: (res) => {
@@ -35,14 +39,13 @@ function useShopFloorPlanSubscription(params) {
35
39
  });
36
40
  return () => {
37
41
  cancelled = true;
38
- unsubscribeQuery(subId);
42
+ unsubscribeQueryRef.current(subId);
39
43
  };
44
+ /** ensureByCode / unsubscribeQuery 走 ref,避免调用方内联函数导致反复退订再订阅 */
40
45
  }, [
41
46
  enabled,
42
47
  code,
43
48
  subscriberId,
44
- ensureByCode,
45
- unsubscribeQuery,
46
49
  osServer
47
50
  ]);
48
51
  }
@@ -1,4 +1,5 @@
1
- import { DataSourceFormColumn, FLOOR_MAP_BUILTIN_SHAPE_ELEMENT_KIND, FLOOR_MAP_IMAGE_ELEMENT_KIND, FLOOR_MAP_STAGE_ELEMENT_KIND, FloorMapBackgroundType, FloorMapCanvas, FloorMapCanvasNameI18n, FloorMapCanvasUiConfig, FloorMapControlsConfig, FloorMapDataSourceRecordFormContent, FloorMapDataSources, FloorMapEdge, FloorMapElementDataBindingPolicy, FloorMapElementDefaultPresentation, FloorMapElementKindCategory, FloorMapElementKindConfig, FloorMapFullscreenMode, FloorMapGridLayoutConfig, FloorMapItemBase, FloorMapItemShape, FloorMapItemUnit, FloorMapLayerConfig, FloorMapLayoutContextValue, FloorMapLayoutMode, FloorMapMergedItem, FloorMapPanBoundary, FloorMapPanConfig, FloorMapRecordFormRenderContext, FloorMapRenderOptions, FloorMapSceneElement, FloorMapSceneElementDataBinding, FloorMapTextSlot, FloorMapViewConfig, FloorMapViewportOverlayRenderArgs, FloorMapZoomConfig, PisellFloorMapLayoutProps, PisellFloorMapLayoutRef } from "./types.js";
1
+ import { FloorMapResourcePickerSlotProps, PisellCardPickerCardMeta, PisellCardPickerCategoryConfig, PisellCardPickerCategoryLevelConfig, PisellCardPickerCategoryMode, PisellCardPickerCategoryPath, PisellCardPickerItem, PisellCardPickerMode, parseNestedPathSegments } from "./utils/floorMapResourcePickerTypes.js";
2
+ import { DataSourceFormColumn, FLOOR_MAP_BUILTIN_SHAPE_ELEMENT_KIND, FLOOR_MAP_IMAGE_ELEMENT_KIND, FLOOR_MAP_STAGE_ELEMENT_KIND, FLOOR_MAP_ZONE_ELEMENT_KIND, FloorMapBackgroundType, FloorMapCanvas, FloorMapCanvasNameI18n, FloorMapCanvasUiConfig, FloorMapControlsConfig, FloorMapDataSourceRecordFormContent, FloorMapDataSources, FloorMapEdge, FloorMapEdgeAnchor, FloorMapEdgeStatus, FloorMapElementDataBindingPolicy, FloorMapElementDefaultPresentation, FloorMapElementKindCategory, FloorMapElementKindConfig, FloorMapFullscreenMode, FloorMapGridLayoutConfig, FloorMapItemBase, FloorMapItemShape, FloorMapItemUnit, FloorMapLayerConfig, FloorMapLayoutContextValue, FloorMapLayoutMode, FloorMapMergedItem, FloorMapPanBoundary, FloorMapPanConfig, FloorMapRecordFormRenderContext, FloorMapRenderOptions, FloorMapSceneElement, FloorMapSceneElementDataBinding, FloorMapTextSlot, FloorMapViewConfig, FloorMapViewportOverlayRenderArgs, FloorMapZoomConfig, PisellFloorMapLayoutProps, PisellFloorMapLayoutRef } from "./types.js";
2
3
  import { PisellFloorMapLayout } from "./PisellFloorMapLayout.js";
3
4
  import { renderFloorMapFallbackPlaceholder } from "./utils/floorMapFallbackRender.js";
4
5
  import { isFloorMapBuiltinShapeElementKind, isFloorMapImageElementKind } from "./utils/floorMapElementKindUtils.js";
@@ -13,6 +14,9 @@ import { sceneElementsToShopLayout, shopLayoutToSceneElements } from "./shopFloo
13
14
  import { WrapFloorMapOnSaveWithRemotePersistParams, wrapFloorMapOnSaveWithRemotePersist } from "./utils/floorMapRemotePersist.js";
14
15
  import { EMPTY_FLOOR_MAP_LAYOUT_CONTEXT, FloorMapLayoutProvider, FloorMapLayoutProviderProps, useFloorMapLayoutContext } from "./context/FloorMapLayoutContext.js";
15
16
  import { mergeFloorMapLayoutPropsFromContext } from "./context/mergeFloorMapLayoutContext.js";
17
+ import { FloorMapResourcePickerRecord, floorMapResourcePickerRecordSearchText, getFloorMapCardPickerCategoryConfig, getFloorMapResourcePickerCardMeta } from "./utils/floorMapResourcePickerDisplay.js";
18
+ import { initFloorMapLayoutLocales } from "./utils/floorMapCanvasDisplay.js";
16
19
  import { FloorMapImageElement } from "./components/FloorMapImageElement.js";
17
20
  import { FloorMapBuiltinShapeElement } from "./components/FloorMapBuiltinShapeElement.js";
21
+ import { FloorMapZoneElement } from "./components/FloorMapZoneElement.js";
18
22
  import { FigmaTableCardItem, FigmaTableStatus, getFigmaTableCardFromMerged, getRenderItemByKindRoundTable, getRenderItemByKindTable, renderFigmaStyleRoundTableCard, renderFigmaStyleTableCard } from "./components/FigmaTableCard.js";