本文将在vue-admin-template模板基础上完成搭建注册与登录页面。文末处有完整的前端源码下载。
环境准备
├── build # 构建相关 ├── mock # 项目mock 模拟数据 ├── plop-templates # 基本模板 ├── public # 静态资源 │ │── favicon.ico # favicon图标 │ └── index.html # html模板 ├── src # 源代码 │ ├── api # 所有请求 │ ├── assets # 主题 字体等静态资源 │ ├── components # 全局公用组件 │ ├── directive # 全局指令 │ ├── filters # 全局 filter │ ├── icons # 项目所有 svg icons │ ├── lang # 国际化 language │ ├── layout # 全局 layout │ ├── router # 路由 │ ├── store # 全局 store管理 │ ├── styles # 全局样式 │ ├── utils # 全局公用方法 │ ├── vendor # 公用vendor │ ├── views # views 所有页面 │ ├── App.vue # 入口页面 │ ├── main.js # 入口文件 加载组件 初始化等 │ └── permission.js # 权限管理 ├── tests # 测试 ├── .env.xxx # 环境变量配置 ├── .eslintrc.js # eslint 配置项 ├── .babelrc # babel-loader 配置 ├── .travis.yml # 自动化CI配置 ├── vue.config.js # vue-cli 配置 ├── postcss.config.js # postcss 配置 └── package.json # package.json
# 安装依赖 npm install # 本地开发 启动项目 npm run dev
<template> <div class="register-container"> <article class="header"> <header> <el-avatar icon="el-icon-user-solid" shape="circle" /> <span class="login"> <em class="bold">已有账号?</em> <a href="/login"> <el-button type="primary" size="mini">登录</el-button> </a> </span> </header> </article> <section> <el-form ref="ruleForm" :model="ruleForm" :rules="rules" label-width="100px" autocomplete="off" hide-required-asterisk="true" size="medium" > <div style="padding-top: 10px"> <el-form-item label="邮箱" prop="email"> <el-col :span="10"> <el-input v-model="ruleForm.email" placeholder="输入邮箱并点击发送验证码" /> </el-col> <el-button :loading="codeLoading" :disabled="isDisable" size="small" round @click="sendMsg" >发送验证码</el-button> <span class="status">{{ statusMsg }}</span> </el-form-item> <el-form-item label="验证码" prop="code"> <el-col :span="10"> <el-input v-model="ruleForm.code" maxlength="6" placeholder="请登录邮箱接收验证码" /> </el-col> </el-form-item> <el-form-item label="密码" prop="pwd"> <el-col :span="10"> <el-input v-model="ruleForm.pwd" type="password" /> </el-col> </el-form-item> <el-form-item label="确认密码" prop="cpwd"> <el-col :span="10"> <el-input v-model="ruleForm.cpwd" type="password" /> </el-col> </el-form-item> <el-form-item> <el-button type="primary" style="width: 40%" @click="register" >注册</el-button> </el-form-item> </div> </el-form> </section> <div class="error">{{ error }}</div> </div> </template> <script> import { getEmailCode, register } from '@/api/register' import { encrypt } from '@/utils/rsaEncrypt' export default { name: 'Register', data() { return { statusMsg: '', error: '', isDisable: false, codeLoading: false, ruleForm: { email: '', code: '', pwd: '', cpwd: '' }, rules: { email: [{ required: true, type: 'email', message: '请输入邮箱', trigger: 'blur' }], code: [{ required: true, type: 'string', message: '请输入验证码', trigger: 'blur' }], pwd: [{ required: true, message: '创建密码', trigger: 'blur' }, { pattern: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,20}$/, message: '密码必须同时包含数字与字母,且长度为 8-20位' }], cpwd: [{ required: true, message: '确认密码', trigger: 'blur' }, { validator: (rule, value, callback) => { if (value === '') { callback(new Error('请再次输入密码')) } else if (value !== this.ruleForm.pwd) { callback(new Error('两次输入密码不一致')) } else { callback() } }, trigger: 'blur' }] } } }, methods: { sendMsg: function() { const self = this let emailPass let timerid if (timerid) { return false } self.statusMsg = '' this.$refs['ruleForm'].validateField('email', (valid) => { emailPass = valid }) // 向后台API验证码发送 if (!emailPass) { self.codeLoading = true self.statusMsg = '验证码发送中...' getEmailCode(self.ruleForm.email).then(res => { this.$message({ showClose: true, message: '发送成功,验证码有效期5分钟', type: 'success' }) let count = 60 self.ruleForm.code = '' self.codeLoading = false self.isDisable = true self.statusMsg = `验证码已发送,${count--}秒后重新发送` timerid = window.setInterval(function() { self.statusMsg = `验证码已发送,${count--}秒后重新发送` if (count <= 0) { window.clearInterval(timerid) self.isDisable = false self.statusMsg = '' } }, 1000) }).catch(err => { this.isDisable = false this.statusMsg = '' this.codeLoading = false console.log(err.response.data.message) }) } }, // 用户注册 register: function() { this.$refs['ruleForm'].validate((valid) => { if (valid) { const user = { email: this.ruleForm.email, code: this.ruleForm.code, password: encrypt(this.ruleForm.pwd) } register(this.ruleForm.code, user).then(res => { this.$message({ showClose: true, message: '注册成功,正在跳转到登录界面...', type: 'success' }) setTimeout(() => { this.$router.push('/') }, 2000) }).catch(err => { console.log(err.response.data.message) }) } }) } } } </script> <style lang="scss"> /* 修复input 背景不协调 和光标变色 */ /* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */ $bg: #283443; $light_gray: #fff; $cursor: #fff; @supports (-webkit-mask: none) and (not (cater-color: $cursor)) { .register-container .el-input input { color: $cursor; } } /* reset element-ui css */ .register-container { .el-input { display: inline-block; height: 47px; width: 95%; input { background: rgba(0, 0, 0, 0.1); border-radius: 5px; border: 1px solid rgba(255, 255, 255, 0.1); -webkit-appearance: none; padding: 12px 5px 12px 15px; color: $light_gray; height: 47px; caret-color: $cursor; &:-webkit-autofill { box-shadow: 0 0 0px 1000px $bg inset !important; -webkit-text-fill-color: $cursor !important; } } } .el-form-item { label { font-style: normal; font-size: 12px; color: $light_gray; } } } </style> <style lang="scss" scoped> $bg: #2d3a4b; $dark_gray: #889aa4; $light_gray: #eee; .register-container { min-height: 100%; width: 100%; background-color: $bg; overflow: hidden; .header { border-bottom: 2px solid rgb(235, 232, 232); min-width: 980px; color: #666; header { margin: 0 auto; padding: 10px 0; width: 980px; .login { float: right; } .bold { font-style: normal; color: $light_gray; } } } > section { margin: 0 auto 30px; padding-top: 30px; width: 980px; min-height: 300px; padding-right: 100px; box-sizing: border-box; .status { font-size: 12px; margin-left: 20px; color: #e6a23c; } .error { color: red; } } .tips { float: right; font-size: 14px; color: #fff; margin-bottom: 10px; span { &:first-of-type { margin-right: 16px; } } } } </style> <style scoped> /* 修改验证器样式 */ /deep/ .el-form-item.is-error .el-input__inner { border-color: #889aa4; } /deep/ .el-form-item.is-error .el-input__validateIcon { color: #889aa4; } /deep/ .el-form-item__error { color: #e6a23c; } </style>
import request from '@/utils/request' //在注册时根据邮箱名获取验证码 export function getEmailCode(email) { return request({ url: '/vue-admin-template/register/getEmailCode', method: 'post', email }) } //提交注册 export function register(code, data) { return request({ url: '/vue-admin-template/register?code=' + code, method: 'post', data }) }
// mock api接口数据 module.exports = [ { url: '/vue-admin-template/register/getEmailCode', type: 'post', response: _ => { return { code: 20000, data: { message: 'success' } } } }, { url: '/vue-admin-template/register\.*', type: 'post', response: _ => { return { code: 20000, data: { message: 'success' } } } } ]
<template> <div class="login-container"> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left"> <div class="title-container"> <h3 class="title">欢迎使用</h3> </div> <el-form-item prop="username"> <span class="svg-container"> <svg-icon icon-class="user" /> </span> <el-input ref="username" v-model="loginForm.username" placeholder="邮箱" name="username" type="text" tabindex="1" auto-complete="on" /> </el-form-item> <el-form-item prop="password"> <span class="svg-container"> <svg-icon icon-class="password" /> </span> <el-input :key="passwordType" ref="password" v-model="loginForm.password" :type="passwordType" placeholder="密码" name="password" tabindex="2" auto-complete="on" @keyup.enter.native="handleLogin" /> <span class="show-pwd" @click="showPwd"> <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" /> </span> </el-form-item> <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button> <p class="tips"> <a href="/register" type="primary">还没有帐号?立即注册</a> </p> </el-form> </div> </template> <script> export default { name: 'Login', data() { return { loginForm: { username: '', password: '' }, loginRules: { username: [{ type: 'email', required: true, trigger: 'blur', message: '请输入邮箱' }], password: [{ required: true, message: '创建密码', trigger: 'blur' }, { pattern: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,20}$/, message: '密码必须同时包含数字与字母,且长度为 8-20位' }] }, loading: false, passwordType: 'password', redirect: undefined } }, watch: { $route: { handler: function(route) { this.redirect = route.query && route.query.redirect }, immediate: true } }, methods: { showPwd() { if (this.passwordType === 'password') { this.passwordType = '' } else { this.passwordType = 'password' } this.$nextTick(() => { this.$refs.password.focus() }) }, handleLogin() { this.$refs.loginForm.validate(valid => { if (valid) { this.loading = true this.$store.dispatch('user/login', this.loginForm).then(() => { this.$router.push({ path: this.redirect || '/' }) this.loading = false }).catch(() => { this.loading = false }) } else { console.log('error submit!!') return false } }) } } } </script> <style lang="scss"> /* 修复input 背景不协调 和光标变色 */ /* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */ $bg:#283443; $light_gray:#fff; $cursor: #fff; @supports (-webkit-mask: none) and (not (cater-color: $cursor)) { .login-container .el-input input { color: $cursor; } } /* reset element-ui css */ .login-container { .el-input { display: inline-block; height: 47px; width: 85%; input { background: transparent; border: 0px; -webkit-appearance: none; border-radius: 0px; padding: 12px 5px 12px 15px; color: $light_gray; height: 47px; caret-color: $cursor; &:-webkit-autofill { box-shadow: 0 0 0px 1000px $bg inset !important; -webkit-text-fill-color: $cursor !important; } } } .el-form-item { border: 1px solid rgba(255, 255, 255, 0.1); background: rgba(0, 0, 0, 0.1); border-radius: 5px; color: #454545; } } </style> <style lang="scss" scoped> $bg:#2d3a4b; $dark_gray:#889aa4; $light_gray:#eee; .login-container { min-height: 100%; width: 100%; background-color: $bg; overflow: hidden; .login-form { position: relative; width: 520px; max-width: 100%; padding: 160px 35px 0; margin: 0 auto; overflow: hidden; } .tips { float: right; font-size: 14px; color: #fff; margin-bottom: 10px; span { &:first-of-type { margin-right: 16px; } } } .svg-container { padding: 6px 5px 6px 15px; color: $dark_gray; vertical-align: middle; width: 30px; display: inline-block; } .title-container { position: relative; .title { font-size: 26px; color: $light_gray; margin: 0px auto 40px auto; text-align: center; font-weight: bold; } } .show-pwd { position: absolute; right: 10px; top: 7px; font-size: 16px; color: $dark_gray; cursor: pointer; user-select: none; } } </style> <style scoped> /* 修改验证器样式 */ /deep/ .el-form-item.is-error .el-input__inner { border-color: #889aa4; } /deep/ .el-form-item.is-error .el-input__validateIcon { color: #889aa4; } /deep/ .el-form-item__error { color: #e6a23c; } </style>