Bootstrap

封裝el-table

<template>
  <div :ref="mainRef" :key="mainRef" class="table-main">
    <div v-if="isShow('query') || isShow('button')" :ref="searchRef">
      <el-row
        v-if="isShow('button')"
        :gutter="10"
        style="margin: 0 0 5px 0"
        class="header-buttons"
      > 
          <slot name="button" :single="single" :multiple="multiple" /> 
      </el-row>
      <el-form
        v-if="showSearch && isShow('query')"
        ref="queryForm"
        :model="queryParams"
        :label-width="labelWidth"
        class="table-query-form"
      >
        <el-row>
          <slot
            name="query"
            :handleQuery="handleQuery"
            :isExpandAll="isExpandAll"
          />
          <el-col :xs="24" :sm="12" :md="8" :lg="6" style="padding-left: 10px"> 
              <el-button icon="el-icon-search" @click="handleQuery"> 搜索</el-button>
              <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
              <el-tag v-if="showExpandQuery" type="success" plain style="cursor: pointer; margin-left: 10px" @click="toggleExpandAll">
                <i :class="isExpandAll ? 'el-icon-sort-up' : 'el-icon-sort-down'" />
                {{ isExpandAll ? "折叠" : "展开" }}
              </el-tag> 
          </el-col>
        </el-row>
      </el-form>
    </div>
    <el-table
      v-if="isShow('table')"
      :ref="tableRef"
      :key="tableRef"
      v-loading="loading"
      :height="height === 'auto' ? null : height || tableHeight"
      :row-key="rowKey"
      :data="listData"
      v-bind="$attrs"
      :highlight-current-row="!hasCheckbox"
      @selection-change="handleSelectionChange"
      @current-change="handleCurrentChange"
      v-on="$listeners"
    >
      <slot name="table" :page="{pageNum,pageSize}" />
    </el-table>
    <el-pagination
      v-show="total > 0"
      class="pagination-container"
      :current-page.sync="currentPageNum"
      :page-size.sync="currentPageSize"
      :layout="layout"
      :page-sizes="pageSizes"
      :pager-count="pagerCount"
      :total="total"
      v-bind="$attrs"
      @size-change="handlePageSizeChange"
      @current-change="handlePageCurrentChange"
    />
  </div>
</template>

