vue3实现购物车功能 computed ref reactive onMounted

在这里插入图片描述
看图描述
可以得到我们实现的购物功能满足 全选、反选,明细选中,明细全选和反选,computed 计算属性 和 总价钱,选中件数等等操作(里面有watch 监听示例,但是不建议用,因为watch 监听太耗用性能,computed 足可以满足我们的需求了)
上代码。

<template>
  <div class="shopCart">
    <div v-if="dataSource.length > 0">
      <a-row class="shopHead">
        <a-col :span="2">
          <a-checkbox v-model:checked="allChecked" @change="checkAll">
            全选
          </a-checkbox>
        </a-col>
        <a-col :span="6" class="colSpan">商品</a-col>
        <a-col :span="4" class="colSpan">单价</a-col>
        <a-col :span="4" class="colSpan">数量</a-col>
        <a-col :span="4" class="colSpan">小计</a-col>
        <a-col :span="4" class="colSpan">操作</a-col>
      </a-row>
      <a-row v-for="item in dataSource" :key="item.id" class="shopMessage">
        <a-col :span="24" class="company">
          <a-checkbox
            v-model:checked="item.check"
            @click="selectShopItems(item)"
          >
            {
   
   { item.comName }}
          </a-checkbox>
        </a-col>
        <a-col v-for="el in item.shopList" :key="el.ids" :span="24">
          <a-row>
            <a-col :span="1" class="checkboxStyle">
              <a-checkbox
                v-model:checked="el.itemCheck"
                @click="checkItem(el, item)"
              />
            </a-col>
            <a-col :span="7" class="shopMsg">
              <img :src="el.url" class="imgStyle" />
              <div class="ShopDiv">
                <p :title="el.shopName">{
   
   { el.shopName }}</p>
                <p>
                  <span class="pSpan">款式:{
   
   { el.type }}</span>
                  <span class="pSpan">颜色{
   
   { el.color }}</span>
                </p>
              </div>
            </a-col>
            <a-col :span="4" class="itemCol">
              <span>{
   
   { el.price }}</span>
            </a-col>
            <a-col :span="4" class="itemCol">
              <div>
                <a-button @click="changeProductCount(el, 'sub')"> - </a-button>
                <a-input-number v-model:value="el.num" :controls="false" />
                <a-button @click="changeProductCount(el, 'add')"> + </a-button>
              </div>
            </a-col>
            <a-col :span="4" class="itemCol" style="color: #f51d29">
              <!-- ¥ <span>{
   
   { el.price * el.num }}</span> -->
              ¥ <span>{
   
   { el.total }}</span>
            </a-col>
            <a-col :span="4" class="itemCol">
              <a-button type="text" @click="handRemove(el, el.ids)">
                删除
              </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()">删除选中</a-col>
        <a-col :span="2" />
        <a-col :span="2" />
        <a-col :span="4" />
        <a-col :span="4" class="colSpan">
          总价(不含邮费): {
   
   { priceTotal }}
        </a-col>
        <a-col :span="4" class="colSpan">
          <a-button> 继续购物 </a-button>
          &nbsp;
          <a-button type="primary" danger @click="settleAccount">
            去结算
          </a-button>
        </a-col>
      </a-row>
    </div>
    <div v-else>
      咋内数据
      <!-- <a-empty :image="simpleImage" /> -->
    </div>
  </div>
</template>

