mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-09-20 05:29:58 +08:00
feat(editor): 使用 floatbox 替换原抽屉栏
This commit is contained in:
parent
260286f9cf
commit
a035f02f83
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<component
|
||||
:is="slideType === 'box' ? MFormBox : MFormDrawer"
|
||||
<MFormBox
|
||||
class="m-editor-code-block-editor"
|
||||
ref="fomDrawer"
|
||||
label-width="80px"
|
||||
@ -20,7 +19,7 @@
|
||||
<template #left>
|
||||
<TMagicButton type="primary" link @click="difVisible = true">查看修改</TMagicButton>
|
||||
</template>
|
||||
</component>
|
||||
</MFormBox>
|
||||
|
||||
<TMagicDialog v-model="difVisible" title="查看修改" fullscreen>
|
||||
<div style="display: flex; margin-bottom: 10px">
|
||||
@ -55,7 +54,7 @@ import { ColumnConfig, FormConfig, FormState, MFormBox, MFormDrawer } from '@tma
|
||||
import type { CodeBlockContent } from '@tmagic/schema';
|
||||
|
||||
import CodeEditor from '@editor/layouts/CodeEditor.vue';
|
||||
import type { Services, SlideType } from '@editor/type';
|
||||
import type { Services } from '@editor/type';
|
||||
import { getConfig } from '@editor/utils/config';
|
||||
|
||||
defineOptions({
|
||||
@ -67,7 +66,6 @@ const props = defineProps<{
|
||||
disabled?: boolean;
|
||||
isDataSource?: boolean;
|
||||
dataSourceType?: string;
|
||||
slideType?: SlideType;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<Teleport to="body" v-if="visible">
|
||||
<div ref="target" class="m-editor-float-box" :style="style">
|
||||
<div ref="target" class="m-editor-float-box" :style="style" @mousedown="nextZIndex">
|
||||
<div ref="dragTarget" class="m-editor-float-box-title">
|
||||
<slot name="title">
|
||||
<span>{{ title }}</span>
|
||||
@ -21,7 +21,7 @@ import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue';
|
||||
import { Close } from '@element-plus/icons-vue';
|
||||
import VanillaMoveable from 'moveable';
|
||||
|
||||
import { TMagicButton } from '@tmagic/design';
|
||||
import { TMagicButton, useZIndex } from '@tmagic/design';
|
||||
|
||||
interface Position {
|
||||
left: number;
|
||||
@ -47,11 +47,15 @@ const emit = defineEmits<{
|
||||
const target = ref<HTMLDivElement>();
|
||||
const dragTarget = ref<HTMLDivElement>();
|
||||
|
||||
const zIndex = useZIndex();
|
||||
const curZIndex = ref<number>(zIndex.nextZIndex());
|
||||
|
||||
const style = computed(() => ({
|
||||
left: `${props.position.left}px`,
|
||||
top: `${props.position.top}px`,
|
||||
width: typeof props.rect.width === 'string' ? props.rect.width : `${props.rect.width}px`,
|
||||
height: typeof props.rect.height === 'string' ? props.rect.height : `${props.rect.height}px`,
|
||||
zIndex: curZIndex.value,
|
||||
}));
|
||||
|
||||
let moveable: VanillaMoveable | null = null;
|
||||
@ -70,6 +74,7 @@ const initMoveable = () => {
|
||||
dragTargetSelf: false,
|
||||
linePadding: 10,
|
||||
controlPadding: 10,
|
||||
bounds: { left: 0, top: 0, right: 0, bottom: 0, position: 'css' },
|
||||
});
|
||||
|
||||
moveable.on('drag', (e) => {
|
||||
@ -111,6 +116,10 @@ const closeHandler = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const nextZIndex = () => {
|
||||
curZIndex.value = zIndex.nextZIndex();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
target,
|
||||
});
|
||||
|
@ -54,7 +54,6 @@ export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
|
||||
codeId.value = id;
|
||||
|
||||
await nextTick();
|
||||
|
||||
codeBlockEditor.value?.show();
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="m-editor-nav-menu" :style="{ height: `${height}px` }">
|
||||
<div class="m-editor-nav-menu" :style="{ height: `${height}px` }" ref="navMenu">
|
||||
<div v-for="key in keys" :class="`menu-${key}`" :key="key" :style="`width: ${columnWidth?.[key]}px`">
|
||||
<ToolButton :data="item" v-for="(item, index) in buttons[key]" :key="index"></ToolButton>
|
||||
</div>
|
||||
@ -7,7 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, markRaw } from 'vue';
|
||||
import { computed, inject, markRaw, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { Back, Delete, FullScreen, Grid, Memo, Right, ScaleToOriginal, ZoomIn, ZoomOut } from '@element-plus/icons-vue';
|
||||
|
||||
import { NodeType } from '@tmagic/schema';
|
||||
@ -178,4 +178,23 @@ const buttons = computed(() => {
|
||||
});
|
||||
return data;
|
||||
});
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
const rect = navMenu.value?.getBoundingClientRect();
|
||||
if (rect) {
|
||||
uiService?.set('navMenuRect', {
|
||||
left: rect.left,
|
||||
top: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
});
|
||||
}
|
||||
});
|
||||
const navMenu = ref<HTMLDivElement>();
|
||||
onMounted(() => {
|
||||
navMenu.value && resizeObserver.observe(navMenu.value);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
resizeObserver.disconnect();
|
||||
});
|
||||
</script>
|
||||
|
@ -204,6 +204,11 @@ const getItemConfig = (data: SideItem): SideComponent => {
|
||||
text: '代码编辑',
|
||||
component: CodeBlockListPanel,
|
||||
slots: {},
|
||||
boxComponentConfig: {
|
||||
props: {
|
||||
slideType: 'box',
|
||||
},
|
||||
},
|
||||
},
|
||||
'data-source': {
|
||||
$key: 'data-source',
|
||||
|
@ -3,7 +3,7 @@
|
||||
<slot name="code-block-panel-header">
|
||||
<div class="search-wrapper">
|
||||
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
||||
<TMagicButton v-if="editable" class="create-code-button" type="primary" size="small" @click="createCodeBlock"
|
||||
<TMagicButton v-if="editable" class="create-code-button" type="primary" size="small" @click="showCreate"
|
||||
>新增</TMagicButton
|
||||
>
|
||||
<slot name="code-block-panel-search"></slot>
|
||||
@ -11,7 +11,7 @@
|
||||
</slot>
|
||||
|
||||
<!-- 代码块列表 -->
|
||||
<CodeBlockList ref="codeBlockList" :custom-error="customError" @edit="editCode" @remove="deleteCode">
|
||||
<CodeBlockList ref="codeBlockList" :custom-error="customError" @edit="showEdit" @remove="deleteCode">
|
||||
<template #code-block-panel-tool="{ id, data }">
|
||||
<slot name="code-block-panel-tool" :id="id" :data="data"></slot>
|
||||
</template>
|
||||
@ -19,23 +19,31 @@
|
||||
</TMagicScrollbar>
|
||||
|
||||
<!-- 代码块编辑区 -->
|
||||
<FloatingBox v-model:visible="popVisible" title="代码编辑" :position="boxPosition">
|
||||
<template #body>
|
||||
<div ref="scrollBar"></div>
|
||||
</template>
|
||||
</FloatingBox>
|
||||
|
||||
<Teleport :to="scrollBar" :disabled="slideType === 'box'" v-if="editVisible">
|
||||
<CodeBlockEditor
|
||||
v-if="codeConfig"
|
||||
ref="codeBlockEditor"
|
||||
:disabled="!editable"
|
||||
:content="codeConfig"
|
||||
:slideType="slideType"
|
||||
@submit="submitCodeBlockHandler"
|
||||
></CodeBlockEditor>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, ref } from 'vue';
|
||||
import { computed, inject, nextTick, ref } from 'vue';
|
||||
|
||||
import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
|
||||
import type { Id } from '@tmagic/schema';
|
||||
|
||||
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
||||
import FloatingBox from '@editor/components/FloatingBox.vue';
|
||||
import SearchInput from '@editor/components/SearchInput.vue';
|
||||
import { useCodeBlockEdit } from '@editor/hooks/use-code-block-edit';
|
||||
import type { CodeBlockListPanelSlots, CodeDeleteErrorType, Services, SlideType } from '@editor/type';
|
||||
@ -48,12 +56,12 @@ defineOptions({
|
||||
name: 'MEditorCodeBlockListPanel',
|
||||
});
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
|
||||
slideType?: SlideType;
|
||||
}>();
|
||||
|
||||
const { codeBlockService } = inject<Services>('services') || {};
|
||||
const { codeBlockService, uiService } = inject<Services>('services') || {};
|
||||
|
||||
const editable = computed(() => codeBlockService?.getEditStatus());
|
||||
|
||||
@ -65,4 +73,35 @@ const codeBlockList = ref<InstanceType<typeof CodeBlockList>>();
|
||||
const filterTextChangeHandler = (val: string) => {
|
||||
codeBlockList.value?.filter(val);
|
||||
};
|
||||
|
||||
const boxPosition = computed(() => {
|
||||
const columnWidth = uiService?.get('columnWidth');
|
||||
const navMenuRect = uiService?.get('navMenuRect');
|
||||
return {
|
||||
left: columnWidth?.left ?? 0,
|
||||
top: (navMenuRect?.top ?? 0) + (navMenuRect?.height ?? 0),
|
||||
};
|
||||
});
|
||||
|
||||
const scrollBar = ref<HTMLDivElement>();
|
||||
const popVisible = ref<boolean>(false);
|
||||
const editVisible = ref<boolean>(false);
|
||||
|
||||
const beforeShowEdit = async () => {
|
||||
if (props.slideType !== 'box') {
|
||||
popVisible.value = true;
|
||||
}
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
const showEdit = async (id: string) => {
|
||||
await beforeShowEdit();
|
||||
editCode(id);
|
||||
};
|
||||
|
||||
const showCreate = async () => {
|
||||
await beforeShowEdit();
|
||||
createCodeBlock();
|
||||
};
|
||||
</script>
|
||||
|
@ -47,8 +47,13 @@ const state = reactive<UiState>({
|
||||
showRule: true,
|
||||
propsPanelSize: 'small',
|
||||
showAddPageButton: true,
|
||||
floatBox: new Map(),
|
||||
hideSlideBar: false,
|
||||
navMenuRect: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const canUsePluginMethods = {
|
||||
@ -110,19 +115,6 @@ class Ui extends BaseService {
|
||||
return Math.min((width - 60) / stageWidth || 1, (height - 80) / stageHeight || 1);
|
||||
}
|
||||
|
||||
public async setFloatBox(keys: string[]) {
|
||||
const map = state.floatBox;
|
||||
for (const key of keys) {
|
||||
if (map.get(key)) continue;
|
||||
map.set(key, {
|
||||
status: false,
|
||||
zIndex: 99,
|
||||
top: 0,
|
||||
left: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public resetState() {
|
||||
this.set('showSrc', false);
|
||||
this.set('uiSelectMode', false);
|
||||
|
@ -1,3 +1,9 @@
|
||||
.m-container-vs-code {
|
||||
.el-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.magic-code-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -72,12 +72,15 @@
|
||||
}
|
||||
|
||||
.m-editor-slide-list-box {
|
||||
min-width: 270px;
|
||||
display: flex;
|
||||
min-width: 240px;
|
||||
min-height: 500px;
|
||||
max-height: 1024px;
|
||||
> div {
|
||||
&:first-child {
|
||||
width: 100%;
|
||||
min-width: 240px;
|
||||
}
|
||||
}
|
||||
.m-form-box {
|
||||
border-left: 1px solid #e0e0e0;
|
||||
}
|
||||
}
|
||||
|
@ -236,20 +236,16 @@ export interface UiState {
|
||||
propsPanelSize: 'large' | 'default' | 'small';
|
||||
/** 是否显示新增页面按钮 */
|
||||
showAddPageButton: boolean;
|
||||
|
||||
/** slide 拖拽悬浮窗 state */
|
||||
floatBox: Map<
|
||||
string,
|
||||
{
|
||||
status: boolean;
|
||||
zIndex: number;
|
||||
top: number;
|
||||
left: number;
|
||||
}
|
||||
>;
|
||||
|
||||
/** 是否隐藏侧边栏 */
|
||||
hideSlideBar: boolean;
|
||||
|
||||
// navMenu 的宽高
|
||||
navMenuRect: {
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface EditorNodeInfo {
|
||||
|
@ -2,7 +2,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
max-width: 90%;
|
||||
.el-box__header {
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import React, { constructor, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import type { MComponent, MContainer, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
||||
@ -16,8 +15,8 @@ const PageFragmentContainer: React.FC<PageFragmentContainerProps> = ({ config })
|
||||
|
||||
if (!app) return null;
|
||||
const MagicUiContainer = app.resolveComponent('container');
|
||||
let containerConfig = {}
|
||||
const fragment = app?.dsl?.items?.find((page) => page.id === config.pageFragmentId)
|
||||
let containerConfig = {};
|
||||
const fragment = app?.dsl?.items?.find((page) => page.id === config.pageFragmentId);
|
||||
if (fragment) {
|
||||
const { id, type, items, ...others } = fragment;
|
||||
const itemsWithoutId = items.map((item: MNode) => {
|
||||
@ -32,8 +31,8 @@ const PageFragmentContainer: React.FC<PageFragmentContainerProps> = ({ config })
|
||||
} else {
|
||||
containerConfig = {
|
||||
...others,
|
||||
items
|
||||
}
|
||||
items,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,9 +42,7 @@ const PageFragmentContainer: React.FC<PageFragmentContainerProps> = ({ config })
|
||||
className="magic-ui-page-fragment-container"
|
||||
style={app.transformStyle(config.style || {})}
|
||||
>
|
||||
<MagicUiContainer
|
||||
config={containerConfig}
|
||||
></MagicUiContainer>
|
||||
<MagicUiContainer config={containerConfig}></MagicUiContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import type { MComponent, MContainer, MPageFragment } from '@tmagic/schema';
|
||||
|
Loading…
x
Reference in New Issue
Block a user