开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第20天,点击查看
------本页内容已结束,喜欢请分享------
前言
已经实现了router、vuex模块,并能够显示对应角色的权限菜单,接下来我们就完成登录模块看看实现的效果。但是之前的layout模块我们遍历的路由还是前端静态的路由,应该让他使用vuex中拼接后的路由,还有一些地方需要添加和修改(导航栏显示用户数据、重置密码模块、退出登录等)。修改layout模块
我们导航栏用户信息操作的区域有一个重置密码的功能,主要步骤为需要弹窗弹出表单、校验信息、提交表单,我们按步来实现。为了防止代码过多,我们将重置密码模块与主模块分开,由于重置密码的弹窗是显示在layout主页面(index.vue
)上,所以弹窗的开关也得在主页面上,主页面只有弹窗。另外添加一个form表单文件(有旧密码、新密码、确认密码字段),主页面引入即可。我们先看看layout总体的目录结构
添加重置密码模块
components/layout/resetPassword.vue<template>
<div>
<el-form ref="ruleFormRef" :model="user" :rules="rules" label-width="80px">
<el-form-item label="旧密码" prop="oldPassword">
<el-input v-model="user.old_password" placeholder="请输入旧密码" type="password" />
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input v-model="user.password" placeholder="请输入新密码" type="password" />
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model="user.repassword" placeholder="请确认密码" type="password" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close(ruleFormRef)">取消</el-button>
<el-button type="primary" @click="submitForm(ruleFormRef)">保存</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { ElMessage, FormInstance } from 'element-plus'
import { updatePwd } from '@/utils/API/user/user';
import { store } from '@/store';
import router from '@/router';
// 引入弹窗关闭的方法
const emit = defineEmits(['closeDialog'])
// 表单实例
const ruleFormRef = ref<FormInstance>()
// 表单数据对象
const user = reactive<resetpass>({
old_password: '',
password: '',
repassword: ''
})
// 校验信息对象
const validatePass = (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error('请输入新密码'))
} else {
if (user.repassword !== '') {
if (!ruleFormRef.value) return
ruleFormRef.value.validateField('confirmPassword', () => null)
}
callback()
}
}
const validatePass2 = (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error('请再次输入确认密码'))
} else if (value !== user.password) {
callback(new Error("两次输入的密码不匹配!"))
} else {
callback()
}
}
// 校验规则
const rules = reactive({
old_password: [
{ required: true, message: "旧密码不能为空", trigger: "blur" }
],
password: [{ validator: validatePass, trigger: 'blur' }],
repassword: [{ validator: validatePass2, trigger: 'blur' }],
})
// 提交表单的方法
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
const form = {
user_id: store.state.user.user_id,
...user
}
if (valid) {
updatePwd(form).then(res => {
ElMessage.success('重置密码成功')
// 重置密码后重新登录
store.dispatch('user/FedLogOut').then(() => {
router.push('/login')
});
emit('closeDialog')
})
}
})
}
// 关闭弹窗的方法
const close = (formEl: FormInstance | undefined) => {
// 清空弹窗
formEl?.resetFields()
emit('closeDialog')
}
</script>
然后在主文件(index.vue
)中添加一个弹窗内置此表单文件,再添加一个关闭弹窗的方法传递给表单组件即可。
<template>
<div class="app-container">
...
<el-dialog v-model="dialogFormVisible" width="30%" title="重置密码">
<reset-password @closeDialog="closeDialog"></reset-password>
</el-dialog>
</div>
</template>
<script setup lang="ts">
...
const dialogFormVisible = ref(false)
...
const closeDialog = () => {
dialogFormVisible.value = false
}
</script>
导航栏显示用户数据及退出登录
index.vue html结构
主要就是利用vuex的全局状态显示用户信息
index.vue js代码
退出登录方法主要是点击后显示确认框,确认后调用vuex中的异步函数,清空用户信息、token等并重置路由然后跳转到登录页重新登录
<script setup lang="ts">
import { ref, computed } from 'vue'
import {
Tools,
} from '@element-plus/icons-vue'
import { useStore } from '@/store';
import { useRouter } from 'vue-router'
import { ElMessageBox } from 'element-plus';
import ResetPassword from './resetPassword.vue';
import SidebarItem from './SidebarItem.vue';
// 导入vuex模块
const store = useStore()
// 导入router模块
const router = useRouter()
// 获得用户权限菜单
const routes = computed(() => store.state.permission.routes);
// 表单显示标识
const dialogFormVisible = ref(false)
// 高亮的菜单项
const activeIndex = ref('1')
// 退出登录的方法
const logout = () => {
ElMessageBox.confirm(
'确定注销并退出系统吗?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
store.dispatch('user/FedLogOut').then(() => {
router.replace('/login')
})
})
}
// 关闭弹窗方法
const closeDialog = () => {
dialogFormVisible.value = false
}
</script>
接下来我们来实现登录模块,就可看到主页的权限效果渲染正确与否
实现登录模块
登录模块也比较简单,就是一个登录表单,涉及到表单校验、提交表单。还有一个记住密码的功能是需要安装额外的模块js-cookie
(对cookie信息进行操作)、jsencrypt
(对密码加密解密)。下面是依赖的版本。
记住密码分两种情况
- 选择了记住密码。那么提交表单的方法我们就要存储用户名密码信息(密码需要用
jesncrypt
加密)到cookie中。下次再需要登录的时候直接先将密码解密再从cookie中取信息放到表单中 - 未选择记住密码就清空cookie中存在的用户名密码信息
<template>
<div class="loginbody">
<div class="logindata">
<div class="logintext">
<h2>Welcome</h2>
</div>
<div class="form">
<el-form ref="ruleFormRef" :model="form" :rules="rules">
<el-form-item prop="username">
<el-input v-model="form.username" clearable placeholder="请输入账号"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="form.password" clearable placeholder="请输入密码" show-password></el-input>
</el-form-item>
<el-form-item prop="checkCode">
<el-input v-model="form.checkCode" placeholder="请输入验证码" class="login-code-input" maxlength="4" minlength="4"
clearable></el-input>
<div class="login-code" @click="getCode" v-html="verifyImg">
</div>
</el-form-item>
<el-form-item prop="remember">
<el-checkbox v-model="form.remember" class="check-box">记住密码</el-checkbox>
</el-form-item>
</el-form>
</div>
<div class="butt">
<el-button :loading="loading" type="primary" @click="submitForm(ruleFormRef)">登录</el-button>
<el-button @click="ruleFormRef?.resetFields()">重置</el-button>
</div>
</div>
</div>
</template>
<script lang="ts">
export default { name: 'Login' };
</script>
<script lang="ts" setup>
import { ref, reactive, toRefs, watch, onMounted } from 'vue'
import router from '@/router'
import type { FormInstance, FormRules } from 'element-plus'
import { useStore } from '@/store'
import { useRoute } from 'vue-router'
import Cookies from 'js-cookie'
import { encrypt, decrypt } from '@/utils/jsencrypt'
import { getCheckCode } from '@/utils/API/user/user'
// 获取state、route
const store = useStore()
const route = useRoute()
// 要重定向的地址
const redirect = ref(undefined as string | undefined)
// 重定向路由其它query信息
const otherQuery = ref({})
// 登录变量、方法
const loading = ref(false)
const ruleFormRef = ref<FormInstance>()
const form = reactive<loginForm>({
username: '',
password: '',
checkCode: '',
remember: false,
uuid: 0
})
// 验证码图片
let verifyImg = ref('')
const rules = reactive<FormRules>({
username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
checkCode: [{ required: true, message: '请输入验证码', trigger: 'blur' }, { min: 4, max: 4, message: '长度为4个字符!', trigger: 'blur' }],
})
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid, fields) => {
if (valid) {
loading.value = true
// 记住密码存储用户信息
if (form.remember) {
Cookies.set('username', form.username, { expires: 7 })
Cookies.set('password', encrypt(form.password), { expires: 7 })
Cookies.set('remember', form.remember, { expires: 7 })
} else {
// 移除用户信息
Cookies.remove('username')
Cookies.remove('password')
Cookies.remove('remember')
}
store.dispatch('user/login', form).then(() => {
router.push({ path: redirect.value || '/', query: otherQuery.value })
loading.value = false
})
.catch(() => {
getCode()
loading.value = false
})
}
})
}
// 监听重定向信息
watch(
route,
() => {
const query = route.query;
if (query) {
redirect.value = query.redirect as string;
otherQuery.value = getOtherQuery(query);
}
},
{
immediate: true
}
);
// 获取重定向query其它信息
function getOtherQuery(query: any) {
return Object.keys(query).reduce((acc: any, cur: any) => {
if (cur !== 'redirect') {
acc[cur] = query[cur];
}
return acc;
}, {});
}
// 获取cookie中的用户名密码信息
function getCookie() {
const username = Cookies.get('username')
const password = Cookies.get('password')
const remember = Cookies.get('remember')
form.username = username === undefined ? form.username : username
form.password = password === undefined ? form.password : decrypt(password)
form.remember = remember === undefined ? form.remember : Boolean(remember)
}
// 获取验证码
function getCode() {
const uuid = new Date().getTime()
form.uuid = uuid
getCheckCode(uuid).then(res => {
verifyImg.value = res.data
})
}
onMounted(() => {
getCode()
getCookie()
})
</script>
<style lang="scss" scoped>
.loginbody {
width: 100%;
height: 100%;
background-image: url('../assets/images/cool-background.png');
background-size: cover;
background-repeat: no-repeat;
position: fixed;
opacity: 0.8;
display: flex;
align-items: center;
justify-content: center;
}
.logintext {
margin-bottom: 20px;
line-height: 50px;
text-align: center;
font-size: 30px;
font-weight: bolder;
color: #fbffc8;
text-shadow: 2px 2px 4px #000000;
}
.logindata {
width: 400px;
height: 300px;
margin-bottom: 150px;
}
.form {
width: 100%;
.el-input {
font-size: 16px;
width: 100%;
height: 40px;
line-height: 40px;
}
.login-code-input {
position: relative;
}
.login-code {
position: absolute;
right: 6px;
top: 4px;
vertical-align: center;
width: 100px;
height: 32px;
cursor: pointer;
vertical-align: middle;
}
.check-box {
font-size: 16px;
font-weight: 600
}
}
.tool {
display: flex;
justify-content: space-between;
color: #606266;
}
.butt {
font-size: 16px;
margin-top: 10px;
text-align: center;
}
.shou {
cursor: pointer;
color: #606266;
}
</style>
测试
- 登录成功展示对应权限菜单
- 登录失败
感谢您的来访,获取更多精彩文章请收藏本站。
© 版权声明
THE END
暂无评论内容