Explorar el Código

refactor: 重新组织几乎所有代码,暂时删去编辑器、阅读器相关内容,重构出几个通用组件,重构教师、学生主页,其余页面待添加。原项目见commit历史,已备份。

ChenYangfan hace 6 años
padre
commit
9b89e3636d
Se han modificado 75 ficheros con 333 adiciones y 2484 borrados
  1. 30 16
      src/App.vue
  2. 0 16
      src/common/input-common.scss
  3. 0 4
      src/common/readme.md
  4. 0 32
      src/common/slide/editing-item.scss
  5. 0 132
      src/common/slide/item.scss
  6. 0 38
      src/common/slide/page.scss
  7. 0 19
      src/common/slide/slide.scss
  8. 0 34
      src/common/tab-common.scss
  9. 17 2
      src/components/CButton.vue
  10. 43 0
      src/components/CInput.vue
  11. 0 47
      src/components/GlobalForm.vue
  12. 5 17
      src/components/IconButton.vue
  13. 0 58
      src/components/Message.vue
  14. 0 19
      src/components/ReturnIconButton.vue
  15. 1 1
      src/components/SearchBar.vue
  16. 0 61
      src/components/SlideListItem.vue
  17. 0 54
      src/deprecated/DiscussButtonLarge.vue
  18. 0 54
      src/deprecated/DiscussButtonSmall.vue
  19. 0 54
      src/deprecated/MarkerButton.vue
  20. 0 108
      src/deprecated/ReadModeButtonGroup.vue
  21. 0 69
      src/deprecated/ScalePad.vue
  22. 0 2
      src/main.js
  23. 0 313
      src/prototype/Typography.vue
  24. BIN
      src/prototype/分解.png
  25. BIN
      src/prototype/好的分解和坏的分解.png
  26. 32 52
      src/router/index.js
  27. 0 16
      src/store/index.js
  28. 0 151
      src/store/modules/editor.js
  29. 0 16
      src/store/modules/message.js
  30. 0 20
      src/store/modules/reader.js
  31. 0 12
      src/store/types/editor-types.js
  32. 0 1
      src/store/types/message-types.js
  33. 0 1
      src/store/types/reader-types.js
  34. 0 0
      src/style/reset.scss
  35. 0 0
      src/style/theme.scss
  36. 0 26
      src/utils/block-type-regs.js
  37. 0 14
      src/utils/default-markdown-fragment.js
  38. 0 14
      src/utils/default-placeholder.js
  39. 0 25
      src/utils/generate-pages.js
  40. 0 20
      src/utils/highlight.js
  41. 0 22
      src/utils/iconfont-map.js
  42. 0 59
      src/utils/item-common.js
  43. 0 3
      src/utils/state-chinese-map.js
  44. 18 12
      src/views/Home.vue
  45. 0 51
      src/views/editor/EditorView.vue
  46. 0 244
      src/views/editor/components/EditableItem.vue
  47. 0 25
      src/views/editor/components/EditingPage.vue
  48. 0 99
      src/views/editor/components/Editor.vue
  49. 0 15
      src/views/editor/readme.md
  50. 15 13
      src/views/login/Login.vue
  51. 0 27
      src/views/reader/ReaderView.vue
  52. 0 70
      src/views/reader/components/Reader.vue
  53. 0 51
      src/views/reader/components/ReaderItem.vue
  54. 0 25
      src/views/reader/components/ReaderPage.vue
  55. 41 8
      src/views/shared/UserHomeLayout.vue
  56. 4 3
      src/views/student/StudentHome.vue
  57. 0 42
      src/views/student/StudentHomeView.vue
  58. 0 12
      src/views/student/tabs/InfoTab.vue
  59. 0 12
      src/views/student/tabs/PersonalTab.vue
  60. 0 12
      src/views/student/tabs/SearchTab.vue
  61. 4 3
      src/views/student/tabs/StudentCoursesManager.vue
  62. 4 3
      src/views/student/tabs/StudentDiscussion.vue
  63. 4 3
      src/views/student/tabs/StudentExplore.vue
  64. 13 0
      src/views/student/tabs/StudentPersonal.vue
  65. 13 0
      src/views/student/tabs/StudentQuiz.vue
  66. 13 13
      src/views/teacher/TeacherHome.vue
  67. 24 13
      src/views/teacher/tabs/CreateSlide.vue
  68. 0 12
      src/views/teacher/tabs/DiscussionTab.vue
  69. 0 66
      src/views/teacher/tabs/HomeTab.vue
  70. 0 12
      src/views/teacher/tabs/PersonalTab.vue
  71. 0 36
      src/views/teacher/tabs/SearchTab.vue
  72. 13 0
      src/views/teacher/tabs/TeacherCoursesManager.vue
  73. 13 0
      src/views/teacher/tabs/TeacherDiscussion.vue
  74. 13 0
      src/views/teacher/tabs/TeacherExplore.vue
  75. 13 0
      src/views/teacher/tabs/TeacherPersonal.vue

+ 30 - 16
src/App.vue

@@ -1,34 +1,48 @@
 <template>
   <div>
     <router-view/>
-    <global-form></global-form>
   </div>
 </template>
 
 <script>
-import GlobalForm from '@/components/GlobalForm'
 
 export default {
-  components: { GlobalForm }
+  components: {}
 }
 </script>
 
 <style lang="scss">
-@import '~@/common/reset.scss';
+@import "~@/style/reset.scss";
+@font-face {
+  font-family: 'iconfont';  /* project id 1598214 */
+  src: url('//at.alicdn.com/t/font_1598214_aycbq7lv7mk.eot');
+  src: url('//at.alicdn.com/t/font_1598214_aycbq7lv7mk.eot?#iefix') format('embedded-opentype'),
+  url('//at.alicdn.com/t/font_1598214_aycbq7lv7mk.woff2') format('woff2'),
+  url('//at.alicdn.com/t/font_1598214_aycbq7lv7mk.woff') format('woff'),
+  url('//at.alicdn.com/t/font_1598214_aycbq7lv7mk.ttf') format('truetype'),
+  url('//at.alicdn.com/t/font_1598214_aycbq7lv7mk.svg#iconfont') format('svg');
+}
+
 body {
-  margin: 0;
-  padding: 0;
+  color: #3a4a5a;
 }
-</style>
 
