mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
feat(editor): 数据源抽屉切floatbox
This commit is contained in:
parent
e2326ff4f6
commit
96149bd2ae
@ -9,59 +9,57 @@
|
|||||||
:before-close="beforeClose"
|
:before-close="beforeClose"
|
||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div ref="floatingBoxBody" style="height: 100%"></div>
|
<MFormBox
|
||||||
|
class="m-editor-code-block-editor"
|
||||||
|
ref="formBox"
|
||||||
|
label-width="80px"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
:title="content.name"
|
||||||
|
:config="functionConfig"
|
||||||
|
:values="content"
|
||||||
|
:disabled="disabled"
|
||||||
|
style="height: 100%"
|
||||||
|
@change="changeHandler"
|
||||||
|
@submit="submitForm"
|
||||||
|
@error="errorHandler"
|
||||||
|
@closed="closedHandler"
|
||||||
|
>
|
||||||
|
<template #left>
|
||||||
|
<TMagicButton v-if="!disabled" type="primary" link @click="difVisible = true">查看修改</TMagicButton>
|
||||||
|
</template>
|
||||||
|
</MFormBox>
|
||||||
</template>
|
</template>
|
||||||
</FloatingBox>
|
</FloatingBox>
|
||||||
|
|
||||||
<Teleport :to="floatingBoxBody" :disabled="slideType === 'box'" v-if="editVisible">
|
<Teleport to="body">
|
||||||
<MFormBox
|
<TMagicDialog title="查看修改" v-model="difVisible" fullscreen>
|
||||||
class="m-editor-code-block-editor"
|
<div style="display: flex; margin-bottom: 10px">
|
||||||
ref="formBox"
|
<div style="flex: 1"><TMagicTag size="small" type="info">修改前</TMagicTag></div>
|
||||||
label-width="80px"
|
<div style="flex: 1"><TMagicTag size="small" type="success">修改后</TMagicTag></div>
|
||||||
:close-on-press-escape="false"
|
</div>
|
||||||
:title="content.name"
|
|
||||||
:config="functionConfig"
|
<CodeEditor
|
||||||
:values="content"
|
v-if="difVisible"
|
||||||
:disabled="disabled"
|
ref="magicVsEditor"
|
||||||
:height="floatingBoxBody?.clientHeight"
|
type="diff"
|
||||||
@change="changeHandler"
|
language="json"
|
||||||
@submit="submitForm"
|
:initValues="content.content"
|
||||||
@error="errorHandler"
|
:modifiedValues="formBox?.form?.values.content"
|
||||||
@closed="closedHandler"
|
:style="`height: ${windowRect.height - 150}px`"
|
||||||
>
|
></CodeEditor>
|
||||||
<template #left>
|
|
||||||
<TMagicButton v-if="!disabled" type="primary" link @click="difVisible = true">查看修改</TMagicButton>
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<TMagicButton size="small" @click="difVisible = false">取消</TMagicButton>
|
||||||
|
<TMagicButton size="small" type="primary" @click="diffChange">确定</TMagicButton>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</MFormBox>
|
</TMagicDialog>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
|
||||||
<TMagicDialog v-model="difVisible" title="查看修改" fullscreen>
|
|
||||||
<div style="display: flex; margin-bottom: 10px">
|
|
||||||
<div style="flex: 1"><TMagicTag size="small" type="info">修改前</TMagicTag></div>
|
|
||||||
<div style="flex: 1"><TMagicTag size="small" type="success">修改后</TMagicTag></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CodeEditor
|
|
||||||
v-if="difVisible"
|
|
||||||
ref="magicVsEditor"
|
|
||||||
type="diff"
|
|
||||||
language="json"
|
|
||||||
:initValues="content.content"
|
|
||||||
:modifiedValues="formBox?.form?.values.content"
|
|
||||||
:style="`height: ${windowRect.height - 150}px`"
|
|
||||||
></CodeEditor>
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<span class="dialog-footer">
|
|
||||||
<TMagicButton size="small" @click="difVisible = false">取消</TMagicButton>
|
|
||||||
<TMagicButton size="small" type="primary" @click="diffChange">确定</TMagicButton>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</TMagicDialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject, nextTick, ref } from 'vue';
|
import { computed, inject, Ref, ref } from 'vue';
|
||||||
|
|
||||||
import { TMagicButton, TMagicDialog, tMagicMessage, tMagicMessageBox, TMagicTag } from '@tmagic/design';
|
import { TMagicButton, TMagicDialog, tMagicMessage, tMagicMessageBox, TMagicTag } from '@tmagic/design';
|
||||||
import { ColumnConfig, FormConfig, FormState, MFormBox } from '@tmagic/form';
|
import { ColumnConfig, FormConfig, FormState, MFormBox } from '@tmagic/form';
|
||||||
@ -69,9 +67,10 @@ import type { CodeBlockContent } from '@tmagic/schema';
|
|||||||
|
|
||||||
import FloatingBox from '@editor/components/FloatingBox.vue';
|
import FloatingBox from '@editor/components/FloatingBox.vue';
|
||||||
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
|
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
|
||||||
|
import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
|
||||||
import { useWindowRect } from '@editor/hooks/use-window-rect';
|
import { useWindowRect } from '@editor/hooks/use-window-rect';
|
||||||
import CodeEditor from '@editor/layouts/CodeEditor.vue';
|
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';
|
import { getConfig } from '@editor/utils/config';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -86,7 +85,6 @@ const props = defineProps<{
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
isDataSource?: boolean;
|
isDataSource?: boolean;
|
||||||
dataSourceType?: string;
|
dataSourceType?: string;
|
||||||
slideType?: SlideType;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@ -229,7 +227,6 @@ const changeHandler = (values: CodeBlockContent) => {
|
|||||||
|
|
||||||
const beforeClose = (done: (cancel?: boolean) => void) => {
|
const beforeClose = (done: (cancel?: boolean) => void) => {
|
||||||
if (!changedValue.value) {
|
if (!changedValue.value) {
|
||||||
editVisible.value = false;
|
|
||||||
done();
|
done();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -242,12 +239,10 @@ const beforeClose = (done: (cancel?: boolean) => void) => {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
changedValue.value && submitForm(changedValue.value);
|
changedValue.value && submitForm(changedValue.value);
|
||||||
editVisible.value = false;
|
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch((action: string) => {
|
.catch((action: string) => {
|
||||||
if (action === 'cancel') {
|
if (action === 'cancel') {
|
||||||
editVisible.value = false;
|
|
||||||
}
|
}
|
||||||
done(action === 'cancel');
|
done(action === 'cancel');
|
||||||
});
|
});
|
||||||
@ -257,35 +252,17 @@ const closedHandler = () => {
|
|||||||
changedValue.value = undefined;
|
changedValue.value = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const editVisible = ref<boolean>(false);
|
const parentFloating = inject<Ref<HTMLDivElement>>('parentFloating');
|
||||||
const floatingBoxBody = ref<HTMLDivElement>();
|
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating);
|
||||||
|
|
||||||
const boxPosition = computed(() => {
|
|
||||||
const columnWidth = services?.uiService?.get('columnWidth');
|
|
||||||
const navMenuRect = services?.uiService?.get('navMenuRect');
|
|
||||||
return {
|
|
||||||
left: columnWidth?.left ?? 0,
|
|
||||||
top: (navMenuRect?.top ?? 0) + (navMenuRect?.height ?? 0),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
async show() {
|
async show() {
|
||||||
if (props.slideType !== 'box') {
|
calcBoxPosition();
|
||||||
boxVisible.value = true;
|
boxVisible.value = true;
|
||||||
await nextTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
editVisible.value = true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async hide() {
|
async hide() {
|
||||||
editVisible.value = false;
|
boxVisible.value = false;
|
||||||
|
|
||||||
if (props.slideType !== 'box') {
|
|
||||||
await nextTick();
|
|
||||||
boxVisible.value = false;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -17,13 +17,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue';
|
import { computed, inject, nextTick, onBeforeUnmount, provide, ref, watch } from 'vue';
|
||||||
import { Close } from '@element-plus/icons-vue';
|
import { Close } from '@element-plus/icons-vue';
|
||||||
import VanillaMoveable from 'moveable';
|
import VanillaMoveable from 'moveable';
|
||||||
|
|
||||||
import { TMagicButton, useZIndex } from '@tmagic/design';
|
import { TMagicButton, useZIndex } from '@tmagic/design';
|
||||||
|
|
||||||
import MIcon from '@editor/components/Icon.vue';
|
import MIcon from '@editor/components/Icon.vue';
|
||||||
|
import type { Services } from '@editor/type';
|
||||||
|
|
||||||
interface Position {
|
interface Position {
|
||||||
left: number;
|
left: number;
|
||||||
@ -50,7 +51,7 @@ const target = ref<HTMLDivElement>();
|
|||||||
const titleEl = ref<HTMLDivElement>();
|
const titleEl = ref<HTMLDivElement>();
|
||||||
|
|
||||||
const zIndex = useZIndex();
|
const zIndex = useZIndex();
|
||||||
const curZIndex = ref<number>(zIndex.nextZIndex());
|
const curZIndex = ref<number>(0);
|
||||||
|
|
||||||
const titleHeight = ref(0);
|
const titleHeight = ref(0);
|
||||||
const bodyHeight = computed(() => {
|
const bodyHeight = computed(() => {
|
||||||
@ -65,12 +66,21 @@ const bodyHeight = computed(() => {
|
|||||||
return 'auto';
|
return 'auto';
|
||||||
});
|
});
|
||||||
|
|
||||||
const style = computed(() => ({
|
const services = inject<Services>('services');
|
||||||
left: `${props.position.left}px`,
|
const frameworkWidth = computed(() => services?.uiService.get('frameworkRect').width || 0);
|
||||||
top: `${props.position.top}px`,
|
const style = computed(() => {
|
||||||
width: width.value ? `${width.value}px` : 'auto',
|
let { left } = props.position;
|
||||||
height: height.value ? `${height.value}px` : 'auto',
|
if (width.value) {
|
||||||
}));
|
left = left + width.value > frameworkWidth.value ? frameworkWidth.value - width.value : left;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
left: `${left}px`,
|
||||||
|
top: `${props.position.top}px`,
|
||||||
|
width: width.value ? `${width.value}px` : 'auto',
|
||||||
|
height: height.value ? `${height.value}px` : 'auto',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
let moveable: VanillaMoveable | null = null;
|
let moveable: VanillaMoveable | null = null;
|
||||||
|
|
||||||
@ -114,6 +124,7 @@ watch(
|
|||||||
async (visible) => {
|
async (visible) => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
curZIndex.value = zIndex.nextZIndex();
|
||||||
|
|
||||||
const targetRect = target.value?.getBoundingClientRect();
|
const targetRect = target.value?.getBoundingClientRect();
|
||||||
if (targetRect) {
|
if (targetRect) {
|
||||||
@ -157,6 +168,8 @@ const nextZIndex = () => {
|
|||||||
curZIndex.value = zIndex.nextZIndex();
|
curZIndex.value = zIndex.nextZIndex();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
provide('parentFloating', target);
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
bodyHeight,
|
bodyHeight,
|
||||||
target,
|
target,
|
||||||
|
@ -7,39 +7,57 @@
|
|||||||
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="newHandler()">添加</TMagicButton>
|
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="newHandler()">添加</TMagicButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MFormDrawer
|
<FloatingBox
|
||||||
ref="addDialog"
|
v-model:visible="addDialogVisible"
|
||||||
label-width="80px"
|
v-model:width="width"
|
||||||
|
v-model:height="editorHeight"
|
||||||
:title="fieldTitle"
|
:title="fieldTitle"
|
||||||
:config="dataSourceFieldsConfig"
|
:position="boxPosition"
|
||||||
:values="fieldValues"
|
>
|
||||||
:parentValues="model[name]"
|
<template #body>
|
||||||
:disabled="disabled"
|
<MFormBox
|
||||||
:width="width"
|
label-width="80px"
|
||||||
@submit="fieldChange"
|
:title="fieldTitle"
|
||||||
></MFormDrawer>
|
:config="dataSourceFieldsConfig"
|
||||||
|
:values="fieldValues"
|
||||||
|
:parentValues="model[name]"
|
||||||
|
:disabled="disabled"
|
||||||
|
@submit="fieldChange"
|
||||||
|
></MFormBox>
|
||||||
|
</template>
|
||||||
|
</FloatingBox>
|
||||||
|
|
||||||
<MFormDrawer
|
<FloatingBox
|
||||||
ref="addFromJsonDialog"
|
v-model:visible="addFromJsonDialogVisible"
|
||||||
|
v-model:width="width"
|
||||||
|
v-model:height="editorHeight"
|
||||||
title="快速添加数据定义"
|
title="快速添加数据定义"
|
||||||
:config="jsonFromConfig"
|
:position="boxPosition"
|
||||||
:values="jsonFromValues"
|
>
|
||||||
:disabled="disabled"
|
<template #body>
|
||||||
:width="width"
|
<MFormBox
|
||||||
@submit="addFromJsonFromChange"
|
:config="jsonFromConfig"
|
||||||
></MFormDrawer>
|
:values="jsonFromValues"
|
||||||
|
:disabled="disabled"
|
||||||
|
@submit="addFromJsonFromChange"
|
||||||
|
></MFormBox>
|
||||||
|
</template>
|
||||||
|
</FloatingBox>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, inject, ref } from 'vue';
|
import { inject, Ref, ref } from 'vue';
|
||||||
|
|
||||||
import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
|
import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
|
||||||
import { type FieldProps, type FormConfig, type FormState, MFormDrawer } from '@tmagic/form';
|
import { type FieldProps, type FormConfig, type FormState, MFormBox } from '@tmagic/form';
|
||||||
import type { DataSchema } from '@tmagic/schema';
|
import type { DataSchema } from '@tmagic/schema';
|
||||||
import { type ColumnConfig, MagicTable } from '@tmagic/table';
|
import { type ColumnConfig, MagicTable } from '@tmagic/table';
|
||||||
import { getDefaultValueFromFields } from '@tmagic/utils';
|
import { getDefaultValueFromFields } from '@tmagic/utils';
|
||||||
|
|
||||||
|
import FloatingBox from '@editor/components/FloatingBox.vue';
|
||||||
|
import { useEditorContentHeight } from '@editor/hooks';
|
||||||
|
import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
|
||||||
import type { Services } from '@editor/type';
|
import type { Services } from '@editor/type';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -61,17 +79,16 @@ const emit = defineEmits(['change']);
|
|||||||
|
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
|
|
||||||
const addDialog = ref<InstanceType<typeof MFormDrawer>>();
|
|
||||||
|
|
||||||
const fieldValues = ref<Record<string, any>>({});
|
const fieldValues = ref<Record<string, any>>({});
|
||||||
const fieldTitle = ref('');
|
const fieldTitle = ref('');
|
||||||
|
|
||||||
const width = computed(() => globalThis.document.body.clientWidth - (services?.uiService.get('columnWidth').left || 0));
|
const width = defineModel<number>('width', { default: 670 });
|
||||||
|
|
||||||
const newHandler = () => {
|
const newHandler = () => {
|
||||||
fieldValues.value = {};
|
fieldValues.value = {};
|
||||||
fieldTitle.value = '新增属性';
|
fieldTitle.value = '新增属性';
|
||||||
addDialog.value?.show();
|
calcBoxPosition();
|
||||||
|
addDialogVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fieldChange = ({ index, ...value }: Record<string, any>) => {
|
const fieldChange = ({ index, ...value }: Record<string, any>) => {
|
||||||
@ -81,7 +98,7 @@ const fieldChange = ({ index, ...value }: Record<string, any>) => {
|
|||||||
props.model[props.name].push(value);
|
props.model[props.name].push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
addDialog.value?.hide();
|
addDialogVisible.value = false;
|
||||||
|
|
||||||
emit('change', props.model[props.name]);
|
emit('change', props.model[props.name]);
|
||||||
};
|
};
|
||||||
@ -122,7 +139,8 @@ const fieldColumns: ColumnConfig[] = [
|
|||||||
index,
|
index,
|
||||||
};
|
};
|
||||||
fieldTitle.value = `编辑${row.title}`;
|
fieldTitle.value = `编辑${row.title}`;
|
||||||
addDialog.value?.show();
|
calcBoxPosition();
|
||||||
|
addDialogVisible.value = true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -220,7 +238,6 @@ const dataSourceFieldsConfig: FormConfig = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const addFromJsonDialog = ref<InstanceType<typeof MFormDrawer>>();
|
|
||||||
const jsonFromConfig: FormConfig = [
|
const jsonFromConfig: FormConfig = [
|
||||||
{
|
{
|
||||||
name: 'data',
|
name: 'data',
|
||||||
@ -238,7 +255,8 @@ const jsonFromValues = ref({
|
|||||||
|
|
||||||
const newFromJsonHandler = () => {
|
const newFromJsonHandler = () => {
|
||||||
jsonFromValues.value.data = getDefaultValueFromFields(props.model[props.name]);
|
jsonFromValues.value.data = getDefaultValueFromFields(props.model[props.name]);
|
||||||
addFromJsonDialog.value?.show();
|
calcBoxPosition();
|
||||||
|
addFromJsonDialogVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getValueType = (value: any) => {
|
const getValueType = (value: any) => {
|
||||||
@ -288,11 +306,18 @@ const addFromJsonFromChange = ({ data }: { data: string }) => {
|
|||||||
|
|
||||||
props.model[props.name] = getFieldsConfig(value, props.model[props.name]);
|
props.model[props.name] = getFieldsConfig(value, props.model[props.name]);
|
||||||
|
|
||||||
addFromJsonDialog.value?.hide();
|
addFromJsonDialogVisible.value = false;
|
||||||
|
|
||||||
emit('change', props.model[props.name]);
|
emit('change', props.model[props.name]);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
tMagicMessage.error(e.message);
|
tMagicMessage.error(e.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addDialogVisible = defineModel<boolean>('visible', { default: false });
|
||||||
|
const addFromJsonDialogVisible = defineModel<boolean>('visible1', { default: false });
|
||||||
|
const { height: editorHeight } = useEditorContentHeight();
|
||||||
|
|
||||||
|
const parentFloating = inject<Ref<HTMLDivElement>>('parentFloating');
|
||||||
|
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating);
|
||||||
</script>
|
</script>
|
||||||
|
@ -6,32 +6,44 @@
|
|||||||
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="newHandler()">添加</TMagicButton>
|
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="newHandler()">添加</TMagicButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MFormDrawer
|
<FloatingBox
|
||||||
ref="addDialog"
|
v-model:visible="addDialogVisible"
|
||||||
label-width="120px"
|
v-model:width="width"
|
||||||
|
v-model:height="editorHeight"
|
||||||
:title="drawerTitle"
|
:title="drawerTitle"
|
||||||
:config="formConfig"
|
:position="boxPosition"
|
||||||
:values="formValues"
|
>
|
||||||
:parentValues="model[name]"
|
<template #body>
|
||||||
:disabled="disabled"
|
<MFormBox
|
||||||
:width="width"
|
ref="addDialog"
|
||||||
@submit="formChangeHandler"
|
label-width="120px"
|
||||||
></MFormDrawer>
|
:config="formConfig"
|
||||||
|
:values="formValues"
|
||||||
|
:parentValues="model[name]"
|
||||||
|
:disabled="disabled"
|
||||||
|
@submit="formChangeHandler"
|
||||||
|
></MFormBox>
|
||||||
|
</template>
|
||||||
|
</FloatingBox>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, inject, ref } from 'vue';
|
import { inject, Ref, ref } from 'vue';
|
||||||
|
|
||||||
import { TMagicButton, tMagicMessageBox, TMagicSwitch } from '@tmagic/design';
|
import { TMagicButton, tMagicMessageBox, TMagicSwitch } from '@tmagic/design';
|
||||||
import { type FieldProps, type FormConfig, type FormState, MFormDrawer } from '@tmagic/form';
|
import { type FieldProps, type FormConfig, type FormState, MFormBox } from '@tmagic/form';
|
||||||
import type { MockSchema } from '@tmagic/schema';
|
import type { MockSchema } from '@tmagic/schema';
|
||||||
import { type ColumnConfig, MagicTable } from '@tmagic/table';
|
import { type ColumnConfig, MagicTable } from '@tmagic/table';
|
||||||
import { getDefaultValueFromFields } from '@tmagic/utils';
|
import { getDefaultValueFromFields } from '@tmagic/utils';
|
||||||
|
|
||||||
|
import FloatingBox from '@editor/components/FloatingBox.vue';
|
||||||
|
import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
|
||||||
import CodeEditor from '@editor/layouts/CodeEditor.vue';
|
import CodeEditor from '@editor/layouts/CodeEditor.vue';
|
||||||
import { Services } from '@editor/type';
|
import { Services } from '@editor/type';
|
||||||
|
|
||||||
|
import { useEditorContentHeight } from '..';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MFieldsDataSourceMocks',
|
name: 'MFieldsDataSourceMocks',
|
||||||
});
|
});
|
||||||
@ -50,9 +62,8 @@ const props = withDefaults(
|
|||||||
const emit = defineEmits(['change']);
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
const width = computed(() => globalThis.document.body.clientWidth - (services?.uiService.get('columnWidth').left || 0));
|
const width = defineModel<number>('width', { default: 670 });
|
||||||
|
|
||||||
const addDialog = ref<InstanceType<typeof MFormDrawer>>();
|
|
||||||
const drawerTitle = ref('');
|
const drawerTitle = ref('');
|
||||||
const formValues = ref<Record<string, any>>({});
|
const formValues = ref<Record<string, any>>({});
|
||||||
|
|
||||||
@ -182,7 +193,8 @@ const columns: ColumnConfig[] = [
|
|||||||
index,
|
index,
|
||||||
};
|
};
|
||||||
drawerTitle.value = `编辑${row.title}`;
|
drawerTitle.value = `编辑${row.title}`;
|
||||||
addDialog.value?.show();
|
calcBoxPosition();
|
||||||
|
addDialogVisible.value = true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -206,7 +218,8 @@ const newHandler = () => {
|
|||||||
enable: isFirstRow,
|
enable: isFirstRow,
|
||||||
};
|
};
|
||||||
drawerTitle.value = '新增Mock';
|
drawerTitle.value = '新增Mock';
|
||||||
addDialog.value?.show();
|
calcBoxPosition();
|
||||||
|
addDialogVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formChangeHandler = ({ index, ...value }: Record<string, any>) => {
|
const formChangeHandler = ({ index, ...value }: Record<string, any>) => {
|
||||||
@ -216,7 +229,7 @@ const formChangeHandler = ({ index, ...value }: Record<string, any>) => {
|
|||||||
props.model[props.name].push(value);
|
props.model[props.name].push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
addDialog.value?.hide();
|
addDialogVisible.value = false;
|
||||||
|
|
||||||
emit('change', props.model[props.name]);
|
emit('change', props.model[props.name]);
|
||||||
};
|
};
|
||||||
@ -234,4 +247,9 @@ const toggleValue = (row: MockSchema, key: 'enable' | 'useInEditor', value: bool
|
|||||||
index,
|
index,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addDialogVisible = defineModel<boolean>('visible', { default: false });
|
||||||
|
const { height: editorHeight } = useEditorContentHeight();
|
||||||
|
const parentFloating = inject<Ref<HTMLDivElement>>('parentFloating');
|
||||||
|
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating);
|
||||||
</script>
|
</script>
|
||||||
|
@ -10,7 +10,7 @@ export const useEditorContentHeight = () => {
|
|||||||
|
|
||||||
const height = ref(0);
|
const height = ref(0);
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (height.value > 0) return;
|
if (height.value > 0 && height.value === editorContentHeight.value) return;
|
||||||
height.value = editorContentHeight.value;
|
height.value = editorContentHeight.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { computed, ComputedRef, ref, watch } from 'vue';
|
import { computed, ComputedRef, inject, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import type { Services } from '@editor/type';
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
status: boolean;
|
status: boolean;
|
||||||
@ -7,6 +9,8 @@ interface State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useFloatBox = (slideKeys: ComputedRef<string[]>) => {
|
export const useFloatBox = (slideKeys: ComputedRef<string[]>) => {
|
||||||
|
const services = inject<Services>('services');
|
||||||
|
|
||||||
const floatBoxStates = ref<{
|
const floatBoxStates = ref<{
|
||||||
[key in (typeof slideKeys.value)[number]]: State;
|
[key in (typeof slideKeys.value)[number]]: State;
|
||||||
}>(
|
}>(
|
||||||
@ -30,9 +34,10 @@ export const useFloatBox = (slideKeys: ComputedRef<string[]>) => {
|
|||||||
|
|
||||||
const dragstartHandler = () => (isDragging.value = true);
|
const dragstartHandler = () => (isDragging.value = true);
|
||||||
const dragendHandler = (key: string, e: DragEvent) => {
|
const dragendHandler = (key: string, e: DragEvent) => {
|
||||||
|
const navMenuRect = services?.uiService?.get('navMenuRect');
|
||||||
floatBoxStates.value[key] = {
|
floatBoxStates.value[key] = {
|
||||||
left: e.clientX,
|
left: e.clientX,
|
||||||
top: e.clientY,
|
top: (navMenuRect?.top ?? 0) + (navMenuRect?.height ?? 0),
|
||||||
status: true,
|
status: true,
|
||||||
};
|
};
|
||||||
isDragging.value = false;
|
isDragging.value = false;
|
||||||
@ -47,12 +52,13 @@ export const useFloatBox = (slideKeys: ComputedRef<string[]>) => {
|
|||||||
() => slideKeys.value,
|
() => slideKeys.value,
|
||||||
(slideKeys) => {
|
(slideKeys) => {
|
||||||
slideKeys.forEach((key) => {
|
slideKeys.forEach((key) => {
|
||||||
if (floatBoxStates.value[key]) return;
|
if (!floatBoxStates.value[key]) {
|
||||||
floatBoxStates.value[key] = {
|
floatBoxStates.value[key] = {
|
||||||
status: false,
|
status: false,
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
29
packages/editor/src/hooks/use-next-float-box-position.ts
Normal file
29
packages/editor/src/hooks/use-next-float-box-position.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { Ref, ref } from 'vue';
|
||||||
|
|
||||||
|
import { UiService } from '@editor/services/ui';
|
||||||
|
|
||||||
|
export const useNextFloatBoxPosition = (uiService?: UiService, parent?: Ref<HTMLDivElement>) => {
|
||||||
|
const boxPosition = ref({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const calcBoxPosition = () => {
|
||||||
|
const columnWidth = uiService?.get('columnWidth');
|
||||||
|
const navMenuRect = uiService?.get('navMenuRect');
|
||||||
|
let left = columnWidth?.left ?? 0;
|
||||||
|
if (parent?.value) {
|
||||||
|
const rect = parent?.value?.getBoundingClientRect();
|
||||||
|
left = (rect?.left ?? 0) + (rect?.width ?? 0);
|
||||||
|
}
|
||||||
|
boxPosition.value = {
|
||||||
|
left,
|
||||||
|
top: (navMenuRect?.top ?? 0) + (navMenuRect?.height ?? 0),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
boxPosition,
|
||||||
|
calcBoxPosition,
|
||||||
|
};
|
||||||
|
};
|
@ -111,8 +111,8 @@
|
|||||||
:key="config.$key ?? index"
|
:key="config.$key ?? index"
|
||||||
v-if="floatBoxStates[config.$key]?.status"
|
v-if="floatBoxStates[config.$key]?.status"
|
||||||
v-model:visible="floatBoxStates[config.$key].status"
|
v-model:visible="floatBoxStates[config.$key].status"
|
||||||
:width="columnLeftWitch"
|
v-model:width="columnLeftWidth"
|
||||||
:height="600"
|
v-model:height="columnLeftHeight"
|
||||||
:title="config.text"
|
:title="config.text"
|
||||||
:position="{
|
:position="{
|
||||||
left: floatBoxStates[config.$key].left,
|
left: floatBoxStates[config.$key].left,
|
||||||
@ -123,8 +123,8 @@
|
|||||||
<div class="m-editor-slide-list-box">
|
<div class="m-editor-slide-list-box">
|
||||||
<component
|
<component
|
||||||
v-if="config && floatBoxStates[config.$key].status"
|
v-if="config && floatBoxStates[config.$key].status"
|
||||||
:is="config.boxComponentConfig?.component || config.component"
|
:is="config.component"
|
||||||
v-bind="config.boxComponentConfig?.props || config.props || {}"
|
v-bind="config.props || {}"
|
||||||
v-on="config?.listeners || {}"
|
v-on="config?.listeners || {}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -140,6 +140,7 @@ import { Coin, EditPen, Goods, List } from '@element-plus/icons-vue';
|
|||||||
|
|
||||||
import FloatingBox from '@editor/components/FloatingBox.vue';
|
import FloatingBox from '@editor/components/FloatingBox.vue';
|
||||||
import MIcon from '@editor/components/Icon.vue';
|
import MIcon from '@editor/components/Icon.vue';
|
||||||
|
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
|
||||||
import { useFloatBox } from '@editor/hooks/use-float-box';
|
import { useFloatBox } from '@editor/hooks/use-float-box';
|
||||||
import {
|
import {
|
||||||
ColumnLayout,
|
ColumnLayout,
|
||||||
@ -176,7 +177,8 @@ const props = withDefaults(
|
|||||||
|
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
|
|
||||||
const columnLeftWitch = computed(() => services?.uiService.get('columnWidth')[ColumnLayout.LEFT] || 0);
|
const columnLeftWidth = computed(() => services?.uiService.get('columnWidth')[ColumnLayout.LEFT] || 0);
|
||||||
|
const { height: columnLeftHeight } = useEditorContentHeight();
|
||||||
|
|
||||||
const activeTabName = ref(props.data?.status);
|
const activeTabName = ref(props.data?.status);
|
||||||
|
|
||||||
@ -209,11 +211,6 @@ const getItemConfig = (data: SideItem): SideComponent => {
|
|||||||
text: '代码编辑',
|
text: '代码编辑',
|
||||||
component: CodeBlockListPanel,
|
component: CodeBlockListPanel,
|
||||||
slots: {},
|
slots: {},
|
||||||
boxComponentConfig: {
|
|
||||||
props: {
|
|
||||||
slideType: 'box',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'data-source': {
|
'data-source': {
|
||||||
$key: 'data-source',
|
$key: 'data-source',
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
ref="codeBlockEditor"
|
ref="codeBlockEditor"
|
||||||
:disabled="!editable"
|
:disabled="!editable"
|
||||||
:content="codeConfig"
|
:content="codeConfig"
|
||||||
:slideType="slideType"
|
|
||||||
@submit="submitCodeBlockHandler"
|
@submit="submitCodeBlockHandler"
|
||||||
></CodeBlockEditor>
|
></CodeBlockEditor>
|
||||||
</template>
|
</template>
|
||||||
@ -37,7 +36,7 @@ import type { Id } from '@tmagic/schema';
|
|||||||
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
||||||
import SearchInput from '@editor/components/SearchInput.vue';
|
import SearchInput from '@editor/components/SearchInput.vue';
|
||||||
import { useCodeBlockEdit } from '@editor/hooks/use-code-block-edit';
|
import { useCodeBlockEdit } from '@editor/hooks/use-code-block-edit';
|
||||||
import type { CodeBlockListPanelSlots, CodeDeleteErrorType, Services, SlideType } from '@editor/type';
|
import type { CodeBlockListPanelSlots, CodeDeleteErrorType, Services } from '@editor/type';
|
||||||
|
|
||||||
import CodeBlockList from './CodeBlockList.vue';
|
import CodeBlockList from './CodeBlockList.vue';
|
||||||
|
|
||||||
@ -49,7 +48,6 @@ defineOptions({
|
|||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
|
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
|
||||||
slideType?: SlideType;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { codeBlockService } = inject<Services>('services') || {};
|
const { codeBlockService } = inject<Services>('services') || {};
|
||||||
|
@ -1,27 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<component
|
<FloatingBox
|
||||||
:is="slideType === 'box' ? MFormBox : MFormDrawer"
|
v-model:visible="boxVisible"
|
||||||
ref="fomDrawer"
|
v-model:width="width"
|
||||||
label-width="80px"
|
v-model:height="editorHeight"
|
||||||
:close-on-press-escape="false"
|
|
||||||
:title="title"
|
:title="title"
|
||||||
:width="size"
|
:position="boxPosition"
|
||||||
:config="dataSourceConfig"
|
>
|
||||||
:values="initValues"
|
<template #body>
|
||||||
:disabled="disabled"
|
<MFormBox
|
||||||
@submit="submitHandler"
|
label-width="80px"
|
||||||
@error="errorHandler"
|
:title="title"
|
||||||
></component>
|
:config="dataSourceConfig"
|
||||||
|
:values="initValues"
|
||||||
|
:disabled="disabled"
|
||||||
|
style="height: 100%"
|
||||||
|
@submit="submitHandler"
|
||||||
|
@error="errorHandler"
|
||||||
|
></MFormBox>
|
||||||
|
</template>
|
||||||
|
</FloatingBox>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, inject, ref, watchEffect } from 'vue';
|
import { inject, Ref, ref, watchEffect } from 'vue';
|
||||||
|
|
||||||
import { tMagicMessage } from '@tmagic/design';
|
import { tMagicMessage } from '@tmagic/design';
|
||||||
import { FormConfig, MFormBox, MFormDrawer } from '@tmagic/form';
|
import { FormConfig, MFormBox } from '@tmagic/form';
|
||||||
import { DataSourceSchema } from '@tmagic/schema';
|
import { DataSourceSchema } from '@tmagic/schema';
|
||||||
|
|
||||||
import type { Services, SlideType } from '@editor/type';
|
import FloatingBox from '@editor/components/FloatingBox.vue';
|
||||||
|
import { useEditorContentHeight } from '@editor/hooks';
|
||||||
|
import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
|
||||||
|
import type { Services } from '@editor/type';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MEditorDataSourceConfigPanel',
|
name: 'MEditorDataSourceConfigPanel',
|
||||||
@ -31,23 +41,27 @@ const props = defineProps<{
|
|||||||
title?: string;
|
title?: string;
|
||||||
values: any;
|
values: any;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
slideType?: SlideType;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const boxVisible = defineModel<boolean>('visible', { default: false });
|
||||||
|
const width = defineModel<number>('width', { default: 670 });
|
||||||
|
|
||||||
const emit = defineEmits(['submit']);
|
const emit = defineEmits(['submit']);
|
||||||
|
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
|
|
||||||
const size = computed(() => globalThis.document.body.clientWidth - (services?.uiService.get('columnWidth').left || 0));
|
|
||||||
|
|
||||||
const fomDrawer = ref<InstanceType<typeof MFormDrawer>>();
|
|
||||||
|
|
||||||
const initValues = ref<Partial<DataSourceSchema>>({});
|
const initValues = ref<Partial<DataSourceSchema>>({});
|
||||||
const dataSourceConfig = ref<FormConfig>([]);
|
const dataSourceConfig = ref<FormConfig>([]);
|
||||||
|
|
||||||
|
const { height: editorHeight } = useEditorContentHeight();
|
||||||
|
|
||||||
|
const parentFloating = inject<Ref<HTMLDivElement>>('parentFloating');
|
||||||
|
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating);
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
initValues.value = props.values;
|
initValues.value = props.values;
|
||||||
dataSourceConfig.value = services?.dataSourceService.getFormConfig(initValues.value.type) || [];
|
dataSourceConfig.value = services?.dataSourceService.getFormConfig(initValues.value.type) || [];
|
||||||
|
console.log(dataSourceConfig.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const submitHandler = (values: any) => {
|
const submitHandler = (values: any) => {
|
||||||
@ -60,11 +74,12 @@ const errorHandler = (error: any) => {
|
|||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show() {
|
show() {
|
||||||
fomDrawer.value?.show();
|
calcBoxPosition();
|
||||||
|
boxVisible.value = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
fomDrawer.value?.hide();
|
boxVisible.value = false;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
:disabled="!editable"
|
:disabled="!editable"
|
||||||
:values="dataSourceValues"
|
:values="dataSourceValues"
|
||||||
:title="dialogTitle"
|
:title="dialogTitle"
|
||||||
:slideType="slideType"
|
|
||||||
@submit="submitDataSourceHandler"
|
@submit="submitDataSourceHandler"
|
||||||
></DataSourceConfigPanel>
|
></DataSourceConfigPanel>
|
||||||
</template>
|
</template>
|
||||||
@ -47,7 +46,7 @@ import type { DataSourceSchema } from '@tmagic/schema';
|
|||||||
|
|
||||||
import SearchInput from '@editor/components/SearchInput.vue';
|
import SearchInput from '@editor/components/SearchInput.vue';
|
||||||
import ToolButton from '@editor/components/ToolButton.vue';
|
import ToolButton from '@editor/components/ToolButton.vue';
|
||||||
import type { DataSourceListSlots, Services, SlideType } from '@editor/type';
|
import type { DataSourceListSlots, Services } from '@editor/type';
|
||||||
|
|
||||||
import DataSourceConfigPanel from './DataSourceConfigPanel.vue';
|
import DataSourceConfigPanel from './DataSourceConfigPanel.vue';
|
||||||
import DataSourceList from './DataSourceList.vue';
|
import DataSourceList from './DataSourceList.vue';
|
||||||
@ -58,10 +57,6 @@ defineOptions({
|
|||||||
name: 'MEditorDataSourceListPanel',
|
name: 'MEditorDataSourceListPanel',
|
||||||
});
|
});
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
slideType?: SlideType;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const { dataSourceService } = inject<Services>('services') || {};
|
const { dataSourceService } = inject<Services>('services') || {};
|
||||||
|
|
||||||
const editDialog = ref<InstanceType<typeof DataSourceConfigPanel>>();
|
const editDialog = ref<InstanceType<typeof DataSourceConfigPanel>>();
|
||||||
|
@ -4,13 +4,3 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.m-editor-code-block-editor {
|
|
||||||
.tmagic-design-table {
|
|
||||||
height: 180px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-drawer__body {
|
|
||||||
padding: 10px 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -401,12 +401,6 @@ export interface SideBarData {
|
|||||||
items: SideItem[];
|
items: SideItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* drawer 抽屉
|
|
||||||
* box 悬浮窗
|
|
||||||
*/
|
|
||||||
export type SlideType = 'drawer' | 'box';
|
|
||||||
|
|
||||||
export interface ComponentItem {
|
export interface ComponentItem {
|
||||||
/** 显示文案 */
|
/** 显示文案 */
|
||||||
text: string;
|
text: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user