vue+elementUI实现表格组件的封装

效果图:

在父组件使用表格组件
js 复制代码
<table-list
          ref="table"
          :stripe="true"
          :loading="loading"
          :set-table-h="slotProps.setMainCardBodyH"
          :table-data="tableData"
          :columns="columns.tableList || []"
          :radio="topicTypeListMapItem.radio"
          show-opear
          show-selection
          :fix-width="240"
          :page-data="pageData"
          :disabled-select-key="topicTypeListMapItem.disabledSelectKey"
          @changeSize="changeSize"
          @changePage="changePage"
        >
          <!--预警灯-->
          <template #warningLight="{ row }">
            <div class="lightCireBlock">
              <div>
                <div v-for="(item,index) in row.warningLight" :key="'warningLight'+index">
                  <svg-icon v-if="Object.keys(columns.warningLight).includes(item)" :icon-class="columns.warningLight[item].icon" class="task-icon" />
                  <span
                    v-if="columns.warningLight[item] && columns.warningLight[item].hasNum && row[`warningLight${item}`] * 1 > 0"
                    class="fontBoxAll fontBox"
                  >{{ row[`warningLight${item}`] }}</span>
                </div>
              </div>
            </div>
          </template>
          <!--标题-->
          <template #taskTitle="{ row }">
            <span class="td-cell lineCell" @click="editMainEvent(row)"> {{ row.taskTitle }}</span>
          </template>
          <!--状态-->
          <template #status="{ row }">
            <el-tag :disable-transitions="true" :class="columns.statusLists[row.status].className">{{ columns.statusLists[row.status].label }}</el-tag>
          </template>
          <!--操作-->
          <template slot-scope="{ row }">
            <div class="table-buttonList">
              <template v-for="(item,index) in columns.btnList">
                <el-button
                  v-if="item.isShow?item.isShow(row):true"
                  :key="index"
                  type="text"
                  @click="btnFn[item.clickFn.fn](row,...(item.clickFn.params?item.clickFn.params:[]))"
                >{{ item.name }}</el-button>
              </template>
            </div>
          </template>
        </table-list>

然后用一个js文件来存储动态的参数

js 复制代码
import { selectOverseerMeetingLeaType } from '@/api/common.js'

export const topicTypeListMap = new Map([
  [
    'keyWork',
    {
      name: '重点工作',
      radio: false, // 表格数据是否单选
      showImportBtn: true, // 是否显示批量导入按钮
      showApprovalBtn: false, // 是否显示送立项按钮
      showImportApprovalBtn: false, // 导入时是否显示送立项按钮
      showItemTypeBtn: false, // 导入时是否显示选择事项类型按钮
      showWorkRequireBtn: false, // 导入时是否显示批量填写工作要求按钮
      showFoliCureBtn: false, // 是否显示送办理按钮
      hasSubtasks: true, // 专题是否有子任务
      isMeetingTopicType: false, // 是否是会议类专题
      disabledSelectKey: [{ label: 'status', value: '0' }, { label: 'status', value: '1' }] // 表格哪些状态的数据禁选
    }
  ],
  [
    'feasibleInstructions',
    {
      name: '督办件',
      radio: false,
      showImportBtn: true,
      showApprovalBtn: true,
      showImportApprovalBtn: true, // 导入时是否显示送立项按钮
      showItemTypeBtn: true, // 导入时是否显示选择事项类型按钮
      showWorkRequireBtn: false, // 导入时是否显示批量填写工作要求按钮
      showFoliCureBtn: true,
      hasSubtasks: false,
      isMeetingTopicType: false,
      disabledSelectKey: []
    }
  ]

])
在组件中

props传的参数

js 复制代码
props: {
    // 表格数据
    tableData: {
      type: Array,
      default: () => {
        return []
      }
    },
    // 表格字段
    columns: {
      type: Array,
      default: () => {
        return []
      }
    },
    // 表格字段的宽度
    fixWidth: {
      type: Number,
      default: 150
    },
    // 单选
    radio: {
      type: Boolean,
      default: false
    },
    // 显示序号
    showIndex: {
      type: Boolean,
      default: false
    },
    // 是否自定义高度
    setTableH: {
      type: Boolean,
      default: true
    },
    // 显示勾选
    showSelection: {
      type: Boolean,
      default: false
    },
    // 显示操作
    showOpear: {
      type: Boolean,
      default: false
    },
    spanMethod: {
      type: Function,
      default() {
        return () => {}
      }
    },
    // 斑马线
    stripe: {
      type: Boolean,
      default: false
    },
    // 分页
    showPagination: {
      type: Boolean,
      default: true
    },
    setCheckbox: {
      type: Boolean,
      default: false
    },
    loading: { // 判断是否正在加载表格数据
      type: Boolean,
      default: false
    },
    align: {
      type: String,
      default: 'left'
    },
    recordCheck: { // 是否在翻页的时候记录之前选择的表格数据
      type: Boolean,
      default: true
    },
    // 分页数据
    pageData: {
      type: Object,
      default: () => {
        return {
          page: 1,
          rows: 15,
          pageSize: [15, 25, 35],
          total: 0
        }
      }
    },
    isImportData: { // 判断是否导入的数据
      type: Boolean,
      default: false
    },
    disabledSelectKey: { // 需要禁用的选择框的数据
      type: Array,
      default: () => {
        return []
      }
    }
  },

