前言
项目地址
本项目是为开发一套容器化的开发、运行、测试环境,用以支持Web开发、程序设计等课程的实验教学。
任务
用pinia、router和localStorage实现本地的注册、登录、重置密码、登出等功能。
store.ts
import { defineStore } from "pinia";
export const useLoginStore = defineStore({
id: "login",
state: () => ({
isLogin: Number(localStorage.getItem('isLogin') || '0'),
userId: localStorage.getItem('userId') || '',
userPassword: localStorage.getItem('userPassword') || '',
}),
getters: {
getIsLogin(state) {
return (state.isLogin == 1) ? true : false;
},
getUserId(state) {
return state.userId;
},
getUserPassword(state) {
return state.userPassword
}
},
actions: {
userLogin(id: string, pwd: string) {
this.isLogin = 1;
this.userId = id;
this.userPassword = pwd;
localStorage.setItem('isLogin', '1');
localStorage.setItem('userId', id);
localStorage.setItem('userPassword', pwd);
},
userLogout() {
this.isLogin = 0;
localStorage.setItem('isLogin', '0');
// this.userId = '';
// this.userPassword = '';
// localStorage.setItem('userId', '');
// localStorage.setItem('userPassword', '');
},
userRegister(id: string, pwd: string) {
localStorage.setItem('userId', id);
localStorage.setItem('userPassword', pwd);
this.userId = id;
this.userPassword = pwd;
},
updateUserPassword(newPass:string){
this.userPassword=newPass;
localStorage.setItem('userPassword',newPass);
}
},
});
注册
<!--注册界面-->
<template>
<div class="login-wrap">
<el-form
ref="registerFormRef"
class="login-container"
:model="registerForm"
:rules="registerRules"
label-position="right"
label-width="80px"
>
<h1 class="title">注册</h1>
<el-form-item label="用户名称" prop="username">
<el-input type="text" placeholder="请输入名称" v-model="registerForm.username"></el-input>
</el-form-item>
<el-form-item label="用户邮箱" prop="useremail">
<el-input type="email" placeholder="请输入邮箱" v-model="registerForm.useremail" size="default"></el-input>
</el-form-item>
<el-form-item label="用户密码" prop="pass">
<el-tooltip content="<span>长度为8~15位,必须同时包含大小写字母及数字</span>" raw-content>
<el-input v-model="registerForm.pass" type="password" placeholder="请输入密码" show-password />
</el-tooltip>
</el-form-item>
<el-form-item label="确认密码" prop="checkPass">
<el-input
v-model="registerForm.checkPass"
type="password"
placeholder="请再次输入密码"
show-password
/>
</el-form-item>
<el-form-item label="验证码" prop="checkCode">
<el-input v-model="registerForm.checkCode" placeholder="请输入验证码" />
</el-form-item>
<el-form-item label-width="100px">
<CharacterVerification ref="ver"></CharacterVerification>
</el-form-item>
<el-form-item label-width="0px">
<el-button type="primary" @click="registerUser()" style="width: 48%;">用户注册</el-button>
<el-button style="width: 48%;" @click="gotoLogin()">返回登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script lang="ts">
import { useRouter } from "vue-router";
import { ref, defineComponent, reactive, unref } from 'vue';
import CharacterVerification from '../components/CharacterVerification.vue';
import { ElMessage } from "element-plus";
import { useLoginStore } from "@/stores/store";
export default defineComponent({
components: { CharacterVerification },
setup() {
const store=useLoginStore();
const router = useRouter();
//表单内容
const registerForm = reactive({
username: '',
useremail: '',
pass: '',
checkPass: '',
checkCode: ''
})
const registerFormRef = ref();
const ver = ref();
// 自定义验证规则
const validatePass = (rule: any, value: any, callback: any) => {
const reg = /^(?=.*\d)(?=.*[a-zA-Z])[\da-zA-Z]{8,15}$/;
if (!reg.test(value)) {
callback(new Error('长度为8~15位,必须同时包含大小写字母及数字'));
}
if (registerForm.checkPass != null && registerForm.checkPass !== '') {
if (!registerFormRef.value) return
registerFormRef.value.validateField('checkPass', () => null)
}
callback();
}
const validateCheckPass = (rule: any, value: any, callback: any) => {
const reg = /^(?=.*\d)(?=.*[a-zA-Z])[\da-zA-Z]{8,15}$/;
if (value !== registerForm.pass) {
callback(new Error('输入密码不一致'));
}
if (!reg.test(value)) {
callback(new Error('长度为8~15位,必须同时包含大小写字母及数字'));
}
callback();
}
//检查验证码是否正确
const validateVerificationCode = (rule: any, value: any, callback: any) => {
if (!ver.value.validate(value)) {
callback(new Error('验证码错误'));
} else {
callback()
}
}
// 定义校验规则
const registerRules = reactive({
username: [
{ required: true, message: '用户名称不能为空', trigger: 'blur' },
{ min: 2, max: 32, message: '名称长度只能在2~32之间', trigger: 'blur' }
],
useremail: [
{ required: true, message: '邮箱不能为空', trigger: 'blur' },
{ type: 'email', message: '邮箱格式不正确', trigger: 'blur' }
],
pass: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 8, max: 15, message: '密码位数只能在8~15之间', trigger: 'blur' },
{ validator: validatePass, trigger: 'blur' }
],
checkPass: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 8, max: 15, message: '密码位数只能在8~15之间', trigger: 'blur' },
{ validator: validateCheckPass, trigger: 'blur' }
],
checkCode: [
{ required: true, message: '验证码不能为空', trigger: 'blur' },
{ validator: validateVerificationCode, trigger: 'blur' }
]
})
const registerUser = async () => {
const form = unref(registerFormRef);
if (!form) return
try {
await form.validate();
const { username, useremail, pass } = registerForm;
console.log(username, useremail, pass);
//todo: 发送注册请求
store.userRegister(username,pass);
ElMessage({
showClose: true,
message: '注册成功',
type: 'success',
center: true,
grouping:true,
onClose:()=>{router.push({ path: "/login" });}
})
} catch (error: any) {
ElMessage({
showClose: true,
message: '输入格式不正确',
type: 'error',
center: true,
grouping:true,
})
}
}
function gotoLogin() {
router.push({ path: "/login" });
}
return {
ver,
registerFormRef,
registerForm,
registerRules,
registerUser,
gotoLogin,
}
}
})
</script>
<style>
.login-wrap {
box-sizing: border-box;
width: 100%;
height: 100%;
padding-top: 10%;
}
.login-container {
border-radius: 10px;
margin: 0px auto;
width: 350px;
padding: 30px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
text-align: left;
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.1);
}
.title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
</style>
登录
<!--登录界面-->
<template>
<div class="login-wrap">
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="loginRules"
class="login-container"
label-position="right"
label-width="80px"
>
<h1 class="title">登录</h1>
<el-form-item label="用户账号" prop="userid">
<el-input v-model="loginForm.userid" placeholder="请输入账号" />
</el-form-item>
<el-form-item label="用户密码" prop="pass">
<el-input v-model="loginForm.pass" type="password" placeholder="请输入密码" show-password />
</el-form-item>
<el-form-item label="验证码" prop="checkCode">
<el-input v-model="loginForm.checkCode" placeholder="请输入验证码" />
</el-form-item>
<el-form-item label-width="100px">
<CharacterVerification ref="ver"></CharacterVerification>
</el-form-item>
<el-form-item label-width="0px">
<el-button type="primary" @click="doLogin()" style="width: 100%;">用户登录</el-button>
</el-form-item>
<el-row :gutter="20" style="text-align: center;">
<el-col :span="5">
<div class="grid-content"></div>
</el-col>
<el-col :span="6">
<el-link @click="gotoRegister()">用户注册</el-link>
</el-col>
<el-col :span="1">
<div class="grid-content"></div>
</el-col>
<el-col :span="6">
<el-link @click="gotoForget()">忘记密码</el-link>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script lang="ts">
import { ref, defineComponent, reactive, unref } from 'vue'
import { useRouter } from "vue-router";
import CharacterVerification from '../components/CharacterVerification.vue';
import { ElMessage } from 'element-plus';
import { useLoginStore } from '@/stores/store';
export default defineComponent({
components: { CharacterVerification },
setup() {
const store = useLoginStore();
const router = useRouter();
const loginFormRef = ref();
const ver = ref();
//表单内容
const loginForm = reactive({
userid: '',
pass: '',
checkCode: ''
})
//自定义表单验证
const validatePass = (rule: any, value: any, callback: any) => {
const reg = /^(?=.*\d)(?=.*[a-zA-Z])[\da-zA-Z]{8,15}$/;
if (!reg.test(value)) {
callback(new Error('长度为8~15位,必须同时包含大小写字母及数字'));
}
callback();
}
//检查验证码是否正确
const validateVerificationCode = (rule: any, value: any, callback: any) => {
if (!ver.value.validate(value)) {
callback(new Error('验证码错误'));
} else {
callback()
}
}
// 定义校验规则
const loginRules = reactive({
userid: [
{ required: true, message: '用户账号不能为空', trigger: 'blur' },
],
pass: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 8, max: 15, message: '密码位数只能在8~15之间', trigger: 'blur' },
{ validator: validatePass, trigger: 'blur' }
],
checkCode: [
{ required: true, message: '验证码不能为空', trigger: 'blur' },
{ validator: validateVerificationCode, trigger: 'blur' }
]
})
function gotoForget() {
router.push({ path: '/forget_password' });
}
function gotoRegister() {
router.push({ path: '/register' });
}
const doLogin = async () => {
const form = unref(loginFormRef);
if (!form) return
try {
await form.validate();
const { userid, pass } = loginForm;
console.log(userid, pass);
//todo: 发送登录请求
const l_id = store.getUserId;
const l_pass = store.getUserPassword;
//登录成功
if (userid == l_id && pass == l_pass) {
store.userLogin(userid, pass);
router.push({ path: '/login_home' });
}
} catch (error: any) {
ElMessage({
showClose: true,
message: '输入格式不正确',
type: 'error',
center: true,
grouping:true,
})
}
}
return {
ver,
loginFormRef,
loginForm,
loginRules,
gotoForget,
gotoRegister,
doLogin,
}
},
})
</script>
<style>
.login-wrap {
box-sizing: border-box;
width: 100%;
height: 100%;
padding-top: 10%;
}
.login-container {
border-radius: 10px;
margin: 0px auto;
width: 350px;
padding: 30px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
text-align: left;
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.1);
}
.title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
</style>
登出
function goto_codingview() {
ElMessageBox.confirm(
'是否确认退出?',
'退出提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
}
)
.then(() => {
//点击确认
store.userLogout();
router.push({ path: '/' });
})
.catch(() => {
//点击取消
router.push({ path: '/login_home' });
})
}
忘记密码
<template>
<div class="email-wrap" v-if="showEmail == 1">
<el-form ref="emailRef" :model="emailForm" :rules="emailRules" class="login-container">
<el-steps :active="0" finish-status="success" simple style="margin-top: 20px">
<el-step title="Step 1" />
<el-step title="Step 2" />
<el-step title="Step 3" />
</el-steps>
<el-form-item label></el-form-item>
<el-form-item label="用户邮箱" prop="email">
<el-input type="email" placeholder="请输入邮箱" v-model="emailForm.email" />
</el-form-item>
<el-button style="margin-top: 12px" @click="getCheckCode">获取邮箱验证码</el-button>
</el-form>
</div>
<div class="email-check-wrap" v-else-if="showEmail == 2">
<el-form
ref="emailCheckFormRef"
:model="emailCheckForm"
:rules="emailCheckRules"
class="email-container"
>
<el-steps :active="1" finish-status="success" simple style="margin-top: 20px">
<el-step title="Step 1" />
<el-step title="Step 2" />
<el-step title="Step 3" />
</el-steps>
<el-form-item label></el-form-item>
<el-form-item label="邮箱验证码" prop="checkCode">
<el-input v-model="emailCheckForm.checkCode" placeholder="请输入验证码" />
</el-form-item>
<el-button style="margin-top: 12px" @click="next">下一步</el-button>
</el-form>
</div>
<div class="password-wrap" v-else>
<el-form ref="resetFormRef" :model="resetForm" :rules="resetRules" class="password-container">
<el-steps :active="2" finish-status="success" simple style="margin-top: 20px">
<el-step title="Step 1" />
<el-step title="Step 2" />
<el-step title="Step 3" />
</el-steps>
<el-form-item label></el-form-item>
<el-form-item label="新的密码" prop="newPass">
<el-input v-model="resetForm.newPass" placeholder="请输入新密码" type="password" show-password />
</el-form-item>
<el-form-item label="确认密码" prop="checkPass">
<el-input v-model="resetForm.checkPass" placeholder="请输入新密码" type="password" show-password />
</el-form-item>
<el-button style="margin-top: 12px" @click="goHome()">确认</el-button>
</el-form>
</div>
</template>
<script lang="ts">
import { ref, defineComponent, reactive, unref } from 'vue'
import { Edit, UploadFilled, Picture } from '@element-plus/icons-vue'
import { useRouter } from "vue-router";
import { ElMessage } from 'element-plus';
import { useLoginStore } from '@/stores/store';
export default defineComponent({
setup() {
const store = useLoginStore();
const router = useRouter();
const showEmail = ref(1);
const emailRef = ref();
const emailForm = reactive({
email: '',
})
const emailRules = reactive({
email: [
{ required: true, message: '邮箱不能为空', trigger: 'blur' },
{ type: 'email', message: '邮箱格式不正确', trigger: 'blur' }
],
})
const emailCheckFormRef = ref();
const emailCheckForm = reactive({
checkCode: '',
})
const resetFormRef = ref();
const resetForm = reactive({
newPass: '',
checkPass: '',
})
const validateCheckCode = (rule: any, value: any, callback: any) => {
//发送获取验证码请求
const checkCode = '123';
if (value != checkCode) {
callback(new Error('验证码错误'));
}
callback();
}
const validateNewPass = (rule: any, value: any, callback: any) => {
const reg = /^(?=.*\d)(?=.*[a-zA-Z])[\da-zA-Z]{8,15}$/;
if (!reg.test(value)) {
callback(new Error('长度为8~15位,必须同时包含大小写字母及数字'));
}
if (resetForm.checkPass != null && resetForm.checkPass !== '') {
if (!resetFormRef.value) return
resetFormRef.value.validateField('checkPass', () => null)
}
callback();
}
const validateCheckPass = (rule: any, value: any, callback: any) => {
const reg = /^(?=.*\d)(?=.*[a-zA-Z])[\da-zA-Z]{8,15}$/;
if (value !== resetForm.newPass) {
callback(new Error('输入密码不一致'));
}
if (!reg.test(value)) {
callback(new Error('长度为8~15位,必须同时包含大小写字母及数字'));
}
callback();
}
const emailCheckRules = reactive({
checkCode: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ validator: validateCheckCode, trigger: 'blur' },
],
})
const resetRules = reactive({
newPass: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 8, max: 15, message: '密码位数只能在8~15之间', trigger: 'blur' },
{ validator: validateNewPass, trigger: 'blur' }
],
checkPass: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 8, max: 15, message: '密码位数只能在8~15之间', trigger: 'blur' },
{ validator: validateCheckPass, trigger: 'blur' }
],
})
const getCheckCode = async () => {
//发送邮箱验证码
const form = unref(emailRef);
if (!form) return
try {
await form.validate();
showEmail.value = 2;
} catch (error: any) {
ElMessage({
showClose: true,
message: '输入格式不正确',
type: 'error',
center: true,
grouping: true,
})
}
}
const next = async () => {
const form = unref(emailCheckFormRef);
if (!form) return
try {
await form.validate();
showEmail.value = 3;
} catch (error) {
ElMessage({
showClose: true,
message: '验证码不正确',
type: 'error',
center: true,
grouping: true
})
}
};
const goHome = async () => {
const form = unref(resetFormRef);
if (!form) return
try {
await form.validate();
//todo 发送修改密码请求
store.updateUserPassword(resetForm.newPass);
ElMessage({
showClose: true,
message: '修改密码成功',
type: 'success',
center: true,
grouping: true,
onClose: () => { router.push({ path: '/login' }) }
})
} catch (error) {
ElMessage({
showClose: true,
message: '密码格式错误',
type: 'error',
center: true,
grouping: true
})
}
}
return {
showEmail,
emailRef,
emailForm,
emailRules,
emailCheckFormRef,
emailCheckForm,
emailCheckRules,
resetForm,
resetRules,
resetFormRef,
getCheckCode,
next,
goHome,
}
},
})
</script>
<style>
.email-wrap {
box-sizing: border-box;
width: 100%;
height: 100%;
padding-top: 10%;
}
.email-check-wrap {
box-sizing: border-box;
width: 100%;
height: 100%;
padding-top: 10%;
}
.email-container {
border-radius: 10px;
margin: 0px auto;
width: 400px;
padding: 30px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
text-align: left;
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.1);
}
.password-wrap {
box-sizing: border-box;
width: 100%;
height: 100%;
padding-top: 10%;
}
.password-container {
border-radius: 10px;
margin: 0px auto;
width: 400px;
padding: 30px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
text-align: left;
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.1);
}
</style>
重置密码
<template>
<div class="login-wrap">
<el-form
ref="uptFormRef"
class="login-container"
:model="uptForm"
:rules="uptRules"
label-position="right"
label-width="80px"
>
<el-form-item label></el-form-item>
<el-form-item label="旧的密码" prop="oldPass">
<el-input v-model="uptForm.oldPass" placeholder="请输入旧密码" type="password" show-password />
</el-form-item>
<el-form-item label="新的密码" prop="newPass">
<el-input v-model="uptForm.newPass" placeholder="请输入新密码" type="password" show-password />
</el-form-item>
<el-form-item label="确认密码" prop="checkPass">
<el-input v-model="uptForm.checkPass" placeholder="请输入新密码" type="password" show-password />
</el-form-item>
<el-button style="margin-top: 12px" @click="next">确认</el-button>
</el-form>
</div>
</template>
<script lang="ts">
import { ref, defineComponent, reactive, unref } from 'vue'
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import { useLoginStore } from "@/stores/store";
export default defineComponent({
setup() {
const router = useRouter();
const store = useLoginStore();
//表单内容
const uptForm = reactive({
oldPass: '',
newPass: '',
checkPass: '',
})
const uptFormRef = ref();
const validateOldPass = (rule: any, value: any, callback: any) => {
//发送获取密码请求
const storedPass = store.getUserPassword;
if (value != storedPass) {
callback(new Error('输入密码错误'));
}
callback();
}
const validateNewPass = (rule: any, value: any, callback: any) => {
const reg = /^(?=.*\d)(?=.*[a-zA-Z])[\da-zA-Z]{8,15}$/;
if (!reg.test(value)) {
callback(new Error('长度为8~15位,必须同时包含大小写字母及数字'));
}
if (uptForm.checkPass != null && uptForm.checkPass !== '') {
if (!uptFormRef.value) return
uptFormRef.value.validateField('checkPass', () => null)
}
callback();
}
const validateCheckPass = (rule: any, value: any, callback: any) => {
const reg = /^(?=.*\d)(?=.*[a-zA-Z])[\da-zA-Z]{8,15}$/;
if (value !== uptForm.newPass) {
callback(new Error('输入密码不一致'));
}
if (!reg.test(value)) {
callback(new Error('长度为8~15位,必须同时包含大小写字母及数字'));
}
callback();
}
// 定义校验规则
const uptRules = reactive({
oldPass: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 8, max: 15, message: '密码位数只能在8~15之间', trigger: 'blur' },
{ validator: validateOldPass, trigger: 'blur' }
],
newPass: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 8, max: 15, message: '密码位数只能在8~15之间', trigger: 'blur' },
{ validator: validateNewPass, trigger: 'blur' }
],
checkPass: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 8, max: 15, message: '密码位数只能在8~15之间', trigger: 'blur' },
{ validator: validateCheckPass, trigger: 'blur' }
],
})
const next = async () => {
const form = unref(uptFormRef);
if (!form) return
try {
await form.validate();
//todo: 发送修改请求
store.updateUserPassword(uptForm.newPass);
ElMessage({
showClose: true,
message: '修改密码成功',
type: 'success',
center: true,
grouping:true,
onClose: () => { router.push({ path: "/login_home" }); }
});
} catch (error: any) {
ElMessage({
showClose: true,
message: '输入格式不正确',
type: 'error',
center: true,
grouping:true,
})
}
}
return {
uptForm,
uptFormRef,
uptRules,
next,
}
}
})
</script>
<style>
.login-wrap {
box-sizing: border-box;
width: 100%;
height: 100%;
padding-top: 10%;
}
.login-container {
border-radius: 10px;
margin: 0px auto;
width: 350px;
padding: 30px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
text-align: left;
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.1);
}
.title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
</style>