最近在写许老师之前做的项目,写到了登录页面,我有一直以为登录页面就几个输入框,几个按钮,然后输入以后直接交互切换页面就行了,是个很简单的页面,结果,许老师给我说,登录页是一个闭环,它是即简单又复杂,登录页有几个选项,输入账号密码,然后去登录,忘记密码,更改新密码后,重新登录,注册账号,注册成功后,去登录,他们是一套,然后登录后,上方还得有退出,退出后更改上方头部样式,出现登录和注册两个按钮,点击登录回到哪个页面,点击注册又回到哪个页面,非常的麻烦,他是一个系统的方式,接下来,我就把我写出的登录闭环系统写成文章,方便未来查询和解惑。
登录页:
注册页:
忘记密码页:
登录后头部形态:
退出后头部形态:
以上就是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>
这里不一样的是,退出后有两个按钮,一个登录,一个注册,那么这里就多了两个交互,点击登录,回到登录页面,这里就是切换路由到登录页就可以了,一个注册,也是切换路由,但是,需要切换路由后回到的页面是忘记密码页,而不是登录页