Browse Source

refactor: 配合商量好的数据结构对前端编辑器界面进行一点重构

ChenYangfan 6 years ago
parent
commit
89b86e1d48

+ 0 - 88
src/components/EditableItem.vue

@@ -1,88 +0,0 @@
-<template>
-  <section>
-    <component
-      ref="element"
-      :is="tagName"
-      contenteditable
-      @keydown.stop="handleKeyDown"
-      @input="checkBlockType"
-    >
-    </component>
-    输入的内容为:{{ content }}
-  </section>
-</template>
-
-<script>
-import {
-  CREATE_ITEM_AFTER,
-  SET_TAG_NAME,
-  CHANGE_CONTENT
-} from '../store/types/editor-types.js'
-import blockTypeRegs from '@/utils/block-type-regs.js'
-export default {
-  name: 'editable-item',
-  props: ['id', 'tagName', 'content', 'isFocus'],
-  data: function() {
-    return {}
-  },
-  mounted() {
-    this.$refs.element.innerHTML = this.content
-    this.isFocus ? this.$refs.element.focus() : this.$refs.element.blur()
-  },
-  methods: {
-    handleKeyDown(e) {
-      switch (e.key) {
-        case 'Enter':
-          e.preventDefault()
-          this.$store.commit(CREATE_ITEM_AFTER, { id: this.id })
-          break
-
-        default:
-          requestAnimationFrame(() => {
-            this.$store.commit(CHANGE_CONTENT, {
-              id: this.id,
-              content: e.target.innerText
-            })
-          })
-          break
-      }
-    },
-    checkBlockType(e) {
-      this.tagName === 'div' &&
-        Object.entries(blockTypeRegs)
-          .map(array => ({ tagName: array[0], reg: array[1] }))
-          .some(({ tagName, reg }) => {
-            if (reg.test(this.content)) {
-              this.$store.commit(SET_TAG_NAME, {
-                id: this.id,
-                tagName: tagName
-              })
-              this.$store.commit(CHANGE_CONTENT, {
-                id: this.id,
-                content: this.content.replace(reg, '')
-              })
-            }
-          })
-    },
-    changeTagName(newTagName) {
-      this.tagName !== newTagName &&
-        this.$store.commit(SET_TAG_NAME, { id: this.id, tagName: newTagName })
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-* [contenteditable] {
-  outline: none;
-  padding: 10px;
-
-  &:hover {
-    background: #fafcfe;
-  }
-
-  &:focus {
-    background: #f9fafc;
-  }
-}
-</style>

+ 74 - 0
src/components/editor/EditableItem.vue

@@ -0,0 +1,74 @@
+<template>
+  <component
+    ref="element"
+    :is="item.type"
+    :id="item.id"
+    @input="handleInput"
+    @keydown.stop="handleKeyDown"
+    contenteditable
+    v-html="item.content"
+  >
+  </component>
+</template>
+
+<script>
+import blockTypeRegs from '../../utils/block-type-regs'
+import { MODIFY_ITEM, ADD_ITEM_AFTER } from '../../store/types/editor-types'
+export default {
+  name: 'editable-item',
+  props: ['item', 'pageId'],
+  data: function() {
+    return {}
+  },
+  mounted() {},
+  updated() {},
+  methods: {
+    handleKeyDown(e) {
+      switch (e.key) {
+        case 'Enter':
+          e.preventDefault()
+          this.$store.commit(ADD_ITEM_AFTER, {
+            pageId: this.pageId,
+            itemId: this.item.id
+          })
+          break
+
+        default:
+          break
+      }
+    },
+    handleInput(e) {
+      const diff = { content: e.target.textContent }
+      this.item.type === 'div' &&
+        Object.entries(blockTypeRegs)
+          .map(array => ({ tagName: array[0], reg: array[1] }))
+          .some(({ tagName, reg }) => {
+            if (reg.test(e.target.textContent)) {
+              diff.type = tagName
+            }
+          })
+
+      this.$store.commit(MODIFY_ITEM, {
+        pageId: this.pageId,
+        itemId: this.item.id,
+        diff
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+* [contenteditable] {
+  outline: none;
+  padding: 10px;
+
+  &:hover {
+    background: #fafcfe;
+  }
+
+  &:focus {
+    background: #f9fafc;
+  }
+}
+</style>

+ 25 - 0
src/components/editor/EditingPage.vue

@@ -0,0 +1,25 @@
+<template>
+  <section>
+    <template v-for="item in page.items">
+      <editable-item-vue :key="item.id" :item="item" :pageId="page.id" />
+    </template>
+  </section>
+</template>
+
+<script>
+import EditableItemVue from './EditableItem.vue'
+export default {
+  name: 'editing-slide',
+  props: ['page'],
+  components: {
+    EditableItemVue
+  },
+  data: function() {
+    return {}
+  },
+  updated() {}
+}
+</script>
+
+<style scoped>
+</style>

+ 38 - 0
src/components/editor/Editor.vue

@@ -0,0 +1,38 @@
+<template>
+  <section id="container">
+    <template v-for="page in pages">
+      <editing-page-vue :key="page.id" :page="page"></editing-page-vue>
+    </template>
+  </section>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+import EditingPageVue from './EditingPage.vue'
+export default {
+  name: 'editor',
+  components: {
+    EditingPageVue
+  },
+  created() {
+  },
+  computed: {
+    ...mapState({
+      pages: state => state.editor.pages,
+      selection: state => state.editor.selection
+    })
+  }
+}
+</script>
+
+<style scoped>
+#container {
+  min-height: 100vh;
+  padding: 10px;
+
+  box-sizing: border-box;
+
+  outline: none;
+  border: 40px solid lightblue;
+}
+</style>

+ 9 - 12
src/router/index.js

@@ -4,18 +4,15 @@ import Home from '../views/Home.vue'
 
 
 Vue.use(VueRouter)
 Vue.use(VueRouter)
 
 
-const routes = [
-  {
-    path: '/',
-    name: 'home',
-    component: Home
-  },
-  {
-    path: '/editor',
-    name: 'editor',
-    component: () => import('../views/editor/Editor.vue')
-  }
-]
+const routes = [{
+  path: '/',
+  name: 'home',
+  component: Home
+}, {
+  path: '/editor',
+  name: 'editor',
+  component: () => import('../views/editor/EditorView.vue')
+}]
 
 
 const router = new VueRouter({
 const router = new VueRouter({
   mode: 'history',
   mode: 'history',

+ 106 - 37
src/store/modules/editor.js

@@ -1,53 +1,122 @@
 import {
 import {
-  CREATE_ITEM_AFTER,
-  SET_TAG_NAME,
-  CHANGE_CONTENT
+  INIT_BY_STATE,
+  INIT_BY_DEFAULT,
+  SET_SELECTION,
+  ADD_PAGE_AFTER,
+  DELETE_PAGE,
+  ADD_ITEM_AFTER,
+  MODIFY_ITEM,
+  DELETE_ITEM
 } from '../types/editor-types'
 } from '../types/editor-types'
+
 import uuid from 'uuid'
 import uuid from 'uuid'
 
 
 export default {
 export default {
   state: {
   state: {
-    editableItems: [{
-      id: uuid.v4(),
-      tagName: 'div',
-      content: '2',
-      isFocus: true
-    }]
+    pages: [],
+    selection: {}
   },
   },
+
   mutations: {
   mutations: {
-    [CREATE_ITEM_AFTER](state, payload) {
-      // todo 快要看不懂了...需要改一改
-      const index = state.editableItems.indexOf(state.editableItems.filter(item => item.id === payload.id)[0])
-      state.editableItems = [...state.editableItems.slice(0, index + 1).map(item => ({
-        ...item,
-        isFocus: false
-      })), {
+    [INIT_BY_STATE](state, payload) {},
+    [INIT_BY_DEFAULT](state, payload) {
+      state.pages = [{
         id: uuid.v4(),
         id: uuid.v4(),
-        tagName: 'div',
-        content: '',
-        isFocus: true
-      }, ...state.editableItems.slice(index + 1).map(item => ({
-        ...item,
-        isFocus: false
-      }))]
+        items: [{
+          id: uuid.v4(),
+          type: 'h1',
+          placeholder: '未命名文档标题',
+          content: ''
+        }, {
+          id: uuid.v4(),
+          type: 'div',
+          placeholder: '未命名小标题',
+          content: ''
+        }]
+      }]
+    },
+    [SET_SELECTION](state, payload) {
+      state.selection = {
+        ...payload.selection
+      }
+    },
+    [ADD_PAGE_AFTER](state, payload) {
+      const index = state.pages.indexOf(state.pages.filter(page => page.id === payload.pageId)[0])
+
+      state.pages = [
+        ...state.pages.slice(0, index + 1), {
+          id: uuid.v4(),
+          items: [{
+            id: uuid.v4(),
+            type: 'h3',
+            placeholder: '未命名副标题',
+            content: ''
+          }]
+        }
+      ]
+    },
+    [DELETE_PAGE](state, payload) {
+      state.pages = [...state.pages.filter(page => page.id !== payload.id)]
     },
     },
-    [SET_TAG_NAME](state, payload) {
-      state.editableItems = [...state.editableItems.map(item =>
-        item.id === payload.id ? {
-          ...item,
-          tagName: payload.tagName
-        } : item
-      )]
+    // [SPLITE_PAGE](state, payload) {},
+    // [JOIN_PAGE](state, payload) {},
+    [ADD_ITEM_AFTER](state, payload) {
+      state.pages = [
+        ...state.pages.map(page => {
+          if (page.id === payload.pageId) {
+            const index = page.items.indexOf(page.items.filter(item => item.id === payload.itemId)[0])
+            return {
+              ...page,
+              items: [
+                ...page.items.slice(0, index + 1), {
+                  id: uuid.v4(),
+                  type: 'div',
+                  content: ''
+                },
+                ...page.items.slice(index + 1)
+              ]
+            }
+          }
+          return page
+        })
+      ]
     },
     },
-    [CHANGE_CONTENT](state, payload) {
-      state.editableItems = [...state.editableItems.map(item =>
-        item.id === payload.id ? {
-          ...item,
-          content: payload.content
-        } : item
-      )]
+    [MODIFY_ITEM](state, payload) {
+      state.pages = [
+        ...state.pages.map(page => {
+          if (page.id === payload.pageId) {
+            return {
+              ...page,
+              items: page.items.map(item => {
+                if (item.id === payload.itemId) {
+                  return {
+                    ...item,
+                    ...payload.diff
+                  }
+                }
+                return item
+              })
+            }
+          }
+          return page
+        })
+      ]
+    },
+    [DELETE_ITEM](state, payload) {
+      state.pages = {
+        ...state.pages.map(page => page.id === payload.id && page.items.filter(item => item.id !== payload.id))
+      }
     }
     }
   },
   },
   actions: {},
   actions: {},
   getters: {}
   getters: {}
 }
 }
+
+// [SET_TAG_NAME](state, payload) {
+//   state.editableItems = [...state.editableItems.map(item =>
+//     item.id === payload.id ? {
+//       ...item,
+//       tagName: payload.tagName
+//     } : item
+//   )]
+// },

+ 10 - 5
src/store/types/editor-types.js

@@ -1,5 +1,10 @@
-export const INIT_BY_SPECIFIC_ITEMS = 'initBySpecialItems'
-export const CREATE_ITEM_AFTER = 'createItemAfter'
-export const REMOVE_ITEM_BEFORE = 'removeItemBefore'
-export const SET_TAG_NAME = 'setTagName'
-export const CHANGE_CONTENT = 'changeContent'
+export const INIT_BY_STATE = 'initByState'
+export const INIT_BY_DEFAULT = 'initByDefault'
+export const SET_SELECTION = 'setSelection'
+export const ADD_PAGE_AFTER = 'addPageAfter'
+export const DELETE_PAGE = 'initByState'
+// export const SPLITE_PAGE = 'splitePage'
+// export const JOIN_PAGE = 'joinPage'
+export const ADD_ITEM_AFTER = 'addItemAfter'
+export const MODIFY_ITEM = 'modifyItem'
+export const DELETE_ITEM = 'deleteItem'

+ 11 - 11
src/utils/block-type-regs.js

@@ -5,22 +5,22 @@
 const blockTypeRegs = {}
 const blockTypeRegs = {}
 
 
 blockTypeRegs.h1 = new RegExp(/^#\s/)
 blockTypeRegs.h1 = new RegExp(/^#\s/)
-// blockTypeRegs.h2 = new RegExp(/^##\s/)
-// blockTypeRegs.h3 = new RegExp(/^###\s/)
-// blockTypeRegs.h4 = new RegExp(/^####\s/)
-// blockTypeRegs.h5 = new RegExp(/^#####\s/)
-// blockTypeRegs.h6 = new RegExp(/^######\s/)
+blockTypeRegs.h2 = new RegExp(/^##\s/)
+blockTypeRegs.h3 = new RegExp(/^###\s/)
+blockTypeRegs.h4 = new RegExp(/^####\s/)
+blockTypeRegs.h5 = new RegExp(/^#####\s/)
+blockTypeRegs.h6 = new RegExp(/^######\s/)
 
 
-// blockTypeRegs.blockquote = new RegExp(/^>\s/)
+blockTypeRegs.blockquote = new RegExp(/^>\s/)
 
 
-// blockTypeRegs.hr = new RegExp(/---/)
+blockTypeRegs.hr = new RegExp(/---/)
 
 
-// blockTypeRegs.slideBreakReg = new RegExp(/----/)
+blockTypeRegs.slideBreakReg = new RegExp(/===/)
 
 
-// blockTypeRegs.pre = new RegExp(/^```([\\s\\S]*?)```$/)
+blockTypeRegs.pre = new RegExp(/^```([\\s\\S]*?)```$/)
 
 
-// blockTypeRegs.ulItemReg = new RegExp(/^[\\s]-\s/)
+blockTypeRegs.ulItemReg = new RegExp(/^[\\s]-\s/)
 
 
-// blockTypeRegs.olItemReg = new RegExp(/^[\\s][0-9]+\s/)
+blockTypeRegs.olItemReg = new RegExp(/^[\\s][0-9]+\s/)
 
 
 export default blockTypeRegs
 export default blockTypeRegs

+ 2 - 1
src/views/Home.vue

@@ -1,11 +1,12 @@
 <template>
 <template>
   <div>
   <div>
-    调试链接:<br>
+    调试链接:<br />
     1. <router-link to="/editor">编辑器</router-link>
     1. <router-link to="/editor">编辑器</router-link>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
+export default {}
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>

+ 0 - 45
src/views/editor/Editor.vue

@@ -1,45 +0,0 @@
-<template>
-  <section id="container">
-    <template v-for="item in editableItems">
-      <editable-item-vue
-        :key="item.id"
-        :id="item.id"
-        :tagName="item.tagName"
-        :content="item.content"
-        :isFocus="item.isFocus"
-      />
-    </template>
-  </section>
-</template>
-
-<script>
-import EditableItemVue from '../../components/EditableItem.vue'
-import { mapState } from 'vuex'
-export default {
-  name: 'editor',
-  components: {
-    EditableItemVue
-  },
-  data: function() {
-    return {}
-  },
-  computed: {
-    ...mapState({
-      editableItems: state => state.editor.editableItems
-    })
-  },
-  created() {}
-}
-</script>
-
-<style lang="scss" scoped>
-#container {
-  min-height: 100vh;
-  padding: 10px;
-
-  box-sizing: border-box;
-
-  outline: none;
-  border: 40px solid lightblue;
-}
-</style>

+ 18 - 0
src/views/editor/EditorView.vue

@@ -0,0 +1,18 @@
+<template>
+  <editor-vue></editor-vue>
+</template>
+
+<script>
+import EditorVue from '../../components/editor/Editor.vue'
+import { INIT_BY_DEFAULT } from '../../store/types/editor-types'
+
+export default {
+  components: { EditorVue },
+  created() {
+    this.$store.commit(INIT_BY_DEFAULT)
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 15 - 0
src/views/editor/readme.md

@@ -0,0 +1,15 @@
+## Editor 组件说明
+
+#### 组成部分
+
+1. EditorView.vue (src/views/editor)
+    1. Editor.vue (components/editor/)
+        1. EditingPage.vue (src/components/editor)
+        2. EditableItem.vue (src/components/editor)
+    2. TextEditor.vue (src/view/editor)
+
+从上到下为父子关系
+
+#### 编辑过程基本逻辑
+
+1. Editor页面下有