<script>
import {
  defineComponent,
  reactive,
  onMounted,
  computed,
  ref,
  watch,
} from "vue";
import { getSearchList } from "./service";
import { useRoute, useRouter } from "vue-router";
import { PlusOutlined, MinusOutlined } from "@ant-design/icons-vue";
export default defineComponent({
  setup() {
    let shopCart = reactive({
      dataSource: [
        {
          comName: "台州电动车公司",
          id: "1",
          check: false,
          shopList: [
            {
              shopName:
                "立马电动车新款IN趣电动摩托车外卖长跑王电瓶车双碟刹长续航通勤代步车 极光烟灰-72V20A",
              url: "xxxxxxxxxxxxx",
              type: "aaa",
              color: "bbb",
              price: 10,
              num: 1,
              total: 10,
              itemCheck: false,
              ids: "1-1",
            },
            {
              shopName:
                "立马电动车新款IN趣电动摩托车外卖长跑王电瓶车双碟刹长续航通勤代步车 极光烟灰-72V20A",
              url: "xxxxxxxxxxxxx",
              type: "aaa",
              color: "bbb",
              price: 10,
              num: 1,
              total: 10,
              itemCheck: false,
              ids: "1-2",
            },
          ],
        },
        {
          comName: "广州电动车公司",
          id: "2",
          check: false,
          shopList: [
            {
              shopName: "商品2",
              url: "xxxxxxxxxxxxx",
              type: "aaa",
              color: "bbb",
              price: 10,
              num: 1,
              total: 10,
              itemCheck: false,
              ids: "2-1",
            },
          ],
        },
      ],
    });
    let allChecked = ref(false);

    // let selectId = ref(0); // 选中的数据列表
    // 明细数量变更
    const changeProductCount = (item, type) => {
      if (type === "sub" && item.num > 1) {
        item.num--;
        item.total = item.price * item.num;
      }
      if (type === "add") {
        item.num++;
        item.total = item.price * item.num;
      }
    };
    // 计算总价
    let priceTotal = computed(() => {
      let totalNum = 0;
      shopCart.dataSource.forEach((item) => {
        if (item.shopList.length > 0) {
          item.shopList.forEach((el) => {
            if (el.itemCheck) {
              totalNum += el.num * el.price;
            }
          });
        }
      });
      return totalNum;
    });

    // 判断选中的件数 和
    let checkNum = computed(() => {
      let checkNumber = 0;
      shopCart.dataSource.forEach((item) => {
        if (item.shopList && item.shopList.length > 0) {
          item.shopList.forEach((el) => {
            if (el.itemCheck) {
              checkNumber += 1;
            }
          });
        }
      });
      return checkNumber;
    });
    // 公司选中的时候改变明细选中状态
    const selectShopItems = (item) => {
      console.log(item);
      item.check = !item.check;
      item.shopList.forEach((p) => {
        p.itemCheck = item.check;
      });
      selectAll();
    };

    // 明细选中
    const checkItem = (el, item) => {
      console.log(el, "每一项 el.itemCheck", item.shopList);
      el.itemCheck = !el.itemCheck;
      let checkCompany = item.shopList.every((s) => s.itemCheck === true);
      console.log(checkCompany);
      item.check = checkCompany;
      selectAll();
    };
    // 选中公司名称判断全选按钮是否选中
    const selectAll = () => {
      let checkAll = shopCart.dataSource.every((item) => item.check === true);
      allChecked.value = checkAll;
    };
    // 全选
    const checkAll = () => {
      console.log(allChecked.value, "allChecked");
      if (allChecked.value) {
        // console.log(allChecked.value, "全选");
        shopCart.dataSource.forEach((item) => {
          item.check = true;
          item.shopList.forEach((p) => {
            p.itemCheck = true;
          });
        });
      } else {
        // console.log(allChecked.value, "取消全选");
        shopCart.dataSource.forEach((item) => {
          item.check = false;
          item.shopList.forEach((p) => {
            p.itemCheck = false;
          });
        });
      }
    };
    // 删除明细行
    const handRemove = (item, ids) => {
      console.log(item, ids, "190");
    };
    // 删除全部
    const removeAll = () => {
      // shopCart.dataSource.splice(0, shopCart.dataSource.length);
    };
    // 去结算
    const route = useRoute();
    const router = useRouter();
    const settleAccount = () => {
      // router.push("/settleAccount");
      router.push({
        path: "/settleAccount",
        query: {},
      });
    };

    const searchCartList = async () => {
      let res = await getSearchList();
      console.log(res, " 获取购物车数据");
    };
    onMounted(() => {
      searchCartList();
    });
    // // 监听选中的数量 不建议使用
    // watch(
    //   () => shopCart.dataSource,
    //   (val) => {
    //     console.log("找到dataSource了", val);
    //     if (val) {
    //       selectId.value = 0;
    //       val.forEach((el) => {
    //         console.log(el, "每一项变化计算总价");
    //         el.shopList?.forEach((itemEl) => {
    //           if (itemEl.itemCheck) {
    //             selectId.value = selectId.value + 1;
    //           }
    //           console.log(selectId.value, "SelectIdSelectIdSelectId");
    //         });
    //       });
    //     }
    //   },
    //   { deep: true }
    // );

    return {
      ...shopCart,
      allChecked,
      priceTotal,
      // selectId,
      checkNum,
      changeProductCount,
      selectShopItems,
      checkAll,
      handRemove,
      removeAll,
      checkItem,
      settleAccount,
    };
  },
});
</script>

<style lang="scss" scoped>
p{
  margin: 0;
  padding: 0;
}
.shopCart{
  padding-top: 30px ;
  width: 1200px;
  margin: 0 auto;
  height: calc(100% - 30px);
   .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: 50px;
    // height: 50px;
     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;
  }
  .shopFooter{
    height: 40px;
    background: #F8F8F8;
    line-height: 40px;
    padding: 0px 20px;
    margin-top:10px;
    .colSpan{
      text-align: center;
    }
  }
}
</style>

猜你喜欢

转载自blog.csdn.net/lzfengquan/article/details/128401811