vue3 | table选择组件封装,含单选、多选、跨页选择功能(vue3+element)

需求

1.单选、多选统一样式,含checkbox选框
2.可跨页多选

商品选择示例

单选时通过样式类 “hidden-checkbox-all” 隐藏checkbox全选框

<template>
  <el-dialog :title="title" v-model="visible" width="1000px" @closed="close">
    <div class="dialog-content">
      <div class="search-box">
        <el-form :inline="true" :model="searchParams" label-width="72px">
          <el-form-item label="商品分类:">
            <el-cascader v-model="searchParams.category" clearable style="width: 200px;" size="large" placeholder="请选择"
              :options="categoryOptions" 
              :props="{
                label: 'name',
                value: 'id',
                checkStrictly: true
              }" 
              filterable 
              @change="onSearch()"
            />
          </el-form-item>
          <el-form-item label="商品编号:">
            <el-input v-model="searchParams.no" size="large" style="width: 200px;" placeholder="请输入" />
          </el-form-item>
          <el-form-item label="商品名称:">
            <el-input v-model="searchParams.name" size="large" style="width: 200px;" placeholder="请输入" />
          </el-form-item>
          <el-form-item label="规格型号:">
            <el-input v-model="searchParams.specification" size="large" style="width: 200px;" placeholder="请输入" />
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="onSearch">查询</el-button>
            <el-button @click="onReset">重置</el-button>
          </el-form-item>
        </el-form>
      </div>

      <div class="table-box">
        <el-table 
          :class="{'hidden-checkbox-all': props.isRadio}"
          ref="multipleTableRef" 
          :data="tableData" 
          height="400px" 
          :loading="loading"
          @select="handleSelectItem" 
          @select-all="(selection)=>{
            handleSelectAll(selection, tableData)
          }"
        >
          <el-table-column type="selection" width="55px" />
          <el-table-column min-width="150px" prop="no" label="商品编号" show-overflow-tooltip :formatter="formatterTableCell"></el-table-column>
          <el-table-column min-width="150px" prop="name" label="商品名称" show-overflow-tooltip :formatter="formatterTableCell"></el-table-column>
          <el-table-column min-width="150px" prop="specification" label="规格型号" show-overflow-tooltip :formatter="formatterTableCell"></el-table-column>
          <el-table-column min-width="150px" prop="unitName" label="单位" show-overflow-tooltip :formatter="formatterTableCell"></el-table-column>
        </el-table>

        <div class="pagination-box">
          <div class="selected-num">已选{{ multipleSelection.length }}</div>
          <el-pagination 
            @size-change="handleSizeChange" 
            @current-change="handleCurrentChange"
            :current-page="paginationConfig.currentPage" 
            :page-sizes="[10, 30, 50, 100, 200, 500]"
            :page-size="paginationConfig.pageSize" 
            layout="total, sizes, prev, pager, next, jumper"
            :total="paginationConfig.total"
          ></el-pagination>
        </div>
      </div>
    </div>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="visible = false">取 消</el-button>
        <el-button type="primary" @click="submit">确 认</el-button>
      </div>
    </template>
  </el-dialog>

  <!-- 图片预览 -->
  <el-image-viewer v-if="previewImgShow" :zoom-rate="1.2" :url-list="[previewImg]"
    @close="previewImgShow = false; previewImg = ''" fit="cover" />
</template>
<script lang='ts' setup>
import { ref, reactive, nextTick } from 'vue';
import { getProductPagePublic } from '@/api/commodityApi/storeApi'
import { categoryTreePublic } from '@/api/commodityApi/classifyApi'

type goodsType = {
  id: string
}

const props = defineProps({
  title: {
    type: String,
    default: '选择商品'
  },
  isRadio: { // 是否单选
    type: Boolean,
    default: false,
  },
})

const emit = defineEmits<{
  (e: 'submit', data: any[]): void
}>()

let visible = ref(false)
let loading = ref(false)

let searchParams = ref({
  no: '',
  name: '',
  category: [],
  publishStatus: 1, // 上架状态
  specification: '', // 规格型号
})
let categoryOptions = ref<any[]>([])
let tableData = ref<any>([])
let multipleSelection = ref<any[]>([])
let paginationConfig = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0
})

let previewImgShow = ref(false)
let previewImg = ref('')

let multipleTableRef = ref()

