我们来做一下购物车的逻辑,首先我们来看全选,和全不选,包括单选,先点击左侧商品按钮,或者说下面的按钮,他可以实现全选或者全不选中状态,我们选择其单个商品,上下面都是不选中状态,如果我们将所有购物单选框全选上了,上下两个总按钮也会自动选上,这是一个联动效果,这一块会把性能拉满,这一节非常重要。
我们来看看,你怎么知道,哪些是选中的,还是没有选中,包括单选,换句话说,只要是我的单选中哪一个,哪一个不选中,我们只要知道他的状态就可以了,比如说,我们若是全选框都选中,我们的全选状态复选框也是选中状态,若单选框也是全选,我们全选框也是选中状态,我们的全选框未选中,你也是未选中状态,创建目标我们去努力就可以了,我们并不知道里面的数据是否被选中,还是未被选中,我们如何打印呢?
如果单选框全部选中,上下两个全选框也会自动选中,他有一个联动效果
性能拉满,业务稍微有点复杂,如果说通过简单代码实现他,这个逻辑一定是复杂的。
我们只要知道单选是否选中,自然而然我们就知道全选是否选中。全选是true,整体就是true,如果全选是false,单选就是false
全选,单选 实现代码如下:
1, src/views/Cart.vue
<template><div class="cart container"><header @click="$router.back()"><i class="iconfont icon-a-huaban1fuben44"></i><span>购物车</span><span>编辑</span></header><section><!-- <section v-if="list.length"> --><div class="cart-title"><!-- {{isCheckedAll}} --><van-checkbox @click="checkAllFn" :value="isCheckedAll"></van-checkbox><span>商品</span></div><ul><li v-for="(item,index) in list" :key="index"><div class="check"><van-checkbox @click="checkItem(index)" v-model="item.checked"></van-checkbox></div><h2><img :src="item.imgUrl" alt="" /></h2><div class="goods"><div class="goods-title"><span>{{item.goods_name}}</span><i class="iconfont icon-shanchu1"></i></div><div class="goods-price">¥{{item.goods_price}}</div><van-stepper v-model="item.goods_num" integer /></div></li></ul><h1>没有购物车数据<router-link to="/home">去首页逛逛吧</router-link></h1><!-- <section v-else>没有购物车数据<router-link to="/home">去首页逛逛吧</router-link></section> --></section><footer v-if="list.length"><div class="radio"><van-checkbox v-model="checked"></van-checkbox></div><div class="total"><div>共有<span class="total-active">1</span>件商品</div><div><span>总计:</span><span class="total-active">¥128.00+0茶币</span></div></div><div class="order">去结算</div></footer><!-- <Tabbar></Tabbar> --></div>
</template><script>import http from '@/common/api/request.js'import {mapState,mapMutations,mapActions,mapGetters} from 'vuex';export default {name: "Cart",data() {return {checked: true}},// 调用 获取 list 的值computed: {...mapState({list: state => state.cart.list}),...mapGetters(['isCheckedAll'])},created() {// console.log(11);this.getData();},methods: {// 使用这个方法...mapMutations(['cartList', 'checkItem']),...mapActions(['checkAllFn']),// 前端给后端发送请求,查询某个用户async getData() {// 发送请求let res = await http.$axios({// 前端给后端查询购物车数据接口发送请求url: '/api/selectCart',method: 'POST',// 查询当前用户购物车数据,要带上 tokenheaders: {token: true}});// 循环 前端添加一项 'checked' 因为前端给后端打交道 (交互) // 将数据与结构进行了重构,前端添加了一项 'checked' // 每一个购物车数据都有一个 'checked:true',通过他我们就可以知道他是选中的res.data.forEach(v => {// 每一项加一个 'checked'v['checked'] = true;})// console.log(res.data);this.cartList(res.data);}}}
</script><style scoped lang="scss">header {display: flex;// 左中右结构justify-content: space-between;// 垂直居中align-items: center;height: 44px;color: #fff;background-color: #b0352f;i {padding: 0 20px;font-size: 24px;}span {padding: 0 20px;font-size: 16px;}}section {background-color: #f5f5f5;.cart-title {display: flex;padding: 20px;span {padding: 0 15px;font-size: 18px;font-weight: 500;}}ul {display: flex;flex-direction: column;li {display: flex;justify-content: space-between;align-items: center;margin: 8px 0;padding: 6px 20px;background-color: #fff;.check {padding-right: 10px;}.goods {display: flex;flex-direction: column;justify-content: space-between;padding-left: 15px;font-size: 12px;.goods-title {display: flex;i {font-size: 24px;}}.goods-price {padding: 3px 0;color: #b0352f;}::v-deep .van-stepper {text-align: right;}}img {width: 74px;height: 74px;}}}}footer {display: flex;justify-content: space-between;align-items: center;height: 50px;border-top: 2px solid #ccc;.radio {padding: o 15px;}.total {flex: 1;font-size: 18px;font-weight: 500;.total-active {color: #b0352f;}}.order {width: 120px;line-height: 50px;font-size: 18px;font-weight: 500;color: #fff;text-align: center;background-color: #b0352f;}}
</style>2, src/views/Detail.vue
<template><div class="detail"><header><div class="header-returns" v-show="isShow"><div @click="goBack"><i class="iconfont icon-a-huaban1fuben44"></i></div><div><i class="iconfont icon-kefu"></i></div></div><div class="header-bar" v-show="!isShow" :style="styleOption"><div @click="goBack"><i class="iconfont icon-a-huaban1fuben44"></i></div><div><span>商品详情</span><span>商品评价</span></div><div><i class="iconfont icon-kefu"></i></div></div></header><section ref="wrapper"><div><div class="swiper-main"><swiper :options="swiperOption"><!-- slides --><swiper-slide v-for="(item,index) in goods" :key="index"><img :src="item.imgUrl" alt="" /></swiper-slide><!-- Optional controls --><div class="swiper-pagination" slot="pagination"></div></swiper></div><div class="goods-name"><h1>{{goods.name}}</h1><!-- <h1>雨前珍稀白茶1号</h1><div>性价首先,茶感十足,鲜醇耐泡的大众口粮茶</div> --></div><div class="goods-price"><div class="oprice"><span>¥</span><b>{{goods.price}}</b><!-- <b>88</b> --></div><div class="pprice"><span>价格:</span><b>{{goods.price}}</b><!-- <del>¥139</del> --></div></div><div><div><img style="width: 100%;height: 500px;" :src="goods.imgUrl" alt="" /><!-- <img style="width: 100%;height: 500px;" src="/images/goods-list1.png" alt="" /><img style="width: 100%;height: 500px;" src="/images/goods-list2.png" alt="" /><img style="width: 100%;height: 500px;" src="/images/goods-list3.png" alt="" /><img style="width: 100%;height: 500px;" src="/images/goods-list4.png" alt="" /> --></div></div></div></section><footer><div class="add-cart" @click="addCart">加入购物车</div><div>立即购买</div></footer></div>
</template><script>import 'swiper/dist/css/swiper.css'import {swiper,swiperSlide} from 'vue-awesome-swiper'import BetterScroll from 'better-scroll'import http from '@/common/api/request.js'import {Toast} from 'mint-ui';export default {name: "Detail",data() {return { // 赋值id: 0,goods: {},styleOption: {},BetterScroll: '',isShow: true,swiperOption: {autoplay: {delay: 3000},loop: true,speed: 1000,pagination: {el: '.swiper-pagination',type: 'fraction'}}// swiperList: [{// imgUrl: './images/goods-list1.png'// }, {// imgUrl: './images/goods-list2.png'// }, {// imgUrl: './images/goods-list3.png'// }, {// imgUrl: './images/goods-list4.png'// }]}},components: {swiper,swiperSlide},// 接收首页传递的数据created() {this.id = this.$route.query.id;// console.log(111);// let id = this.$route.query.idthis.getData();// console.log(this.$route); // 隐式// console.log(this.$route.query.id); // 显式 {id: '4'}},mounted() {this.BetterScroll = new BetterScroll(this.$refs.wrapper, {movable: true,zoom: true,click: true,probeType: 3,bounce: false})// 滑动事件this.BetterScroll.on('scroll', (pos) => {// console.log(pos);let posY = Math.abs(pos.y);let opacity = posY / 180;// 如果 opacity 的值大于1,就让opacity的值等于1,否则就一直动态改变opacity = opacity > 1 ? 1 : opacity;// console.log(posY / 180);// 将opacity动态值赋值给opacitythis.styleOption = {opacity: opacity}// 去判断if (posY >= 50) {this.isShow = false;} else {this.isShow = true;}})},activated() {// let id = this.$route.query.id;// console.log(this.id, this.$route.query.id);// 用动态ID和存储ID做比较 如果相等就不发送请求,如果不等就再次发送请求// 判断当前Url的id和之前的id是否一致if (this.$route.query.id != this.id) {this.getData();this.id = this.$route.query.id; // 重新赋值}},methods: {async getData() {// 接收到的路径传值参数let id = this.$route.query.id;let res = await http.$axios({url: '/api/goods/id',params: { // 前端给后端传递数据id}});// 直接返回对象,然后将对象直接渲染到页面上this.goods = res;// console.log(res);// this.items = Object.freeze(res.topBar);// this.newData = Object.freeze(res.data);},// 发送一个请求->加入购物车addCart() {let id = this.$route.query.id;http.$axios({url: '/api/addCart',method: 'post',data: {goodsId: id},headers: {token: true}}).then(res => {// 后端返回的数据// console.log(res);if (res.success) {// 提示信息 Toast('添加购物车成功');}});},goBack() {// 返回上一页this.$router.back();}}}
</script><style scoped lang="scss">.detail {display: flex;flex-direction: column;width: 100vw;height: 100vh;overflow: hidden;}header {position: fixed;left: 0;top: 0;z-index: 999;width: 100%;height: 44px;.header-returns {display: flex;justify-content: space-between;align-items: center;width: 100%;height: 44px;div {margin: 0 10px;width: 35px;line-height: 34px;text-align: center;border-radius: 50%;background-color: rgba(0, 0, 0, 0.3);}i {color: #fff;font-size: 24px;}}.header-bar {display: flex;justify-content: space-between;align-items: center;width: 100%;height: 44px;background-color: #fff;font-size: 16px;font-weight: 400;span {padding: 0 10px;}i {padding: 0 10px;font-size: 26px;}}}section {flex: 1;overflow: hidden;}.swiper-main {position: relative;width: 100%;height: 375px;overflow: hidden;/* margin-top: 120px; */}.swiper-container {width: 100%;height: 375px;}.swiper-main img {width: 100%;height: 375px;}.swiper-pagination {bottom: 10px;width: 100%;/* 轮播图的小圆点放右边 */text-align: right;color: #FFFFFF;font-size: 18PX;}::v-deep.swiper-pagination-fraction,.swiper-pagination-custom,.swiper-container-horizontal>.swiper-pagination-bullets {left: -20px}::v-deep .swiper-pagination-bullet-active {background-color: #b0352f;}::v-deep .swiper-pagination-bullet {margin: 3px;}.goods-name {padding: 20px 10px;border-bottom: 1px solid #cccccc;h1 {font-size: 24px;font-weight: 500;}div {padding: 3px 0;font-size: 18px;color: #999999;}}.goods-price {padding: 20px 10px;.oprice {color: red;span {font-size: 12px;}}.pprice {color: #999999;font-size: 16px;}}footer {display: flex;justify-content: center;align-items: center;position: fixed;bottom: 0;left: 0;width: 100%;height: 49px;border-top: 1px solid #cccccc;background-color: #fff;div {width: 50%;line-height: 49px;font-size: 16px;text-align: center;color: #fff;background-color: #b0352f;&.add-cart {background-color: #FF9500;}}}
</style>3, src/store/modules/cart.js
import {CART_LIST,CHECK_ALL,UN_CHECK_ALL,CHECK_ITEM
} from "./mutations-types"export default {state: {list: [], // 是购物车的数据selectList: [] // 选中数据的 id 值},getters: {isCheckedAll(state) {return state.list.length == state.selectList.length;},//isCheckedItem(state) {return state.list.length != state.selectList.length;},},mutations: {[CART_LIST](state, cartArr) {state.list = cartArr;// console.log(state.list);// 添加值 初始化cartArr.forEach(v => {state.selectList.push(v.id);})},// 全选 循环遍历 list,还要向selectList数组里面赋值为新的id数组[CHECK_ALL](state) {state.selectList = state.list.map(v => {v.checked = true;return v.id;})},// 全不选[UN_CHECK_ALL](state) {state.list.forEach(v => {v.checked = false;})// 且赋值为空数组state.selectList = [];},// 单选 [CHECK_ITEM](state, index) {// 获取 idlet id = state.list[index].id;// 通过 id 去找到 selectList 的 id let i = state.selectList.indexOf(id);// 判断 能在 selectList 找到对应的 id,就删除if (id > -1) {// console.log(state.selectList);return state.selectList.splice(i, 1);}// 如果之前没有选中,就给selectList添加一个ID进去state.selectList.push(id);// console.log(state.list[index].id, state.selectList);}},actions: {// 提交 什么时候相等,什么时候不相等checkAllFn({commit,getters}) {getters.isCheckedAll ? commit('unCheckAll') : commit('checkAll');}}
}3, src/store/modules/mutations-types.js
// 用户
export const USER_LOGIN = 'userLogin';
export const INIT_USER = 'initUser';
export const LOGIN_OUT = 'loginOut';// 购物车
export const CART_LIST = 'cartList';
export const CHECK_ALL = 'checkAll';
export const UN_CHECK_ALL = 'unCheckAll';
export const CHECK_ITEM = 'checkItem';// export const USER_LOGIN = 'USER_LOGIN';
// export const INIT_USER = 'INIT_USER';
// export const LOGIN_OUT = 'LOGIN_OUT';
单选

全选

全选或全不选
1,引入 mapActions

2,使用 mapActions(['checkAllFn'])

3, 添加 @click='checkAllFn' 事件

后端返回

问题

解决方案
v-model 是一个双向绑定数据流,而我们的VUEX是单向数据流,所以我们需要将
'v-model' 改为 ':value'
问题完美解决

以上就是全选与全不选实现的代码和效果图