Kaynağa Gözat

feat: 主页退出登录按钮

ChenYangfan 6 yıl önce
ebeveyn
işleme
0d62fefadd

+ 6 - 17
src/App.vue

@@ -25,17 +25,6 @@ export default {
     window.addEventListener('beforeunload', () => {
       sessionStorage.setItem('store', JSON.stringify(this.$store.state))
     })
-
-    // getUserInfo().then(res => {
-    //   this.userInfo = res
-    //   this.$router.beforeEach((to, from, next) => {
-    //     if (to.meta.roles && to.meta.roles.includes(this.userInfo.type.toLowerCase())) {
-    //       next()
-    //     } else {
-    //       next({ path: '/login' })
-    //     }
-    //   })
-    // })
   }
 }
 </script>
@@ -46,12 +35,12 @@ export default {
 
 @font-face {
   font-family: 'iconfont';  /* project id 1598214 */
-  src: url('//at.alicdn.com/t/font_1598214_5pufddythnd.eot');
-  src: url('//at.alicdn.com/t/font_1598214_5pufddythnd.eot?#iefix') format('embedded-opentype'),
-  url('//at.alicdn.com/t/font_1598214_5pufddythnd.woff2') format('woff2'),
-  url('//at.alicdn.com/t/font_1598214_5pufddythnd.woff') format('woff'),
-  url('//at.alicdn.com/t/font_1598214_5pufddythnd.ttf') format('truetype'),
-  url('//at.alicdn.com/t/font_1598214_5pufddythnd.svg#iconfont') format('svg');
+  src: url('//at.alicdn.com/t/font_1598214_onv4tkkd5vc.eot');
+  src: url('//at.alicdn.com/t/font_1598214_onv4tkkd5vc.eot?#iefix') format('embedded-opentype'),
+  url('//at.alicdn.com/t/font_1598214_onv4tkkd5vc.woff2') format('woff2'),
+  url('//at.alicdn.com/t/font_1598214_onv4tkkd5vc.woff') format('woff'),
+  url('//at.alicdn.com/t/font_1598214_onv4tkkd5vc.ttf') format('truetype'),
+  url('//at.alicdn.com/t/font_1598214_onv4tkkd5vc.svg#iconfont') format('svg');
 }
 
 body {

+ 10 - 0
src/components/Background.vue

@@ -76,6 +76,16 @@ export default {}
   }
 }
 
