Vue3 implements the shopping cart function and changes the selected state after calling the selected state interface

<script>
import { defineComponent, reactive, onMounted, computed, ref } from "vue";
import {
  getSearchList,
  getUpdateCheck,
  getDelete,
  changNumberValue,
  getInventory,
} from "./service";
import { useRouter } from "vue-router";
import defaultSvg from "~/assets/icons/icon-nograph.svg";
import { ElImage } from "~/components/el";
import { useGoodsSearchKeyStore } from "~/store/goods";
import { message, notification } from "ant-design-vue";
export default defineComponent({
  setup() {
    let shopCart = reactive({
      dataSource: [],
    });
    let allChecked = ref(false);
    const useGoodsSearchKey = useGoodsSearchKeyStore();
    let loading = ref(false);
    let WithIconType = ref("success");
    // 明细数量变更
    const changeProductCount = (item, type) => {
      if (type === "sub" && item.num > 1) {
        item.num--;
        item.totalPrice = (item.price * item.num).toFixed(2);
        inventory(item); 
      }
      if (type === "add") {
        item.num++;
        item.totalPrice = (item.price * item.num).toFixed(2);
        inventory(item); 
      }
      if (type === "change") {
        if (item.num == "" || item.num == null) {
          item.num = 1;
          inventory(item);
          // return;
        } else {
          if (item.num == 0) {
            item.num = 1;
            inventory(item);
          }
          inventory(item);
        }
      }
    };
    // 库存接口
    const inventory = async (item) => {
      let params = {
        ouId: item.ouId,
        skuIds: [item.itemId],
      };
      const res = await getInventory(params);
      if (res.code === 200 && res.success) {
        let arr = res.data || [];
        arr.forEach((el) => {
          if (item.num > el.avalQty) {
            item.num = el.avalQty;
            changNumber(item);
            notification.warning({
              message: "温馨提示",
              description: `不能超过最大库存${el.avalQty}`,
            });
          } else {
            changNumber(item);
          }
        });
      }
    };

    // 改变商品数量接口
    const changNumber = async (item) => {
      let params = {
        num: item.num,
        skuId: item.itemId,
      };
      const res = await changNumberValue(params);
      if (res.success) {
        WatchCartNum();
      }
    };

    // 数量变化的时候调用购物车数量执行接口
    const WatchCartNum = () => {
      let sum = 0;
      shopCart.dataSource?.forEach((item) => {
        if (item?.itemList && item?.itemList.length > 0) {
          item?.itemList.forEach((el) => {
            sum += el.num;
          });
        }
      });
      useGoodsSearchKey.set_num(sum);
    };

    // 计算属性--计算总价
    let priceTotal = computed(() => {
      let totalNum = 0;
      shopCart.dataSource?.forEach((item) => {
        if (item?.itemList.length > 0) {
          item?.itemList.forEach((el) => {
            if (el.itemCheck) {
              totalNum += el.num * el.price;
            }
          });
        }
      });
      return totalNum.toFixed(2);
    });

    // 计算属性-- 判断选中的件数 和
    let checkNum = computed(() => {
      let checkNumber = 0;
      shopCart.dataSource?.forEach((item) => {
        if (item?.itemList && item?.itemList.length > 0) {
          item?.itemList.forEach((el) => {
            if (el.itemCheck) {
              checkNumber += 1;
            }
          });
        }
      }); 
      return checkNumber;
    });
	
    // 公司选中的时候改变明细选中状态
    const selectShopItems = async (item) => {
      let cartItemCheckParamVOS = [];
      item?.itemList?.forEach((i) => {
        if (item.check === false) {
          cartItemCheckParamVOS.push({
            check: true,
            skuId: i.itemId,
          });
        } else {
          cartItemCheckParamVOS.push({
            check: false,
            skuId: i.itemId,
          });
        }
      });
      const res = await getUpdateCheck(cartItemCheckParamVOS);
      if (res.success) {
        item.check = !item.check;
        item?.itemList.forEach((p) => {
          p.itemCheck = item.check;
        });
        selectAll(item);
      } else {
        item.check = item.check;
        // let checkCompany = item?.itemList.every((s) => s.itemCheck === true);
        // item.check = checkCompany;
        item?.itemList.forEach((p) => {
          p.itemCheck = item.check;
        });
        selectAll(item);
      }
    };

    // 明细选中
    const checkItem = async (el, item) => {
      let cartItemCheckParamVOS = [];
      item?.itemList?.forEach((i) => {
        if (i.itemId == el.itemId) {
          cartItemCheckParamVOS.push({
            check: !i.itemCheck,
            skuId: i.itemId,
          });
        } else {
          cartItemCheckParamVOS.push({
            check: i.itemCheck,
            skuId: i.itemId,
          });
        }
      });
      // 更新商品选中状态接口
      const res = await getUpdateCheck(cartItemCheckParamVOS); 
      if (res.success) {
        el.itemCheck = !el.itemCheck;
        let checkCompany = item?.itemList.every((s) => s.itemCheck === true);
        item.check = checkCompany;
        selectAll(item);
      } else {
        el.itemCheck = el.itemCheck;
        let checkCompany = item?.itemList.every((s) => s.itemCheck === true);
        item.check = checkCompany;
        selectAll(item);
      }
    };

    // 选中公司名称判断全选按钮是否选中
    const selectAll = async (itemDate) => {
      let checkAll = shopCart.dataSource.every((item) => item.check === true);
      allChecked.value = checkAll;
    };

    // 全选
    const checkAll = async (e) => {
      let checkAllData = [];
      if (allChecked.value) {
        shopCart?.dataSource?.map((item) => {
          item?.itemList.forEach((p) => {
            checkAllData.push({
              check: true,
              skuId: p.itemId,
            });
          });
        });
      } else {
        shopCart.dataSource?.map((item) => {
          item?.itemList.forEach((p) => {
            checkAllData.push({
              check: false,
              skuId: p.itemId,
            });
          });
        });
      } 
      const res = await getUpdateCheck(checkAllData);
      if (res.success) {
        shopCart?.dataSource?.forEach((item) => {
          item.check = e.target.checked;
          item?.itemList.forEach((p) => {
            p.itemCheck = e.target.checked;
          });
        });
      } else {
        shopCart?.dataSource?.forEach((item) => {
          item.check = item.check;
          item?.itemList.forEach((p) => {
            p.itemCheck = p.itemCheck;
          });
        });
      }
    };

    // 删除明细行
    const handRemove = async (item, itemId) => {
      let params = [itemId];
      const res = await getDelete(params);
      if (res.success) {
        searchCartList();
      }
    };

    // 删除全部
    const removeAll = async () => {
      let skuIds = [];
      shopCart.dataSource?.forEach((item) => {
        if (item?.itemList.length > 0) {
          item?.itemList?.forEach((el) => {
            if (el.itemCheck) {
              skuIds.push(el.itemId);
            }
          });
        }
      });
      const res = await getDelete(skuIds);
      if (res.success) {
        // 删除成功之后调取 获取 购物车的接口数据达到刷新页面的效果
        searchCartList();
      }
    };

    // 去结算
    const router = useRouter();
    const settleAccount = () => {
      if (checkNum.value > 0) {
        router.push({
          path: "/settleAccount",
        });
      } else {
        notification["warning"]({
          message: "温馨提示",
          description: "请选择要结算的数据",
          duration: 2,
        });
      }
    };
    // 继续购物
    const goShopHome = () => {
      router.push({
        path: "/home",
      });
    };

    // 商品名称进入详情页面
    const handleDetail = (id) => {
      router.push({
        path: "/itemGoodsDetail",
        query: {
          id: id,
        },
      });
    };

    const searchCartList = async () => {
      loading.value = true;
      let res = await getSearchList();
      loading.value = false;
      if (res.success) {
        shopCart.dataSource = res.data?.cartItemList || [];
        WatchCartNum();
      } else {
        shopCart.dataSource = [];
      }
    };

    onMounted(() => {
      searchCartList(); //获取购物车数据
    });

    return {
      shopCart,
      allChecked,
      priceTotal,
      defaultSvg,
      checkNum,
      loading,
      changeProductCount,
      selectShopItems,
      checkAll,
      handRemove,
      removeAll,
      checkItem,
      goShopHome,
      settleAccount,
      handleDetail,
    };
  },
});
</script>

