Browse Source

幻灯片修改

黄琪勇 4 months ago
parent
commit
f2969a4790

+ 1 - 1
src/store/user.ts

@@ -6,7 +6,7 @@ import { store } from "./index"
 import { getUserInfoApi } from "@/api/user"
 interface userType {
   userInfo: {
-    username?: string
+    nickname?: string
   }
   roles: ""
 }

+ 16 - 5
src/views/Editor/CanvasTool/index.vue

@@ -144,7 +144,7 @@
         <img class="itemImg" src="./imgs/gdgj.png" alt="" />
         <Popover trigger="click" v-model:value="moreToolsVisible" :offset="10" @click.stop>
           <template #content>
-            <PopoverMenuItem>
+            <PopoverMenuItem @click="chartPoolVisible = true">
               <Popover trigger="click" v-model:value="chartPoolVisible" placement="right" :offsetOne="50" :offset="36">
                 <template #content>
                   <ChartPool
@@ -162,7 +162,7 @@
                 </div>
               </Popover>
             </PopoverMenuItem>
-            <PopoverMenuItem>
+            <PopoverMenuItem @click="tableGeneratorVisible = true">
               <Popover trigger="click" v-model:value="tableGeneratorVisible" placement="right" :offsetOne="130" :offset="36">
                 <template #content>
                   <TableGenerator
@@ -181,8 +181,15 @@
                 </div>
               </Popover>
             </PopoverMenuItem>
-            <PopoverMenuItem>
-              <div class="menuItem" @click="latexEditorVisible = true">
+            <PopoverMenuItem
+              @click="
+                () => {
+                  moreToolsVisible = false
+                  latexEditorVisible = true
+                }
+              "
+            >
+              <div class="menuItem">
                 <img src="./imgs/gs.png" alt="" />
                 <div class="tit">公式</div>
               </div>