-<style lang="scss">
-@font-face {
-  font-family: 'iconfont';  /* project id 1598214 */
-  src: url('//at.alicdn.com/t/font_1598214_furlqpeeigp.eot');
-  src: url('//at.alicdn.com/t/font_1598214_furlqpeeigp.eot?#iefix') format('embedded-opentype'),
-  url('//at.alicdn.com/t/font_1598214_furlqpeeigp.woff2') format('woff2'),
-  url('//at.alicdn.com/t/font_1598214_furlqpeeigp.woff') format('woff'),
-  url('//at.alicdn.com/t/font_1598214_furlqpeeigp.ttf') format('truetype'),
-  url('//at.alicdn.com/t/font_1598214_furlqpeeigp.svg#iconfont') format('svg');
+.title {
+  font-size: 28px;
+  font-weight: 600;
+  margin-bottom: 12px;
+}
+
+.sub-title {
+  font-size: 22px;
+  font-weight: 600;
+  margin-bottom: 16px;
 }
+
+a {
+  outline: none;
+  text-decoration: none;
+  color: black;
+}
+
 </style>

+ 0 - 16
src/common/input-common.scss

@@ -1,16 +0,0 @@
-@import "theme.scss";
-
-select, input {
-  box-sizing: border-box;
-  font-size: 16px;
-  font-weight: bold;
-  color: #3a4a5a;
-  background: $input-background-color;
-  border-radius: 8px;
-  border: none;
-  outline: none;
-  width: 100%;
-  height: 48px;
-  line-height: 40px;
-  padding: 0 0 0 12px;
-}

+ 0 - 4
src/common/readme.md

@@ -1,4 +0,0 @@
-# 关于这些公共样式文件的说明:
-
-### 幻灯片样式
-幻灯片样式由于存在放映模式、编辑模式,各种模式中都存在各级组件,因此将样式按组件粒度进行编写有助于减少最终代码的体积

+ 0 - 32
src/common/slide/editing-item.scss

@@ -1,32 +0,0 @@
-@import "./item.scss";
-
-* [contenteditable] {
-  cursor: text;
-  outline: none;
-
-  //&:hover {
-  //  background: #fafcfe;
-  //}
-  //
-  //&:focus {
-  //  background: #f9fafc;
-  //}
-
-  &::selection {
-    background-color: #ddeeff;
-  }
-
-  &:empty:before {
-    content: attr(placeholder);
-    color: #bbb;
-  }
-}
-
-.slideBreak {
-  color: lightblue;
-
-  &:empty:before {
-    content: attr(placeholder);
-    color: lightblue;
-  }
-}

+ 0 - 132
src/common/slide/item.scss

@@ -1,132 +0,0 @@
-@import './../reset.scss';
-@import './../theme.scss';
-
-h1, h2, h3, h4, h5, h6 {
-  font-weight: 600;
-  padding: 3px 0;
-}
-
-h1 {
-  font-size: 2em;
-}
-
-h2 {
-  font-size: 1.8em;
-}
-
-h3 {
-  font-size: 1.5em;
-}
-
-h4 {
-  font-size: 1.35em;
-}
-
-h5 {
-  font-size: 1.2em;
-}
-
-h6 {
-  font-size: 1.1em;
-}
-
-p,
-div {
-  padding: 2px 0;
-  line-height: 1.8;
-}
-
-li {
-  margin: 0.19em;
-}
-
-ul,
-ol {
-  list-style: none;
-  padding-left: 1em;
-  margin-top: 0.3em;
-  margin-bottom: 0.8em;
-}
-
-ul>li::before {
-  content: '\02022';
-  display: inline-block;
-  color: $theme-blue;
-  width: 1em;
-  margin-left: -1em;
-}
-
-ol {
-  counter-reset: li;
-  margin: 0 0 0 0.5em;
-
-  li {
-    text-align: left;
-  }
-}
-
-ol>li::before {
-  font-family: 'Courier', 'Courier New', monospace !important;
-  overflow: hidden;
-  font-size: 1em;
-  line-height: 0;
-  content: counter(li) '.';
-  color: $theme-blue;
-  display: inline;
-  margin-left: -1.6em;
-  margin-right: 0.4em;
-  text-align: right;
-  counter-increment: li;
-}
-
-blockquote {
-  margin: 1em 0;
-  font-family: Georgia, 'Times New Roman', Times, serif;
-  font-size: 1.5em;
-  position: relative;
-  font-style: italic;
-  width: 100%;
-  font-weight: 600;
-}
-
-a {
-  text-decoration: underline solid transparent;
-  transition: 0.2s all ease-out;
-
-  &:hover {
-    text-decoration: underline solid #ccc;
-  }
-}
-
-li>a,
-p>a {
-  color: #1976d2;
-}
-
-img {
-  max-width: 100%;
-  max-height: 100vh;
-  margin: 0.94em auto 1.88em auto;
-  display: block;
-  box-shadow: 0 0.13em 0.63em 0 #e5e5e5;
-}
-
-.code {
-  font-family: 'Courier New', Courier, monospace;
-  font-weight: bold;
-  text-align: left;
-  word-break: break-all;
-
-  line-height: 2;
-  display: block;
-  padding: 10px 15px;
-  border: gray 1px solid;
-}
-
-code {
-  width: 100%;
-  overflow: auto;
-  padding: 0 0.19em;
-  font-size: 0.8em;
-  font-weight: bold;
-}

+ 0 - 38
src/common/slide/page.scss

@@ -1,38 +0,0 @@
-.page {
-  display: flex;
-  flex-wrap: wrap;
-
-  margin: 0 auto;
-  align-items: center;
-
-  width: 100%;
-  padding: 0 2rem;
-
-  box-sizing: border-box;
-  font-weight: 600;
-
-  &>div {
-    width: 100%;
-    box-sizing: border-box;
-  }
-}
-
-.read-mode {
-  .page {
-    min-height: unset;
-    padding-top: 20px;
-    padding-bottom: 20px;
-    font-size: 1rem;
-  }
-}
-
-.show-mode {
-  .page {
-    min-height: 100vh;
-  }
-
-  code {
-    font-weight: 800;
-    font-size: 1em
-  }
-}

+ 0 - 19
src/common/slide/slide.scss

@@ -1,19 +0,0 @@
-$slide-width: 1100px;
-
-.slide {
-  box-sizing: border-box;
-  text-align: justify;
-  outline: none;
-  color: #1a2a3a;
-  max-width: $slide-width;
-
-  line-height: 1.6;
-
-  font-size: 24px;
-}
-
-@media (min-width: 900px) {
-  .slide {
-    padding: 0 4vw;
-  }
-}

+ 0 - 34
src/common/tab-common.scss

@@ -1,34 +0,0 @@
-@import '~@/common/theme.scss';
-
-.tab-wrapper {
-  max-width: 320px;
-  min-height: 320px;
-  margin: 0 auto;
-  flex: 1;
-
-  color: #3a4a5a;
-}
-
-@media (max-width: 599px) {
-  .tab-wrapper {
-    margin: 0 auto;
-  }
-}
-
-.title {
-  font-size: 28px;
-  font-weight: 600;
-  margin-bottom: 12px;
-}
-
-.sub-title {
-  font-size: 22px;
-  font-weight: 600;
-  margin-bottom: 16px;
-}
-
-a {
-  outline: none;
-  text-decoration: none;
-  color: black;
-}

+ 17 - 2
src/common/button-common.scss → src/components/CButton.vue

@@ -1,4 +1,19 @@
-@import "theme.scss";
+<template>
+  <button class="c-button">
+    <span v-if="icon" class="icon">{{icon}}</span>
+    <slot></slot>
+  </button>
+</template>
+
+<script>
+export default {
+  props: ['icon']
+}
+</script>
+
+<style lang="scss" scoped>
+@import '~@/style/theme.scss';
+
 button {
   border: none;
   outline: none;
@@ -6,7 +21,6 @@ button {
 
   cursor: pointer;
 
-
   -webkit-tap-highlight-color: transparent;
 
   display: block;
@@ -26,3 +40,4 @@ button {
     background: darken($theme-blue, 10);
   }
 }
+</style>

+ 43 - 0
src/components/CInput.vue

@@ -0,0 +1,43 @@
+<template>
+  <div class="c-input">
+    <label>
+      <input
+        type="text"
+        :placeholder="placeholder"
+        :value="value"
+        @input="$emit('input', $event.target.value)"
+      />
+    </label>
+  </div>
+</template>
+
+<script>
+export default {
+  model: {
+    prop: 'value',
+    event: 'input'
+  },
+  props: {
+    value: String,
+    placeholder: String
+  }
+}
+</script>
+<style lang="scss" scoped>
+@import '~@/style/theme.scss';
+input {
+  box-sizing: border-box;
+  font-size: 16px;
+  font-weight: bold;
+  color: #3a4a5a;
+  background: $input-background-color;
+  border-radius: 8px;
+  border: none;
+  outline: none;
+  width: 100%;
+  height: 48px;
+  line-height: 40px;
+  padding: 0 0 0 12px;
+}
+
+</style>

+ 0 - 47
src/components/GlobalForm.vue

@@ -1,47 +0,0 @@
-<template>
-  <div class="global-form-wrapper">
-    <div class="global-form">
-      <slot></slot>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {}
-</script>
-
-<style lang="scss" scoped>
-.global-form-wrapper {
-  display: none;
-  position: fixed;
-  top: 0;
-  bottom: 0;
-  right: 0;
-  left: 0;
-  backdrop-filter: saturate(180%) blur(20px);
-  background: rgba(255, 255, 255, 0.6);
-}
-
-.global-form {
-  position: absolute;
-  top: 0;
-  bottom: 0;
-  right: 0;
-  left: 0;
-  max-width: 600px;
-  max-height: 600px;
-  background: white;
-  margin: auto;
-  padding: 16px;
-  box-sizing: border-box;
-  box-shadow: 0 0 20px -4px rgba(0, 0, 0, .2);
-}
-
-@media (max-width: 600px) {
-  .global-form {
-    margin: auto;
-    max-height: 100vh;
-    overflow: auto;
-  }
-}
-</style>

+ 5 - 17
src/components/IconLink.vue → src/components/IconButton.vue

@@ -1,32 +1,20 @@
 <template>
-  <router-link :to="tab.name" class="icon-link">
     <div
-      class="icon"
-      :class="currentRouteName === tab.name ? 'active' : ''"
+      class="icon-link"
+      :class="active ? 'active' : ''"
     >{{ tab.unicode }}</div>
-  </router-link>
 </template>
 
 <script>
 export default {
-  props: ['tab'],
-  data () {
-    return {
-      currentRouteName: this.$route.name
-    }
-  },
-  watch: {
-    $route () {
-      this.currentRouteName = this.$route.name
-    }
-  }
+  props: ['tab', 'active', 'handleClick']
 }
 </script>
 
 <style lang="scss" scoped>
-@import '~@/common/theme.scss';
+@import '~@/style/theme.scss';
 
-.icon {
+.icon-link {
   user-select: none;
   cursor: pointer;
   text-align: center;

+ 0 - 58
src/components/Message.vue

@@ -1,58 +0,0 @@
-<template>
-  <div class="message-wrapper">
-    <div class="message">
-      <div class="icon warning">{{iconfontMap['error']}}</div>
-      自动保存成功!
-    </div>
-  </div>
-
-</template>
-
-<script>
-import iconfontMap from '@/utils/iconfont-map'
-
-export default {
-  data () {
-    return {
-      iconfontMap: iconfontMap
-    }
-  },
-  computed: {}
-}
-</script>
-
-<style lang="scss" scoped>
-.message-wrapper {
-  display: flex;
-  justify-content: center;
-}
-
-.icon {
-  font-family: 'iconfont', sans-serif;
-  padding-right: 8px;
-  font-size: 1.4em;
-  line-height: 0;
-  user-select: none;
-}
-
-.warning {
-  color: darkorange;
-}
-
-.message {
-  color: #2a3a4a;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  position: fixed;
-  top: 20px;
-  z-index: 2;
-  font-size: 16px;
-  font-weight: bold;
-  margin: 0 auto;
-  padding: 8px 10px;
-  border-radius: 100px;
-  backdrop-filter: saturate(180%) blur(20px);
-  background: rgba(230, 230, 230, 0.6);
-}
-</style>

+ 0 - 19
src/components/ReturnIconButton.vue

@@ -1,19 +0,0 @@
-<template>
-  <button>&#xe714;</button>
-</template>
-
-<script>
-export default {}
-</script>
-
-<style lang="scss" scoped>
-@import "~@/common/button-common.scss";
-button {
-  font-family: 'iconfont', sans-serif;
-  font-size: 34px;
-  user-select: none;
-  width: 50px;
-  height: 50px;
-  border-radius: 100px;
-}
-</style>

+ 1 - 1
src/components/SearchBar.vue

@@ -23,7 +23,7 @@ export default {
 }
 </script>
 <style lang="scss" scoped>
-$input-background-color: #ebf0f5;
+@import '~@/style/theme.scss';
 input {
   box-sizing: border-box;
   font-size: 16px;

+ 0 - 61
src/components/SlideListItem.vue

@@ -1,61 +0,0 @@
-<template>
-  <div class="slide-list-item-container" @click="handleClick">
-    <div class="content">
-      <div class="title">
-        {{ slide.name }}
-        <span class="tag">{{ stateChinese }} </span>
-      </div>
-      <div>修改时间:{{ slide.updateTime }}</div>
-      <div>所属课程:{{ slide.courseName }}</div>
-    </div>
-  </div>
-</template>
-
-<script>
-import stateMap from '@/utils/state-chinese-map'
-
-export default {
-  props: ['slide'],
-  data () {
-    return {
-      stateChinese: stateMap[this.slide.state]
-    }
-  },
-  methods: {
-    handleClick () {
-      this.$router.push('/editor/' + this.slide.id)
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-.slide-list-item-container {
-  margin: 24px 0 0 0;
-  padding-bottom: 8px;
-  cursor: pointer;
-}
-
-.content > div {
-  color: gray;
-  margin: 4px 0;
-  font-size: 14px;
-}
-
-.content .title {
-  font-size: 1.2em;
-  margin: 8px 0;
-
-  color: #555;
-}
-
-.tag {
-  font-size: 12px;
-  display: inline-block;
-  background: #fcecb8;
-  color: #a88100;
-  border-radius: 6px;
-  padding: 2px 8px;
-  transform: translateY(-2px);
-}
-</style>

+ 0 - 54
src/deprecated/DiscussButtonLarge.vue

@@ -1,54 +0,0 @@
-<template>
-  <div>
-    <button @click="handleClick" :class="isActive ? 'active' : ''">&#xe610;</button>
-  </div>
-</template>
-
-<script>
-export default {
-  data () {
-    return {
-      isActive: false
-    }
-  },
-  methods: {
-    handleClick () {
-      this.isActive = !this.isActive
-      this.$emit('discuss-mode-change', this.isActive)
-    }
-  }
-}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/button-common';
-
-button {
-  white-space: nowrap;
-  width: 40px;
-  height: 40px;
-  border-radius: 100px;
-  margin: 0 24px;
-
-  font-family: 'iconfont', sans-serif;
-  font-size: 26px;
-
-  color: #ffffff;
-  // backdrop-filter: saturate(180%) blur(20px);
-  background-color: #aabaca;
-
-  &::after {
-    content: '\e813';
-    position: absolute;
-    z-index: -1;
-    color: #aabaca;
-    transform: translateX(-72px);
-    transition: 0.2s;
-  }
-}
-
-.active {
-  &::after {
-    transform: translateX(16px) rotateY(180deg);
-  }
-}
-</style>

+ 0 - 54
src/deprecated/DiscussButtonSmall.vue

@@ -1,54 +0,0 @@
-<template>
-  <div class="marker-button-wrapper">
-    <button>&#xe610;</button>
-    <div>&#xe7d9;</div>
-  </div>
-</template>
-
-<script>
-export default {}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/button-common';
-
-button {
-  color: #999;
-  font-family: 'iconfont', sans-serif;
-  font-weight: bold;
-  font-size: 24px;
-
-  transform: translateY(1px);
-  transition: 0.1s color;
-
-  &:hover {
-    color: #5a5a5a;
-  }
-}
-
-.marker-button-wrapper {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  box-sizing: border-box;
-
-  border-radius: 100px;
-  backdrop-filter: saturate(180%) blur(20px);
-  background-color: rgba(170, 170, 170, 0.13);
-  transition: 0.2s;
-
-  font-family: 'iconfont', sans-serif;
-  font-size: 16px;
-  font-weight: bold;
-
-  color: #777;
-
-  & > div {
-    margin-right: 12px;
-
-    &:hover {
-      cursor: default;
-      color: #3a3a3a;
-    }
-  }
-}
-</style>

+ 0 - 54
src/deprecated/MarkerButton.vue

@@ -1,54 +0,0 @@
-<template>
-  <div class="marker-button-wrapper">
-    <button>&#xe6d6;</button>
-    <div>标记</div>
-  </div>
-</template>
-
-<script>
-export default {}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/button-common';
-
-button {
-  color: #999;
-  font-family: 'iconfont', sans-serif;
-  font-weight: bold;
-  font-size: 24px;
-
-  transform: translateY(1px);
-  transition: 0.1s color;
-
-  &:hover {
-    color: #5a5a5a;
-  }
-}
-
-.marker-button-wrapper {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  box-sizing: border-box;
-
-  border-radius: 100px;
-  backdrop-filter: saturate(180%) blur(20px);
-  background-color: rgba(170, 170, 170, 0.13);
-  transition: 0.2s;
-
-  font-family: 'iconfont', sans-serif;
-  font-size: 16px;
-  font-weight: bold;
-
-  color: #777;
-
-  & > div {
-    margin-right: 12px;
-
-    &:hover {
-      cursor: default;
-      color: #3a3a3a;
-    }
-  }
-}
-</style>

+ 0 - 108
src/deprecated/ReadModeButtonGroup.vue

@@ -1,108 +0,0 @@
-<template>
-  <div class="button-group-wrapper" :class="mode">
-    <button
-      v-for="button of buttons"
-      :key="button.value"
-      @click="changeMode(button.value)"
-    >{{ button.name }}
-    </button>
-  </div>
-</template>
-
-<script>
-export default {
-  props: ['defaultMode'],
-  data () {
-    return {
-      mode: this.defaultMode,
-      buttons: [
-        {
-          value: 'show',
-          name: '放映'
-        },
-        {
-          value: 'read',
-          name: '阅读'
-        },
-        {
-          value: 'auto',
-          name: '自适应'
-        }
-      ]
-    }
-  },
-  methods: {
-    changeMode (value) {
-      this.mode = value
-      this.$emit('change-mode', this.mode)
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-@import '~@/common/button-common';
-
-button {
-  flex: 1;
-  display: block;
-  width: 100%;
-  padding: 8px 0;
-
-  color: #777;
-  font-weight: bold;
-  font-size: 14px;
-
-  transition: 0.1s color;
-
-  &:hover {
-    color: #3a3a3a;
-  }
-}
-
-.button-group-wrapper {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  box-sizing: border-box;
-
-  width: 300px;
-  border-radius: 100px;
-  backdrop-filter: saturate(180%) blur(20px);
-  background-color: rgba(170, 170, 170, 0.13);
-  transition: 0.2s background-color;
-
-  &:hover {
-    background-color: rgba(170, 170, 170, 0.2);
-  }
-
-  &::before {
-    content: '';
-    // bug fix: safari 宽度计算机制太奇葩,只好写死宽度外加 media
-    width: calc(100px - 3px);
-    z-index: -1;
-    height: calc(100% - 8px);
-    border-radius: 100px;
-    display: inline-block;
-    position: absolute;
-    opacity: 0.8;
-    background: #ffffff;
-    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.13);
-    transition: 0.2s cubic-bezier(0, 0.5, 0.5, 1.25);
-  }
-}
-
-@media (max-width: 300px) {
-  .button-group-wrapper::before {
-    width: calc(33.3333% - 3px);
-  }
-}
-
-.show::before {
-  transform: translateX(-100%);
-}
-
-.auto::before {
-  transform: translateX(100%);
-}
-</style>

+ 0 - 69
src/deprecated/ScalePad.vue

@@ -1,69 +0,0 @@
-<template>
-  <div class="scale-pad-wrapper">
-    <button>&#xe775;</button>
-    <div>
-      <span class="icon">&#xe7f9;</span>
-      <span class="text"> 100%</span>
-    </div>
-    <button>&#xe71d;</button>
-  </div>
-</template>
-
-<script>
-export default {}
-</script>
-
-<style lang="scss" scoped>
-@import '~@/common/button-common';
-
-.scale-pad-wrapper {
-  display: inline-flex;
-  align-items: center;
-  justify-content: space-around;
-  box-sizing: border-box;
-
-  border-radius: 100px;
-  backdrop-filter: saturate(180%) blur(20px);
-  background-color: rgba(170, 170, 170, 0.13);
-  font-family: 'iconfont', sans-serif;
-  font-size: 16px;
-  font-weight: bold;
-
-  transition: 0.1s;
-  color: #777;
-
-  & > div {
-    &:hover {
-      cursor: default;
-      color: #3a3a3a;
-    }
-
-    & > .class {
-      display: inline-block;
-      font-size: 18px;
-      vertical-align: -1px;
-    }
-  }
-}
-
-@media (max-width: 300px) {
-  .scale-pad-wrapper > div > .text {
-    display: none;
-  }
-}
-
-button {
-  color: #999;
-  font-family: 'iconfont', sans-serif;
-  font-weight: bold;
-  font-size: 20px;
-
-  padding: 6px;
-
-  transition: 0.1s color;
-
-  &:hover {
-    color: #5a5a5a;
-  }
-}
-</style>

+ 0 - 2
src/main.js

@@ -3,7 +3,6 @@ import './plugins/axios'
 import App from './App.vue'
 import './registerServiceWorker'
 import router from './router'
-import store from './store'
 import uuid from 'uuid'
 
 Vue.config.productionTip = false
@@ -11,6 +10,5 @@ Vue.prototype.$uuid = uuid
 
 new Vue({
   router,
-  store,
   render: h => h(App)
 }).$mount('#app')

+ 0 - 313
src/prototype/Typography.vue

@@ -1,313 +0,0 @@
-<template>
-  <section :class="showMode ? 'slide show-mode' : 'slide read-mode'">
-    <section class="page">
-      <div>
-        <h1>编程基础 I</h1>
-        <div>刘钦</div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>Outline</h3>
-        <ul>
-          <li>代码是用来读的(实践经验)</li>
-          <li>降低复杂度 — 分解与抽象(方法学)</li>
-          <li>编程=数据结构+算法(理论逻辑)</li>
-          <li>算法建模(理论逻辑)</li>
-          <li>数据建模(理论逻辑)</li>
-          <li>编程范式 — 声明式与命令式(表现形式)</li>
-          <li>编程典型场景 — 数据处理(理论逻辑)</li>
-        </ul>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h2>1. 代码是用来读的!</h2>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>Write once, Read many times</h3>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <blockquote>
-          When you program, you have to think about how someone will read your
-          code, not just how a computer will interpret it.
-        </blockquote>
-        <div>—— Kent Beck</div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>代码可读</h3>
-        <ul>
-          <li>团队的需要</li>
-          <li>维护的需要</li>
-        </ul>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>糟糕的变量名字</h3>
-        <div class="code java">
-          <code>
-            var x = x - xx <br/>
-            var xxx = aretha + SalesTax( aretha )<br/>
-            var x = x + LateFee( x1, x ) + xxx <br/>
-            var x = x + Interest( x1, x )
-          </code>
-        </div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>良好的变量名字</h3>
-        <div class="code java">
-          <code>
-            var balance = balance - lastPayment <br/>
-            var monthlyTotal = NewPurchases + SalesTax( newPurchases ) <br/>
-            var balance = balance + LateFee( customerID, balance ) +
-            monthlyTotal<br/>
-            var balance = balance + Interest( customerID, balance )
-          </code>
-        </div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>代码是用来读的!</h3>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h2>2. 降低复杂度 - 分解与抽象</h2>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h2>2. 降低复杂度 - 分解与抽象</h2>
-        <div>分解与抽象</div>
-        <div>分解与抽象</div>
-        <div>分解与抽象</div>
-        <div>分解与抽象</div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>为什么要降低复杂度</h3>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>世界是复杂的吗</h3>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>程序员是干什么的?</h3>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>将复杂的问题转化为代码</h3>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>有什么降低复杂度的方法?</h3>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>代码是用来读的!</h3>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <div>降低复杂度的方法 -</div>
-        <h2>分解(组合)</h2>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <div>*</div>
-        <div>// 打印一颗 *</div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <div class="code">
-          <code> print("*")</code>
-        </div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <div>#</div>
-        <div>// 打印一颗 #</div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <div class="code">
-          <code>
-            public PageResourceResponse getSlides(@RequestParam(required =
-            false, defaultValue = "") String key, @ApiParam(hidden = true)
-            @PageableDefault(size = Integer.MAX_VALUE, sort = "updateTime",
-            direction = Sort.Direction.DESC) Pageable pageable) {<br/>
-            List&lt;SlideVO&gt; slideVOs = slideService.getSlides(key,
-            pageable);<br/>
-            return PageResourceResponse.of(slideVOs,
-            PageResourceResponse.PageInfo.of(pageable.getPageNumber(),
-            slideVOs.size())); }
-          </code>
-        </div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h3>*#</h3>
-        <br/>
-        <div>// 打印 *#</div>
-        <div>// 打印一颗 * + 打印一颗 #</div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <div class="code">
-          <code>
-            print("*")<br/>
-            print("#")
-          </code>
-        </div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <img src="./分解.png" alt=""/>
-        <div>分解(组合)</div>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <h2>分解(组合)的关键点</h2>
-        <ul>
-          <li>分解之后,每一部分复杂度要变小</li>
-          <li>相互之间关联要小,相对独立</li>
-        </ul>
-      </div>
-    </section>
-    <section class="page">
-      <div>
-        <img src="./好的分解和坏的分解.png" alt=""/>
-        <div>好的分解和坏的分解</div>
-      </div>
-    </section>
-  </section>
-</template>
-
-<script>
-import Hljs from '@/utils/highlight'
-import 'highlight.js/styles/idea.css'
-
-export default {
-  data () {
-    return {
-      showMode: true,
-      milestones: [],
-      animating: false
-    }
-  },
-  mounted () {
-    this.highlightCode()
-    const handleResize = () => {
-      this.showMode = window.innerWidth >= 700
-
-      const pages = document.getElementsByClassName('page')
-      pages.forEach(page => {
-        this.milestones.push(page.offsetTop)
-      })
-    }
-    handleResize()
-    window.addEventListener('resize', handleResize)
-
-    const handleSpace = e => {
-      if (e.key === ' ') {
-        e.preventDefault()
-        this.nextPage()
-      } else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
-        e.preventDefault()
-        this.lastPage()
-      } else if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
-        e.preventDefault()
-        this.nextPage()
-      }
-    }
-    window.addEventListener('keydown', handleSpace)
-  },
-  updated () {
-    this.highlightCode()
-  },
-  methods: {
-    highlightCode () {
-      const preEl = document.querySelectorAll('.code')
-      preEl.forEach(el => {
-        Hljs.highlightBlock(el)
-      })
-    },
-    nextPage () {
-      const top = this.getCurrentScrollTop()
-      this.milestones.some(milestone => {
-        if (top < milestone - 10) {
-          this.scrollToY(top, milestone, 3, position =>
-            window.scrollTo(0, position)
-          )
-          return true
-        }
-      })
-    },
-    lastPage () {
-      const currentPosition = this.getCurrentScrollTop()
-      let target = currentPosition
-      this.milestones.some(milestone => {
-        if (currentPosition > milestone + 10) {
-          target = milestone
-        } else {
-          return true
-        }
-      })
-      this.scrollToY(currentPosition, target, 3, position =>
-        window.scrollTo(0, position)
-      )
-    },
-    getCurrentScrollTop () {
-      return document.documentElement.scrollTop || document.body.scrollTop
-    },
-    scrollToY (start, end, rate, callback) {
-      this.animating = true
-
-      let step = () => {
-        if (Math.abs(end - start) < 0.5) {
-          callback(end)
-          this.animating = false
-          return
-        }
-        start = start + (end - start) / rate
-        callback(start)
-        requestAnimationFrame(step)
-      }
-
-      step()
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-@import '~@/common/reset.scss';
-@import '~@/common/slide/slide.scss';
-@import '~@/common/slide/page.scss';
-@import '~@/common/slide/item.scss';
-</style>

BIN
src/prototype/分解.png


BIN
src/prototype/好的分解和坏的分解.png


+ 32 - 52
src/router/index.js

@@ -8,81 +8,61 @@ const routes = [{
   path: '/',
   name: 'home',
   component: Home
-}, {
-  path: '/editor/:id',
-  name: 'editor',
-  component: () => import('../views/editor/EditorView.vue')
 }, {
   path: '/login',
   name: 'login',
   component: () => import('../views/login/Login.vue')
-}, {
-  path: '/reader/:id',
-  name: 'reader',
-  component: () => import('../views/reader/ReaderView.vue')
-}, {
-  path: '/typography',
-  name: 'typography',
-  component: () => import('../prototype/Typography.vue')
 }, {
   path: '/teacher',
   name: 'teacher',
-  redirect: '/teacher/teacher-home',
-  component: () => import('../views/teacher/TeacherHomeView.vue'),
+  redirect: '/teacher/explore',
+  component: () => import('../views/teacher/TeacherHome.vue'),
   children: [{
-    path: 'teacher-home',
-    name: 'teacher-home',
-    component: () => import('../views/teacher/tabs/HomeTab.vue')
+    path: 'explore',
+    name: 'teacher-explore',
+    component: () => import('../views/teacher/tabs/TeacherExplore.vue')
   }, {
-    path: 'teacher-search',
-    name: 'teacher-search',
-    component: () => import('../views/teacher/tabs/SearchTab.vue')
+    path: 'create-slide',
+    name: 'create-slide',
+    component: () => import('../views/teacher/tabs/CreateSlide.vue')
   }, {
-    path: 'teacher-add',
-    name: 'teacher-add',
-    component: () => import('../views/teacher/tabs/AddTab.vue')
-  }, {
-    path: 'teacher-personal',
-    name: 'teacher-personal',
-    component: () => import('../views/teacher/tabs/PersonalTab.vue')
+    path: 'courses',
+    name: 'teacher-courses',
+    component: () => import('../views/teacher/tabs/TeacherCoursesManager.vue')
   }, {
-    path: 'teacher-discussion',
+    path: 'discussion',
     name: 'teacher-discussion',
-    component: () => import('../views/teacher/tabs/DiscussionTab.vue')
+    component: () => import('../views/teacher/tabs/TeacherDiscussion.vue')
   }, {
-    path: 'teacher-info',
-    name: 'teacher-info',
-    component: () => import('../views/teacher/tabs/InfoTab.vue')
+    path: 'personal',
+    name: 'teacher-personal',
+    component: () => import('../views/teacher/tabs/TeacherPersonal.vue')
   }]
 }, {
   path: '/student',
   name: 'student',
-  redirect: '/student/student-home',
-  component: () => import('../views/student/StudentHomeView.vue'),
+  redirect: '/student/explore',
+  component: () => import('../views/student/StudentHome.vue'),
   children: [{
-    path: 'student-home',
-    name: 'student-home',
-    component: () => import('../views/student/tabs/HomeTab.vue')
-  }, {
-    path: 'student-search',
-    name: 'student-search',
-    component: () => import('../views/student/tabs/SearchTab.vue')
+    path: 'explore',
+    name: 'student-explore',
+    component: () => import('../views/student/tabs/StudentExplore.vue')
   }, {
-    path: 'student-history',
-    name: 'student-history',
-    component: () => import('../views/student/tabs/HistoryTab.vue')
+    path: 'quiz',
+    name: 'student-quiz',
+    component: () => import('../views/student/tabs/StudentQuiz.vue')
   }, {
-    path: 'student-personal',
-    name: 'student-personal',
-    component: () => import('../views/student/tabs/PersonalTab.vue')
+    path: 'courses',
+    name: 'student-courses',
+    component: () => import('../views/student/tabs/StudentCoursesManager.vue')
   }, {
-    path: 'student-discussion',
+    path: 'discussion',
     name: 'student-discussion',
-    component: () => import('../views/student/tabs/DiscussionTab.vue')
+    component: () => import('../views/student/tabs/StudentDiscussion.vue')
   }, {
-    path: 'student-info',
-    name: 'student-info',
-    component: () => import('../views/student/tabs/InfoTab.vue')
+    path: 'personal',
+    name: 'student-personal',
+    component: () => import('../views/student/tabs/StudentPersonal.vue')
   }]
 }]
 

+ 0 - 16
src/store/index.js

@@ -1,16 +0,0 @@
-import Vue from 'vue'
-import Vuex from 'vuex'
-import editor from './modules/editor'
-import reader from './modules/reader'
-
-Vue.use(Vuex)
-
-export default new Vuex.Store({
-  state: {},
-  mutations: {},
-  actions: {},
-  modules: {
-    editor: editor,
-    reader: reader
-  }
-})

+ 0 - 151
src/store/modules/editor.js

@@ -1,151 +0,0 @@
-import {
-  INIT_BY_STATE,
-  INIT_BY_DEFAULT,
-  ADD_ITEM_AFTER,
-  MODIFY_ITEM,
-  DELETE_ITEM,
-  SET_FOCUS,
-  SET_FOCUS_BEFORE,
-  SET_FOCUS_AFTER,
-  USE_FOCUS,
-  SET_HOVER,
-  DIFF
-} from '../types/editor-types'
-
-import uuid from 'uuid'
-
-export default {
-  state: {
-    slideInfo: null,
-    items: [],
-    focus: null,
-    hover: null
-  },
-
-  mutations: {
-    [INIT_BY_STATE] (state, payload) {
-      console.log(payload)
-      state.items = [...payload.items]
-      state.slideInfo = {
-        ...payload
-      }
-    },
-    [INIT_BY_DEFAULT] (state, payload) {
-      const newItemId = uuid.v4()
-      state.items = [{
-        frontendId: newItemId,
-        placeholder: '标题',
-        content: '# '
-      }]
-      state.focus = {
-        focusId: newItemId,
-        offset: 0
-      }
-      state.slideInfo = {
-        ...payload
-      }
-    },
-    [ADD_ITEM_AFTER] (state, payload) {
-      const index = state.items.indexOf(state.items.filter(item => item.frontendId === payload.frontendId)[0])
-
-      state.items = [
-        ...state.items.slice(0, index + 1), {
-          frontendId: uuid.v4(),
-          content: '',
-          placeholder: '正文'
-        },
-        ...state.items.slice(index + 1)
-      ]
-    },
-    [MODIFY_ITEM] (state, payload) {
-      state.items = [...state.items.map(item => {
-        if (item.frontendId === payload.frontendId) {
-          return {
-            ...item,
-            ...payload.diff
-          }
-        } else {
-          return item
-        }
-      })]
-    },
-    [DELETE_ITEM] (state, payload) {
-      state.items = [...state.items.filter((item, index) => index === 0 || item.frontendId !== payload.frontendId)]
-    },
-    [SET_FOCUS] (state, payload) {
-      state.focus = {
-        ...state.focus,
-        ...payload.diff
-      }
-    },
-    [SET_FOCUS_AFTER] (state, payload) {
-      let focusId = ''
-      let tagetedFlag = false
-      const isHit = state.items.some(item => {
-        if (tagetedFlag) {
-          focusId = item.frontendId
-          return true
-        }
-        if (item.frontendId === payload.focusId) {
-          tagetedFlag = true
-        }
-        return false
-      })
-
-      if (isHit) {
-        state.focus = {
-          focusId: focusId,
-          offset: payload.offset ? payload.offset : 0
-        }
-      } else {
-        // todo warn
-      }
-    },
-    [SET_FOCUS_BEFORE] (state, payload) {
-      let focusId = ''
-      let tagetedFlag = false
-      const isHit = [...state.items].reverse().some(item => {
-        if (tagetedFlag) {
-          focusId = item.frontendId
-          return true
-        }
-        if (item.frontendId === payload.focusId) {
-          tagetedFlag = true
-        }
-        return false
-      })
-
-      if (isHit) {
-        state.focus = {
-          focusId: focusId,
-          offset: payload.offset ? payload.offset : 99999
-        }
-      } else {
-        // todo warn
-      }
-    },
-    [USE_FOCUS] (state, payload) {
-      state.focus = null
-    },
-    [SET_HOVER] (state, payload) {
-      state.hover = payload.frontendId
-    },
-    [DIFF] (state, payload) {
-      const getIdByFrontendId = frontendId => {
-        const item = payload.items.filter(item => item.frontendId === frontendId)[0]
-        return item.id
-      }
-      state.items = [...state.items.map(item => {
-        if (!item.id) {
-          return {
-            ...item,
-            id: getIdByFrontendId(item.frontendId)
-          }
-        } else {
-          return item
-        }
-      })]
-      console.log(state.items)
-    }
-  }
-}

+ 0 - 16
src/store/modules/message.js

@@ -1,16 +0,0 @@
-import {
-  SET_MESSAGE
-} from '../types/message-types'
-
-export default {
-  state: {
-    message: null,
-    type: null
-  },
-
-  mutations: {
-    [SET_MESSAGE] (state, payload) {
-      this.message = payload.message
-    }
-  }
-}

+ 0 - 20
src/store/modules/reader.js

@@ -1,20 +0,0 @@
-import {
-  READER_INIT
-} from '../types/reader-types'
-
-export default {
-  state: {
-    slideInfo: null,
-    items: []
-  },
-
-  mutations: {
-    [READER_INIT] (state, payload) {
-      console.log(payload)
-      state.items = [...payload.items]
-      state.slideInfo = {
-        ...payload
-      }
-    }
-  }
-}

+ 0 - 12
src/store/types/editor-types.js

@@ -1,12 +0,0 @@
-export const INIT_BY_STATE = 'initByState'
-export const INIT_BY_DEFAULT = 'initByDefault'
-export const ADD_ITEM_AFTER = 'addItemAfter'
-export const MODIFY_ITEM = 'modifyItem'
-export const DELETE_ITEM = 'deleteItem'
-export const SET_FOCUS = 'setFocus'
-export const SET_FOCUS_BEFORE = 'setFocusBefore'
-export const SET_FOCUS_AFTER = 'setFocusAfter'
-export const USE_FOCUS = 'useFocus'
-export const SET_HOVER = 'setHover'
-
-export const DIFF = 'diff'

+ 0 - 1
src/store/types/message-types.js

@@ -1 +0,0 @@
-export const SET_MESSAGE = 'setMessage'

+ 0 - 1
src/store/types/reader-types.js

@@ -1 +0,0 @@
-export const READER_INIT = 'reader_init'

+ 0 - 0
src/common/reset.scss → src/style/reset.scss


+ 0 - 0
src/common/theme.scss → src/style/theme.scss


+ 0 - 26
src/utils/block-type-regs.js

@@ -1,26 +0,0 @@
-/**
- * todo 正则完全不熟,先放着到时候仍然需要检验写得对不对
- */
-
-const blockTypeRegs = {}
-
-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.blockquote = new RegExp(/^>\s/)
-
-blockTypeRegs.hr = new RegExp(/^---$/)
-
-blockTypeRegs.slideBreak = new RegExp(/^====$/)
-
-blockTypeRegs.pre = new RegExp(/^```([\\s\\S]*?)```$/)
-
-blockTypeRegs.ulli = new RegExp(/^[\\s]-\s/)
-
-blockTypeRegs.olli = new RegExp(/^[\\s][0-9]+\s/)
-
-export default blockTypeRegs

+ 0 - 14
src/utils/default-markdown-fragment.js

@@ -1,14 +0,0 @@
-const defaultMarkdownFragment = {}
-defaultMarkdownFragment.div = ''
-defaultMarkdownFragment.h1 = '# '
-defaultMarkdownFragment.h2 = '## '
-defaultMarkdownFragment.h3 = '### '
-defaultMarkdownFragment.h4 = '#### '
-defaultMarkdownFragment.h5 = '#####'
-defaultMarkdownFragment.h6 = '######'
-defaultMarkdownFragment.blockquote = '> '
-defaultMarkdownFragment.olli = '1. '
-defaultMarkdownFragment.ulli = '- '
-defaultMarkdownFragment.slideBreak = '===='
-
-export default defaultMarkdownFragment

+ 0 - 14
src/utils/default-placeholder.js

@@ -1,14 +0,0 @@
-const defaultPlaceholder = {}
-defaultPlaceholder.div = '正文'
-defaultPlaceholder.h1 = '标题'
-defaultPlaceholder.h2 = '标题'
-defaultPlaceholder.h3 = '小标题'
-defaultPlaceholder.h4 = '小标题'
-defaultPlaceholder.h5 = '小标题'
-defaultPlaceholder.h6 = '小标题'
-defaultPlaceholder.blockquote = '引用'
-defaultPlaceholder.ol = '有序列表项'
-defaultPlaceholder.ul = '无序列表项'
-defaultPlaceholder.slideBreak = '== 分页符 =='
-
-export default defaultPlaceholder

+ 0 - 25
src/utils/generate-pages.js

@@ -1,25 +0,0 @@
-import uuid from 'uuid'
-
-export default function (items) {
-  const pages = []
-  let lastIndex = 0
-  let times = 0
-  items.forEach((item, index) => {
-    if (item.content === '====') {
-      pages.push({
-        items: [...items].slice(lastIndex, index + 1),
-        id: uuid.v4()
-      })
-      lastIndex = index + 1
-    }
-    times = index
-  })
-  if (times !== items.length) {
-    const page = {
-      items: [...items].slice(lastIndex),
-      id: uuid.v4()
-    }
-    pages.push(page)
-  }
-  return pages
-}

+ 0 - 20
src/utils/highlight.js

@@ -1,20 +0,0 @@
-// reference: https://99mycql.github.io/application/%E9%80%9A%E8%BF%87highlight%E5%9C%A8vue%E4%B8%AD%E5%AE%9E%E7%8E%B0%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE.html
-
-import hljs from 'highlight.js/lib/highlight'
-hljs.registerLanguage('bash', require('highlight.js/lib/languages/bash'))
-hljs.registerLanguage('cpp', require('highlight.js/lib/languages/cpp'))
-hljs.registerLanguage('xml', require('highlight.js/lib/languages/xml'))
-hljs.registerLanguage('css', require('highlight.js/lib/languages/css'))
-hljs.registerLanguage('markdown', require('highlight.js/lib/languages/markdown'))
-hljs.registerLanguage('dockerfile', require('highlight.js/lib/languages/dockerfile'))
-hljs.registerLanguage('java', require('highlight.js/lib/languages/java'))
-hljs.registerLanguage('javascript', require('highlight.js/lib/languages/javascript'))
-hljs.registerLanguage('go', require('highlight.js/lib/languages/go'))
-hljs.registerLanguage('python', require('highlight.js/lib/languages/python'))
-hljs.registerLanguage('typescript', require('highlight.js/lib/languages/typescript'))
-
-hljs.configure({
-  useBR: true
-})
-
-export default hljs

+ 0 - 22
src/utils/iconfont-map.js

@@ -1,22 +0,0 @@
-const iconfontMap = {}
-
-// editor
-iconfontMap.div = '\ue737'
-iconfontMap.h1 = '\ue61c'
-iconfontMap.h2 = '\ue61a'
-iconfontMap.h3 = '\ue61d'
-iconfontMap.h4 = '\ue61f'
-iconfontMap.h5 = '\ue61b'
-iconfontMap.h6 = '\ue61e'
-iconfontMap.blockquote = '\ue7f7'
-iconfontMap.olli = '\ue7f8'
-iconfontMap.ulli = '\ue7fa'
-iconfontMap.slideBreak = '\ue673'
-
-// warning success error info
-iconfontMap.success = '\ue67b'
-iconfontMap.warning = '\ue6bc'
-iconfontMap.error = '\ue686'
-iconfontMap.info = '\ue68e'
-
-export default iconfontMap

+ 0 - 59
src/utils/item-common.js

@@ -1,59 +0,0 @@
-import blockTypeRegs from './block-type-regs'
-
-const matchType = (content) => {
-  let res = 'div'
-  Object.entries(blockTypeRegs)
-    .map(array => ({
-      type: array[0],
-      reg: array[1]
-    }))
-    .some(({
-      type,
-      reg
-    }) => {
-      if (reg.test(content)) {
-        res = type
-      }
-    })
-  return res
-}
-
-const generateShowInfo = (type, item) => {
-  if (type === 'slideBreak') {
-    const content = ''
-    return {
-      tagName: 'div',
-      className: {
-        placeholder: true,
-        slideBreak: true
-      },
-      content,
-      placeholder: item.placeholder
-    }
-  } else if (type === 'div') {
-    const content = item.content
-    return {
-      tagName: 'div',
-      className: {
-        placeholder: content === ''
-      },
-      content,
-      placeholder: item.placeholder
-    }
-  } else {
-    const content = item.content.replace(blockTypeRegs[type], '')
-    return {
-      tagName: type,
-      className: {
-        placeholder: content === ''
-      },
-      content,
-      placeholder: item.placeholder
-    }
-  }
-}
-
-export {
-  matchType,
-  generateShowInfo
-}

+ 0 - 3
src/utils/state-chinese-map.js

@@ -1,3 +0,0 @@
-export default {
-  DRAFT: '草稿'
-}

+ 18 - 12
src/views/Home.vue

@@ -1,20 +1,26 @@
 <template>
-  <div>
-    调试链接:<br/>
-    <router-link to="/typography">排版布局原型</router-link>
-    <br/>
-    <router-link to="/teacher">教师主页</router-link>
-    <br/>
-    <router-link to="/student">学生主页</router-link>
-    <br/>
-    <router-link to="/reader/1">阅读器🔗</router-link>
-    <br/>
+  <div class="home-container">
+    <c-button @click.native="handleLoginButtonClick">去登录</c-button>
   </div>
 </template>
 
 <script>
-export default {}
+import CButton from '@/components/CButton'
+export default {
+  components: { CButton },
+  methods: {
+    handleLoginButtonClick () {
+      this.$router.push('/login')
+    }
+  }
+}
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
+div {
+  display: flex;
+  height: 90vh;
+  align-items: center;
+  justify-content: center;
+}
 </style>

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

@@ -1,51 +0,0 @@
-<template>
-  <section>
-    <editor></editor>
-    <button class="save-button" @click="save">save</button>
-  </section>
-</template>
-
-<script>
-import Editor from './components/Editor.vue'
-import { updateSlide, teacherGetSlideById } from '../../server/slide'
-import { DIFF, INIT_BY_STATE } from '../../store/types/editor-types'
-import { mapState } from 'vuex'
-
-export default {
-  components: { Editor },
-  data () {
-    return {
-      slideId: this.$route.params.id
-    }
-  },
-  created () {
-    teacherGetSlideById(this.slideId).then(res => {
-      this.$store.commit(INIT_BY_STATE, res)
-    })
-  },
-  mounted () {},
-  methods: {
-    save () {
-      updateSlide(
-        this.$store.state.editor.slideInfo,
-        this.$store.state.editor.items
-      ).then(res => {
-        this.$store.commit(DIFF, res)
-      })
-    }
-  },
-  computed: {
-    ...mapState({
-      slideInfo: state => state.editor.slideInfo
-    })
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-.save-button {
-  position: fixed;
-  top: 10px;
-  left: 10px;
-}
-</style>

+ 0 - 244
src/views/editor/components/EditableItem.vue

@@ -1,244 +0,0 @@
-<template>
-  <div
-    class="reset-div item-container"
-    @mouseenter="handleMouseEnter"
-    @mouseleave="handleMouseLeave"
-  >
-    <div
-      class="item-menu"
-      :class="className"
-    >{{iconfontMap[type]}}</div>
-    <component
-      ref="element"
-      :is="showInfo.tagName"
-      :id="item.frontendId"
-      @compositionstart.stop="handleCompositionStart"
-      @compositionend.stop="handleCompositionEnd"
-      @input.stop="handleInput"
-      @keydown.stop="handleKeyDown"
-      contenteditable
-      v-html="showInfo.content"
-      :class="showInfo.className"
-      :placeholder="showInfo.placeholder"
-    >
-    </component>
-  </div>
-</template>
-
-<script>
-import {
-  MODIFY_ITEM,
-  ADD_ITEM_AFTER,
-  DELETE_ITEM,
-  SET_FOCUS_AFTER,
-  SET_FOCUS_BEFORE,
-  SET_FOCUS, SET_HOVER
-} from '@/store/types/editor-types'
-import defaultPlaceholder from '@/utils/default-placeholder'
-import defaultMarkdownFragment from '@/utils/default-markdown-fragment'
-import { matchType, generateShowInfo } from '@/utils/item-common'
-import iconfontMap from '@/utils/iconfont-map'
-import { mapState } from 'vuex'
-
-export default {
-  name: 'editable-item',
-  props: ['item'],
-  data: function () {
-    return {
-      isCompositionStarted: false,
-      range: null,
-      type: 'div',
-      iconfontMap: iconfontMap
-    }
-  },
-  mounted () {
-    this.type = this.matchType(this.item.content)
-  },
-  updated () {
-    this.type = this.matchType(this.item.content)
-  },
-  computed: {
-    ...mapState({
-      hover: state => state.editor.hover
-    }),
-    showInfo () {
-      return generateShowInfo(this.type, this.item)
-    },
-    className() {
-      return {
-        'icon-hover': this.hover === this.item.frontendId,
-        'heading': this.type.match(/h[0-6]/)
-      }
-    }
-  },
-  methods: {
-    matchType,
-    handleKeyDown (e) {
-      if (!this.isCompositionStarted) {
-        switch (e.key) {
-          case 'Enter':
-            e.preventDefault()
-            this.$store.commit(ADD_ITEM_AFTER, {
-              frontendId: this.item.frontendId
-            })
-            this.$store.commit(SET_FOCUS_AFTER, {
-              focusId: this.item.frontendId
-            })
-            break
-
-          case 'Backspace':
-            if (!this.item.content) {
-              e.preventDefault()
-              this.$store.commit(SET_FOCUS_BEFORE, {
-                focusId: this.item.frontendId
-              })
-              this.$store.commit(DELETE_ITEM, {
-                frontendId: this.item.frontendId
-              })
-              break
-            }
-
-            this.range = window.getSelection().getRangeAt(0)
-            const offset = this.range.startOffset
-
-            if (offset === 0 && this.range.collapsed && this.type !== 'div') {
-              this.$store.commit(MODIFY_ITEM, {
-                frontendId: this.item.frontendId,
-                diff: {
-                  placeholder: defaultPlaceholder['div'],
-                  content: this.showInfo.content
-                }
-              })
-
-              this.$store.commit(SET_FOCUS, {
-                diff: { focusId: this.item.frontendId, offset }
-              })
-            }
-            break
-
-          case 'ArrowUp':
-            this.handleRangeChanged(() => {
-              this.$store.commit(SET_FOCUS_BEFORE, {
-                focusId: this.item.frontendId
-              })
-            })
-
-            break
-          case 'ArrowDown':
-            this.handleRangeChanged(() => {
-              this.$store.commit(SET_FOCUS_AFTER, {
-                focusId: this.item.frontendId
-              })
-            })
-            break
-
-          default:
-            if (this.type === 'slideBreak') {
-              e.preventDefault()
-            }
-            break
-        }
-      }
-    },
-    handleCompositionStart () {
-      this.isCompositionStarted = true
-    },
-    handleCompositionEnd (e) {
-      this.isCompositionStarted = false
-      this.handleInputValue(e)
-    },
-    handleInput (e) {
-      !this.isCompositionStarted && this.handleInputValue(e)
-    },
-    handleInputValue (e) {
-      const diff = {
-        content: defaultMarkdownFragment[this.type] + e.target.textContent
-      }
-      if (this.type === 'div' && this.matchType(diff.content) !== this.type) {
-        diff.placeholder = defaultPlaceholder[this.matchType(diff.content)]
-      }
-
-      this.$store.commit(MODIFY_ITEM, {
-        frontendId: this.item.frontendId,
-        diff
-      })
-
-      if (this.matchType(diff.content) === 'slideBreak') {
-        this.$store.commit(ADD_ITEM_AFTER, { frontendId: this.item.frontendId })
-
-        this.$store.commit(SET_FOCUS_AFTER, {
-          focusId: this.item.frontendId,
-          offset: 0
-        })
-      } else {
-        this.range = window.getSelection().getRangeAt(0)
-        const offset = this.range.startOffset
-
-        this.$store.commit(SET_FOCUS, {
-          diff: { focusId: this.item.frontendId, offset }
-        })
-      }
-    },
-    handleRangeChanged (cb) {
-      let range = window.getSelection().getRangeAt(0)
-      const firstOffset = range.startOffset
-      console.log(range)
-
-      this.$nextTick(() => {
-        range = window.getSelection().getRangeAt(0)
-        const nextOffset = range.startOffset
-        console.log(range)
-        if (firstOffset === nextOffset) {
-          cb()
-        }
-      })
-    },
-    handleMouseEnter () {
-      this.$store.commit(SET_HOVER, { frontendId: this.item.frontendId })
-    },
-    handleMouseLeave () {
-      this.$store.commit(SET_HOVER, { frontendId: '' })
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-@import '~@/common/slide/editing-item.scss';
-
-.reset-div {
-  padding: unset;
-  line-height: unset;
-}
-
-.item-menu {
-  opacity: 0;
-  cursor: pointer;
-  font-family: 'iconfont', sans-serif;
-  position: absolute;
-  transform: translateX(-32px);
-  color: #9aaaba;
-  font-size: .9em;
-  transition: 0.2s;
-  &:hover {
-    color: #6a7a8a;
-  }
-}
-
-.icon-hover {
-  opacity: 1;
-}
-
-.heading {
-  transform: translateX(-32px) scaleY(.9);
-}
-
-.item-container {
-  display: flex;
-  align-items: center;
-
-  & > * {
-    flex: 1;
-  }
-}
-</style>

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

@@ -1,25 +0,0 @@
-<template>
-  <section class="page">
-    <div>
-      <template v-for="item in page.items">
-        <editable-item :item="item" :key="item.frontendId"/>
-      </template>
-    </div>
-  </section>
-</template>
-
-<script>
-import EditableItem from './EditableItem.vue'
-
-export default {
-  name: 'editing-page',
-  props: ['page'],
-  components: {
-    EditableItem
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-@import '~@/common/slide/page.scss';
-</style>

+ 0 - 99
src/views/editor/components/Editor.vue

@@ -1,99 +0,0 @@
-<template>
-  <div class="slide show-mode">
-    <template v-for="page in pages">
-      <editing-page :key="page.id" :page="page"></editing-page>
-    </template>
-    <div v-show="false">{{ focus }}</div>
-  </div>
-</template>
-
-<script>
-import { mapState } from 'vuex'
-import EditingPage from './EditingPage.vue'
-import { USE_FOCUS, INIT_BY_DEFAULT } from '@/store/types/editor-types'
-import generatePages from '@/utils/generate-pages'
-
-export default {
-  name: 'editor',
-  components: {
-    EditingPage
-  },
-  computed: {
-    ...mapState({
-      items: state => state.editor.items
-    }),
-    pages () {
-      const items = this.$store.state.editor.items
-      if (items.length === 0) {
-        this.$nextTick(() => {
-          this.$store.commit(
-            INIT_BY_DEFAULT,
-            this.$store.state.editor.slideInfo
-          )
-        })
-      }
-
-      return generatePages(items)
-    },
-    focus () {
-      const res = this.$store.state.editor.focus
-      res !== null &&
-      this.$nextTick(() => {
-        this.checkFocus()
-      })
-      return this.$store.state.editor.focus
-    }
-  },
-  methods: {
-    checkFocus () {
-      if (this.focus !== null) {
-        if (this.focus.focusId) {
-          this.$nextTick(() => {
-            const item = document.getElementById(this.focus.focusId)
-            item.focus()
-            const offset = this.focus.offset
-            const computeResult = this.computeContainerAndOffset(item, offset)
-            const range = document.getSelection().getRangeAt(0)
-            range.setStart(computeResult.container, computeResult.offset)
-            range.collapse()
-            this.$store.commit(USE_FOCUS)
-          })
-        }
-      }
-    },
-    computeContainerAndOffset (item, offset) {
-      // todo test
-      if (offset === 0) {
-        return { container: item, offset: 0 }
-      }
-
-      let restOffset = offset
-      if (item.hasChildNodes()) {
-        for (const node of item.childNodes) {
-          if (node.length > restOffset) {
-            return {
-              container: node,
-              offset: restOffset
-            }
-          } else {
-            restOffset -= node.length
-          }
-        }
-      } else {
-        return { container: item, offset: 0 }
-      }
-
-      return {
-        container: item,
-        offset: 1
-      }
-    }
-  }
-}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/slide/slide.scss';
-.slide {
-  margin: 0 auto;
-}
-</style>

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

@@ -1,15 +0,0 @@
-## 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页面下有   

+ 15 - 13
src/views/login/Login.vue

@@ -2,28 +2,30 @@
   <div class="login-view-container">
     <div>
       <h1><span>SEEC</span> 用户登录</h1>
-      <label>
-        <input type="text" placeholder="用户名">
-      </label>
-      <label>
-        <input type="password" placeholder="密码">
-      </label>
+      <c-input placeholder="用户名"></c-input>
+      <c-input placeholder="密码"></c-input>
       <div class="button-area">
-        <button>确认</button>
-        <button class="help-button">帮助</button>
+        <c-button @click.native="handleSubmit">确认</c-button>
+        <c-button class="help-button">帮助</c-button>
       </div>
     </div>
   </div>
 </template>
 
 <script>
-export default {}
+import CInput from '@/components/CInput'
+import CButton from '@/components/CButton'
+export default {
+  components: { CButton, CInput },
+  methods: {
+    handleSubmit () {
+      this.$router.push('/teacher')
+    }
+  }
+}
 </script>
 
 <style lang="scss" scoped>
-@import '~@/common/input-common.scss';
-@import '~@/common/button-common.scss';
-
 h1 {
   color: #3a4a5a;
   font-size: 28px;
@@ -48,7 +50,7 @@ h1 {
   }
 }
 
-input {
+.c-input {
   margin-bottom: 24px;
   background: #f2f6fa;
 }

+ 0 - 27
src/views/reader/ReaderView.vue

@@ -1,27 +0,0 @@
-<template>
-  <reader></reader>
-</template>
-
-<script>
-import Reader from './components/Reader.vue'
-import { teacherGetSlideById } from '@/server/slide'
-import { READER_INIT } from '@/store/types/reader-types'
-
-export default {
-  components: { Reader },
-  data () {
-    return {
-      slideId: this.$route.params.id,
-      slide: null
-    }
-  },
-  created () {
-    teacherGetSlideById(this.slideId).then(res => {
-      this.$store.commit(READER_INIT, res)
-    })
-  }
-}
-</script>
-
-<style>
-</style>

+ 0 - 70
src/views/reader/components/Reader.vue

@@ -1,70 +0,0 @@
-<template>
-  <div class="reader-container">
-    <div class="slide show-mode">
-      <template v-for="page in pages">
-        <reader-page :key="page.id" :page="page"></reader-page>
-      </template>
-    </div>
-
-    <div class="tools-wrapper">
-      <div class="tools">
-        <return-icon-button></return-icon-button>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import { mapState } from 'vuex'
-import generatePages from '@/utils/generate-pages'
-import ReaderPage from './ReaderPage.vue'
-import ReturnIconButton from '@/components/ReturnIconButton'
-
-export default {
-  components: {
-    ReaderPage,
-    ReturnIconButton
-  },
-  computed: {
-    ...mapState({
-      items: state => state.reader.items
-    }),
-    pages () {
-      return generatePages(this.$store.state.reader.items)
-    }
-  },
-  methods: {}
-}
-</script>
-
-<style lang="scss" scoped>
-@import '~@/common/slide/slide.scss';
-
-.tools-wrapper {
-  width: 50px;
-  height: 100vh;
-  position: absolute;
-}
-
-.tools {
-  /*position: fixed;*/
-}
-
-.reader-container {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.slide {
-  position: relative;
-  flex-grow: 1;
-  margin: 0 auto;
-}
-
-.discuss-button-large {
-  /*position: fixed;*/
-  right: 24px;
-  top: calc(50vh - 20px);
-}
-</style>

+ 0 - 51
src/views/reader/components/ReaderItem.vue

@@ -1,51 +0,0 @@
-<template>
-  <component
-    ref="element"
-    :is="showInfo.tagName"
-    :id="item.frontendId"
-    v-html="showInfo.content"
-    :class="showInfo.className"
-  >
-  </component>
-</template>
-
-<script>
-import { matchType, generateShowInfo } from '@/utils/item-common'
-export default {
-  props: ['item'],
-  data: function() {
-    return {
-      type: 'div'
-    }
-  },
-  mounted() {
-    this.type = this.matchType(this.item.content)
-  },
-  updated() {
-    this.type = this.matchType(this.item.content)
-  },
-  computed: {
-    showInfo() {
-      return generateShowInfo(this.type, this.item)
-    }
-  },
-  methods: {
-    matchType
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-@import '~@/common/slide/item.scss';
-* [contenteditable] {
-  outline: none;
-  width: 100%;
-
-  &:hover {
-    background: #fafcfe;
-  }
-}
-.slideBreak {
-  color: lightblue;
-}
-</style>

+ 0 - 25
src/views/reader/components/ReaderPage.vue

@@ -1,25 +0,0 @@
-<template>
-  <section class="page">
-    <div>
-      <template v-for="item in page.items">
-        <reader-item :item="item" :key="item.frontendId"/>
-      </template>
-    </div>
-  </section>
-</template>
-
-<script>
-import ReaderItem from './ReaderItem.vue'
-
-export default {
-  name: 'editing-page',
-  props: ['page'],
-  components: {
-    ReaderItem
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-@import '~@/common/slide/page.scss';
-</style>

+ 41 - 8
src/layouts/RoleHome.vue → src/views/shared/UserHomeLayout.vue

@@ -3,7 +3,13 @@
     <div class="menu-container">
       <div class="tab-container-large">
         <template v-for="tab of tabs">
-          <icon-link :tab="tab" :key="tab.unicode"></icon-link>
+          <icon-button
+            :active="currentRouteName.match(tab.name)"
+            :tab="tab"
+            :key="tab.unicode"
+            @click.native="handleIconButtonClick(tab.name)"
+          >
+          </icon-button>
         </template>
       </div>
       <div class="view-container">
@@ -18,28 +24,44 @@
 
     <div class="tab-container-small">
       <template v-for="tab of tabs">
-        <icon-link :tab="tab" :key="tab.unicode"></icon-link>
+        <icon-button
+          :active="currentRouteName.match(tab.name)"
+          :tab="tab"
+          :key="tab.unicode"
+          @click.native="handleIconButtonClick(tab.name)"
+        >
+        </icon-button>
       </template>
     </div>
   </div>
 </template>
 
 <script>
-import IconLink from '@/components/IconLink'
+import IconButton from '@/components/IconButton'
 
 export default {
-  components: { IconLink },
+  components: { IconButton },
   props: ['tabs'],
   data () {
     return {
-      viewWidth: window.innerWidth
+      currentRouteName: this.$route.name
+    }
+  },
+  watch: {
+    $route () {
+      this.currentRouteName = this.$route.name
+    }
+  },
+  methods: {
+    handleIconButtonClick (name) {
+      this.$router.push(name)
     }
   }
 }
 </script>
 
 <style lang="scss" scoped>
-@import '~@/common/theme.scss';
+@import '~@/style/theme.scss';
 
 .main-container {
   height: 100vh;
@@ -74,7 +96,7 @@ export default {
     display: flex;
     align-items: center;
     padding: 24px 18px;
-    width: 460px;
+    max-width: 460px;
   }
 
   .tab-container-small {
@@ -82,6 +104,13 @@ export default {
   }
 }
 
+.tab {
+  min-height: 320px;
+  padding: 20px;
+  width: 320px;
+  flex: 1;
+}
+
 // 小屏幕情况
 @media (max-width: 599px) {
   .tab-container-large {
@@ -103,7 +132,7 @@ export default {
   .tab-container-small {
     z-index: 2;
     backdrop-filter: saturate(180%) blur(20px);
-    background: rgba(255,255,255,0.8);
+    background: rgba(255, 255, 255, 0.8);
     width: 100%;
     position: fixed;
     padding: 12px 0;
@@ -114,5 +143,9 @@ export default {
     align-items: center;
     justify-content: space-around;
   }
+  .tab {
+    width: unset;
+  }
 }
+
 </style>

+ 4 - 3
src/views/teacher/tabs/InfoTab.vue → src/views/student/StudentHome.vue

@@ -1,12 +1,13 @@
 <template>
-  <div class="tab-wrapper">
-    Info
+  <div>
+
   </div>
 </template>
 
 <script>
 export default {}
 </script>
+
 <style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
+
 </style>

+ 0 - 42
src/views/student/StudentHomeView.vue

@@ -1,42 +0,0 @@
-<template>
-  <role-home :tabs="tabs">
-    <template v-slot:preview></template>
-  </role-home>
-</template>
-
-<script>
-import RoleHome from '@/layouts/RoleHome.vue'
-
-export default {
-  components: { RoleHome },
-  data () {
-    return {
-      tabs: [
-        {
-          name: 'student-home',
-          unicode: '\ue68b'
-        },
-        {
-          name: 'student-search',
-          unicode: '\ue6a2'
-        },
-        {
-          name: 'student-history',
-          unicode: '\ue68c'
-        },
-        {
-          name: 'student-personal',
-          unicode: '\ue790'
-        },
-        {
-          name: 'student-discussion',
-          unicode: '\ue610'
-        }
-      ]
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-</style>

+ 0 - 12
src/views/student/tabs/InfoTab.vue

@@ -1,12 +0,0 @@
-<template>
-  <div class="tab-wrapper">
-    feedback
-  </div>
-</template>
-
-<script>
-export default {}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
-</style>

+ 0 - 12
src/views/student/tabs/PersonalTab.vue

@@ -1,12 +0,0 @@
-<template>
-  <div class="tab-wrapper">
-    feedback
-  </div>
-</template>
-
-<script>
-export default {}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
-</style>

+ 0 - 12
src/views/student/tabs/SearchTab.vue

@@ -1,12 +0,0 @@
-<template>
-  <div class="tab-wrapper">
-    feedback
-  </div>
-</template>
-
-<script>
-export default {}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
-</style>

+ 4 - 3
src/views/student/tabs/HistoryTab.vue → src/views/student/tabs/StudentCoursesManager.vue

@@ -1,12 +1,13 @@
 <template>
-  <div class="tab-wrapper">
-    feedback
+  <div>
+    teacher course manager
   </div>
 </template>
 
 <script>
 export default {}
 </script>
+
 <style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
+
 </style>

+ 4 - 3
src/views/student/tabs/HomeTab.vue → src/views/student/tabs/StudentDiscussion.vue

@@ -1,12 +1,13 @@
 <template>
-  <div class="tab-wrapper">
-    feedback
+  <div>
+    explore
   </div>
 </template>
 
 <script>
 export default {}
 </script>
+
 <style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
+
 </style>

+ 4 - 3
src/views/student/tabs/DiscussionTab.vue → src/views/student/tabs/StudentExplore.vue

@@ -1,12 +1,13 @@
 <template>
-  <div class="tab-wrapper">
-    feedback
+  <div>
+    explore
   </div>
 </template>
 
 <script>
 export default {}
 </script>
+
 <style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
+
 </style>

+ 13 - 0
src/views/student/tabs/StudentPersonal.vue

@@ -0,0 +1,13 @@
+<template>
+  <div>
+    explore
+  </div>
+</template>
+
+<script>
+export default {}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 13 - 0
src/views/student/tabs/StudentQuiz.vue

@@ -0,0 +1,13 @@
+<template>
+  <div>
+    create slide
+  </div>
+</template>
+
+<script>
+export default {}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 13 - 13
src/views/teacher/TeacherHomeView.vue → src/views/teacher/TeacherHome.vue

@@ -1,36 +1,36 @@
 <template>
-  <role-home :tabs="tabs">
+  <user-home-layout :tabs="tabs">
     <template v-slot:preview></template>
-  </role-home>
+  </user-home-layout>
 </template>
 
 <script>
-import RoleHome from '@/layouts/RoleHome.vue'
+import UserHomeLayout from '@/views/shared/UserHomeLayout'
 
 export default {
-  components: { RoleHome },
+  components: { UserHomeLayout },
   data () {
     return {
       tabs: [
         {
-          name: 'teacher-home',
-          unicode: '\ue68b'
+          name: 'explore',
+          unicode: '\ue684'
         },
         {
-          name: 'teacher-search',
-          unicode: '\ue6a2'
+          name: 'courses',
+          unicode: '\ue67d'
         },
         {
-          name: 'teacher-add',
+          name: 'create-slide',
           unicode: '\ue71b'
         },
         {
-          name: 'teacher-personal',
-          unicode: '\ue790'
+          name: 'discussion',
+          unicode: '\ue610'
         },
         {
-          name: 'teacher-discussion',
-          unicode: '\ue610'
+          name: 'personal',
+          unicode: '\ue790'
         }
       ]
     }

+ 24 - 13
src/views/teacher/tabs/AddTab.vue → src/views/teacher/tabs/CreateSlide.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="tab-wrapper">
+  <div class="tab">
     <div class="title">新建幻灯片</div>
     <div class="sub-title">所属课程:</div>
     <div class="select-wrapper">
@@ -13,24 +13,25 @@
       </label>
     </div>
     <div class="sub-title">幻灯片名称:</div>
-    <label>
-      <input
-        type="text"
-        v-model="userInput"
-        placeholder="请勿与同课程幻灯片重名"
-      />
-    </label>
-    <button class="my-button" @click.prevent="handleSubmit">
+    <c-input
+      type="text"
+      v-model="userInput"
+      placeholder="请勿与同课程幻灯片重名"
+    />
+    <c-button class="my-button" @click.native.prevent="handleSubmit">
       添加
-    </button>
+    </c-button>
   </div>
 </template>
 
 <script>
 import { getTeacherCourse } from '@/server/course'
 import { createSlide } from '@/server/slide'
+import CInput from '@/components/CInput'
+import CButton from '@/components/CButton'
 
 export default {
+  components: { CButton, CInput },
   data () {
     return {
       courses: [],
@@ -53,9 +54,7 @@ export default {
 }
 </script>
 <style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
-@import '~@/common/input-common.scss';
-@import '~@/common/button-common.scss';
+@import '~@/style/theme.scss';
 
 .title {
   margin-bottom: 32px;
@@ -68,6 +67,18 @@ export default {
 }
 
 select {
+  box-sizing: border-box;
+  font-size: 16px;
+  font-weight: bold;
+  color: #3a4a5a;
+  background: $input-background-color;
+  border-radius: 8px;
+  border: none;
+  outline: none;
+  width: 100%;
+  height: 48px;
+  line-height: 40px;
+  padding: 0 0 0 12px;
   //隐藏select的下拉图标
   appearance: none;
   -webkit-appearance: none;

+ 0 - 12
src/views/teacher/tabs/DiscussionTab.vue

@@ -1,12 +0,0 @@
-<template>
-  <div class="tab-wrapper">
-    feedback
-  </div>
-</template>
-
-<script>
-export default {}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
-</style>

+ 0 - 66
src/views/teacher/tabs/HomeTab.vue

@@ -1,66 +0,0 @@
-<template>
-  <div class="tab-wrapper">
-    <section class="slide-section">
-      <div class="title">最近修改</div>
-      <section class="recent-slides-section">
-        <div v-if="recentSlides && recentSlides.length === 0">
-          <div class="no-slide-warning">
-            您还没有创建过幻灯片 (´・ω・`)
-          </div>
-          <router-link to="/teacher/teacher-add">
-            <button>新建幻灯片</button>
-          </router-link>
-        </div>
-        <div v-else>
-          <slide-list-item
-            v-for="slide of recentSlides"
-            :key="slide.id"
-            :slide="slide"
-          ></slide-list-item>
-        </div>
-      </section>
-    </section>
-  </div>
-</template>
-
-<script>
-import SlideListItem from '@/components/SlideListItem.vue'
-import { teacherGetRecentSlides } from '@/server/slide'
-
-export default {
-  data () {
-    return {
-      recentSlides: []
-    }
-  },
-  mounted () {
-    teacherGetRecentSlides().then(res => {
-      this.recentSlides = res
-    })
-  },
-  components: { SlideListItem }
-}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
-@import '~@/common/button-common.scss';
-
-.tab-wrapper {
-  max-height: 80vh;
-  overflow: auto;
-}
-
-.recent-slides-section {
-  max-height: 50vh;
-  overflow: auto;
-
-  .no-slide-warning {
-    color: #aabaca;
-    font-size: 20px;
-    font-weight: bold;
-    display: flex;
-    align-items: center;
-    padding: 50px 0;
-  }
-}
-</style>

+ 0 - 12
src/views/teacher/tabs/PersonalTab.vue

@@ -1,12 +0,0 @@
-<template>
-  <div class="tab-wrapper">
-    personal
-  </div>
-</template>
-
-<script>
-export default {}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
-</style>

+ 0 - 36
src/views/teacher/tabs/SearchTab.vue

@@ -1,36 +0,0 @@
-<template>
-  <div class="tab-wrapper">
-    <div class="title">搜索</div>
-    <search-bar v-model="searchKey"></search-bar>
-    <div v-if="!searchKey" class="tip">
-      请输入关键字查询
-    </div>
-    <template v-else></template>
-  </div>
-</template>
-
-<script>
-import SearchBar from '@/components/SearchBar'
-
-export default {
-  data () {
-    return {
-      searchKey: ''
-    }
-  },
-  components: { SearchBar }
-}
-</script>
-<style lang="scss" scoped>
-@import '~@/common/tab-common.scss';
-
-.tip {
-  color: #aabaca;
-  font-size: 20px;
-  font-weight: bold;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  height: 200px;
-}
-</style>

+ 13 - 0
src/views/teacher/tabs/TeacherCoursesManager.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="tab">
+    teacher course manager
+  </div>
+</template>
+
+<script>
+export default {}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 13 - 0
src/views/teacher/tabs/TeacherDiscussion.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="tab">
+    explore
+  </div>
+</template>
+
+<script>
+export default {}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 13 - 0
src/views/teacher/tabs/TeacherExplore.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="tab">
+    explore
+  </div>
+</template>
+
+<script>
+export default {}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 13 - 0
src/views/teacher/tabs/TeacherPersonal.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="tab">
+    explore
+  </div>
+</template>
+
+<script>
+export default {}
+</script>
+
+<style lang="scss" scoped>
+
+</style>