refactor(editor): 新增useServices hook,减少使用可选链

This commit is contained in:
roymondchen 2025-02-11 16:24:24 +08:00
parent 9cabbe8c7c
commit 900b701c80
62 changed files with 486 additions and 508 deletions

View File

@ -74,9 +74,9 @@ import {
import FloatingBox from '@editor/components/FloatingBox.vue';
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
import { useServices } from '@editor/hooks/use-services';
import { useWindowRect } from '@editor/hooks/use-window-rect';
import CodeEditor from '@editor/layouts/CodeEditor.vue';
import type { Services } from '@editor/type';
import { getEditorConfig } from '@editor/utils/config';
defineOptions({
@ -97,7 +97,7 @@ const emit = defineEmits<{
submit: [values: CodeBlockContent, eventData: ContainerChangeEventData];
}>();
const services = inject<Services>('services');
const { codeBlockService, uiService } = useServices();
const { height: codeBlockEditorHeight } = useEditorContentHeight();
@ -192,7 +192,7 @@ const functionConfig = computed<FormConfig>(() => [
label: '描述',
name: 'extra',
},
services?.codeBlockService.getParamsColConfig() || defaultParamColConfig,
codeBlockService.getParamsColConfig() || defaultParamColConfig,
],
},
{
@ -259,7 +259,7 @@ const closedHandler = () => {
};
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating);
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
defineExpose({
async show() {

View File

@ -17,14 +17,14 @@
</template>
<script setup lang="ts">
import { computed, inject, nextTick, onBeforeUnmount, provide, ref, useTemplateRef, watch } from 'vue';
import { computed, nextTick, onBeforeUnmount, provide, ref, useTemplateRef, watch } from 'vue';
import { Close } from '@element-plus/icons-vue';
import VanillaMoveable from 'moveable';
import { TMagicButton, useZIndex } from '@tmagic/design';
import MIcon from '@editor/components/Icon.vue';
import type { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
interface Position {
left: number;
@ -66,8 +66,8 @@ const bodyHeight = computed(() => {
return 'auto';
});
const services = inject<Services>('services');
const frameworkWidth = computed(() => services?.uiService.get('frameworkRect').width || 0);
const { uiService } = useServices();
const frameworkWidth = computed(() => uiService.get('frameworkRect').width || 0);
const style = computed(() => {
let { left } = props.position;
if (width.value) {

View File

@ -49,7 +49,7 @@
</template>
<script lang="ts" setup>
import { computed, inject } from 'vue';
import { computed } from 'vue';
import { ArrowDown } from '@element-plus/icons-vue';
import {
@ -62,8 +62,10 @@ import {
TMagicTooltip,
} from '@tmagic/design';
import { useServices } from '@editor/hooks/use-services';
import MIcon from '../components/Icon.vue';
import type { MenuButton, MenuComponent, Services } from '../type';
import type { MenuButton, MenuComponent } from '../type';
defineOptions({
name: 'MEditorToolButton',
@ -82,7 +84,7 @@ const props = withDefaults(
eventType: 'click',
},
);
const services = inject<Services>('services');
const services = useServices();
const disabled = computed(() => {
if (typeof props.data === 'string') return false;
@ -104,7 +106,7 @@ const display = computed(() => {
const buttonHandler = (item: MenuButton | MenuComponent, event: MouseEvent) => {
if (disabled.value) return;
if (typeof (item as MenuButton).handler === 'function' && services) {
if (typeof (item as MenuButton).handler === 'function') {
(item as MenuButton).handler?.(services, event);
}
};

View File

@ -15,7 +15,7 @@
class="tree-node"
:class="{ selected, expanded }"
:style="`padding-left: ${indent}px`"
@contextmenu="nodeContentmenuHandler"
@contextmenu="nodeContextmenuHandler"
@mouseenter="mouseenterHandler"
>
<MIcon
@ -139,7 +139,7 @@ const handleDragEnd = (event: DragEvent) => {
treeEmit?.('node-dragend', event, props.data);
};
const nodeContentmenuHandler = (event: MouseEvent) => {
const nodeContextmenuHandler = (event: MouseEvent) => {
treeEmit?.('node-contextmenu', event, props.data);
};

View File

@ -16,7 +16,7 @@
</template>
<script lang="ts" setup>
import { computed, inject, watch } from 'vue';
import { computed, watch } from 'vue';
import { isEmpty } from 'lodash-es';
import { HookCodeType, HookType } from '@tmagic/core';
@ -24,7 +24,7 @@ import { TMagicCard } from '@tmagic/design';
import type { ContainerChangeEventData, FieldProps, FormItem, GroupListConfig } from '@tmagic/form';
import { MContainer } from '@tmagic/form';
import type { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
defineOptions({
name: 'MFieldsCodeSelect',
@ -34,7 +34,7 @@ const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
}>();
const services = inject<Services>('services');
const { dataSourceService, codeBlockService } = useServices();
const props = withDefaults(
defineProps<
@ -59,7 +59,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
return index;
}
const ds = services?.dataSourceService.getDataSourceById(model.codeId[0]);
const ds = dataSourceService.getDataSourceById(model.codeId[0]);
return `${ds?.title} / ${model.codeId[1]}`;
}
@ -98,7 +98,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
span: 18,
labelWidth: 0,
display: (mForm, { model }) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD,
notEditable: () => !services?.codeBlockService.getEditStatus(),
notEditable: () => !codeBlockService.getEditStatus(),
},
{
type: 'data-source-method-select',
@ -106,7 +106,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
span: 18,
labelWidth: 0,
display: (mForm, { model }) => model.codeType === HookCodeType.DATA_SOURCE_METHOD,
notEditable: () => !services?.dataSourceService.get('editable'),
notEditable: () => !dataSourceService.get('editable'),
},
],
},

View File

@ -52,7 +52,8 @@ import {
import CodeParams from '@editor/components/CodeParams.vue';
import MIcon from '@editor/components/Icon.vue';
import type { CodeParamStatement, CodeSelectColConfig, EventBus, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { CodeParamStatement, CodeSelectColConfig, EventBus } from '@editor/type';
import { SideItemKey } from '@editor/type';
defineOptions({
@ -60,7 +61,7 @@ defineOptions({
});
const mForm = inject<FormState | undefined>('mForm');
const services = inject<Services>('services');
const { codeBlockService, uiService } = useServices();
const eventBus = inject<EventBus>('eventBus');
const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
@ -73,7 +74,7 @@ const props = withDefaults(defineProps<FieldProps<CodeSelectColConfig>>(), {
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
const hasCodeBlockSidePanel = computed(() =>
(services?.uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.CODE_BLOCK),
(uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.CODE_BLOCK),
);
/**
@ -94,7 +95,7 @@ const getParamItemsConfig = (codeId?: Id): CodeParamStatement[] => {
}));
};
const codeDsl = computed(() => services?.codeBlockService.getCodeDsl());
const codeDsl = computed(() => codeBlockService.getCodeDsl());
const paramsConfig = ref<CodeParamStatement[]>(getParamItemsConfig(props.model[props.name]));
watch(

View File

@ -27,12 +27,13 @@
</template>
<script setup lang="ts">
import { computed, inject } from 'vue';
import { computed } from 'vue';
import { getDesignConfig, TMagicSelect } from '@tmagic/design';
import type { FieldProps } from '@tmagic/form';
import type { CondOpSelectConfig, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { CondOpSelectConfig } from '@editor/type';
import { arrayOptions, eqOptions, numberOptions } from '@editor/utils';
defineOptions({
@ -43,7 +44,7 @@ const emit = defineEmits<{
change: [value: string];
}>();
const { dataSourceService } = inject<Services>('services') || {};
const { dataSourceService } = useServices();
const props = defineProps<FieldProps<CondOpSelectConfig>>();
@ -52,7 +53,7 @@ const optionComponent = getDesignConfig('components')?.option;
const options = computed(() => {
const [id, ...fieldNames] = [...(props.config.parentFields || []), ...props.model.field];
const ds = dataSourceService?.getDataSourceById(id);
const ds = dataSourceService.getDataSourceById(id);
let fields = ds?.fields || [];
let type = '';

View File

@ -69,13 +69,14 @@
import { computed, inject, ref, watch } from 'vue';
import { Edit, View } from '@element-plus/icons-vue';
import { DataSourceFieldType } from '@tmagic/core';
import type { DataSourceFieldType } from '@tmagic/core';
import { getDesignConfig, TMagicButton, TMagicCascader, TMagicSelect, TMagicTooltip } from '@tmagic/design';
import { type FilterFunction, filterFunction, type FormState, type SelectOption } from '@tmagic/form';
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue';
import { type EventBus, type Services, SideItemKey } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import { type EventBus, SideItemKey } from '@editor/type';
import { getCascaderOptionsFromFields, removeDataSourceFieldPrefix } from '@editor/utils';
const props = defineProps<{
@ -101,11 +102,11 @@ const modelValue = defineModel<string[] | any>('modelValue', { default: [] });
const optionComponent = getDesignConfig('components')?.option;
const services = inject<Services>('services');
const { dataSourceService, uiService } = useServices();
const mForm = inject<FormState | undefined>('mForm');
const eventBus = inject<EventBus>('eventBus');
const dataSources = computed(() => services?.dataSourceService.get('dataSources') || []);
const dataSources = computed(() => dataSourceService.get('dataSources') || []);
const valueIsKey = computed(() => props.value === 'key');
const notEditable = computed(() => filterFunction(mForm, props.notEditable, props));
@ -172,7 +173,7 @@ const onChangeHandler = (v: string[] = []) => {
};
const hasDataSourceSidePanel = computed(() =>
(services?.uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE),
uiService.get('sideBarItems').find((item) => item.$key === SideItemKey.DATA_SOURCE),
);
const editHandler = (id: string) => {

View File

@ -48,7 +48,8 @@ import type { ContainerChangeEventData, FieldProps, FormState } from '@tmagic/fo
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue';
import type { DataSourceFieldSelectConfig, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { DataSourceFieldSelectConfig } from '@editor/type';
import { removeDataSourceFieldPrefix } from '@editor/utils';
import FieldSelect from './FieldSelect.vue';
@ -83,10 +84,10 @@ watch(
},
);
const services = inject<Services>('services');
const { dataSourceService } = useServices();
const mForm = inject<FormState | undefined>('mForm');
const dataSources = computed(() => services?.dataSourceService.get('dataSources') || []);
const dataSources = computed(() => dataSourceService.get('dataSources') || []);
const type = computed((): string => {
let type = props.config.fieldConfig?.type;

View File

@ -64,7 +64,7 @@ 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 { useServices } from '@editor/hooks/use-services';
defineOptions({
name: 'MFieldsDataSourceFields',
@ -85,7 +85,7 @@ const emit = defineEmits<{
change: [v: any, eventData?: ContainerChangeEventData];
}>();
const services = inject<Services>('services');
const { uiService } = useServices();
const fieldValues = ref<Record<string, any>>({});
const fieldTitle = ref('');
@ -344,5 +344,5 @@ const addFromJsonDialogVisible = defineModel<boolean>('visible1', { default: fal
const { height: editorHeight } = useEditorContentHeight();
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating);
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
</script>

View File

@ -48,7 +48,7 @@
</template>
<script setup lang="ts">
import { computed, inject, nextTick, ref, useTemplateRef, watch } from 'vue';
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue';
import { Coin } from '@element-plus/icons-vue';
import type { DataSchema, DataSourceSchema } from '@tmagic/core';
@ -57,7 +57,7 @@ import type { FieldProps, FormItem } from '@tmagic/form';
import { getKeysArray, isNumber } from '@tmagic/utils';
import Icon from '@editor/components/Icon.vue';
import type { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import { getDisplayField } from '@editor/utils/data-source';
defineOptions({
@ -81,7 +81,7 @@ const emit = defineEmits<{
change: [value: string];
}>();
const { dataSourceService } = inject<Services>('services') || {};
const { dataSourceService } = useServices();
const autocompleteRef = useTemplateRef<InstanceType<typeof TMagicAutocomplete>>('autocomplete');
const isFocused = ref(false);
@ -89,7 +89,7 @@ const state = ref('');
const displayState = ref<{ value: string; type: 'var' | 'text' }[]>([]);
const input = computed<HTMLInputElement>(() => autocompleteRef.value?.inputRef?.input);
const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
const dataSources = computed(() => dataSourceService.get('dataSources'));
const setDisplayState = () => {
displayState.value = getDisplayField(dataSources.value, state.value);

View File

@ -41,37 +41,36 @@ import { createValues, type FieldProps, filterFunction, type FormState, MContain
import CodeParams from '@editor/components/CodeParams.vue';
import MIcon from '@editor/components/Icon.vue';
import type { CodeParamStatement, DataSourceMethodSelectConfig, EventBus, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { CodeParamStatement, DataSourceMethodSelectConfig, EventBus } from '@editor/type';
import { SideItemKey } from '@editor/type';
defineOptions({
name: 'MFieldsDataSourceMethodSelect',
});
const { dataSourceService, uiService } = useServices();
const mForm = inject<FormState | undefined>('mForm');
const services = inject<Services>('services');
const eventBus = inject<EventBus>('eventBus');
const emit = defineEmits(['change']);
const dataSourceService = services?.dataSourceService;
const props = withDefaults(defineProps<FieldProps<DataSourceMethodSelectConfig>>(), {
disabled: false,
});
const hasDataSourceSidePanel = computed(() =>
(services?.uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE),
(uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE),
);
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
const dataSources = computed(() => dataSourceService?.get('dataSources'));
const dataSources = computed(() => dataSourceService.get('dataSources'));
const isCustomMethod = computed(() => {
const [id, name] = props.model[props.name];
const dataSource = dataSourceService?.getDataSourceById(id);
const dataSource = dataSourceService.getDataSourceById(id);
return Boolean(dataSource?.methods.find((method) => method.name === name));
});
@ -107,7 +106,7 @@ const setParamsConfig = (dataSourceMethod: [Id, string], formState: any = {}) =>
const methodsOptions = computed(
() =>
dataSources.value
?.filter((ds) => ds.methods?.length || dataSourceService?.getFormMethod(ds.type).length)
?.filter((ds) => ds.methods?.length || dataSourceService.getFormMethod(ds.type).length)
?.map((ds) => ({
label: ds.title || ds.id,
value: ds.id,
@ -144,7 +143,7 @@ const onChangeHandler = (value: any) => {
const editCodeHandler = () => {
const [id] = props.model[props.name];
const dataSource = dataSourceService?.getDataSourceById(id);
const dataSource = dataSourceService.getDataSourceById(id);
if (!dataSource) return;

View File

@ -38,8 +38,8 @@ import { getDefaultValueFromFields } from '@tmagic/utils';
import FloatingBox from '@editor/components/FloatingBox.vue';
import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
import { useServices } from '@editor/hooks/use-services';
import CodeEditor from '@editor/layouts/CodeEditor.vue';
import { Services } from '@editor/type';
import { useEditorContentHeight } from '..';
@ -60,7 +60,7 @@ const props = withDefaults(
const emit = defineEmits(['change']);
const services = inject<Services>('services');
const { uiService } = useServices();
const width = defineModel<number>('width', { default: 670 });
const drawerTitle = ref('');
@ -250,5 +250,5 @@ const toggleValue = (row: MockSchema, key: 'enable' | 'useInEditor', value: bool
const addDialogVisible = defineModel<boolean>('visible', { default: false });
const { height: editorHeight } = useEditorContentHeight();
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating);
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
</script>

View File

@ -27,7 +27,8 @@ import { TMagicButton, TMagicTooltip } from '@tmagic/design';
import { type FieldProps, filterFunction, type FormState, MSelect, type SelectConfig } from '@tmagic/form';
import MIcon from '@editor/components/Icon.vue';
import type { DataSourceSelect, EventBus, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { DataSourceSelect, EventBus } from '@editor/type';
import { SideItemKey } from '@editor/type';
defineOptions({
@ -41,15 +42,15 @@ const props = withDefaults(defineProps<FieldProps<DataSourceSelect>>(), {
});
const mForm = inject<FormState | undefined>('mForm');
const { dataSourceService, uiService } = inject<Services>('services') || {};
const { dataSourceService, uiService } = useServices();
const eventBus = inject<EventBus>('eventBus');
const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
const dataSources = computed(() => dataSourceService.get('dataSources'));
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
const hasDataSourceSidePanel = computed(() =>
(uiService?.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE),
uiService.get('sideBarItems').find((item) => item.$key === SideItemKey.DATA_SOURCE),
);
const selectConfig = computed<SelectConfig>(() => {
@ -87,7 +88,7 @@ const editHandler = () => {
const id = typeof value === 'string' ? value : value.dataSourceId;
const dataSource = dataSourceService?.getDataSourceById(id);
const dataSource = dataSourceService.getDataSourceById(id);
if (!dataSource) return;

View File

@ -26,7 +26,7 @@ import {
MGroupList,
} from '@tmagic/form';
import type { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import { getCascaderOptionsFromFields } from '@editor/utils';
defineOptions({
@ -49,7 +49,7 @@ const props = withDefaults(
},
);
const { dataSourceService } = inject<Services>('services') || {};
const { dataSourceService } = useServices();
const mForm = inject<FormState | undefined>('mForm');
const parentFields = computed(() => filterFunction<string[]>(mForm, props.config.parentFields, props) || []);
@ -71,7 +71,7 @@ const config = computed<GroupListConfig>(() => ({
type: 'cascader',
options: () => {
const [dsId, ...keys] = parentFields.value;
const ds = dataSourceService?.getDataSourceById(dsId);
const ds = dataSourceService.getDataSourceById(dsId);
if (!ds) {
return [];
}
@ -113,7 +113,7 @@ const config = computed<GroupListConfig>(() => ({
type: (mForm, { model }) => {
const [id, ...fieldNames] = [...parentFields.value, ...model.field];
const ds = dataSourceService?.getDataSourceById(id);
const ds = dataSourceService.getDataSourceById(id);
let fields = ds?.fields || [];
let type = '';

View File

@ -50,7 +50,7 @@
</template>
<script lang="ts" setup>
import { computed, inject } from 'vue';
import { computed } from 'vue';
import { Delete } from '@element-plus/icons-vue';
import { has } from 'lodash-es';
@ -68,7 +68,8 @@ import type {
import { MContainer as MFormContainer, MPanel } from '@tmagic/form';
import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, traverseNode } from '@tmagic/utils';
import type { CodeSelectColConfig, DataSourceMethodSelectConfig, EventSelectConfig, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { CodeSelectColConfig, DataSourceMethodSelectConfig, EventSelectConfig } from '@editor/type';
import { getCascaderOptionsFromFields } from '@editor/utils';
defineOptions({
@ -81,12 +82,7 @@ const emit = defineEmits<{
change: [v: any, eventData?: ContainerChangeEventData];
}>();
const services = inject<Services>('services');
const editorService = services?.editorService;
const dataSourceService = services?.dataSourceService;
const eventsService = services?.eventsService;
const codeBlockService = services?.codeBlockService;
const { editorService, dataSourceService, eventsService, codeBlockService } = useServices();
//
const eventNameConfig = computed(() => {
@ -108,13 +104,11 @@ const eventNameConfig = computed(() => {
options: (mForm: FormState, { formValue }: any) => {
let events: EventOption[] | CascaderOption[] = [];
if (!eventsService || !dataSourceService) return events;
if (props.config.src === 'component') {
events = eventsService.getEvent(formValue.type);
if (formValue.type === 'page-fragment-container' && formValue.pageFragmentId) {
const pageFragment = editorService?.get('root')?.items?.find((page) => page.id === formValue.pageFragmentId);
const pageFragment = editorService.get('root')?.items?.find((page) => page.id === formValue.pageFragmentId);
if (pageFragment) {
events = [
{
@ -185,7 +179,7 @@ const actionTypeConfig = computed(() => {
{
text: '代码',
label: '代码',
disabled: !Object.keys(codeBlockService?.getCodeDsl() || {}).length,
disabled: !Object.keys(codeBlockService.getCodeDsl() || {}).length,
value: ActionType.CODE,
},
{
@ -193,7 +187,7 @@ const actionTypeConfig = computed(() => {
label: '数据源',
disabled: !dataSourceService
?.get('dataSources')
?.filter((ds) => ds.methods?.length || dataSourceService?.getFormMethod(ds.type).length).length,
?.filter((ds) => ds.methods?.length || dataSourceService.getFormMethod(ds.type).length).length,
value: ActionType.DATA_SOURCE,
},
],
@ -222,7 +216,7 @@ const compActionConfig = computed(() => {
name: 'method',
text: '动作',
type: (mForm, { model }: any) => {
const to = editorService?.getNodeById(model.to);
const to = editorService.getNodeById(model.to);
if (to && to.type === 'page-fragment-container' && to.pageFragmentId) {
return 'cascader';
@ -233,20 +227,20 @@ const compActionConfig = computed(() => {
checkStrictly: () => props.config.src !== 'component',
display: (mForm, { model }: any) => model.actionType === ActionType.COMP,
options: (mForm: FormState, { model }: any) => {
const node = editorService?.getNodeById(model.to);
const node = editorService.getNodeById(model.to);
if (!node?.type) return [];
let methods: EventOption[] | CascaderOption[] = [];
methods = eventsService?.getMethod(node.type) || [];
methods = eventsService.getMethod(node.type);
if (node.type === 'page-fragment-container' && node.pageFragmentId) {
const pageFragment = editorService?.get('root')?.items?.find((page) => page.id === node.pageFragmentId);
const pageFragment = editorService.get('root')?.items?.find((page) => page.id === node.pageFragmentId);
if (pageFragment) {
methods = [];
pageFragment.items.forEach((node: MComponent | MContainer) => {
traverseNode<MComponent | MContainer>(node, (node) => {
const nodeMethods = (node.type && eventsService?.getMethod(node.type)) || [];
const nodeMethods = (node.type && eventsService.getMethod(node.type)) || [];
if (nodeMethods.length) {
methods.push({
@ -277,7 +271,7 @@ const codeActionConfig = computed(() => {
type: 'code-select-col',
text: '代码块',
name: 'codeId',
notEditable: () => !codeBlockService?.getEditStatus(),
notEditable: () => !codeBlockService.getEditStatus(),
display: (mForm, { model }) => model.actionType === ActionType.CODE,
};
return { ...defaultCodeActionConfig, ...props.config.codeActionConfig };
@ -289,7 +283,7 @@ const dataSourceActionConfig = computed(() => {
type: 'data-source-method-select',
text: '数据源方法',
name: 'dataSourceMethod',
notEditable: () => !services?.dataSourceService.get('editable'),
notEditable: () => !dataSourceService.get('editable'),
display: (mForm, { model }) => model.actionType === ActionType.DATA_SOURCE,
};
return { ...defaultDataSourceActionConfig, ...props.config.dataSourceActionConfig };
@ -305,7 +299,7 @@ const tableConfig = computed(() => ({
label: '事件名',
type: eventNameConfig.value.type,
options: (mForm: FormState, { formValue }: any) =>
eventsService?.getEvent(formValue.type).map((option: any) => ({
eventsService.getEvent(formValue.type).map((option: any) => ({
text: option.label,
value: option.value,
})),
@ -320,10 +314,10 @@ const tableConfig = computed(() => ({
label: '动作',
type: compActionConfig.value.type,
options: (mForm: FormState, { model }: any) => {
const node = editorService?.getNodeById(model.to);
const node = editorService.getNodeById(model.to);
if (!node?.type) return [];
return eventsService?.getMethod(node.type).map((option: any) => ({
return eventsService.getMethod(node.type).map((option: any) => ({
text: option.label,
value: option.value,
}));

View File

@ -16,27 +16,28 @@
</template>
<script lang="ts" setup>
import { computed, inject } from 'vue';
import { computed } from 'vue';
import { Edit } from '@element-plus/icons-vue';
import { Id, NodeType } from '@tmagic/core';
import { FieldProps } from '@tmagic/form';
import Icon from '@editor/components/Icon.vue';
import type { PageFragmentSelectConfig, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { PageFragmentSelectConfig } from '@editor/type';
defineOptions({
name: 'MFieldsPageFragmentSelect',
});
const services = inject<Services>('services');
const { editorService } = useServices();
const emit = defineEmits(['change']);
const props = withDefaults(defineProps<FieldProps<PageFragmentSelectConfig>>(), {
disabled: false,
});
const pageList = computed(() =>
services?.editorService.get('root')?.items.filter((item) => item.type === NodeType.PAGE_FRAGMENT),
editorService.get('root')?.items.filter((item) => item.type === NodeType.PAGE_FRAGMENT),
);
const selectConfig = {
@ -58,6 +59,6 @@ const changeHandler = async () => {
};
const editPageFragment = (id: Id) => {
services?.editorService.select(id);
editorService.select(id);
};
</script>

View File

@ -50,7 +50,8 @@ import { TMagicButton, TMagicTooltip } from '@tmagic/design';
import type { FieldProps, FormItem, FormState } from '@tmagic/form';
import { getIdFromEl } from '@tmagic/utils';
import { type Services, UI_SELECT_MODE_EVENT_NAME } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import { UI_SELECT_MODE_EVENT_NAME } from '@editor/utils/const';
defineOptions({
name: 'MFieldsUISelect',
@ -60,14 +61,14 @@ const props = defineProps<FieldProps<{ type: 'ui-select' } & FormItem>>();
const emit = defineEmits(['change']);
const services = inject<Services>('services');
const { editorService, uiService, stageOverlayService } = useServices();
const mForm = inject<FormState>('mForm');
const val = computed(() => props.model[props.name]);
const uiSelectMode = ref(false);
const cancelHandler = () => {
if (!services?.uiService) return;
services.uiService.set('uiSelectMode', false);
uiService.set('uiSelectMode', false);
uiSelectMode.value = false;
globalThis.document.removeEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as EventListener);
};
@ -89,13 +90,12 @@ const clickHandler = ({ detail }: Event & { detail: HTMLElement | MNode }) => {
};
const toName = computed(() => {
const config = services?.editorService.getNodeById(val.value);
const config = editorService.getNodeById(val.value);
return config?.name || '';
});
const startSelect = () => {
if (!services?.uiService) return;
services.uiService.set('uiSelectMode', true);
uiService.set('uiSelectMode', true);
uiSelectMode.value = true;
globalThis.document.addEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as EventListener);
};
@ -109,24 +109,21 @@ const deleteHandler = () => {
};
const selectNode = async (id: Id) => {
if (!services) return;
await services.editorService.select(id);
services.editorService.get('stage')?.select(id);
services.stageOverlayService.get('stage')?.select(id);
await editorService.select(id);
editorService.get('stage')?.select(id);
stageOverlayService.get('stage')?.select(id);
};
const highlight = throttle((id: Id) => {
if (!services) return;
services.editorService.highlight(id);
services.editorService.get('stage')?.highlight(id);
services.stageOverlayService.get('stage')?.highlight(id);
editorService.highlight(id);
editorService.get('stage')?.highlight(id);
stageOverlayService.get('stage')?.highlight(id);
}, 150);
const unhighlight = () => {
if (!services) return;
services.editorService.set('highlightNode', null);
services.editorService.get('stage')?.clearHighlight();
services.stageOverlayService.get('stage')?.clearHighlight();
editorService.set('highlightNode', null);
editorService.get('stage')?.clearHighlight();
stageOverlayService.get('stage')?.clearHighlight();
};
</script>

View File

@ -28,3 +28,4 @@ export * from './use-next-float-box-position';
export * from './use-node-status';
export * from './use-stage';
export * from './use-window-rect';
export * from './use-services';

View File

@ -5,20 +5,15 @@ import type { CodeBlockContent } from '@tmagic/core';
import { tMagicMessage } from '@tmagic/design';
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
import type { CodeBlockService } from '@editor/services/codeBlock';
import type { Services } from '@editor/type';
export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
export const useCodeBlockEdit = (codeBlockService: Services['codeBlockService']) => {
const codeConfig = ref<CodeBlockContent>();
const codeId = ref<string>();
const codeBlockEditorRef = useTemplateRef<InstanceType<typeof CodeBlockEditor>>('codeBlockEditor');
// 新增代码块
const createCodeBlock = async () => {
if (!codeBlockService) {
tMagicMessage.error('新增代码块失败');
return;
}
codeConfig.value = {
name: '',
content: `({app, params, flowState}) => {\n // place your code here\n}`,
@ -34,7 +29,7 @@ export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
// 编辑代码块
const editCode = async (id: string) => {
const codeBlock = await codeBlockService?.getCodeContentById(id);
const codeBlock = await codeBlockService.getCodeContentById(id);
if (!codeBlock) {
tMagicMessage.error('获取代码块内容失败');
@ -59,13 +54,13 @@ export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
// 删除代码块
const deleteCode = async (key: string) => {
codeBlockService?.deleteCodeDslByIds([key]);
codeBlockService.deleteCodeDslByIds([key]);
};
const submitCodeBlockHandler = async (values: CodeBlockContent) => {
if (!codeId.value) return;
await codeBlockService?.setCodeDslById(codeId.value, values);
await codeBlockService.setCodeDslById(codeId.value, values);
codeBlockEditorRef.value?.hide();
};

View File

@ -4,20 +4,20 @@ import type { DataSourceSchema } from '@tmagic/core';
import type { ContainerChangeEventData } from '@tmagic/form';
import DataSourceConfigPanel from '@editor/layouts/sidebar/data-source/DataSourceConfigPanel.vue';
import type { DataSourceService } from '@editor/services/dataSource';
import type { Services } from '@editor/type';
export const useDataSourceEdit = (dataSourceService?: DataSourceService) => {
export const useDataSourceEdit = (dataSourceService: Services['dataSourceService']) => {
const dialogTitle = ref('');
const editDialog = ref<InstanceType<typeof DataSourceConfigPanel>>();
const dataSourceValues = ref<Record<string, any>>({});
const editable = computed(() => dataSourceService?.get('editable') ?? true);
const editable = computed(() => dataSourceService.get('editable'));
const editHandler = (id: string) => {
if (!editDialog.value) return;
dataSourceValues.value = {
...dataSourceService?.getDataSourceById(id),
...(dataSourceService.getDataSourceById(id) || {}),
};
dialogTitle.value = `编辑${dataSourceValues.value.title || ''}`;
@ -27,9 +27,9 @@ export const useDataSourceEdit = (dataSourceService?: DataSourceService) => {
const submitDataSourceHandler = (value: DataSourceSchema, eventData: ContainerChangeEventData) => {
if (value.id) {
dataSourceService?.update(value, { changeRecords: eventData.changeRecords });
dataSourceService.update(value, { changeRecords: eventData.changeRecords });
} else {
dataSourceService?.add(value);
dataSourceService.add(value);
}
editDialog.value?.hide();

View File

@ -1,11 +1,11 @@
import { computed, inject, ref, watch } from 'vue';
import { computed, ref, watch } from 'vue';
import type { Services } from '@editor/type';
import { useServices } from './use-services';
export const useEditorContentHeight = () => {
const services = inject<Services>('services');
const frameworkHeight = computed(() => services?.uiService.get('frameworkRect').height || 0);
const navMenuHeight = computed(() => services?.uiService.get('navMenuRect').height || 0);
const { uiService } = useServices();
const frameworkHeight = computed(() => uiService.get('frameworkRect').height);
const navMenuHeight = computed(() => uiService.get('navMenuRect').height);
const editorContentHeight = computed(() => frameworkHeight.value - navMenuHeight.value);
const height = ref(0);

View File

@ -1,6 +1,6 @@
import { computed, ComputedRef, inject, onBeforeUnmount, ref, watch } from 'vue';
import { computed, ComputedRef, onBeforeUnmount, ref, watch } from 'vue';
import type { Services } from '@editor/type';
import { useServices } from './use-services';
interface State {
status: boolean;
@ -9,7 +9,7 @@ interface State {
}
export const useFloatBox = (slideKeys: ComputedRef<string[]>) => {
const services = inject<Services>('services');
const { uiService } = useServices();
const floatBoxStates = ref<{
[key in (typeof slideKeys.value)[number]]: State;
@ -57,10 +57,10 @@ export const useFloatBox = (slideKeys: ComputedRef<string[]>) => {
Math.abs(startOffset.x - e.clientX) > effectiveDistance ||
Math.abs(startOffset.y - e.clientY) > effectiveDistance
) {
const navMenuRect = services?.uiService?.get('navMenuRect');
const navMenuRect = uiService.get('navMenuRect');
floatBoxStates.value[key] = {
left: e.clientX,
top: (navMenuRect?.top ?? 0) + (navMenuRect?.height ?? 0),
top: navMenuRect.top + navMenuRect.height,
status: true,
};
}

View File

@ -1,24 +1,24 @@
import { Ref, ref } from 'vue';
import { type Ref, ref } from 'vue';
import { UiService } from '@editor/services/ui';
import type { Services } from '@editor/type';
export const useNextFloatBoxPosition = (uiService?: UiService, parent?: Ref<HTMLDivElement | null>) => {
export const useNextFloatBoxPosition = (uiService: Services['uiService'], parent?: Ref<HTMLDivElement | null>) => {
const boxPosition = ref({
left: 0,
top: 0,
});
const calcBoxPosition = () => {
const columnWidth = uiService?.get('columnWidth');
const navMenuRect = uiService?.get('navMenuRect');
let left = columnWidth?.left ?? 0;
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);
const rect = parent.value.getBoundingClientRect();
left = rect.left + rect.width;
}
boxPosition.value = {
left,
top: (navMenuRect?.top ?? 0) + (navMenuRect?.height ?? 0),
top: navMenuRect.top + navMenuRect.height,
};
};

View File

@ -0,0 +1,13 @@
import { inject } from 'vue';
import type { Services } from '@editor/type';
export const useServices = () => {
const services = inject<Services>('services');
if (!services) {
throw new Error('services is required');
}
return services;
};

View File

@ -6,12 +6,8 @@ import { getIdFromEl } from '@tmagic/utils';
import editorService from '@editor/services/editor';
import uiService from '@editor/services/ui';
import {
H_GUIDE_LINE_STORAGE_KEY,
StageOptions,
UI_SELECT_MODE_EVENT_NAME,
V_GUIDE_LINE_STORAGE_KEY,
} from '@editor/type';
import type { StageOptions } from '@editor/type';
import { H_GUIDE_LINE_STORAGE_KEY, UI_SELECT_MODE_EVENT_NAME, V_GUIDE_LINE_STORAGE_KEY } from '@editor/utils/const';
import { getGuideLineFromCache } from '@editor/utils/editor';
const root = computed(() => editorService.get('root'));

View File

@ -19,13 +19,13 @@
</template>
<script lang="ts" setup>
import { inject, toRaw } from 'vue';
import { toRaw } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import { NodeType } from '@tmagic/core';
import MIcon from '@editor/components/Icon.vue';
import type { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import { generatePageNameByApp } from '@editor/utils';
defineOptions({
@ -36,13 +36,9 @@ defineProps<{
disabledPageFragment: boolean;
}>();
const services = inject<Services>('services');
const { editorService } = useServices();
const clickHandler = (type: NodeType.PAGE | NodeType.PAGE_FRAGMENT) => {
const { editorService } = services || {};
if (!editorService) return;
const root = toRaw(editorService.get('root'));
if (!root) throw new Error('root 不能为空');

View File

@ -65,7 +65,8 @@ import { computed, inject, onBeforeUnmount, onMounted, useTemplateRef, watch } f
import type { MPage, MPageFragment } from '@tmagic/core';
import SplitView from '@editor/components/SplitView.vue';
import type { FrameworkSlots, GetColumnWidth, PageBarSortOptions, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { FrameworkSlots, GetColumnWidth, PageBarSortOptions } from '@editor/type';
import { getEditorConfig } from '@editor/utils/config';
import {
DEFAULT_LEFT_COLUMN_WIDTH,
@ -90,34 +91,27 @@ defineProps<{
}>();
const codeOptions = inject('codeOptions', {});
const { editorService, uiService } = inject<Services>('services') || {};
const { editorService, uiService } = useServices();
const contentEl = useTemplateRef<HTMLDivElement>('content');
const splitViewRef = useTemplateRef<InstanceType<typeof SplitView>>('splitView');
const root = computed(() => editorService?.get('root'));
const page = computed(() => editorService?.get('page'));
const root = computed(() => editorService.get('root'));
const page = computed(() => editorService.get('page'));
const pageLength = computed(() => editorService?.get('pageLength') || 0);
const showSrc = computed(() => uiService?.get('showSrc'));
const pageLength = computed(() => editorService.get('pageLength') || 0);
const showSrc = computed(() => uiService.get('showSrc'));
const columnWidth = computed(
() =>
uiService?.get('columnWidth') || {
left: 0,
center: 0,
right: 0,
},
);
const columnWidth = computed(() => uiService.get('columnWidth'));
watch(pageLength, () => {
splitViewRef.value?.updateWidth();
});
watch(
() => uiService?.get('hideSlideBar'),
() => uiService.get('hideSlideBar'),
(hideSlideBar) => {
uiService?.set('columnWidth', {
uiService.set('columnWidth', {
...columnWidth.value,
left: hideSlideBar
? 0
@ -129,14 +123,14 @@ watch(
const columnWidthChange = (columnW: GetColumnWidth) => {
globalThis.localStorage.setItem(LEFT_COLUMN_WIDTH_STORAGE_KEY, `${columnW.left}`);
globalThis.localStorage.setItem(RIGHT_COLUMN_WIDTH_STORAGE_KEY, `${columnW.right}`);
uiService?.set('columnWidth', columnW);
uiService.set('columnWidth', columnW);
};
const frameworkRect = computed(() => uiService?.get('frameworkRect'));
const frameworkRect = computed(() => uiService.get('frameworkRect'));
const resizerObserver = new ResizeObserver((entries) => {
const { contentRect } = entries[0];
uiService?.set('frameworkRect', {
uiService.set('frameworkRect', {
width: contentRect.width,
height: contentRect.height,
left: contentRect.left,
@ -157,7 +151,7 @@ onBeforeUnmount(() => {
const saveCode = (value: string) => {
try {
const parseDSL = getEditorConfig('parseDSL');
editorService?.set('root', parseDSL(value));
editorService.set('root', parseDSL(value));
} catch (e: any) {
console.error(e);
}

View File

@ -7,13 +7,14 @@
</template>
<script lang="ts" setup>
import { computed, inject, markRaw, onBeforeUnmount, onMounted, useTemplateRef } from 'vue';
import { computed, markRaw, onBeforeUnmount, onMounted, useTemplateRef } from 'vue';
import { Back, Delete, FullScreen, Grid, Memo, Right, ScaleToOriginal, ZoomIn, ZoomOut } from '@element-plus/icons-vue';
import { NodeType } from '@tmagic/core';
import ToolButton from '@editor/components/ToolButton.vue';
import { ColumnLayout, MenuBarData, MenuButton, MenuComponent, MenuItem, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import { ColumnLayout, MenuBarData, MenuButton, MenuComponent, MenuItem } from '@editor/type';
defineOptions({
name: 'MEditorNavMenu',
@ -30,15 +31,14 @@ const props = withDefaults(
},
);
const services = inject<Services>('services');
const uiService = services?.uiService;
const { uiService, editorService, historyService } = useServices();
const columnWidth = computed(() => services?.uiService.get('columnWidth'));
const columnWidth = computed(() => uiService.get('columnWidth'));
const keys = Object.values(ColumnLayout);
const showGuides = computed((): boolean => uiService?.get('showGuides') ?? true);
const showRule = computed((): boolean => uiService?.get('showRule') ?? true);
const zoom = computed((): number => uiService?.get('zoom') ?? 1);
const showGuides = computed((): boolean => uiService.get('showGuides'));
const showRule = computed((): boolean => uiService.get('showRule'));
const zoom = computed((): number => uiService.get('zoom'));
const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
const ctrl = isMac ? 'Command' : 'Ctrl';
@ -70,10 +70,10 @@ const getConfig = (item: MenuItem): (MenuButton | MenuComponent)[] => {
className: 'delete',
icon: markRaw(Delete),
tooltip: `刪除(Delete)`,
disabled: () => services?.editorService.get('node')?.type === NodeType.PAGE,
disabled: () => editorService.get('node')?.type === NodeType.PAGE,
handler: () => {
const node = services?.editorService.get('node');
node && services?.editorService.remove(node);
const node = editorService.get('node');
node && editorService.remove(node);
},
});
break;
@ -83,8 +83,8 @@ const getConfig = (item: MenuItem): (MenuButton | MenuComponent)[] => {
className: 'undo',
icon: markRaw(Back),
tooltip: `后退(${ctrl}+z)`,
disabled: () => !services?.historyService.state.canUndo,
handler: () => services?.editorService.undo(),
disabled: () => !historyService.state.canUndo,
handler: () => editorService.undo(),
});
break;
case 'redo':
@ -93,8 +93,8 @@ const getConfig = (item: MenuItem): (MenuButton | MenuComponent)[] => {
className: 'redo',
icon: markRaw(Right),
tooltip: `前进(${ctrl}+Shift+z)`,
disabled: () => !services?.historyService.state.canRedo,
handler: () => services?.editorService.redo(),
disabled: () => !historyService.state.canRedo,
handler: () => editorService.redo(),
});
break;
case 'zoom-in':
@ -184,7 +184,7 @@ const navMenuEl = useTemplateRef<HTMLDivElement>('navMenu');
const resizeObserver = new ResizeObserver(() => {
const rect = navMenuEl.value?.getBoundingClientRect();
if (rect) {
uiService?.set('navMenuRect', {
uiService.set('navMenuRect', {
left: rect.left,
top: rect.top,
width: rect.width,

View File

@ -33,7 +33,7 @@
</template>
<script setup lang="ts">
import { computed, inject, toRaw } from 'vue';
import { computed, toRaw } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import { NodeType } from '@tmagic/core';
@ -41,21 +41,18 @@ import { TMagicPopover } from '@tmagic/design';
import Icon from '@editor/components/Icon.vue';
import ToolButton from '@editor/components/ToolButton.vue';
import type { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import { generatePageNameByApp } from '@editor/utils/editor';
defineOptions({
name: 'MEditorPageBarAddButton',
});
const services = inject<Services>('services');
const uiService = services?.uiService;
const editorService = services?.editorService;
const { editorService, uiService } = useServices();
const showAddPageButton = computed(() => uiService?.get('showAddPageButton'));
const showAddPageButton = computed(() => uiService.get('showAddPageButton'));
const addPage = (type: NodeType.PAGE | NodeType.PAGE_FRAGMENT) => {
if (!editorService) return;
const root = toRaw(editorService.get('root'));
if (!root) throw new Error('root 不能为空');
const pageConfig = {

View File

@ -68,14 +68,15 @@
</template>
<script lang="ts" setup>
import { computed, inject, ref, useTemplateRef, watch } from 'vue';
import { computed, ref, useTemplateRef, watch } from 'vue';
import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue';
import { type Id, type MPage, type MPageFragment, NodeType } from '@tmagic/core';
import { TMagicIcon, TMagicPopover } from '@tmagic/design';
import ToolButton from '@editor/components/ToolButton.vue';
import type { PageBarSortOptions, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { PageBarSortOptions } from '@editor/type';
import AddButton from './AddButton.vue';
import PageBarScrollContainer from './PageBarScrollContainer.vue';
@ -97,11 +98,10 @@ const props = withDefaults(
},
);
const services = inject<Services>('services');
const editorService = services?.editorService;
const { editorService } = useServices();
const root = computed(() => editorService?.get('root'));
const page = computed(() => editorService?.get('page'));
const root = computed(() => editorService.get('root'));
const page = computed(() => editorService.get('page'));
const query = ref<{
pageType: NodeType[];
@ -129,19 +129,19 @@ const list = computed(() => {
});
const switchPage = (id: Id) => {
editorService?.select(id);
editorService.select(id);
};
const copy = (node: MPage | MPageFragment) => {
node && editorService?.copy(node);
editorService?.paste({
node && editorService.copy(node);
editorService.paste({
left: 0,
top: 0,
});
};
const remove = (node: MPage | MPageFragment) => {
editorService?.remove(node);
editorService.remove(node);
};
const pageBarScrollContainerRef = useTemplateRef<InstanceType<typeof PageBarScrollContainer>>('pageBarScrollContainer');

View File

@ -24,14 +24,15 @@
</template>
<script setup lang="ts">
import { computed, inject, nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
import { computed, nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue';
import Sortable, { type SortableEvent } from 'sortablejs';
import type { Id } from '@tmagic/core';
import Icon from '@editor/components/Icon.vue';
import type { PageBarSortOptions, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { PageBarSortOptions } from '@editor/type';
defineOptions({
name: 'MEditorPageBarScrollContainer',
@ -42,15 +43,13 @@ const props = defineProps<{
length: number;
}>();
const services = inject<Services>('services');
const editorService = services?.editorService;
const uiService = services?.uiService;
const { editorService, uiService } = useServices();
const itemsContainerEl = useTemplateRef<HTMLElement>('itemsContainer');
const canScroll = ref(false);
const showAddPageButton = computed(() => uiService?.get('showAddPageButton'));
const showPageListButton = computed(() => uiService?.get('showPageListButton'));
const showAddPageButton = computed(() => uiService.get('showAddPageButton'));
const showPageListButton = computed(() => uiService.get('showPageListButton'));
const itemsContainerWidth = ref(0);
@ -145,7 +144,7 @@ watch(
beforeDragList = sortable.toArray();
},
onUpdate: async (event: SortableEvent) => {
await editorService?.sort(
await editorService.sort(
beforeDragList[event.oldIndex as number],
beforeDragList[event.newIndex as number],
);

View File

@ -35,14 +35,14 @@
</template>
<script setup lang="ts">
import { computed, inject } from 'vue';
import { computed } from 'vue';
import { Files } from '@element-plus/icons-vue';
import { Id, MPage, MPageFragment } from '@tmagic/core';
import { TMagicIcon, TMagicPopover } from '@tmagic/design';
import ToolButton from '@editor/components/ToolButton.vue';
import type { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
defineOptions({
name: 'MEditorPageList',
});
@ -51,13 +51,11 @@ defineProps<{
list: (MPage | MPageFragment)[];
}>();
const services = inject<Services>('services');
const uiService = services?.uiService;
const editorService = services?.editorService;
const { editorService, uiService } = useServices();
const showPageListButton = computed(() => uiService?.get('showPageListButton'));
const page = computed(() => editorService?.get('page'));
const showPageListButton = computed(() => uiService.get('showPageListButton'));
const page = computed(() => editorService.get('page'));
const switchPage = async (id: Id) => {
await editorService?.select(id);
await editorService.select(id);
};
</script>

View File

@ -51,7 +51,7 @@ import { MForm } from '@tmagic/form';
import MIcon from '@editor/components/Icon.vue';
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
import type { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import CodeEditor from '../CodeEditor.vue';
@ -80,13 +80,15 @@ const emit = defineEmits<{
mounted: [internalInstance: any];
}>();
const services = inject<Services>('services');
const services = useServices();
const { editorService, uiService } = services;
const codeOptions = inject('codeOptions', {});
const showSrc = ref(false);
const propsPanelSize = computed(() => services?.uiService.get('propsPanelSize') || 'small');
const propsPanelSize = computed(() => uiService.get('propsPanelSize') || 'small');
const { height: editorContentHeight } = useEditorContentHeight();
const stage = computed(() => services?.editorService.get('stage'));
const stage = computed(() => editorService.get('stage'));
const configFormRef = useTemplateRef<InstanceType<typeof MForm>>('configForm');

View File

@ -52,7 +52,7 @@
</template>
<script lang="ts" setup>
import { computed, inject, onBeforeUnmount, ref, useTemplateRef, watch, watchEffect } from 'vue';
import { computed, onBeforeUnmount, ref, useTemplateRef, watch, watchEffect } from 'vue';
import { Close, Sugar } from '@element-plus/icons-vue';
import type { OnDrag } from 'gesto';
@ -63,7 +63,8 @@ import { setValueByKeyPath } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue';
import Resizer from '@editor/components/Resizer.vue';
import type { PropsPanelSlots, Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { PropsPanelSlots } from '@editor/type';
import { styleTabConfig } from '@editor/utils';
import { RIGHT_COLUMN_WIDTH_STORAGE_KEY } from '@editor/utils/const';
@ -87,13 +88,13 @@ const emit = defineEmits<{
mounted: [internalInstance: InstanceType<typeof FormPanel>];
}>();
const services = inject<Services>('services');
const { editorService, uiService, propsService, storageService } = useServices();
const values = ref<FormValue>({});
// tsFormConfig any
const curFormConfig = ref<any>([]);
const node = computed(() => services?.editorService.get('node'));
const nodes = computed(() => services?.editorService.get('nodes') || []);
const node = computed(() => editorService.get('node'));
const nodes = computed(() => editorService.get('nodes'));
const styleFormConfig = [
{
@ -109,15 +110,15 @@ const init = async () => {
}
const type = node.value.type || (node.value.items ? 'container' : 'text');
curFormConfig.value = (await services?.propsService.getPropsConfig(type)) || [];
curFormConfig.value = await propsService.getPropsConfig(type);
values.value = node.value;
};
watchEffect(init);
services?.propsService.on('props-configs-change', init);
propsService.on('props-configs-change', init);
onBeforeUnmount(() => {
services?.propsService.off('props-configs-change', init);
propsService.off('props-configs-change', init);
});
const submit = async (v: MNode, eventData?: ContainerChangeEventData) => {
@ -145,7 +146,7 @@ const submit = async (v: MNode, eventData?: ContainerChangeEventData) => {
});
}
services?.editorService.update(newValue, { changeRecords: eventData?.changeRecords });
editorService.update(newValue, { changeRecords: eventData?.changeRecords });
} catch (e: any) {
emit('submit-error', e);
}
@ -163,7 +164,7 @@ const mountedHandler = () => {
const propsPanelEl = useTemplateRef('propsPanel');
const widthChange = ({ deltaX }: OnDrag) => {
if (!propsPanelEl.value || !services) {
if (!propsPanelEl.value) {
return;
}
@ -172,22 +173,24 @@ const widthChange = ({ deltaX }: OnDrag) => {
);
let value = width - deltaX;
if (value > services.uiService.get('columnWidth').right) {
value = services.uiService.get('columnWidth').right - 40;
if (value > uiService.get('columnWidth').right) {
value = uiService.get('columnWidth').right - 40;
}
propsPanelEl.value.style.setProperty('--props-style-panel-width', `${value}px`);
};
const { showStylePanel, showStylePanelToggleButton, showStylePanelHandler, closeStylePanelHandler } =
useStylePanel(services);
const { showStylePanel, showStylePanelToggleButton, showStylePanelHandler, closeStylePanelHandler } = useStylePanel({
storageService,
uiService,
});
watch(showStylePanel, (showStylePanel) => {
if (!propsPanelEl.value || !services) {
if (!propsPanelEl.value) {
return;
}
const columnWidth = {
...services.uiService.get('columnWidth'),
...uiService.get('columnWidth'),
};
const width = globalThis.parseFloat(
@ -210,7 +213,7 @@ watch(showStylePanel, (showStylePanel) => {
}
globalThis.localStorage.setItem(RIGHT_COLUMN_WIDTH_STORAGE_KEY, `${columnWidth.right}`);
services.uiService.set('columnWidth', columnWidth);
uiService.set('columnWidth', columnWidth);
});
const propertyFormPanelRef = useTemplateRef<InstanceType<typeof FormPanel>>('propertyFormPanel');

View File

@ -3,32 +3,28 @@ import { computed } from 'vue';
import { Protocol } from '@editor/services/storage';
import { Services } from '@editor/type';
export const useStylePanel = (services?: Services) => {
export const useStylePanel = ({ uiService, storageService }: Pick<Services, 'uiService' | 'storageService'>) => {
const showStylePanelStorageKey = 'props-panel-show-style-panel';
const showStylePanelStorageValue = services?.storageService.getItem(showStylePanelStorageKey, {
const showStylePanelStorageValue = storageService.getItem(showStylePanelStorageKey, {
protocol: Protocol.BOOLEAN,
});
if (typeof showStylePanelStorageValue === 'boolean') {
services?.uiService.set('showStylePanel', showStylePanelStorageValue);
uiService.set('showStylePanel', showStylePanelStorageValue);
}
const showStylePanel = computed(
() => showStylePanelToggleButton.value && (services?.uiService.get('showStylePanel') ?? true),
);
const showStylePanel = computed(() => showStylePanelToggleButton.value && (uiService.get('showStylePanel') ?? true));
const showStylePanelToggleButton = computed(
() => !(services && services.uiService.get('frameworkRect').width < 1280),
);
const showStylePanelToggleButton = computed(() => uiService.get('frameworkRect').width >= 1280);
const showStylePanelHandler = () => {
services?.uiService.set('showStylePanel', true);
services?.storageService.setItem(showStylePanelStorageKey, true, { protocol: Protocol.BOOLEAN });
uiService.set('showStylePanel', true);
storageService.setItem(showStylePanelStorageKey, true, { protocol: Protocol.BOOLEAN });
};
const closeStylePanelHandler = () => {
services?.uiService.set('showStylePanel', false);
services?.storageService.setItem(showStylePanelStorageKey, false, { protocol: Protocol.BOOLEAN });
uiService.set('showStylePanel', false);
storageService.setItem(showStylePanelStorageKey, false, { protocol: Protocol.BOOLEAN });
};
return {

View File

@ -43,12 +43,12 @@ import { removeClassNameByClassName } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue';
import SearchInput from '@editor/components/SearchInput.vue';
import { useServices } from '@editor/hooks/use-services';
import {
type ComponentGroup,
type ComponentItem,
ComponentListPanelSlots,
DragType,
type Services,
type StageOptions,
} from '@editor/type';
@ -64,12 +64,12 @@ const filterTextChangeHandler = (v: string) => {
searchText.value = v;
};
const services = inject<Services>('services');
const { editorService, componentListService } = useServices();
const stageOptions = inject<StageOptions>('stageOptions');
const stage = computed(() => services?.editorService.get('stage'));
const stage = computed(() => editorService.get('stage'));
const list = computed<ComponentGroup[]>(() =>
(services?.componentListService.getList() || []).map((group: ComponentGroup) => ({
componentListService.getList().map((group: ComponentGroup) => ({
...group,
items: group.items.filter((item: ComponentItem) => item.text.includes(searchText.value)),
})),
@ -85,7 +85,7 @@ let clientX: number;
let clientY: number;
const appendComponent = ({ text, type, data = {} }: ComponentItem): void => {
services?.editorService.add({
editorService.add({
name: text,
type,
...data,

View File

@ -149,19 +149,19 @@
</template>
<script setup lang="ts">
import { computed, inject, nextTick, ref, watch } from 'vue';
import { computed, nextTick, ref, watch } from 'vue';
import { Coin, EditPen, Goods, List } from '@element-plus/icons-vue';
import FloatingBox from '@editor/components/FloatingBox.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 { useServices } from '@editor/hooks/use-services';
import {
ColumnLayout,
CustomContentMenuFunction,
type MenuButton,
type MenuComponent,
type Services,
type SideBarData,
type SidebarSlots,
type SideComponent,
@ -197,11 +197,11 @@ const props = withDefaults(
},
);
const services = inject<Services>('services');
const { depService, uiService } = useServices();
const collecting = computed(() => services?.depService.get('collecting'));
const collecting = computed(() => depService.get('collecting'));
const columnLeftWidth = computed(() => services?.uiService.get('columnWidth')[ColumnLayout.LEFT] || 0);
const columnLeftWidth = computed(() => uiService.get('columnWidth')[ColumnLayout.LEFT]);
const { height: editorContentHeight } = useEditorContentHeight();
const columnLeftHeight = ref(0);
@ -282,7 +282,7 @@ const sideBarItems = computed(() => props.data.items.map((item) => getItemConfig
watch(
sideBarItems,
(items) => {
services?.uiService.set('sideBarItems', items);
uiService.set('sideBarItems', items);
},
{
immediate: true,
@ -310,10 +310,10 @@ watch(
const nextSlideBarItem = sideBarItems.value.find((sideBarItem) => !showingBoxKeys.value.includes(sideBarItem.$key));
if (!nextSlideBarItem) {
activeTabName.value = '';
services?.uiService.set('hideSlideBar', true);
uiService.set('hideSlideBar', true);
return;
}
services?.uiService.set('hideSlideBar', false);
uiService.set('hideSlideBar', false);
activeTabName.value = nextSlideBarItem?.text;
},
);

View File

@ -35,7 +35,7 @@
</template>
<script lang="ts" setup>
import { computed, inject } from 'vue';
import { computed } from 'vue';
import { Close, Edit, View } from '@element-plus/icons-vue';
import type { Id, MNode } from '@tmagic/core';
@ -46,7 +46,8 @@ import Icon from '@editor/components/Icon.vue';
import Tree from '@editor/components/Tree.vue';
import { useFilter } from '@editor/hooks/use-filter';
import { useNodeStatus } from '@editor/hooks/use-node-status';
import { type CodeBlockListSlots, CodeDeleteErrorType, type Services, type TreeNodeData } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import { type CodeBlockListSlots, CodeDeleteErrorType, type TreeNodeData } from '@editor/type';
defineSlots<CodeBlockListSlots>();
@ -66,19 +67,18 @@ const emit = defineEmits<{
'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
}>();
const services = inject<Services>('services');
const { codeBlockService, depService, editorService } = services || {};
const { codeBlockService, depService, editorService } = useServices();
const collecting = computed(() => depService?.get('collecting'));
const collecting = computed(() => depService.get('collecting'));
//
const codeList = computed<TreeNodeData[]>(() =>
Object.entries(codeBlockService?.getCodeDsl() || {}).map(([codeId, code]) => {
const target = depService?.getTarget(codeId, DepTargetType.CODE_BLOCK);
Object.entries(codeBlockService.getCodeDsl() || {}).map(([codeId, code]) => {
const target = depService.getTarget(codeId, DepTargetType.CODE_BLOCK);
//
const pageList: TreeNodeData[] =
editorService?.get('root')?.items.map((page) => ({
editorService.get('root')?.items.map((page) => ({
name: page.devconfig?.tabName || page.name,
type: 'node',
id: `${codeId}_${page.id}`,
@ -108,7 +108,7 @@ const codeList = computed<TreeNodeData[]>(() =>
key: codeId,
name: code.name,
type: 'code',
codeBlockContent: codeBlockService?.getCodeContentById(codeId),
codeBlockContent: codeBlockService.getCodeContentById(codeId),
//
items: pageList.length > 1 ? pageList.filter((page) => page.items?.length) : pageList[0]?.items || [],
};
@ -127,12 +127,12 @@ const filterNode = (value: string, data: MNode): boolean => {
const { nodeStatusMap } = useNodeStatus(codeList);
const { filterTextChangeHandler } = useFilter(codeList, nodeStatusMap, filterNode);
const editable = computed(() => codeBlockService?.getEditStatus());
const editable = computed(() => codeBlockService.getEditStatus());
//
const selectComp = (compId: Id) => {
const stage = editorService?.get('stage');
editorService?.select(compId);
const stage = editorService.get('stage');
editorService.select(compId);
stage?.select(compId);
};
@ -150,7 +150,7 @@ const editCode = (id: string) => {
const deleteCode = async (id: string) => {
const currentCode = codeList.value.find((codeItem) => codeItem.id === id);
const existBinds = Boolean(currentCode?.items?.length);
const undeleteableList = codeBlockService?.getUndeletableList() || [];
const undeleteableList = codeBlockService.getUndeletableList() || [];
if (!existBinds && !undeleteableList.includes(id)) {
await tMagicMessageBox.confirm('确定删除该代码块吗?', '提示', {
confirmButtonText: '确定',

View File

@ -55,6 +55,7 @@ import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
import ContentMenu from '@editor/components/ContentMenu.vue';
import SearchInput from '@editor/components/SearchInput.vue';
import { useCodeBlockEdit } from '@editor/hooks/use-code-block-edit';
import { useServices } from '@editor/hooks/use-services';
import type {
CodeBlockListPanelSlots,
CodeDeleteErrorType,
@ -62,7 +63,6 @@ import type {
EventBus,
MenuButton,
MenuComponent,
Services,
} from '@editor/type';
import CodeBlockList from './CodeBlockList.vue';
@ -82,9 +82,10 @@ const props = defineProps<{
}>();
const eventBus = inject<EventBus>('eventBus');
const { codeBlockService } = inject<Services>('services') || {};
const editable = computed(() => codeBlockService?.getEditStatus());
const { codeBlockService } = useServices();
const editable = computed(() => codeBlockService.getEditStatus());
const { codeBlockEditor, codeConfig, editCode, deleteCode, createCodeBlock, submitCodeBlockHandler } =
useCodeBlockEdit(codeBlockService);

View File

@ -3,7 +3,7 @@ import { CopyDocument, Delete, Edit } from '@element-plus/icons-vue';
import { cloneDeep } from 'lodash-es';
import ContentMenu from '@editor/components/ContentMenu.vue';
import type { EventBus, MenuButton, MenuComponent, Services, TreeNodeData } from '@editor/type';
import type { EventBus, MenuButton, MenuComponent, TreeNodeData } from '@editor/type';
export const useContentMenu = (deleteCode: (id: string) => void) => {
const eventBus = inject<EventBus>('eventBus');
@ -16,7 +16,7 @@ export const useContentMenu = (deleteCode: (id: string) => void) => {
type: 'button',
text: '编辑',
icon: Edit,
display: (services) => services?.codeBlockService?.getEditStatus() ?? true,
display: ({ codeBlockService }) => codeBlockService.getEditStatus(),
handler: () => {
if (!selectId) {
return;
@ -29,7 +29,7 @@ export const useContentMenu = (deleteCode: (id: string) => void) => {
type: 'button',
text: '复制并粘贴至当前',
icon: markRaw(CopyDocument),
handler: async ({ codeBlockService }: Services) => {
handler: async ({ codeBlockService }) => {
if (!selectId) {
return;
}

View File

@ -29,9 +29,9 @@ import { tMagicMessage } from '@tmagic/design';
import { type ContainerChangeEventData, type FormConfig, MFormBox } from '@tmagic/form';
import FloatingBox from '@editor/components/FloatingBox.vue';
import { useEditorContentHeight } from '@editor/hooks';
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
import type { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
defineOptions({
name: 'MEditorDataSourceConfigPanel',
@ -50,7 +50,7 @@ const emit = defineEmits<{
submit: [v: any, eventData: ContainerChangeEventData];
}>();
const services = inject<Services>('services');
const { uiService, dataSourceService } = useServices();
const initValues = ref<Partial<DataSourceSchema>>({});
const dataSourceConfig = ref<FormConfig>([]);
@ -58,11 +58,11 @@ const dataSourceConfig = ref<FormConfig>([]);
const { height: editorHeight } = useEditorContentHeight();
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating);
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
watchEffect(() => {
initValues.value = props.values;
dataSourceConfig.value = services?.dataSourceService.getFormConfig(initValues.value.type) || [];
dataSourceConfig.value = dataSourceService.getFormConfig(initValues.value.type);
});
const submitHandler = (values: any, data: ContainerChangeEventData) => {

View File

@ -34,7 +34,7 @@
</template>
<script lang="ts" setup>
import { computed, inject } from 'vue';
import { computed } from 'vue';
import { Close, Edit, View } from '@element-plus/icons-vue';
import { DepData, DepTargetType, Id, MNode } from '@tmagic/core';
@ -44,7 +44,8 @@ import Icon from '@editor/components/Icon.vue';
import Tree from '@editor/components/Tree.vue';
import { useFilter } from '@editor/hooks/use-filter';
import { useNodeStatus } from '@editor/hooks/use-node-status';
import type { DataSourceListSlots, Services, TreeNodeData } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { DataSourceListSlots, TreeNodeData } from '@editor/type';
defineSlots<DataSourceListSlots>();
@ -63,17 +64,17 @@ const emit = defineEmits<{
'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
}>();
const { depService, editorService, dataSourceService } = inject<Services>('services') || {};
const { depService, editorService, dataSourceService } = useServices();
const collecting = computed(() => depService?.get('collecting'));
const collecting = computed(() => depService.get('collecting'));
const editable = computed(() => dataSourceService?.get('editable') ?? true);
const editable = computed(() => dataSourceService.get('editable'));
const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
const dataSources = computed(() => dataSourceService.get('dataSources'));
const dsDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE) || {});
const dsMethodDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE_METHOD) || {});
const dsCondDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE_COND) || {});
const dsDep = computed(() => depService.getTargets(DepTargetType.DATA_SOURCE));
const dsMethodDep = computed(() => depService.getTargets(DepTargetType.DATA_SOURCE_METHOD));
const dsCondDep = computed(() => depService.getTargets(DepTargetType.DATA_SOURCE_COND));
const getKeyTreeConfig = (dep: DepData[string], type?: string, parentKey?: Id) =>
dep.keys.map((key) => ({
@ -122,7 +123,7 @@ const list = computed(() =>
const dsCondDeps = dsCondDep.value[ds.id]?.deps || {};
const items =
editorService?.get('root')?.items.map((page) => ({
editorService.get('root')?.items.map((page) => ({
name: page.devconfig?.tabName || page.name,
type: 'node',
id: `${ds.id}_${page.id}`,
@ -166,8 +167,8 @@ const removeHandler = async (id: string) => {
//
const selectComp = (compId: Id) => {
const stage = editorService?.get('stage');
editorService?.select(compId);
const stage = editorService.get('stage');
editorService.select(compId);
stage?.select(compId);
};

View File

@ -68,14 +68,8 @@ import ContentMenu from '@editor/components/ContentMenu.vue';
import SearchInput from '@editor/components/SearchInput.vue';
import ToolButton from '@editor/components/ToolButton.vue';
import { useDataSourceEdit } from '@editor/hooks/use-data-source-edit';
import type {
CustomContentMenuFunction,
DataSourceListSlots,
EventBus,
MenuButton,
MenuComponent,
Services,
} from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { CustomContentMenuFunction, DataSourceListSlots, EventBus, MenuButton, MenuComponent } from '@editor/type';
import DataSourceConfigPanel from './DataSourceConfigPanel.vue';
import DataSourceList from './DataSourceList.vue';
@ -94,7 +88,7 @@ const props = defineProps<{
}>();
const eventBus = inject<EventBus>('eventBus');
const { dataSourceService } = inject<Services>('services') || {};
const { dataSourceService } = useServices();
const { editDialog, dataSourceValues, dialogTitle, editable, editHandler, submitDataSourceHandler } =
useDataSourceEdit(dataSourceService);
@ -103,7 +97,7 @@ const datasourceTypeList = computed(() =>
[
{ text: '基础', type: 'base' },
{ text: 'HTTP', type: 'http' },
].concat(dataSourceService?.get('datasourceTypeList') ?? []),
].concat(dataSourceService.get('datasourceTypeList')),
);
const addHandler = (type: string) => {
@ -113,7 +107,7 @@ const addHandler = (type: string) => {
dataSourceValues.value = mergeWith(
{ type, title: datasourceType?.text },
dataSourceService?.getFormValue(type) || {},
dataSourceService.getFormValue(type),
(objValue, srcValue) => {
if (Array.isArray(srcValue)) {
return srcValue;
@ -133,7 +127,7 @@ const removeHandler = async (id: string) => {
type: 'warning',
});
dataSourceService?.remove(id);
dataSourceService.remove(id);
};
const dataSourceList = ref<InstanceType<typeof DataSourceList>>();

View File

@ -3,7 +3,7 @@ import { CopyDocument, Delete, Edit } from '@element-plus/icons-vue';
import { cloneDeep } from 'lodash-es';
import ContentMenu from '@editor/components/ContentMenu.vue';
import type { EventBus, MenuButton, MenuComponent, Services, TreeNodeData } from '@editor/type';
import type { EventBus, MenuButton, MenuComponent, TreeNodeData } from '@editor/type';
export const useContentMenu = () => {
const eventBus = inject<EventBus>('eventBus');
@ -16,7 +16,7 @@ export const useContentMenu = () => {
type: 'button',
text: '编辑',
icon: Edit,
display: (services) => services?.dataSourceService?.get('editable') ?? true,
display: ({ dataSourceService }) => dataSourceService.get('editable'),
handler: () => {
if (!selectId) {
return;
@ -29,7 +29,7 @@ export const useContentMenu = () => {
type: 'button',
text: '复制并粘贴至当前',
icon: markRaw(CopyDocument),
handler: ({ dataSourceService }: Services) => {
handler: ({ dataSourceService }) => {
if (!selectId) {
return;
}

View File

@ -3,14 +3,15 @@
</template>
<script lang="ts" setup>
import { computed, inject, markRaw, useTemplateRef } from 'vue';
import { computed, markRaw, useTemplateRef } from 'vue';
import { Files, Plus } from '@element-plus/icons-vue';
import { isPage, isPageFragment } from '@tmagic/utils';
import ContentMenu from '@editor/components/ContentMenu.vue';
import { useServices } from '@editor/hooks/use-services';
import FolderMinusIcon from '@editor/icons/FolderMinusIcon.vue';
import type { ComponentGroup, CustomContentMenuFunction, MenuButton, MenuComponent, Services } from '@editor/type';
import type { ComponentGroup, CustomContentMenuFunction, MenuButton, MenuComponent } from '@editor/type';
import { useCopyMenu, useDeleteMenu, useMoveToMenu, usePasteMenu } from '@editor/utils/content-menu';
defineOptions({
@ -26,11 +27,13 @@ const emit = defineEmits<{
'collapse-all': [];
}>();
const services = inject<Services>('services');
const services = useServices();
const { editorService, componentListService } = services;
const menuRef = useTemplateRef<InstanceType<typeof ContentMenu>>('menu');
const node = computed(() => services?.editorService.get('node'));
const nodes = computed(() => services?.editorService.get('nodes'));
const componentList = computed(() => services?.componentListService.getList() || []);
const node = computed(() => editorService.get('node'));
const nodes = computed(() => editorService.get('nodes'));
const componentList = computed(() => componentListService.getList());
const createMenuItems = (group: ComponentGroup): MenuButton[] =>
group.items.map((component) => ({
@ -38,7 +41,7 @@ const createMenuItems = (group: ComponentGroup): MenuButton[] =>
type: 'button',
icon: component.icon,
handler: () => {
services?.editorService.add({
editorService.add({
name: component.text,
type: component.type,
...(component.data || {}),
@ -54,7 +57,7 @@ const getSubMenuData = computed<MenuButton[]>(() => {
type: 'button',
icon: Files,
handler: () => {
services?.editorService.add({
editorService.add({
type: 'tab-pane',
});
},

View File

@ -6,24 +6,20 @@
</template>
<script setup lang="ts">
import { inject } from 'vue';
import { Hide, View } from '@element-plus/icons-vue';
import type { MNode } from '@tmagic/core';
import MIcon from '@editor/components/Icon.vue';
import { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
const props = defineProps<{
data: MNode;
}>();
const services = inject<Services>('services');
const editorService = services?.editorService;
const { editorService } = useServices();
const setNodeVisible = (visible: boolean) => {
if (!editorService) return;
editorService.update({
id: props.data.id,
visible,

View File

@ -47,7 +47,7 @@
</template>
<script setup lang="ts">
import { computed, inject, useTemplateRef } from 'vue';
import { computed, useTemplateRef } from 'vue';
import type { MNode } from '@tmagic/core';
import { TMagicScrollbar } from '@tmagic/design';
@ -55,14 +55,8 @@ import { TMagicScrollbar } from '@tmagic/design';
import SearchInput from '@editor/components/SearchInput.vue';
import Tree from '@editor/components/Tree.vue';
import { useFilter } from '@editor/hooks/use-filter';
import type {
CustomContentMenuFunction,
LayerPanelSlots,
MenuButton,
MenuComponent,
Services,
TreeNodeData,
} from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { CustomContentMenuFunction, LayerPanelSlots, MenuButton, MenuComponent, TreeNodeData } from '@editor/type';
import LayerMenu from './LayerMenu.vue';
import LayerNodeTool from './LayerNodeTool.vue';
@ -84,12 +78,12 @@ defineProps<{
customContentMenu: CustomContentMenuFunction;
}>();
const services = inject<Services>('services');
const editorService = services?.editorService;
const services = useServices();
const { editorService } = services;
const treeRef = useTemplateRef<InstanceType<typeof Tree>>('tree');
const page = computed(() => editorService?.get('page'));
const page = computed(() => editorService.get('page'));
const nodeData = computed<TreeNodeData[]>(() => (!page.value ? [] : [page.value]));
const { nodeStatusMap } = useNodeStatus(services);

View File

@ -4,18 +4,19 @@ import { throttle } from 'lodash-es';
import { Id, MNode } from '@tmagic/core';
import { isPage, isPageFragment } from '@tmagic/utils';
import { LayerNodeStatus, Services, TreeNodeData, UI_SELECT_MODE_EVENT_NAME } from '@editor/type';
import type { LayerNodeStatus, Services, TreeNodeData } from '@editor/type';
import { UI_SELECT_MODE_EVENT_NAME } from '@editor/utils/const';
import { updateStatus } from '@editor/utils/tree';
import LayerMenu from './LayerMenu.vue';
export const useClick = (
services: Services | undefined,
{ editorService, stageOverlayService, uiService }: Services,
isCtrlKeyDown: Ref<boolean>,
nodeStatusMap: ComputedRef<Map<Id, LayerNodeStatus> | undefined>,
menuRef: ShallowRef<InstanceType<typeof LayerMenu> | null>,
) => {
const isMultiSelect = computed(() => isCtrlKeyDown.value && !services?.editorService.get('disabledMultiSelect'));
const isMultiSelect = computed(() => isCtrlKeyDown.value && !editorService.get('disabledMultiSelect'));
// 触发画布选中
const select = async (data: MNode) => {
@ -26,9 +27,9 @@ export const useClick = (
if (isMultiSelect.value) {
multiSelect(data);
} else {
await services?.editorService.select(data);
services?.editorService.get('stage')?.select(data.id);
services?.stageOverlayService.get('stage')?.select(data.id);
await editorService.select(data);
editorService.get('stage')?.select(data.id);
stageOverlayService.get('stage')?.select(data.id);
}
};
@ -37,7 +38,7 @@ export const useClick = (
return;
}
const nodes = services?.editorService.get('nodes') || [];
const nodes = editorService.get('nodes') || [];
const newNodes: Id[] = [];
let isCancel = false;
@ -59,9 +60,9 @@ export const useClick = (
newNodes.push(data.id);
}
await services?.editorService.multiSelect(newNodes);
services?.editorService.get('stage')?.multiSelect(newNodes);
services?.stageOverlayService.get('stage')?.multiSelect(newNodes);
await editorService.multiSelect(newNodes);
editorService.get('stage')?.multiSelect(newNodes);
stageOverlayService.get('stage')?.multiSelect(newNodes);
};
const throttleTime = 300;
@ -75,15 +76,15 @@ export const useClick = (
// 触发画布高亮
const highlight = (data: TreeNodeData) => {
services?.editorService?.highlight(data);
services?.editorService?.get('stage')?.highlight(data.id);
services?.stageOverlayService?.get('stage')?.highlight(data.id);
editorService.highlight(data);
editorService.get('stage')?.highlight(data.id);
stageOverlayService.get('stage')?.highlight(data.id);
};
const nodeClickHandler = (event: MouseEvent, data: TreeNodeData): void => {
if (!nodeStatusMap?.value) return;
if (services?.uiService.get('uiSelectMode')) {
if (uiService.get('uiSelectMode')) {
document.dispatchEvent(new CustomEvent(UI_SELECT_MODE_EVENT_NAME, { detail: data }));
return;
}
@ -107,7 +108,7 @@ export const useClick = (
nodeContentMenuHandler(event: MouseEvent, data: TreeNodeData): void {
event.preventDefault();
const nodes = services?.editorService.get('nodes') || [];
const nodes = editorService.get('nodes') || [];
if (nodes.length < 2 || !nodes.includes(data)) {
nodeClickHandler(event, data);
}

View File

@ -42,7 +42,7 @@ const removeStatusClass = (el: HTMLElement | null) => {
* dragover
* dom事件触发的
*/
export const useDrag = (services: Services | undefined) => {
export const useDrag = ({ editorService }: Services) => {
const handleDragStart = (event: DragEvent) => {
if (!event.dataTransfer || !event.target || !event.currentTarget) return;
@ -144,12 +144,12 @@ export const useDrag = (services: Services | undefined) => {
removeStatusClass(dragState.container);
if (node && dragState.dragOverNodeId && dragState.dropType && services) {
if (node && dragState.dragOverNodeId && dragState.dropType) {
if (dragState.dragOverNodeId === node.id) {
return;
}
const targetInfo = services.editorService.getNodeInfo(dragState.dragOverNodeId, false);
const targetInfo = editorService.getNodeInfo(dragState.dragOverNodeId, false);
const targetNode = targetInfo.node;
let targetParent = targetInfo.parent;
@ -168,12 +168,12 @@ export const useDrag = (services: Services | undefined) => {
targetIndex += 1;
}
const selectedNodes = services.editorService.get('nodes');
const selectedNodes = editorService.get('nodes');
if (selectedNodes.find((n) => `${n.id}` === `${node.id}`)) {
services.editorService.dragTo(selectedNodes, targetParent, targetIndex);
editorService.dragTo(selectedNodes, targetParent, targetIndex);
} else {
services.editorService.dragTo([node], targetParent, targetIndex);
editorService.dragTo([node], targetParent, targetIndex);
}
}

View File

@ -5,11 +5,9 @@ import type { Services } from '@editor/type';
import { KeyBindingContainerKey } from '@editor/utils/keybinding-config';
export const useKeybinding = (
services: Services | undefined,
{ keybindingService }: Services,
container: ShallowRef<InstanceType<typeof Tree> | null>,
) => {
const keybindingService = services?.keybindingService;
// 是否多选
const isCtrlKeyDown = ref(false);
@ -17,15 +15,15 @@ export const useKeybinding = (
isCtrlKeyDown.value = false;
};
keybindingService?.registerCommand('layer-panel-global-keyup', () => {
keybindingService.registerCommand('layer-panel-global-keyup', () => {
isCtrlKeyDown.value = false;
});
keybindingService?.registerCommand('layer-panel-global-keydown', () => {
keybindingService.registerCommand('layer-panel-global-keydown', () => {
isCtrlKeyDown.value = true;
});
keybindingService?.register([
keybindingService.register([
{
command: 'layer-panel-global-keydown',
keybinding: 'ctrl',
@ -41,10 +39,10 @@ export const useKeybinding = (
watchEffect(() => {
if (container.value) {
globalThis.addEventListener('blur', windowBlurHandler);
keybindingService?.registerEl(KeyBindingContainerKey.LAYER_PANEL, container.value.$el);
keybindingService.registerEl(KeyBindingContainerKey.LAYER_PANEL, container.value.$el);
} else {
globalThis.removeEventListener('blur', windowBlurHandler);
keybindingService?.unregisterEl(KeyBindingContainerKey.LAYER_PANEL);
keybindingService.unregisterEl(KeyBindingContainerKey.LAYER_PANEL);
}
});

View File

@ -1,4 +1,4 @@
import { computed, ref, watch } from 'vue';
import { computed, onBeforeUnmount, ref, watch } from 'vue';
import type { Id, MNode, MPage, MPageFragment } from '@tmagic/core';
import { getNodePath, isPage, isPageFragment, traverseNode } from '@tmagic/utils';
@ -33,9 +33,9 @@ const createPageNodeStatus = (page: MPage | MPageFragment, initialLayerNodeStatu
return map;
};
export const useNodeStatus = (services: Services | undefined) => {
const page = computed(() => services?.editorService.get('page'));
const nodes = computed(() => services?.editorService.get('nodes') || []);
export const useNodeStatus = ({ editorService }: Services) => {
const page = computed(() => editorService.get('page'));
const nodes = computed(() => editorService.get('nodes'));
/** 所有页面的节点状态 */
const nodeStatusMaps = ref(new Map<Id, Map<Id, LayerNodeStatus>>());
@ -83,7 +83,7 @@ export const useNodeStatus = (services: Services | undefined) => {
},
);
services?.editorService.on('add', (newNodes: MNode[]) => {
const addHandler = (newNodes: MNode[]) => {
newNodes.forEach((node) => {
if (isPage(node) || isPageFragment(node)) return;
@ -96,14 +96,23 @@ export const useNodeStatus = (services: Services | undefined) => {
});
});
});
});
};
services?.editorService.on('remove', (nodes: MNode[]) => {
editorService.on('add', addHandler);
const removeHandler = (nodes: MNode[]) => {
nodes.forEach((node) => {
traverseNode(node, (node: MNode) => {
nodeStatusMap.value?.delete(node.id);
});
});
};
editorService.on('remove', removeHandler);
onBeforeUnmount(() => {
editorService.off('remove', removeHandler);
editorService.off('add', addHandler);
});
return {

View File

@ -8,28 +8,27 @@
</template>
<script setup lang="ts">
import { computed, inject } from 'vue';
import { computed } from 'vue';
import type { MNode } from '@tmagic/core';
import { TMagicButton } from '@tmagic/design';
import { getNodePath } from '@tmagic/utils';
import type { Services } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
defineOptions({
name: 'MEditorBreadcrumb',
});
const services = inject<Services>('services');
const editorService = services?.editorService;
const { editorService } = useServices();
const node = computed(() => editorService?.get('node'));
const nodes = computed(() => editorService?.get('nodes') || []);
const root = computed(() => editorService?.get('root'));
const node = computed(() => editorService.get('node'));
const nodes = computed(() => editorService.get('nodes'));
const root = computed(() => editorService.get('root'));
const path = computed(() => getNodePath(node.value?.id || '', root.value?.items || []));
const select = async (node: MNode) => {
await editorService?.select(node);
editorService?.get('stage')?.select(node.id);
await editorService.select(node);
editorService.get('stage')?.select(node.id);
};
</script>

View File

@ -19,14 +19,8 @@
<script lang="ts" setup>
import { computed, inject } from 'vue';
import type {
CustomContentMenuFunction,
MenuButton,
MenuComponent,
Services,
StageOptions,
WorkspaceSlots,
} from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { CustomContentMenuFunction, MenuButton, MenuComponent, StageOptions, WorkspaceSlots } from '@editor/type';
import MagicStage from './viewer/Stage.vue';
import Breadcrumb from './Breadcrumb.vue';
@ -50,7 +44,7 @@ withDefaults(
const stageOptions = inject<StageOptions>('stageOptions');
const services = inject<Services>('services');
const { editorService } = useServices();
const page = computed(() => services?.editorService.get('page'));
const page = computed(() => editorService.get('page'));
</script>

View File

@ -22,7 +22,7 @@
</template>
<script lang="ts" setup>
import { computed, inject, nextTick, ref, useTemplateRef, watch } from 'vue';
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue';
import type { MNode } from '@tmagic/core';
import { TMagicTooltip } from '@tmagic/design';
@ -31,20 +31,21 @@ import { getIdFromEl } from '@tmagic/utils';
import FloatingBox from '@editor/components/FloatingBox.vue';
import Tree from '@editor/components/Tree.vue';
import { useFilter } from '@editor/hooks/use-filter';
import { useServices } from '@editor/hooks/use-services';
import { useNodeStatus } from '@editor/layouts/sidebar/layer/use-node-status';
import type { Services, TreeNodeData } from '@editor/type';
import type { TreeNodeData } from '@editor/type';
const services = inject<Services>('services');
const editorService = services?.editorService;
const services = useServices();
const { editorService } = services;
const visible = ref(false);
const buttonVisible = ref(false);
const buttonEl = useTemplateRef<HTMLDivElement>('button');
const boxRef = useTemplateRef<InstanceType<typeof FloatingBox>>('box');
const stage = computed(() => editorService?.get('stage'));
const page = computed(() => editorService?.get('page'));
const nodes = computed(() => editorService?.get('nodes') || []);
const stage = computed(() => editorService.get('stage'));
const page = computed(() => editorService.get('page'));
const nodes = computed(() => editorService.get('nodes'));
const nodeData = computed<TreeNodeData[]>(() => (!page.value ? [] : [page.value]));
const { nodeStatusMap } = useNodeStatus(services);
@ -89,7 +90,7 @@ watch(
);
const clickHandler = async (event: MouseEvent, data: TreeNodeData) => {
await editorService?.select(data.id);
await editorService.select(data.id);
stage.value?.select(data.id);
};

View File

@ -45,7 +45,6 @@
<script lang="ts" setup>
import {
computed,
inject,
markRaw,
nextTick,
onBeforeUnmount,
@ -62,8 +61,9 @@ import StageCore, { getOffset, Runtime } from '@tmagic/stage';
import { calcValueByFontsize, getIdFromEl } from '@tmagic/utils';
import ScrollViewer from '@editor/components/ScrollViewer.vue';
import { useServices } from '@editor/hooks';
import { useStage } from '@editor/hooks/use-stage';
import type { CustomContentMenuFunction, MenuButton, MenuComponent, Services, StageOptions } from '@editor/type';
import type { CustomContentMenuFunction, MenuButton, MenuComponent, StageOptions } from '@editor/type';
import { DragType, Layout } from '@editor/type';
import { getEditorConfig } from '@editor/utils/config';
import { KeyBindingContainerKey } from '@editor/utils/keybinding-config';
@ -91,22 +91,22 @@ const props = withDefaults(
let stage: StageCore | null = null;
let runtime: Runtime | null = null;
const services = inject<Services>('services');
const { editorService, uiService, keybindingService } = useServices();
const stageLoading = computed(() => services?.editorService.get('stageLoading') || false);
const stageLoading = computed(() => editorService.get('stageLoading'));
const stageWrapRef = useTemplateRef<InstanceType<typeof ScrollViewer>>('stageWrap');
const stageContainerEl = useTemplateRef<HTMLDivElement>('stageContainer');
const menuRef = useTemplateRef<InstanceType<typeof ViewerMenu>>('menu');
const nodes = computed(() => services?.editorService.get('nodes') || []);
const nodes = computed(() => editorService.get('nodes'));
const isMultiSelect = computed(() => nodes.value.length > 1);
const stageRect = computed(() => services?.uiService.get('stageRect'));
const stageContainerRect = computed(() => services?.uiService.get('stageContainerRect'));
const root = computed(() => services?.editorService.get('root'));
const page = computed(() => services?.editorService.get('page'));
const zoom = computed(() => services?.uiService.get('zoom') || 1);
const node = computed(() => services?.editorService.get('node'));
const stageRect = computed(() => uiService.get('stageRect'));
const stageContainerRect = computed(() => uiService.get('stageContainerRect'));
const root = computed(() => editorService.get('root'));
const page = computed(() => editorService.get('page'));
const zoom = computed(() => uiService.get('zoom'));
const node = computed(() => editorService.get('node'));
watchEffect(() => {
if (stage || !page.value) return;
@ -120,7 +120,7 @@ watchEffect(() => {
stageWrapRef.value?.container?.focus();
});
services?.editorService.set('stage', markRaw(stage));
editorService.set('stage', markRaw(stage));
stage.mount(stageContainerEl.value);
@ -141,7 +141,7 @@ watchEffect(() => {
onBeforeUnmount(() => {
stage?.destroy();
services?.editorService.set('stage', null);
editorService.set('stage', null);
});
watch(zoom, (zoom) => {
@ -152,14 +152,14 @@ watch(zoom, (zoom) => {
let timeoutId: NodeJS.Timeout | null = null;
watch(page, (page) => {
if (runtime && page) {
services?.editorService.set('stageLoading', true);
editorService.set('stageLoading', true);
if (timeoutId) {
globalThis.clearTimeout(timeoutId);
}
timeoutId = globalThis.setTimeout(() => {
services?.editorService.set('stageLoading', false);
editorService.set('stageLoading', false);
timeoutId = null;
}, 3000);
@ -176,11 +176,11 @@ const rootChangeHandler = (root: MApp) => {
}
};
services?.editorService.on('root-change', rootChangeHandler);
editorService.on('root-change', rootChangeHandler);
const resizeObserver = new ResizeObserver((entries) => {
for (const { contentRect } of entries) {
services?.uiService.set('stageContainerRect', {
uiService.set('stageContainerRect', {
width: contentRect.width,
height: contentRect.height,
});
@ -190,16 +190,16 @@ const resizeObserver = new ResizeObserver((entries) => {
onMounted(() => {
if (stageWrapRef.value?.container) {
resizeObserver.observe(stageWrapRef.value.container);
services?.keybindingService.registerEl(KeyBindingContainerKey.STAGE, stageWrapRef.value.container);
keybindingService.registerEl(KeyBindingContainerKey.STAGE, stageWrapRef.value.container);
}
});
onBeforeUnmount(() => {
stage?.destroy();
resizeObserver.disconnect();
services?.editorService.set('stage', null);
services?.keybindingService.unregisterEl('stage');
services?.editorService.off('root-change', rootChangeHandler);
editorService.set('stage', null);
keybindingService.unregisterEl('stage');
editorService.off('root-change', rootChangeHandler);
});
const parseDSL = getEditorConfig('parseDSL');
@ -236,11 +236,11 @@ const dropHandler = async (e: DragEvent) => {
let parent: MContainer | undefined | null = page.value;
const parentId = getIdFromEl()(parentEl);
if (parentId) {
parent = services?.editorService.getNodeById(parentId, false) as MContainer;
parent = editorService.getNodeById(parentId, false) as MContainer;
}
if (parent && stageContainerEl.value && stage) {
const layout = await services?.editorService.getLayout(parent);
const layout = await editorService.getLayout(parent);
const containerRect = stageContainerEl.value.getBoundingClientRect();
const { scrollTop, scrollLeft } = stage.mask!;
@ -275,7 +275,7 @@ const dropHandler = async (e: DragEvent) => {
config.data.inputEvent = e;
services?.editorService.add(config.data, parent);
editorService.add(config.data, parent);
}
};
</script>

View File

@ -13,18 +13,19 @@ import { CloseBold } from '@element-plus/icons-vue';
import { TMagicIcon } from '@tmagic/design';
import type { Services, StageOptions } from '@editor/type';
import { useServices } from '@editor/hooks/use-services';
import type { StageOptions } from '@editor/type';
const services = inject<Services>('services');
const { stageOverlayService, editorService } = useServices();
const stageOptions = inject<StageOptions>('stageOptions');
const stageOverlayEl = useTemplateRef<HTMLDivElement>('stageOverlay');
const stageOverlayVisible = computed(() => services?.stageOverlayService.get('stageOverlayVisible'));
const wrapWidth = computed(() => services?.stageOverlayService.get('wrapWidth') || 0);
const wrapHeight = computed(() => services?.stageOverlayService.get('wrapHeight') || 0);
const stage = computed(() => services?.editorService.get('stage'));
const stageOverlayVisible = computed(() => stageOverlayService.get('stageOverlayVisible'));
const wrapWidth = computed(() => stageOverlayService.get('wrapWidth'));
const wrapHeight = computed(() => stageOverlayService.get('wrapHeight'));
const stage = computed(() => editorService.get('stage'));
const style = computed(() => ({
width: `${wrapWidth.value}px`,
@ -35,18 +36,16 @@ watch(stage, (stage) => {
if (stage) {
stage.on('dblclick', async (event: MouseEvent) => {
const el = (await stage.actionManager?.getElementFromPoint(event)) || null;
services?.stageOverlayService.openOverlay(el);
stageOverlayService.openOverlay(el);
});
} else {
services?.stageOverlayService.closeOverlay();
stageOverlayService.closeOverlay();
}
});
watch(stageOverlayEl, (stageOverlay) => {
if (!services) return;
const subStage = services.stageOverlayService.createStage(stageOptions);
services?.stageOverlayService.set('stage', subStage);
const subStage = stageOverlayService.createStage(stageOptions);
stageOverlayService.set('stage', subStage);
if (stageOverlay && subStage) {
subStage.mount(stageOverlay);
@ -56,18 +55,18 @@ watch(stageOverlayEl, (stageOverlay) => {
const { contentWindow } = renderer!;
mask?.showRule(false);
services?.stageOverlayService.updateOverlay();
stageOverlayService.updateOverlay();
contentWindow?.magic.onRuntimeReady({});
}
});
onBeforeUnmount(() => {
services?.stageOverlayService.get('stage')?.destroy();
services?.stageOverlayService.set('stage', null);
stageOverlayService.get('stage')?.destroy();
stageOverlayService.set('stage', null);
});
const closeOverlayHandler = () => {
services?.stageOverlayService.closeOverlay();
stageOverlayService.closeOverlay();
};
</script>

View File

@ -3,15 +3,16 @@
</template>
<script lang="ts" setup>
import { computed, inject, markRaw, ref, useTemplateRef, watch } from 'vue';
import { computed, markRaw, ref, useTemplateRef, watch } from 'vue';
import { Bottom, Top } from '@element-plus/icons-vue';
import { NodeType } from '@tmagic/core';
import { isPage, isPageFragment } from '@tmagic/utils';
import ContentMenu from '@editor/components/ContentMenu.vue';
import { useServices } from '@editor/hooks/use-services';
import CenterIcon from '@editor/icons/CenterIcon.vue';
import { CustomContentMenuFunction, LayerOffset, Layout, MenuButton, MenuComponent, Services } from '@editor/type';
import { CustomContentMenuFunction, LayerOffset, Layout, MenuButton, MenuComponent } from '@editor/type';
import { useCopyMenu, useDeleteMenu, useMoveToMenu, usePasteMenu } from '@editor/utils/content-menu';
defineOptions({
@ -29,14 +30,14 @@ const props = withDefaults(
},
);
const services = inject<Services>('services');
const editorService = services?.editorService;
const services = useServices();
const { editorService } = services;
const menuRef = useTemplateRef<InstanceType<typeof ContentMenu>>('menu');
const canCenter = ref(false);
const node = computed(() => editorService?.get('node'));
const nodes = computed(() => editorService?.get('nodes'));
const parent = computed(() => editorService?.get('parent'));
const node = computed(() => editorService.get('node'));
const nodes = computed(() => editorService.get('nodes'));
const parent = computed(() => editorService.get('parent'));
const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
props.customContentMenu(
@ -48,7 +49,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
display: () => canCenter.value,
handler: () => {
if (!nodes.value) return;
editorService?.alignCenter(nodes.value);
editorService.alignCenter(nodes.value);
},
},
useCopyMenu(),
@ -67,7 +68,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
icon: markRaw(Top),
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
handler: () => {
editorService?.moveLayer(1);
editorService.moveLayer(1);
},
},
{
@ -76,7 +77,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
icon: markRaw(Bottom),
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
handler: () => {
editorService?.moveLayer(-1);
editorService.moveLayer(-1);
},
},
{
@ -85,7 +86,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
icon: markRaw(Top),
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
handler: () => {
editorService?.moveLayer(LayerOffset.TOP);
editorService.moveLayer(LayerOffset.TOP);
},
},
{
@ -94,7 +95,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
icon: markRaw(Bottom),
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
handler: () => {
editorService?.moveLayer(LayerOffset.BOTTOM);
editorService.moveLayer(LayerOffset.BOTTOM);
},
},
useMoveToMenu(services),
@ -112,7 +113,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
type: 'button',
text: '清空参考线',
handler: () => {
editorService?.get('stage')?.clearGuides();
editorService.get('stage')?.clearGuides();
},
},
...props.stageContentMenu,
@ -124,7 +125,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
watch(
parent,
async () => {
if (!parent.value || !editorService) return (canCenter.value = false);
if (!parent.value) return (canCenter.value = false);
const layout = await editorService.getLayout(parent.value);
const isLayoutConform = [Layout.ABSOLUTE, Layout.FIXED].includes(layout);
const isTypeConform = nodes.value?.every(

View File

@ -321,9 +321,9 @@ export interface MenuButton {
/** Vue组件或url */
icon?: string | Component<{}, {}, any>;
/** 是否置灰默认为false */
disabled?: boolean | ((data?: Services) => boolean);
disabled?: boolean | ((data: Services) => boolean);
/** 是否显示默认为true */
display?: boolean | ((data?: Services) => boolean);
display?: boolean | ((data: Services) => boolean);
/** type为button/dropdown时点击运行的方法 */
handler?: (data: Services, event: MouseEvent) => Promise<any> | any;
className?: string;
@ -344,7 +344,7 @@ export interface MenuComponent {
slots?: Record<string, any>;
/** 是否显示默认为true */
className?: string;
display?: boolean | ((data?: Services) => Promise<boolean> | boolean);
display?: boolean | ((data: Services) => Promise<boolean> | boolean);
[key: string]: any;
}
@ -473,9 +473,6 @@ export enum Keys {
ESCAPE = 'Space',
}
export const H_GUIDE_LINE_STORAGE_KEY = '$MagicStageHorizontalGuidelinesData';
export const V_GUIDE_LINE_STORAGE_KEY = '$MagicStageVerticalGuidelinesData';
export interface ScrollViewerEvent {
scrollLeft: number;
scrollTop: number;
@ -721,9 +718,6 @@ export enum DragType {
LAYER_TREE = 'layer-tree',
}
/** 当uiService.get('uiSelectMode')为true,点击组件(包括任何形式,组件树/画布)时触发的事件名 */
export const UI_SELECT_MODE_EVENT_NAME = 'ui-select';
export interface TreeNodeData {
id: Id;
name?: string;

View File

@ -1,5 +1,11 @@
/** 当uiService.get('uiSelectMode')为true,点击组件(包括任何形式,组件树/画布)时触发的事件名 */
export const UI_SELECT_MODE_EVENT_NAME = 'ui-select';
export const LEFT_COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorLeftColumnWidthData';
export const RIGHT_COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorRightColumnWidthData';
export const DEFAULT_LEFT_COLUMN_WIDTH = 310;
export const DEFAULT_RIGHT_COLUMN_WIDTH = 480;
export const H_GUIDE_LINE_STORAGE_KEY = '$MagicStageHorizontalGuidelinesData';
export const V_GUIDE_LINE_STORAGE_KEY = '$MagicStageVerticalGuidelinesData';

View File

@ -13,13 +13,13 @@ export const useDeleteMenu = (): MenuButton => ({
type: 'button',
text: '删除',
icon: Delete,
display: (services) => {
const node = services?.editorService?.get('node');
display: ({ editorService }) => {
const node = editorService.get('node');
return node?.type !== NodeType.ROOT && !isPage(node) && !isPageFragment(node);
},
handler: (services) => {
const nodes = services?.editorService?.get('nodes');
nodes && services?.editorService?.remove(nodes);
handler: ({ editorService }) => {
const nodes = editorService.get('nodes');
nodes && editorService.remove(nodes);
},
});
@ -27,9 +27,9 @@ export const useCopyMenu = (): MenuButton => ({
type: 'button',
text: '复制',
icon: markRaw(CopyDocument),
handler: (services) => {
const nodes = services?.editorService?.get('nodes');
nodes && services?.editorService?.copy(nodes);
handler: ({ editorService }) => {
const nodes = editorService?.get('nodes');
nodes && editorService?.copy(nodes);
},
});
@ -37,57 +37,55 @@ export const usePasteMenu = (menu?: ShallowRef<InstanceType<typeof ContentMenu>
type: 'button',
text: '粘贴',
icon: markRaw(DocumentCopy),
display: (services) => !!services?.storageService?.getItem(COPY_STORAGE_KEY),
handler: (services) => {
const nodes = services?.editorService?.get('nodes');
display: ({ storageService }) => !!storageService.getItem(COPY_STORAGE_KEY),
handler: ({ editorService, uiService }) => {
const nodes = editorService?.get('nodes');
if (!nodes || nodes.length === 0) return;
if (menu?.value?.$el) {
const stage = services?.editorService?.get('stage');
const stage = editorService.get('stage');
const rect = menu.value.$el.getBoundingClientRect();
const parentRect = stage?.container?.getBoundingClientRect();
const initialLeft =
calcValueByFontsize(stage?.renderer?.getDocument(), (rect.left || 0) - (parentRect?.left || 0)) /
services.uiService.get('zoom');
uiService.get('zoom');
const initialTop =
calcValueByFontsize(stage?.renderer?.getDocument(), (rect.top || 0) - (parentRect?.top || 0)) /
services.uiService.get('zoom');
services?.editorService?.paste({ left: initialLeft, top: initialTop });
uiService.get('zoom');
editorService.paste({ left: initialLeft, top: initialTop });
} else {
services?.editorService?.paste();
editorService.paste();
}
},
});
const moveTo = (id: Id, services?: Services) => {
if (!services?.editorService) return;
const nodes = services.editorService.get('nodes') || [];
const parent = services.editorService.getNodeById(id) as MContainer;
const moveTo = (id: Id, { editorService }: Services) => {
const nodes = editorService.get('nodes') || [];
const parent = editorService.getNodeById(id) as MContainer;
if (!parent) return;
services?.editorService.add(nodes, parent);
services?.editorService.remove(nodes);
editorService.add(nodes, parent);
editorService.remove(nodes);
};
export const useMoveToMenu = (services?: Services): MenuButton => {
const root = computed(() => services?.editorService?.get('root'));
export const useMoveToMenu = ({ editorService }: Services): MenuButton => {
const root = computed(() => editorService.get('root'));
return {
type: 'button',
text: '移动至',
display: (services) => {
const node = services?.editorService?.get('node');
const pageLength = services?.editorService?.get('pageLength') || 0;
display: ({ editorService }) => {
const node = editorService.get('node');
const pageLength = editorService.get('pageLength');
return !isPage(node) && pageLength > 1;
},
items: (root.value?.items || [])
.filter((page) => page.id !== services?.editorService?.get('page')?.id)
.filter((page) => page.id !== editorService.get('page')?.id)
.map((page) => ({
text: `${page.name}(${page.id})`,
type: 'button',
handler: (services?: Services) => {
handler: (services: Services) => {
moveTo(page.id, services);
},
})),

View File

@ -26,3 +26,4 @@ export * from './idle-task';
export * from './scroll-viewer';
export * from './tree';
export * from './undo-redo';
export * from './const';