完整代码

js 复制代码
<template>
  <div ref="wrap" class="wrap">
    <el-table
      ref="table"
      :key="'tableData'+tableData.length"
      v-loading="loading"
      element-loading-text="正在处理,请稍候!"
      :data="tableData"
      :stripe="stripe"
      :span-method="spanMethod"
      :header-row-class-name="headerRowClassName(radio)"
      :height="setTableH?setTableHeight:'100%'"
      class="table-list"
      border
      @sort-change="sortChange"
      @selection-change="handleSelectionChange"
    >
      <template slot="empty">
        <img src="@/assets/images/empty.png">
        <p>暂无数据</p>
      </template>

      <!--选择框-->
      <el-table-column v-if="showSelection" type="selection" width="42" :selectable="checkSelectable" />
      <!--序号-->
      <el-table-column v-if="showIndex" type="index" label="序号" width="55" align="center" />
      <el-table-column
        v-for="item in columns"
        :key="item.lable"
        :label="item.title"
        :prop="item.slot || item.lable"
        :min-width="item.minWidth || 140"
        :width="item.width"
        :sortable="item.sortable"
        :show-overflow-tooltip="item.overflow"
        :align="item.align"
      >
        <!-- 二级表头 -->
        <el-table-column
          v-for="subItem in item.columns"
          :key="subItem.label"
          :label="subItem.title"
          :prop="subItem.slot || subItem.lable"
          :min-width="subItem.minWidth || 140"
          :width="subItem.width"
          :sortable="subItem.sortable"
          :show-overflow-tooltip="subItem.overflow"
          :align="subItem.align"
        >
          <template slot="header">
            <!-- <span v-if="item.isRequest" style="color:red">*</span> -->
            <span> {{ subItem.title }}</span>
          </template>
          <template slot-scope="{ row }">
            <slot :name="subItem.slot" :row="row">
              <span class="td-cell"> {{ isImportData ? row[subItem.label].value : row[subItem.label] }}</span>
              <template v-if="isImportData">
                <el-popover
                  v-if="typeof(row[subItem.label].success) === 'boolean' && !row[subItem.label].success"
                  placement="top"
                  title=""
                  trigger="hover"
                  :content="row[subItem.label].message"
                >
                  <i slot="reference" class="el-icon-warning" style="color:#F44336" />
                </el-popover>
              </template>
            </slot>
          </template>
        </el-table-column>
        <template slot="header">
          <!-- <span v-if="item.isRequest" style="color:red">*</span> -->
          <span> {{ item.title }}</span>
        </template>
        <template slot-scope="{ row }">
          <slot :name="item.slot" :row="row">
            <span class="td-cell">{{ isImportData ? row[item.label].value : row[item.label] }}</span>
            <template v-if="isImportData">
              <el-popover
                v-if="typeof(row[item.label].success) === 'boolean' && !row[item.label].success"
                placement="top"
                title=""
                trigger="hover"
                :content="row[item.label].message"
              >
                <i slot="reference" class="el-icon-warning" style="color:#F44336" />
              </el-popover>
            </template>
          </slot>
        </template>
      </el-table-column>

      <!--操作按钮-->
      <el-table-column v-if="showOpear" label="操作" prop="operation" :width="fixWidth" fixed="right">
        <template #default="{ row, $index }">
          <slot :row="row" :$index="$index" />
        </template>
      </el-table-column>

    </el-table>
    <el-pagination
      v-if="showPagination && tableData.length>0"
      background
      :current-page="pageData.page"
      :page-size="pageData.rows"
      :page-sizes="pageData.pageSize"
      layout="sizes, prev, pager, next, slot, jumper"
      :total="pageData.total"
      class="pagination"
      @current-change="handleCurrentChange"
      @size-change="handleSizeChange"
    >
      <span class="pagination-total">共{{ pageData.total }}条,</span>
    </el-pagination>
  </div>
</template>

