mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
chore(editor): 重构tool-button,将功能逻辑移到nav-menu中
This commit is contained in:
parent
ca5e4d2702
commit
4872e5352b
@ -8,7 +8,7 @@
|
|||||||
"build": "npm run clean:top && vuepress build src -d dist"
|
"build": "npm run clean:top && vuepress build src -d dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.0.6",
|
"@element-plus/icons-vue": "^2.0.9",
|
||||||
"@tmagic/form": "1.1.0-beta.12",
|
"@tmagic/form": "1.1.0-beta.12",
|
||||||
"@tmagic/schema": "1.1.0-beta.12",
|
"@tmagic/schema": "1.1.0-beta.12",
|
||||||
"@tmagic/utils": "1.1.0-beta.12",
|
"@tmagic/utils": "1.1.0-beta.12",
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.18.0",
|
"@babel/core": "^7.18.0",
|
||||||
"@element-plus/icons-vue": "^2.0.6",
|
"@element-plus/icons-vue": "^2.0.9",
|
||||||
"@tmagic/core": "1.1.0-beta.12",
|
"@tmagic/core": "1.1.0-beta.12",
|
||||||
"@tmagic/form": "1.1.0-beta.12",
|
"@tmagic/form": "1.1.0-beta.12",
|
||||||
"@tmagic/schema": "1.1.0-beta.12",
|
"@tmagic/schema": "1.1.0-beta.12",
|
||||||
|
@ -64,7 +64,7 @@ import historyService from './services/history';
|
|||||||
import propsService from './services/props';
|
import propsService from './services/props';
|
||||||
import storageService from './services/storage';
|
import storageService from './services/storage';
|
||||||
import uiService from './services/ui';
|
import uiService from './services/ui';
|
||||||
import type { ComponentGroup, MenuBarData, MenuItem, Services, SideBarData, StageRect } from './type';
|
import type { ComponentGroup, MenuBarData, MenuButton, MenuComponent, Services, SideBarData, StageRect } from './type';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'm-editor',
|
name: 'm-editor',
|
||||||
@ -97,12 +97,12 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
layerContentMenu: {
|
layerContentMenu: {
|
||||||
type: Array as PropType<MenuItem[]>,
|
type: Array as PropType<(MenuButton | MenuComponent)[]>,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
|
||||||
stageContentMenu: {
|
stageContentMenu: {
|
||||||
type: Array as PropType<MenuItem[]>,
|
type: Array as PropType<(MenuButton | MenuComponent)[]>,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -25,13 +25,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted, onUnmounted, ref } from 'vue';
|
import { nextTick, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
|
||||||
import { MenuButton, MenuItem } from '../type';
|
import { MenuButton, MenuComponent } from '../type';
|
||||||
|
|
||||||
import ToolButton from './ToolButton.vue';
|
import ToolButton from './ToolButton.vue';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
menuData?: MenuItem[];
|
menuData?: (MenuButton | MenuComponent)[];
|
||||||
isSubMenu?: boolean;
|
isSubMenu?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
@ -45,7 +45,7 @@ const emit = defineEmits(['hide', 'show']);
|
|||||||
const menu = ref<HTMLDivElement>();
|
const menu = ref<HTMLDivElement>();
|
||||||
const subMenu = ref<any>();
|
const subMenu = ref<any>();
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const subMenuData = ref<MenuItem[]>([]);
|
const subMenuData = ref<(MenuButton | MenuComponent)[]>([]);
|
||||||
const menuStyle = ref({
|
const menuStyle = ref({
|
||||||
left: '0',
|
left: '0',
|
||||||
top: '0',
|
top: '0',
|
||||||
@ -94,7 +94,7 @@ const show = (e: MouseEvent) => {
|
|||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
const showSubMenu = (item: MenuItem) => {
|
const showSubMenu = (item: MenuButton | MenuComponent) => {
|
||||||
const menuItem = item as MenuButton;
|
const menuItem = item as MenuButton;
|
||||||
if (typeof item !== 'object' || !menuItem.items?.length) {
|
if (typeof item !== 'object' || !menuItem.items?.length) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,29 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-icon v-if="!icon"><edit></edit></el-icon>
|
<el-icon v-if="!icon"><Edit></Edit></el-icon>
|
||||||
<el-icon v-else-if="typeof icon === 'string' && icon.startsWith('http')"><img :src="icon" /></el-icon>
|
<el-icon v-else-if="typeof icon === 'string' && icon.startsWith('http')"><img :src="icon" /></el-icon>
|
||||||
<i v-else-if="typeof icon === 'string'" :class="icon"></i>
|
<i v-else-if="typeof icon === 'string'" :class="icon"></i>
|
||||||
<el-icon v-else><component :is="toRaw(icon)"></component></el-icon>
|
<el-icon v-else><component :is="toRaw(icon)"></component></el-icon>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { Component, defineComponent, PropType, toRaw } from 'vue';
|
import { toRaw } from 'vue';
|
||||||
import { Edit } from '@element-plus/icons-vue';
|
import { Edit } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
export default defineComponent({
|
defineProps<{
|
||||||
name: 'm-icon',
|
icon?: any;
|
||||||
|
}>();
|
||||||
components: { Edit },
|
|
||||||
|
|
||||||
props: {
|
|
||||||
icon: {
|
|
||||||
type: [String, Object] as PropType<string | Component>,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
return {
|
|
||||||
toRaw,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -2,169 +2,80 @@
|
|||||||
<div
|
<div
|
||||||
v-if="display"
|
v-if="display"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="item.type"
|
:class="`${data.type} ${data.className}`"
|
||||||
@click="clickHandler(item, $event)"
|
@click="clickHandler(data, $event)"
|
||||||
@mousedown="mousedownHandler(item, $event)"
|
@mousedown="mousedownHandler(data, $event)"
|
||||||
@mouseup="mouseupHandler(item, $event)"
|
@mouseup="mouseupHandler(data, $event)"
|
||||||
>
|
>
|
||||||
<el-divider v-if="item.type === 'divider'" :direction="item.direction || 'vertical'"></el-divider>
|
<el-divider v-if="data.type === 'divider'" :direction="data.direction || 'vertical'"></el-divider>
|
||||||
<div v-else-if="item.type === 'text'" class="menu-item-text">{{ item.text }}</div>
|
<div v-else-if="data.type === 'text'" class="menu-item-text">{{ data.text }}</div>
|
||||||
|
|
||||||
<template v-else-if="item.type === 'zoom'">
|
<template v-else-if="data.type === 'button'">
|
||||||
<tool-button
|
<el-tooltip v-if="data.tooltip" effect="dark" placement="bottom-start" :content="data.tooltip">
|
||||||
:data="{ type: 'button', icon: ZoomOut, handler: zoomOutHandler, tooltip: '缩小' }"
|
|
||||||
:event-type="eventType"
|
|
||||||
></tool-button>
|
|
||||||
<span class="menu-item-text" style="margin: 0 5px">{{ parseInt(`${zoom * 100}`, 10) }}%</span>
|
|
||||||
<tool-button
|
|
||||||
:data="{ type: 'button', icon: ZoomIn, handler: zoomInHandler, tooltip: '放大' }"
|
|
||||||
:event-type="eventType"
|
|
||||||
></tool-button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else-if="item.type === 'button'">
|
|
||||||
<el-tooltip v-if="item.tooltip" effect="dark" placement="bottom-start" :content="item.tooltip">
|
|
||||||
<el-button size="small" text :disabled="disabled"
|
<el-button size="small" text :disabled="disabled"
|
||||||
><m-icon v-if="item.icon" :icon="item.icon"></m-icon><span>{{ item.text }}</span></el-button
|
><MIcon v-if="data.icon" :icon="data.icon"></MIcon><span>{{ data.text }}</span></el-button
|
||||||
>
|
>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-button v-else size="small" text :disabled="disabled"
|
<el-button v-else size="small" text :disabled="disabled"
|
||||||
><m-icon v-if="item.icon" :icon="item.icon"></m-icon><span>{{ item.text }}</span></el-button
|
><MIcon v-if="data.icon" :icon="data.icon"></MIcon><span>{{ data.text }}</span></el-button
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-dropdown v-else-if="item.type === 'dropdown'" trigger="click" :disabled="disabled" @command="dropdownHandler">
|
<el-dropdown v-else-if="data.type === 'dropdown'" trigger="click" :disabled="disabled" @command="dropdownHandler">
|
||||||
<span class="el-dropdown-link menubar-menu-button">
|
<span class="el-dropdown-link menubar-menu-button">
|
||||||
{{ item.text }}<el-icon class="el-icon--right"><arrow-down></arrow-down></el-icon>
|
{{ data.text }}<el-icon class="el-icon--right"><ArrowDown></ArrowDown></el-icon>
|
||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu v-if="item.items && item.items.length">
|
<el-dropdown-menu v-if="data.items && data.items.length">
|
||||||
<el-dropdown-item v-for="(subItem, index) in item.items" :key="index" :command="{ item, subItem }">{{
|
<el-dropdown-item v-for="(subItem, index) in data.items" :key="index" :command="{ data, subItem }">{{
|
||||||
subItem.text
|
subItem.text
|
||||||
}}</el-dropdown-item>
|
}}</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
|
|
||||||
<component v-else-if="item.type === 'component'" v-bind="item.props || {}" :is="item.component"></component>
|
<component v-else-if="data.type === 'component'" v-bind="data.props || {}" :is="data.component"></component>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject, markRaw, PropType } from 'vue';
|
import { computed, inject } from 'vue';
|
||||||
import { ArrowDown, Back, Delete, Grid, Right, ScaleToOriginal, ZoomIn, ZoomOut } from '@element-plus/icons-vue';
|
import { ArrowDown } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
import { NodeType } from '@tmagic/schema';
|
|
||||||
|
|
||||||
import MIcon from '../components/Icon.vue';
|
import MIcon from '../components/Icon.vue';
|
||||||
import type { MenuButton, MenuComponent, MenuItem, Services } from '../type';
|
import type { MenuButton, MenuComponent, Services } from '../type';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = withDefaults(
|
||||||
data: {
|
defineProps<{
|
||||||
type: [Object, String] as PropType<MenuItem | string>,
|
data?: MenuButton | MenuComponent;
|
||||||
require: true,
|
eventType?: 'mousedown' | 'mouseup' | 'click';
|
||||||
default: () => ({
|
}>(),
|
||||||
|
{
|
||||||
|
data: () => ({
|
||||||
type: 'text',
|
type: 'text',
|
||||||
display: false,
|
display: false,
|
||||||
}),
|
}),
|
||||||
|
eventType: 'click',
|
||||||
},
|
},
|
||||||
|
);
|
||||||
eventType: {
|
|
||||||
type: String as PropType<'mousedown' | 'mouseup' | 'click'>,
|
|
||||||
default: 'click',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
const uiService = services?.uiService;
|
|
||||||
|
|
||||||
const zoom = computed((): number => uiService?.get<number>('zoom') ?? 1);
|
|
||||||
const showGuides = computed((): boolean => uiService?.get<boolean>('showGuides') ?? true);
|
|
||||||
const showRule = computed((): boolean => uiService?.get<boolean>('showRule') ?? true);
|
|
||||||
|
|
||||||
const zoomInHandler = () => uiService?.zoom(0.1);
|
|
||||||
const zoomOutHandler = () => uiService?.zoom(-0.1);
|
|
||||||
|
|
||||||
const item = computed((): MenuButton | MenuComponent => {
|
|
||||||
if (typeof props.data !== 'string') {
|
|
||||||
return props.data;
|
|
||||||
}
|
|
||||||
switch (props.data) {
|
|
||||||
case '/':
|
|
||||||
return {
|
|
||||||
type: 'divider',
|
|
||||||
};
|
|
||||||
case 'zoom':
|
|
||||||
return {
|
|
||||||
type: 'zoom',
|
|
||||||
};
|
|
||||||
case 'delete':
|
|
||||||
return {
|
|
||||||
type: 'button',
|
|
||||||
icon: markRaw(Delete),
|
|
||||||
tooltip: '刪除',
|
|
||||||
disabled: () => services?.editorService.get('node')?.type === NodeType.PAGE,
|
|
||||||
handler: () => services?.editorService.remove(services?.editorService.get('node')),
|
|
||||||
};
|
|
||||||
case 'undo':
|
|
||||||
return {
|
|
||||||
type: 'button',
|
|
||||||
icon: markRaw(Back),
|
|
||||||
tooltip: '后退',
|
|
||||||
disabled: () => !services?.historyService.state.canUndo,
|
|
||||||
handler: () => services?.editorService.undo(),
|
|
||||||
};
|
|
||||||
case 'redo':
|
|
||||||
return {
|
|
||||||
type: 'button',
|
|
||||||
icon: markRaw(Right),
|
|
||||||
tooltip: '前进',
|
|
||||||
disabled: () => !services?.historyService.state.canRedo,
|
|
||||||
handler: () => services?.editorService.redo(),
|
|
||||||
};
|
|
||||||
case 'zoom-in':
|
|
||||||
return {
|
|
||||||
type: 'button',
|
|
||||||
icon: markRaw(ZoomIn),
|
|
||||||
tooltip: '放大',
|
|
||||||
handler: zoomInHandler,
|
|
||||||
};
|
|
||||||
case 'zoom-out':
|
|
||||||
return {
|
|
||||||
type: 'button',
|
|
||||||
icon: markRaw(ZoomOut),
|
|
||||||
tooltip: '縮小',
|
|
||||||
handler: zoomOutHandler,
|
|
||||||
};
|
|
||||||
case 'rule':
|
|
||||||
return {
|
|
||||||
type: 'button',
|
|
||||||
icon: markRaw(ScaleToOriginal),
|
|
||||||
tooltip: showRule.value ? '隐藏标尺' : '显示标尺',
|
|
||||||
handler: () => uiService?.set('showRule', !showRule.value),
|
|
||||||
};
|
|
||||||
case 'guides':
|
|
||||||
return {
|
|
||||||
type: 'button',
|
|
||||||
icon: markRaw(Grid),
|
|
||||||
tooltip: showGuides.value ? '隐藏参考线' : '显示参考线',
|
|
||||||
handler: () => uiService?.set('showGuides', !showGuides.value),
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
type: 'text',
|
|
||||||
text: props.data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const disabled = computed(() => {
|
const disabled = computed(() => {
|
||||||
if (typeof item.value === 'string') return false;
|
if (typeof props.data === 'string') return false;
|
||||||
if (item.value.type === 'component') return false;
|
if (props.data.type === 'component') return false;
|
||||||
if (typeof item.value.disabled === 'function') {
|
if (typeof props.data.disabled === 'function') {
|
||||||
return item.value.disabled(services);
|
return props.data.disabled(services);
|
||||||
}
|
}
|
||||||
return item.value.disabled;
|
return props.data.disabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
const display = computed(() => {
|
||||||
|
if (!props.data) return false;
|
||||||
|
if (typeof props.data === 'string') return true;
|
||||||
|
if (typeof props.data.display === 'function') {
|
||||||
|
return props.data.display(services);
|
||||||
|
}
|
||||||
|
return props.data.display ?? true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttonHandler = (item: MenuButton | MenuComponent, event: MouseEvent) => {
|
const buttonHandler = (item: MenuButton | MenuComponent, event: MouseEvent) => {
|
||||||
@ -174,15 +85,6 @@ const buttonHandler = (item: MenuButton | MenuComponent, event: MouseEvent) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const display = computed(() => {
|
|
||||||
if (!item.value) return false;
|
|
||||||
if (typeof item.value === 'string') return true;
|
|
||||||
if (typeof item.value.display === 'function') {
|
|
||||||
return item.value.display(services);
|
|
||||||
}
|
|
||||||
return item.value.display ?? true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const dropdownHandler = (command: any) => {
|
const dropdownHandler = (command: any) => {
|
||||||
if (command.item.handler) {
|
if (command.item.handler) {
|
||||||
command.item.handler(services);
|
command.item.handler(services);
|
||||||
|
@ -45,6 +45,7 @@ export { default as LayerPanel } from './layouts/sidebar/LayerPanel.vue';
|
|||||||
export { default as PropsPanel } from './layouts/PropsPanel.vue';
|
export { default as PropsPanel } from './layouts/PropsPanel.vue';
|
||||||
export { default as ToolButton } from './components/ToolButton.vue';
|
export { default as ToolButton } from './components/ToolButton.vue';
|
||||||
export { default as ContentMenu } from './components/ContentMenu.vue';
|
export { default as ContentMenu } from './components/ContentMenu.vue';
|
||||||
|
export { default as Icon } from './components/Icon.vue';
|
||||||
|
|
||||||
const defaultInstallOpt: InstallOptions = {
|
const defaultInstallOpt: InstallOptions = {
|
||||||
// @todo, 自定义图片上传方法等编辑器依赖的外部选项
|
// @todo, 自定义图片上传方法等编辑器依赖的外部选项
|
||||||
|
@ -1,41 +1,151 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="m-editor-nav-menu" :style="{ height: `${height}px` }">
|
<div class="m-editor-nav-menu" :style="{ height: `${height}px` }">
|
||||||
<div v-for="key in keys" :class="`menu-${key}`" :key="key" :style="`width: ${columnWidth?.[key]}px`">
|
<div v-for="key in keys" :class="`menu-${key}`" :key="key" :style="`width: ${columnWidth?.[key]}px`">
|
||||||
<tool-button :data="item" v-for="(item, index) in data[key]" :key="index"></tool-button>
|
<tool-button :data="item" v-for="(item, index) in buttons[key]" :key="index"></tool-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { computed, defineComponent, inject, PropType } from 'vue';
|
import { computed, inject, markRaw } from 'vue';
|
||||||
|
import { Back, Delete, Grid, Memo, Right, ZoomIn, ZoomOut } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { NodeType } from '@tmagic/schema';
|
||||||
|
|
||||||
import ToolButton from '../components/ToolButton.vue';
|
import ToolButton from '../components/ToolButton.vue';
|
||||||
import { GetColumnWidth, MenuBarData, Services } from '../type';
|
import { ColumnLayout, GetColumnWidth, MenuBarData, MenuButton, MenuComponent, MenuItem, Services } from '../type';
|
||||||
|
|
||||||
export default defineComponent({
|
const props = withDefaults(
|
||||||
name: 'nav-menu',
|
defineProps<{
|
||||||
|
data?: MenuBarData;
|
||||||
components: { ToolButton },
|
height?: number;
|
||||||
|
}>(),
|
||||||
props: {
|
{
|
||||||
data: {
|
data: () => ({}),
|
||||||
type: Object as PropType<MenuBarData>,
|
height: 35,
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
|
||||||
setup(props) {
|
const services = inject<Services>('services');
|
||||||
const services = inject<Services>('services');
|
const uiService = services?.uiService;
|
||||||
|
|
||||||
return {
|
const columnWidth = computed(() => services?.uiService.get<GetColumnWidth>('columnWidth'));
|
||||||
keys: computed(() => Object.keys(props.data) as Array<keyof MenuBarData>),
|
const keys = Object.values(ColumnLayout);
|
||||||
|
|
||||||
columnWidth: computed(() => services?.uiService.get<GetColumnWidth>('columnWidth')),
|
const showGuides = computed((): boolean => uiService?.get<boolean>('showGuides') ?? true);
|
||||||
};
|
const showRule = computed((): boolean => uiService?.get<boolean>('showRule') ?? true);
|
||||||
},
|
const zoom = computed((): number => uiService?.get<number>('zoom') ?? 1);
|
||||||
|
|
||||||
|
const getConfig = (item: MenuItem): (MenuButton | MenuComponent)[] => {
|
||||||
|
if (typeof item !== 'string') {
|
||||||
|
return [item];
|
||||||
|
}
|
||||||
|
const config: (MenuButton | MenuComponent)[] = [];
|
||||||
|
switch (item) {
|
||||||
|
case '/':
|
||||||
|
config.push({
|
||||||
|
type: 'divider',
|
||||||
|
className: 'divider',
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'zoom':
|
||||||
|
config.push(
|
||||||
|
...getConfig('zoom-out'),
|
||||||
|
...getConfig(`${parseInt(`${zoom.value * 100}`, 10)}%`),
|
||||||
|
...getConfig('zoom-in'),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
config.push({
|
||||||
|
type: 'button',
|
||||||
|
className: 'delete',
|
||||||
|
icon: markRaw(Delete),
|
||||||
|
tooltip: '刪除',
|
||||||
|
disabled: () => services?.editorService.get('node')?.type === NodeType.PAGE,
|
||||||
|
handler: () => services?.editorService.remove(services?.editorService.get('node')),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'undo':
|
||||||
|
config.push({
|
||||||
|
type: 'button',
|
||||||
|
className: 'undo',
|
||||||
|
icon: markRaw(Back),
|
||||||
|
tooltip: '后退',
|
||||||
|
disabled: () => !services?.historyService.state.canUndo,
|
||||||
|
handler: () => services?.editorService.undo(),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'redo':
|
||||||
|
config.push({
|
||||||
|
type: 'button',
|
||||||
|
className: 'redo',
|
||||||
|
icon: markRaw(Right),
|
||||||
|
tooltip: '前进',
|
||||||
|
disabled: () => !services?.historyService.state.canRedo,
|
||||||
|
handler: () => services?.editorService.redo(),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'zoom-in':
|
||||||
|
config.push({
|
||||||
|
type: 'button',
|
||||||
|
className: 'zoom-in',
|
||||||
|
icon: markRaw(ZoomIn),
|
||||||
|
tooltip: '放大',
|
||||||
|
handler: () => uiService?.zoom(0.1),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'zoom-out':
|
||||||
|
config.push({
|
||||||
|
type: 'button',
|
||||||
|
className: 'zoom-out',
|
||||||
|
icon: markRaw(ZoomOut),
|
||||||
|
tooltip: '縮小',
|
||||||
|
handler: () => uiService?.zoom(-0.1),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'rule':
|
||||||
|
config.push({
|
||||||
|
type: 'button',
|
||||||
|
className: 'rule',
|
||||||
|
icon: markRaw(Memo),
|
||||||
|
tooltip: showRule.value ? '隐藏标尺' : '显示标尺',
|
||||||
|
handler: () => uiService?.set('showRule', !showRule.value),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'guides':
|
||||||
|
config.push({
|
||||||
|
type: 'button',
|
||||||
|
className: 'guides',
|
||||||
|
icon: markRaw(Grid),
|
||||||
|
tooltip: showGuides.value ? '隐藏参考线' : '显示参考线',
|
||||||
|
handler: () => uiService?.set('showGuides', !showGuides.value),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
config.push({
|
||||||
|
type: 'text',
|
||||||
|
text: item,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons = computed(() => {
|
||||||
|
const data: {
|
||||||
|
[ColumnLayout.LEFT]: (MenuButton | MenuComponent)[];
|
||||||
|
[ColumnLayout.CENTER]: (MenuButton | MenuComponent)[];
|
||||||
|
[ColumnLayout.RIGHT]: (MenuButton | MenuComponent)[];
|
||||||
|
} = {
|
||||||
|
[ColumnLayout.LEFT]: [],
|
||||||
|
[ColumnLayout.CENTER]: [],
|
||||||
|
[ColumnLayout.RIGHT]: [],
|
||||||
|
};
|
||||||
|
keys.forEach((key) => {
|
||||||
|
const items = props.data[key] || [];
|
||||||
|
items.forEach((item) => {
|
||||||
|
data[key].push(...getConfig(item));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return data;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -9,7 +9,7 @@ import { Delete, DocumentCopy, Files, Plus } from '@element-plus/icons-vue';
|
|||||||
import { NodeType } from '@tmagic/schema';
|
import { NodeType } from '@tmagic/schema';
|
||||||
|
|
||||||
import ContentMenu from '../../components/ContentMenu.vue';
|
import ContentMenu from '../../components/ContentMenu.vue';
|
||||||
import type { ComponentGroup, MenuButton, MenuItem, Services } from '../../type';
|
import type { ComponentGroup, MenuButton, MenuComponent, Services } from '../../type';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { ContentMenu },
|
components: { ContentMenu },
|
||||||
@ -22,7 +22,7 @@ export default defineComponent({
|
|||||||
const isPage = computed(() => node.value?.type === NodeType.PAGE);
|
const isPage = computed(() => node.value?.type === NodeType.PAGE);
|
||||||
const componentList = computed(() => services?.componentListService.getList() || []);
|
const componentList = computed(() => services?.componentListService.getList() || []);
|
||||||
|
|
||||||
const layerContentMenu = inject<MenuItem[]>('layerContentMenu', []);
|
const layerContentMenu = inject<(MenuComponent | MenuButton)[]>('layerContentMenu', []);
|
||||||
|
|
||||||
const createMenuItems = (group: ComponentGroup): MenuButton[] =>
|
const createMenuItems = (group: ComponentGroup): MenuButton[] =>
|
||||||
group.items.map((component) => ({
|
group.items.map((component) => ({
|
||||||
@ -77,7 +77,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
menu,
|
menu,
|
||||||
menuData: computed<MenuItem[]>(() => [
|
menuData: computed<(MenuButton | MenuComponent)[]>(() => [
|
||||||
{
|
{
|
||||||
type: 'button',
|
type: 'button',
|
||||||
text: '新增',
|
text: '新增',
|
||||||
|
@ -12,7 +12,7 @@ import { isPage } from '@tmagic/utils';
|
|||||||
|
|
||||||
import ContentMenu from '../../components/ContentMenu.vue';
|
import ContentMenu from '../../components/ContentMenu.vue';
|
||||||
import storageService from '../../services/storage';
|
import storageService from '../../services/storage';
|
||||||
import { LayerOffset, Layout, MenuItem, Services } from '../../type';
|
import { LayerOffset, Layout, MenuButton, MenuComponent, Services } from '../../type';
|
||||||
import { COPY_STORAGE_KEY } from '../../utils/editor';
|
import { COPY_STORAGE_KEY } from '../../utils/editor';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{ isMultiSelect?: boolean }>(), { isMultiSelect: false });
|
const props = withDefaults(defineProps<{ isMultiSelect?: boolean }>(), { isMultiSelect: false });
|
||||||
@ -28,9 +28,9 @@ const nodes = computed(() => editorService?.get<MNode[]>('nodes'));
|
|||||||
const parent = computed(() => editorService?.get('parent'));
|
const parent = computed(() => editorService?.get('parent'));
|
||||||
const stage = computed(() => editorService?.get<StageCore>('stage'));
|
const stage = computed(() => editorService?.get<StageCore>('stage'));
|
||||||
|
|
||||||
const stageContentMenu = inject<MenuItem[]>('stageContentMenu', []);
|
const stageContentMenu = inject<(MenuButton | MenuComponent)[]>('stageContentMenu', []);
|
||||||
|
|
||||||
const menuData = reactive<MenuItem[]>([
|
const menuData = reactive<(MenuButton | MenuComponent)[]>([
|
||||||
{
|
{
|
||||||
type: 'button',
|
type: 'button',
|
||||||
text: '水平居中',
|
text: '水平居中',
|
||||||
|
@ -69,5 +69,11 @@
|
|||||||
.menu-item-text {
|
.menu-item-text {
|
||||||
color: $--nav-color;
|
color: $--nav-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.rule {
|
||||||
|
.el-icon {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,16 +82,22 @@ export interface ComponentGroupState {
|
|||||||
list: ComponentGroup[];
|
list: ComponentGroup[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ColumnLayout {
|
||||||
|
LEFT = 'left',
|
||||||
|
CENTER = 'center',
|
||||||
|
RIGHT = 'right',
|
||||||
|
}
|
||||||
|
|
||||||
export interface SetColumnWidth {
|
export interface SetColumnWidth {
|
||||||
left?: number;
|
[ColumnLayout.LEFT]?: number;
|
||||||
center?: number | 'auto';
|
[ColumnLayout.CENTER]?: number | 'auto';
|
||||||
right?: number;
|
[ColumnLayout.RIGHT]?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetColumnWidth {
|
export interface GetColumnWidth {
|
||||||
left: number;
|
[ColumnLayout.LEFT]: number;
|
||||||
center: number;
|
[ColumnLayout.CENTER]: number;
|
||||||
right: number;
|
[ColumnLayout.RIGHT]: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StageRect {
|
export interface StageRect {
|
||||||
@ -174,6 +180,7 @@ export interface MenuButton {
|
|||||||
/** type为button/dropdown时点击运行的方法 */
|
/** type为button/dropdown时点击运行的方法 */
|
||||||
handler?: (data: Services, event: MouseEvent) => Promise<any> | any;
|
handler?: (data: Services, event: MouseEvent) => Promise<any> | any;
|
||||||
/** type为dropdown时,下拉的菜单列表, 或者有子菜单时 */
|
/** type为dropdown时,下拉的菜单列表, 或者有子菜单时 */
|
||||||
|
className?: string;
|
||||||
items?: MenuButton[];
|
items?: MenuButton[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +194,7 @@ export interface MenuComponent {
|
|||||||
listeners?: Record<string, Function>;
|
listeners?: Record<string, Function>;
|
||||||
slots?: Record<string, any>;
|
slots?: Record<string, any>;
|
||||||
/** 是否显示,默认为true */
|
/** 是否显示,默认为true */
|
||||||
|
className?: string;
|
||||||
display?: boolean | ((data?: Services) => Promise<boolean> | boolean);
|
display?: boolean | ((data?: Services) => Promise<boolean> | boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,16 +217,17 @@ export type MenuItem =
|
|||||||
| 'guides'
|
| 'guides'
|
||||||
| 'rule'
|
| 'rule'
|
||||||
| MenuButton
|
| MenuButton
|
||||||
| MenuComponent;
|
| MenuComponent
|
||||||
|
| string;
|
||||||
|
|
||||||
/** 工具栏 */
|
/** 工具栏 */
|
||||||
export interface MenuBarData {
|
export interface MenuBarData {
|
||||||
/** 顶部工具栏左边项 */
|
/** 顶部工具栏左边项 */
|
||||||
left?: MenuItem[];
|
[ColumnLayout.LEFT]?: MenuItem[];
|
||||||
/** 顶部工具栏中间项 */
|
/** 顶部工具栏中间项 */
|
||||||
center?: MenuItem[];
|
[ColumnLayout.CENTER]?: MenuItem[];
|
||||||
/** 顶部工具栏右边项 */
|
/** 顶部工具栏右边项 */
|
||||||
right?: MenuItem[];
|
[ColumnLayout.RIGHT]?: MenuItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SideComponent extends MenuComponent {
|
export interface SideComponent extends MenuComponent {
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"url": "https://github.com/Tencent/tmagic-editor.git"
|
"url": "https://github.com/Tencent/tmagic-editor.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.0.6",
|
"@element-plus/icons-vue": "^2.0.9",
|
||||||
"@tmagic/utils": "1.1.0-beta.12",
|
"@tmagic/utils": "1.1.0-beta.12",
|
||||||
"element-plus": "^2.2.6",
|
"element-plus": "^2.2.6",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"serve": "vite preview"
|
"serve": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.0.6",
|
"@element-plus/icons-vue": "^2.0.9",
|
||||||
"@tmagic/editor": "1.1.0-beta.12",
|
"@tmagic/editor": "1.1.0-beta.12",
|
||||||
"@tmagic/form": "1.1.0-beta.12",
|
"@tmagic/form": "1.1.0-beta.12",
|
||||||
"@tmagic/schema": "1.1.0-beta.12",
|
"@tmagic/schema": "1.1.0-beta.12",
|
||||||
|
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
@ -74,7 +74,7 @@ importers:
|
|||||||
|
|
||||||
docs:
|
docs:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@element-plus/icons-vue': ^2.0.6
|
'@element-plus/icons-vue': ^2.0.9
|
||||||
'@tmagic/form': 1.1.0-beta.12
|
'@tmagic/form': 1.1.0-beta.12
|
||||||
'@tmagic/schema': 1.1.0-beta.12
|
'@tmagic/schema': 1.1.0-beta.12
|
||||||
'@tmagic/utils': 1.1.0-beta.12
|
'@tmagic/utils': 1.1.0-beta.12
|
||||||
@ -92,7 +92,7 @@ importers:
|
|||||||
vue: ^3.2.37
|
vue: ^3.2.37
|
||||||
vuepress: ^2.0.0-beta.49
|
vuepress: ^2.0.0-beta.49
|
||||||
dependencies:
|
dependencies:
|
||||||
'@element-plus/icons-vue': 2.0.6_vue@3.2.37
|
'@element-plus/icons-vue': 2.0.9_vue@3.2.37
|
||||||
'@tmagic/form': link:../packages/form
|
'@tmagic/form': link:../packages/form
|
||||||
'@tmagic/schema': link:../packages/schema
|
'@tmagic/schema': link:../packages/schema
|
||||||
'@tmagic/utils': link:../packages/utils
|
'@tmagic/utils': link:../packages/utils
|
||||||
@ -154,7 +154,7 @@ importers:
|
|||||||
packages/editor:
|
packages/editor:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@babel/core': ^7.18.0
|
'@babel/core': ^7.18.0
|
||||||
'@element-plus/icons-vue': ^2.0.6
|
'@element-plus/icons-vue': ^2.0.9
|
||||||
'@tmagic/core': 1.1.0-beta.12
|
'@tmagic/core': 1.1.0-beta.12
|
||||||
'@tmagic/form': 1.1.0-beta.12
|
'@tmagic/form': 1.1.0-beta.12
|
||||||
'@tmagic/schema': 1.1.0-beta.12
|
'@tmagic/schema': 1.1.0-beta.12
|
||||||
@ -184,7 +184,7 @@ importers:
|
|||||||
vue-tsc: ^0.39.4
|
vue-tsc: ^0.39.4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.18.2
|
'@babel/core': 7.18.2
|
||||||
'@element-plus/icons-vue': 2.0.6_vue@3.2.37
|
'@element-plus/icons-vue': 2.0.9_vue@3.2.37
|
||||||
'@tmagic/core': link:../core
|
'@tmagic/core': link:../core
|
||||||
'@tmagic/form': link:../form
|
'@tmagic/form': link:../form
|
||||||
'@tmagic/schema': link:../schema
|
'@tmagic/schema': link:../schema
|
||||||
@ -217,7 +217,7 @@ importers:
|
|||||||
packages/form:
|
packages/form:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@babel/core': ^7.18.0
|
'@babel/core': ^7.18.0
|
||||||
'@element-plus/icons-vue': ^2.0.6
|
'@element-plus/icons-vue': ^2.0.9
|
||||||
'@tmagic/utils': 1.1.0-beta.12
|
'@tmagic/utils': 1.1.0-beta.12
|
||||||
'@types/lodash-es': ^4.17.4
|
'@types/lodash-es': ^4.17.4
|
||||||
'@types/node': ^15.12.4
|
'@types/node': ^15.12.4
|
||||||
@ -235,7 +235,7 @@ importers:
|
|||||||
vue: ^3.2.37
|
vue: ^3.2.37
|
||||||
vue-tsc: ^0.39.4
|
vue-tsc: ^0.39.4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@element-plus/icons-vue': 2.0.6_vue@3.2.37
|
'@element-plus/icons-vue': 2.0.9_vue@3.2.37
|
||||||
'@tmagic/utils': link:../utils
|
'@tmagic/utils': link:../utils
|
||||||
element-plus: 2.2.6_vue@3.2.37
|
element-plus: 2.2.6_vue@3.2.37
|
||||||
lodash-es: 4.17.21
|
lodash-es: 4.17.21
|
||||||
@ -427,7 +427,7 @@ importers:
|
|||||||
|
|
||||||
playground:
|
playground:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@element-plus/icons-vue': ^2.0.6
|
'@element-plus/icons-vue': ^2.0.9
|
||||||
'@tmagic/editor': 1.1.0-beta.12
|
'@tmagic/editor': 1.1.0-beta.12
|
||||||
'@tmagic/form': 1.1.0-beta.12
|
'@tmagic/form': 1.1.0-beta.12
|
||||||
'@tmagic/schema': 1.1.0-beta.12
|
'@tmagic/schema': 1.1.0-beta.12
|
||||||
@ -450,7 +450,7 @@ importers:
|
|||||||
vue-router: ^4.0.10
|
vue-router: ^4.0.10
|
||||||
vue-tsc: ^0.39.4
|
vue-tsc: ^0.39.4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@element-plus/icons-vue': 2.0.6_vue@3.2.37
|
'@element-plus/icons-vue': 2.0.9_vue@3.2.37
|
||||||
'@tmagic/editor': link:../packages/editor
|
'@tmagic/editor': link:../packages/editor
|
||||||
'@tmagic/form': link:../packages/form
|
'@tmagic/form': link:../packages/form
|
||||||
'@tmagic/schema': link:../packages/schema
|
'@tmagic/schema': link:../packages/schema
|
||||||
@ -1175,8 +1175,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-HsbMKc0ZAQH+EUeCmI/2PvTYSybmkaWwakU8QGDYYgMVIg9BQ5sM0A0Nnombjxo2+JzXHxmH+jw//yGX+y6GYw==}
|
resolution: {integrity: sha512-HsbMKc0ZAQH+EUeCmI/2PvTYSybmkaWwakU8QGDYYgMVIg9BQ5sM0A0Nnombjxo2+JzXHxmH+jw//yGX+y6GYw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@element-plus/icons-vue/2.0.6_vue@3.2.37:
|
/@element-plus/icons-vue/2.0.9_vue@3.2.37:
|
||||||
resolution: {integrity: sha512-lPpG8hYkjL/Z97DH5Ei6w6o22Z4YdNglWCNYOPcB33JCF2A4wye6HFgSI7hEt9zdLyxlSpiqtgf9XcYU+m5mew==}
|
resolution: {integrity: sha512-okdrwiVeKBmW41Hkl0eMrXDjzJwhQMuKiBOu17rOszqM+LS/yBYpNQNV5Jvoh06Wc+89fMmb/uhzf8NZuDuUaQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3.2.0
|
vue: ^3.2.0
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3324,7 +3324,7 @@ packages:
|
|||||||
vue: ^3.2.0
|
vue: ^3.2.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ctrl/tinycolor': 3.4.1
|
'@ctrl/tinycolor': 3.4.1
|
||||||
'@element-plus/icons-vue': 2.0.6_vue@3.2.37
|
'@element-plus/icons-vue': 2.0.9_vue@3.2.37
|
||||||
'@floating-ui/dom': 0.5.4
|
'@floating-ui/dom': 0.5.4
|
||||||
'@popperjs/core': /@sxzz/popperjs-es/2.11.7
|
'@popperjs/core': /@sxzz/popperjs-es/2.11.7
|
||||||
'@types/lodash': 4.14.182
|
'@types/lodash': 4.14.182
|
||||||
|
Loading…
x
Reference in New Issue
Block a user