+// todo 小屏幕下先完全隐藏
+@media (max-width: 700px) {
+  .mountain {
+    display: none;
+  }
+  .sun {
+    display: none;
+  }
+}
+
 @media (max-height: 700px) {
   .mountain {
     display: none;

+ 21 - 1
src/components/CourseDetail.vue

@@ -13,6 +13,10 @@
         <div>创建时间:</div>
         <div>{{createTime}}</div>
       </div>
+      <div v-if="this.code">
+        <div>选课码:</div>
+        <div>{{code}}</div>
+      </div>
       <div>
         <div>教师:</div>
         <div>{{course.teacherName}}</div>
@@ -22,8 +26,24 @@
 </template>
 
 <script>
+import { getCourseCode } from '@/server/course'
+
 export default {
-  props: ['course'],
+  props: ['course', 'showCode'],
+  data () {
+    return {
+      code: ''
+    }
+  },
+  mounted () {
+    if (this.showCode) {
+      getCourseCode({ courseId: this.course.id }).then(res => {
+        this.code = res
+      }).catch(err => {
+        this.$setErrorMessage(err.response.data.msg)
+      })
+    }
+  },
   computed: {
     createTime () {
       return this.$moment(this.course.createAt).fromNow()

+ 4 - 0
src/router/index.js

@@ -16,6 +16,10 @@ const routes = [{
   path: '/register',
   name: 'register',
   component: () => import(/* webpackChunkName: "main" */ '../views/register/Register.vue')
+}, {
+  path: '/forget-password',
+  name: 'forget-password',
+  component: () => import(/* webpackChunkName: "main" */ '../views/forget-password/ForgetPassword.vue')
 }, {
   meta: { roles: ['teacher'] },
   path: '/pdf-reader',

+ 6 - 0
src/server/course.js

@@ -50,6 +50,12 @@ export function quitCourse({ courseId }) {
     .post(useCourseServer('/quit/') + courseId)
 }
 
+export function getCourseCode({ courseId }) {
+  return axios
+    .get(useCourseServer() + '/' + courseId + '/code')
+    .then(res => res.data)
+}
+
 export function getCourses({ key = '', page = 0, size = 10, sort = '' }) {
   return axios
     .get(useCourseServer(), {

+ 1 - 1
src/server/user.js

@@ -51,7 +51,7 @@ export function resetPasswordByEmail({ email, code, password }) {
   }
 
   return axios
-    .put('/resetPassword/email', data)
+    .put(useUserServer('/resetPassword/email'), data)
     .then(res => {})
 }
 

+ 2 - 1
src/utils/icon-map.js

@@ -24,7 +24,8 @@ const iconMap = {
   'delete': '\ue67e',
   'send': '\ue763',
   'remove': '\ue774',
-  'close': '\ue6ec'
+  'close': '\ue6ec',
+  'exit': '\ue685'
 }
 
 export default iconMap

+ 106 - 0
src/views/forget-password/ForgetPassword.vue

@@ -0,0 +1,106 @@
+<template>
+  <div class="forget-password">
+    <div>
+      <back-title>重设密码</back-title>
+      <c-input class="mb-16" placeholder="邮箱" v-model="email"></c-input>
+      <div class="verified-code-wrapper">
+        <c-input placeholder="邮箱验证码" type="number" v-model="code"></c-input>
+        <c-button class="secondary" :disabled="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 class="secondary full-width mb-32" @click="handleModifyPassword">确认修改密码</c-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import BackTitle from '@/components/BackTitle'
+import CInput from '@/components/CInput'
+import CButton from '@/components/CButton'
+import { resetPasswordByEmail } from '@/server/user'
+import { sendEmailCode } from '@/server/code'
+export default {
+  components: { CButton, CInput, BackTitle },
+  data () {
+    return {
+      emailButtonContent: '发送验证码',
+      email: '',
+      code: '',
+      password: '',
+      passwordEnsure: ''
+    }
+  },
+  methods: {
+    handleSendEmailCode () {
+      this.$setInfoMessage('正在尝试发送验证码')
+      if (this.email.match(/.+@(nju.edu.cn|smail.nju.edu.cn)/)) {
+        this.setEmailCodeCd()
+        sendEmailCode({ email: this.email }).then(res => {
+          this.$setSuccessMessage('邮箱验证码发送成功')
+        }).catch(err => {
+          this.$setErrorMessage(err.response.data.msg)
+        })
+      } else {
+        this.$setErrorMessage('本邮箱不符合南京大学校邮格式')
+      }
+    },
+    setEmailCodeCd () {
+      this._emailCodeCdInterval && clearInterval(this._emailCodeCdInterval)
+      this.emailButtonContent = 60
+      this._emailCodeCdInterval = setInterval(() => {
+        this.emailButtonContent--
+        if (typeof this.emailButtonContent !== typeof 0 || this.emailButtonContent <= 0) {
+          clearInterval(this._emailCodeCdInterval)
+          this.emailButtonContent = '发送验证码'
+        }
+      }, 1000)
+    },
+    handleModifyPassword () {
+      if (!this.password || this.password !== this.passwordEnsure) {
+        this.$setInfoMessage('两次输入的密码不一致')
+        return
+      }
+
+      resetPasswordByEmail({ email: this.email, code: this.code, password: this.password }).then(res => {
+        this.$setInfoMessage('密码修改成功')
+      }).catch(err => {
+        this.$setErrorMessage(err.response.data.msg)
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "~@/style/theme.scss";
+.forget-password {
+  display: flex;
+  min-height: 100vh;
+  align-items: center;
+  justify-content: center;
+  & > div {
+    flex: 1;
+    max-width: 400px;
+  }
+}
+
+.verified-code-wrapper {
+  margin-bottom: 16px;
+  display: flex;
+  align-items: center;
+  flex: 1;
+
+  .c-input {
+    margin-bottom: unset;
+    flex: 1;
+  }
+
+  .c-button {
+    margin-left: 12px;
+  }
+}
+
+</style>

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

@@ -13,6 +13,7 @@
         <c-button @click="handleSubmit" :disabled="!formChecked()">登录</c-button>
         <c-button class="secondary" @click="handleToRegister">去注册</c-button>
       </div>
+      <router-link class="link" to="/forget-password">忘记密码?</router-link>
       <background></background>
     </div>
   </div>
@@ -63,6 +64,8 @@ export default {
 </script>
 
 <style lang="scss" scoped>
+@import "~@/style/theme.scss";
+
 .login-view-container {
   min-height: 100vh;
   display: flex;
@@ -103,4 +106,16 @@ export default {
   color: #7a8a9a;
   font-size: 12px;
 }
+
+.link {
+  color: #aabaca;
+  display: block;
+  margin-top: 16px;
+  font-weight: bold;
+  font-size: 14px;
+  transition: .1s;
+  &:hover {
+    color: $theme-blue;
+  }
+}
 </style>

+ 26 - 2
src/views/shared/UserHomeLayout.vue

@@ -29,18 +29,26 @@
         </tab-icon-button>
       </template>
     </div>
+    <div class="exit" @click="handleLogout">
+      <icon :unicode="iconMap.exit"></icon>
+      退出登录
+    </div>
   </div>
 </template>
 
 <script>
 import TabIconButton from '@/components/TabIconButton'
+import Icon from '@/components/Icon'
+import iconMap from '@/utils/icon-map'
+import { logout } from '@/server/user'
 
 export default {
-  components: { TabIconButton },
+  components: { Icon, TabIconButton },
   props: ['tabs', 'role'],
   data () {
     return {
-      currentRouteName: this.$route.name
+      currentRouteName: this.$route.name,
+      iconMap
     }
   },
   mounted () {
@@ -65,6 +73,11 @@ export default {
     },
     handleIconButtonClick (name) {
       this.$router.push(`/${this.role}/${name}`)
+    },
+    handleLogout () {
+      logout()
+      this.$setSuccessMessage('您已退出登录')
+      this.$router.push({ name: 'login' })
     }
   }
 }
@@ -152,6 +165,17 @@ export default {
   }
 }
 
+.exit {
+  cursor: pointer;
+  position: absolute;
+  top: 10px;
+  right: 24px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  color: #6a7a8a;
+}
+
 @supports (not(backdrop-filter: saturate(180%) blur(15px))) {
   .menu-container-small {
     background: rgb(255, 255, 255);

+ 21 - 7
src/views/student/tabs/StudentPersonal.vue

@@ -2,11 +2,11 @@
   <div class="tab">
     <h1 class="title">个人信息设置 <span class="tag">教师</span></h1>
     <div class="sub-title">修改姓名</div>
-    <c-input placeholder="姓名" class="mb-16" v-model="newName"></c-input>
+    <c-input :placeholder="'目前资料中的姓名:' + userInfo.name" class="mb-16" v-model="newName"></c-input>
     <c-button class="warning full-width mb-32" @click="handleModifyName">确认修改姓名</c-button>
     <div class="sub-title">修改密码</div>
     <div class="verified-code-wrapper">
-      <c-input placeholder="邮箱验证码" type="number"></c-input>
+      <c-input placeholder="邮箱验证码" type="number" v-model="code"></c-input>
       <c-button class="secondary" :disabled="typeof this.emailButtonContent === typeof 0"
                 @click="handleSendEmailCode">{{ emailButtonContent }}
       </c-button>
@@ -23,26 +23,39 @@ import CButton from '@/components/CButton'
 import CInput from '@/components/CInput'
 import { sendEmailCode } from '@/server/code'
 import { logout, modifyUser, resetPasswordByEmail } from '@/server/user'
+import { mapState } from 'vuex'
 
 export default {
   components: { CInput, CButton },
   data () {
     return {
-      email: '',
       emailButtonContent: '发送验证码',
       newName: '',
+      code: '',
       password: '',
       passwordEnsure: ''
     }
   },
+  computed: {
+    ...mapState({
+      userInfo: state => state.main.userInfo
+    }),
+    email () {
+      return this.userInfo.email
+    }
+  },
   methods: {
     handleSendEmailCode () {
-      this.$showMessage({ message: '正在尝试发送验证码', type: 'info' })
+      this.$setInfoMessage('正在尝试发送验证码')
       if (this.email.match(/.+@(nju.edu.cn|smail.nju.edu.cn)/)) {
         this.setEmailCodeCd()
-        sendEmailCode({ email: this.email })
+        sendEmailCode({ email: this.email }).then(res => {
+          this.$setSuccessMessage('邮箱验证码发送成功')
+        }).catch(err => {
+          this.$setErrorMessage(err.response.data.msg)
+        })
       } else {
-        this.$showMessage({ message: '本邮箱不符合南京大学校邮格式', type: 'error' })
+        this.$setErrorMessage('本邮箱不符合南京大学校邮格式')
       }
     },
     setEmailCodeCd () {
@@ -58,6 +71,7 @@ export default {
     },
     handleLogout () {
       logout()
+      this.$setSuccessMessage('您已退出登录')
       this.$router.push({ name: 'login' })
     },
     handleModifyPassword () {
@@ -67,7 +81,7 @@ export default {
       }
 
       resetPasswordByEmail({ email: this.email, code: this.code, password: this.password }).then(res => {
-        this.$setInfoMessage('密码修改成功')
+        this.$setSuccessMessage('密码修改成功')
       }).catch(err => {
         this.$setErrorMessage(err.response.data.msg)
       })

+ 2 - 4
src/views/student/tabs/StudentQuiz.vue

@@ -3,13 +3,11 @@
     <h1 class="title">课程测验 <span class="tag">学生</span></h1>
     <div class="sub-title">待完成的测验</div>
     <div class="no-content-warning">
-      本页面目前尚未完成 <br>
-      请前往相应课件详情处进入测验界面
+      测验功能预计将于 2 月底上线
     </div>
     <div class="sub-title">已完成的测验 & 结果</div>
     <div class="no-content-warning">
-      本页面目前尚未完成 <br>
-      请前往相应课件详情处进入测验界面
+      测验功能预计将于 2 月底上线
     </div>
   </div>
 </template>

+ 1 - 1
src/views/teacher/components/TeacherCourseDetail.vue

@@ -1,7 +1,7 @@
 <template>
   <tab-side-component>
     <back-title>课程详情</back-title>
-    <course-detail :course="course"></course-detail>
+    <course-detail :course="course" show-code="true"></course-detail>
     <div class="sub-title">修改选课码:</div>
     <div class="change-wrapper mb-24">
       <c-input placeholder="修改不会影响已选学生" v-model="newCode"/>

+ 9 - 0
src/views/teacher/components/TeacherSlideDetail.vue

@@ -20,6 +20,9 @@
     <c-button v-if="nextState" class="warning full-width mb-16" @click="handleToNextState">
       将幻灯片状态设置为:{{slideStateTranslation[nextState]}}
     </c-button>
+    <div v-if="this.slide.state === 'DRAFT'" class="tip mb-16">
+      提示:学生无法查看处于草稿状态的幻灯片,如果希望公开请设置为其他状态
+    </div>
     <c-button class="error full-width mb-16" @click="handleDeleteSlide">删除课件</c-button>
   </tab-side-component>
 </template>
@@ -149,6 +152,12 @@ export default {
   }
 }
 
+.tip {
+  font-weight: bold;
+  color: $theme-orange;
+  font-size: 12px;
+}
+
 @media (max-width: 600px) {
   .file-uploader {
     font-size: 14px;

+ 15 - 7
src/views/teacher/tabs/TeacherPersonal.vue

@@ -6,7 +6,7 @@
     <c-button class="warning full-width mb-32" @click="handleModifyName">确认修改姓名</c-button>
     <div class="sub-title">修改密码</div>
     <div class="verified-code-wrapper">
-      <c-input placeholder="邮箱验证码" type="number"></c-input>
+      <c-input placeholder="邮箱验证码" type="number" v-model="this.code"></c-input>
       <c-button class="secondary" :disabled="typeof this.emailButtonContent === typeof 0"
                 @click="handleSendEmailCode">{{ emailButtonContent }}
       </c-button>
@@ -29,26 +29,33 @@ export default {
   components: { CInput, CButton },
   data () {
     return {
-      email: '',
       emailButtonContent: '发送验证码',
       newName: '',
       password: '',
+      code: '',
       passwordEnsure: ''
     }
   },
   computed: {
     ...mapState({
       userInfo: state => state.main.userInfo
-    })
+    }),
+    email () {
+      return this.userInfo.email
+    }
   },
   methods: {
     handleSendEmailCode () {
-      this.$showMessage({ message: '正在尝试发送验证码', type: 'info' })
+      this.$setInfoMessage('正在尝试发送验证码')
       if (this.email.match(/.+@(nju.edu.cn|smail.nju.edu.cn)/)) {
         this.setEmailCodeCd()
-        sendEmailCode({ email: this.email })
+        sendEmailCode({ email: this.email }).then(res => {
+          this.$setSuccessMessage('邮箱验证码发送成功')
+        }).catch(err => {
+          this.$setErrorMessage(err.response.data.msg)
+        })
       } else {
-        this.$showMessage({ message: '本邮箱不符合南京大学校邮格式', type: 'error' })
+        this.$setErrorMessage('本邮箱不符合南京大学校邮格式')
       }
     },
     setEmailCodeCd () {
@@ -64,6 +71,7 @@ export default {
     },
     handleLogout () {
       logout()
+      this.$setSuccessMessage('您已退出登录')
       this.$router.push({ name: 'login' })
     },
     handleModifyPassword () {
@@ -73,7 +81,7 @@ export default {
       }
 
       resetPasswordByEmail({ email: this.email, code: this.code, password: this.password }).then(res => {
-        this.$setInfoMessage('密码修改成功')
+        this.$setSuccessMessage('密码修改成功')
       }).catch(err => {
         this.$setErrorMessage(err.response.data.msg)
       })