pipipi-pikachu 5 rokov pred
rodič
commit
a1fba0dbea

+ 8 - 3
package-lock.json

@@ -5986,6 +5986,11 @@
       "integrity": "sha1-AU7o+PZpxcWAI9pkuBecCDooxGw=",
       "dev": true
     },
+    "dexie": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npm.taobao.org/dexie/download/dexie-3.0.3.tgz",
+      "integrity": "sha1-7eY4Sd/l8H4T6Zu3KgQOisHSnas="
+    },
     "diff": {
       "version": "4.0.2",
       "resolved": "https://registry.npm.taobao.org/diff/download/diff-4.0.2.tgz?cache=0&sync_timestamp=1604803633979&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdiff%2Fdownload%2Fdiff-4.0.2.tgz",
@@ -7319,7 +7324,7 @@
     },
     "fork-ts-checker-webpack-plugin-v5": {
       "version": "npm:fork-ts-checker-webpack-plugin@5.2.1",
-      "resolved": "https://registry.npm.taobao.org/fork-ts-checker-webpack-plugin/download/fork-ts-checker-webpack-plugin-5.2.1.tgz?cache=0&sync_timestamp=1608028579163&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffork-ts-checker-webpack-plugin%2Fdownload%2Ffork-ts-checker-webpack-plugin-5.2.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/fork-ts-checker-webpack-plugin/download/fork-ts-checker-webpack-plugin-5.2.1.tgz?cache=0&sync_timestamp=1607084938170&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffork-ts-checker-webpack-plugin%2Fdownload%2Ffork-ts-checker-webpack-plugin-5.2.1.tgz",
       "integrity": "sha1-eTJthpeXkG+osk4qvPlCH8gFRQ0=",
       "dev": true,
       "optional": true,
@@ -7416,7 +7421,7 @@
         },
         "supports-color": {
           "version": "7.2.0",
-          "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1606205010380&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz",
           "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
           "dev": true,
           "optional": true,
@@ -15944,7 +15949,7 @@
         },
         "supports-color": {
           "version": "7.2.0",
-          "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1606205010380&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz",
           "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
           "dev": true,
           "optional": true,

+ 1 - 0
package.json

@@ -13,6 +13,7 @@
     "clipboard": "^2.0.6",
     "core-js": "^3.6.5",
     "crypto-js": "^4.0.0",
+    "dexie": "^3.0.3",
     "lodash": "^4.17.20",
     "store2": "^2.12.0",
     "vue": "^3.0.0",

+ 5 - 3
src/App.vue

@@ -2,17 +2,19 @@
   <router-view/>
 </template>
 
-<script>
+<script lang="ts">
 import { defineComponent, onMounted } from 'vue'
 import { useStore } from 'vuex'
-import { MutationTypes } from '@/store'
+import { MutationTypes, ActionTypes, State } from '@/store'
 
 export default defineComponent({
   name: 'app',
   setup() {
-    const store = useStore()
+    const store = useStore<State>()
+
     onMounted(() => {
       store.commit(MutationTypes.SET_AVAILABLE_FONTS)
+      store.dispatch(ActionTypes.INIT_SNAPSHOT_DATABASE)
     })
   },
 })

+ 15 - 0
src/hooks/useAddHistorySnapshot.ts

@@ -0,0 +1,15 @@
+import { useStore } from 'vuex'
+import debounce from 'lodash/debounce'
+import { State, ActionTypes } from '@/store'
+
+export default () => {
+  const store = useStore<State>()
+
+  const addHistorySnapshot = debounce(function() {
+    store.dispatch(ActionTypes.ADD_SNAPSHOT)
+  }, 300, { trailing: true })
+
+  return {
+    addHistorySnapshot,
+  }
+}

+ 20 - 0
src/hooks/useRedoAndUndo.ts

@@ -0,0 +1,20 @@
+import { useStore } from 'vuex'
+import throttle from 'lodash/throttle'
+import { State, ActionTypes } from '@/store'
+
+export default () => {
+  const store = useStore<State>()
+
+  const redo = throttle(function() {
+    store.dispatch(ActionTypes.RE_DO)
+  }, 100, { leading: true, trailing: false })
+
+  const undo = throttle(function() {
+    store.dispatch(ActionTypes.UN_DO)
+  }, 100, { leading: true, trailing: false })
+
+  return {
+    redo,
+    undo,
+  }
+}

+ 73 - 0
src/store/actions.ts

@@ -0,0 +1,73 @@
+import { ActionTree } from 'vuex'
+import { IndexableTypeArray } from 'dexie'
+import { State } from './index'
+import { ActionTypes, MutationTypes } from './constants'
+import db, { Snapshot } from '@/utils/database'
+
+export const actions: ActionTree<State, State> = {
+  async [ActionTypes.INIT_SNAPSHOT_DATABASE]({ commit }) {
+    const snapshots: Snapshot[] = await db.snapshots.orderBy('id').toArray()
+    const snapshot = snapshots.slice(-1)[0]
+
+    if(snapshot) {
+      db.snapshots.clear()
+      commit(MutationTypes.SET_SLIDES, snapshot.slides)
+    }
+  },
+
+  async [ActionTypes.ADD_SNAPSHOT]({ state, commit }) {
+    const allKeys = await db.snapshots.orderBy('id').keys()
+
+    let needDeleteKeys: IndexableTypeArray = []
+
+    if(state.snapshotCursor >= 0 && state.snapshotCursor < allKeys.length - 1) {
+      needDeleteKeys = allKeys.slice(state.snapshotCursor + 1)
+    }
+
+    const snapshot = {
+      index: state.slideIndex,
+      slides: state.slides,
+    }
+    await db.snapshots.add(snapshot)
+
+    let snapshotLength = allKeys.length - needDeleteKeys.length + 1
+
+    if(snapshotLength > 20) {
+      needDeleteKeys.push(allKeys[0])
+      snapshotLength--
+    }
+
+    await db.snapshots.bulkDelete(needDeleteKeys)
+
+    commit(MutationTypes.SET_SNAPSHOT_CURSOR, snapshotLength - 1)
+    commit(MutationTypes.SET_SNAPSHOT_LENGTH, snapshotLength)
+  },
+
+  async [ActionTypes.UN_DO]({ state, commit }) {
+    if(state.snapshotCursor > 0) return
+
+    const snapshotCursor = state.snapshotCursor - 1
+    const snapshots: Snapshot[] = await db.snapshots.orderBy('id').toArray()
+    const snapshot = snapshots[snapshotCursor]
+    const { index, slides } = snapshot
+
+    commit(MutationTypes.SET_SLIDES, slides)
+    commit(MutationTypes.UPDATE_SLIDE_INDEX, index)
+    commit(MutationTypes.SET_SNAPSHOT_CURSOR, snapshotCursor)
+    commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
+  },
+
+  async [ActionTypes.RE_DO]({ state, commit }) {
+    if(state.snapshotCursor < state.snapshotLength - 1) return
+
+    const snapshotCursor = state.snapshotCursor + 1
+    const snapshots: Snapshot[] = await db.snapshots.orderBy('id').toArray()
+    const snapshot = snapshots[snapshotCursor]
+    const { index, slides } = snapshot
+
+    commit(MutationTypes.SET_SLIDES, slides)
+    commit(MutationTypes.UPDATE_SLIDE_INDEX, index)
+    commit(MutationTypes.SET_SNAPSHOT_CURSOR, snapshotCursor)
+    commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
+  },
+}

+ 10 - 5
src/store/constants.ts

@@ -18,13 +18,18 @@ export enum MutationTypes {
   ADD_ELEMENT = 'addElement',
   UPDATE_ELEMENT = 'updateElement',
 
-  // history
-  SET_CURSOR = 'setCursor',
-  UNDO = 'undo',
-  REDO = 'redo',
-  SET_HISTORY_RECORD_LENGTH = 'setHistoryRecordLength',
+  // snapshot
+  SET_SNAPSHOT_CURSOR = 'setSnapshotCursor',
+  SET_SNAPSHOT_LENGTH = 'setSnapshotLength',
 
   // keyboard
   SET_CTRL_KEY_STATE = 'setCtrlKeyState',
   SET_SHIFT_KEY_STATE = 'setShiftKeyState',
+}
+
+export enum ActionTypes {
+  INIT_SNAPSHOT_DATABASE = 'initSnapshotDatabase',
+  ADD_SNAPSHOT = 'addSnapshot',
+  UN_DO = 'undo',
+  RE_DO = 'redo',
 }

+ 2 - 2
src/store/getters.ts

@@ -30,11 +30,11 @@ export const getters: GetterTree<State, State> = {
   },
 
   canUndo(state) {
-    return state.cursor > 0
+    return state.snapshotCursor > 0
   },
 
   canRedo(state) {
-    return state.cursor < state.historyRecordLength - 1
+    return state.snapshotCursor < state.snapshotLength - 1
   },
 
   ctrlOrShiftKeyActive(state) {

+ 9 - 7
src/store/index.ts

@@ -1,13 +1,14 @@
 import { createStore } from 'vuex'
-import { mutations } from './mutations'
 import { getters } from './getters'
-import { MutationTypes } from './constants'
+import { actions } from './actions'
+import { mutations } from './mutations'
+import { MutationTypes, ActionTypes } from './constants'
 
 import { Slide } from '@/types/slides'
 import { slides } from '@/mocks/index'
 import { FontName } from '@/configs/fontName'
 
-export { MutationTypes }
+export { MutationTypes, ActionTypes }
 
 export interface State {
   activeElementIdList: string[];
@@ -19,8 +20,8 @@ export interface State {
   availableFonts: FontName[];
   slides: Slide[];
   slideIndex: number;
-  cursor: number;
-  historyRecordLength: number;
+  snapshotCursor: number;
+  snapshotLength: number;
   ctrlKeyState: boolean;
   shiftKeyState: boolean;
 }
@@ -35,8 +36,8 @@ const state: State = {
   availableFonts: [],
   slides: slides,
   slideIndex: 0,
-  cursor: -1,
-  historyRecordLength: 0,
+  snapshotCursor: -1,
+  snapshotLength: 0,
   ctrlKeyState: false,
   shiftKeyState: false,
 }
@@ -45,4 +46,5 @@ export default createStore({
   state,
   getters,
   mutations,
+  actions,
 })

+ 5 - 13
src/store/mutations.ts

@@ -101,22 +101,14 @@ export const mutations: MutationTree<State> = {
     state.slides[slideIndex].elements = (elements as PPTElement[])
   },
 
-  // history
+  // snapshot
 
-  [MutationTypes.SET_CURSOR](state, cursor: number) {
-    state.cursor = cursor
+  [MutationTypes.SET_SNAPSHOT_CURSOR](state, cursor: number) {
+    state.snapshotCursor = cursor
   },
 
-  [MutationTypes.UNDO](state) {
-    state.cursor -= 1
-  },
-  
-  [MutationTypes.REDO](state) {
-    state.cursor += 1
-  },
-
-  [MutationTypes.SET_HISTORY_RECORD_LENGTH](state, length: number) {
-    state.historyRecordLength = length
+  [MutationTypes.SET_SNAPSHOT_LENGTH](state, length: number) {
+    state.snapshotLength = length
   },
 
   // keyBoard

+ 21 - 0
src/utils/database.ts

@@ -0,0 +1,21 @@
+import Dexie from 'dexie'
+import { Slide } from '@/types/slides'
+
+export interface Snapshot {
+  index: number;
+  slides: Slide[];
+}
+
+class SnapshotDatabase extends Dexie {
+  public snapshots: Dexie.Table<Snapshot, number>
+
+  public constructor() {
+    super('SnapshotDatabase')
+    this.version(1).stores({
+      snapshots: '++id'
+    })
+    this.snapshots = this.table('snapshots')
+  }
+}
+
+export default new SnapshotDatabase()