Просмотр исходного кода

Merge remote-tracking branch 'origin/refactor-admin' into refactor-evaluation

# Conflicts:
#	src/apis/index.ts
白白 1 год назад
Родитель
Сommit
3864a2353f

+ 21 - 0
src/apis/auth.ts

@@ -0,0 +1,21 @@
+import axiosInstance from "@/apis/axios.config";
+import { type ILoginUserFrom, type ILoginEmailFrom } from "@/types/user";
+
+const AUTH_PREFIX = "/api/auth"
+
+const authApis = {
+    login: (userForm: ILoginUserFrom) => {
+        return axiosInstance.post(`${AUTH_PREFIX}/login`, {
+            ...userForm
+        })
+    },
+
+    loginByEmail: (userForm: ILoginEmailFrom) => {
+        return axiosInstance.post(`${AUTH_PREFIX}/loginByEmail`, {
+            ...userForm
+        })
+    },
+
+}
+
+export default authApis

+ 2 - 2
src/apis/axios.config.ts

@@ -1,9 +1,9 @@
 import axios from "axios";
 
 // const BASEURL = 'http://47.111.23.171:8081'
-// const BASEURL = 'http://localhost:8080'
+const BASEURL = 'http://localhost:8080'
 // const BASEURL = 'http://8.130.126.86:8080'
-const BASEURL = 'https://air.seec.seecoder.cn/'
+// const BASEURL = 'https://air.seec.seecoder.cn/'
 
 const axiosInstance = axios.create({
     baseURL: BASEURL,

+ 29 - 0
src/apis/email.ts

@@ -0,0 +1,29 @@
+import axiosInstance from "@/apis/axios.config";
+
+const EMAIL_PREFIX = "/api/email"
+
+const emailApis = {
+    sendRegisterVerifyCode: (officialEmail:string) => {
+        return axiosInstance.get(`${EMAIL_PREFIX}/registerVerifyCode`, {
+            params: {
+                officialEmail
+            }
+        })
+    },
+    sendLoginVerifyCode: (officialEmail:string) => {
+        return axiosInstance.get(`${EMAIL_PREFIX}/loginVerifyCode`, {
+            params: {
+                officialEmail
+            }
+        })
+    },
+    sendPasswordChangeVerifyCode: (officialEmail:string) => {
+        return axiosInstance.get(`${EMAIL_PREFIX}/passwordChangeVerifyCode`, {
+            params: {
+                officialEmail
+            }
+        })
+    },
+}
+
+export default emailApis

+ 3 - 0
src/apis/index.ts

@@ -8,9 +8,11 @@ import assignmentApis from "@/apis/assignment";
 import courseApis from "@/apis/course";
 import fileApis from "@/apis/file";
 import evaluationApis from "@/apis/evaluation";
+import emailApis from "@/apis/email";
 
 
 const apis = {
+    ...authApis,
     ...userApis,
     ...testApis,
     ...editorApis,
@@ -21,6 +23,7 @@ const apis = {
     ...courseApis,
     ...fileApis,
     ...evaluationApis,
+    ...emailApis
 }
 
 // hook

+ 5 - 16
src/apis/user.ts

@@ -1,16 +1,9 @@
 import axiosInstance from "@/apis/axios.config";
-import { type ILoginUserFrom } from "@/types/user";
-import type {UserRegisterDTO, UserUpdateDTO} from "@/types/dto";
+import type {UserRegisterDTO, UserUpdateDTO, PasswordChangeDTO} from "@/types/dto";
 
 const USER_PREFIX = "/api/user"
 
 const userApis = {
-    login: (userForm: ILoginUserFrom) => {
-        return axiosInstance.post(`${USER_PREFIX}/login`, {
-            ...userForm
-        })
-    },
-
     findUserById: (id:number) => {
         return axiosInstance.get(`${USER_PREFIX}`, {
             params: {
@@ -19,14 +12,6 @@ const userApis = {
         })
     },
 
-    sendVerifyCode: (officialEmail:string) => {
-        return axiosInstance.get(`${USER_PREFIX}/verifyCode`, {
-            params: {
-                officialEmail
-            }
-        })
-    },
-
     register: (userRegisterDTO: UserRegisterDTO) => {
         return axiosInstance.post(`${USER_PREFIX}/register`, userRegisterDTO)
     },
@@ -37,6 +22,10 @@ const userApis = {
                 id: id,
             }
         })
+    },
+
+    updatePassword: (passwordChangeDTO: PasswordChangeDTO) => {
+        return axiosInstance.post(`${USER_PREFIX}/updatePassword`, passwordChangeDTO)
     }
 
 }

+ 8 - 9
src/layout/Header/Header.vue

@@ -21,30 +21,28 @@
     </div>
     <!-- 用户头像和昵称 -->
     <div class="header-userName">
-      <el-dropdown @command="handleCommand">
+      <el-dropdown class="el-dropdown" @command="handleCommand">
         <div class="user-info">
-          <!-- 如果是登录状态 -->
-          <template v-if="store.token !== ''">
-            <!-- 用户有头像 -->
+          <!-- <template v-if="store.token !== ''">
             <template v-if="store.user.userAvatar !== ''">
               <el-avatar :size="32" :shape="'circle'" :src="store.user.userAvatar" />
             </template>
-            <!-- 用户没有头像 -->
             <template v-else>
               <el-avatar :size="32" :shape="'circle'">{{ store.user.name.charAt(0) }}</el-avatar>
             </template>
             <div class="nickName">{{ store.user.name }}</div>
           </template>
-          <!-- 如果是未登录状态 -->
-          <template v-else>
+          <template v-else>            
             <el-avatar :size="32" :shape="'circle'">U</el-avatar>
             <div class="nickName">user</div>
-          </template>
+          </template> -->
+          <el-icon><User /></el-icon>
+          <div class="nickName">{{ store.user.userAvatar !== '' ? store.user.name : "user"}}</div>
         </div>
         <template #dropdown>
           <el-dropdown-menu>
-            <el-dropdown-item command="logout">退出登录</el-dropdown-item>
             <el-dropdown-item command="profile">个人资料</el-dropdown-item>
+            <el-dropdown-item command="logout">退出登录</el-dropdown-item>
           </el-dropdown-menu>
         </template>
       </el-dropdown>
@@ -53,6 +51,7 @@
 </template>
 
 <script setup lang="ts">
+import {User} from "@element-plus/icons-vue";
 import router from "../../router/index.js";
 import { onMounted, ref } from "vue";
 import './index.scss';

+ 2 - 0
src/layout/Header/index.scss

@@ -59,10 +59,12 @@
     .user-info {
       display: flex;
       align-items: center;
+      outline: unset;
     }
 
     .nickName {
       margin-left: 10px;
     }
   }
+
 }

