|
@@ -0,0 +1,293 @@
|
|
|
+<template>
|
|
|
+ <el-form size="large" class="login-content-form" ref="ruleFormRef" :model="ruleForm" @submit.native.prevent label-width="100px">
|
|
|
+ <el-form-item class="login-animation1" label="旧密码" prop="username" :rules="[{ required: true, message: '请输入旧密码', trigger: 'blur' }]">
|
|
|
+ <el-input type="password" class="inputDeep" placeholder="请输入旧密码" show-password v-model="ruleForm.username" clearable @keyup.enter="onSignIn(ruleFormRef)" autocomplete="off">
|
|
|
+ <template #prefix>
|
|
|
+ <el-icon class="el-input__icon">
|
|
|
+ <ele-Unlock />
|
|
|
+ </el-icon>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item class="login-animation2" prop="password" label="新密码" :rules="[{required: true,validator: checkPassword,trigger: ['change','blur']}]">
|
|
|
+ <el-input class="inputDeep" clearable type="password" show-password placeholder="请输入新密码" v-model="ruleForm.password" @keyup.enter="onSignIn(ruleFormRef)"
|
|
|
+ autocomplete="off">
|
|
|
+ <template #prefix>
|
|
|
+ <el-icon class="el-input__icon">
|
|
|
+ <ele-Unlock />
|
|
|
+ </el-icon>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ <div class="intensity login-animation4">
|
|
|
+ <span class="psdText">密码强度:{{modes === 1 ? '低' : modes === 2 ? '中' : modes === 3 ? '高' : ''}}</span>
|
|
|
+ <span
|
|
|
+ class="line"
|
|
|
+ :class="modes === 1 ? 'low' : ''"
|
|
|
+ ></span>
|
|
|
+ <span
|
|
|
+ class="line"
|
|
|
+ :class="modes === 2 ? 'middle' : ''"
|
|
|
+ ></span>
|
|
|
+ <span
|
|
|
+ class="line"
|
|
|
+ :class="modes === 3 ? 'high' : ''"
|
|
|
+ ></span>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item prop="confirmPwd" label="确认密码" class="login-animation2" :rules="[{required: true,validator: checkConfirmPassword,trigger: 'blur'}]">
|
|
|
+ <el-input class="inputDeep" clearable type="password" show-password placeholder="请再次输入密码" v-model="ruleForm.confirmPwd" @keyup.enter="onSignIn(ruleFormRef)"
|
|
|
+ autocomplete="off">
|
|
|
+ <template #prefix>
|
|
|
+ <el-icon class="el-input__icon">
|
|
|
+ <ele-Unlock />
|
|
|
+ </el-icon>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item class="login-animation4">
|
|
|
+ <el-button type="primary" class="login-content-submit" round @click="onSignIn(ruleFormRef)" v-waves="'light'" :loading="loading.signIn">登录</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts">
|
|
|
+import { toRefs, reactive, defineComponent, computed, ref } from 'vue';
|
|
|
+import { useRoute, useRouter } from 'vue-router';
|
|
|
+import { ElMessage } from 'element-plus';
|
|
|
+import Cookies from 'js-cookie';
|
|
|
+import { storeToRefs } from 'pinia';
|
|
|
+import { useThemeConfig } from '/@/stores/themeConfig';
|
|
|
+import { initFrontEndControlRoutes } from '/@/router/frontEnd';
|
|
|
+import { initBackEndControlRoutes } from '/@/router/backEnd';
|
|
|
+import { Session, Local } from '/@/utils/storage';
|
|
|
+import { formatAxis } from '/@/utils/formatTime';
|
|
|
+import { NextLoading } from '/@/utils/loading';
|
|
|
+import Watermark from '/@/utils/wartermark';
|
|
|
+import type { FormInstance } from 'element-plus';
|
|
|
+import { signIn } from '/@/api/login';
|
|
|
+// 登录参数类型
|
|
|
+interface LoginState {
|
|
|
+ grant_type: string;
|
|
|
+ client_id:string;
|
|
|
+ client_secret:string;
|
|
|
+ username:string;
|
|
|
+ password:string;
|
|
|
+ confirmPwd:string
|
|
|
+}
|
|
|
+export default defineComponent({
|
|
|
+ name: 'changePwd',
|
|
|
+ setup() {
|
|
|
+ const storesThemeConfig = useThemeConfig();
|
|
|
+ const { themeConfig } = storeToRefs(storesThemeConfig);
|
|
|
+ const route = useRoute();
|
|
|
+ const router = useRouter();
|
|
|
+ const state = reactive({
|
|
|
+ ruleForm: {
|
|
|
+ username: '',
|
|
|
+ password: '',
|
|
|
+ confirmPwd:''
|
|
|
+ },
|
|
|
+ loading: {
|
|
|
+ signIn: false,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ const ruleFormRef = ref<FormInstance>()
|
|
|
+ // 时间获取
|
|
|
+ const currentTime = computed(() => {
|
|
|
+ return formatAxis(new Date());
|
|
|
+ });
|
|
|
+ // 存储布局配置
|
|
|
+ const setLocalThemeConfig = () => {
|
|
|
+ Local.remove('themeConfig');
|
|
|
+ Local.set('themeConfig', themeConfig.value);
|
|
|
+ };
|
|
|
+ // 登录
|
|
|
+ const onSignIn = async (formEl: FormInstance | undefined) => {
|
|
|
+ if (!formEl) return
|
|
|
+ await formEl.validate((valid, fields) => {
|
|
|
+ if (valid) {
|
|
|
+ state.loading.signIn = true;
|
|
|
+ let req:LoginState = Object.assign({grant_type:'password',client_id:'hotline_admin',client_secret:'8c6c0b2b-6fd8-401c-849c-95888f4248ed'},state.ruleForm);
|
|
|
+ signIn(req).then(async (res:any)=>{//登录
|
|
|
+ // 存储 token 到浏览器缓存
|
|
|
+ Session.set('token', res.access_token);
|
|
|
+ // 模拟数据,对接接口时,记得删除多余代码及对应依赖的引入。用于 `/src/stores/userInfo.ts` 中不同用户登录判断(模拟数据)
|
|
|
+ Cookies.set('userName', state.ruleForm.username);
|
|
|
+ if (!themeConfig.value.isRequestRoutes) {
|
|
|
+ // 前端控制路由,2、请注意执行顺序
|
|
|
+ await initFrontEndControlRoutes();
|
|
|
+ signInSuccess();
|
|
|
+ } else {
|
|
|
+ // 模拟后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
|
|
+ // 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
|
|
|
+ await initBackEndControlRoutes();
|
|
|
+ // 执行完 initBackEndControlRoutes,再执行 signInSuccess
|
|
|
+ signInSuccess();
|
|
|
+ }
|
|
|
+ }).catch(()=>{
|
|
|
+ state.loading.signIn = false;
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ console.log('error submit!', fields)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ };
|
|
|
+ // 登录成功后的跳转
|
|
|
+ const signInSuccess = () => {
|
|
|
+ // 初始化登录成功时间问候语
|
|
|
+ let currentTimeInfo = currentTime.value;
|
|
|
+ // 登录成功,跳到转首页
|
|
|
+ // 如果是复制粘贴的路径,非首页/登录页,那么登录成功后重定向到对应的路径中
|
|
|
+ if (route.query?.redirect) {
|
|
|
+ router.push({
|
|
|
+ path: <string>route.query?.redirect,
|
|
|
+ query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ router.push('/');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置水印
|
|
|
+ themeConfig.value.isWartermark = true;
|
|
|
+ themeConfig.value.wartermarkText = state.ruleForm.username;
|
|
|
+ Watermark.set(state.ruleForm.username)
|
|
|
+ setLocalThemeConfig();
|
|
|
+
|
|
|
+ // 登录成功提示
|
|
|
+ // 关闭 loading
|
|
|
+ state.loading.signIn = true;
|
|
|
+ const signInText = '欢迎回来!';
|
|
|
+ ElMessage.success(`${currentTimeInfo},${signInText}`);
|
|
|
+ NextLoading.start();
|
|
|
+ };
|
|
|
+ // 检查密码强度
|
|
|
+ let modes = ref<number>(0);
|
|
|
+ const checkPassword = (rule:any,value:string, callback:Function)=>{
|
|
|
+ if (!value) {
|
|
|
+ modes.value = 0;
|
|
|
+ return callback('新密码不能为空')
|
|
|
+ }
|
|
|
+ if (value.length < 8) {
|
|
|
+ modes.value = 0;
|
|
|
+ return callback('新密码不少于8位')
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ 最短8位, {6,}
|
|
|
+ 可以包含小写大母 [a-z] 和大写字母 [A-Z]
|
|
|
+ 可以包含数字 [0-9]
|
|
|
+ 可以包含下划线 [ _ ] 和减号 [ - ]
|
|
|
+ */
|
|
|
+ if (/^[\w_-]{6,}$/.test(state.ruleForm.password)) {
|
|
|
+ modes.value = 1;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ 最短8位, {8,}
|
|
|
+ 必须包含1个数字
|
|
|
+ 必须包含1个小写字母
|
|
|
+ 必须包含1个大写字母
|
|
|
+ 必须包含1个特殊字符
|
|
|
+ */
|
|
|
+ if (/^\S*(?=\S{8,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*.?])\S*$/.test(state.ruleForm.password)) modes.value = 2 ; //中等
|
|
|
+ /*
|
|
|
+ 最短8位, {8,}
|
|
|
+ 必须包含1个数字
|
|
|
+ 必须包含1个小写字母
|
|
|
+ 必须包含1个大写字母
|
|
|
+ 必须包含2个特殊字符
|
|
|
+ */
|
|
|
+ if (/^\S*(?=\S{8,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*.?]{2,})\S*$/.test(state.ruleForm.password)) modes.value = 3; //强密码
|
|
|
+ if(modes.value == 0 || modes.value == 1){
|
|
|
+ return callback('提示:密码不得少于8位数,且必须包含字母大小写和特殊字符') //弱密码
|
|
|
+ }
|
|
|
+ console.log(modes.value)
|
|
|
+ return callback()
|
|
|
+ }
|
|
|
+ // 检查输入密码是否一致
|
|
|
+ const checkConfirmPassword =(rule:any,value:string, callback:Function)=>{
|
|
|
+ if (!value) {
|
|
|
+ return callback('请再次输入密码')
|
|
|
+ }
|
|
|
+ if (value != state.ruleForm.password) {
|
|
|
+ return callback('两次密码输入不一致,请重新输入')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ onSignIn,
|
|
|
+ ruleFormRef,
|
|
|
+ modes,
|
|
|
+ checkPassword,
|
|
|
+ checkConfirmPassword,
|
|
|
+ ...toRefs(state),
|
|
|
+ };
|
|
|
+ },
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.login-content-form {
|
|
|
+ margin-top: 20px;
|
|
|
+ @for $i from 1 through 4 {
|
|
|
+ .login-animation#{$i} {
|
|
|
+ opacity: 0;
|
|
|
+ animation-name: error-num;
|
|
|
+ animation-duration: 0.5s;
|
|
|
+ animation-fill-mode: forwards;
|
|
|
+ animation-delay: calc($i/10) + s;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .login-content-password {
|
|
|
+ display: inline-block;
|
|
|
+ width: 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ &:hover {
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .login-content-code {
|
|
|
+ width: 100%;
|
|
|
+ padding: 0;
|
|
|
+ font-weight: bold;
|
|
|
+ letter-spacing: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .login-content-submit {
|
|
|
+ width: 100%;
|
|
|
+ font-weight: 300;
|
|
|
+ margin-top: 15px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .login-msg {
|
|
|
+ color: var(--el-text-color-placeholder);
|
|
|
+ }
|
|
|
+ .intensity {
|
|
|
+ .psdText {
|
|
|
+ font-size: 14px;
|
|
|
+ margin-right: 10px;
|
|
|
+ color: #5a5a5a;
|
|
|
+ }
|
|
|
+ .line {
|
|
|
+ display: inline-block;
|
|
|
+ width: 48px;
|
|
|
+ height: 4px;
|
|
|
+ background: #d8d8d8;
|
|
|
+ border-radius: 3px;
|
|
|
+ margin-right: 8px;
|
|
|
+ &.low {
|
|
|
+ background: #f4664a;
|
|
|
+ }
|
|
|
+ &.middle {
|
|
|
+ background: #ffb700;
|
|
|
+ }
|
|
|
+ &.high {
|
|
|
+ background: #2cbb79;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .level {
|
|
|
+ margin: 0 16px 0 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|