mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-05 19:41:40 +08:00
fix(editor): 页面列表显示问题
This commit is contained in:
parent
9ac3e12487
commit
a4dd4eac02
@ -1,191 +1,73 @@
|
||||
<template>
|
||||
<div class="m-editor-page-bar" ref="pageBar">
|
||||
<div id="m-editor-page-bar-add-icon" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="addPage">
|
||||
<el-icon><plus></plus></el-icon>
|
||||
</div>
|
||||
<PageBarScrollContainer>
|
||||
<div
|
||||
v-if="scrollState.canScroll"
|
||||
class="m-editor-page-bar-item m-editor-page-bar-item-icon"
|
||||
@click="scrollState.scroll('left')"
|
||||
v-for="item in (root && root.items) || []"
|
||||
class="m-editor-page-bar-item"
|
||||
:key="item.key"
|
||||
:class="{ active: page?.id === item.id }"
|
||||
@click="switchPage(item)"
|
||||
>
|
||||
<el-icon><arrow-left-bold></arrow-left-bold></el-icon>
|
||||
</div>
|
||||
<div
|
||||
v-if="root"
|
||||
class="m-editor-page-bar-items"
|
||||
ref="itemsContainer"
|
||||
:style="`width: ${scrollState.itemsContainerWidth}px`"
|
||||
>
|
||||
<div
|
||||
v-for="item in root.items"
|
||||
:key="item.key"
|
||||
class="m-editor-page-bar-item"
|
||||
:class="{ active: page?.id === item.id }"
|
||||
@click="switchPage(item)"
|
||||
>
|
||||
<div class="m-editor-page-bar-title">
|
||||
<slot name="page-bar-title" :page="item">
|
||||
<el-tooltip effect="dark" placement="top-start" :content="item.name">
|
||||
<span>{{ item.name }}</span>
|
||||
</el-tooltip>
|
||||
<div class="m-editor-page-bar-title">
|
||||
<slot name="page-bar-title" :page="item">
|
||||
<el-tooltip effect="dark" placement="top-start" :content="item.name">
|
||||
<span>{{ item.name }}</span>
|
||||
</el-tooltip>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<el-popover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
|
||||
<div>
|
||||
<slot name="page-bar-popover" :page="item">
|
||||
<tool-button
|
||||
:data="{
|
||||
type: 'button',
|
||||
text: '复制',
|
||||
icon: DocumentCopy,
|
||||
handler: () => copy(item),
|
||||
}"
|
||||
></tool-button>
|
||||
<tool-button
|
||||
:data="{
|
||||
type: 'button',
|
||||
text: '删除',
|
||||
icon: Delete,
|
||||
handler: () => remove(item),
|
||||
}"
|
||||
></tool-button>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<el-popover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
|
||||
<div>
|
||||
<slot name="page-bar-popover" :page="item">
|
||||
<tool-button
|
||||
:data="{
|
||||
type: 'button',
|
||||
text: '复制',
|
||||
icon: DocumentCopy,
|
||||
handler: () => copy(item),
|
||||
}"
|
||||
></tool-button>
|
||||
<tool-button
|
||||
:data="{
|
||||
type: 'button',
|
||||
text: '删除',
|
||||
icon: Delete,
|
||||
handler: () => remove(item),
|
||||
}"
|
||||
></tool-button>
|
||||
</slot>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-icon class="m-editor-page-bar-menu-icon">
|
||||
<caret-bottom></caret-bottom>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-icon class="m-editor-page-bar-menu-icon">
|
||||
<caret-bottom></caret-bottom>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div
|
||||
v-if="scrollState.canScroll"
|
||||
class="m-editor-page-bar-item m-editor-page-bar-item-icon"
|
||||
@click="scrollState.scroll('right')"
|
||||
>
|
||||
<el-icon><arrow-right-bold></arrow-right-bold></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</PageBarScrollContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ComputedRef, inject, onMounted, onUnmounted, ref, toRaw, watch } from 'vue';
|
||||
import { ArrowLeftBold, ArrowRightBold, CaretBottom, Delete, DocumentCopy, Plus } from '@element-plus/icons-vue';
|
||||
import { computed, inject } from 'vue';
|
||||
import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue';
|
||||
|
||||
import type { MApp, MPage } from '@tmagic/schema';
|
||||
import { NodeType } from '@tmagic/schema';
|
||||
|
||||
import ToolButton from '../../components/ToolButton.vue';
|
||||
import type { Services } from '../../type';
|
||||
import { generatePageNameByApp } from '../../utils/editor';
|
||||
|
||||
const useScroll = (root: ComputedRef<MApp | undefined>) => {
|
||||
const pageBar = ref<HTMLDivElement>();
|
||||
const itemsContainer = ref<HTMLDivElement>();
|
||||
|
||||
const pageBarWidth = ref(0);
|
||||
const canScroll = ref(false);
|
||||
|
||||
const itemsContainerWidth = computed(() => pageBarWidth.value - 105);
|
||||
|
||||
let translateLeft = 0;
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const { contentRect } of entries) {
|
||||
const { width } = contentRect;
|
||||
pageBarWidth.value = width || 0;
|
||||
|
||||
setCanScroll();
|
||||
}
|
||||
});
|
||||
|
||||
const setCanScroll = () => {
|
||||
if (itemsContainer.value) {
|
||||
canScroll.value = itemsContainer.value.scrollWidth > pageBarWidth.value - 105;
|
||||
}
|
||||
};
|
||||
|
||||
const scroll = (type: 'left' | 'right' | 'start' | 'end') => {
|
||||
if (!itemsContainer.value) return;
|
||||
|
||||
const maxScrollLeft = itemsContainer.value.scrollWidth - itemsContainerWidth.value;
|
||||
|
||||
if (type === 'left') {
|
||||
translateLeft += 100;
|
||||
|
||||
if (translateLeft > 0) {
|
||||
translateLeft = 0;
|
||||
}
|
||||
} else if (type === 'right') {
|
||||
translateLeft -= 100;
|
||||
|
||||
if (-translateLeft > maxScrollLeft) {
|
||||
translateLeft = -maxScrollLeft;
|
||||
}
|
||||
} else if (type === 'start') {
|
||||
translateLeft = 0;
|
||||
} else if (type === 'end') {
|
||||
translateLeft = -maxScrollLeft;
|
||||
}
|
||||
|
||||
itemsContainer.value.style.transform = `translate(${translateLeft}px, 0px)`;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
pageBar.value && resizeObserver.observe(pageBar.value);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
resizeObserver.disconnect();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => root.value?.items.length,
|
||||
(length = 0, preLength = 0) => {
|
||||
setTimeout(() => {
|
||||
setCanScroll();
|
||||
if (length < preLength) {
|
||||
scroll('start');
|
||||
} else {
|
||||
scroll('end');
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
pageBar,
|
||||
itemsContainer,
|
||||
canScroll,
|
||||
|
||||
itemsContainerWidth,
|
||||
|
||||
scroll,
|
||||
};
|
||||
};
|
||||
import PageBarScrollContainer from './PageBarScrollContainer.vue';
|
||||
|
||||
const services = inject<Services>('services');
|
||||
const editorService = services?.editorService;
|
||||
|
||||
const root = computed(() => editorService?.get<MApp>('root'));
|
||||
|
||||
const scrollState = useScroll(root);
|
||||
const page = computed(() => editorService?.get('page'));
|
||||
|
||||
const switchPage = (page: MPage) => {
|
||||
editorService?.select(page);
|
||||
};
|
||||
|
||||
const addPage = () => {
|
||||
if (!editorService) return;
|
||||
const pageConfig = {
|
||||
type: NodeType.PAGE,
|
||||
name: generatePageNameByApp(toRaw(editorService.get('root'))),
|
||||
};
|
||||
editorService.add(pageConfig);
|
||||
};
|
||||
|
||||
const copy = (node: MPage) => {
|
||||
node && editorService?.copy(node);
|
||||
editorService?.paste({
|
||||
|
111
packages/editor/src/layouts/workspace/PageBarScrollContainer.vue
Normal file
111
packages/editor/src/layouts/workspace/PageBarScrollContainer.vue
Normal file
@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div class="m-editor-page-bar" ref="pageBar">
|
||||
<div id="m-editor-page-bar-add-icon" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="addPage">
|
||||
<el-icon><plus></plus></el-icon>
|
||||
</div>
|
||||
<div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('left')">
|
||||
<el-icon><arrow-left-bold></arrow-left-bold></el-icon>
|
||||
</div>
|
||||
<div v-if="root" class="m-editor-page-bar-items" ref="itemsContainer" :style="`width: ${itemsContainerWidth}px`">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('right')">
|
||||
<el-icon><arrow-right-bold></arrow-right-bold></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, onMounted, onUnmounted, ref, toRaw, watch } from 'vue';
|
||||
import { ArrowLeftBold, ArrowRightBold, Plus } from '@element-plus/icons-vue';
|
||||
|
||||
import { MApp, NodeType } from '@tmagic/schema';
|
||||
|
||||
import type { Services } from '../../type';
|
||||
import { generatePageNameByApp } from '../../utils/editor';
|
||||
|
||||
const services = inject<Services>('services');
|
||||
const editorService = services?.editorService;
|
||||
|
||||
const pageBar = ref<HTMLDivElement>();
|
||||
const itemsContainer = ref<HTMLDivElement>();
|
||||
const pageBarWidth = ref(0);
|
||||
const canScroll = ref(false);
|
||||
|
||||
const itemsContainerWidth = computed(() => pageBarWidth.value - 105);
|
||||
|
||||
let translateLeft = 0;
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const { contentRect } of entries) {
|
||||
const { width } = contentRect;
|
||||
pageBarWidth.value = width || 0;
|
||||
|
||||
setCanScroll();
|
||||
}
|
||||
});
|
||||
|
||||
const setCanScroll = () => {
|
||||
if (itemsContainer.value) {
|
||||
canScroll.value = itemsContainer.value.scrollWidth > pageBarWidth.value - 105;
|
||||
}
|
||||
};
|
||||
|
||||
const scroll = (type: 'left' | 'right' | 'start' | 'end') => {
|
||||
if (!itemsContainer.value) return;
|
||||
|
||||
const maxScrollLeft = itemsContainer.value.scrollWidth - itemsContainerWidth.value;
|
||||
|
||||
if (type === 'left') {
|
||||
translateLeft += 100;
|
||||
|
||||
if (translateLeft > 0) {
|
||||
translateLeft = 0;
|
||||
}
|
||||
} else if (type === 'right') {
|
||||
translateLeft -= 100;
|
||||
|
||||
if (-translateLeft > maxScrollLeft) {
|
||||
translateLeft = -maxScrollLeft;
|
||||
}
|
||||
} else if (type === 'start') {
|
||||
translateLeft = 0;
|
||||
} else if (type === 'end') {
|
||||
translateLeft = -maxScrollLeft;
|
||||
}
|
||||
|
||||
itemsContainer.value.style.transform = `translate(${translateLeft}px, 0px)`;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
pageBar.value && resizeObserver.observe(pageBar.value);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
resizeObserver.disconnect();
|
||||
});
|
||||
|
||||
const root = computed(() => editorService?.get<MApp>('root'));
|
||||
|
||||
watch(
|
||||
() => root.value?.items.length,
|
||||
(length = 0, preLength = 0) => {
|
||||
setTimeout(() => {
|
||||
setCanScroll();
|
||||
if (length < preLength) {
|
||||
scroll('start');
|
||||
} else {
|
||||
scroll('end');
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const addPage = () => {
|
||||
if (!editorService) return;
|
||||
const pageConfig = {
|
||||
type: NodeType.PAGE,
|
||||
name: generatePageNameByApp(toRaw(editorService.get('root'))),
|
||||
};
|
||||
editorService.add(pageConfig);
|
||||
};
|
||||
</script>
|
Loading…
x
Reference in New Issue
Block a user