乐趣区

关于vue.js:实现类似购物车的单选多选全选操作

指标:实现相似购物车的单选、多选、全选

实现如下:

<template>
  <div class="div-table">
    <a-row type="flex" class="div-table-thead">
      <a-col :span="aRowCol.img">
        <a-checkbox v-model:checked="checkedAll" @change="onChangeCheckedAll"></a-checkbox>
        <span style="display: inline; margin-left: 20px"> 图片 </span>
      </a-col>
      <a-col :span="aRowCol.name">
        <span style="text-align: left"> 名称 </span>
      </a-col>
      <a-col :span="aRowCol.price">
        <span> 价格 </span>
      </a-col>
      <a-col :span="aRowCol.quantity">
        <span> 数量 </span>
      </a-col>
      <a-col :span="aRowCol.discount">
        <span> 优惠 </span>
      </a-col>
      <a-col :span="aRowCol.amount">
        <span> 小计 </span>
      </a-col>
      <a-col :span="aRowCol.leftNum">
        <span> 残余数量 </span>
      </a-col>
    </a-row>

    <div class="div-table-tbody" v-if="acData.length">
      <div class="div-table-tbody-item" v-for="(item, index) in acData" :key="index">
        <div class="tbody-item-title">
          <a-checkbox v-model:checked="item.checked" @change="onChangeCheckedItemAll(item)"></a-checkbox>
          <span style="margin-left: 20px">{{item.acName}}</span>
        </div>
        <a-row type="flex" align="middle" class="tbody-item-row" v-for="(i, ind) in item.acList" :key="ind">
          <a-col :span="aRowCol.img">
            <a-checkbox v-model:checked="i.checked" @change="onChangeChecked(item, i)" style="margin-right: 10px"></a-checkbox>
            <a-image
              style="width: 96px; height: 96px; border-radius: 5px; margin-right: 10px"
              :preview="false"
              :src="i.url ? i.url :''"
              :fallback="fallImage"
            />
          </a-col>
          <a-col :span="aRowCol.name" class="card-info">
            <span>{{i.skuName}}</span>
            <span class="font12 light-gray">{{i.skuModel}}</span>
          </a-col>
          <a-col :span="aRowCol.price">
            <span>¥{{i.price}}</span>
          </a-col>
          <a-col :span="aRowCol.quantity">
            <span>{{i.totalNum}}</span>
          </a-col>
          <a-col :span="aRowCol.discount">
            <span>¥{{i.discountAmount}}</span>
          </a-col>
          <a-col :span="aRowCol.amount">
            <span>¥{{i.amount}}</span>
          </a-col>
          <a-col :span="aRowCol.leftNum">
            <span>{{i.leftNum}}</span>
          </a-col>
        </a-row>
      </div>
    </div>
  </div>
</template>

<script setup>
import {ref} from "@vue/reactivity"

// 图片占位符
const fallImage = ''

const aRowCol = ref({
  img: 4,
  name: 5,
  quantity: 3,
  price: 3,
  discount: 3,
  amount: 3,
  leftNum: 3
})

const acData = ref([
  {
    acName: '第一个分组',
    acId: 1,
    acList: [{
        url: null,
        rowId: '1',
        skuName: '1-1 子分组的名字',
        skuCode: '1- 1 的 id',
        skuModel: '类型类型类型类型类型类型 11111 型',
        price: 100.00,
        totalNum: 2,
        discountAmount: 100.00,
        leftNum: 1,
        amount: 100.00,
        acType: null,
        acName: "普通商品",
        acId: 1
      }, 
      {
        url: null,
        rowId: '1',
        skuName: '1-2 子分组的名字',
        skuCode: '1- 2 的 id',
        skuModel: '类型类型类型类型类型类型类型类型 111111',
        price: 200.00,
        totalNum: 1,
        discountAmount: 100.00,
        leftNum: 1,
        amount: 300.00,
        acType: null,
        acName: "普通商品",
        acId: 1
      }]
  },
  {
    acName: '第 2 个分组',
    acId: 2,
    acList: [{
      url: null,
      rowId: '2',
      skuName: '2-1 子分组的名字',
      skuCode: '2- 1 的 id',
      skuModel: '类型类型类型 222222222222',
      price: 100.00,
      totalNum: 2,
      discountAmount: 50.00,
      leftNum: 1,
      amount: 250.00,
      acName: '第 2 个分组',
      acId: 2
    }, 
    {
      url: null,
      rowId: '2',
      skuName: '2-2 子分组的名字',
      skuCode: '2- 2 的 id',
      skuModel: '类型类型类型类型类型类 222',
      price: 50.00,
      totalNum: 1,
      discountAmount: 0.00,
      leftNum: 0,
      amount: 100.00,
      acName: '第 2 个分组',
      acId: 2
    }]
  }
]
)

const checkedAll = ref(false)
const checkedList = ref([])

// 数组去重
const uniqueArr = arr => {const obj = {} // 辅助数组
  return arr.reduce((sum, idx) => {if (!obj[idx.rowId]) {
      // 判断以后 orderRowId 是否曾经在 checkedList 外面
      // 如果不在
      obj[idx.rowId] = true // 则将以后 orderRowId 的值设置为 true(也能够是其余
      sum.push(idx) // 而后 push 进以后的数组
    }
    return sum
  }, [])
}

