目标:实现类似购物车的单选、多选、全选
image.png
image.png

实现如下:

<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>

Nanana
129 声望4 粉丝