<template>
  <a-spin :spinning="loading" size="large" wrapperClassName="el-spin">
    <div class="shopCart">
      <div v-if="shopCart?.dataSource.length > 0">
        <a-row class="shopHead">
          <a-col :span="1">
            <a-checkbox v-model:checked="allChecked" @change="checkAll">
              <!-- 全选 -->
            </a-checkbox>
          </a-col>
          <a-col :span="1">序号 </a-col>
          <a-col :span="11" class="colSpan">商品</a-col>
          <a-col :span="2" class="colSpan">单价</a-col>
          <a-col :span="4" class="colSpan">数量</a-col>
          <a-col :span="2" class="colSpan">小计</a-col>
          <a-col :span="2" class="colSpan">操作</a-col>
        </a-row>
        <a-row
          v-for="item in shopCart.dataSource"
          :key="item.id"
          class="shopMessage"
        >
          <a-col :span="24" class="company">
            <a-checkbox :checked="item.check" @click="selectShopItems(item)">
              {
   
   { item.sellerName }}
            </a-checkbox>
          </a-col>
          <a-col v-for="(el, i) in item.itemList" :key="el.itemId" :span="24">
            <a-row>
              <a-col :span="1" class="checkboxStyle">
                <a-checkbox
                  :checked="el.itemCheck"
                  @click="checkItem(el, item)"
                />
              </a-col>
              <a-col :span="1" class="checkboxStyle">
                {
   
   { i + 1 }}
              </a-col>
              <a-col :span="11" class="shopMsg">
                <ElImage
                  class="imgStyle"
                  :value="el.image != '' ? el.image : defaultSvg"
                  :preview="false"
                  style="height: 100%; width: 80px"
                />
                <div class="ShopDiv">
                  <p
                    :title="el.spuName"
                    @click="handleDetail(el.spuId)"
                    style="cursor: pointer"
                  >
                    {
   
   { el.spuName }}
                  </p>
                  <p>
                    <span class="pSpan" v-if="el.itemName" :title="el.itemName">
                      款式:{
   
   { el.itemName }}
                    </span>
                    <span
                      class="pSpan"
                      v-if="el.skuProps.length > 0"
                      style="margin-left: 3px"
                    >
                      <span v-for="(f, i) in el.skuProps" :key="i">
                        {
   
   { f.name }} : {
   
   { f.value }}
                      </span>
                    </span>
                    <!-- <span class="pSpan" v-if="el.itemSpec">
                    颜色:{
   
   { el.itemSpec }}
                  </span> -->
                    <span class="pSpan" v-if="el.itemSpec == null">
                      规格:默认
                    </span>
                  </p>
                </div>
              </a-col>
              <a-col :span="2" class="itemCol">
                <span>{
   
   { el.price }}</span>
              </a-col>
              <a-col :span="4" class="itemCol">
                <div
                  style="text-align: center; height: 28px; line-height: 28px"
                >
                  <a-button
                    @click="changeProductCount(el, 'sub')"
                    class="itemCol-button1"
                  >
                    <span style="margin-left: -5px">—</span>
                  </a-button>
                  <a-input-number
                    v-model:value="el.num"
                    :controls="false"
                    @change="changeProductCount(el, 'change')"
                    style="margin-top: 2px"
                  />
                  <a-button
                    @click="changeProductCount(el, 'add')"
                    class="itemCol-button1"
                  >
                    <span class="itemCol-button1-span2"> + </span>
                  </a-button>
                </div>
              </a-col>
              <a-col :span="2" class="itemCol" style="color: #f51d29">
                ¥<span>{
   
   { (el.price * el.num).toFixed(2) }}</span>
              </a-col>
              <a-col :span="2" class="itemCol">
                <a-button type="text" @click="handRemove(el, el.itemId)">
                  删除
                </a-button>
              </a-col>
            </a-row>
          </a-col>
        </a-row>
        <a-row class="shopFooter">
          <a-col :span="2">
            <a-checkbox v-model:checked="allChecked" @change="checkAll">
              全选
            </a-checkbox>
          </a-col>
          <a-col :span="3" class="colSpan"> 已选择 {
   
   { checkNum }} 件 </a-col>
          <a-col
            :span="3"
            class="colSpan"
            @click="removeAll()"
            style="cursor: pointer"
            >删除选中</a-col
          >
          <a-col :span="2" />
          <a-col :span="2" />
          <a-col :span="2" />
          <a-col :span="6" class="colSpan">
            总价(不含邮费):{
   
   { priceTotal }}
          </a-col>
          <a-col :span="4" class="colSpan">
            <a-button @click="goShopHome"> 继续购物 </a-button>
            &nbsp;
            <a-button type="primary" danger @click="settleAccount">
              去结算
            </a-button>
          </a-col>
        </a-row>
      </div>
      <div v-if="shopCart?.dataSource.length == 0">
        <div class="noGoods">
          <div class="noGoods-img">
            <img src="/src/assets/icons/empty-shopping.svg" alt="未找到结果" />
          </div>
          <div class="noGoods-txt">暂无数据</div>
          <div class="noGoods-explain">
            <span class="noGoods-explain-span">
              您可以:
              <br />
              1、尽量描述清楚您需要搜索的商品,缩短或修改您的搜索词,重新搜索。
              <br />
              2、<a href="/home">去首页</a>
            </span>
          </div>
        </div>
      </div>
    </div>
  </a-spin>
