diff --git a/docs/api/editor/editorServiceMethods.md b/docs/api/editor/editorServiceMethods.md
index 924dcf3d..96380af7 100644
--- a/docs/api/editor/editorServiceMethods.md
+++ b/docs/api/editor/editorServiceMethods.md
@@ -3,7 +3,7 @@
## get
- **参数:**
- - `{'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength' | 'stage' | 'stageLoading' | 'disabledMultiSelect'} name`
+ - `{'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength' | 'stage' | 'stageLoading' | 'disabledMultiSelect' | 'alwaysMultiSelect'} name`
- **返回:**
- `{any} value`
@@ -36,6 +36,8 @@
'disabledMultiSelect': 是否禁用多选
+ 'alwaysMultiSelect': 是否始终启用多选模式(无需按住 Ctrl/Meta)
+
- **示例:**
```js
@@ -46,7 +48,7 @@ const node = editorService.get("node");
## set
-- `{'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength' | 'stage' | 'stageLoading' | 'disabledMultiSelect'} name`
+- `{'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength' | 'stage' | 'stageLoading' | 'disabledMultiSelect' | 'alwaysMultiSelect'} name`
- `{any} value`
- **详情:**
diff --git a/docs/api/editor/props.md b/docs/api/editor/props.md
index 7e6331a4..955b0f94 100644
--- a/docs/api/editor/props.md
+++ b/docs/api/editor/props.md
@@ -1043,7 +1043,26 @@ const updateDragEl = (el, target) => {
```
-
+
+## alwaysMultiSelect
+
+- **详情:**
+
+ 始终启用多选模式:开启后无需按住 `Ctrl/Meta` 键,组件树和画布上点击即多选。
+ 当 [`disabledMultiSelect`](#disabledmultiselect) 为 `true` 时本配置失效。
+
+- **类型:** `boolean`
+
+- **默认值:** `false`
+
+- **示例:**
+
+```html
+
+
+
+```
+
## guidesOptions
- **详情:**
diff --git a/docs/api/stage/coreMethods.md b/docs/api/stage/coreMethods.md
index 83469f9e..e2884fc2 100644
--- a/docs/api/stage/coreMethods.md
+++ b/docs/api/stage/coreMethods.md
@@ -113,6 +113,13 @@
- **类型**:`() => void`
- **详情**:启用多选能力
+## setAlwaysMultiSelect
+
+- **类型**:`(value: boolean) => void`
+- **参数**:
+ - `value`:是否始终启用多选模式(无需按住 `Ctrl/Meta` 键)
+- **详情**:设置是否始终启用多选模式。当多选被 `disableMultiSelect` 禁用时,本方法不会启用多选
+
## reloadIframe
- **类型**:`(url: string) => void`
diff --git a/packages/editor/src/Editor.vue b/packages/editor/src/Editor.vue
index e37ff2bf..2252e0ac 100644
--- a/packages/editor/src/Editor.vue
+++ b/packages/editor/src/Editor.vue
@@ -219,6 +219,7 @@ const stageOptions: StageOptions = {
renderType: props.renderType,
guidesOptions: props.guidesOptions,
disabledMultiSelect: props.disabledMultiSelect,
+ alwaysMultiSelect: props.alwaysMultiSelect,
beforeDblclick: props.beforeDblclick,
};
diff --git a/packages/editor/src/editorProps.ts b/packages/editor/src/editorProps.ts
index 17a4b0b7..8feec1d0 100644
--- a/packages/editor/src/editorProps.ts
+++ b/packages/editor/src/editorProps.ts
@@ -77,6 +77,11 @@ export interface EditorProps {
guidesOptions?: Partial;
/** 禁止多选 */
disabledMultiSelect?: boolean;
+ /**
+ * 始终启用多选模式:开启后无需按住 Ctrl/Meta,点击即多选;
+ * 默认 false。当 `disabledMultiSelect` 为 true 时本配置失效
+ */
+ alwaysMultiSelect?: boolean;
/** 禁用页面片 */
disabledPageFragment?: boolean;
/** 禁用双击在浮层中单独编辑选中组件 */
@@ -127,6 +132,7 @@ export interface EditorProps {
export const defaultEditorProps = {
renderType: RenderType.IFRAME,
disabledMultiSelect: false,
+ alwaysMultiSelect: false,
disabledPageFragment: false,
disabledStageOverlay: false,
containerHighlightClassName: CONTAINER_HIGHLIGHT_CLASS_NAME,
diff --git a/packages/editor/src/hooks/use-stage.ts b/packages/editor/src/hooks/use-stage.ts
index 97366be9..0fa0d2bb 100644
--- a/packages/editor/src/hooks/use-stage.ts
+++ b/packages/editor/src/hooks/use-stage.ts
@@ -46,6 +46,7 @@ export const useStage = (stageOptions: StageOptions) => {
updateDragEl: stageOptions.updateDragEl,
guidesOptions: stageOptions.guidesOptions,
disabledMultiSelect: stageOptions.disabledMultiSelect,
+ alwaysMultiSelect: stageOptions.alwaysMultiSelect,
disabledRule: stageOptions.disabledRule,
});
@@ -60,6 +61,13 @@ export const useStage = (stageOptions: StageOptions) => {
},
);
+ watch(
+ () => editorService.get('alwaysMultiSelect'),
+ (alwaysMultiSelect) => {
+ stage.setAlwaysMultiSelect(Boolean(alwaysMultiSelect));
+ },
+ );
+
const hGuidesCache = getGuideLineFromCache(getGuideLineKey(H_GUIDE_LINE_STORAGE_KEY));
const vGuidesCache = getGuideLineFromCache(getGuideLineKey(V_GUIDE_LINE_STORAGE_KEY));
diff --git a/packages/editor/src/initService.ts b/packages/editor/src/initService.ts
index e3aaebd3..97059a6a 100644
--- a/packages/editor/src/initService.ts
+++ b/packages/editor/src/initService.ts
@@ -72,6 +72,16 @@ export const initServiceState = (
},
);
+ watch(
+ () => props.alwaysMultiSelect,
+ (alwaysMultiSelect) => {
+ editorService.set('alwaysMultiSelect', alwaysMultiSelect || false);
+ },
+ {
+ immediate: true,
+ },
+ );
+
watch(
() => props.componentGroupList,
(componentGroupList) => componentGroupList && componentListService.setList(componentGroupList),
diff --git a/packages/editor/src/layouts/sidebar/layer/use-click.ts b/packages/editor/src/layouts/sidebar/layer/use-click.ts
index cc6a3fd4..515352b0 100644
--- a/packages/editor/src/layouts/sidebar/layer/use-click.ts
+++ b/packages/editor/src/layouts/sidebar/layer/use-click.ts
@@ -2,7 +2,7 @@ import { computed, type ComputedRef, nextTick, type Ref, type ShallowRef } from
import { throttle } from 'lodash-es';
import { Id, MNode } from '@tmagic/core';
-import { isPage, isPageFragment } from '@tmagic/utils';
+import { getElById, isPage, isPageFragment } from '@tmagic/utils';
import type { LayerNodeStatus, Services, TreeNodeData } from '@editor/type';
import { UI_SELECT_MODE_EVENT_NAME } from '@editor/utils/const';
@@ -16,7 +16,9 @@ export const useClick = (
nodeStatusMap: ComputedRef