pipipi-pikachu 5 rokov pred
rodič
commit
f135829a99

+ 6 - 0
src/router/index.ts

@@ -19,4 +19,10 @@ const router = createRouter({
   routes,
 })
 
+router.beforeEach((to, from) => {
+  if(to.name === 'Player' && from.name !== 'Editor') {
+    return router.push({ path: '/' })
+  }
+})
+
 export default router

+ 1 - 1
src/store/constants.ts

@@ -5,9 +5,9 @@ export enum MutationTypes {
   SET_HANDLE_ELEMENT_ID = 'setHandleElementId',
   SET_EDITOR_AREA_SHOW_SCALE = 'setEditorAreaShowScale',
   SET_CANVAS_SCALE = 'setCanvasScale',
-  TOGGLE_SHOW_GRID_LINES = 'toggleShowGridLines',
   SET_THUMBNAILS_FOCUS = 'setThumbnailsFocus',
   SET_EDITORAREA_FOCUS = 'setEditorAreaFocus',
+  SET_DISABLE_HOTKEYS_STATE = 'setDisableHotkeysState',
   SET_AVAILABLE_FONTS = 'setAvailableFonts',
   SET_SAVE_STATE = 'setSaveState',
 

+ 18 - 1
src/store/getters.ts

@@ -1,7 +1,9 @@
-import { PPTElement } from '@/types/slides'
+import { PPTElement, Slide, PPTAnimation } from '@/types/slides'
 import { State } from './state'
 
 export type Getters = {
+  currentSlide(state: State): Slide | null;
+  currentSlideAnimations(state: State): PPTAnimation[] | null;
   activeElementList(state: State): PPTElement[];
   handleElement(state: State): PPTElement | null;
   canUndo(state: State): boolean;
@@ -9,6 +11,21 @@ export type Getters = {
 }
 
 export const getters: Getters = {
+  currentSlide(state) {
+    return state.slides[state.slideIndex] || null
+  },
+
+  currentSlideAnimations(state) {
+    const currentSlide = state.slides[state.slideIndex]
+    if(!currentSlide) return null
+    const animations = currentSlide.animations
+    if(!animations) return null
+
+    const els = currentSlide.elements
+    const elIds = els.map(el => el.elId)
+    return animations.filter(animation => elIds.includes(animation.elId))
+  },
+
   activeElementList(state) {
     const currentSlide = state.slides[state.slideIndex]
     if(!currentSlide || !currentSlide.elements) return []

+ 5 - 5
src/store/mutations.ts

@@ -32,9 +32,9 @@ export type Mutations = {
   [MutationTypes.SET_HANDLE_ELEMENT_ID](state: State, handleElementId: string): void;
   [MutationTypes.SET_EDITOR_AREA_SHOW_SCALE](state: State, scale: number): void;
   [MutationTypes.SET_CANVAS_SCALE](state: State, scale: number): void;
-  [MutationTypes.TOGGLE_SHOW_GRID_LINES](state: State): void;
   [MutationTypes.SET_THUMBNAILS_FOCUS](state: State, isFocus: boolean): void;
   [MutationTypes.SET_EDITORAREA_FOCUS](state: State, isFocus: boolean): void;
+  [MutationTypes.SET_DISABLE_HOTKEYS_STATE](state: State, disable: boolean): void;
   [MutationTypes.SET_AVAILABLE_FONTS](state: State): void;
   [MutationTypes.SET_SAVE_STATE](state: State, saveState: SaveState ): void;
   [MutationTypes.SET_SLIDES](state: State, slides: Slide[]): void;
@@ -75,10 +75,6 @@ export const mutations: Mutations = {
     state.canvasScale = scale
   },
 
-  [MutationTypes.TOGGLE_SHOW_GRID_LINES](state) {
-    state.isShowGridLines = !state.isShowGridLines
-  },
-
   [MutationTypes.SET_THUMBNAILS_FOCUS](state, isFocus) {
     state.thumbnailsFocus = isFocus
   },
@@ -87,6 +83,10 @@ export const mutations: Mutations = {
     state.editorAreaFocus = isFocus
   },
 
+  [MutationTypes.SET_DISABLE_HOTKEYS_STATE](state, disable) {
+    state.disableHotkeys = disable
+  },
+
   [MutationTypes.SET_AVAILABLE_FONTS](state) {
     state.availableFonts = FONT_NAMES.filter(font => isSupportFontFamily(font.en))
   },

+ 2 - 2
src/store/state.ts

@@ -7,11 +7,11 @@ export type SaveState = 'complete' | 'pending'
 export type State = {
   activeElementIdList: string[];
   handleElementId: string;
-  isShowGridLines: boolean;
   editorAreaShowScale: number;
   canvasScale: number;
   thumbnailsFocus: boolean;
   editorAreaFocus: boolean;
+  disableHotkeys: boolean;
   availableFonts: FontName[];
   saveState: SaveState;
   slides: Slide[];
@@ -23,11 +23,11 @@ export type State = {
 export const state: State = {
   activeElementIdList: [],
   handleElementId: '',
-  isShowGridLines: false,
   editorAreaShowScale: 85,
   canvasScale: 1,
   thumbnailsFocus: false,
   editorAreaFocus: false,
+  disableHotkeys: false,
   availableFonts: [],
   saveState: 'complete',
   slides: slides,

+ 6 - 0
src/views/Editor/Canvas/AlignmentLine.vue

@@ -23,6 +23,12 @@ interface Axis {
   y: number;
 }
 
+export interface AlignmentLineProps {
+  type: AlignmentLineType;
+  axis: Axis;
+  length: number;
+}
+
 export default {
   name: 'alignment-line',
   props: {

+ 30 - 0
src/views/Editor/Canvas/index.vue

@@ -25,6 +25,16 @@
         :height="mouseSelectionState.height" 
         :quadrant="mouseSelectionState.quadrant"
       />
+
+      <SlideBackground 
+        :background="currentSlide?.background"
+        :isShowGridLines="isShowGridLines"
+      />
+
+      <AlignmentLine 
+        v-for="(line, index) in alignmentLines" :key="index" 
+        :type="line.type" :axis="line.axis" :length="line.length"
+      />
     </div>
   </div>
 </template>
@@ -38,13 +48,20 @@ import { ContextmenuItem } from '@/components/Contextmenu/types'
 import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
 
 import MouseSelection from './MouseSelection.vue'
+import SlideBackground from './SlideBackground.vue'
+import AlignmentLine, { AlignmentLineProps } from './AlignmentLine.vue'
 
 export default defineComponent({
   name: 'v-canvas',
   components: {
     MouseSelection,
+    SlideBackground,
+    AlignmentLine,
   },
   setup() {
+    const isShowGridLines = ref(false)
+    const alignmentLines = ref<AlignmentLineProps[]>([])
+
     const viewportStyles = reactive({
       width: VIEWPORT_SIZE,
       height: VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO,
@@ -57,6 +74,8 @@ export default defineComponent({
     const canvasScale = ref(1)
 
     const store = useStore<State>()
+    const currentSlide = computed(() => store.getters.currentSlide)
+
     const editorAreaShowScale = computed(() => store.state.editorAreaShowScale)
     const setViewportSize = () => {
       if(!canvasRef.value) return
@@ -179,9 +198,17 @@ export default defineComponent({
           children: [
             {
               text: '打开',
+              disable: isShowGridLines.value,
+              icon: isShowGridLines.value ? 'icon-check' : '',
+              iconPlacehoder: true,
+              action: () => isShowGridLines.value = true,
             },
             {
               text: '关闭',
+              disable: !isShowGridLines.value,
+              icon: !isShowGridLines.value ? 'icon-check' : '',
+              iconPlacehoder: true,
+              action: () => isShowGridLines.value = false,
             },
           ],
         },
@@ -202,6 +229,9 @@ export default defineComponent({
       mouseSelectionState,
       handleClickBlankArea,
       removeEditorAreaFocus,
+      currentSlide,
+      isShowGridLines,
+      alignmentLines,
       contextmenus,
     }
   },

+ 35 - 0
src/views/Editor/index.vue

@@ -42,6 +42,7 @@ export default defineComponent({
     const store = useStore<State>()
     const editorAreaFocus = computed(() => store.state.editorAreaFocus)
     const thumbnailsFocus = computed(() => store.state.thumbnailsFocus)
+    const disableHotkeys = computed(() => store.state.disableHotkeys)
 
     const save = () => {
       message.success('save')
@@ -112,15 +113,49 @@ export default defineComponent({
       if(shiftKeyDown.value) shiftKeyDown.value = false
     }
 
+    const pasteImageFile = (imageFile: File) => {
+      console.log(imageFile)
+    }
+
+    const pasteText = (text: string) => {
+      console.log(text)
+    }
+
+    const pasteListener = (e: ClipboardEvent) => {
+      if(!editorAreaFocus.value && !thumbnailsFocus.value) return
+      if(disableHotkeys.value) return
+
+      if(!e.clipboardData) return
+
+      const clipboardDataItems = e.clipboardData.items
+      const clipboardDataFirstItem = clipboardDataItems[0]
+
+      if(!clipboardDataFirstItem) return
+
+      for(const item of clipboardDataItems) {
+        if(item.kind === 'file' && item.type.indexOf('image') !== -1) {
+          const imageFile = item.getAsFile()
+          if(imageFile) pasteImageFile(imageFile)
+          return
+        }
+      }
+
+      if( clipboardDataFirstItem.kind === 'string' && clipboardDataFirstItem.type === 'text/plain' ) {
+        clipboardDataFirstItem.getAsString(text => pasteText(text))
+      }
+    }
+
     onMounted(() => {
       document.addEventListener('keydown', keydownListener)
       document.addEventListener('keyup', keyupListener)
       window.addEventListener('blur', keyupListener)
+      document.addEventListener('paste', pasteListener)
     })
     onUnmounted(() => {
       document.removeEventListener('keydown', keydownListener)
       document.removeEventListener('keyup', keyupListener)
       window.removeEventListener('blur', keyupListener)
+      document.removeEventListener('paste', pasteListener)
     })
   },
 })