</template>

<style lang="scss" scoped>
p{
  margin: 0;
  padding: 0;
}
.shopCart{
  padding-top: 30px ; 
  height: calc(100% - 30px);
  margin: auto;
  min-width: 1200px;
  width: 1200px;
   .shopHead{
    height: 40px;
    background: #F8F8F8;
    line-height: 40px;
    padding: 0 20px;
    .colSpan{
      text-align: center;
    }
  }
  .cloudt-layout-row{
    row-gap: 0px;
    margin: 10px 0;
  }
  .shopMessage{
    padding: 0 20px;
  }
  .checkboxStyle{
    display: flex;
    align-items: center;
  }
  .company{
    line-height: 60px;
  }
  .shopMsg{
    display: flex;
    width: 80px;
    height: 80px;
  }
 
  .imgStyle{ 
    width: 80px;
    height: 80px;
    object-fit: contain;
  }
  .ShopDiv{
    width: calc(100% - 100px);
    margin-left: 20px;
    .pSpan{
      font-size: 12px;
      color: #999999;
      line-height: 20px;
      font-weight: 400;
      // padding: 5px;
    }
  }
  .itemCol{
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 12px;
    line-height: 20px;
    font-weight: 600;
    .itemCol-button1{
      background: #fafafa;
      border: 1px solid #e5e5e5;
      width: 10px;
      .itemCol-button1-span2{
        margin-left: -10px;
        font-size: 21px;
        margin-top: -16px;
        width: 20px;
        height: 20px;
        line-height: 20px;
      }
    }
  }
  .shopFooter{
    height: 40px;
    background: #F8F8F8;
    line-height: 40px;
    padding: 0px 20px;
    margin-top:10px;
    .colSpan{
      text-align: center;
    }
  }
}
// .cloudt-layout-input-number-input{
//   text-align: center !important;
// }
.cloudt-layout-input-number-input{
  width: 100%;
    height: 30px;
    padding: 0 11px;
    text-align: center !important;
    background-color: transparent;
    border: 0;
    border-radius: 2px;
    outline: 0;
    transition: all 0.3s linear;
    appearance: textfield !important;
}
.cloudt-layout-input-number-input-wrap{
  margin-top:-1px;
} 

.noGoods {
  min-width: 400px;
  margin-top: 80px;
  .noGoods-img {
    width: 160px;
    height: 160px;
    margin: auto;
  }
  .noGoods-txt {
    margin: 40px 0;
    font-family: PingFangSC-Semibold;
    font-size: 16px;
    color: #333333;
    line-height: 24px;
    font-weight: 600;
    text-align: center;
  }
  .noGoods-explain {
    display: flex;
    font-family: PingFangSC-Regular;
    font-size: 14px;
    color: #333333;
    line-height: 22px;
    font-weight: 400;
    .noGoods-explain-span {
      margin: auto;
      text-align: left;
    }
  }
}
</style>

Guess you like

Origin blog.csdn.net/lzfengquan/article/details/128785809