登录页静态布局
1.先重置默认样式
找到styles/common.less文件,没有就新建
// 重置默认样式
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
// 文字溢出省略号
.text-ellipsis-2 {
overflow: hidden;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
}
.van-nav-bar {
.van-nav-bar__arrow{
color: #333;
}
}
2.在main.js 中导入应用
import '@/styles/common.less'
3.导入素材
可以另起一个文件夹在终端输入,这个命令,找到里面的src/assets,把里面的文件全部复制过来即可
git clone https://gitee.com/yangLLHGS/vue2-shopping.git
4.在utils/vant-ui.js下导入使用
import { NavBar } from 'vant'
Vue.use(NavBar)
5.复制下面的基本静态布局到views/login/index.vue
<template>
<div class="login">
<van-nav-bar title="会员登录" left-arrow @click-left="$router.go(-1)" />
<div class="container">
<div class="title">
<h3>手机号登录</h3>
<p>未注册的手机号登录后将自动注册</p>
</div>
<div class="form">
<div class="form-item">
<input class="inp" maxlength="11" placeholder="请输入手机号码" type="text">
</div>
<div class="form-item">
<input class="inp" maxlength="5" placeholder="请输入图形验证码" type="text">
<img src="@/assets/code.png" alt="">
</div>
<div class="form-item">
<input class="inp" placeholder="请输入短信验证码" type="text">
<button>获取验证码</button>
</div>
</div>
<div class="login-btn">登录</div>
</div>
</div>
</template>
<script>
export default {
name: 'LoginPage'
}
</script>
<style lang="less" scoped>
.container {
padding: 49px 29px;
.title {
margin-bottom: 20px;
h3 {
font-size: 26px;
font-weight: normal;
}
p {
line-height: 40px;
font-size: 14px;
color: #b8b8b8;
}
}
.form-item {
border-bottom: 1px solid #f3f1f2;
padding: 8px;
margin-bottom: 14px;
display: flex;
align-items: center;
.inp {
display: block;
border: none;
outline: none;
height: 32px;
font-size: 14px;
flex: 1;
}
img {
width: 94px;
height: 31px;
}
button {
height: 31px;
border: none;
font-size: 13px;
color: #cea26a;
background-color: transparent;
padding-right: 9px;
}
}
.login-btn {
width: 100%;
height: 42px;
margin-top: 39px;
background: linear-gradient(90deg,#ecb53c,#ff9211);
color: #fff;
border-radius: 39px;
box-shadow: 0 10px 20px 0 rgba(0,0,0,.1);
letter-spacing: 2px;
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
requests模块
api的接口文档(你可以点击查看)wiki - 智慧商城-实战项目 (apifox.com)https://apifox.com/apidoc/shared-12ab6b18-adc2-444c-ad11-0e60f5693f66/doc-2221080
axios封装
先去下载axios,npm install axios,报错的话可以切换成这个npm install axios --save --legacy-peer-deps
然后新建 utils/request.js
封装 axios 模块
/* 封装axios用于发送请求 */
import axios from 'axios'
// 约定一个统一键名
const INFO_KEY = 'hm_shopping_info'
// 创建一个新的axios实例
const request = axios.create({
baseURL: 'http://cba.itlike.com/public/index.php?s=/api/',
timeout: 5000,
headers: {
platform: 'H5',
'Access-Token': localStorage.getItem(INFO_KEY) || '' // 设置请求头,携带token
}
})
// 添加请求拦截器
request.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
// 添加响应拦截器
request.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response.data
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error)
})
export default request
图形验证码功能
此处的功能具体编写请见下方的api接口,因为我们是需要将这个接口封装到appi里面的
api接口模块
所以要将所有请求封装在api模块里,统一管理
好处是
新建 api/login.js
提供获取图形验证码 Api 函数
import request from '@/utils/request'
// 获取图形验证码
export const getPicCode = () => {
return request.get('/captcha/image')
}
在login/index.vue
页面中的script标签调用测试
import { getPicCode } from '@/api/login'
data () {
return {
picCode: '', // 用户输入的图形验证码
picUrl: '', // 存储请求渲染的图片地址
picKey: '', // 图形验证码的唯一标识
}
},
async created () {
this.getPicCode()
},
// 获取图片验证码
async getPicCode () {
const { data: { base64, key } } = await getPicCode()
this.picUrl = base64
this.picKey = key // 将来验证需要携带
// this.$toast('图片验证码已更新')
},
之后在模板中,也就是<template>标签 改成如下,添加了点击事件,src改成动态的:src,v-if是为了防止图片加载的时候不会闪,因为一开始的图片默认是空的
如果这里能成功拿到图片验证码,并且点击之后会切换那么这个图片验证码功能完成了
Toast轻提示
所以,我们先在utils/vant-ui.js导入
import { Toast } from 'vant'
Vue.use(Toast)
然后直接在我们刚才写的getPrcCode()这个方法去调用,这样
有这种效果表明Toast轻提示使用成功
短信验证功能
1.首先我们需要封装请求验证码的接口 api/login.js
// 获取短信验证码
export const getMsgCode = (captchaCode, captchaKey, mobile) => {
return request.post('/captcha/sendSmsCaptcha', {
form: {
captchaCode,
captchaKey,
mobile
}
})
}
注意,此时模板要进行v-model绑定
2.然后我们需要 准备data数据
data () {
return {
totalSecond: 60, // 总秒数
second: 60, // 倒计时的秒数
timer: null // 定时器 id
}
},
3.给按钮注册点击事件
<button @click="getCode">
{{ second === totalSecond ? '获取验证码' : second + `秒后重新发送`}}
</button>
4. methods中封装校验方法
// 校验输入框内容
validFn () {
if (!/^1[3-9]\d{9}$/.test(this.mobile)) {
this.$toast('请输入正确的手机号')
return false
}
if (!/^\w{4}$/.test(this.picCode)) {
this.$toast('请输入正确的图形验证码')
return false
}
return true
},
5.之后开启倒计时去获取验证码
不过验证之前是需要校验的
// 获取短信验证码
async getCode () {
// 如果输入的手机号和图像验证码正确才往下走
if (!this.validFn()) {
return// 不合法就不往下走
}
// 当前定时器没开且秒数相等
if (!this.timer && this.second === this.totalSecond) {
// 发送请求
// const res = await getMsgCode(this.picCode, this.picKey, this.mobile)
await getMsgCode(this.picCode, this.picKey, this.mobile)
this.$toast('发送成功,请注意查收')
// 对于非200的状态码,要跑出一个Promise的错误
// console.log(res)
this.timer = setInterval(() => {
this.second--
if (this.second <= 0) {
clearInterval(this.timer)
this.timer = null // 清除定时器
this.second = this.totalSecond // 重新赋值
}
}, 1000)
this.$toast('短信验证码已发送')
}
},
6.离开页面是也要记得清除定时器
destroyed () {
// 离开页面时清除定时器
clearInterval(this.timer)
}
登录功能
登录功能先提供api函数
// 登录
export const CodeLogin = (mobile, smsCode) => {
return request.post('/passport/login', {
form: {
isParty: false,
mobile,
partyData: {},
smsCode
}
})
}
然后在页面去调用
async login () {
// 校验手机号和短信验证码是否合法
if (!this.validFn()) {
return
}
if (!/^\d{6}$/.test(this.msgCode)) {
this.$toast('请输入正确的短信验证码')
return
}
// 发送请求
const res = await CodeLogin(this.mobile, this.msgCode)
// console.log(res)
this.$toast('登录成功')
// 登录成功后跳转到个人中心
// 得先判断是否有回跳地址
const url = this.$route.query.backUrl || '/'
this.$router.replace(url)
}
响应拦截器统一处理错误提示
但是忘记请求失败咋办,他会有状态码返回过来的,那么
在响应拦截器里面填写
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对相应数据做点什么
const res = response.data
// console.log(res)
if (res.status !== 200) {
Toast(res.message) // 这个要用全局导入,因为当前的this不是组件
return Promise.reject(res.message)
} else {
Toast.clear() // 清除loading
}
// 对响应数据做点什么
return res
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error)
})
登录权证信息
为啥要有这个,你登录之后软件肯定要存储你的登录信息,之后每次获取页面都需要去从vuex读取,判断token是否合法
Storage存储模块---vuex持久化处理
vuex的数据刷新之后会消失的,那么用户的信息是需要保存到本地里面的,所以新建utils/storage.js,需要持久化保存数据的则在这个文件去写方法
// 约定一个统一键名
const INFO_KEY = 'hm_shopping_info'
// 获取个人信息
export const getInfo = () => {
const defaultObj = { token: '', userId: '' }
const res = localStorage.getItem(INFO_KEY)
return res ? JSON.parse(res) : defaultObj // 有值赋值,没有值默认值
}
// 设置个人信息
export const setInfo = (info) => {
localStorage.setItem(INFO_KEY, JSON.stringify(info))
}
// 移除个人信息
export const removeInfo = () => {
localStorage.removeItem(INFO_KEY)
}
然后在下方文件=创建如下文件
之后在user.js中,导入刚才编写的持久化存储
import { getInfo, setInfo } from '@/utils/storage'
export default {
namespaced: true,
state () {
return {
userInfo: getInfo()
}
},
mutations: {
// mutations中任何一个放法的第一个参数就是state
setUserInfo (state, obj) {
// 修改state中的数据
state.userInfo = obj
// 修改本地存储的值
setInfo(obj)
}
},
actions: {
logout (context) {
// 个人信息重置
context.commit('setUserInfo', {})
// 购物车信息重置(跨模块调用) ---> 开启全局模式
context.commit('cart/setCartList', [], { root: true })
}
},
getters: {}
}
然后挂载在index.js上
最后在登录页面去调用这个vuex的user模块的方法
添加loading效果
那么我们在utils/request.js中去请求拦截器添加loading效果
因为这是网络原因造成的,肯定要走axios,所以在这一步添加
之后在响应拦截器清除loading效果
至此我们的登录功能这个大模块就正式完成了。
接下来我会更新其他页面的功能,大家敬请期待~~~~