Przeglądaj źródła

improvement: 教师界面整理

ChenYangfan 6 lat temu
rodzic
commit
231c3a9251

+ 14 - 8
src/animations/ShowTransition.vue

@@ -11,33 +11,39 @@ export default {}
 <style lang="scss" scoped>
 .show-enter-active {
   overflow: hidden;
-  animation: show .5s forwards cubic-bezier(0, 0.5, 0.5, 1);
+  animation: show .6s forwards cubic-bezier(0, 0.5, 0.5, 1);
 }
 
 .show-leave-active {
   overflow: hidden;
-  animation: hide .5s forwards cubic-bezier(0, 0.5, 0.5, 1);
+  animation: hide .4s forwards cubic-bezier(0, 0.5, 0.5, 1);
 }
 
 @keyframes show {
-  from {
+  0% {
     max-height: 0;
-    opacity: 0.3;
+    opacity: 0;
   }
-  to {
+  50% {
+    opacity: 1;
+  }
+  100% {
     opacity: 1;
     max-height: 200px;
   }
 }
 
 @keyframes hide {
-  from {
+  0% {
     opacity: 1;
     max-height: 200px;
   }
-  to {
+  50% {
+    opacity: 0;
+  }
+  100% {
     max-height: 0;
-    opacity: 0.3;
+    opacity: 0;
   }
 }
 </style>

+ 10 - 4
src/animations/ShowTransitionLarge.vue

@@ -20,22 +20,28 @@ export default {}
 }
 
 @keyframes show-large {
-  from {
+  0% {
     max-height: 0;
     opacity: 0;
   }
-  to {
+  50% {
+    opacity: 0;
+  }
+  100% {
     opacity: 1;
     max-height: 1200px;
   }
 }
 
 @keyframes hide-large {
-  from {
+  0% {
     opacity: 1;
     max-height: 1200px;
   }
-  to {
+  50% {
+    opacity: 0;
+  }
+  100% {
     max-height: 0;
     opacity: 0;
   }

+ 13 - 7
src/animations/ShowTransitionSmall.vue

@@ -11,33 +11,39 @@ export default {}
 <style lang="scss" scoped>
 .show-small-enter-active {
   overflow: hidden;
-  animation: show-small 1s forwards;
+  animation: show-small .4s forwards;
 }
 
 .show-small-leave-active {
   overflow: hidden;
-  animation: hide-small .5s forwards cubic-bezier(0, .5, .5, 1);
+  animation: hide-small .25s forwards cubic-bezier(0, .5, .5, 1);
 }
 
 @keyframes show-small {
-  from {
+  0% {
     max-height: 0;
     opacity: 0;
   }
-  to {
+  50% {
+    opacity: 0;
+  }
+  100% {
     opacity: 1;
     max-height: 50px;
   }
 }
 
 @keyframes hide-small {
-  from {
+  0% {
     opacity: 1;
     max-height: 50px;
   }
-  to {
-    max-height: 0;
+  50% {
     opacity: 0;
   }
+  100% {
+    opacity: 0;
+    max-height: 0;
+  }
 }
 </style>

+ 18 - 2
src/common-style.scss

@@ -65,9 +65,25 @@
 }
 
 .mt-24 {
-  margin-top: 16px;
+  margin-top: 24px;
 }
 
 .mt-32 {
-  margin-top: 16px;
+  margin-top: 32px;
+}
+
+.mr-2 {
+  margin-right: 2px;
+}
+
+.mr-4 {
+  margin-right: 4px;
+}
+
+.mr-8 {
+  margin-right: 8px;
+}
+
+.mr-16 {
+  margin-right: 16px;
 }

+ 5 - 3
src/components/Basic/CButton.vue

@@ -1,6 +1,6 @@
 <template>
   <button class="c-button" :style="style" :class="classList" @click="$emit('click')">
-    <c-icon bold :right="6" :left="-4" v-if="icon" class="icon" :size="22">{{icon}}</c-icon>
+    <c-icon :bold="!noBold" :right="6" :left="-4" v-if="icon" class="icon" :size="20">{{icon}}</c-icon>
     <slot></slot>
   </button>
 </template>
@@ -52,6 +52,10 @@ export default {
       type: Boolean,
       default: false
     },
+    noBold: {
+      type: Boolean,
+      default: false
+    },
     left: {
       type: Number,
       default: 0
@@ -98,7 +102,6 @@ button {
   display: flex;
   align-items: center;
   justify-content: center;
-
   -webkit-tap-highlight-color: transparent;
 
   background: $theme-background-grey;
@@ -120,7 +123,6 @@ button {
 }
 
 .c-button--block {
-  display: block;
   width: 100%;
 }
 

+ 7 - 11
src/components/Basic/CInput.vue

@@ -1,11 +1,11 @@
 <template>
   <component
     class="c-input"
-    :is="inputType"
+    :is="textarea ? 'textarea' : 'input'"
     :type="type ? type : 'text'"
     :placeholder="placeholder"
     :value="value"
-    :maxlength="maxlength ? maxlength : ''"
+    :maxlength="maxlength ? maxlength : null"
     @input="$emit('input', $event.target.value)"
   />
 </template>
@@ -19,14 +19,9 @@ export default {
   props: {
     value: String,
     placeholder: String,
-    textarea: String,
+    textarea: Boolean,
     type: String,
-    maxlength: String
-  },
-  data () {
-    return {
-      inputType: this.textarea ? 'textarea' : 'input'
-    }
+    maxlength: Number
   }
 }
 </script>
@@ -43,8 +38,8 @@ input, textarea {
   border: none;
   outline: none;
   width: 100%;
-  height: 48px;
-  line-height: 2;
+  height: 40px;
+  line-height: 1.5;
   padding: 0 12px 0 12px;
   transition: .2s;
 
@@ -60,6 +55,7 @@ input, textarea {
 textarea {
   min-height: 96px;
   height: 100%;
+  padding: 6px 12px;
   resize: none;
 }
 

+ 62 - 0
src/components/Basic/CSelect.vue

@@ -0,0 +1,62 @@
+<template>
+  <component :class="value === '' && 'unfilled'" :is="'select'" :value="value" @change="$emit('input', $event.target.value)">
+    <option disabled :value="''" selected>{{disabledDisplay}}</option>
+    <option v-for="item in list" :value="item.value" :key="item.value">
+      {{ item.display }}
+    </option>
+  </component>
+</template>
+
+<script>
+export default {
+  model: {
+    prop: 'value',
+    event: 'input'
+  },
+  props: {
+    value: {
+      type: String,
+      default: ''
+    },
+    disabledDisplay: {
+      type: String,
+      default: '[ 未填写 ]'
+    },
+    // 形式如: [ { value: xx, display: xx } ]
+    list: Array
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '~@/style/theme.scss';
+
+select {
+  font-weight: bold;
+  box-sizing: border-box;
+  font-size: 16px;
+  color: #3a4a5a;
+  background: $input-background-color;
+  border-radius: 8px;
+  border: none;
+  outline: none;
+  width: 100%;
+  height: 40px;
+  line-height: 40px;
+  padding: 0 0 0 12px;
+  //隐藏select的下拉图标
+  appearance: none;
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  //通过padding-left的值让文字居中
+  transition: .2s;
+
+  &:hover {
+    background: darken($input-background-color, 5);
+  }
+}
+
+.unfilled {
+  color: $theme-orange
+}
+</style>

+ 2 - 2
src/components/Basic/FileUploader.vue

@@ -38,7 +38,7 @@ export default {
   align-items: center;
   overflow: hidden;
   box-sizing: border-box;
-  font-size: 16px;
+  font-size: 14px;
   font-weight: bold;
   color: #7a8a9a;
   background: $input-background-color;
@@ -46,7 +46,7 @@ export default {
   border: none;
   outline: none;
   width: 100%;
-  height: 48px;
+  height: 40px;
   line-height: 40px;
   padding: 0 0 0 12px;
   transition: .2s;

+ 6 - 2
src/components/Basic/IconButton.vue

@@ -38,7 +38,9 @@ export default {
     },
 
     // icon button 大小
-    size: Number
+    size: Number,
+    left: Number,
+    right: Number
   },
   computed: {
     classList () {
@@ -48,7 +50,9 @@ export default {
       return {
         color: this.color.match(/^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/) ? this.color : '',
         width: this.size ? this.size + 'px' : null,
-        height: this.size ? this.size + 'px' : null
+        height: this.size ? this.size + 'px' : null,
+        marginLeft: this.left ? this.left + 'px' : null,
+        marginRight: this.right ? this.right + 'px' : null
       }
     }
   }

+ 29 - 6
src/components/Cards/CourseCard.vue

@@ -4,16 +4,25 @@
       <c-icon theme="orange" :left="-2" :right="6" :size="18">book</c-icon>
       {{course.name || '暂无课程名称'}}
     </div>
-    <div v-if="!hideBio"  class="course-card-bio">{{course.bio || '暂无课程简介'}}</div>
-    <div v-if="!hideTeacher"  class="course-card-teacher">{{course.teacherName}}</div>
+    <show-transition-small>
+      <div v-if="!hideBio"  class="course-card-bio">{{course.bio || '暂无课程简介'}}</div>
+    </show-transition-small>
+    <show-transition-small>
+      <div v-if="!hideTeacher" class="course-card-teacher">教师:{{course.teacherName}}</div>
+    </show-transition-small>
+    <show-transition-small>
+      <div v-if="!hideTime"  class="course-card-time">创建时间:{{createAt}}</div>
+    </show-transition-small>
   </div>
 </template>
 
 <script>
 import CIcon from '@/components/Basic/CIcon'
+import ShowTransitionSmall from '@/animations/ShowTransitionSmall'
+import momentFromNow from '@/utils/moment-from-now'
 
 export default {
-  components: { CIcon },
+  components: { CIcon, ShowTransitionSmall },
   props: {
     clickHandler: {
       type: Function,
@@ -29,12 +38,21 @@ export default {
     hideBio: {
       type: Boolean,
       default: false
+    },
+    hideTime: {
+      type: Boolean,
+      default: false
     }
   },
   methods: {
     handleClick () {
       this.clickHandler(this.course)
     }
+  },
+  computed: {
+    createAt () {
+      return momentFromNow(this.course.createAt)
+    }
   }
 }
 </script>
@@ -61,7 +79,6 @@ export default {
   font-size: 16px;
   display: flex;
   align-items: center;
-  margin-bottom: 6px;
   position: relative;
   line-height: 1.8;
 }
@@ -69,12 +86,18 @@ export default {
 .course-card-bio {
   font-size: 14px;
   color: #6a7a8a;
-  margin-bottom: 6px;
 }
 
 .course-card-teacher {
   font-size: 14px;
-  color: #6a7a8a;
+  color: #8a9aaa;
+  line-height: 1.8;
+}
+
+.course-card-time {
+  font-size: 12px;
+  color: #aabaca;
+  line-height: 1.8;
 }
 
 .c-icon {

+ 17 - 17
src/components/Cards/SlideCard.vue

@@ -5,9 +5,15 @@
       {{ slide.name }}
       <c-tag :theme="tagTheme" v-if="!hideState">{{slideStateTranslation[slide.state]}}</c-tag>
     </div>
-    <div v-if="!hideUpdateTime" class="slide-card-time">最近更新:{{ updateTime }}</div>
-    <div v-if="!hideCourse" class="slide-card-course">所属课程:{{ slide.courseName }}</div>
-    <div v-if="!hideTeacher" class="slide-card-teacher">教师:{{ slide.teacherName }}</div>
+    <show-transition-small>
+      <div v-if="!hideUpdateTime" class="slide-card-time">最近更新:{{ updateTime }}</div>
+    </show-transition-small>
+    <show-transition-small>
+      <div v-if="!hideCourse" class="slide-card-course">所属课程:{{ slide.courseName }}</div>
+    </show-transition-small>
+    <show-transition-small>
+      <div v-if="!hideTeacher" class="slide-card-teacher">教师:{{ slide.teacherName }}</div>
+    </show-transition-small>
   </div>
 </template>
 
@@ -16,9 +22,11 @@ import slideStateTranslation from '@/server/enums/slide-state-translation'
 import CIcon from '@/components/Basic/CIcon'
 import CTag from '@/components/Basic/CTag'
 import momentFromNow from '@/utils/moment-from-now'
+import ShowTransitionSmall from '@/animations/ShowTransitionSmall'
+import stateColor from '@/utils/state-color'
 
 export default {
-  components: { CIcon, CTag },
+  components: { CIcon, CTag, ShowTransitionSmall },
   props: {
     clickHandler: {
       type: Function,
@@ -54,15 +62,7 @@ export default {
       return momentFromNow(this.slide.updateAt)
     },
     tagTheme () {
-      if (this.slide.state === 'IN_CLASS') {
-        return 'green'
-      } else if (this.slide.state === 'DRAFT') {
-        return 'red'
-      } else if (this.slide.state === 'BEFORE_CLASS' || this.slide.state === 'FINISH') {
-        return 'orange'
-      } else {
-        return 'blue'
-      }
+      return stateColor(this.slide.state)
     }
   },
   methods: {
@@ -78,6 +78,7 @@ export default {
 
 .slide-card {
   padding: 6px 8px 6px 16px;
+  margin: 12px 0;
   border-radius: 12px;
   // border-left: 4px solid $input-background-color;
   background-color: $card-background;
@@ -94,18 +95,17 @@ export default {
   display: flex;
   align-items: center;
   font-size: 16px;
-  margin-bottom: 4px;
   line-height: 1.9;
 }
 
 .slide-card-time, .slide-card-course {
-  font-size: 14px;
+  font-size: 12px;
   color: #6a7a8a;
-  margin-bottom: 4px;
+  line-height: 1.8;
 }
 
 .slide-card-teacher {
-  font-size: 14px;
+  font-size: 12px;
   color: #6a7a8a;
 }
 

+ 28 - 28
src/components/Details/CourseDetail.vue

@@ -1,26 +1,24 @@
 <template>
-  <div>
-    <div class="detail">
-      <div>
-        <div>课程名称:</div>
-        <div>{{course.name}}</div>
-      </div>
-      <div>
-        <div>课程简介:</div>
-        <div>{{course.bio}}</div>
-      </div>
-      <div>
-        <div>创建时间:</div>
-        <div>{{createTime}}</div>
-      </div>
-      <div v-if="userInfo.type === 'TEACHER'">
-        <div>选课码:</div>
-        <div>{{code}}</div>
-      </div>
-      <div>
-        <div>教师:</div>
-        <div>{{course.teacherName}}</div>
-      </div>
+  <div class="course-detail">
+    <div>
+      <div>课程名称:</div>
+      <div>{{course.name}}</div>
+    </div>
+    <div>
+      <div>课程简介:</div>
+      <div>{{course.bio}}</div>
+    </div>
+    <div>
+      <div>创建时间:</div>
+      <div>{{createTime}}</div>
+    </div>
+    <div v-if="userInfo.type === 'TEACHER'">
+      <div>选课码:</div>
+      <div>{{code}}</div>
+    </div>
+    <div>
+      <div>教师:</div>
+      <div>{{course.teacherName}}</div>
     </div>
   </div>
 </template>
@@ -58,14 +56,17 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.detail {
-  font-size: 14px;
-  margin-bottom: 24px;
-  color: #7a8a9a;
+.course-detail {
+  font-weight: bold;
+  font-size: 13px;
+  line-height: 1.6;
+  color: #9aaaba;
+  box-sizing: border-box;
 
   & > div {
-    margin-top: 2px;
+    box-sizing: border-box;
     display: flex;
+
     & > div:first-child {
       min-width: 80px;
     }
@@ -73,7 +74,6 @@ export default {
 
   & > div:not(:last-child) {
     border-bottom: 1px dashed #aabaca;
-    padding-bottom: 2px;
   }
 }
 </style>

+ 4 - 6
src/components/Details/SlideDetail.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="detail">
+  <div class="slide-detail">
     <div>
       <div>课件名称:</div>
       <div>{{slide.name}}</div>
@@ -50,13 +50,12 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.detail {
-  font-size: 14px;
-  margin-bottom: 24px;
+.slide-detail {
+  font-size: 13px;
   color: #7a8a9a;
+  line-height: 1.6;
 
   & > div {
-    margin-top: 2px;
     display: flex;
     & > div:first-child {
       min-width: 80px;
@@ -65,7 +64,6 @@ export default {
 
   & > div:not(:last-child) {
     border-bottom: 1px dashed #aabaca;
-    padding-bottom: 2px;
   }
 }
 </style>

+ 17 - 5
src/components/Details/SlideStateBar.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="slide-state-bar">
-    <div v-for="button of buttons" :class="state === button.state ? 'active' : ''"
+    <div v-for="button of buttons" :class="state === button.state ? color(button.state) : ''"
          :key="button.state"
          @click="handleChangeState(button.state)">{{button.value}}
     </div>
@@ -8,6 +8,7 @@
 </template>
 
 <script>
+import stateColor from '@/utils/state-color'
 
 export default {
   props: ['state'],
@@ -32,6 +33,9 @@ export default {
     }
   },
   methods: {
+    color (state) {
+      return stateColor(state)
+    },
     handleChangeState (state) {
       this.$emit('change-state', state)
     }
@@ -41,6 +45,7 @@ export default {
 
 <style lang="scss" scoped>
 @import '~@/style/theme.scss';
+@import '~@/style/mixin/set-theme.scss';
 
 .slide-state-bar {
   display: flex;
@@ -49,18 +54,25 @@ export default {
   font-size: 14px;
   position: relative;
   margin: 0 0 24px 0;
+  color: #9aaaba;
 
   & > div {
     cursor: pointer;
-    color: #9aaaba;
     display: inline-block;
     padding: 4px 8px;
     transition: .2s;
   }
+}
 
-  .active {
-    background: $theme-green-transparent;
-    color: $theme-green;
+@mixin theme($name, $color, $backgroundColor) {
+  .#{$name} {
+    color: $color;
+    background-color: $backgroundColor;
+    &:hover {
+      background-color: darken($backgroundColor, 5);
+    }
   }
 }
+
+@include setTheme()
 </style>

+ 29 - 0
src/components/Layouts/TabActions.vue

@@ -0,0 +1,29 @@
+<template>
+  <show-transition-small>
+    <div v-if="ifCondition" class="tab-actions">
+      <slot></slot>
+    </div>
+  </show-transition-small>
+</template>
+
+<script>
+import ShowTransitionSmall from '@/animations/ShowTransitionSmall'
+
+export default {
+  components: { ShowTransitionSmall },
+  props: {
+    ifCondition: {
+      type: Boolean,
+      default: false
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.tab-actions {
+  display: flex;
+  width: 100%;
+  transform: translateY(-12px);
+}
+</style>

+ 2 - 2
src/style/theme.scss

@@ -1,10 +1,10 @@
 $theme-blue: #5588e5;
-$theme-orange: #ffaa44;
+$theme-orange: darken(#ffaa44, 4);
 $theme-red: #f44336;
 $theme-green: #4caf50;
 
 $theme-blue-transparent: rgba(84, 133, 229, .1);
-$theme-orange-transparent: rgba(255, 170, 68, .15);
+$theme-orange-transparent: rgba(255, 170, 68, .12);
 $theme-red-transparent: rgba(244, 67, 54, .1);
 $theme-green-transparent: rgba(76, 175, 80, .1);
 

+ 11 - 0
src/utils/state-color.js

@@ -0,0 +1,11 @@
+export default function stateColor(state) {
+  if (state === 'IN_CLASS') {
+    return 'green'
+  } else if (state === 'DRAFT') {
+    return 'red'
+  } else if (state === 'BEFORE_CLASS' || state === 'FINISH') {
+    return 'orange'
+  } else {
+    return 'blue'
+  }
+}

+ 3 - 3
src/views/teacher/components/CreateNewCourse.vue

@@ -4,10 +4,10 @@
     <div class="sub-title">课程名称:</div>
     <c-input placeholder="不能与已有课程名称重名" v-model="name"/>
     <div class="sub-title">课程简介:</div>
-    <c-input textarea="true" maxlength="200" v-model="bio" placeholder="课程简要介绍,字数上限为 200"/>
+    <c-input textarea :maxlength="200" v-model="bio" placeholder="课程简要介绍"/>
     <div class="sub-title">选课码:</div>
     <c-input placeholder="学生将通过选课码注册课程" v-model="code"/>
-    <c-button theme="blue" block @click="handleCreate" noClickWarn="!formChecked()">确认创建</c-button>
+    <c-button theme="blue" block @click="handleCreate" :no-click-warn="!formChecked()">确认创建</c-button>
   </tab-side-component>
 </template>
 
@@ -68,6 +68,6 @@ export default {
 
 <style lang="scss" scoped>
 .c-input {
-  margin-bottom: 24px;
+  margin-bottom: 16px;
 }
 </style>

+ 21 - 23
src/views/teacher/components/TeacherCourseDetail.vue

@@ -1,14 +1,22 @@
 <template>
   <tab-side-component>
-    <back-title>课程详情</back-title>
-    <course-detail :course="course"></course-detail>
+    <back-title>
+      课程详情
+      <c-button :theme="showMore ? 'blue' : 'normal'" :left="8" text :icon="showMore ? 'unfold_less' : 'unfold_more'"
+                @click="showMore = !showMore">
+        {{showMore ? '收起' : '显示'}}详情
+      </c-button>
+    </back-title>
+    <show-transition>
+      <course-detail v-if="showMore" :course="course"></course-detail>
+    </show-transition>
     <div class="sub-title">修改选课码:</div>
     <div class="change-wrapper mb-24">
       <c-input placeholder="修改不会影响已选学生" v-model="newCode"/>
-      <c-button theme="blue" @click="handleChangeCode">确认修改</c-button>
+      <c-button text theme="green" icon="check">确认</c-button>
     </div>
-    <c-button theme="blue" block class="mb-16" @click="handleCheckSlideList">点击查看课件列表</c-button>
-    <c-button theme="red" block @click="handleDeleteCourse">删除课程</c-button>
+    <c-button icon="list" theme="blue" block class="mb-16" @click="handleCheckSlideList">课件列表</c-button>
+    <c-button icon="delete" theme="red" block @click="handleDeleteCourse">删除课程</c-button>
   </tab-side-component>
 </template>
 
@@ -21,12 +29,14 @@ import { mapState } from 'vuex'
 import { modifyCourse, removeCourse } from '@/server/course'
 import CourseDetail from '@/components/Details/CourseDetail'
 import momentFromNow from '@/utils/moment-from-now'
+import ShowTransition from '@/animations/ShowTransition'
 
 export default {
-  components: { CourseDetail, BackTitle, CButton, CInput, TabSideComponent },
+  components: { CourseDetail, BackTitle, CButton, CInput, TabSideComponent, ShowTransition },
   data () {
     return {
-      newCode: ''
+      newCode: '',
+      showMore: true
     }
   },
   computed: {
@@ -64,22 +74,6 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.detail {
-  font-size: 14px;
-  margin-bottom: 24px;
-  color: #7a8a9a;
-
-  div {
-    margin-top: 2px;
-
-  }
-
-  div:not(:last-child) {
-    border-bottom: 1px dashed #aabaca;
-    padding-bottom: 2px;
-  }
-}
-
 .c-input {
   margin-bottom: 24px;
 }
@@ -97,4 +91,8 @@ export default {
     margin-left: 12px;
   }
 }
+
+.course-detail {
+  transform: translateY(-16px);
+}
 </style>

+ 32 - 7
src/views/teacher/components/TeacherCourseSlides.vue

@@ -1,8 +1,22 @@
 <template>
   <tab-side-component>
-    <back-title>{{course.name}}</back-title>
-    <course-detail :course="course"></course-detail>
-    <div class="sub-title">课件列表</div>
+    <back-title>课件列表
+      <c-button :theme="showActions ? 'orange' : 'normal'" :left="8" text icon="settings"
+                @click="showActions = !showActions">设置
+      </c-button>
+    </back-title>
+    <tab-actions :if-condition="showActions">
+      <c-button theme="green" icon="add" class="mr-8" @click="handleToManager">管理本课程</c-button>
+      <c-button theme="blue" :icon="showMore ? 'unfold_less' : 'unfold_more'" @click="showMore = !showMore">
+        {{showMore ? '收起' : '展开'}}详情
+      </c-button>
+    </tab-actions>
+    <div class="sub-title">
+      {{course.name}}
+    </div>
+    <show-transition>
+      <course-detail v-if="showMore" :course="course"></course-detail>
+    </show-transition>
     <div class="no-content-warning" v-if="slides === null">
       <div class="no-content-warning-tip">加载中...</div>
     </div>
@@ -11,9 +25,9 @@
       <c-button block theme="blue" @click="handleToCreateCourse">去创建课件</c-button>
     </div>
     <template v-for="slide of slides">
-      <slide-card :key="slide.id" hide-teacher hide-course :slide="slide" :click-handler="handleSlideCardClick"></slide-card>
+      <slide-card :key="slide.id" :hide-teacher="!showMore" :hide-course="!showMore" :hide-update-time="!showMore"
+                  :slide="slide" :click-handler="handleSlideCardClick"></slide-card>
     </template>
-    <c-button theme="blue" block class="mt-16" @click="handleToManager">管理本课程</c-button>
   </tab-side-component>
 </template>
 
@@ -26,13 +40,17 @@ import { teacherGetSlidesByCourse } from '@/server/slide'
 import { SET_SLIDE_INFO } from '@/store/types/pdf-types'
 import { SET_COURSE } from '@/store/types/main-types'
 import { mapState } from 'vuex'
+import TabActions from '@/components/Layouts/TabActions'
 import CourseDetail from '@/components/Details/CourseDetail'
+import ShowTransition from '@/animations/ShowTransition'
 
 export default {
-  components: { CourseDetail, CButton, SlideCard, BackTitle, TabSideComponent },
+  components: { CourseDetail, ShowTransition, TabActions, CButton, SlideCard, BackTitle, TabSideComponent },
   data () {
     return {
-      slides: null
+      slides: null,
+      showActions: false,
+      showMore: false
     }
   },
   watch: {
@@ -85,4 +103,11 @@ export default {
 .no-content-warning {
   height: unset;
 }
+
+.sub-title {
+  display: flex;
+  align-items: center;
+  margin: 0;
+  padding-bottom: 4px;
+}
 </style>

+ 50 - 25
src/views/teacher/components/TeacherSlideDetail.vue

@@ -1,30 +1,48 @@
 <template>
   <tab-side-component>
-    <back-title>课件详情</back-title>
-    <slide-detail :slide="slide"></slide-detail>
-    <c-button block theme="blue" standout class="mb-16" @click="handleReadSlide">浏览课件</c-button>
+    <back-title>
+      课件详情
+      <c-button :theme="showMore ? 'blue' : 'normal'" :left="8" text :icon="showMore ? 'unfold_less' : 'unfold_more'"
+                @click="showMore = !showMore">
+        {{showMore ? '收起' : '显示'}}详情
+      </c-button>
+    </back-title>
+    <show-transition>
+      <slide-detail v-if="showMore" :slide="slide"></slide-detail>
+    </show-transition>
+    <c-button block theme="blue" standout class="mb-12" @click="handleReadSlide" icon="play_arrow" no-bold>浏览课件</c-button>
     <a :href="slideUrl">
-      <c-button block theme="blue" class="mb-32">
+      <c-button block theme="green" no-bold class="mb-32" icon="cloud_download">
         下载课件
       </c-button>
     </a>
-    <div class="sub-title">课件信息设置</div>
-    <div class="mb-8">
-      <b>修改课件名称</b>
-    </div>
-    <div class="mb-16 modify-slide-name">
-      <c-input placeholder="在此输入课件新名称" v-model="newSlideName"></c-input>
-      <c-button theme="blue" @click="handleModifySlideName">确认修改</c-button>
-    </div>
-    <div class="mb-8">
-      <b>幻灯片状态设置</b>
-    </div>
-    <slide-state-bar :state="this.slide.state" @change-state="handleChangeState"></slide-state-bar>
-    <div v-if="this.slide.state === 'DRAFT'" class="tip mb-32">
-      提示:学生无法查看处于草稿状态的幻灯片,如果希望公开请设置为其他状态
+    <div class="sub-title">
+      课件信息设置
+      <c-button :theme="showSettings ? 'blue' : 'normal'" :left="8" text :icon="showSettings ? 'unfold_less' : 'unfold_more'"
+                @click="showSettings = !showSettings">
+        {{showSettings ? '收起' : '更多'}}设置
+      </c-button>
     </div>
-    <c-button block theme="blue" class="mb-16" @click="handleQuizSetting">习题设置 & 习题完成统计</c-button>
-    <c-button block theme="red" class="mb-16" @click="handleDeleteSlide">删除课件</c-button>
+    <show-transition>
+      <div v-if="showSettings">
+        <div class="mb-8">
+          <b>修改课件名称</b>
+        </div>
+        <div class="mb-16 modify-slide-name">
+          <c-input placeholder="在此输入课件新名称" v-model="newSlideName"></c-input>
+          <c-button :left="8" text theme="green" icon="check" @click="handleModifySlideName">确认</c-button>
+        </div>
+        <div class="mb-8">
+          <b>幻灯片状态设置</b>
+        </div>
+        <slide-state-bar :state="this.slide.state" @change-state="handleChangeState"></slide-state-bar>
+        <div v-if="this.slide.state === 'DRAFT'" class="tip mb-32">
+          提示:学生无法查看处于草稿状态的幻灯片,如果希望公开请设置为其他状态
+        </div>
+      </div>
+    </show-transition>
+    <c-button block theme="blue" class="mb-16" @click="handleQuizSetting" icon="edit">习题设置</c-button>
+    <c-button block theme="red" class="mb-16" @click="handleDeleteSlide" icon="close">删除课件</c-button>
   </tab-side-component>
 </template>
 
@@ -39,14 +57,17 @@ import SlideDetail from '@/components/Details/SlideDetail'
 import slideStateTranslation from '@/server/enums/slide-state-translation'
 import CInput from '@/components/Basic/CInput'
 import SlideStateBar from '@/components/Details/SlideStateBar'
+import ShowTransition from '@/animations/ShowTransition'
 
 export default {
-  components: { CInput, BackTitle, CButton, TabSideComponent, SlideDetail, SlideStateBar },
+  components: { CInput, BackTitle, CButton, TabSideComponent, SlideDetail, SlideStateBar, ShowTransition },
   data () {
     return {
       slideUrl: '',
       slideStateTranslation,
-      newSlideName: ''
+      newSlideName: '',
+      showMore: false,
+      showSettings: false
     }
   },
   computed: {
@@ -121,6 +142,8 @@ export default {
 }
 
 .sub-title {
+  display: flex;
+  align-items: center;
   margin-bottom: 24px;
 }
 
@@ -142,9 +165,8 @@ export default {
 .modify-slide-name {
   display: flex;
   align-items: center;
-
-  .c-button {
-    margin-left: 12px;
+  .c-input {
+    flex: 1
   }
 }
 
@@ -153,6 +175,9 @@ export default {
   font-size: 12px;
 }
 
+.slide-detail {
+  transform: translateY(-16px);
+}
 @media (max-width: 600px) {
   .file-uploader {
     font-size: 14px;

+ 6 - 56
src/views/teacher/tabs/CreateSlide.vue

@@ -2,32 +2,17 @@
   <div class="tab">
     <h1 class="title">新建幻灯片</h1>
     <div class="sub-title">所属课程:</div>
-    <div class="select-wrapper">
-      <label>
-        <select v-model="selectedCourseId">
-          <option disabled value="">[ 未填写 ]</option>
-          <option v-for="course in courses" :value="course.id" :key="course.id">
-            {{ course.name }}
-          </option>
-        </select>
-      </label>
-    </div>
+    <c-select v-model="selectedCourseId" class="mb-16"
+              :list="this.courses.map(course => ({value: course.id, display: course.name}))"></c-select>
     <div class="sub-title">课件名称:</div>
-    <c-input
+    <c-input class="mb-16"
       v-model="slideName"
       placeholder="请勿与同课程课件重名"
     />
     <div class="sub-title">课件文件
       <c-tag theme="green">目前支持 PDF 格式</c-tag>
     </div>
-    <file-uploader accept="application/pdf" class="mb-24" @change="handleFileChanged"/>
-<!--    <label class="draft-checkbox">-->
-<!--      <input type="checkbox">-->
-<!--      <b>是否将课件初始状态设为草稿状态(学生不可见)</b>-->
-<!--    </label>-->
-<!--    <div class="tip mb-16">-->
-<!--      提示:课件创建成功后您需将课件状态设置为非草稿状态,学生才能进行浏览-->
-<!--    </div>-->
+    <file-uploader accept="application/pdf" class="mb-16" @change="handleFileChanged"/>
     <c-button theme="blue" block :no-click-warn="!formChecked()" @click="handleSubmit">
       添加
     </c-button>
@@ -41,9 +26,10 @@ import FileUploader from '@/components/Basic/FileUploader'
 import { teacherGetCourses } from '@/server/course'
 import { createSlide } from '@/server/slide'
 import CTag from '@/components/Basic/CTag'
+import CSelect from '@/components/Basic/CSelect'
 
 export default {
-  components: { CTag, FileUploader, CButton, CInput },
+  components: { CSelect, CTag, FileUploader, CButton, CInput },
   data () {
     return {
       courses: [],
@@ -60,7 +46,6 @@ export default {
       this.$setErrorMessage('获取课件列表过程中:' + err.response.data.msg)
     })
   },
-  computed: {},
   methods: {
     formChecked () {
       if (!this.selectedCourseId) {
@@ -103,40 +88,5 @@ export default {
 }
 </script>
 <style lang="scss" scoped>
-@import '~@/style/theme.scss';
 @import "~@/views/shared/tab-common";
-
-select {
-  box-sizing: border-box;
-  font-size: 16px;
-  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;
-  -moz-appearance: none;
-  //通过padding-left的值让文字居中
-
-  transition: .2s;
-
-  &:hover {
-    background: darken($input-background-color, 5);
-  }
-}
-
-.select-wrapper, .c-input {
-  margin-bottom: 24px;
-}
-
-.tip {
-  color: $theme-red;
-  font-size: 12px;
-}
 </style>

+ 21 - 7
src/views/teacher/tabs/TeacherCoursesManager.vue

@@ -1,7 +1,18 @@
 <template>
   <div class="tab complex-tab">
     <div class="left">
-      <h1 class="title">课程管理</h1>
+      <h1 class="title">
+        课程管理
+        <c-button :theme="showActions ? 'orange' : 'normal'" :left="8" text icon="settings"
+                  @click="showActions = !showActions">新建 & 设置
+        </c-button>
+      </h1>
+      <tab-actions :if-condition="showActions">
+        <c-button theme="green" icon="add" class="mr-8" @click="handleCreateNewCourse">添加课程</c-button>
+        <c-button theme="blue" :icon="showMore ? 'unfold_less' : 'unfold_more'" @click="showMore = !showMore">
+          {{showMore ? '收起' : '展开'}}详情
+        </c-button>
+      </tab-actions>
       <div class="sub-title">我的课程</div>
       <div class="no-content-warning" v-if="courses === null">
         <div class="no-content-warning-tip">加载中...</div>
@@ -10,10 +21,9 @@
         <div class="no-content-warning-tip">你还没有过开设课程~</div>
       </div>
       <template v-for="(course, index) of courses">
-        <course-card hide-teacher :key="course.id" :course="course" :index="index + 1"
+        <course-card hide-teacher :hide-time="!showMore" :hide-bio="!showMore" :key="course.id" :course="course" :index="index + 1"
                      :clickHandler="handleCourseCardClick"></course-card>
       </template>
-      <c-button @click="handleCreateNewCourse" block theme="blue">新建课程</c-button>
     </div>
     <slide-in-different-transition>
       <router-view/>
@@ -27,12 +37,15 @@ import CourseCard from '@/components/Cards/CourseCard'
 import { teacherGetCourses } from '@/server/course'
 import { SET_COURSE } from '@/store/types/main-types'
 import SlideInDifferentTransition from '@/animations/SlideInDifferentTransition'
+import TabActions from '@/components/Layouts/TabActions'
 
 export default {
-  components: { CButton, CourseCard, SlideInDifferentTransition },
+  components: { TabActions, CButton, CourseCard, SlideInDifferentTransition },
   data () {
     return {
-      courses: null
+      courses: null,
+      showActions: false,
+      showMore: false
     }
   },
   mounted () {
@@ -73,7 +86,8 @@ export default {
 <style lang="scss" scoped>
 @import "~@/views/shared/tab-common";
 
-.c-button {
-  margin-top: 24px;
+.title {
+  display: flex;
+  align-items: center;
 }
 </style>

+ 26 - 10
src/views/teacher/tabs/TeacherExplore.vue

@@ -1,9 +1,22 @@
 <template>
   <div class="tab complex-tab">
     <div class="left">
-      <h1 class="title">我的主页</h1>
+      <h1 class="title">
+        我的主页
+        <c-button :theme="showActions ? 'orange' : 'normal'" :left="8" text icon="settings"
+                  @click="showActions = !showActions">设置 & 题库
+        </c-button>
+      </h1>
+      <tab-actions :if-condition="showActions">
+        <router-link :to="{name: 'questions'}">
+          <c-button :right="8" theme="green" icon="assignment">题库设置</c-button>
+        </router-link>
+        <c-button theme="blue" :icon="showMore ? 'unfold_less' : 'unfold_more'" @click="showMore = !showMore">
+          {{showMore ? '收起' : '展开'}}详情
+        </c-button>
+      </tab-actions>
       <!--      <search-bar></search-bar>-->
-      <section>
+      <section class="mb-24">
         <h1 class="sub-title">最近编辑</h1>
         <div class="no-content-warning" v-if="slides === null">
           <div class="no-content-warning-tip">加载中...</div>
@@ -12,10 +25,9 @@
           <div class="no-content-warning-tip">无最近编辑记录~</div>
         </div>
         <template v-for="slide of slides">
-          <slide-card hide-course hide-teacher :key="slide.id" :slide="slide"
+          <slide-card :hide-update-time="!showMore" :hide-course="!showMore" :hide-teacher="!showMore" :key="slide.id" :slide="slide"
                       :click-handler="handleSlideCardClick"></slide-card>
         </template>
-        <br>
       </section>
       <section>
         <h1 class="sub-title">课程列表</h1>
@@ -27,8 +39,8 @@
           <c-button block theme="blue" @click="handleToCreateCourse">去创建课程</c-button>
         </div>
         <template v-for="(course, index) of courses">
-          <course-card :key="course.id" :course="course" :index="index + 1" hide-bio
-                       hide-teacher :clickHandler="handleCourseCardClick"></course-card>
+          <course-card :key="course.id" :course="course" :index="index + 1" :hide-bio="!showMore"
+                       :hide-teacher="!showMore" :hide-time="!showMore" :clickHandler="handleCourseCardClick"></course-card>
         </template>
       </section>
     </div>
@@ -48,13 +60,16 @@ import { SET_SLIDE_INFO } from '@/store/types/pdf-types'
 import { SET_COURSE } from '@/store/types/main-types'
 import { mapState } from 'vuex'
 import SlideInDifferentTransition from '@/animations/SlideInDifferentTransition'
+import TabActions from '@/components/Layouts/TabActions'
 
 export default {
-  components: { CButton, CourseCard, SlideCard, SlideInDifferentTransition },
+  components: { TabActions, CButton, CourseCard, SlideCard, SlideInDifferentTransition },
   data () {
     return {
       courses: null,
-      slides: null
+      slides: null,
+      showActions: false,
+      showMore: false
     }
   },
   computed: {
@@ -78,7 +93,7 @@ export default {
   },
   methods: {
     updateInfo () {
-      teacherGetSlides({ size: 1, sort: 'updateAt,desc' }).then(res => {
+      teacherGetSlides({ size: 2, sort: 'updateAt,desc' }).then(res => {
         this.slides = res.data
       }).catch(err => {
         this.$setErrorMessage(err.response.data.msg)
@@ -113,7 +128,8 @@ export default {
 @import "~@/views/shared/tab-common";
 
 .title {
-  margin-bottom: 16px;
+  display: flex;
+  align-items: center;
 }
 
 .search-bar {

+ 59 - 25
src/views/teacher/tabs/TeacherPersonal.vue

@@ -5,21 +5,45 @@
         个人信息设置
         <c-tag theme="blue">教师</c-tag>
       </h1>
-      <c-button block theme="blue" class="mb-24" @click="handleToQuestions">题库设置</c-button>
-      <div class="sub-title">修改姓名</div>
-      <c-input :placeholder="'目前资料中的姓名:' + userInfo.name" class="mb-16" v-model="newName"></c-input>
-      <c-button theme="orange" block class="mb-32" @click="handleModifyName">确认修改姓名</c-button>
-      <div class="sub-title">修改密码</div>
-      <div class="verified-code-wrapper">
-        <c-input placeholder="邮箱验证码" type="number" v-model="this.code"></c-input>
-        <c-button theme="blue" :no-click-warn="typeof this.emailButtonContent === typeof 0"
-                  @click="handleSendEmailCode">{{ emailButtonContent }}
+      <div class="info">
+        <div>邮箱:{{userInfo.email}}</div>
+        <div>手机:{{userInfo.phone}}</div>
+        <div>姓名:{{userInfo.name}}</div>
+        <div>加入时间:{{momentFromNow(userInfo.createAt)}}</div>
+      </div>
+      <div class="sub-title">
+        修改姓名
+        <c-button :theme="showChangeProfile ? 'blue' : 'normal'" :left="8" text :icon="showChangeProfile ? 'unfold_less' : 'unfold_more'"
+                  @click="showChangeProfile = !showChangeProfile">
+          {{showChangeProfile ? '收起' : '展开'}}
+        </c-button>
+      </div>
+      <show-transition>
+        <div v-if="showChangeProfile">
+          <c-input :placeholder="'目前资料中的姓名:' + userInfo.name" class="mb-16" v-model="newName"></c-input>
+          <c-button theme="orange" block class="mb-32" @click="handleModifyName">确认修改姓名</c-button>
+        </div>
+      </show-transition>
+      <div class="sub-title">
+        修改密码
+        <c-button :theme="showChangePassword ? 'blue' : 'normal'" :left="8" text :icon="showChangePassword ? 'unfold_less' : 'unfold_more'"
+                  @click="showChangePassword = !showChangePassword">
+          {{showChangePassword ? '收起' : '展开'}}
         </c-button>
       </div>
-      <c-input placeholder="新密码" type="password" class="mb-16" v-model="password"></c-input>
-      <c-input placeholder="确认新密码" type="password" class="mb-16" v-model="passwordEnsure"></c-input>
-      <c-button block theme="orange" class="mb-32" @click="handleModifyPassword">确认修改密码</c-button>
-      <c-button block theme="red" @click="handleLogout">登出</c-button>
+      <show-transition>
+        <div v-if="showChangePassword">
+          <div class="verified-code-wrapper">
+            <c-input placeholder="邮箱验证码" type="number" v-model="this.code"></c-input>
+            <c-button theme="blue" :no-click-warn="typeof this.emailButtonContent === typeof 0"
+                      @click="handleSendEmailCode">{{ emailButtonContent }}
+            </c-button>
+          </div>
+          <c-input placeholder="新密码" type="password" class="mb-16" v-model="password"></c-input>
+          <c-input placeholder="确认新密码" type="password" class="mb-16" v-model="passwordEnsure"></c-input>
+          <c-button block theme="orange" @click="handleModifyPassword">确认修改密码</c-button>
+        </div>
+      </show-transition>
     </div>
     <slide-in-different-transition>
       <router-view/>
@@ -31,20 +55,24 @@
 import CButton from '@/components/Basic/CButton'
 import CInput from '@/components/Basic/CInput'
 import { sendEmailCode } from '@/server/code'
-import { logout, modifyUser, resetPasswordByEmail } from '@/server/user'
+import { modifyUser, resetPasswordByEmail } from '@/server/user'
 import { mapState } from 'vuex'
 import CTag from '@/components/Basic/CTag'
 import SlideInDifferentTransition from '@/animations/SlideInDifferentTransition'
+import ShowTransition from '@/animations/ShowTransition'
+import momentFromNow from '@/utils/moment-from-now'
 
 export default {
-  components: { CTag, CInput, CButton, SlideInDifferentTransition },
+  components: { CTag, CInput, CButton, SlideInDifferentTransition, ShowTransition },
   data () {
     return {
       emailButtonContent: '发送验证码',
       newName: '',
       password: '',
       code: '',
-      passwordEnsure: ''
+      passwordEnsure: '',
+      showChangeProfile: false,
+      showChangePassword: false
     }
   },
   computed: {
@@ -56,6 +84,7 @@ export default {
     }
   },
   methods: {
+    momentFromNow,
     handleSendEmailCode () {
       this.$setInfoMessage('正在尝试发送验证码')
       if (this.email.match(/.+@(nju.edu.cn|smail.nju.edu.cn)/)) {
@@ -80,11 +109,6 @@ export default {
         }
       }, 1000)
     },
-    handleLogout () {
-      logout()
-      this.$setSuccessMessage('您已退出登录')
-      this.$router.push({ name: 'login' })
-    },
     handleModifyPassword () {
       if (!this.password || this.password !== this.passwordEnsure) {
         this.$setInfoMessage('两次输入的密码不一致')
@@ -108,9 +132,6 @@ export default {
       }).catch(err => {
         this.$setErrorMessage(err.response.data.msg)
       })
-    },
-    handleToQuestions () {
-      this.$router.push({ name: 'questions' })
     }
   }
 }
@@ -120,6 +141,12 @@ export default {
 @import "~@/style/theme.scss";
 @import "~@/views/shared/tab-common";
 
+.sub-title {
+  font-size: 20px;
+  display: flex;
+  align-items: center;
+}
+
 .verified-code-wrapper {
   margin-bottom: 16px;
   display: flex;
@@ -135,5 +162,12 @@ export default {
     margin-left: 12px;
   }
 }
-
+.info {
+  font-size: 14px;
+  padding: 4px 12px;
+  background-color: #f4f4f4;
+  color: #7a8a9a;
+  line-height: 2;
+  margin-bottom: 16px;
+}
 </style>