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