<script>
export default {
  name: 'ListTable',
  props: {
    queryParams: {
      type: Object,
      default: () => {}
    },
    tableData: {
      type: Array,
      default: () => null
    },
    // 列表整体高度
    height: {
      type: [Number, String],
      default: null
    },
    // 其它高度(用于自动计算高度时,除查询条件高度后的其它高度)
    otherHeight: {
      type: Number,
      default: 0
    },
    // 本地分页
    localPage: {
      type: Boolean,
      default: false
    },
    // 默认是否加载数据
    defaultLoadData: {
      type: Boolean,
      default: true
    },
    // 默认是否加载数据
    labelWidth: {
      type: String,
      default: '120px'
    },
    showExpandQuery: {
      type: Boolean,
      default: true
    },
    // 行数据的 Key,用来优化 Table 的渲染;在使用 reserve-selection 功能与显示树形数据时,该属性是必填的。类型为 String 时,支持多层访问:user.info.id,但不支持 user.info[0].id,此种情况请使用 Function
    rowKey: {
      type: [Function, String],
      default: 'id'
    },
    pageNum: {
      type: Number,
      default: 1
    },
    pageSize: {
      type: Number,
      default: 20
    },
    pageSizes: {
      type: Array,
      default() {
        return [10, 20, 30, 50]
      }
    },
    // 移动端页码按钮的数量端默认值5
    pagerCount: {
      type: Number,
      default: document.body.clientWidth < 992 ? 5 : 7
    },
    layout: {
      type: String,
      default: 'total, prev, pager, next, jumper'
    }
  },
  data () {
    return {
      mainRef: `main${Math.random()}`,
      // 表名
      tableRef: `list${Math.random()}`,
      // 查询
      searchRef: `search${Math.random()}`,
      // 遮罩层
      loading: false,
      // 选中数组
      selectData: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总数
      total: 0,
      // 表格数据
      listData: [],
      // 本地分页所有数据
      localPageData: [],
      // 高度
      tableHeight: null,
      // 展开折叠
      isExpandAll: false,
      // 新增数据集
      addData: [],
      // 删除数据集
      delData: [],
      // 已选数据rowKey集
      selectRowKeyDatas: [],
      // 是否有复选框
      hasCheckbox: false,
      // 页码
      currentPageNum: this.pageNum,
      // 每页多少
      currentPageSize: this.pageSize
    }
  },
  watch: {
    pageNum(newVal) {
      this.currentPageNum = newVal
    },
    pageSize(newVal) {
      this.currentPageSize = newVal
    },
    showSearch: {
      handler (route) {
        this.resizeTableHeight()
      },
      immediate: true
    },
    otherHeight: {
      handler (route) {
        this.resizeTableHeight()
      },
      immediate: true
    },
    tableData: {
      deep: true,
      handler (val) {
        if (val) {
          if (this.localPage) {
            this.localPageData = val
            this.total = this.localPageData.length
            this.getLocalPageList()
          } else {
            this.listData = val
          }
        }
      },
      immediate: true
    }
  },
  created () {
    const _this = this
    if (_this.defaultLoadData) {
      _this.getList()
    }
    _this.addData = []
    _this.delData = []
    window.addEventListener('resize', function () {
      _this.$nextTick(() => {
        _this.resizeTableHeight()
      })
    })
    this.$nextTick(() => {
      _this.checkForCheckbox()
    })
  },
  methods: {
    // 判断slot存在否
    isShow (name) {
      const flag =
        this.$slots[name] !== undefined ||
        this.$scopedSlots[name] !== undefined
      return flag
    },
    /* 计算table高度 */
    resizeTableHeight () {
      if (this.height === 'auto') {return}
      this.$nextTick(() => {
        // 浏览器呈现高度-搜索框居顶部位置-搜索框高度=列表高度
        let height = window.innerHeight
        if (this.$refs[this.tableRef]) {
          const rect = this.$refs[this.tableRef].$el.getBoundingClientRect()
          height = height - rect.y

          if (this.total === 0) {
            height = height - 18
          } else {
            height = height - 60
          }
          this.tableHeight = height - this.otherHeight
        }
      })
    },
    /** 查询列表 */
    getList (type) {
      if (type === 1 && this.localPage) {
        this.getLocalPageList()
      }
      // 本地分页-点击搜索调用搜索方法
      if (this.$listeners['query']) {
        this.$emit('query', this.queryParams)
      } else {
        if (!this.$listeners['loadData']) {return}
        this.loading = true
        const params = Object.assign({ pageNum: this.currentPageNum, pageSize: this.currentPageSize }, this.queryParams)
        this.$emit('loadData', params, (callback) => {
          if (callback && callback.rows) {
            this.listData = callback.rows
            this.total = callback.total
          } else {
            this.listData = []
            this.total = 0
          }

          this.loading = false
          this.selectRowSelection()
          this.resizeTableHeight()
        })
      }
    },
    /** 本地分页 */
    getLocalPageList() {
      this.listData = this.localPageData.slice(
        (this.currentPageNum - 1) * this.currentPageSize,
        this.currentPageNum * this.currentPageSize
      )
      this.selectRowSelection()
      this.resizeTableHeight()
    },
    /** 搜索按钮操作 */
    handleQuery () {
      this.currentPageNum = 1
      this.getList()
    },
    /** 重置按钮操作 */
    resetQuery () {
      if (this.$listeners['resetQuery']) {
        this.$emit('resetQuery', {}, (callback) => {
          this.handleQuery()
        })
      } else {
        this.$refs.queryForm.resetFields()
        this.handleQuery()
      }
    },
    /** 多选框选中数据 */
    handleSelectionChange(selection) {
      const _this = this
      if (!_this.addData) _this.addData = []
      if (!_this.delData) _this.delData = []
      _this.listData.forEach((row) => {
        const selected = selection.indexOf(row) !== -1
        const rowKey = _this.selectRowKeyDatas.find(t => t === row[_this.rowKey])
        const rowAdd = _this.addData.find(t => {return t[_this.rowKey] === row[_this.rowKey] })
        if (selected && !rowKey) {
          _this.addData.push(row)
          _this.selectRowKeyDatas.push(row[_this.rowKey])
          const dd = _this.delData.find(t => t[_this.rowKey] === row[_this.rowKey])
          if (dd) {
            const index = _this.delData.indexOf(dd)
            if (index > -1) {
              _this.delData.splice(index, 1)
            }
          }
        }
        if (!selected && rowAdd) {
          const index = _this.addData.indexOf(rowAdd)
          if (index > -1) {
            _this.addData.splice(index, 1)
          }
        }
        if (!selected && rowKey) {
          _this.delData.push(row)
          const index = _this.selectRowKeyDatas.indexOf(rowKey)
          if (index > -1) {
            _this.selectRowKeyDatas.splice(index, 1)
          }
        }
        if (selected && rowKey) {
          const dd = _this.delData.find(t => t[this.rowKey] === rowKey)
          if (dd) {
            const index = _this.delData.indexOf(dd)
            if (index > -1) {
              _this.delData.splice(index, 1)
            }
          }
        }
      })
      _this.setSelectData()
      _this.$emit('selectionChange', selection)
    },
    /** 单选选中数据 */
    handleCurrentChange(selection) {
      if (!this.hasCheckbox && selection) {
        this.addData = []
        this.addData.push(selection)
        this.setSelectData()
      }
      this.$emit('currentChange', selection)
    },
    /** 设置已选集合 */
    setSelectDatas(val) {
      this.checkForCheckbox()
      if (val) {
        if (!Array.isArray(val)) {
          val = [val]
        }
        if (val.length === 0) {
          this.clearSelectDatas()
        } else {
          if (this.$refs[this.tableRef]) {
            this.$refs[this.tableRef].clearSelection()
            this.$refs[this.tableRef].setCurrentRow()
          }
          this.selectRowKeyDatas = this.$vzcCopyObj(val)
          this.selectRowSelection()
        }
      }
    },
    /** 清除选中集合*/
    clearSelectDatas() {
      this.selectRowKeyDatas = []
      this.delData = []
      this.addData = []
      if (this.$refs[this.tableRef]) {
        this.$refs[this.tableRef].clearSelection()
        this.$refs[this.tableRef].setCurrentRow()
      }
    },
    /** 设置选中行*/
    selectRowSelection() {
      if (this.hasCheckbox) {
        this.selectRowKeyDatas.forEach(t => {
          const data = this.listData.find(m => {
            return m[this.rowKey] === t
          })
          if (data) {
            this.$nextTick(() => {
              this.$refs[this.tableRef].toggleRowSelection(data, true)
            })
          }
        })
      } else {
        this.selectRowKeyDatas.forEach(t => {
          const data = this.listData.find(m => {
            return m[this.rowKey] === t
          })
          if (data) {
            this.$refs[this.tableRef].setCurrentRow(data)
          }
        })
      }

      if (!this.addData) this.addData = []
      this.addData.forEach(t => {
        if (t) {
          const data = this.listData.find(m => {
            return m[this.rowKey] === t[this.rowKey]
          })
          if (data) {
            if (this.hasCheckbox) {
              this.$refs[this.tableRef].toggleRowSelection(data, true)
            } else {
              this.$refs[this.tableRef].setCurrentRow(data)
            }
          }
        }
      })
    },
    /** 获取选中行值 */
    getSelectData() {
      return { addList: this.addData, delList: this.delData }
    },
    /** 设置选中对象是单还是集合 */
    setSelectData() {
      this.single = this.addData.length !== 1
      this.multiple = !this.addData.length
    },
    /** 是否有复选框 */
    checkForCheckbox() {
      if (this.$refs[this.tableRef] && this.$refs[this.tableRef].$children) {
        this.hasCheckbox = this.$refs[this.tableRef].$children.some(column => {
          return column.type === 'selection'
        })
      }
    },
    // 展开折叠
    toggleExpandAll () {
      this.isExpandAll = !this.isExpandAll
      this.resizeTableHeight()
    },
    setCurrentRow (row) {
      this.$nextTick(() => {
        this.$refs[this.tableRef].setCurrentRow(row)
      })
    },
    handlePageSizeChange(val) {
      if (this.currentPageNum * val > this.total) {
        this.currentPageNum = 1
      }
      this.getList(1)
      this.$emit('pagination', { pageNum: this.currentPageNum, pageSize: val })
    },
    handlePageCurrentChange(val) {
      this.currentPageNum = val
      this.getList(1)
      this.$emit('pagination', { pageNum: val, pageSize: this.currentPageSize })
    }
  }
}
</script>

