day-061-sixty-one-20230504-vue2项目-跳转拦截-重定向并返回前一页-使用vuex调用接口-全选与全不选-总价计算
vue2项目
跳转拦截
设置跳转拦截,比如在用户没token时,不能进入具体详情页,而是进入登录页进行登录。
跳转拦截具体思路
- 前端和后端约定好一个返回码,如resultCode为416,那么前端就跳转到登录页。
- 前端根据这返回码在axios的响应拦截器axios.interceptors.response.use()钩子函数中进行处理,如跳转到首页或登录面之类的操作。
- 前端发起一个需要token的请求。
- 后端收到前端的请求,校验token是否存在或合法。
- 如果存在并合法,正常返回数据。
- 如果token不存在或不合法,返回resultCode并为416。
- 如果后端返回resultCode并为416,就会触发axios的响应拦截器中的钩子函数,重定向到登录页。
跳转拦截实际步骤
- 在axios实例对象或axios原型上设置响应拦截器
-
/src/http/http.js
import axios from "axios"; import router from '../router/index.js' // 添加响应拦截器 axios.interceptors.response.use( (response) => { // 2xx 范围内的状态码都会触发该函数。 // 对响应数据做点什么 // 后端的resultCode为416时,要求重新登录。 let { message, resultCode } = response.data; if (+resultCode === 416) { router.push(`/LoginView`) } return response.data; }, function (error) { return Promise.reject(error); } ); export default axios;
-
重定向并返回前一页
因重定向跳转登录页并登录成功后,在登录页中跳转返回前一页
重定向并返回具体思路
- 在在axios的响应拦截器axios.interceptors.response.use()钩子函数中跳转到登录页时,给路由加一个字段表示要重返前一页,如needback为true。
- 在登录页中,设置登录成功后,如果
this.$route?.query?.needback
存在,就返回到前一页。
重定向并返回实际步骤
-
在axios实例对象或axios原型上的响应拦截器进行跳转时,在需要的路由中加上
?needback=true
。-
/src/http/http.js
import axios from "axios"; import router from '../router/index.js' // 添加响应拦截器 axios.interceptors.response.use( (response) => { // 2xx 范围内的状态码都会触发该函数。 // 对响应数据做点什么 // 后端的resultCode为416时,要求重新登录。 let { message, resultCode } = response.data; if (+resultCode === 416) { router.push(`/LoginView?needback=true`) } return response.data; }, function (error) { return Promise.reject(error); } ); export default axios;
-
-
在登录成功后,跳转的页面中根据
this.$route?.query?.needback
决定是返回前一页还是首页。-
/src/views/LoginView.vue
<script> import { Notify } from "vant"; export default { components: { Verify, }, data() { return { username: "", password: "", flag: true, verifyFlag: false, }; }, methods: { onSubmit() { this.$api .login(this.username, this.password) .then((value) => { let { resultCode, message, data } = value; if (resultCode !== 200) { return; } // 有needback就返回上一页 if (this.$route?.query?.needback) { this.$router.back(); return; } // 无needback就返回前面 this.$router.push("/IndexView"); }) .catch((error) => { console.log(`error-->`, error); }) .finally(() => { console.log("登录操作后执行"); }); }, }, }; </script>
-
使用vuex调用接口
使用vuex调用接口,以便使用vuex中的全局数据,制作如购物车这类效果。
使用vuex调用接口具体思路
- 定义一个接口。
- 在vuex中配置state,及mutations用于同步修改state,actions用于请求数据并调用mautaions方法间接修改state。
- 在需要中页面中引入state中的数据及actions方法,根据需要使用state中的数据和调用actions中的方法。
使用vuex调用接口实际步骤
-
在axios中添加一个接口,用于vuex中进行操作。
-
/src/http/index.js
import http from "./http.js"; // 获取购物车所有的产品 function getCartList() { return http.get("/shop-cart"); } export default { getCartList, };
-
-
在vuex中配置state,及mutations用于同步修改state,actions用于请求数据并调用mautaions方法间接修改state。
-
/src/store/index.js
import Vue from "vue"; import Vuex from "vuex"; import api from "../http/index"; Vue.use(Vuex); export default new Vuex.Store({ state: { list: null, }, mutations: { changeList(state, payload) { // 默认添加选中状态 payload.map((item) => { item.selected = true; return item; }); state.list = payload; }, }, actions: { async getCartListAsync({ commit }) { try { let res = await api.getCartList(); console.log(`res-->`, res); let { data, resultCode } = res; if (+resultCode !== 200) { commit("changeList", []); return; } commit("changeList", data); } catch (err) { console.log(`err-->`, err); } }, }, });
-
-
在需要中页面中引入state中的数据及actions方法,根据需要使用state中的数据和调用actions中的方法。
-
/src/views/DetialView.vue
<template> <div class="detial-wrap"> <van-goods-action> <van-goods-action-icon v-show="list?.length <= 0" icon="cart-o" text="购物车" /> <van-goods-action-icon v-show="list?.length > 0" icon="cart-o" text="购物车" :badge="list?.length" /> </van-goods-action> </div> </template> <script> import { mapState, mapActions } from "vuex"; export default { data() { return { detialData: {}, }; }, computed: { ...mapState(["list"]) }, created() { if (this.list === null) { this.getCartListAsync(); } }, methods: { ...mapActions(["getCartListAsync"]), }, }; </script>
-
/src/components/FooterPage.vue
<template> <div> <van-tabbar v-model="active" active-color="#1baeae" :route="true"> <van-tabbar-item icon="shopping-cart-o" v-show="list?.length <= 0" to="/CartView" > 购物车 </van-tabbar-item> <van-tabbar-item icon="shopping-cart-o" v-show="list?.length > 0" :badge="list?.length" to="/CartView" > 购物车 </van-tabbar-item> </van-tabbar> </div> </template> <script> import { mapState, mapActions } from "vuex"; export default { data() { return { active: 0, }; }, computed: { ...mapState(["list"]), }, methods: { ...mapActions(["getCartListAsync"]), }, created() { if (this.list === null) { this.getCartListAsync(); } }, }; </script>
-
全选与全不选
- 可以用一个变量isAll表示是否全选,用一个数组变量来记录所有已选值对应id。
- 之后对已选数组进行监听或对于所有会改变的已选数组的事件都执行判断已选数组长度与选项数组总长度是否全等。
- 若已选选项数组长度与选项数组总长度相等,那么就为true。
- 若已选选项数组长度与选项数组总长度不相等,值就为false。
- 全选与全不选中间态用另一个变量来表示。
- 如果已选数组长度为0 或 已选数组长度与选项数组总长度全等,值为false。
- 已选数组长度不为0并且已选数组长度与选项数组总长度不全等,值为true。
- 之后对已选数组进行监听或对于所有会改变的已选数组的事件都执行判断已选数组长度与选项数组总长度是否全等。
- 可以用计算属性来做处理,用对象变量的布尔值属性来表示该对象是否被选中。
- 在data或computed中定义一个基础对象数组,对象上有一个属性表示是否选中。
- 在计算属性的get()来做返回的结果值。
- 在计算属性的set()里来修改源头数组对象的表示是否选中的属性值,进而让get()得到的值发生改变。
- 这个的前提是数组里对象及它的属性值是响应式的。
全选与全不选-计算属性版具体思路
- 在data或computed中定义一个基础对象数组,对象上有一个属性表示是否选中。
- 在计算属性的get()来做返回的结果值。
- 在计算属性的set()里来修改源头数组对象的表示是否选中的属性值,进而让get()得到的值发生改变。
全选与全不选实际步骤
-
在vuex中定义好state并引入到单文件组件中的计算属性。
-
/src/store/index.js
import Vue from "vue"; import Vuex from "vuex"; import api from "../http/index"; Vue.use(Vuex); export default new Vuex.Store({ state: { list: null, }, mutations: { changeList(state, payload) { // 默认添加选中状态 payload.map((item) => { item.selected = true; return item; }); state.list = payload; }, }, actions: { async getCartListAsync({ commit }) { try { let res = await api.getCartList(); console.log(`res-->`, res); let { data, resultCode } = res; if (+resultCode !== 200) { commit("changeList", []); return; } commit("changeList", data); } catch (err) { console.log(`err-->`, err); } }, }, });
-
-
或者是在单文件组件里的data或computed中定义好要用的基础对象数组。
-
/src/views/CartView.vue
<script> import { mapState, mapActions } from "vuex"; export default { computed: { ...mapState(["list"]), }, created() { if (this.list === null) { this.getCartListAsync(); } }, methods: { ...mapActions(["getCartListAsync"]), }, }; </script>
-
-
在计算属性的get()来做返回的结果值,通过基础对象数组的every()函数。
-
/src/views/CartView.vue
<script> import { mapState, mapActions } from "vuex"; export default { computed: { ...mapState(["list"]), checkedAll: { get() { return this.list?.every((item) => item.selected === true); }, }, }, created() { if (this.list === null) { this.getCartListAsync(); } }, methods: { ...mapActions(["getCartListAsync"]), }, }; </script>
-
-
在计算属性的set()里来通过基础对象数组map()修改源头数组对象的表示是否选中的属性值,进而让get()得到的值发生改变。
-
/src/views/CartView.vue
<script> import { mapState, mapActions } from "vuex"; export default { computed: { ...mapState(["list"]), checkedAll: { set(value) { this.list?.map((item) => { item.selected = value; return item; }); }, }, }, created() { if (this.list === null) { this.getCartListAsync(); } }, methods: { ...mapActions(["getCartListAsync"]), }, }; </script>
-
/src/views/CartView.vue
完整<template> <div class="cart-wrap"> <div class="content"> <van-swipe-cell v-for="item in list" :key="item.goodsId"> <div class="item"> <van-checkbox v-model="item.selected" checked-color="#1BAEAE" ></van-checkbox> <img :src="item.goodsCoverImg" alt="" /> <div> <p>{{ item.goodsName }}</p> <h3> <span>¥{{ item.sellingPrice }}</span> <van-stepper v-model="item.goodsCount" /> </h3> </div> </div> <template #right> <van-button square text="删除" type="danger" class="delete-button" /> </template> </van-swipe-cell> </div> <van-submit-bar :price="allPrice" button-text="结算" button-color="#1BAEAE" > <van-checkbox v-model="checkedAll">全选</van-checkbox> </van-submit-bar> </div> </template> <script> import { mapState, mapActions } from "vuex"; export default { data() { return { }; }, computed: { ...mapState(["list"]), checkedAll: { get() { return this.list?.every((item) => item.selected === true); }, set(value) { this.list?.map((item) => { item.selected = value; return item; }); }, }, allPrice() { return this.list?.reduce((res, item) => { return ( res + (item.selected ? item.sellingPrice * item.goodsCount * 100 : 0) ); },0); }, }, created() { if (this.list === null) { this.getCartListAsync(); } }, methods: { ...mapActions(["getCartListAsync"]), }, }; </script>
-
总价计算
总价计算思路
- 在data或computed中定义一个基础对象数组,对象上有想要进行计算所需的全部数据。
- 在计算属性的get()来做返回的结果值或计算属性的函数式写法中计算出想要的结果。
总价计算步骤
-
在vuex中定义好state并引入到单文件组件中的计算属性。
-
/src/store/index.js
import Vue from "vue"; import Vuex from "vuex"; import api from "../http/index"; Vue.use(Vuex); export default new Vuex.Store({ state: { list: null, }, mutations: { changeList(state, payload) { // 默认添加选中状态 payload.map((item) => { item.selected = true; return item; }); state.list = payload; }, }, actions: { async getCartListAsync({ commit }) { try { let res = await api.getCartList(); console.log(`res-->`, res); let { data, resultCode } = res; if (+resultCode !== 200) { commit("changeList", []); return; } commit("changeList", data); } catch (err) { console.log(`err-->`, err); } }, }, });
-
-
在data或computed中定义一个基础对象数组,对象上有想要进行计算所需的全部数据。
-
/src/views/CartView.vue
<script> import { mapState, mapActions } from "vuex"; export default { computed: { ...mapState(["list"]), }, created() { if (this.list === null) { this.getCartListAsync(); } }, methods: { ...mapActions(["getCartListAsync"]), }, }; </script>
-
-
在计算属性的函数式写法中用reduce()计算出想要的结果。
-
/src/views/CartView.vue
<script> import { mapState, mapActions } from "vuex"; export default { data() { return { }; }, computed: { ...mapState(["list"]), allPrice() { return this.list?.reduce((res, item) => { return ( res + (item.selected ? item.sellingPrice * item.goodsCount * 100 : 0) ); },0); }, }, created() { if (this.list === null) { this.getCartListAsync(); } }, methods: { ...mapActions(["getCartListAsync"]), }, }; </script>
-
/src/views/CartView.vue
完整<template> <div class="cart-wrap"> <div class="content"> <van-swipe-cell v-for="item in list" :key="item.goodsId"> <div class="item"> <van-checkbox v-model="item.selected" checked-color="#1BAEAE" ></van-checkbox> <img :src="item.goodsCoverImg" alt="" /> <div> <p>{{ item.goodsName }}</p> <h3> <span>¥{{ item.sellingPrice }}</span> <van-stepper v-model="item.goodsCount" /> </h3> </div> </div> <template #right> <van-button square text="删除" type="danger" class="delete-button" /> </template> </van-swipe-cell> </div> <van-submit-bar :price="allPrice" button-text="结算" button-color="#1BAEAE" > <van-checkbox v-model="checkedAll">全选</van-checkbox> </van-submit-bar> </div> </template> <script> import { mapState, mapActions } from "vuex"; export default { data() { return { }; }, computed: { ...mapState(["list"]), checkedAll: { get() { return this.list?.every((item) => item.selected === true); }, set(value) { this.list?.map((item) => { item.selected = value; return item; }); }, }, allPrice() { return this.list?.reduce((res, item) => { return ( res + (item.selected ? item.sellingPrice * item.goodsCount * 100 : 0) ); },0); }, }, created() { if (this.list === null) { this.getCartListAsync(); } }, methods: { ...mapActions(["getCartListAsync"]), }, }; </script>
-