useSlideHandler.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import { computed } from 'vue'
  2. import { storeToRefs } from 'pinia'
  3. import { nanoid } from 'nanoid'
  4. import { useMainStore, useSlidesStore } from '@/store'
  5. import type { Slide } from '@/types/slides'
  6. import { copyText, readClipboard } from '@/utils/clipboard'
  7. import { encrypt } from '@/utils/crypto'
  8. import { createElementIdMap } from '@/utils/element'
  9. import { KEYS } from '@/configs/hotkey'
  10. import message from '@/utils/message'
  11. import usePasteTextClipboardData from '@/hooks/usePasteTextClipboardData'
  12. import useHistorySnapshot from '@/hooks/useHistorySnapshot'
  13. import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
  14. export default () => {
  15. const mainStore = useMainStore()
  16. const slidesStore = useSlidesStore()
  17. const { selectedSlidesIndex: _selectedSlidesIndex, activeElementIdList } = storeToRefs(mainStore)
  18. const { currentSlide, slides, theme, slideIndex } = storeToRefs(slidesStore)
  19. const selectedSlidesIndex = computed(() => [..._selectedSlidesIndex.value, slideIndex.value])
  20. const selectedSlides = computed(() => slides.value.filter((item, index) => selectedSlidesIndex.value.includes(index)))
  21. const selectedSlidesId = computed(() => selectedSlides.value.map(item => item.id))
  22. const { pasteTextClipboardData } = usePasteTextClipboardData()
  23. const { addSlidesFromData } = useAddSlidesOrElements()
  24. const { addHistorySnapshot } = useHistorySnapshot()
  25. // 重置幻灯片
  26. const resetSlides = () => {
  27. const emptySlide: Slide = {
  28. id: nanoid(10),
  29. elements: [],
  30. background: {
  31. type: 'solid',
  32. color: theme.value.backgroundColor,
  33. },
  34. }
  35. slidesStore.updateSlideIndex(0)
  36. mainStore.setActiveElementIdList([])
  37. slidesStore.setSlides([emptySlide])
  38. }
  39. /**
  40. * 移动页面焦点
  41. * @param command 移动页面焦点命令:上移、下移
  42. */
  43. const updateSlideIndex = (command: string) => {
  44. if (command === KEYS.UP && slideIndex.value > 0) {
  45. if (activeElementIdList.value.length) mainStore.setActiveElementIdList([])
  46. slidesStore.updateSlideIndex(slideIndex.value - 1)
  47. }
  48. else if (command === KEYS.DOWN && slideIndex.value < slides.value.length - 1) {
  49. if (activeElementIdList.value.length) mainStore.setActiveElementIdList([])
  50. slidesStore.updateSlideIndex(slideIndex.value + 1)
  51. }
  52. }
  53. // 将当前页面数据加密后复制到剪贴板
  54. const copySlide = () => {
  55. const text = encrypt(JSON.stringify({
  56. type: 'slides',
  57. data: selectedSlides.value,
  58. }))
  59. copyText(text).then(() => {
  60. mainStore.setThumbnailsFocus(true)
  61. })
  62. }
  63. // 尝试将剪贴板页面数据解密后添加到下一页(粘贴)
  64. const pasteSlide = () => {
  65. readClipboard().then(text => {
  66. pasteTextClipboardData(text, { onlySlide: true })
  67. }).catch(err => message.warning(err))
  68. }
  69. // 创建一页空白页并添加到下一页
  70. const createSlide = () => {
  71. const emptySlide: Slide = {
  72. id: nanoid(10),
  73. elements: [],
  74. background: {
  75. type: 'solid',
  76. color: theme.value.backgroundColor,
  77. },
  78. }
  79. mainStore.setActiveElementIdList([])
  80. slidesStore.addSlide(emptySlide)
  81. addHistorySnapshot()
  82. }
  83. // 根据模板创建新页面
  84. const createSlideByTemplate = (slide: Slide) => {
  85. const { groupIdMap, elIdMap } = createElementIdMap(slide.elements)
  86. for (const element of slide.elements) {
  87. element.id = elIdMap[element.id]
  88. if (element.groupId) element.groupId = groupIdMap[element.groupId]
  89. }
  90. const newSlide = {
  91. ...slide,
  92. id: nanoid(10),
  93. }
  94. mainStore.setActiveElementIdList([])
  95. slidesStore.addSlide(newSlide)
  96. addHistorySnapshot()
  97. }
  98. // 将当前页复制一份到下一页
  99. const copyAndPasteSlide = () => {
  100. const slide = JSON.parse(JSON.stringify(currentSlide.value))
  101. addSlidesFromData([slide])
  102. }
  103. // 删除当前页,若将删除全部页面,则执行重置幻灯片操作
  104. const deleteSlide = (targetSlidesId = selectedSlidesId.value) => {
  105. if (slides.value.length === targetSlidesId.length) resetSlides()
  106. else slidesStore.deleteSlide(targetSlidesId)
  107. mainStore.updateSelectedSlidesIndex([])
  108. addHistorySnapshot()
  109. }
  110. // 将当前页复制后删除(剪切)
  111. // 由于复制操作会导致多选状态消失,所以需要提前将需要删除的页面ID进行缓存
  112. const cutSlide = () => {
  113. const targetSlidesId = [...selectedSlidesId.value]
  114. copySlide()
  115. deleteSlide(targetSlidesId)
  116. }
  117. // 选中全部幻灯片
  118. const selectAllSlide = () => {
  119. const newSelectedSlidesIndex = Array.from(Array(slides.value.length), (item, index) => index)
  120. mainStore.setActiveElementIdList([])
  121. mainStore.updateSelectedSlidesIndex(newSelectedSlidesIndex)
  122. }
  123. // 拖拽调整幻灯片顺序同步数据
  124. const sortSlides = (newIndex: number, oldIndex: number) => {
  125. if (oldIndex === newIndex) return
  126. const _slides: Slide[] = JSON.parse(JSON.stringify(slides.value))
  127. const movingSlide = _slides[oldIndex]
  128. const movingSlideSection = movingSlide.sectionTag
  129. if (movingSlideSection) {
  130. const movingSlideSectionNext = _slides[oldIndex + 1]
  131. delete movingSlide.sectionTag
  132. if (movingSlideSectionNext && !movingSlideSectionNext.sectionTag) {
  133. movingSlideSectionNext.sectionTag = movingSlideSection
  134. }
  135. }
  136. if (newIndex === 0) {
  137. const firstSection = _slides[0].sectionTag
  138. if (firstSection) {
  139. delete _slides[0].sectionTag
  140. movingSlide.sectionTag = firstSection
  141. }
  142. }
  143. const _slide = _slides[oldIndex]
  144. _slides.splice(oldIndex, 1)
  145. _slides.splice(newIndex, 0, _slide)
  146. slidesStore.setSlides(_slides)
  147. slidesStore.updateSlideIndex(newIndex)
  148. }
  149. const isEmptySlide = computed(() => {
  150. if (slides.value.length > 1) return false
  151. if (slides.value[0].elements.length > 0) return false
  152. return true
  153. })
  154. return {
  155. resetSlides,
  156. updateSlideIndex,
  157. copySlide,
  158. pasteSlide,
  159. createSlide,
  160. createSlideByTemplate,
  161. copyAndPasteSlide,
  162. deleteSlide,
  163. cutSlide,
  164. selectAllSlide,
  165. sortSlides,
  166. isEmptySlide,
  167. }
  168. }