一、导航守卫
全局守卫:前置守卫(在路由跳转之间进行判断) to:可以获取到你要跳转到哪个路由信息 from:可以获取到你从哪个路由而来的信息
next:放行函数 next()放行 next(path)放行到指定路由
放行的条件首先是要看登没登陆,所以要先识别token
从store获取token,在router.js文件中引入
import store from "@/store";
//全局守卫:前置守卫(在路由跳转之间进行判断)
router.beforeEach(async(to, from, next) => {
// to and from are both route objects. must call `next`.
//获取仓库中的token-----可以确定用户是登录了
let token = store.state.user.token;
let name = store.state.user.userInfo.name;
//用户登录了
if (token) {
//已经登录而且还想去登录------不行
if (to.path == "/login" || to.path == "/register") {
next("/home");
} else {
//已经登陆了,访问的是非登录与注册
//登录了且拥有用户信息放行
if (name) {
next();
} else {
//登陆了且没有用户信息
//在路由跳转之前获取用户信息且放行
try {
//获取用户信息
await store.dispatch("getUserInfo");
next();
} catch (error) {
//token失效从新登录
//清除token
await store.dispatch("logout");
// 回到登录页
this.$router.push("/login");
}
}
}
} else {
//未登录:不能去交易相关、不能去支付相关【pay|paysuccess】、不能去个人中心
//未登录去上面这些路由-----登录
let toPath = to.path;
if (toPath.includes("/trade") || toPath.includes("/pay") || toPath.includes("/center")) {
//把未登录的时候向去而没有去成的信息,存储于地址栏中【路由】
next("/login?redirect=" + toPath);
// console.log(toPath);
} else {
//去的不是上面这些路由(home|search|shopCart)---放行
next();
}
}
});
守卫的多种情况:
已经登录了:
- 已经登录了还想转跳到登录(不能放行)
- 已经登录了还想转跳到非登录和注册(不能放行)
- 登录了且拥有用户信息(放行)
- 登录了但是没有用户信息,进行判断,获取到了用户信息(放行),没获取到(放回到登录页)
未登录:
- 不能去交易支付等页面,还有个人中心
- 把未登录的时候向去而没有去成的信息,存储于地址栏中【路由】
next("/login?redirect=" + toPath);
,这一点很妙,多看看
二、交易页面的静态导入
这里很简单,就跳过了
给购物车里的结算添加转跳
<div class="sumbtn">
<router-link class="sum-btn" to="/trade">结算</router-link>
</div>
三、获取、渲染交易页数据
1. 写api
//获取用户地址信息
//URL:/api/user/userAddress/auth/findUserAddressList method:get
export const reqAddressInfo = () =>
requests({
url: "/user/userAddress/auth/findUserAddressList/",
method: "get"
});
//获取商品清单
//URL:/api/order/auth/trade method:get
export const reqOrderInfo = () =>
requests({
url: "/order/auth/trade",
method: "get"
});
2. 写仓库Trade.js
import { reqAddressInfo, reqOrderInfo } from "@/api";
const actions = {
//获取用户地址信息
async getUserAddress({ commit }) {
let result = await reqAddressInfo();
// console.log(result);
if (result.code == 200) {
commit("GETUSERADDRESS", result.data);
}
},
//获取商品清单数据
async getOrderInfo({ commit }) {
let result = await reqOrderInfo();
if (result.code == 200) {
commit("GETORDERINFO", result.data);
}
},
};
const mutations = {
GETUSERADDRESS(state, address) {
state.address = address;
},
GETORDERINFO(state, orderInfo) {
state.orderInfo = orderInfo;
},
};
const state = {
address: [],
orderInfo: {},
};
const getters = {};
export default {
actions,
mutations,
state,
getters,
};
3. 在支付Trade.html页面派发action
mounted() {
this.$store.dispatch("getUserAddress");
this.$store.dispatch("getOrderInfo");
},
4. 渲染数据
拿数据
computed: {
...mapState({
addressInfo: (state) => state.trade.address,
orderInfo: (state) => state.trade.orderInfo,
}),
放动态数据
利用排他思想来写默认地址
//修改默认地址
changeDefault(address, addressInfo) {
addressInfo.forEach((item) => {
//全部的为0
item.isDefault = 0;
});
address.isDefault = 1; //点击的为1
},
动态拿到选中的默认地址,然后渲染到提交订单上面的地址栏
将来提交订单最终选中地址
userDefaultAddress() {
//find:查找数组当中符合条件的元素返回,最为最终结果
return this.addressInfo.find((item) => item.isDefault == 1) || {};
},
5. 用户留言
data() {
return {
msg: "", //收集买家的留言信息
orderId: "", //订单号
};
},
写一个msg来存用户的留言,用v-model来绑定
<div class="bbs">
<h5>买家留言:</h5>
<textarea placeholder="建议留言前先与商家沟通确认" class="remarks-cont" v-model="msg" ></textarea>
</div>
四、提交订单
1. 导入静态组件
2. 配路由
path: "/pay",
component: () =>
import ("@/pagaes/Pay"),
meta: {
show: true,
},
3.提交订单分析
当你点击提交订单,应该告知服务器是谁买了,还有地址、电话、支付、金额、产品
在点击提交订单的按钮时,还需要向服务器发起一次请求【告知上面的信息】
4.写提交订单api
//提交订单的接口
//URL:/api/order/auth/submitOrder?tradeNo={tradeNo} method:post
export const reqSubmitOrder = (tradeNo, data) =>
requests({
url: `/order/auth/submitOrder?tradeNo=${tradeNo}`,
data,
method: "post"
});
在这就没用到vuex把信息存入仓库了,但是要怎么拿到api呢
在文件里引入
import { reqSubmitOrder } from "@/api";
但是这种方法只能用在一个文件,要是要引用多个,要写非常多个api引入,非常不模块化
这时候可以在main.js里统一接口api
//统一接口api文件夹里面全部请求函数
//统一引入
import * as API from "@/api";
这样就只用在文件里引用一次,就可以全部api了
5.拿数据
//提交订单
async submitOrder() {
//交易编码
let { tradeNo } = this.orderInfo;
//其余的六个参数
let data = {
consignee: this.userDefaultAddress.consignee, //最终收件人的名字
consigneeTel: this.userDefaultAddress.phoneNum, //最终收件人的手机号
deliveryAddress: this.userDefaultAddress.fullAddress, //收件人的地址
paymentWay: "ONLINE", //支付方式
orderComment: this.msg, //买家的留言信息
orderDetailList: this.orderInfo.detailArrayList, //商品清单
};
//需要带参数的:tradeNo
let result = await this.$API.reqSubmitOrder(tradeNo, data);
// console.log(result);
if (result.code == 200) {
(this.orderId = result.data),
//路由跳转 + 路由传递参数
this.$router.push("/pay?orderId=" + this.orderId);
} else {
alert(result.data);
}
},
五、获取订单支付信息
//获取支付信息
//URL:/api/payment/weixin/createNative/{orderId} GET
export const reqPayInfo = (orderId) =>
requests({
url: `/payment/weixin/createNative/${orderId}`,
method: "get"
});
//工作的时候:尽量别再生命周期函数中async|await
mounted() {
this.getPayInfo();
},
methods: {
async getPayInfo() {
let result = await this.$API.reqPayInfo(this.orderId);
// console.log(result);
//如果成功:组件当中存储支付信息
if (result.code == 200) {
this.payInfo = result.data;
} else {
alert(result.data);
}
},
}
六、微信支付
1. 引入ElementUI组件
import { Button, MessageBox } from "element-ui";
Vue.component(Button.name, Button);
//ElementUI注册组件的时候,还有一种写法,挂在原型上
Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$alert = MessageBox.alert;
2. 使用弹框组件
open() {
this.$alert(`<img src=${url} />`, "请你微信支付", {
dangerouslyUseHTMLString: true,
//中间布局
center: true,
//是否显示取消按钮
showCancelButton: true,
//取消按钮的文本内容
cancelButtonText: "支付遇见问题",
//确定按钮的文本
confirmButtonText: "已支付成功",
//右上角的叉子没了
showClose: false,
}
3.使用QRCODE
安装脚手架 npm i qrcode
//生成二维(地址)
let url = await QRCode.toDataURL(this.payInfo.codeUrl);
用模板字符串写入返回的二维码
//获取支付订单状态
//URL:/api/payment/weixin/queryPayStatus/{orderId} get
export const reqPayStatus = (orderId) =>
requests({
url: `/payment/weixin/queryPayStatus/${orderId}`,
method: 'get'
});
设置一个长询问定时器,每隔一秒查看用户是否支付
async getPayInfo() {
let result = await this.$API.reqPayInfo(this.orderId);
// console.log(result);
//如果成功:组件当中存储支付信息
if (result.code == 200) {
this.payInfo = result.data;
} else {
alert(result.data);
}
},
async open() {
//生成二维(地址)
let url = await QRCode.toDataURL(this.payInfo.codeUrl);
this.$alert(`<img src=${url} />`, "请你微信支付", {
dangerouslyUseHTMLString: true,
//中间布局
center: true,
//是否显示取消按钮
showCancelButton: true,
//取消按钮的文本内容
cancelButtonText: "支付遇见问题",
//确定按钮的文本
confirmButtonText: "已支付成功",
//右上角的叉子没了
showClose: false,
//关闭弹出框的配置值
beforeClose: (type, instance, done) => {
//type:区分取消|确定按钮
//instance:当前组件实例
//done:关闭弹出框的方法
if (type == "cancel") {
//点击支付遇到问题
alert("请联系管理员");
//清除定时器
clearInterval(this.timer);
this.timer = null;
//关闭弹出框
done();
} else {
// 点击支付成功
//判断是否真的支付了
//开发人员:为了自己方便,这里判断先不要了
// if (this.code == 200) {
//清除定时器
clearInterval(this.timer);
this.timer = null;
//关闭弹出框
done();
//跳转到下一路由
this.$router.push("/paysuccess");
}
},
});
//你需要知道支付成功|失败
//支付成功,路由的跳转,如果支付失败,提示信息
//定时器没有,开启一个新的定时器
if (!this.timer) {
this.timer = setInterval(async () => {
let result = await this.$API.reqPayStatus(this.orderId);
// console.log(result);
if (result.code == 200) {
//第一步:清除定时器
clearInterval(this.timer);
this.timer = null;
//保存支付成功返回的code
this.code = result.data;
//关闭弹出框
this.$msgbox.close();
//跳转到下一路由
this.$router.push("/paysuccess");
} else {
console.log('未支付直接跳转了');
}
}, 1000);
}
},
这里设置200是因为防止用户未支付就点击已支付成功
beforeClose: (type, instance, done)
type
:区分取消|确定按钮
instance
:当前组件实例
done
:关闭弹出框的方法
七、个人中心
1. 二级路由的创建
当有一种页面是有分侧边栏和主页面,切换不同页面时,可以使用二级路由导航
在侧边栏的文件夹里再分出来内容这一栏的小组件
声明式导航
还要设置路由组件出口
都设置完了会发现,当我们点击进来的时候,只有侧边栏,内容部分缺失,这时候需要重定向路由,让你你点进去页面,先显示主页面
{ //重定向一上来就展示myorder组件
path: '',
redirect: 'myorder'
}
2. 获取数据、展示数据
//获取个人中心的数据
//api/order/auth/{page}/{limit} get
export const reqMyOrderList = (page, limit) =>
requests({
url: `/order/auth/${page}/${limit}`,
method: 'get'
});
data() {
return {
//初始化参数
//当前第几页
page: 1,
//每一页展示数据个数
limit: 3,
//存储我的订单的数据
myOrder: {},
};
},
mounted() {
//获取我的订单的数据方法
this.getData();
},
methods: {
//获取我的订单的方法
async getData() {
//解构出参数
const { page, limit } = this;
let result = await this.$API.reqMyOrderList(page, limit);
if (result.code == 200) {
this.myOrder = result.data;
}
},
//获取当前点击那一页
getPageNo(page){
//修改组件响应式数据page
this.page = page;
this.getData();
}
},
渲染数据
当三个都是一样的时候,评论晒单只要一个,只要第一个数据,后面的都不要
v-if="index == 0"
3. 分页器
<Pagination
:pageNo="page"
:pageSize="limit"
:total="myOrder.total"
:continues="5"
@getPageNo="getPageNo"
/>
methods: {
//获取我的订单的方法
async getData() {
//解构出参数
const { page, limit } = this;
let result = await this.$API.reqMyOrderList(page, limit);
if (result.code == 200) {
this.myOrder = result.data;
}
},
//获取当前点击那一页
getPageNo(page){
//修改组件响应式数据page
this.page = page;
this.getData();
}
},
八、路由独享与组件内守卫
1. 路由独享守卫
想一想有一种情况,当用户登陆之后,他直接在地址栏输入到支付界面,这肯定是不行的,那要怎么实现当用户登录之后,也只能通过正常的点击商品购买才能到支付界面和支付成功界面
{
path: "/trade",
component: () =>
import("@/pagaes/Trade"),
meta: {
show: true,
},
// 路由独享守卫
beforeEnter: (to, from, next) => {
if (from.path == '/shopcart') {
next()
} else {
next(false)
}
}
},
next(false)意思为:从哪来,回哪去
支付页面也要设置
{
path: "/pay",
component: () =>
import("@/pagaes/Pay"),
meta: {
show: true,
},
// 路由独享守卫
beforeEnter: (to, from, next) => {
if (from.path == '/trade') {
next()
} else {
next(false)
}
}
},
2. 组件内守卫
也可以直接写在组件里
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
if (from.path == "/pay") {
next();
} else {
next(false);
}
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
console.log("12313131311313");
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
next();
},
九、图片懒加载
类似于大型的淘宝商城、京东等网页,设计大量的商品图片信息,如果我们使页面包含的所有图片一次性加载完成,那用户体验很差。
目前流行的做法是滚动动态加载,也就是懒加载,显示在屏幕之外的图片默认不加载,随着页面的滚动,图片进入了显示的范围,则触发图片的加载显示
这样做的好处,一是页面加载速度快(浏览器进度条和加载转圈很快就结束了,这样用户的体验也比较好),而是节省流量,因为不可能每一个用户会把页面从上到下滚动完
npm i vue-lazyload
引入插件
import VueLazyload from 'vue-lazyload'
安装插件
// or with options
const loadimage = require('./assets/loading.gif')
const errorimage = require('./assets/error.gif')
配置对象
Vue.use(VueLazyload, {
preLoad: 1.3,
error: errorimage,
loading: loadimage,
attempt: 1
})
加入自定义指令
<ul>
<li v-for="img in list">
<img v-lazy="img.src" >
</li>
</ul>
十、路由懒加载
当打包构建应用时,javascript包会变得非常大,影响页面加载
如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
例子
{
path: "/paySuccess",
component: () =>
import("@/pagaes/PaySuccess"),
meta: {
show: true,
},
},
十一、vee-validate表单验证插件
npm i vee-validate
引入
import Vue from 'vue';
import VeeValidate from 'vee-validate';
使用
Vue.vue(VeeValidate)
在入口文件引一次插件
import ‘@/plugins/validate’;
表单验证
//表单验证
VeeValidate.Validator.localize("zh_CN", {
messages: {
...zh_CN.messages,
is: (field) => `${field}必须与密码相同`, // 修改内置规则的 message,让确认密码和密码相同
},
attributes: {
phone: "手机号",
code: "验证码",
password: "密码",
password1: "确认密码",
agree:'协议'
},
});
自定义校验规则
//自定义校验规则
VeeValidate.Validator.extend("tongyi", {
validate: (value) => {
return value;
},
getMessage: (field) => field + "必须同意",
});
使用
<div class="content">
<label>手机号:</label>
<input
placeholder="请输入你的手机号"
v-model="phone"
name="phone"
v-validate="{ required: true, regex: /^1\d{10}$/ }"
:class="{ invalid: errors.has('phone') }"
/>
<span class="error-msg">{{ errors.first("phone") }}</span>
</div>
能确定你是否全部验证成功,返回值是布尔值
const success = await this.$validator.validateAll();