VUE复习(购物平台项目)持续更新...
一.明确功能模块
二. 路由配置 - 一级路由
但凡是单个页面,独立展示的,都是一级路由
路由设计:
登录页
首页架子
首页 - 二级
分类页 - 二级
购物车 - 二级
我的 - 二级
搜索页
搜索列表页
商品详情页
结算支付页
我的订单页
router/index.js
配置一级路由,新建对应的页面文件
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/views/layout'
import Search from '@/views/search'
import SearchList from '@/views/search/list'
import ProDetail from '@/views/prodetail'
import Login from '@/views/login'
import Pay from '@/views/pay'
import MyOrder from '@/views/myorder'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{
path: '/login',
component: Login
},
{
path: '/',
component: Layout
},
{
path: '/search',
component: Search
},
{
path: '/searchlist',
component: SearchList
},
{
path: '/prodetail/:id',
component: ProDetail
},
{
path: '/pay',
component: Pay
},
{
path: '/myorder',
component: MyOrder
}
]
})
export default router
三. 路由配置-tabbar标签页
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/tabbar
vant-ui.js
引入组件
import { Tabbar, TabbarItem } from 'vant'
Vue.use(Tabbar)
Vue.use(TabbarItem)
layout.vue
复制官方代码
修改显示文本及显示的图标
配置高亮颜色
<template>
<div>
<!-- 二级路由出口 -->
<van-tabbar active-color="#ee0a24" inactive-color="#000">
<van-tabbar-item icon="wap-home-o">首页</van-tabbar-item>
<van-tabbar-item icon="apps-o">分类页</van-tabbar-item>
<van-tabbar-item icon="shopping-cart-o">购物车</van-tabbar-item>
<van-tabbar-item icon="user-o">我的</van-tabbar-item>
</van-tabbar>
</div>
</template>
四. 路由配置 - 二级路由
router/index.js
配置二级路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/views/layout'
import Search from '@/views/search'
import SearchList from '@/views/search/list'
import ProDetail from '@/views/prodetail'
import Login from '@/views/login'
import Pay from '@/views/pay'
import MyOrder from '@/views/myorder'
import Home from '@/views/layout/home'
import Category from '@/views/layout/category'
import Cart from '@/views/layout/cart'
import User from '@/views/layout/user'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{
path: '/login',
component: Login
},
{
path: '/',
component: Layout,
redirect: '/home',
children: [
{
path: 'home',
component: Home
},
{
path: 'category',
component: Category
},
{
path: 'cart',
component: Cart
},
{
path: 'user',
component: User
}
]
},
{
path: '/search',
component: Search
},
{
path: '/searchlist',
component: SearchList
},
{
path: '/prodetail/:id',
component: ProDetail
},
{
path: '/pay',
component: Pay
},
{
path: '/myorder',
component: MyOrder
}
]
})
export default router
准备对应的组件文件
layout/home.vue
layout/category.vue
layout/cart.vue
layout/user.vue
layout.vue
配置路由出口, 配置 tabbar
<template>
<div>
<router-view></router-view>
<van-tabbar route active-color="#ee0a24" inactive-color="#000">
<van-tabbar-item to="/home" icon="wap-home-o">首页</van-tabbar-item>
<van-tabbar-item to="/category" icon="apps-o">分类页</van-tabbar-item>
<van-tabbar-item to="/cart" icon="shopping-cart-o">购物车</van-tabbar-item>
<van-tabbar-item to="/user" icon="user-o">我的</van-tabbar-item>
</van-tabbar>
</div>
</template>
五.登录页静态布局
使用组件
van-nav-bar
vant-ui.js
注册
import { NavBar } from 'vant'
Vue.use(NavBar)
Login.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>
添加通用样式
styles/common.less
设置导航条,返回箭头颜色
// 设置导航条 返回箭头 颜色
.van-nav-bar {
.van-icon-arrow-left {
color: #333;
}
}
六. request模块 - axios封装
接口文档:https://apifox.com/apidoc/shared-12ab6b18-adc2-444c-ad11-0e60f5693f66/doc-2221080
演示地址:http://cba.itlike.com/public/mweb/#/
基地址:http://cba.itlike.com/public/index.php?s=/api/
使用 axios 来请求后端接口, 一般都会对 axios 进行一些配置 (比如: 配置基础地址,请求响应拦截器等等)
一般项目开发中, 都会对 axios 进行基本的二次封装, 单独封装到一个模块中, 便于使用
目标:将 axios 请求方法,封装到 request 模块
安装 axios
npm i axios
新建
utils/request.js
封装 axios 模块利用 axios.create 创建一个自定义的 axios 来使用
/* 封装axios用于发送请求 */
import axios from 'axios'
// 创建一个新的axios实例
const request = axios.create({
baseURL: 'http://cba.itlike.com/public/index.php?s=/api/',
timeout: 5000
})
// 添加请求拦截器
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
获取图形验证码,请求测试
import request from '@/utils/request'
export default {
name: 'LoginPage',
async created () {
const res = await request.get('/captcha/image')
console.log(res)
}
}
七. 图形验证码功能
准备数据,获取图形验证码后存储图片路径,存储图片唯一标识
async created () {
this.getPicCode()
},
data () {
return {
picUrl: '',
picKey: ''
}
},
methods: {
// 获取图形验证码
async getPicCode () {
const { data: { base64, key } } = await request.get('/captcha/image')
this.picUrl = base64
this.picKey = key
}
}
动态渲染图形验证码,并且点击时要重新刷新验证码
<img v-if="picUrl" :src="picUrl" @click="getPicCode">
16. toast 轻提示
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/toast
两种使用方式
导入调用 ( 组件内 或 非组件中均可 )
import { Toast } from 'vant';
Toast('提示内容');
通过this直接调用 ( 组件内 )
main.js 注册绑定到原型
import { Toast } from 'vant';
Vue.use(Toast)
this.$toast('提示内容')
八. 短信验证倒计时功能
(一) 倒计时基础效果
准备 data 数据
data () {
return {
totalSecond: 60, // 总秒数
second: 60, // 倒计时的秒数
timer: null // 定时器 id
}
},
给按钮注册点击事件
<button @click="getCode">
{{ second === totalSecond ? '获取验证码' : second + `秒后重新发送`}}
</button>
开启倒计时时
async getCode () {
if (!this.timer && this.second === this.totalSecond) {
// 开启倒计时
this.timer = setInterval(() => {
this.second--
if (this.second < 1) {
clearInterval(this.timer)
this.timer = null
this.second = this.totalSecond
}
}, 1000)
// 发送请求,获取验证码
this.$toast('发送成功,请注意查收')
}
}
离开页面销毁定时器
destroyed () {
clearInterval(this.timer)
}
(二) 验证码请求校验处理
输入框 v-model 绑定变量
data () {
return {
mobile: '', // 手机号
picCode: '' // 图形验证码
}
},
<input v-model="mobile" class="inp" maxlength="11" placeholder="请输入手机号码" type="text">
<input v-model="picCode" class="inp" maxlength="5" placeholder="请输入图形验证码" type="text">
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
},
请求倒计时前进行校验
// 获取短信验证码
async getCode () {
if (!this.validFn()) {
return
}
...
}
(三) 封装接口,请求获取验证码
封装接口
api/login.js
// 获取短信验证码
export const getMsgCode = (captchaCode, captchaKey, mobile) => {
return request.post('/captcha/sendSmsCaptcha', {
form: {
captchaCode,
captchaKey,
mobile
}
})
}
调用接口,添加提示
// 获取短信验证码
async getCode () {
if (!this.validFn()) {
return
}
if (!this.timer && this.second === this.totalSecond) {
// 发送请求,获取验证码
await getMsgCode(this.picCode, this.picKey, this.mobile)
this.$toast('发送成功,请注意查收')
// 开启倒计时
...
}
}
九. 封装api接口 - 登录功能
api/login.js
提供登录 Api 函数
// 验证码登录
export const codeLogin = (mobile, smsCode) => {
return request.post('/passport/login', {
form: {
isParty: false,
mobile,
partyData: {},
smsCode
}
})
}
login/index.vue
登录功能
<input class="inp" v-model="msgCode" maxlength="6" placeholder="请输入短信验证码" type="text">
<div class="login-btn" @click="login">登录</div>
data () {
return {
msgCode: '',
}
},
methods: {
async login () {
if (!this.validFn()) {
return
}
if (!/^\d{6}$/.test(this.msgCode)) {
this.$toast('请输入正确的手机验证码')
return
}
await codeLogin(this.mobile, this.msgCode)
this.$router.push('/')
this.$toast('登录成功')
}
}