<script>
import { clone } from 'lodash'
export default {
  name: 'TableList',
  props: {
    // 表格数据
    tableData: {
      type: Array,
      default: () => {
        return []
      }
    },
    // 表格字段
    columns: {
      type: Array,
      default: () => {
        return []
      }
    },
    // 表格字段的宽度
    fixWidth: {
      type: Number,
      default: 150
    },
    // 单选
    radio: {
      type: Boolean,
      default: false
    },
    // 显示序号
    showIndex: {
      type: Boolean,
      default: false
    },
    // 是否自定义高度
    setTableH: {
      type: Boolean,
      default: true
    },
    // 显示勾选
    showSelection: {
      type: Boolean,
      default: false
    },
    // 显示操作
    showOpear: {
      type: Boolean,
      default: false
    },
    spanMethod: {
      type: Function,
      default() {
        return () => {}
      }
    },
    // 斑马线
    stripe: {
      type: Boolean,
      default: false
    },
    // 分页
    showPagination: {
      type: Boolean,
      default: true
    },
    setCheckbox: {
      type: Boolean,
      default: false
    },
    loading: { // 判断是否正在加载表格数据
      type: Boolean,
      default: false
    },
    align: {
      type: String,
      default: 'left'
    },
    recordCheck: { // 是否在翻页的时候记录之前选择的表格数据
      type: Boolean,
      default: true
    },
    // 分页数据
    pageData: {
      type: Object,
      default: () => {
        return {
          page: 1,
          rows: 15,
          pageSize: [15, 25, 35],
          total: 0
        }
      }
    },
    isImportData: { // 判断是否导入的数据
      type: Boolean,
      default: false
    },
    disabledSelectKey: { // 需要禁用的选择框的数据
      type: Array,
      default: () => {
        return []
      }
    }
  },
  data() {
    return {
      saveMultipleSelection: {}, // 用来保存每个分页勾选的数据
      multipleSelection: [],
      setTableHeight: 300
    }
  },
  computed: {
    headerRowClassName() {
      return (radio) => {
        if (radio) {
          return 'tableHead isRadio'
        } else {
          return 'tableHead'
        }
      }
    }
  },
  watch: {
    tableData: {
      handler(newVal) {
        this.$nextTick(() => {
          if (this.multipleSelection && this.recordCheck) {
            const idList = this.multipleSelection.map(item => { return item.businessKey })
            newVal.forEach(item => {
              if (idList.indexOf(item.businessKey) > -1) {
                this.$refs.table.toggleRowSelection(item, true)
              }
            })
          }
        })
      },
      deep: true
    },
    saveMultipleSelection: {
      handler(newVal) {
        this.multipleSelection = Object.keys(newVal).reduce((prev, next) => {
          prev = prev.concat(newVal[next])
          return [...new Set(prev)]
        }, [])
        this.$EventBus.$emit('selection-change', this.multipleSelection)
      },
      deep: true
    }
  },
  updated() {
    this.$nextTick(() => {
      this.setTableHeight = this.$refs.wrap.offsetHeight - document.querySelector('.el-table__header-wrapper').offsetHeight - 48 - 10
      // 10是给分页腾一点空间
    })
  },
  methods: {
    handleSizeChange(e) {
      this.$emit('changeSize', e)
    },
    handleCurrentChange(e) {
      this.$emit('changePage', e)
    },
    clearSort() {
      this.$refs.table.clearSort()
    },
    getSelection() {
      return clone(this.$refs.table.selection)
    },
    sortChange(e) {
      this.$emit('sortChange', e)
    },
    checkSelectable(row) {
      let check = true
      if (this.disabledSelectKey.length > 0) {
        this.disabledSelectKey.forEach(item => {
          if (row[item.label] === item.value) {
            check = false
          }
        })
      }
      return check
    },
    handleSelectionChange(val) {
      if (this.radio) { // 单选
        // var newRows = val.filter((it, index) => {
        //   if (index === val.length - 1) {
        //     this.$refs.table.toggleRowSelection(it, true)
        //     return true
        //   } else {
        //     this.$refs.table.toggleRowSelection(it, false)
        //     return false
        //   }
        // })
        // this.saveMultipleSelection = newRows
        this.$nextTick(() => {
          var newRows = val.filter((it, index) => {
            if (index === val.length - 1) {
              this.$refs.table.toggleRowSelection(it, true)
              return true
            } else {
              this.$refs.table.toggleRowSelection(it, false)
              return false
            }
          })
          if (val.length > 0) {
            this.saveMultipleSelection = {}
            this.$set(this.saveMultipleSelection, this.pageData.page, newRows)
          }
          // this.saveMultipleSelection = newRows
        })
      } else {
        this.$nextTick(() => {
          this.$set(this.saveMultipleSelection, this.pageData.page, val)
        })
      }
    },
    toggleRowSelection(row, bool) {
      this.$refs.table.toggleRowSelection(row, bool)
    }
  }

}
</script>
<style scoped lang="scss">
   .wrap{
     height: 100%;
      ::v-deep .isRadio {
        .el-checkbox{
          display: none;
        }
      }
   }
   ::v-deep .el-table ::-webkit-scrollbar{
      height: 8px;
    }
</style>
相关推荐
小远yyds16 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
程序媛小果35 分钟前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
小光学长39 分钟前
基于vue框架的的流浪宠物救助系统25128(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
数据库·vue.js·宠物
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱1 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai1 小时前
uniapp
前端·javascript·vue.js·uni-app
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓3 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4113 小时前
无网络安装ionic和运行
前端·npm
理想不理想v3 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试