@@ -393,7 +400,10 @@ const toggleNotesPanel = () => {
     cursor: pointer;
     &.disable {
       opacity: 0.5;
+      cursor: not-allowed;
+      background-color: transparent !important;
     }
+    &:hover,
     &.active {
       background: rgba(34, 71, 133, 0.08);
       border-radius: 6px;
@@ -453,7 +463,7 @@ const toggleNotesPanel = () => {
     align-items: center;
     justify-content: center;
     cursor: pointer;
-    width: 72px;
+    width: 68px;
     height: 53px;
     &:hover,
     &.active {
@@ -511,6 +521,7 @@ const toggleNotesPanel = () => {
     line-height: 32px;
     text-align: center;
     cursor: pointer;
+    &:hover,
     &.canvasScaleVisible {
       border-radius: 6px;
       background-color: rgba(34, 71, 133, 0.08);

+ 1 - 0
src/views/Editor/EditorHeader/index.vue

@@ -113,6 +113,7 @@ const setDialogForExport = (type: DialogForExportTypes) => {
   height: 34px;
   border-radius: 6px;
   margin-left: -8px;
+  &:hover,
   &.menuVisible {
     background: rgba(34, 71, 133, 0.08);
   }

+ 50 - 48
src/views/Editor/NotesPanel.vue

@@ -1,10 +1,10 @@
 <template>
-  <MoveablePanel 
-    class="notes-panel" 
-    :width="300" 
-    :height="560" 
-    :title="`幻灯片${slideIndex + 1}的批注`" 
-    :left="-270" 
+  <MoveablePanel
+    class="notes-panel"
+    :width="300"
+    :height="560"
+    :title="`幻灯片${slideIndex + 1}的批注`"
+    :left="-270"
     :top="90"
     :minWidth="300"
     :minHeight="400"
@@ -15,7 +15,7 @@
   >
     <div class="container">
       <div class="notes">
-        <div class="note" :class="{ 'active': activeNoteId === note.id }" v-for="note in notes" :key="note.id" @click="handleClickNote(note)">
+        <div class="note" :class="{ active: activeNoteId === note.id }" v-for="note in notes" :key="note.id" @click="handleClickNote(note)">
           <div class="header note-header">
             <div class="user">
               <div class="avatar"><IconUser /></div>
@@ -58,13 +58,18 @@
         <div class="empty" v-if="!notes.length">本页暂无批注</div>
       </div>
       <div class="send">
-        <TextArea 
+        <TextArea
           ref="textAreaRef"
           v-model:value="content"
           :padding="6"
-          :placeholder="`输入批注(为${handleElementId ? '选中元素' : '当前页幻灯片' })`"
+          :placeholder="`输入批注(为${handleElementId ? '选中元素' : '当前页幻灯片'})`"
           :rows="2"
-          @focus="replyNoteId = ''; activeNoteId = ''"
+          @focus="
+            () => {
+              replyNoteId = ''
+              activeNoteId = ''
+            }
+          "
         />
         <div class="footer">
           <IconDelete class="btn icon" v-tooltip="'清空本页批注'" style="flex: 1" @click="clear()" />
@@ -76,31 +81,34 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, computed, watch } from 'vue'
-import { storeToRefs } from 'pinia'
-import { nanoid } from 'nanoid'
-import { useMainStore, useSlidesStore } from '@/store'
-import type { Note } from '@/types/slides'
+import { ref, computed, watch } from "vue"
+import { storeToRefs } from "pinia"
+import { nanoid } from "nanoid"
+import { useMainStore, useSlidesStore } from "@/store"
+import type { Note } from "@/types/slides"
 
-import MoveablePanel from '@/components/MoveablePanel.vue'
-import TextArea from '@/components/TextArea.vue'
-import Button from '@/components/Button.vue'
+import MoveablePanel from "@/components/MoveablePanel.vue"
+import TextArea from "@/components/TextArea.vue"
+import Button from "@/components/Button.vue"
+import userStore from "@/store/user"
 
 const slidesStore = useSlidesStore()
 const mainStore = useMainStore()
 const { slideIndex, currentSlide } = storeToRefs(slidesStore)
 const { handleElementId } = storeToRefs(mainStore)
 
-const content = ref('')
-const replyContent = ref('')
+const content = ref("")
+const replyContent = ref("")
 const notes = computed(() => currentSlide.value?.notes || [])
-const activeNoteId = ref('')
-const replyNoteId = ref('')
+const activeNoteId = ref("")
+const replyNoteId = ref("")
 const textAreaRef = ref<InstanceType<typeof TextArea>>()
 
+const userStoreHook = userStore()
+
 watch(slideIndex, () => {
-  activeNoteId.value = ''
-  replyNoteId.value = ''
+  activeNoteId.value = ""
+  replyNoteId.value = ""
 })
 
 const createNote = () => {
@@ -113,17 +121,14 @@ const createNote = () => {
     id: nanoid(),
     content: content.value,
     time: new Date().getTime(),
-    user: '测试用户',
+    user: userStoreHook.userInfo.nickname || "用户" + new Date().getTime()
   }
   if (handleElementId.value) newNote.elId = handleElementId.value
 
-  const newNotes = [
-    ...notes.value,
-    newNote,
-  ]
+  const newNotes = [...notes.value, newNote]
   slidesStore.updateSlide({ notes: newNotes })
 
-  content.value = ''
+  content.value = ""
 }
 
 const deleteNote = (id: string) => {
@@ -133,40 +138,40 @@ const deleteNote = (id: string) => {
 
 const createNoteReply = () => {
   if (!replyContent.value) return
-  
+
   const currentNote = notes.value.find(note => note.id === replyNoteId.value)
   if (!currentNote) return
 
   const newReplies = [
-    ...currentNote.replies || [],
+    ...(currentNote.replies || []),
     {
       id: nanoid(),
       content: replyContent.value,
       time: new Date().getTime(),
-      user: '测试用户',
-    },
+      user: userStoreHook.userInfo.nickname || "用户" + new Date().getTime()
+    }
   ]
   const newNote: Note = {
     ...currentNote,
-    replies: newReplies,
+    replies: newReplies
   }
-  const newNotes = notes.value.map(note => note.id === replyNoteId.value ? newNote : note)
+  const newNotes = notes.value.map(note => (note.id === replyNoteId.value ? newNote : note))
   slidesStore.updateSlide({ notes: newNotes })
 
-  replyContent.value = ''
-  replyNoteId.value = ''
+  replyContent.value = ""
+  replyNoteId.value = ""
 }
 
 const deleteReply = (noteId: string, replyId: string) => {
   const currentNote = notes.value.find(note => note.id === noteId)
   if (!currentNote || !currentNote.replies) return
-  
+
   const newReplies = currentNote.replies.filter(reply => reply.id !== replyId)
   const newNote: Note = {
     ...currentNote,
-    replies: newReplies,
+    replies: newReplies
   }
-  const newNotes = notes.value.map(note => note.id === noteId ? newNote : note)
+  const newNotes = notes.value.map(note => (note.id === noteId ? newNote : note))
   slidesStore.updateSlide({ notes: newNotes })
 }
 
@@ -177,10 +182,8 @@ const handleClickNote = (note: Note) => {
     const elIds = currentSlide.value.elements.map(item => item.id)
     if (elIds.includes(note.elId)) {
       mainStore.setActiveElementIdList([note.elId])
-    }
-    else mainStore.setActiveElementIdList([])
-  }
-  else mainStore.setActiveElementIdList([])
+    } else mainStore.setActiveElementIdList([])
+  } else mainStore.setActiveElementIdList([])
 }
 
 const clear = () => {
@@ -314,7 +317,6 @@ const close = () => {
   flex-direction: column;
   justify-content: flex-end;
 
-  
   .footer {
     margin-top: 10px;
     display: flex;
@@ -323,7 +325,7 @@ const close = () => {
       display: flex;
       justify-content: center;
       align-items: center;
-      
+
       &.icon {
         font-size: 18px;
         color: #666;
@@ -336,4 +338,4 @@ const close = () => {
     }
   }
 }
-</style>
+</style>

+ 13 - 16
src/views/Editor/Thumbnails/LayoutPool.vue

@@ -2,11 +2,7 @@
   <div class="layout-pool">
     <div class="header">页面模板</div>
     <div class="list">
-      <div 
-        class="layout-item"
-        v-for="slide in layouts" 
-        :key="slide.id"
-      >
+      <div class="layout-item" v-for="slide in layouts" :key="slide.id">
         <ThumbnailSlide class="thumbnail" :slide="slide" :size="180" />
 
         <div class="btns">
@@ -14,26 +10,26 @@
         </div>
       </div>
     </div>
-    </div>
+  </div>
 </template>
 
 <script lang="ts" setup>
-import { storeToRefs } from 'pinia'
-import { useSlidesStore } from '@/store'
-import type { Slide } from '@/types/slides'
+import { storeToRefs } from "pinia"
+import { useSlidesStore } from "@/store"
+import type { Slide } from "@/types/slides"
 
-import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
-import Button from '@/components/Button.vue'
+import ThumbnailSlide from "@/views/components/ThumbnailSlide/index.vue"
+import Button from "@/components/Button.vue"
 
 const emit = defineEmits<{
-  (event: 'select', payload: Slide): void
+  (event: "select", payload: Slide): void
 }>()
 
 const slidesStore = useSlidesStore()
 const { layouts } = storeToRefs(slidesStore)
 
 const insertTemplate = (slide: Slide) => {
-  emit('select', slide)
+  emit("select", slide)
 }
 </script>
 
@@ -61,7 +57,8 @@ const insertTemplate = (slide: Slide) => {
   position: relative;
   @include flex-grid-layout-children(2, 48%);
 
-  &:nth-last-child(2), &:last-child {
+  &:nth-last-child(2),
+  &:last-child {
     margin-bottom: 0;
   }
 
@@ -80,7 +77,7 @@ const insertTemplate = (slide: Slide) => {
     justify-content: center;
     align-items: center;
     display: flex;
-    background-color: rgba($color: #000, $alpha: .25);
+    background-color: rgba($color: #000, $alpha: 0.25);
     opacity: 0;
     transition: opacity $transitionDelay;
   }
@@ -92,4 +89,4 @@ const insertTemplate = (slide: Slide) => {
     cursor: pointer;
   }
 }
-</style>
+</style>

BIN
src/views/Editor/Thumbnails/imgs/add.png


BIN
src/views/Editor/Thumbnails/imgs/list.png


BIN
src/views/Editor/Thumbnails/imgs/play.png


+ 171 - 134
src/views/Editor/Thumbnails/index.vue

@@ -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;