最近在写许老师之前做的项目,写到了登录页面,我有一直以为登录页面就几个输入框,几个按钮,然后输入以后直接交互切换页面就行了,是个很简单的页面,结果,许老师给我说,登录页是一个闭环,它是即简单又复杂,登录页有几个选项,输入账号密码,然后去登录,忘记密码,更改新密码后,重新登录,注册账号,注册成功后,去登录,他们是一套,然后登录后,上方还得有退出,退出后更改上方头部样式,出现登录和注册两个按钮,点击登录回到哪个页面,点击注册又回到哪个页面,非常的麻烦,他是一个系统的方式,接下来,我就把我写出的登录闭环系统写成文章,方便未来查询和解惑。
登录页:
注册页:
忘记密码页:
登录后头部形态:
退出后头部形态:
以上就是ui画出的登录系统的一套,接下来,我们就开始代码逻辑:
1、 判断布局:
首先,看到这个设计稿,我们能清楚的知道,用户进入页面的第一选择,一定是登录页,登录页是两部分组成左边的背景图加logo,右边的登录的一套,那么我们可以知道,左边是不动的,动的只有右边的登录,注册和忘记密码,这样,我们就可以有一个主页面,里面写死的是左边的背景图和logo,而右边是由三个组件组成的,三个组件相互切换就可以了,接下来,我们就开始给vue配置他所需要的包
2、配置包:
因为我现在写的是登录,所以,其他的我们不管,先配置登录所需要的包,分别是vuex(状态管理器),less(css框架),router(路由),elementUi(ui框架),具体配置方式就不写了,前面的文章有写。配置好包以后,先把登录页作为主页面,就是首页,给他添加路由:
{
path: "/",
component: HomePage,
},
然后把数据结构整理出来:
很明显,登录后要跳转到另外一个页面,所以,要给另外一个页面也配置好路由:
{
path: "/CapitalPage",
component: CapitalPage,
},
配置好后,就开始我们的代码。
3、代码书写:
1、封装公用组件baseButton和baseInput:
basebutton:
<template>
<div class="BaseButton">
<button :style="styles" class="BassButtonStyle" @click="buttonClick">
<slot />
</button>
</div>
</template>
<script>
export default {
name: "BaseButton",
props: {
styles: Object,
buttonClick: {
type: Function,
default: () => {},
},
},
};
</script>
<style lang="less" scoped>
@import "@/baseStyle.less";
.BaseButton {
.BassButtonStyle {
border-radius: 12px;
cursor: pointer;
width: 73px;
height: 32px;
border: 1px solid @bg_color_yellow;
background-color: @bg_color_yellow;
font-size: 14px;
box-sizing: border-box;
.displayFLexAllCenter();
border: 1px solid;
}
}
.buttonErr {
.BassButtonStyle {
background-color: @bg_err;
cursor: not-allowed;
}
}
</style>
basebutton这个组件,我将style封装成了一个变量,并且既然是按钮就肯定有点击事件,所以我给他写了一个点击事件,并给点击事件赋值,当他没有被调用的时候,他就是一个空的函数。
baseInput:
<template>
<div class="BaseInput">
<el-input
v-model="value"
class="loginInput"
:placeholder="placeholder"
:style="inputStyle"
@input="changeInputValue"
:show-password="showPassword"
>
<button class="sendButton" slot="suffix" v-if="buttonShow">
Send
</button></el-input
>
</div>
</template>
<script>
export default {
name: "BaseInput",
props: {
placeholder: String,
inputValue: String,
inputStyle: Object,
changeInputValue: {
type: Function,
default: () => {},
},
buttonShow: {
type: Boolean,
default: false,
},
showPassword: Boolean,
},
data: function () {
return {
value: "",
};
},
watch: {
inputValue: function (val) {
this.value = val;
// localStorage.setItem("user", {
// email: "1",
// password: 123,
// });
// localStorage.getItem("name");
},
},
};
</script>
<style lang="less" scoped>
.sendButton {
width: 47px;
height: 24px;
background-color: rgba(242, 216, 85, 0.2);
margin-top: 10px;
border: none;
border-radius: 8px;
color: #f2d855;
cursor: pointer;
font-size: 12px;
margin-right: 12px;
}
</style>
input里面的讲究就有点多了,首先,他是从elementUi调用的一个输入框,这个输入框接受几个变量:style,placeholder,show-passwprd,并且有一个onchange事件,但是ui框架是封装好的,他的onchange事件,名字就叫input,所以,这里是@input,并给onchange事件赋值,当他没有被调用的时候,他就是一个空的函数,然后在他的内部添加一个button按钮,给一个v-if,因为有收取邮箱验证码的选项,所以这个按钮就是为了点击给邮箱发送验证码,但是不是每一个输入框都有,所以给他一个v-if判断那个需要哪个不需要。这里最重要的一点就是elementUi的样式更改,需要打开控制台,然后看他自带的class类名,然后用他的class类名去更改样式才能生效。
2、书写登录页:
<template>
<div class="signIn">
<div class="signInHead">Sign in</div>
<BaseInput
class="signMargin"
:placeholder="'Email'"
:changeInputValue="email"
/>
<BaseInput
class="signMargin"
:placeholder="'Password'"
:changeInputValue="password"
:showPassword="true"
/>
<div class="loginErr" v-if="loginErr">您输入的账号密码有误</div>
<BaseButton
class="signMargin"
:styles="{ width: 380 + 'px', height: 44 + 'px', fontSize: 16 + 'px' }"
:buttonClick="loginPage"
>
Sign in
</BaseButton>
<div class="passWordAndNewUser signMargin">
<div class="forgotPassword signFontSize" @click="forgot(3)">
Forgot password
</div>
<div class="newUserSignUp">
<div class="newUser signFontSize">New user?</div>
<div class="SignUp signFontSize" @click="signUp(2)">Sign up</div>
</div>
</div>
</div>
</template>
<script>
import BaseInput from "@/components/BaseInput.vue";
import BaseButton from "@/components/BaseButton.vue";
export default {
name: "SignIn",
data: () => {
return {
loginEmail: "",
loginPassword: "",
loginErr: false,
};
},
components: {
BaseInput,
BaseButton,
},
props: {
signUp: Function,
forgot: Function,
},
methods: {
email: function (eml) {
this.loginEmail = eml;
},
password: function (pas) {
this.loginPassword = pas;
},
loginPage: function () {
const obj = JSON.parse(localStorage.getItem("user")); // 反序列化
console.log(obj.userEmail);
if (
this.loginEmail === obj.userEmail &&
this.loginPassword === obj.userPassword
) {
this.loginErr = false;
this.$router.push("/CapitalPage");
} else {
this.loginErr = true;
}
},
},
};
</script>
在登录页中,提前写好点击事件,分别是下方的忘记密码和注册,点击的时候需要去切换组件,用v-if。
注册和忘记密码页代码:
<template>
<div class="createAccount">
<div class="signInHead">Sign in</div>
<BaseInput
class="signMargin"
:placeholder="'Email'"
:changeInputValue="emailChange"
/>
<BaseInput
class="signMargin"
:placeholder="'Password'"
:changeInputValue="passwordChange"
:showPassword="true"
/>
<BaseInput
:class="
this.changeInputBorder === true ? 'signMargin' : 'errInput signMargin'
"
:placeholder="'Re-enter password'"
:changeInputValue="passwordEnter"
:showPassword="true"
/>
<div class="errTipOf" v-if="!changeInputBorder">您两次输入的密码不一致</div>
<BaseInput
class="signMargin"
:placeholder="'Email verification code'"
:changeInputValue="emailCode1"
buttonShow
/>
<BaseButton
:class="
this.changeInputBorder === true ? 'signMargin' : 'buttonErr signMargin'
"
:styles="{ width: 380 + 'px', height: 44 + 'px', fontSize: 16 + 'px' }"
:buttonClick="getValue"
:disabled="changeInputBorder"
>Create account</BaseButton
>
<div class="doYouHaveAccount signMargin">
<div class="inquire signFontSize">Already have account?</div>
<div class="SignUp signFontSize" @click="haveAccount(1)">Sign in</div>
</div>
</div>
</template>
<script>
import BaseInput from "@/components/BaseInput.vue";
import BaseButton from "@/components/BaseButton.vue";
export default {
name: "SignIn",
data: () => {
return {
email: "",
password: "",
enterPassword: "",
emailCode: "",
changeInputBorder: true,
};
},
components: {
BaseInput,
BaseButton,
},
props: {
haveAccount: Function,
},
watch: {},
methods: {
// 这下面四步都是在获取input里面的值
emailChange: function (val) {
this.email = val;
},
passwordChange: function (password) {
this.password = password;
},
passwordEnter: function (enter) {
// 这里是在判断,注册确认密码的时候必须要与上一次输入的密码一致,不然改变input状态和按钮的状态
this.enterPassword = enter;
console.log(this.password);
if (
(this.enterPassword !== this.password) &
(this.enterPassword !== "")
) {
this.changeInputBorder = false;
} else {
this.changeInputBorder = true;
}
},
emailCode1: function (code) {
this.emailCode = code;
},
// 这里是点击注册按钮后将input的值存入localStorage里面,并跳转至登录页面
getValue: function () {
localStorage.setItem(
"user",
JSON.stringify({
// 序列化
userEmail: this.email,
userPassword: this.password,
userEnterPas: this.enterPassword,
userEmailCode: this.emailCode,
})
);
this.haveAccount(1);
},
},
};
</script>
<template>
<div class="createAccount">
<div class="signInHead">Forgot password</div>
<BaseInput
class="signMargin"
:placeholder="'Email'"
:changeInputValue="changeEmail"
/>
<BaseInput
class="signMargin"
:placeholder="'New password'"
:changeInputValue="passwordNew"
:showPassword="true"
/>
<BaseInput
:class="this.changeBoo === true ? 'signMargin' : 'errInput signMargin'"
:placeholder="'Re-enter new password'"
:changeInputValue="enterNewPassword"
:showPassword="true"
/>
<div class="errTipOf" v-if="!changeBoo">您两次输入的密码不一致</div>
<BaseInput
class="signMargin"
:placeholder="'Email verification code'"
:changeInputValue="codeNewPassword"
buttonShow
/>
<BaseButton
:class="this.changeBoo === true ? 'signMargin' : 'buttonErr signMargin'"
:styles="{ width: 380 + 'px', height: 44 + 'px', fontSize: 16 + 'px' }"
:buttonClick="verifyPassword"
:disabled="changeBoo"
>Set up</BaseButton
>
<div class="doYouHaveAccount signMargin">
<div class="SignUp signFontSize" @click="signIn(1)">Sign in</div>
</div>
</div>
</template>
<script>
import BaseInput from "@/components/BaseInput.vue";
import BaseButton from "@/components/BaseButton.vue";
export default {
name: "SignIn",
data: () => {
return {
email: "",
newPassword: "",
newPasswordEnter: "",
newPasswordCode: "",
changeBoo: true,
};
},
components: {
BaseInput,
BaseButton,
},
props: {
signIn: Function,
},
methods: {
changeEmail: function (ema) {
this.email = ema;
},
passwordNew: function (pas) {
this.newPassword = pas;
},
enterNewPassword: function (ent) {
this.newPasswordEnter = ent;
if (
(this.newPasswordEnter !== this.newPassword) &
(this.newPasswordEnter !== "")
) {
this.changeBoo = false;
} else {
this.changeBoo = true;
}
},
codeNewPassword: function (cod) {
this.newPasswordCode = cod;
},
verifyPassword: function () {
if (
this.email !== "" &&
this.newPassword !== "" &&
this.newPasswordEnter !== "" &&
this.newPasswordCode !== ""
) {
console.log(123);
localStorage.setItem(
"user",
JSON.stringify({
// 序列化
userEmail: this.email,
userPassword: this.newPassword,
userEnterPas: this.newPasswordEnter,
})
);
this.signIn(1);
}
},
},
};
</script>
在这个地方,他就形成了一个闭环,我进入页面的时候是登录页,点击注册后,输入邮箱密码和验证码后,注册成功,然后返回登录页,忘记密码页输入账号密码验证码后,依旧回到登录页。在这里用到了一个新东西,就是localStroage储存,这里把输入框输入的东西储存到变量中,然后将变量中的内容储存到localStorage里面,返回登录页后,去判断,你输入的账号密码和储存的账号密码是否一致,如果一致,那么就跳转页面,如果不一致,那么就给你说,你输入的账号密码有误,请重新输入。
3、书写路由跳转:
我们书写完登录系统以后,我们需要知道,账号密码输入正确有以后我们应该做什么,那么我们看项目ui,
很明显,下一个页面就是这个,输入正确的账号密码以后,路由跳转就应该跳转到这个页面中,因为我现在写的是登录系统,那么我们就看到这个页面的头部也是登录系统的一环,上面是我们登录成功以后的头部,然后继续看ui,我们会看到,点击退出以后,他会改变头部的状态:
那么我们先去书写登录后的头部形态,在写之前,我们先把页面布局出来,把路由跳转写好:
新建一个文件夹,然后用来储存这个页面需要应用的文件,然后在page文件夹新启一个文档,用来做这个页面的主页面存储。
登录后头部形态书写:
<template>
<!--这个是页面的头部-->
<div class="homeHeadSecond">
<img :src="headLogo" alt="" class="headLogo" />
<div class="headNavigator">
<div class="navigatorName">
<div class="headName">Home</div>
<div class="headName">Tasks</div>
<div class="headName">Docs</div>
<div class="headName">News</div>
</div>
<div class="logInAndLogOut">
<img :src="userHead" alt="" />
<div class="userEmail">{{ userEmail }}</div>
<div class="loginOutElement">
<el-col :span="12">
<el-dropdown trigger="click">
<span class="el-dropdown-link">
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-circle-check">
<div class="overLoginOut" @click="changeHead">
<img :src="overLoginOut" />
<div class="overLoginOutS">Sign out</div>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</div>
</div>
</div>
</div>
</template>
<script>
import headLogo from "../../assets/headLogo.png";
import userHead from "../../assets/userHead.png";
import getOut from "../../assets/getOut.png";
import overLoginOut from "../../assets/overLoginOut.png";
export default {
name: "HomeHead",
data: () => {
return {
headLogo: headLogo,
userEmail: "",
userHead: userHead,
getOut: getOut,
overLoginOut: overLoginOut,
};
},
components: {},
methods: {},
beforeMount: function () {
const obj = JSON.parse(localStorage.getItem("user"));
this.userEmail = obj.userEmail;
},
props: {
changeHead: Function,
},
};
</script>
这篇代码中涉及了一个新知识:vue的生命周期中的其中一个函数:beforeMount,这里用到他是因为在头部登录形态中,我们看到了用户头像的后面增加了他的登录邮箱,所以我们需要再页面挂载前就获取到localStorage中的数据,然后将他存在变量中,赋值于上面。
然后书写退出后的头部形态:
<template>
<!--这个是页面的头部-->
<div class="homeHead">
<img :src="headLogo" alt="" class="headLogo" />
<div class="headNavigator">
<div class="navigatorName">
<div class="headName">Home</div>
<div class="headName">Tasks</div>
<div class="headName">Docs</div>
<div class="headName">News</div>
</div>
<div class="logInAndLogOut">
<BaseButton>Sign Out</BaseButton>
<BaseButton
:styles="{
backgroundColor: 'transparent',
width: '69px',
borderColor: '#f2d855',
color: '#f2d855',
marginLeft: '10px',
}"
:buttonClick="logIn"
>Sign in</BaseButton
>
</div>
</div>
</div>
</template>
<script>
import headLogo from "../../assets/headLogo.png";
import BaseButton from "@/components/BaseButton.vue";
export default {
name: "HomeHead",
data: () => {
return {
headLogo: headLogo,
};
},
components: {
BaseButton,
},
methods: {
logIn: function () {
this.$router.push("/");
},
},
};
</script>
这里不一样的是,退出后有两个按钮,一个登录,一个注册,那么这里就多了两个交互,点击登录,回到登录页面,这里就是切换路由到登录页就可以了,一个注册,也是切换路由,但是,需要切换路由后回到的页面是忘记密码页,而不是登录页