@@ -50,11 +51,11 @@
import { computed, inject, onUnmounted, ref } from 'vue';
import { TMagicButton, TMagicDialog, tMagicMessage, tMagicMessageBox, TMagicTag } from '@tmagic/design';
-import { ColumnConfig, FormConfig, FormState, MFormDrawer } from '@tmagic/form';
+import { ColumnConfig, FormConfig, FormState, MFormBox, MFormDrawer } from '@tmagic/form';
import type { CodeBlockContent } from '@tmagic/schema';
import CodeEditor from '@editor/layouts/CodeEditor.vue';
-import type { Services } from '@editor/type';
+import type { Services, SlideType } from '@editor/type';
import { getConfig } from '@editor/utils/config';
defineOptions({
@@ -66,6 +67,7 @@ const props = defineProps<{
disabled?: boolean;
isDataSource?: boolean;
dataSourceType?: string;
+ slideType?: SlideType;
}>();
const emit = defineEmits<{
diff --git a/packages/editor/src/hooks/index.ts b/packages/editor/src/hooks/index.ts
index 5eab9d68..7c1396f4 100644
--- a/packages/editor/src/hooks/index.ts
+++ b/packages/editor/src/hooks/index.ts
@@ -19,3 +19,4 @@
export * from './use-code-block-edit';
export * from './use-data-source-method';
export * from './use-stage';
+export * from './use-float-box';
diff --git a/packages/editor/src/hooks/use-code-block-edit.ts b/packages/editor/src/hooks/use-code-block-edit.ts
index 64fa7eb2..a3d0feeb 100644
--- a/packages/editor/src/hooks/use-code-block-edit.ts
+++ b/packages/editor/src/hooks/use-code-block-edit.ts
@@ -68,6 +68,8 @@ export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
await codeBlockService?.setCodeDslById(codeId.value, values);
+ tMagicMessage.success('代码块保存成功');
+
codeBlockEditor.value?.hide();
};
diff --git a/packages/editor/src/hooks/use-float-box.ts b/packages/editor/src/hooks/use-float-box.ts
new file mode 100644
index 00000000..b6cfad17
--- /dev/null
+++ b/packages/editor/src/hooks/use-float-box.ts
@@ -0,0 +1,153 @@
+import { computed, ComputedRef, inject, nextTick, ref, watch } from 'vue';
+import Moveable from 'moveable';
+
+import { type Services } from '@editor/type';
+
+export const useFloatBox = (slideKeys: ComputedRef) => {
+ const services = inject('services');
+ const moveable = ref();
+ const floatBox = ref();
+
+ const floatBoxStates = computed(() => services?.uiService.get('floatBox'));
+
+ const curKey = ref('');
+ const target = computed(() =>
+ floatBox.value
+ ? floatBox.value.find((item) => item.classList.contains(`m-editor-float-box-${curKey.value}`))
+ : undefined,
+ );
+
+ const showingBoxKeys = computed(() =>
+ [...(floatBoxStates.value?.keys() ?? [])].filter((key) => floatBoxStates.value?.get(key)?.status),
+ );
+
+ const isDraging = ref(false);
+
+ const showFloatBox = async (key: string) => {
+ const curBoxStatus = floatBoxStates.value?.get(curKey.value)?.status;
+ if (curKey.value === key && curBoxStatus) return;
+ curKey.value = key;
+ setSlideState(key, {
+ zIndex: getMaxZIndex() + 1,
+ status: true,
+ });
+
+ await nextTick();
+ if (moveable.value) {
+ moveable.value.target = target.value;
+ moveable.value.dragTarget = getDragTarget();
+ moveable.value.updateRect();
+ } else {
+ initFloatBoxMoveable();
+ }
+ };
+
+ const setSlideState = (key: string, data: Record) => {
+ const slideState = floatBoxStates.value?.get(key);
+ if (!slideState) return;
+ floatBoxStates.value?.set(key, {
+ ...slideState,
+ ...data,
+ });
+ };
+
+ const getDragTarget = (key?: string) => `.m-editor-float-box-header-${key ?? curKey.value}`;
+
+ const closeFloatBox = (key: string) => {
+ setSlideState(key, {
+ status: false,
+ });
+
+ // 如果只有一个,关掉后需要销毁moveable实例
+ if (!floatBoxStates.value?.values) return;
+ const keys = [...floatBoxStates.value?.keys()];
+ const values = [...floatBoxStates.value?.values()];
+ const lastFloatBoxLen = values.filter((state) => state.status).length;
+ if (lastFloatBoxLen === 0) {
+ moveable.value?.destroy();
+ moveable.value = undefined;
+ return;
+ }
+
+ // 如果关掉的 box 是最大的,需要选中下面的一层
+ if (key === curKey.value) {
+ // 查找显示的最大 zIndex 对应的 index
+ const zIndexList = values.filter((item) => item.status).map((item) => item.zIndex);
+ const maxZIndex = Math.max(...zIndexList);
+ const key = keys.find((key) => floatBoxStates.value?.get(key)?.zIndex === maxZIndex);
+ if (!key) return;
+ showFloatBox(key);
+ }
+ };
+
+ const getMaxZIndex = () => {
+ if (!floatBoxStates.value?.values()) return 0;
+ const list = [...floatBoxStates.value?.values()].map((state) => state.zIndex);
+ return Math.max(...list) ?? 0;
+ };
+
+ const initFloatBoxMoveable = () => {
+ const dragTarget = getDragTarget();
+ moveable.value = new Moveable(document.body, {
+ target: target.value,
+ draggable: true,
+ resizable: true,
+ edge: true,
+ keepRatio: false,
+ origin: false,
+ snappable: true,
+ dragTarget,
+ dragTargetSelf: false,
+ linePadding: 10,
+ controlPadding: 10,
+ elementGuidelines: [...(floatBoxStates.value?.keys() ?? [])].map((key) => getDragTarget(key)),
+ bounds: { left: 0, top: 0, right: 0, bottom: 0, position: 'css' },
+ });
+ moveable.value.on('drag', (e) => {
+ e.target.style.transform = e.transform;
+ });
+ moveable.value.on('resize', (e) => {
+ e.target.style.width = `${e.width}px`;
+ e.target.style.height = `${e.height}px`;
+ e.target.style.transform = e.drag.transform;
+ });
+ };
+
+ const dragendHandler = (key: string, e: DragEvent) => {
+ setSlideState(key, {
+ left: e.clientX,
+ top: e.clientY,
+ });
+ showFloatBox(key);
+ isDraging.value = false;
+ };
+
+ document.body.addEventListener('dragover', (e: DragEvent) => {
+ if (!isDraging.value) return;
+ e.preventDefault();
+ });
+
+ const dragstartHandler = () => (isDraging.value = true);
+
+ // 监听 slide 长度变化,更新 ui serice map
+ watch(
+ () => slideKeys.value,
+ () => {
+ services?.uiService.setFloatBox(slideKeys.value);
+ },
+ {
+ deep: true,
+ immediate: true,
+ },
+ );
+
+ return {
+ showFloatBox,
+ closeFloatBox,
+ dragstartHandler,
+ dragendHandler,
+ floatBoxStates,
+ floatBox,
+ showingBoxKeys,
+ };
+};
diff --git a/packages/editor/src/layouts/Framework.vue b/packages/editor/src/layouts/Framework.vue
index 19eb9190..5cd90b49 100644
--- a/packages/editor/src/layouts/Framework.vue
+++ b/packages/editor/src/layouts/Framework.vue
@@ -82,8 +82,10 @@ const showSrc = computed(() => uiService?.get('showSrc'));
const LEFT_COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorLeftColumnWidthData';
const RIGHT_COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorRightColumnWidthData';
-const leftColumnWidthCacheData =
+const getLeftColumnWidthCacheData = () =>
Number(globalThis.localStorage.getItem(LEFT_COLUMN_WIDTH_STORAGE_KEY)) || DEFAULT_LEFT_COLUMN_WIDTH;
+
+const leftColumnWidthCacheData = getLeftColumnWidthCacheData();
const RightColumnWidthCacheData =
Number(globalThis.localStorage.getItem(RIGHT_COLUMN_WIDTH_STORAGE_KEY)) || DEFAULT_RIGHT_COLUMN_WIDTH;
@@ -118,6 +120,13 @@ watch(
},
);
+watch(
+ () => uiService?.get('hideSlideBar'),
+ (hideSlideBar) => {
+ columnWidth.value.left = hideSlideBar ? 0 : getLeftColumnWidthCacheData();
+ },
+);
+
const columnWidthChange = (columnW: GetColumnWidth) => {
columnWidth.value.left = columnW.left;
columnWidth.value.center = columnW.center;
diff --git a/packages/editor/src/layouts/sidebar/Sidebar.vue b/packages/editor/src/layouts/sidebar/Sidebar.vue
index db79ee25..8039995c 100644
--- a/packages/editor/src/layouts/sidebar/Sidebar.vue
+++ b/packages/editor/src/layouts/sidebar/Sidebar.vue
@@ -1,5 +1,5 @@
-