web_ppt / frontend /src /views /Editor /SelectPanel.vue
CatPtain's picture
Upload 339 files
89ce340 verified
raw
history blame
7.38 kB
<template>
<MoveablePanel
class="select-panel"
:width="200"
:height="360"
:title="`选择(${activeElementIdList.length}/${currentSlide.elements.length})`"
:left="-270"
:top="90"
@close="close()"
>
<div class="handler" v-if="elements.length">
<div class="btns">
<Button size="small" style="margin-right: 5px;" @click="showAllElements()">全部显示</Button>
<Button size="small" @click="hideAllElements()">全部隐藏</Button>
</div>
<div class="icon-btns" v-if="handleElement">
<IconDown class="icon-btn" @click="orderElement(handleElement!, ElementOrderCommands.UP)" />
<IconUp class="icon-btn" @click="orderElement(handleElement!, ElementOrderCommands.DOWN)" />
</div>
</div>
<div class="element-list">
<template v-for="item in elements" :key="item.id">
<div class="group-els" v-if="item.type === 'group'">
<div class="group-title">组合</div>
<div
class="item"
:class="{
'active': activeElementIdList.includes(groupItem.id),
'group-active': activeGroupElementId.includes(groupItem.id),
}"
v-for="groupItem in item.elements"
:key="groupItem.id"
@click="selectGroupEl(item, groupItem.id)"
@dblclick="enterEdit(groupItem.id)"
>
<input
:id="`select-panel-input-${groupItem.id}`"
:value="groupItem.name || ELEMENT_TYPE_ZH[groupItem.type]"
class="input"
type="text"
v-if="editingElId === groupItem.id"
@blur="$event => saveElementName($event, groupItem.id)"
@keydown.enter="$event => saveElementName($event, groupItem.id)"
>
<div v-else class="name">{{groupItem.name || ELEMENT_TYPE_ZH[groupItem.type]}}</div>
<div class="icons">
<IconPreviewClose style="font-size: 17px;" @click.stop="toggleHideElement(groupItem.id)" v-if="hiddenElementIdList.includes(groupItem.id)" />
<IconPreviewOpen style="font-size: 17px;" @click.stop="toggleHideElement(groupItem.id)" v-else />
</div>
</div>
</div>
<div
class="item"
:class="{ 'active': activeElementIdList.includes(item.id) }"
v-else
@click="selectElement(item.id)"
@dblclick="enterEdit(item.id)"
>
<input
:id="`select-panel-input-${item.id}`"
:value="item.name || ELEMENT_TYPE_ZH[item.type]"
class="input"
type="text"
v-if="editingElId === item.id"
@blur="$event => saveElementName($event, item.id)"
@keydown.enter="$event => saveElementName($event, item.id)"
>
<div v-else class="name">{{item.name || ELEMENT_TYPE_ZH[item.type]}}</div>
<div class="icons">
<IconPreviewClose style="font-size: 17px;" @click.stop="toggleHideElement(item.id)" v-if="hiddenElementIdList.includes(item.id)" />
<IconPreviewOpen style="font-size: 17px;" @click.stop="toggleHideElement(item.id)" v-else />
</div>
</div>
</template>
</div>
</MoveablePanel>
</template>
<script lang="ts" setup>
import { computed, nextTick, ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useSlidesStore, useMainStore } from '@/store'
import type { PPTElement } from '@/types/slides'
import { ELEMENT_TYPE_ZH } from '@/configs/element'
import useOrderElement from '@/hooks/useOrderElement'
import useHideElement from '@/hooks/useHideElement'
import useSelectElement from '@/hooks/useSelectElement'
import { ElementOrderCommands } from '@/types/edit'
import MoveablePanel from '@/components/MoveablePanel.vue'
import Button from '@/components/Button.vue'
const slidesStore = useSlidesStore()
const mainStore = useMainStore()
const { currentSlide } = storeToRefs(slidesStore)
const { handleElement, handleElementId, activeElementIdList, activeGroupElementId, hiddenElementIdList } = storeToRefs(mainStore)
const { orderElement } = useOrderElement()
const { selectElement } = useSelectElement()
const { toggleHideElement, showAllElements, hideAllElements } = useHideElement()
interface GroupElements {
type: 'group'
id: string
elements: PPTElement[]
}
type ElementItem = PPTElement | GroupElements
const elements = computed<ElementItem[]>(() => {
const _elements: ElementItem[] = []
for (const el of currentSlide.value.elements) {
if (el.groupId) {
const lastItem = _elements[_elements.length - 1]
if (lastItem && lastItem.type === 'group' && lastItem.id && lastItem.id === el.groupId) {
lastItem.elements.push(el)
}
else _elements.push({ type: 'group', id: el.groupId, elements: [el] })
}
else _elements.push(el)
}
return _elements
})
const selectGroupEl = (item: GroupElements, id: string) => {
if (handleElementId.value === id) return
if (hiddenElementIdList.value.includes(id)) return
const idList = item.elements.map(el => el.id)
mainStore.setActiveElementIdList(idList)
mainStore.setHandleElementId(id)
nextTick(() => mainStore.setActiveGroupElementId(id))
}
const editingElId = ref('')
const saveElementName = (e: FocusEvent | KeyboardEvent, id: string) => {
const name = (e.target as HTMLInputElement).value
slidesStore.updateElement({ id, props: { name } })
editingElId.value = ''
}
const enterEdit = (id: string) => {
editingElId.value = id
nextTick(() => {
const inputRef = document.querySelector(`#select-panel-input-${id}`) as HTMLInputElement
inputRef.focus()
})
}
const close = () => {
mainStore.setSelectPanelState(false)
}
</script>
<style lang="scss" scoped>
.select-panel {
height: 100%;
font-size: 12px;
user-select: none;
}
.handler {
height: 24px;
margin-bottom: 8px;
display: flex;
align-items: center;
justify-content: space-between;
.icon-btns {
height: 100%;
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
}
.icon-btn {
width: 16px;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:hover {
color: $themeColor;
}
}
}
.element-list {
height: calc(100% - 32px);
padding-right: 10px;
margin-right: -10px;
overflow: auto;
}
.item {
padding: 5px;
font-size: 12px;
border-radius: $borderRadius;
display: flex;
align-items: center;
cursor: pointer;
&.active {
background-color: rgba($color: $themeColor, $alpha: .1);
}
&.group-active {
background-color: rgba($color: $themeColor, $alpha: .2);
}
&:hover {
background-color: rgba($color: $themeColor, $alpha: .25);
}
.name {
height: 18px;
line-height: 18px;
flex: 1;
@include ellipsis-oneline();
}
.icons {
width: 20px;
display: flex;
align-items: center;
justify-content: center;
margin-left: 5px;
}
}
.group-els {
padding: 5px 0;
.group-title {
margin-bottom: 5px;
padding: 0 5px;
}
.item {
margin-left: 15px;
}
}
.input {
width: 100%;
height: 16px;
border: 0;
outline: 0;
padding-left: 0;
padding-right: 0;
flex: 1;
font-size: 12px;
background-color: transparent;
}
</style>