|  | @@ -9,9 +9,18 @@
 | 
	
		
			
				|  |  |        <div class="btn" @click="createSlide()"><IconPlus class="icon" />添加幻灯片</div>
 | 
	
		
			
				|  |  |        <Popover trigger="click" placement="bottom-start" v-model:value="presetLayoutPopoverVisible" center>
 | 
	
		
			
				|  |  |          <template #content>
 | 
	
		
			
				|  |  | -          <LayoutPool @select="slide => { createSlideByTemplate(slide); presetLayoutPopoverVisible = false }" />
 | 
	
		
			
				|  |  | +          <LayoutPool
 | 
	
		
			
				|  |  | +            @select="
 | 
	
		
			
				|  |  | +              slide => {
 | 
	
		
			
				|  |  | +                createSlideByTemplate(slide)
 | 
	
		
			
				|  |  | +                presetLayoutPopoverVisible = false
 | 
	
		
			
				|  |  | +              }
 | 
	
		
			
				|  |  | +            "
 | 
	
		
			
				|  |  | +          />
 | 
	
		
			
				|  |  |          </template>
 | 
	
		
			
				|  |  | -        <div class="select-btn"><IconDown /></div>
 | 
	
		
			
				|  |  | +        <div class="select-btn">
 | 
	
		
			
				|  |  | +          <img src="./imgs/list.png" alt="" />
 | 
	
		
			
				|  |  | +        </div>
 | 
	
		
			
				|  |  |        </Popover>
 | 
	
		
			
				|  |  |      </div>
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -28,7 +37,8 @@
 | 
	
		
			
				|  |  |      >
 | 
	
		
			
				|  |  |        <template #item="{ element, index }">
 | 
	
		
			
				|  |  |          <div class="thumbnail-container">
 | 
	
		
			
				|  |  | -          <div class="section-title"
 | 
	
		
			
				|  |  | +          <div
 | 
	
		
			
				|  |  | +            class="section-title"
 | 
	
		
			
				|  |  |              :data-section-id="element?.sectionTag?.id || ''"
 | 
	
		
			
				|  |  |              v-if="element.sectionTag || (hasSection && index === 0)"
 | 
	
		
			
				|  |  |              v-contextmenu="contextmenusSection"
 | 
	
	
		
			
				|  | @@ -41,23 +51,29 @@
 | 
	
		
			
				|  |  |                @blur="$event => saveSection($event)"
 | 
	
		
			
				|  |  |                @keydown.enter.stop="$event => saveSection($event)"
 | 
	
		
			
				|  |  |                v-if="editingSectionId === element?.sectionTag?.id || (index === 0 && editingSectionId === 'default')"
 | 
	
		
			
				|  |  | -            >
 | 
	
		
			
				|  |  | +            />
 | 
	
		
			
				|  |  |              <span class="text" v-else>
 | 
	
		
			
				|  |  | -              <div class="text-content">{{ element?.sectionTag ? (element?.sectionTag?.title || '无标题节') : '默认节' }}</div>
 | 
	
		
			
				|  |  | +              <div class="text-content">{{ element?.sectionTag ? element?.sectionTag?.title || "无标题节" : "默认节" }}</div>
 | 
	
		
			
				|  |  |              </span>
 | 
	
		
			
				|  |  |            </div>
 | 
	
		
			
				|  |  |            <div
 | 
	
		
			
				|  |  |              class="thumbnail-item"
 | 
	
		
			
				|  |  |              :class="{
 | 
	
		
			
				|  |  | -              'active': slideIndex === index,
 | 
	
		
			
				|  |  | -              'selected': selectedSlidesIndex.includes(index),
 | 
	
		
			
				|  |  | +              active: slideIndex === index,
 | 
	
		
			
				|  |  | +              selected: selectedSlidesIndex.includes(index)
 | 
	
		
			
				|  |  |              }"
 | 
	
		
			
				|  |  |              @mousedown="$event => handleClickSlideThumbnail($event, index)"
 | 
	
		
			
				|  |  |              @dblclick="enterScreening()"
 | 
	
		
			
				|  |  |              v-contextmenu="contextmenusThumbnailItem"
 | 
	
		
			
				|  |  |            >
 | 
	
		
			
				|  |  |              <div class="label" :class="{ 'offset-left': index >= 99 }">{{ fillDigit(index + 1, 2) }}</div>
 | 
	
		
			
				|  |  | -            <ThumbnailSlide class="thumbnail" :slide="element" :size="120" :visible="index < slidesLoadLimit" />
 | 
	
		
			
				|  |  | +            <div class="thumbnail">
 | 
	
		
			
				|  |  | +              <ThumbnailSlide :slide="element" :size="180" :visible="index < slidesLoadLimit" />
 | 
	
		
			
				|  |  | +              <div class="tools" v-if="slideIndex === index">
 | 
	
		
			
				|  |  | +                <img src="./imgs/play.png" @click="enterScreening" alt="" />
 | 
	
		
			
				|  |  | +                <img src="./imgs/add.png" @click="createSlide" alt="" />
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +            </div>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              <div class="note-flag" v-if="element.notes && element.notes.length" @click="openNotesPanel()">{{ element.notes.length }}</div>
 | 
	
		
			
				|  |  |            </div>
 | 
	
	
		
			
				|  | @@ -65,26 +81,26 @@
 | 
	
		
			
				|  |  |        </template>
 | 
	
		
			
				|  |  |      </Draggable>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    <div class="page-number">幻灯片 {{slideIndex + 1}} / {{slides.length}}</div>
 | 
	
		
			
				|  |  | +    <div class="page-number">幻灯片 {{ slideIndex + 1 }} / {{ slides.length }}</div>
 | 
	
		
			
				|  |  |    </div>
 | 
	
		
			
				|  |  |  </template>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  <script lang="ts" setup>
 | 
	
		
			
				|  |  | -import { computed, nextTick, ref, watch } from 'vue'
 | 
	
		
			
				|  |  | -import { storeToRefs } from 'pinia'
 | 
	
		
			
				|  |  | -import { useMainStore, useSlidesStore, useKeyboardStore } from '@/store'
 | 
	
		
			
				|  |  | -import { fillDigit } from '@/utils/common'
 | 
	
		
			
				|  |  | -import { isElementInViewport } from '@/utils/element'
 | 
	
		
			
				|  |  | -import type { ContextmenuItem } from '@/components/Contextmenu/types'
 | 
	
		
			
				|  |  | -import useSlideHandler from '@/hooks/useSlideHandler'
 | 
	
		
			
				|  |  | -import useSectionHandler from '@/hooks/useSectionHandler'
 | 
	
		
			
				|  |  | -import useScreening from '@/hooks/useScreening'
 | 
	
		
			
				|  |  | -import useLoadSlides from '@/hooks/useLoadSlides'
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
 | 
	
		
			
				|  |  | -import LayoutPool from './LayoutPool.vue'
 | 
	
		
			
				|  |  | -import Popover from '@/components/Popover.vue'
 | 
	
		
			
				|  |  | -import Draggable from 'vuedraggable'
 | 
	
		
			
				|  |  | +import { computed, nextTick, ref, watch } from "vue"
 | 
	
		
			
				|  |  | +import { storeToRefs } from "pinia"
 | 
	
		
			
				|  |  | +import { useMainStore, useSlidesStore, useKeyboardStore } from "@/store"
 | 
	
		
			
				|  |  | +import { fillDigit } from "@/utils/common"
 | 
	
		
			
				|  |  | +import { isElementInViewport } from "@/utils/element"
 | 
	
		
			
				|  |  | +import type { ContextmenuItem } from "@/components/Contextmenu/types"
 | 
	
		
			
				|  |  | +import useSlideHandler from "@/hooks/useSlideHandler"
 | 
	
		
			
				|  |  | +import useSectionHandler from "@/hooks/useSectionHandler"
 | 
	
		
			
				|  |  | +import useScreening from "@/hooks/useScreening"
 | 
	
		
			
				|  |  | +import useLoadSlides from "@/hooks/useLoadSlides"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import ThumbnailSlide from "@/views/components/ThumbnailSlide/index.vue"
 | 
	
		
			
				|  |  | +import LayoutPool from "./LayoutPool.vue"
 | 
	
		
			
				|  |  | +import Popover from "@/components/Popover.vue"
 | 
	
		
			
				|  |  | +import Draggable from "vuedraggable"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const mainStore = useMainStore()
 | 
	
		
			
				|  |  |  const slidesStore = useSlidesStore()
 | 
	
	
		
			
				|  | @@ -103,45 +119,32 @@ const hasSection = computed(() => {
 | 
	
		
			
				|  |  |    return slides.value.some(item => item.sectionTag)
 | 
	
		
			
				|  |  |  })
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -const {
 | 
	
		
			
				|  |  | -  copySlide,
 | 
	
		
			
				|  |  | -  pasteSlide,
 | 
	
		
			
				|  |  | -  createSlide,
 | 
	
		
			
				|  |  | -  createSlideByTemplate,
 | 
	
		
			
				|  |  | -  copyAndPasteSlide,
 | 
	
		
			
				|  |  | -  deleteSlide,
 | 
	
		
			
				|  |  | -  cutSlide,
 | 
	
		
			
				|  |  | -  selectAllSlide,
 | 
	
		
			
				|  |  | -  sortSlides,
 | 
	
		
			
				|  |  | -} = useSlideHandler()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -const {
 | 
	
		
			
				|  |  | -  createSection,
 | 
	
		
			
				|  |  | -  removeSection,
 | 
	
		
			
				|  |  | -  removeAllSection,
 | 
	
		
			
				|  |  | -  removeSectionSlides,
 | 
	
		
			
				|  |  | -  updateSectionTitle,
 | 
	
		
			
				|  |  | -} = useSectionHandler()
 | 
	
		
			
				|  |  | +const { copySlide, pasteSlide, createSlide, createSlideByTemplate, copyAndPasteSlide, deleteSlide, cutSlide, selectAllSlide, sortSlides } =
 | 
	
		
			
				|  |  | +  useSlideHandler()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const { createSection, removeSection, removeAllSection, removeSectionSlides, updateSectionTitle } = useSectionHandler()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // 页面被切换时
 | 
	
		
			
				|  |  |  const thumbnailsRef = ref<InstanceType<typeof Draggable>>()
 | 
	
		
			
				|  |  | -watch(() => slideIndex.value, () => {
 | 
	
		
			
				|  |  | +watch(
 | 
	
		
			
				|  |  | +  () => slideIndex.value,
 | 
	
		
			
				|  |  | +  () => {
 | 
	
		
			
				|  |  | +    // 清除多选状态的幻灯片
 | 
	
		
			
				|  |  | +    if (selectedSlidesIndex.value.length) {
 | 
	
		
			
				|  |  | +      mainStore.updateSelectedSlidesIndex([])
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // 清除多选状态的幻灯片
 | 
	
		
			
				|  |  | -  if (selectedSlidesIndex.value.length) {
 | 
	
		
			
				|  |  | -    mainStore.updateSelectedSlidesIndex([])
 | 
	
		
			
				|  |  | +    // 检查当前页缩略图是否在可视范围,不在的话需要滚动到对应的位置
 | 
	
		
			
				|  |  | +    nextTick(() => {
 | 
	
		
			
				|  |  | +      const activeThumbnailRef: HTMLElement = thumbnailsRef.value?.$el?.querySelector(".thumbnail-item.active")
 | 
	
		
			
				|  |  | +      if (thumbnailsRef.value && activeThumbnailRef && !isElementInViewport(activeThumbnailRef, thumbnailsRef.value.$el)) {
 | 
	
		
			
				|  |  | +        setTimeout(() => {
 | 
	
		
			
				|  |  | +          activeThumbnailRef.scrollIntoView({ behavior: "smooth" })
 | 
	
		
			
				|  |  | +        }, 100)
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // 检查当前页缩略图是否在可视范围,不在的话需要滚动到对应的位置
 | 
	
		
			
				|  |  | -  nextTick(() => {
 | 
	
		
			
				|  |  | -    const activeThumbnailRef: HTMLElement = thumbnailsRef.value?.$el?.querySelector('.thumbnail-item.active')
 | 
	
		
			
				|  |  | -    if (thumbnailsRef.value && activeThumbnailRef && !isElementInViewport(activeThumbnailRef, thumbnailsRef.value.$el)) {
 | 
	
		
			
				|  |  | -      setTimeout(() => {
 | 
	
		
			
				|  |  | -        activeThumbnailRef.scrollIntoView({ behavior: 'smooth' })
 | 
	
		
			
				|  |  | -      }, 100)
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  })
 | 
	
		
			
				|  |  | -})
 | 
	
		
			
				|  |  | +)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // 切换页面
 | 
	
		
			
				|  |  |  const changeSlideIndex = (index: number) => {
 | 
	
	
		
			
				|  | @@ -168,13 +171,11 @@ const handleClickSlideThumbnail = (e: MouseEvent, index: number) => {
 | 
	
		
			
				|  |  |        const newSelectedSlidesIndex = selectedSlidesIndex.value.filter(item => item !== index)
 | 
	
		
			
				|  |  |        mainStore.updateSelectedSlidesIndex(newSelectedSlidesIndex)
 | 
	
		
			
				|  |  |        changeSlideIndex(selectedSlidesIndex.value[0])
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    else {
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  |        if (selectedSlidesIndex.value.includes(index)) {
 | 
	
		
			
				|  |  |          const newSelectedSlidesIndex = selectedSlidesIndex.value.filter(item => item !== index)
 | 
	
		
			
				|  |  |          mainStore.updateSelectedSlidesIndex(newSelectedSlidesIndex)
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      else {
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  |          const newSelectedSlidesIndex = [...selectedSlidesIndex.value, index]
 | 
	
		
			
				|  |  |          mainStore.updateSelectedSlidesIndex(newSelectedSlidesIndex)
 | 
	
		
			
				|  |  |        }
 | 
	
	
		
			
				|  | @@ -223,14 +224,14 @@ const openNotesPanel = () => {
 | 
	
		
			
				|  |  |    mainStore.setNotesPanelState(true)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -const editingSectionId = ref('')
 | 
	
		
			
				|  |  | +const editingSectionId = ref("")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const editSection = (id: string) => {
 | 
	
		
			
				|  |  |    mainStore.setDisableHotkeysState(true)
 | 
	
		
			
				|  |  | -  editingSectionId.value = id || 'default'
 | 
	
		
			
				|  |  | +  editingSectionId.value = id || "default"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    nextTick(() => {
 | 
	
		
			
				|  |  | -    const inputRef = document.querySelector(`#section-title-input-${id || 'default'}`) as HTMLInputElement
 | 
	
		
			
				|  |  | +    const inputRef = document.querySelector(`#section-title-input-${id || "default"}`) as HTMLInputElement
 | 
	
		
			
				|  |  |      inputRef.focus()
 | 
	
		
			
				|  |  |    })
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -239,7 +240,7 @@ const saveSection = (e: FocusEvent | KeyboardEvent) => {
 | 
	
		
			
				|  |  |    const title = (e.target as HTMLInputElement).value
 | 
	
		
			
				|  |  |    updateSectionTitle(editingSectionId.value, title)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  editingSectionId.value = ''
 | 
	
		
			
				|  |  | +  editingSectionId.value = ""
 | 
	
		
			
				|  |  |    mainStore.setDisableHotkeysState(false)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -248,24 +249,24 @@ const contextmenusSection = (el: HTMLElement): ContextmenuItem[] => {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    return [
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '删除节',
 | 
	
		
			
				|  |  | -      handler: () => removeSection(sectionId),
 | 
	
		
			
				|  |  | +      text: "删除节",
 | 
	
		
			
				|  |  | +      handler: () => removeSection(sectionId)
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '删除节和幻灯片',
 | 
	
		
			
				|  |  | +      text: "删除节和幻灯片",
 | 
	
		
			
				|  |  |        handler: () => {
 | 
	
		
			
				|  |  |          mainStore.setActiveElementIdList([])
 | 
	
		
			
				|  |  |          removeSectionSlides(sectionId)
 | 
	
		
			
				|  |  | -      },
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '删除所有节',
 | 
	
		
			
				|  |  | -      handler: removeAllSection,
 | 
	
		
			
				|  |  | +      text: "删除所有节",
 | 
	
		
			
				|  |  | +      handler: removeAllSection
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '重命名节',
 | 
	
		
			
				|  |  | -      handler: () => editSection(sectionId),
 | 
	
		
			
				|  |  | -    },
 | 
	
		
			
				|  |  | +      text: "重命名节",
 | 
	
		
			
				|  |  | +      handler: () => editSection(sectionId)
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    ]
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -274,77 +275,77 @@ const { enterScreening, enterScreeningFromStart } = useScreening()
 | 
	
		
			
				|  |  |  const contextmenusThumbnails = (): ContextmenuItem[] => {
 | 
	
		
			
				|  |  |    return [
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '粘贴',
 | 
	
		
			
				|  |  | -      subText: 'Ctrl + V',
 | 
	
		
			
				|  |  | -      handler: pasteSlide,
 | 
	
		
			
				|  |  | +      text: "粘贴",
 | 
	
		
			
				|  |  | +      subText: "Ctrl + V",
 | 
	
		
			
				|  |  | +      handler: pasteSlide
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '全选',
 | 
	
		
			
				|  |  | -      subText: 'Ctrl + A',
 | 
	
		
			
				|  |  | -      handler: selectAllSlide,
 | 
	
		
			
				|  |  | +      text: "全选",
 | 
	
		
			
				|  |  | +      subText: "Ctrl + A",
 | 
	
		
			
				|  |  | +      handler: selectAllSlide
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '新建页面',
 | 
	
		
			
				|  |  | -      subText: 'Enter',
 | 
	
		
			
				|  |  | -      handler: createSlide,
 | 
	
		
			
				|  |  | +      text: "新建页面",
 | 
	
		
			
				|  |  | +      subText: "Enter",
 | 
	
		
			
				|  |  | +      handler: createSlide
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '幻灯片放映',
 | 
	
		
			
				|  |  | -      subText: 'F5',
 | 
	
		
			
				|  |  | -      handler: enterScreeningFromStart,
 | 
	
		
			
				|  |  | -    },
 | 
	
		
			
				|  |  | +      text: "幻灯片放映",
 | 
	
		
			
				|  |  | +      subText: "F5",
 | 
	
		
			
				|  |  | +      handler: enterScreeningFromStart
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    ]
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const contextmenusThumbnailItem = (): ContextmenuItem[] => {
 | 
	
		
			
				|  |  |    return [
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '剪切',
 | 
	
		
			
				|  |  | -      subText: 'Ctrl + X',
 | 
	
		
			
				|  |  | -      handler: cutSlide,
 | 
	
		
			
				|  |  | +      text: "剪切",
 | 
	
		
			
				|  |  | +      subText: "Ctrl + X",
 | 
	
		
			
				|  |  | +      handler: cutSlide
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '复制',
 | 
	
		
			
				|  |  | -      subText: 'Ctrl + C',
 | 
	
		
			
				|  |  | -      handler: copySlide,
 | 
	
		
			
				|  |  | +      text: "复制",
 | 
	
		
			
				|  |  | +      subText: "Ctrl + C",
 | 
	
		
			
				|  |  | +      handler: copySlide
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '粘贴',
 | 
	
		
			
				|  |  | -      subText: 'Ctrl + V',
 | 
	
		
			
				|  |  | -      handler: pasteSlide,
 | 
	
		
			
				|  |  | +      text: "粘贴",
 | 
	
		
			
				|  |  | +      subText: "Ctrl + V",
 | 
	
		
			
				|  |  | +      handler: pasteSlide
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '全选',
 | 
	
		
			
				|  |  | -      subText: 'Ctrl + A',
 | 
	
		
			
				|  |  | -      handler: selectAllSlide,
 | 
	
		
			
				|  |  | +      text: "全选",
 | 
	
		
			
				|  |  | +      subText: "Ctrl + A",
 | 
	
		
			
				|  |  | +      handler: selectAllSlide
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      { divider: true },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '新建页面',
 | 
	
		
			
				|  |  | -      subText: 'Enter',
 | 
	
		
			
				|  |  | -      handler: createSlide,
 | 
	
		
			
				|  |  | +      text: "新建页面",
 | 
	
		
			
				|  |  | +      subText: "Enter",
 | 
	
		
			
				|  |  | +      handler: createSlide
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '复制页面',
 | 
	
		
			
				|  |  | -      subText: 'Ctrl + D',
 | 
	
		
			
				|  |  | -      handler: copyAndPasteSlide,
 | 
	
		
			
				|  |  | +      text: "复制页面",
 | 
	
		
			
				|  |  | +      subText: "Ctrl + D",
 | 
	
		
			
				|  |  | +      handler: copyAndPasteSlide
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '删除页面',
 | 
	
		
			
				|  |  | -      subText: 'Delete',
 | 
	
		
			
				|  |  | -      handler: () => deleteSlide(),
 | 
	
		
			
				|  |  | +      text: "删除页面",
 | 
	
		
			
				|  |  | +      subText: "Delete",
 | 
	
		
			
				|  |  | +      handler: () => deleteSlide()
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '增加节',
 | 
	
		
			
				|  |  | +      text: "增加节",
 | 
	
		
			
				|  |  |        handler: createSection,
 | 
	
		
			
				|  |  | -      disable: !!currentSlide.value.sectionTag,
 | 
	
		
			
				|  |  | +      disable: !!currentSlide.value.sectionTag
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      { divider: true },
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -      text: '从当前放映',
 | 
	
		
			
				|  |  | -      subText: 'Shift + F5',
 | 
	
		
			
				|  |  | -      handler: enterScreening,
 | 
	
		
			
				|  |  | -    },
 | 
	
		
			
				|  |  | +      text: "从当前放映",
 | 
	
		
			
				|  |  | +      subText: "Shift + F5",
 | 
	
		
			
				|  |  | +      handler: enterScreening
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    ]
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  </script>
 | 
	
	
		
			
				|  | @@ -358,34 +359,48 @@ const contextmenusThumbnailItem = (): ContextmenuItem[] => {
 | 
	
		
			
				|  |  |    user-select: none;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  .add-slide {
 | 
	
		
			
				|  |  | -  height: 40px;
 | 
	
		
			
				|  |  | +  margin-top: 16px;
 | 
	
		
			
				|  |  | +  margin-bottom: 8px;
 | 
	
		
			
				|  |  |    font-size: 12px;
 | 
	
		
			
				|  |  |    display: flex;
 | 
	
		
			
				|  |  |    flex-shrink: 0;
 | 
	
		
			
				|  |  | -  border-bottom: 1px solid $borderColor;
 | 
	
		
			
				|  |  | -  cursor: pointer;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +  justify-content: center;
 | 
	
		
			
				|  |  | +  align-items: center;
 | 
	
		
			
				|  |  |    .btn {
 | 
	
		
			
				|  |  | -    flex: 1;
 | 
	
		
			
				|  |  | +    cursor: pointer;
 | 
	
		
			
				|  |  | +    flex-shrink: 1;
 | 
	
		
			
				|  |  |      display: flex;
 | 
	
		
			
				|  |  |      justify-content: center;
 | 
	
		
			
				|  |  |      align-items: center;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +    width: 180px;
 | 
	
		
			
				|  |  | +    height: 30px;
 | 
	
		
			
				|  |  | +    background: #ffffff;
 | 
	
		
			
				|  |  | +    border-radius: 4px;
 | 
	
		
			
				|  |  | +    border: 1px solid #c4c4c4;
 | 
	
		
			
				|  |  |      &:hover {
 | 
	
		
			
				|  |  |        background-color: $lightGray;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    .select-btn {
 | 
	
		
			
				|  |  | +    cursor: pointer;
 | 
	
		
			
				|  |  | +    margin-left: 12px;
 | 
	
		
			
				|  |  |      width: 30px;
 | 
	
		
			
				|  |  |      height: 100%;
 | 
	
		
			
				|  |  |      display: flex;
 | 
	
		
			
				|  |  |      justify-content: center;
 | 
	
		
			
				|  |  |      align-items: center;
 | 
	
		
			
				|  |  | -    border-left: 1px solid $borderColor;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +    width: 30px;
 | 
	
		
			
				|  |  | +    height: 30px;
 | 
	
		
			
				|  |  | +    background: #ffffff;
 | 
	
		
			
				|  |  | +    border-radius: 4px;
 | 
	
		
			
				|  |  | +    border: 1px solid #c4c4c4;
 | 
	
		
			
				|  |  |      &:hover {
 | 
	
		
			
				|  |  |        background-color: $lightGray;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    & > img {
 | 
	
		
			
				|  |  | +      width: 20px;
 | 
	
		
			
				|  |  | +      height: 20px;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    .icon {
 | 
	
	
		
			
				|  | @@ -394,7 +409,6 @@ const contextmenusThumbnailItem = (): ContextmenuItem[] => {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  .thumbnail-list {
 | 
	
		
			
				|  |  | -  padding: 5px 0;
 | 
	
		
			
				|  |  |    flex: 1;
 | 
	
		
			
				|  |  |    overflow: auto;
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -402,12 +416,32 @@ const contextmenusThumbnailItem = (): ContextmenuItem[] => {
 | 
	
		
			
				|  |  |    display: flex;
 | 
	
		
			
				|  |  |    justify-content: center;
 | 
	
		
			
				|  |  |    align-items: center;
 | 
	
		
			
				|  |  | -  padding: 5px 0;
 | 
	
		
			
				|  |  | +  padding: 8px 0;
 | 
	
		
			
				|  |  |    position: relative;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    .thumbnail {
 | 
	
		
			
				|  |  | -    border-radius: $borderRadius;
 | 
	
		
			
				|  |  | -    outline: 2px solid rgba($color: $themeColor, $alpha: .15);
 | 
	
		
			
				|  |  | +    overflow: hidden;
 | 
	
		
			
				|  |  | +    border-radius: 4px;
 | 
	
		
			
				|  |  | +    outline: 1px solid #dedede;
 | 
	
		
			
				|  |  | +    position: relative;
 | 
	
		
			
				|  |  | +    .tools {
 | 
	
		
			
				|  |  | +      position: absolute;
 | 
	
		
			
				|  |  | +      bottom: 8px;
 | 
	
		
			
				|  |  | +      left: 50%;
 | 
	
		
			
				|  |  | +      transform: translateX(-50%);
 | 
	
		
			
				|  |  | +      display: flex;
 | 
	
		
			
				|  |  | +      & > img {
 | 
	
		
			
				|  |  | +        cursor: pointer;
 | 
	
		
			
				|  |  | +        width: 32px;
 | 
	
		
			
				|  |  | +        height: 32px;
 | 
	
		
			
				|  |  | +        &:hover {
 | 
	
		
			
				|  |  | +          opacity: 0.8;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        &:last-child {
 | 
	
		
			
				|  |  | +          margin-left: 40px;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    &.active {
 | 
	
	
		
			
				|  | @@ -420,7 +454,7 @@ const contextmenusThumbnailItem = (): ContextmenuItem[] => {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    &.selected {
 | 
	
		
			
				|  |  |      .thumbnail {
 | 
	
		
			
				|  |  | -      outline-color: $themeColor;
 | 
	
		
			
				|  |  | +      outline: 2px solid $themeColor;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      .note-flag {
 | 
	
		
			
				|  |  |        background-color: $themeColor;
 | 
	
	
		
			
				|  | @@ -436,24 +470,24 @@ const contextmenusThumbnailItem = (): ContextmenuItem[] => {
 | 
	
		
			
				|  |  |      height: 12px;
 | 
	
		
			
				|  |  |      border-radius: 1px;
 | 
	
		
			
				|  |  |      position: absolute;
 | 
	
		
			
				|  |  | -    left: 8px;
 | 
	
		
			
				|  |  | -    top: 13px;
 | 
	
		
			
				|  |  | +    left: 22px;
 | 
	
		
			
				|  |  | +    top: 26px;
 | 
	
		
			
				|  |  |      font-size: 8px;
 | 
	
		
			
				|  |  | -    background-color: rgba($color: $themeColor, $alpha: .75);
 | 
	
		
			
				|  |  | +    background-color: rgba($color: $themeColor, $alpha: 0.75);
 | 
	
		
			
				|  |  |      color: #fff;
 | 
	
		
			
				|  |  |      text-align: center;
 | 
	
		
			
				|  |  |      line-height: 12px;
 | 
	
		
			
				|  |  |      cursor: pointer;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      &::after {
 | 
	
		
			
				|  |  | -      content: '';
 | 
	
		
			
				|  |  | +      content: "";
 | 
	
		
			
				|  |  |        width: 0;
 | 
	
		
			
				|  |  |        height: 0;
 | 
	
		
			
				|  |  |        position: absolute;
 | 
	
		
			
				|  |  |        top: 10px;
 | 
	
		
			
				|  |  |        left: 4px;
 | 
	
		
			
				|  |  |        border: 4px solid transparent;
 | 
	
		
			
				|  |  | -      border-top-color: rgba($color: $themeColor, $alpha: .75);
 | 
	
		
			
				|  |  | +      border-top-color: rgba($color: $themeColor, $alpha: 0.75);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -461,7 +495,10 @@ const contextmenusThumbnailItem = (): ContextmenuItem[] => {
 | 
	
		
			
				|  |  |    font-size: 12px;
 | 
	
		
			
				|  |  |    color: #999;
 | 
	
		
			
				|  |  |    width: 20px;
 | 
	
		
			
				|  |  | +  font-weight: 600;
 | 
	
		
			
				|  |  | +  font-size: 14px;
 | 
	
		
			
				|  |  |    cursor: grab;
 | 
	
		
			
				|  |  | +  margin-right: 18px;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    &.offset-left {
 | 
	
		
			
				|  |  |      position: relative;
 | 
	
	
		
			
				|  | @@ -502,7 +539,7 @@ const contextmenusThumbnailItem = (): ContextmenuItem[] => {
 | 
	
		
			
				|  |  |      position: relative;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      &::before {
 | 
	
		
			
				|  |  | -      content: '';
 | 
	
		
			
				|  |  | +      content: "";
 | 
	
		
			
				|  |  |        width: 0;
 | 
	
		
			
				|  |  |        height: 0;
 | 
	
		
			
				|  |  |        border-top: 3px solid transparent;
 |