pipipi-pikachu 5 år sedan
förälder
incheckning
5b333c4dab

+ 16 - 0
src/App.vue

@@ -2,6 +2,22 @@
   <router-view/>
 </template>
 
+<script>
+import { defineComponent, onMounted } from 'vue'
+import { useStore } from 'vuex'
+import { MutationTypes } from '@/store/constants'
+
+export default defineComponent({
+  name: 'app',
+  setup() {
+    const store = useStore()
+    onMounted(() => {
+      store.commit(MutationTypes.SET_AVAILABLE_FONTS)
+    })
+  },
+})
+</script>
+
 <style lang="scss">
 #app {
   height: 100%;

+ 18 - 18
src/configs/element.ts

@@ -19,24 +19,24 @@ export const ELEMENT_TYPE_TABS = {
   table: { key: 'element-table', label: '表格属性' },
 }
 
-export const ELEMENTS = {
-  text: '文本',
-  image: '图片',
-  shape: '形状',
-  icon: '图标',
-  line: '线条',
-  chart: '图表',
-  iframe: 'Iframe',
-  table: '表格',
+export enum ELEMENTS {
+  text = '文本',
+  image = '图片',
+  shape = '形状',
+  icon = '图标',
+  line = '线条',
+  chart = '图表',
+  iframe = 'Iframe',
+  table = '表格',
 }
 
-export const OPERATE_KEYS = {
-  LEFT_TOP: 1,
-  TOP: 2,
-  RIGHT_TOP: 3,
-  LEFT: 4,
-  RIGHT: 5,
-  LEFT_BOTTOM: 6,
-  BOTTOM: 7,
-  RIGHT_BOTTOM: 8,
+export enum OPERATE_KEYS {
+  LEFT_TOP = 1,
+  TOP = 2,
+  RIGHT_TOP = 3,
+  LEFT = 4,
+  RIGHT = 5,
+  LEFT_BOTTOM = 6,
+  BOTTOM = 7,
+  RIGHT_BOTTOM = 8,
 }

+ 7 - 1
src/configs/fontName.ts

@@ -1,4 +1,10 @@
-export const FONT_FAMILYS = [
+export interface FontName {
+  source: string;
+  zh: string;
+  en: string;
+}
+
+export const FONT_NAMES = [
   { source: 'windows', zh: '微软雅黑', en: 'Microsoft Yahei' },
   { source: 'windows', zh: '宋体', en: 'SimSun' },
   { source: 'windows', zh: '黑体', en: 'SimHei' },

+ 17 - 17
src/configs/keyCode.ts

@@ -1,18 +1,18 @@
-export const KEYCODE = {
-  S: 83,
-  C: 67,
-  X: 88,
-  Z: 90,
-  Y: 89,
-  A: 65,
-  G: 71,
-  L: 76,
-  DELETE: 46,
-  UP: 38,
-  DOWN: 40,
-  LEFT: 37,
-  RIGHT: 39,
-  ENTER: 13,
-  SPACE: 32,
-  TAB: 9,
+export enum KEYCODE {
+  S = 83,
+  C = 67,
+  X = 88,
+  Z = 90,
+  Y = 89,
+  A = 65,
+  G = 71,
+  L = 76,
+  DELETE = 46,
+  UP = 38,
+  DOWN = 40,
+  LEFT = 37,
+  RIGHT = 39,
+  ENTER = 13,
+  SPACE = 32,
+  TAB = 9,
 }

+ 27 - 3
src/store/mutations.ts

@@ -1,6 +1,8 @@
 import { MutationTypes } from './constants'
 import { State, SaveState } from './state'
 import { Slide, PPTElement } from '@/types/slides'
+import { FONT_NAMES } from '@/configs/fontName'
+import { isSupportFontFamily } from '@/utils/index'
 
 interface AddSlidesData {
   index?: number;
@@ -33,7 +35,7 @@ export type Mutations = {
   [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_AVAILABLE_FONTS](state: State, fonts: string[]): 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;
   [MutationTypes.ADD_SLIDES](state: State, data: AddSlidesData): void;
@@ -44,6 +46,10 @@ export type Mutations = {
   [MutationTypes.UPDATE_SLIDE_INDEX](state: State, index: number): void;
   [MutationTypes.ADD_ELEMENTS](state: State, elements: PPTElement[]): void;
   [MutationTypes.UPDATE_ELEMENT](state: State, data: UpdateElementData): void;
+  [MutationTypes.SET_CURSOR](state: State, cursor: number): void;
+  [MutationTypes.UNDO](state: State): void;
+  [MutationTypes.REDO](state: State): void;
+  [MutationTypes.SET_HISTORY_RECORD_LENGTH](state: State, length: number): void;
 }
 
 export const mutations: Mutations = {
@@ -81,8 +87,8 @@ export const mutations: Mutations = {
     state.editorAreaFocus = isFocus
   },
 
-  [MutationTypes.SET_AVAILABLE_FONTS](state, fonts) {
-    state.availableFonts = fonts
+  [MutationTypes.SET_AVAILABLE_FONTS](state) {
+    state.availableFonts = FONT_NAMES.filter(font => isSupportFontFamily(font.en))
   },
 
   [MutationTypes.SET_SAVE_STATE](state, saveState) {
@@ -146,4 +152,22 @@ export const mutations: Mutations = {
     })
     state.slides[slideIndex].elements = (elements as PPTElement[])
   },
+
+  // history
+
+  [MutationTypes.SET_CURSOR](state, cursor) {
+    state.cursor = cursor
+  },
+
+  [MutationTypes.UNDO](state) {
+    state.cursor -= 1
+  },
+  
+  [MutationTypes.REDO](state) {
+    state.cursor += 1
+  },
+
+  [MutationTypes.SET_HISTORY_RECORD_LENGTH](state, length) {
+    state.historyRecordLength = length
+  },
 }

+ 2 - 1
src/store/state.ts

@@ -1,5 +1,6 @@
 import { Slide } from '@/types/slides'
 import { slides } from '@/mocks/index'
+import { FontName } from '@/configs/fontName'
 
 export type SaveState = 'complete' | 'pending'
 
@@ -11,7 +12,7 @@ export type State = {
   canvasScale: number;
   thumbnailsFocus: boolean;
   editorAreaFocus: boolean;
-  availableFonts: string[];
+  availableFonts: FontName[];
   saveState: SaveState;
   slides: Slide[];
   slideIndex: number;

+ 3 - 3
src/utils/index.ts

@@ -10,7 +10,7 @@ export const createRandomNumber = (min: number, max: number) => {
 }
 
 // 生成随机码
-export const createRandomCode = (len: number = 6) => {
+export const createRandomCode = (len = 6) => {
   const charset = `_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`
   const maxLen = charset.length
   let ret = ''
@@ -30,7 +30,7 @@ export const createUUID = () => {
 }
 
 // 获取当前日期字符串
-export const getDateTime = (format: string = 'yyyy-MM-dd hh:mm:ss') => {
+export const getDateTime = (format = 'yyyy-MM-dd hh:mm:ss') => {
   const date = new Date()
 
   const formatMap = {
@@ -176,7 +176,7 @@ export const encrypt = (msg: string) => {
 
 // 解密函数
 export const decrypt = (ciphertext: string) => {
-  const bytes  = CryptoJS.AES.decrypt(ciphertext, CRYPTO_KEY)
+  const bytes = CryptoJS.AES.decrypt(ciphertext, CRYPTO_KEY)
   return bytes.toString(CryptoJS.enc.Utf8)
 }
 

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

@@ -4,6 +4,7 @@
     ref="canvasRef"
     @mousedown="$event => handleClickBlankArea($event)"
     v-contextmenu="contextmenus"
+    v-click-outside="removeEditorAreaFocus"
   >
     <div 
       class="viewport" 
@@ -32,6 +33,7 @@
 import { computed, defineComponent, onMounted, onUnmounted, reactive, ref } from 'vue'
 import { useStore } from 'vuex'
 import { State } from '@/store/state'
+import { MutationTypes } from '@/store/constants'
 import { ContextmenuItem } from '@/components/Contextmenu/types'
 import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
 
@@ -149,8 +151,16 @@ export default defineComponent({
         mouseSelectionState.isShow = false
       }
     }
+
+    const editorAreaFocus = computed(() => store.state.editorAreaFocus)
+
     const handleClickBlankArea = (e: MouseEvent) => {
       updateMouseSelection(e)
+      if(!editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true)
+    }
+
+    const removeEditorAreaFocus = () => {
+      if(editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, false)
     }
 
     const contextmenus = (): ContextmenuItem[] => {
@@ -191,6 +201,7 @@ export default defineComponent({
       viewportStyles,
       mouseSelectionState,
       handleClickBlankArea,
+      removeEditorAreaFocus,
       contextmenus,
     }
   },

+ 94 - 1
src/views/Editor/index.vue

@@ -13,7 +13,12 @@
 </template>
 
 <script lang="ts">
-import { defineComponent } from 'vue'
+import { computed, defineComponent, onMounted, onUnmounted, ref } from 'vue'
+import { useStore } from 'vuex'
+import { State } from '@/store/state'
+import { KEYCODE } from '@/configs/keyCode'
+
+import { message } from 'ant-design-vue'
 
 import EditorHeader from './EditorHeader/index.vue'
 import Canvas from './Canvas/index.vue'
@@ -30,6 +35,94 @@ export default defineComponent({
     Thumbnails,
     Toolbar,
   },
+  setup() {
+    const ctrlKeyDown = ref(false)
+    const shiftKeyDown = ref(false)
+
+    const store = useStore<State>()
+    const editorAreaFocus = computed(() => store.state.editorAreaFocus)
+    const thumbnailsFocus = computed(() => store.state.thumbnailsFocus)
+
+    const save = () => {
+      message.success('save')
+    }
+    const copy = () => {
+      message.success('copy')
+    }
+    const cut = () => {
+      message.success('cut')
+    }
+    const undo = () => {
+      message.success('undo')
+    }
+    const redo = () => {
+      message.success('redo')
+    }
+    const selectAll = () => {
+      message.success('selectAll')
+    }
+    const lock = () => {
+      message.success('lock')
+    }
+    const combine = () => {
+      message.success('combine')
+    }
+    const uncombine = () => {
+      message.success('uncombine')
+    }
+    const remove = () => {
+      message.success('remove')
+    }
+    const move = (key: number) => {
+      message.success(`move: ${key}`)
+    }
+    const create = () => {
+      message.success('create')
+    }
+
+    const keydownListener = (e: KeyboardEvent) => {
+      const { keyCode, ctrlKey, shiftKey } = e
+
+      if(ctrlKey && !ctrlKeyDown.value) ctrlKeyDown.value = true
+      if(shiftKey && !shiftKeyDown.value) shiftKeyDown.value = true
+      
+      if(!editorAreaFocus.value && !thumbnailsFocus.value) return
+
+      e.preventDefault()
+      
+      if(ctrlKey && keyCode === KEYCODE.S) save()
+      if(ctrlKey && keyCode === KEYCODE.C) copy()
+      if(ctrlKey && keyCode === KEYCODE.X) cut()
+      if(ctrlKey && keyCode === KEYCODE.Z) undo()
+      if(ctrlKey && keyCode === KEYCODE.Y) redo()
+      if(ctrlKey && keyCode === KEYCODE.A) selectAll()
+      if(ctrlKey && keyCode === KEYCODE.L) lock()
+      if(!shiftKey && ctrlKey && keyCode === KEYCODE.G) combine()
+      if(shiftKey && ctrlKey && keyCode === KEYCODE.G) uncombine()
+      if(keyCode === KEYCODE.DELETE) remove()
+      if(keyCode === KEYCODE.UP) move(KEYCODE.UP)
+      if(keyCode === KEYCODE.DOWN) move(KEYCODE.DOWN)
+      if(keyCode === KEYCODE.LEFT) move(KEYCODE.LEFT)
+      if(keyCode === KEYCODE.RIGHT) move(KEYCODE.RIGHT)
+      if(keyCode === KEYCODE.ENTER) create()
+    }
+    
+    const keyupListener = () => {
+      if(ctrlKeyDown.value) ctrlKeyDown.value = false
+      if(shiftKeyDown.value) shiftKeyDown.value = false
+    }
+
+    onMounted(() => {
+      document.addEventListener('keydown', keydownListener)
+      document.addEventListener('keyup', keyupListener)
+      window.addEventListener('blur', keyupListener)
+    })
+    onUnmounted(() => {
+      document.removeEventListener('keydown', keydownListener)
+      document.removeEventListener('keyup', keyupListener)
+      window.removeEventListener('blur', keyupListener)
+    })
+  },
 })
 </script>