+ 6 - 2
src/router/index.ts

@@ -29,7 +29,11 @@ const router = createRouter({
       name: 'register', 
       component: () => import('../views/LoginPage/RegisterPage.vue')
     },
-
+    {
+      path: '/passwordChange',
+      name: 'passwordChange',
+      component: () => import('../views/LoginPage/PasswordChangePage.vue')
+    },
     // 用户相关界面
     {
       path: '/home',
@@ -159,7 +163,7 @@ const router = createRouter({
 router.beforeEach((to) => {
   const store = useStore()
   // 判断是否是登录页面,如果是则直接放行
-  if (to.path === '/login' || to.path === '/register' || to.path === '/admin/login') {
+  if (to.path === '/login' || to.path === '/register' || to.path === '/passwordChange'|| to.path === '/admin/login') {
     return true;
   }
   // 判断是否登录,未登录需要登录

+ 5 - 0
src/types/dto.ts

@@ -64,3 +64,8 @@ export interface UserUpdateDTO {
     contentEmail?: string;
 }
 
+export interface PasswordChangeDTO {
+    password: string; 
+    officialEmail: string; 
+    verifyCode: string; 
+}

+ 5 - 0
src/types/user.ts

@@ -17,3 +17,8 @@ export interface ILoginUserFrom {
     officialNumber: string;
     password: string;
 }
+
+export interface ILoginEmailFrom {
+    officialEmail: string;
+    verifyCode: string;
+}

+ 29 - 0
src/views/LoginPage/PasswordChangePage.vue

@@ -0,0 +1,29 @@
+<template>
+  <div class="login-container">
+    <div class="container-box">
+      <div class="login-logo">
+        <div class="logo-image">
+          <img src="../LoginPage/seecoderLogo.png" alt="logo" width="400vh">
+        </div>
+      </div>
+      <div class="login-box">
+        <div class="register-box-content">
+          <PasswordChange/>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import "./index.scss"
+import PasswordChange from "@/views/LoginPage/component/PasswordChange.vue";
+    
+  </script>
+  <script lang="ts">
+
+  export default {
+    name: "PasswordChangePage"
+  }
+  </script>
+  

+ 72 - 28
src/views/LoginPage/component/Login.vue

@@ -27,12 +27,8 @@
                 v-model="data.code"
                 placeholder="请输入验证码"
             >
-              <template #append>
-                <el-button
-                    :class="isStart? '' : 'code-button-active'"
-                    @click="getCode"
-                    :disabled="isStart"
-                >
+              <template #suffix>
+                <el-button type="text" @click="sendLoginVerifyCode" :disabled="isStart">
                   {{ isStart ? countdown : '获取验证码' }}
                 </el-button>
               </template>
@@ -46,24 +42,24 @@
             />
           </template>
         </el-form-item>
-        <div class="forget">忘记密码?</div>
+        <div class="forget" @click="toPasswordChange">忘记密码?</div>
         <el-form-item>
-          <el-button class="login-button" @click="handleLogin">用户登录</el-button>
+          <el-button class="login-button" @click="handleLogin(form)">用户登录</el-button>
         </el-form-item>
-        <div class="sign" @click="onClickSign">去注册?</div>
+        <div class="sign" @click="onClickSign">注册账号</div>
       </el-form>
     </div>
   </div>
 </template>
 
 <script lang="ts" setup>
-  import { ref, reactive,defineProps } from 'vue';
+  import { ref, reactive, defineProps } from 'vue';
   import { useStore } from '@/store';
   import useApis from '@/apis';
   import router from "@/router";
   import { useCountdown } from '@/hooks/useCountdown';
   import "./index.scss"
-  import { ElMessage } from 'element-plus';
+  import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
 
 
   interface LoginProps {
@@ -100,6 +96,7 @@
     code: '',
   });
 
+  const form = ref<FormInstance>()
   const rules = reactive({
     officialNumber: [
       { required: true, message: props.isCode ? '邮箱不能为空' : '账号不能为空', trigger: 'blur' },
@@ -108,16 +105,44 @@
       { required: true, message: props.isCode ? '验证码不能为空' : '密码不能为空', trigger: 'blur' },
     ],
   });
-
-  // 这个功能实现了吗? 
-  const getCode = () => {
+ 
+  const sendLoginVerifyCode = () => {
     start();
     props.onCode?.();
-    // console.log('获取验证码');
+    
+    apis.sendLoginVerifyCode(data.officialNumber)
+    .then((res: any) => {
+      if (res.data.code === 200) {
+        ElMessage({message: '验证码发送成功,请前往您的邮箱查看', type: 'success'})
+      } else {
+        ElMessage({message: '验证码发送失败',type: 'error'})
+        console.log(res.data.msg)
+      }
+    })
+    .catch((err: any) => {
+      ElMessage({message: '验证码发送失败',type: 'error'})
+      console.log(err)
+    })
+    .finally(() => {});
   };
 
   let assignmentId = ref(1)
-  const handleLogin = () => {
+  const handleLogin = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    await formEl.validate((valid, fields) => {
+      if (valid) {
+        if(!props.isCode) {
+          login()
+        } else {
+          loginByEmail()
+        }
+      } else {
+        ElMessage({message: '请将信息填写完整',type: 'warning'})
+      }
+    })
+  };
+
+  const login = () => {
     apis.login({
       officialNumber: data.officialNumber,
       password: data.password
@@ -127,27 +152,46 @@
         store.setUser(data);
         store.setToken(data?.token)
         localStorage.setItem('Authorization', data?.token);
-        localStorage.setItem('User', data);
-        // router.push("/")
+        localStorage.setItem('User', JSON.stringify(data));
         router.push(`/home`)
       } else if(_res.data.code === 400){
-        ElMessage({
-          message: '用户名或密码错误',
-          type: 'error',
-        })
+        ElMessage({message: '用户名或密码错误', type: 'error'})
       }
     }).catch((_err:any) => {
-      ElMessage({
-        message: '网络错误',
-        type: 'error',
-      })
+      ElMessage({message: '网络错误', type: 'error'})
       console.log("登陆失败", _err)
-    })
-  };
+    })    
+  }
+
+  const loginByEmail = () => {
+    apis.loginByEmail({
+      officialEmail: data.officialNumber,
+      verifyCode: data.code
+    }).then((_res:any) => {
+      const data = _res.data.data
+      if (_res.data.code === 200) {
+        store.setUser(data);
+        store.setToken(data?.token)
+        localStorage.setItem('Authorization', data?.token);
+        localStorage.setItem('User', JSON.stringify(data));
+        router.push(`/home`)
+      } else if(_res.data.code === 400){
+        ElMessage({message: '邮箱或验证码错误' + _res.data.msg, type: 'error'})
+      }
+    }).catch((_err:any) => {
+      ElMessage({message: '网络错误', type: 'error'})
+      console.log("登陆失败", _err)
+    })    
+  } 
+
   const onClickSign = () => {
     router.push('/register');
   };
 
+  const toPasswordChange = () => {
+    router.push('/passwordChange');
+  }
+
 </script>
 
 <script lang="ts">

+ 163 - 0
src/views/LoginPage/component/PasswordChange.vue

@@ -0,0 +1,163 @@
+<template>
+    <div class="login-style">
+      <div class="box-vessel">
+  
+        <div class="vessel-title">重置密码</div>
+
+        <el-form label-position="right" label-width="0" :rules="rules" :model="passwordChangeDTO" ref="formRef" :size="formSize">
+
+          <!--邮箱-->
+          <el-form-item prop="officialEmail">
+            <el-input v-model="passwordChangeDTO.officialEmail" placeholder="请输入邮箱"></el-input>
+          </el-form-item>
+
+          <!--密码-->
+          <el-form-item prop="password1">
+            <el-input v-model="passwordChangeDTO.password1" placeholder="请输入新密码" show-password></el-input>
+          </el-form-item>
+
+          <!--再次输入密码-->
+          <el-form-item prop="password2">
+            <el-input v-model="passwordChangeDTO.password2" placeholder="请再次输入密码" show-password></el-input>
+          </el-form-item>
+
+          <!--输入验证码-->
+            <el-form-item prop="verifyCode">
+              <el-input v-model="passwordChangeDTO.verifyCode" placeholder="输入验证码">
+                <template #suffix>
+                  <el-button type="text" @click="sendPasswordChangeVerifyCode" :disabled="isStart">
+                    {{ isStart ? countdown : '获取验证码' }}
+                  </el-button>
+                </template>
+              </el-input>
+            </el-form-item>
+  
+          <div style="display: flex; justify-content: center; align-items: center;">
+            <el-button @click="changePassword(formRef)" class="login-button">重置密码</el-button>
+          </div>
+  
+          <!-- 返回登录页面,下划线灰色小字 -->
+          <div style="display: flex; justify-content: center; align-items: center; margin-top: 10px;">
+            <router-link to="/login" style="color: #8e8e8e; text-decoration: underline;">记得密码? 前往登录</router-link>
+          </div>
+  
+        </el-form>
+  
+      </div>
+  
+    </div>
+  
+  
+  
+  </template>
+  
+  <script lang="ts" setup>
+  import "./index.scss";
+  import { reactive, ref } from "vue";
+  import useApis from "@/apis";
+  import { type ComponentSize, ElMessage, type FormInstance, type FormRules } from "element-plus";
+  import router from "@/router";
+  import { useCountdown } from '@/hooks/useCountdown';
+
+  const { start, isStart, countdown } = useCountdown();
+  
+  const apis = useApis()
+
+  type passwordChangeDTO = {
+    officialEmail: "",
+    password1: "",
+    password2: "",
+    verifyCode: ""
+  }
+  let passwordChangeDTO = reactive<passwordChangeDTO>({} as passwordChangeDTO)
+  
+
+  const formSize = ref<ComponentSize>('default')
+  const formRef = ref<FormInstance>()
+  const rules = reactive<FormRules<passwordChangeDTO>>({
+    officialEmail: [
+      {
+        required: true,
+        message: "邮箱不能为空",
+        trigger: 'blur'
+      }
+    ],
+    password1: [
+      {
+        required: true,
+        message: "请输入密码",
+        trigger: 'blur'
+      }
+    ],
+    password2: [
+      {
+        required: true,
+        message: "请再次输入密码",
+        trigger: 'blur'
+      }
+    ],
+    verifyCode: [
+      {
+        required: true,
+        message: "验证码不能为空",
+        trigger: 'blur'
+      }
+    ]
+  })
+
+
+  
+  const changePassword = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    await formEl.validate((valid, fields) => {
+      if (valid) {
+        if(passwordChangeDTO.password1 != passwordChangeDTO.password2) {
+          ElMessage({message: '两次输入密码不一致', type: 'error'})
+          return
+        } 
+        let data = {
+          officialEmail: passwordChangeDTO.officialEmail,
+          password: passwordChangeDTO.password1,
+          verifyCode: passwordChangeDTO.verifyCode
+        }
+        apis.updatePassword(data).then((res: any) => {
+          if (res.data.code === 200) {
+            ElMessage({message: '密码修改成功',type: 'success'})
+            router.push(`/login`);
+          } else {
+            ElMessage({message: '密码修改失败' + res.data.msg, type: 'error'})
+          }
+        }).catch((err: any) => {
+          ElMessage({message: '网络错误',type: 'error'})
+          console.log(err)
+        }).finally(() => {});
+      } else {
+        ElMessage({message: '请将信息填写完整',type: 'warning'})
+      }
+    })
+  }
+  
+  const sendPasswordChangeVerifyCode = () => {
+    start();
+    
+    apis.sendPasswordChangeVerifyCode(passwordChangeDTO.officialEmail)
+    .then((res: any) => {
+      if (res.data.code === 200) {
+        ElMessage({message: '验证码发送成功,请前往您的邮箱查看',type: 'success'})
+      } else {
+        ElMessage({message: '验证码发送失败' + res.data.msg, type: 'error'})
+      }
+    })
+    .catch((err: any) => {
+      ElMessage({message: '验证码发送失败',type: 'error'})
+      console.log(err)
+    })
+    .finally(() => {});
+  }
+  </script>
+  
+  <script lang="ts">
+  export default {
+    name: "PasswordChange"
+  }
+  </script>

+ 27 - 45
src/views/LoginPage/component/Register.vue

@@ -11,9 +11,10 @@
         欢迎注册 SEEAI 平台账号
       </div>
       <!--角色-->
-      <el-form :rules="userRules" :model="userRegisterDTO" ref="formRef" :size="formSize">
-        <el-form-item label="账号角色" prop="role">
-          <el-select v-model="userRegisterDTO.role" placeholder="请选择注册账号的角色,注意角色选定无法修改">
+      <el-form label-position="right" label-width="65px" :rules="userRules" :model="userRegisterDTO" ref="formRef" :size="formSize">
+
+        <el-form-item label="角色" prop="role">
+          <el-select v-model="userRegisterDTO.role" placeholder="请选择注册账号的角色">
             <el-option v-for="item in roleOptions" :key="item.value" :label="item.label" :value="item.value"
               :disabled="item.disabled" />
           </el-select>
@@ -26,7 +27,7 @@
 
         <!--学号-->
         <el-form-item label="学号" prop="officialNumber">
-          <el-input v-model="userRegisterDTO.officialNumber" placeholder="请输入您的南大学号或工号"></el-input>
+          <el-input v-model="userRegisterDTO.officialNumber" placeholder="请输入南大学号或工号"></el-input>
         </el-form-item>
 
         <!--邮箱-->
@@ -40,13 +41,15 @@
         </el-form-item>
 
         <!--输入验证码-->
-        <!--TODO:发送验证码按钮和输入框在一起,参考登录界面-->
-        <div class="verify-code">
-          <el-form-item label="验证码" prop="verifyCode" class="form-item">
-            <el-input v-model="userRegisterDTO.verifyCode" placeholder="输入验证码"></el-input>
+          <el-form-item label="验证码" prop="verifyCode">
+            <el-input v-model="userRegisterDTO.verifyCode" placeholder="输入验证码">
+              <template #suffix>
+                <el-button type="text" @click="sendRegisterVerifyCode" :disabled="isStart">
+                  {{ isStart ? countdown : '获取验证码' }}
+                </el-button>
+              </template>
+            </el-input>
           </el-form-item>
-          <el-button @click="sendVerifyCode" class="button">发送验证码</el-button>
-        </div>
 
         <div style="display: flex; justify-content: center; align-items: center;">
           <el-button @click="register(formRef)" class="login-button">注册</el-button>
@@ -75,7 +78,9 @@ import useApis from "@/apis";
 import { Role } from "@/types/enums";
 import { type ComponentSize, ElMessage, type FormInstance, type FormRules } from "element-plus";
 import router from "@/router";
+import { useCountdown } from '@/hooks/useCountdown';
 
+const { start, isStart, countdown } = useCountdown();
 const apis = useApis()
 let userRegisterDTO = reactive<UserRegisterDTO>({} as UserRegisterDTO)
 
@@ -144,65 +149,42 @@ const register = async (formEl: FormInstance | undefined) => {
   if (!formEl) return
   await formEl.validate((valid, fields) => {
     if (valid) {
-      console.log("合法输入")
       apis.register(userRegisterDTO)
         .then((res: any) => {
           if (res.data.code === 200) {
-            ElMessage({
-              message: '注册成功',
-              type: 'success',
-            })
+            ElMessage({message: '注册成功', type: 'success'})
             router.push(`/login`)
           } else {
-            ElMessage({
-              message: '注册失败: ' + res.data.msg,
-              type: 'error',
-            })
+            ElMessage({message: '注册失败: ' + res.data.msg, type: 'error'})
           }
         })
         .catch((err: any) => {
           console.error(err);
-          ElMessage({
-            message: '注册失败',
-            type: 'error',
-          })
+          ElMessage({message: '注册失败',type: 'error'})
         })
-        .finally(() => {
-        });
+        .finally(() => {});
     } else {
-      ElMessage({
-        message: '请将信息填写完整',
-        type: 'warning',
-      })
+      ElMessage({message: '请将信息填写完整', type: 'warning'})
     }
   })
 
 }
 
-const sendVerifyCode = () => {
-  apis.sendVerifyCode(userRegisterDTO.officialEmail)
+const sendRegisterVerifyCode = () => {
+  start()
+  apis.sendRegisterVerifyCode(userRegisterDTO.officialEmail)
     .then((res: any) => {
       if (res.data.code === 200) {
-        ElMessage({
-        message: '验证码发送成功,请前往您的邮箱查看',
-          type: 'success',
-        })
+        ElMessage({message: '验证码发送成功,请前往您的邮箱查看', type: 'success'})
       } else {
-        ElMessage({
-          message: '验证码发送失败: ' + res.data.msg,
-          type: 'error',
-        })
+        ElMessage({message: '验证码发送失败' + res.data.msg, type: 'error'})
       }
     })
     .catch((err: any) => {
-      ElMessage({
-        message: '验证码发送失败',
-        type: 'error',
-      })
+      ElMessage({message: '验证码发送失败', type: 'error'})
       console.log(err)
     })
-    .finally(() => {
-    });
+    .finally(() => {});
 }
 </script>
 

+ 0 - 1
src/views/LoginPage/component/index.scss

@@ -14,7 +14,6 @@
 
       &:hover {
         color: #fff;
-        //background-color: rgba(var(--main-color), 0.9);
         background-color: rgba(#1677ff, 0.5);
       }
     }

+ 4 - 4
src/views/LoginPage/index.scss

@@ -45,11 +45,11 @@
       position: relative;
       .register-box-content {
         position: absolute;
-        width: 450px;
+        width: 400px;
         @extend .position-center-center;
         border: solid 1px var(--boder-color);
         border-radius: 7px;
-        height: 450px;
+        min-height: 350px;
         display: flex;
         flex-direction: column;
 
@@ -58,11 +58,11 @@
 
       .box-content {
         position: absolute;
-        width: 350px;
+        width: 400px;
         @extend .position-center-center;
         border: solid 1px var(--boder-color);
         border-radius: 7px;
-        height: 380px;
+        min-height: 350px;
         display: flex;
         flex-direction: column;
 

+ 157 - 104
src/views/Profile.vue

@@ -1,68 +1,60 @@
 <template>
   <div class="personal-center">
     <el-card class="box-card">
-<!--      <div slot="header" class="clearfix">-->
-<!--        <span style="font-size: 25px; font-weight: bold">个人中心</span>-->
-<!--      </div>-->
-<!--      <el-row :gutter="20">-->
-<!--        <el-col :span="12" class="main-info">-->
-<!--          <el-avatar :size="100" :src="user.userAvatar" />-->
-<!--          <div>{{ user.name }}</div>-->
-<!--          <div>{{ user.officialNumber }}</div>-->
-<!--          <div>{{ user.officialEmail }}</div>-->
-<!--        </el-col>-->
-<!--        <el-col :span="12">-->
-<!--          <el-descriptions title="账户信息" :column="1">-->
-<!--            <template #extra>-->
-<!--              <el-button type="primary" @click="visible=true">修改信息</el-button>-->
-<!--            </template>-->
-<!--            <el-descriptions-item label="英文名">{{ user.englishName }}</el-descriptions-item>-->
-<!--            <el-descriptions-item label="性别">{{ user.gender == 'MAN' ? '男': '女' }}</el-descriptions-item>-->
-<!--            <el-descriptions-item label="注册时间">{{ user.createTime }}</el-descriptions-item>-->
-<!--            <el-descriptions-item label="手机号">{{ user.phone }}</el-descriptions-item>-->
-<!--            <el-descriptions-item label="生日">{{ user.birthday }}</el-descriptions-item>-->
-<!--          </el-descriptions>-->
-<!--        </el-col>-->
-<!--      </el-row>-->
-      <el-descriptions
-          title="个人中心"
-          border
-          :column="3">
+
+      <el-descriptions title="个人中心" border :column="3">
           <template #extra>
-            <el-button type="primary" @click="visible=true">修改信息</el-button>
-          </template>
-        <el-descriptions-item
-            :rowspan="2"
-            :width="140"
-            label="Photo"
-            align="center"
-        >
-          <el-image
-              style="width: 100px; height: 100px"
-              :src="user.userAvatar"
-          />
-        </el-descriptions-item>
-        <el-descriptions-item label="Name">{{ user.name }}</el-descriptions-item>
-        <el-descriptions-item label="EnglishName">{{ user.englishName }}</el-descriptions-item>
-        <el-descriptions-item label="Gender">{{ user.gender == 'MAN' ? 'man': 'woman' }}</el-descriptions-item>
-        <el-descriptions-item label="Role">
-          <template v-if="user.role == 'STUDENT'">
-            <el-tag size="small" >Student</el-tag>
-          </template>
-          <template v-else>
-            <el-tag size="small" type="error">Teacher</el-tag>
+            <el-button type="primary" @click="passwordChangeVisible=true">修改密码</el-button>
+            <!-- <el-button type="primary" @click="infoChangeVisible=true">修改信息</el-button> -->
           </template>
+        <!-- <el-descriptions-item :rowspan="3" :width="120" label="头像">
+          <el-avatar align="center" style="width: 100px; height: 100px" :src="user.userAvatar"/>
+        </el-descriptions-item> -->
+        <el-descriptions-item label="姓名">{{ user.name }}</el-descriptions-item>
+        <el-descriptions-item label="角色">
+          {{ user.role == 'STUDENT' ? '学生' : '教师'}}
+          <!-- <el-tag size="small">{{ user.role == 'STUDENT' ? '学生' : '教师'}}</el-tag> -->
         </el-descriptions-item>
-        <el-descriptions-item label="Birthday">{{ user.birthday }}</el-descriptions-item>
-        <el-descriptions-item label="Phone">{{ user.phone }}</el-descriptions-item>
-        <el-descriptions-item label="OfficialNumber">{{ user.officialNumber }}</el-descriptions-item>
-        <el-descriptions-item label="contentEmail">{{ user.contentEmail }}</el-descriptions-item>
-        <el-descriptions-item label="OfficialEmail">{{ user.officialEmail }}</el-descriptions-item>
+        <el-descriptions-item label="账号">{{ user.officialNumber }}</el-descriptions-item>
+        <el-descriptions-item label="邮箱">{{ user.officialEmail }}</el-descriptions-item>
       </el-descriptions>
     </el-card>
   </div>
 
-  <el-dialog v-model="visible" title="编辑">
+  <el-dialog v-model="passwordChangeVisible" title="修改密码">
+    <el-form :rules="rules" :model="passwordChangeDTO" label-width="auto" ref="formRef">
+      <!--邮箱-->
+      <!-- <el-form-item prop="officialEmail">
+        <el-input v-model="passwordChangeDTO.officialEmail" placeholder="请输入邮箱"></el-input>
+      </el-form-item> -->
+      <!--密码-->
+      <el-form-item prop="password1">
+        <el-input v-model="passwordChangeDTO.password1" placeholder="请输入新密码" show-password></el-input>
+      </el-form-item>
+      <!--再次输入密码-->
+      <el-form-item prop="password2">
+        <el-input v-model="passwordChangeDTO.password2" placeholder="请再次输入密码" show-password></el-input>
+      </el-form-item>
+      <!--输入验证码-->
+      <el-form-item prop="verifyCode">
+        <el-input v-model="passwordChangeDTO.verifyCode" placeholder="向邮箱发生验证码,输入验证码">
+          <template #suffix>
+            <el-button type="text" @click="sendPasswordChangeVerifyCode" :disabled="isStart">
+              {{ isStart ? countdown : '获取验证码' }}
+            </el-button>
+          </template>
+        </el-input>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="passwordChangeVisible = false">取消</el-button>
+        <el-button type="primary" @click="changePassword(formRef)">确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
+
+  <el-dialog v-model="infoChangeVisible" title="编辑">
     <el-form :model="userUpdateDTO" label-width="auto" ref="formRef">
       <el-form-item label="头像">
         <el-upload
@@ -91,37 +83,12 @@
         </el-upload>
       </el-form-item>
       <el-form-item label="姓名" >
-        <el-input v-model="user.name" placeholder="请输入名称" disabled></el-input>
-      </el-form-item>
-      <el-form-item label="学号" >
-        <el-input v-model="user.officialNumber" placeholder="请输入名称" disabled></el-input>
-      </el-form-item>
-      <el-form-item label="邮箱" >
-        <el-input v-model="user.officialEmail" placeholder="请输入名称" disabled></el-input>
-      </el-form-item>
-      <el-form-item label="联系邮箱" >
-        <el-input v-model="userUpdateDTO.contentEmail" placeholder="请输入名称"></el-input>
-      </el-form-item>
-      <el-form-item label="英文名" >
-        <el-input v-model="userUpdateDTO.englishName" placeholder="请输入名称"></el-input>
-      </el-form-item>
-      <el-form-item label="性别" >
-        <el-radio-group v-model="userUpdateDTO.gender" placeholder="请输入名称">
-          <el-radio value="MAN">男</el-radio>
-          <el-radio value="WOMAN">女</el-radio>
-        </el-radio-group>
-      </el-form-item>
-      <el-form-item label="手机号" >
-        <el-input v-model="userUpdateDTO.phone" placeholder="请输入名称"></el-input>
-      </el-form-item>
-      <el-form-item label="生日" >
-        <el-date-picker v-model="userUpdateDTO.birthday" type="date" placeholder="生日" style="width: 180px"/>
+        <el-input v-model="user.name" placeholder="请输入名称"></el-input>
       </el-form-item>
     </el-form>
     <template #footer>
       <div class="dialog-footer">
-        <el-button @click="visible = false">取消</el-button>
-<!--        <el-button type="primary" @click="submitForm(formRef)">确定</el-button>-->
+        <el-button @click="infoChangeVisible = false">取消</el-button>
         <el-button type="primary" @click="handleSubmit">确定</el-button>
       </div>
     </template>
@@ -140,7 +107,9 @@
     ElDescriptionsItem,
     ElTable,
     ElTableColumn,
-    ElMessage, type UploadUserFile, type UploadProps, type UploadFile, type UploadFiles, type FormInstance
+    ElMessage, 
+    type FormRules,
+    type UploadUserFile, type UploadProps, type UploadFile, type UploadFiles, type FormInstance
   } from 'element-plus';
     import useApis from "@/apis";
     import {useStore} from "@/store";
@@ -148,13 +117,56 @@
   import {convertIUserToUserUpdateDTO} from "@/utils/convertUtil";
   import type {UserUpdateDTO} from "@/types/dto";
   import {useFormatDate} from "@/hooks/useFormatDate";
+  import { useCountdown } from '@/hooks/useCountdown';
 
   const apis = useApis()
   const store = useStore()
+  const { start, isStart, countdown } = useCountdown();
+
+  const rules = reactive<FormRules<passwordChangeDTO>>({
+    officialEmail: [
+      {
+        required: true,
+        message: "邮箱不能为空",
+        trigger: 'blur'
+      }
+    ],
+    password1: [
+      {
+        required: true,
+        message: "请输入密码",
+        trigger: 'blur'
+      }
+    ],
+    password2: [
+      {
+        required: true,
+        message: "请再次输入密码",
+        trigger: 'blur'
+      }
+    ],
+    verifyCode: [
+      {
+        required: true,
+        message: "验证码不能为空",
+        trigger: 'blur'
+      }
+    ]
+  })
+
+  const formRef = ref<FormInstance>()
+  type passwordChangeDTO = {
+    officialEmail: "",
+    password1: "",
+    password2: "",
+    verifyCode: ""
+  }
+  let passwordChangeDTO = reactive<passwordChangeDTO>({} as passwordChangeDTO)
 
   const user = reactive<IUser>({} as IUser)
   let userUpdateDTO = reactive<UserUpdateDTO>({} as UserUpdateDTO)
-  let visible = ref(false)
+  let infoChangeVisible = ref(false)
+  let passwordChangeVisible = ref(false)
   const uploadRef = ref()
   // 用于编辑头像展示预览图片
   const fileList = ref<UploadUserFile[]>([])
@@ -163,44 +175,32 @@
     apis.findUserById(store.user.id)
         .then((res:any) => {
           let data = res.data.data.records[0]
-          data = processData(data)
           Object.assign(user, data)
           Object.assign(userUpdateDTO, convertIUserToUserUpdateDTO(user))
+          if(user.userAvatar == null) {
+            user.userAvatar = "https://cube.elemecdn.com/e/fd/0fc7d20532fdaf769a25683617711png.png"
+          }
         })
         .catch((err:any) => {
-          ElMessage({
-            message: "获取用户信息失败:"+err,
-            type: "error"
-          })
+          ElMessage({message: "获取用户信息失败:"+err, type: "error"})
         })
   }
 
-  // 预处理个人信息的注册时间展示
-  const processData = (data:IUser) => {
-    data.createTime = useFormatDate().formatDate(data.createTime)
-    data.birthday = useFormatDate().formatBirthday(data.birthday)
-    return data
-  }
 
   const handleSubmit = () => {
     console.log(userUpdateDTO)
+    return
     apis.updateUser(user.id, userUpdateDTO)
         .then((res:any) => {
-          ElMessage({
-            message: "个人信息修改成功",
-            type: "success"
-          })
+          ElMessage({message: "个人信息修改成功", type: "success"})
           fetchUserData()
         })
         .catch((err:any) => {
-          ElMessage({
-            message: "个人信息修改失败",
-            type: "error"
-          })
+          ElMessage({message: "个人信息修改失败", type: "error"})
           console.log(err)
         })
         .finally(() => {
-          visible.value = false
+          infoChangeVisible.value = false
         })
   }
 
@@ -250,6 +250,59 @@
   onMounted(() => {
     fetchUserData()
   })
+
+  const sendPasswordChangeVerifyCode = () => {
+    passwordChangeDTO.officialEmail = JSON.parse(localStorage.getItem("User")).officialEmail
+    start();
+    console.log(passwordChangeDTO)
+    apis.sendPasswordChangeVerifyCode(passwordChangeDTO.officialEmail)
+    .then((res: any) => {
+      if (res.data.code === 200) {
+        ElMessage({message: '验证码发送成功,请前往您的邮箱查看',type: 'success'})
+      } else {
+        ElMessage({message: '验证码发送失败' + res.data.msg, type: 'error'})
+      }
+    })
+    .catch((err: any) => {
+      ElMessage({message: '验证码发送失败',type: 'error'})
+      console.log(err)
+    })
+    .finally(() => {});
+  }
+
+  const changePassword = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    await formEl.validate((valid, fields) => {
+      if (valid) {
+        if(passwordChangeDTO.password1 != passwordChangeDTO.password2) {
+          ElMessage({message: '两次输入密码不一致', type: 'error'})
+          return
+        } 
+        let data = {
+          officialEmail: passwordChangeDTO.officialEmail,
+          password: passwordChangeDTO.password1,
+          verifyCode: passwordChangeDTO.verifyCode
+        }
+        apis.updatePassword(data).then((res: any) => {
+          if (res.data.code === 200) {
+            ElMessage({message: '密码修改成功',type: 'success'})
+            passwordChangeVisible.value = false
+            passwordChangeDTO.password1 = ''
+            passwordChangeDTO.password2 = ''
+            passwordChangeDTO.verifyCode = ''
+          } else {
+            ElMessage({message: '密码修改失败' + res.data.msg, type: 'error'})
+          }
+        }).catch((err: any) => {
+          ElMessage({message: '网络错误',type: 'error'})
+          console.log(err)
+        }).finally(() => {});
+      } else {
+        ElMessage({message: '请将信息填写完整',type: 'warning'})
+      }
+    })
+  }
+    
 </script>
 
 <style scoped>