<style lang="scss" scoped>
//主色
$main-color:#2C91FF;
//背景
$bg-color1: #ACABAB;
$bg-color2: #021E3A;
$bg-color3: #094AA4;
$bg-color4: #F5A623;
$bg-color5: #0D2B65;
$bg-color6: #02192C;
$bg-color7: #FFFFFF;
$bg-color8: #4e4949;
//线条色
$link-color1: #043f78;
$link-color2:#094AA4;
$link-color3:#F5A623;
$link-color4:#E4E7ED;
//字体色
$font-color1:#C1E9FF;
$font-color2:#FFFFFF;
$font-color3:#05A5FF;
$font-color4:#F5A623;
$font-color5:#D2CFCF;
$font-color6:#06EB00;
//辅色
$fu-color1:#F5A623;
$fu-color2:#F71617;
$fu-color3:#50E3C2;
$fu-color4:#06EB00;
$fu-color5:#8D8E8D;
$fu-color6:#BD8628;

.table-main {
  .table-query-form {
    background: linear-gradient($bg-color5, $bg-color3);
    padding: 5px;
    margin-bottom: 5px;
  }
  .pagination-container {
    float: right;
    background: transparent;
    padding: 5px 10px;
    margin-top: 10px;
    white-space:nowrap;
    color:$font-color1 !important;
    font-weight:700 ;

    ::v-deep {

      & button:hover {
        background: radial-gradient(ellipse at center, $bg-color3, $bg-color4);
        border-color: $link-color3;
        color: $fu-color1;
      }

      & button:disabled {
        color: #fff;
        background-color: $link-color1;
        cursor: default
      }

      & .btn-next, &n .btn-prev {
        background: radial-gradient(ellipse at center, $bg-color2, $bg-color3);
        border: 1px solid $main-color;
        color: $font-color1;
      }

      & .el-pager li.disabled {
        color: #fff;
        cursor: default
      }

      .el-pagination__sizes {
        margin: 0 10px 0 0;
        font-weight: 400;
        color: $font-color1
      }

      .el-pagination__sizes .el-input .el-input__inner {
        font-size: 13px;
        padding-left: 8px
      }

      .el-pagination__sizes .el-input .el-input__inner:hover {
        border-color: $main-color
      }

      .el-pagination__total {
        margin-right: 10px;
        font-weight: 400;
        color: $font-color1
      }

      .el-pagination__jump {
        margin-left: 24px;
        font-weight: 400;
        color: $font-color1
      }

      &.is-background .btn-next, &.is-background .btn-prev, &.is-background .el-pager li {
        margin: 0 5px;
        background-color: #113558;
        color: $font-color1;
        min-width: 30px;
        border-radius: 2px
      }

      &.is-background .btn-next.disabled, &.is-background .btn-prev.disabled, &.is-background .el-pager li.disabled {
        color: #fff
      }

      &.is-background .btn-next:disabled, &.is-background .btn-prev:disabled {
        background: radial-gradient(ellipse at center, $bg-color2, $bg-color3);
        border: 1px solid $main-color;
        color: $font-color1;
      }

      &.is-background .el-pager li:not(.disabled):hover {
        background: radial-gradient(ellipse at center, $bg-color3, $bg-color4);
        border-color: $link-color3;
        color: $fu-color1;
      }

      &.is-background .el-pager li:not(.disabled).active {
        background: radial-gradient(ellipse at center, $bg-color3, $bg-color4);
        border-color: $link-color3;
        color: $fu-color1;
      }

      .el-pager li {
        background:radial-gradient(ellipse at center, $bg-color2, $bg-color3);
        border: 1px solid $main-color;
        color: $font-color1;
      }

      .el-pager li.btn-quicknext, .el-pager li.btn-quickprev {
        line-height: 28px;
        color: $font-color1
      }

      .el-pager li.btn-quicknext.disabled, .el-pager li.btn-quickprev.disabled {
        color: #fff
      }

      .el-pager li.active + li {
        border-left: 1px solid $main-color;
      }

      .el-pager li:hover {
        color: $fu-color1;
      }

      .el-pager li.active {
        background: radial-gradient(ellipse at center, $bg-color3, $bg-color4);
        border-color: $link-color3;
        color: $fu-color1;
        cursor: default
      }
    }
  }
}
</style>

;