index.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <template>
  2. <div class="editor-header">
  3. <div class="left">
  4. <Popover trigger="click" placement="bottom-start" v-model:value="mainMenuVisible">
  5. <template #content>
  6. <!-- <PopoverMenuItem @click="openAIPPTDialog(); mainMenuVisible = false">AI 生成 PPT</PopoverMenuItem> -->
  7. <FileInput accept="application/vnd.openxmlformats-officedocument.presentationml.presentation" @change="files => {
  8. importPPTXFile(files)
  9. mainMenuVisible = false
  10. }">
  11. <PopoverMenuItem>导入 PPTX 文件</PopoverMenuItem>
  12. </FileInput>
  13. <!-- <FileInput accept=".json" @change="files => {
  14. importJSON(files)
  15. mainMenuVisible = false
  16. }">
  17. <PopoverMenuItem>导入 JSON 文件</PopoverMenuItem>
  18. </FileInput> -->
  19. <!-- <FileInput accept=".pptist" @change="files => {
  20. importSpecificFile(files)
  21. mainMenuVisible = false
  22. }">
  23. <PopoverMenuItem>导入 pptist 文件</PopoverMenuItem>
  24. </FileInput> -->
  25. <!-- <PopoverMenuItem @click="setDialogForExport('pptx')">导出文件</PopoverMenuItem> -->
  26. <PopoverMenuItem @click="resetSlides(); mainMenuVisible = false">重置幻灯片</PopoverMenuItem>
  27. <!-- <PopoverMenuItem @click="openMarkupPanel(); mainMenuVisible = false">幻灯片类型标注</PopoverMenuItem> -->
  28. <!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/issues')">意见反馈</PopoverMenuItem> -->
  29. <!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/blob/master/doc/Q&A.md')">常见问题</PopoverMenuItem> -->
  30. <PopoverMenuItem @click="mainMenuVisible = false; hotkeyDrawerVisible = true">快捷操作</PopoverMenuItem>
  31. </template>
  32. <div class="menu-item"><IconHamburgerButton class="icon" /></div>
  33. </Popover>
  34. <div class="title" v-show="false">
  35. <Input
  36. class="title-input"
  37. ref="titleInputRef"
  38. v-model:value="titleValue"
  39. @blur="handleUpdateTitle()"
  40. v-if="editingTitle"
  41. ></Input>
  42. <div
  43. class="title-text"
  44. @click="startEditTitle()"
  45. :title="title"
  46. v-else
  47. >{{ title }}</div>
  48. </div>
  49. </div>
  50. <div class="right">
  51. <div class="group-menu-item">
  52. <div class="menu-item" v-tooltip="'幻灯片放映(F5)'" @click="enterScreening()">
  53. <IconPpt class="icon" />
  54. </div>
  55. <Popover trigger="click" center>
  56. <template #content>
  57. <PopoverMenuItem @click="enterScreeningFromStart()">从头开始</PopoverMenuItem>
  58. <PopoverMenuItem @click="enterScreening()">从当前页开始</PopoverMenuItem>
  59. </template>
  60. <div class="arrow-btn"><IconDown class="arrow" /></div>
  61. </Popover>
  62. </div>
  63. <!-- <div class="menu-item" v-tooltip="'学生视图'" @click="enterStudentView()">
  64. <IconUser class="icon" />
  65. </div> -->
  66. <!-- <div class="menu-item" v-tooltip="'AI生成PPT'" @click="openAIPPTDialog(); mainMenuVisible = false">
  67. <span class="text ai">AI</span>
  68. </div> -->
  69. <div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')">
  70. <IconDownload class="icon" />
  71. </div>
  72. <!-- <a class="github-link" v-tooltip="'Copyright © 2020-PRESENT pipipi-pikachu'" href="https://github.com/pipipi-pikachu/PPTist" target="_blank">
  73. <div class="menu-item"><IconGithub class="icon" /></div>
  74. </a> -->
  75. </div>
  76. <Drawer
  77. :width="320"
  78. v-model:visible="hotkeyDrawerVisible"
  79. placement="right"
  80. >
  81. <HotkeyDoc />
  82. <template v-slot:title>快捷操作</template>
  83. </Drawer>
  84. <FullscreenSpin :loading="exporting" tip="正在导入..." />
  85. </div>
  86. </template>
  87. <script lang="ts" setup>
  88. import { nextTick, ref, useTemplateRef, inject } from 'vue'
  89. import { storeToRefs } from 'pinia'
  90. import { useMainStore, useSlidesStore } from '@/store'
  91. import useScreening from '@/hooks/useScreening'
  92. import useImport from '@/hooks/useImport'
  93. import useSlideHandler from '@/hooks/useSlideHandler'
  94. import type { DialogForExportTypes } from '@/types/export'
  95. import HotkeyDoc from './HotkeyDoc.vue'
  96. import FileInput from '@/components/FileInput.vue'
  97. import FullscreenSpin from '@/components/FullscreenSpin.vue'
  98. import Drawer from '@/components/Drawer.vue'
  99. import Input from '@/components/Input.vue'
  100. import Popover from '@/components/Popover.vue'
  101. import PopoverMenuItem from '@/components/PopoverMenuItem.vue'
  102. const mainStore = useMainStore()
  103. const slidesStore = useSlidesStore()
  104. const { title } = storeToRefs(slidesStore)
  105. const { enterScreening, enterScreeningFromStart } = useScreening()
  106. const { importSpecificFile, importPPTXFile, importJSON, exporting } = useImport()
  107. const { resetSlides } = useSlideHandler()
  108. const mainMenuVisible = ref(false)
  109. const hotkeyDrawerVisible = ref(false)
  110. const editingTitle = ref(false)
  111. const titleValue = ref('')
  112. const titleInputRef = useTemplateRef<InstanceType<typeof Input>>('titleInputRef')
  113. const startEditTitle = () => {
  114. titleValue.value = title.value
  115. editingTitle.value = true
  116. nextTick(() => titleInputRef.value?.focus())
  117. }
  118. const handleUpdateTitle = () => {
  119. slidesStore.setTitle(titleValue.value)
  120. editingTitle.value = false
  121. }
  122. const goLink = (url: string) => {
  123. window.open(url)
  124. mainMenuVisible.value = false
  125. }
  126. const setDialogForExport = (type: DialogForExportTypes) => {
  127. mainStore.setDialogForExport(type)
  128. mainMenuVisible.value = false
  129. }
  130. const openMarkupPanel = () => {
  131. mainStore.setMarkupPanelState(true)
  132. }
  133. const openAIPPTDialog = () => {
  134. mainStore.setAIPPTDialogState(true)
  135. }
  136. const enterStudentView = () => {
  137. // 通过路由跳转到学生模式
  138. window.location.href = '/?mode=student'
  139. }
  140. const setTitle = (newTitle: string) => {
  141. titleValue.value = newTitle
  142. slidesStore.setTitle(newTitle)
  143. }
  144. (window as any).setTitle = setTitle
  145. </script>
  146. <style lang="scss" scoped>
  147. .editor-header {
  148. background-color: #fff;
  149. user-select: none;
  150. border-bottom: 1px solid $borderColor;
  151. display: flex;
  152. justify-content: space-between;
  153. padding: 0 5px;
  154. }
  155. .left, .right {
  156. display: flex;
  157. justify-content: center;
  158. align-items: center;
  159. }
  160. .menu-item {
  161. height: 30px;
  162. display: flex;
  163. justify-content: center;
  164. align-items: center;
  165. font-size: 14px;
  166. padding: 0 10px;
  167. border-radius: $borderRadius;
  168. cursor: pointer;
  169. .icon {
  170. font-size: 18px;
  171. color: #666;
  172. }
  173. .text {
  174. width: 18px;
  175. text-align: center;
  176. font-size: 17px;
  177. }
  178. .ai {
  179. background: linear-gradient(270deg, #d897fd, #33bcfc);
  180. background-clip: text;
  181. color: transparent;
  182. font-weight: 700;
  183. }
  184. &:hover {
  185. background-color: #f1f1f1;
  186. }
  187. }
  188. .group-menu-item {
  189. height: 30px;
  190. display: flex;
  191. margin: 0 8px;
  192. padding: 0 2px;
  193. border-radius: $borderRadius;
  194. &:hover {
  195. background-color: #f1f1f1;
  196. }
  197. .menu-item {
  198. padding: 0 3px;
  199. }
  200. .arrow-btn {
  201. display: flex;
  202. justify-content: center;
  203. align-items: center;
  204. cursor: pointer;
  205. }
  206. }
  207. .title {
  208. height: 30px;
  209. margin-left: 2px;
  210. font-size: 13px;
  211. .title-input {
  212. width: 200px;
  213. height: 100%;
  214. padding-left: 0;
  215. padding-right: 0;
  216. ::v-deep(input) {
  217. height: 28px;
  218. line-height: 28px;
  219. }
  220. }
  221. .title-text {
  222. min-width: 20px;
  223. max-width: 400px;
  224. line-height: 30px;
  225. padding: 0 6px;
  226. border-radius: $borderRadius;
  227. cursor: pointer;
  228. @include ellipsis-oneline();
  229. &:hover {
  230. background-color: #f1f1f1;
  231. }
  232. }
  233. }
  234. .github-link {
  235. display: inline-block;
  236. height: 30px;
  237. }
  238. </style>