const formatterTableCell = (row, column, cellValue, index) => {
  return ['', null].includes(cellValue) ? '- -' : cellValue
}

const show = (checkedGoods?: goodsType[]) => {
  multipleSelection.value = checkedGoods ? JSON.parse(JSON.stringify(checkedGoods)) : []
  visible.value = true;
  onSearch()
  getCategoryTree()
}

const previewImage = url => {
  previewImg.value = url
  previewImgShow.value = true
}

const getCategoryTree = () => {
  categoryTreePublic({ name: '' }).then(res => {
    categoryOptions.value = res.items
  })
}

// 选中状态处理
const initCheckedStatus = () => {
  nextTick(() => {
    let checkedIds = multipleSelection.value.map(v => v.id);
    multipleTableRef.value!.clearSelection(); // 清空所有选中
    if (checkedIds.length) {
      let checkedArr = tableData.value.filter(item => { return checkedIds.includes(item.id) })
      checkedArr.forEach(row => {
        multipleTableRef.value!.toggleRowSelection(row, undefined);
      })
    }
  })
}

const handleSelectAll = (selection: any[], pageSelectionData: any[]) => {
  let checked = selection.length ? true : false; // selection为空数组时代表取消全选
  if(checked){
    let mIds = multipleSelection.value.map(v=>v.id);
    let filterData = pageSelectionData.filter(v=>{ // 筛选出非重复项
      return !mIds.includes(v.id)
    })
    multipleSelection.value = multipleSelection.value.concat(filterData);
  }else{
    let pIds = pageSelectionData.map(v=>v.id);
    multipleSelection.value = multipleSelection.value.filter(v => !pIds.includes(v.id))
  }
}

const handleSelectItem = (selection: any[], row) => {
  console.log(selection)
  let checked = !multipleSelection.value.map(v=>v.id).includes(row.id); // 选项原先是否存在于已选数组中,取反表示当前操作
  if (props.isRadio) {
    // 单选
    multipleTableRef.value!.clearSelection();
    multipleTableRef.value!.toggleRowSelection(row, checked);
    multipleSelection.value = checked ? [row] : [];
  } else {
    // 多选
    multipleTableRef.value!.toggleRowSelection(row, checked);
    multipleSelection.value = checked ? multipleSelection.value.filter(v => v.id !== row.id).concat([row]) : multipleSelection.value.filter(v => v.id !== row.id)
  }
}

const handleSizeChange = val => {
  paginationConfig.currentPage = 1
  paginationConfig.pageSize = val
  getList()
}

const handleCurrentChange = val => {
  paginationConfig.currentPage = val
  getList()
}

const getList = () => {
  let { category } = searchParams.value
  let params = {
    ...searchParams.value,
    categoryId: category && category.length ? category[category.length - 1] : null,
    skipCount: (paginationConfig.currentPage - 1) * paginationConfig.pageSize,
    maxResultCount: paginationConfig.pageSize
  }
  loading.value = true
  getProductPagePublic(params)
    .then(res => {
      tableData.value = res.items
      paginationConfig.total = res.totalCount
      initCheckedStatus()
    }).finally(() => {
      loading.value = false
    })
}

const onSearch = () => {
  paginationConfig.currentPage = 1
  getList()
}

const onReset = () => {
  searchParams.value = {
    no: '',
    name: '',
    category: [],
    publishStatus: 1, // 上架状态
    specification: '', // 规格型号
  }
  onSearch();
}

const submit = () => {
  console.log('选中商品:', multipleSelection.value)
  emit('submit', multipleSelection.value)
  visible.value = false;
}

const close = () => {
  tableData.value = [];
  multipleSelection.value = [];
};

defineExpose({
  show,
})

</script>
<style lang='scss' scoped>
.dialog-content {
  .table-box {
    padding: 20px;
    background-color: #ffffff;
    border: 1px solid #d8e2f0;
    box-shadow: 0px 12px 36px 0px rgba(211, 215, 221, 0.4);
    border-radius: 8px;
    .hidden-checkbox-all :deep(.el-table__header .el-table-column--selection .el-checkbox){ // 隐藏全选checkbox
      display: none;
    }
    .pagination-box {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-top: 10px;
      .selected-num {
        font-size: 14px;
        color: #666;
      }

      :deep(.el-pagination) {
        margin-top: 0;
      }
    }
  }
}
</style>
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值