// 是否全选(只依据流动类型全选可用)const isCheckedAll = itemData => {
  // 全选 checkedList 与 addCommodityData 雷同
  const tempList = []
  checkedList.value.forEach(item => {const sameItemIdx = acData.value.findIndex(a => a.acName == item.acName) // 比照原数组与选中数组各项的长度是否一样
    tempList.push(acData.value[sameItemIdx].acList.length ==
        item.acList.length
    )
  })
  const uncheckLen = tempList.findIndex(item => item == false) // 找出长度不统一的值
  if (itemData.checked && checkedList.value.length == acData.value.length && uncheckLen < 0) {
    // 只有与原数组各项长度保持一致能力选中全选
    return true
  } else if (itemData.checked && itemData.acList.length == 6) {
    // 以后流动的长度与后盾以后页面长度(假如页面长度为 6)一样
    return true
  } else if (
    itemData.checked &&
    acData.value.length == 1 &&
    itemData.acList.length == acData.value[0].acList.length
  ) {
    // 以后流动所有数据与接口返回数据长度一样且接口返回只有一个数组
    return true
  } else {return false}
}

// 全选
const onChangeCheckedAll = all => {
  acData.value && acData.value.map(item => {
    item.checked = all.target.checked
    item.acList.map(i => {i.checked = all.target.checked})
  })

  if (all.target.checked) { // 选中
    // 比拟 checkedList 与 acData 是否有雷同分组
    if (checkedList.value.length > 0) {const tempList = JSON.parse(JSON.stringify(acData.value)) // 深拷贝数组
      checkedList.value.forEach((item, index) => {
        tempList.forEach(i => {
          // 比拟 checkedList 与 acData 是否有雷同流动,属于雷同流动,增加进对应的流动中
          if (i.acId == item.acId) {checkedList.value[index].acList = [...checkedList.value[index].acList, ...i.acList]

            // 数组去重操作
            checkedList.value[index].acList = uniqueArr(checkedList.value[index].acList)
          } else {checkedList.value.push(i)
          }
        })
      })
    } else {
      // 第一次勾选,checkedList 为空
      checkedList.value = JSON.parse(JSON.stringify(acData.value)) // 深拷贝数组,第一次勾全选
    }
  } else { // 勾销选中
    // 比拟 acList 与 checkedList 的长度值,如果 checkedList 长度大于 acList,勾销勾选,就从 checkedList 里删掉 acList
    checkedList.value.forEach(item => {const sameItemIdx = acData.value.findIndex(a => a.acId == item.acId) // 比照原数组与选中数组各项的长度是否一样
      if (acData.value[sameItemIdx].acList.length == item.acList.length) {checkedList.value = []
      } else {
        // 找到匹配的数据,删除
        acData.value[sameItemIdx].acList.forEach(a => {item.acList.map((i, ind) => {if (i.rowId == a.rowId) {item.acList.splice(ind, 1)
            }
          })
        })
      }
    })
  }
  console.log('all', checkedList.value)
}

// 依据分组全选
const onChangeCheckedItemAll = itemData => {const hasItemIdx = checkedList.value.findIndex(item => item.acId == itemData.acId)
  console.log('hasItemIdx', hasItemIdx)
  if (itemData.checked) {
    // 如果之前有选中项
    itemData.acList.map(item => {item.checked = true})
    const copyArr = JSON.parse(JSON.stringify(itemData)) // 深拷贝
    if (hasItemIdx < 0) { // 从未有选中的数据
      checkedList.value.push(copyArr)
    } else { // 之前曾经有选中的数据
      checkedList.value.forEach((item, index) => {
        copyArr.acList.forEach(i => {if (copyArr.acId == item.acId) {checkedList.value[index].acList = [...checkedList.value[index].acList, i]
            // 数组去重操作
            checkedList.value[index].acList = uniqueArr(checkedList.value[index].acList)
          }
        })
      })
    }
    checkedAll.value = isCheckedAll(itemData)
  } else {
    itemData.acList.map(item => {item.checked = false})
    if (checkedList.value[hasItemIdx].acList.length == itemData.acList.length) {checkedList.value.splice(hasItemIdx, 1)
    } else {
      itemData.acList.forEach(a => {checkedList.value[hasItemIdx].acList.forEach((b, idx) => {if (b.rowId == a.rowId) {checkedList.value[hasItemIdx].acList.splice(idx, 1)
          }
        })
      })
    }
    checkedAll.value = false
  }
  console.log('huodong', checkedList.value)
}

// 单个选中
const onChangeChecked = (itemData, iData) => {const itemIdx = checkedList.value.findIndex(item => item.acName == itemData.acName)
  let iIdx
  if (itemIdx > -1) {iIdx = checkedList.value[itemIdx].acList.findIndex(i => i.rowId == iData.rowId)
  }

  if (iData.checked) {
    // 选中
    if (itemIdx > -1) {checkedList.value[itemIdx].acList.push(iData)
    } else {
      checkedList.value.push({
        acName: itemData.acName,
        acId: itemData.acId,
        acList: [iData]
      })
    }

    // 勾选操作
    if (itemData.acList.length == 1) {itemData.checked = true} else if (itemData.acList.length > 1) {if (itemIdx > -1 && checkedList.value[itemIdx].acList.length == itemData.acList.length)
      itemData.checked = true
    }
    checkedAll.value = isCheckedAll(itemData)
  } else {
    // 勾销选中
    if (itemIdx > -1) {if (checkedList.value.length == 1) {checkedList.value[0].acList.length == 1 ? (checkedList.value = []) : checkedList.value[itemIdx].acList.splice(iIdx, 1)
      } else if (checkedList.value.length > 1) {checkedList.value[itemIdx].acList.length == 1 ? checkedList.value.splice(itemIdx, 1) : checkedList.value[itemIdx].acList.splice(iIdx, 1)
      }
    }
    iData.checked = false
    checkedAll.value = false
    itemData.checked = false
  }

  console.log('www', checkedList.value)
}

</script>
退出移动版