一个基于vue功能强大的表格组件--vxe-table的二次封装

时间:2024-10-17 07:39:01

基础使用

一个基于 vue 的 PC 端表格组件,支持增删改查、虚拟滚动、懒加载、快捷菜单、数据校验、树形结构、打印导出、表单渲染、数据分页、虚拟列表、模态窗口、自定义模板、渲染器、贼灵活的配置项、扩展接口等…

<vxe-grid v-bind="gridOptions1">
          <template #toolbar_buttons>
            <vxe-button @click="gridOptions1.align = 'left'">居左</vxe-button>
            <vxe-button @click="gridOptions1.align = 'center'">居中</vxe-button>
            <vxe-button @click="gridOptions1.align = 'right'">居右</vxe-button>
          </template>
        </vxe-grid>

 

export default {
          data () {
            return {
              gridOptions1: {
                border: true,
                resizable: true,
                showOverflow: true,
                height: 300,
                align: 'left',
                toolbarConfig: {
                  slots: {
                    // 自定义工具栏模板
                    buttons: 'toolbar_buttons'
                  }
                },
                columns: [
                  { type: 'seq', width: 50 },
                  { field: 'name', title: 'Name' },
                  { field: 'sex', title: 'Sex', showHeaderOverflow: true },
                  { field: 'address', title: 'Address', showOverflow: true }
                ],
                data: [
                  { id: 10001, name: 'Test1', nickname: 'T1', role: 'Develop', sex: 'Man', age: 0, address: 'Shenzhen' },
                  { id: 10002, name: 'Test2', nickname: 'T2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' },
                  { id: 10003, name: 'Test3', nickname: 'T3', role: 'PM', sex: 'Man', age: 100, address: 'Shanghai' },
                  { id: 10004, name: 'Test4', nickname: 'T4', role: 'Designer', sex: 'Women', age: 70, address: 'Shenzhen' },
                  { id: 10005, name: 'Test5', nickname: 'T5', role: 'Develop', sex: 'Women', age: 10, address: 'Shanghai' },
                  { id: 10006, name: 'Test6', nickname: 'T6', role: 'Designer', sex: 'Women', age: 90, address: 'Shenzhen' },
                  { id: 10007, name: 'Test7', nickname: 'T7', role: 'Test', sex: 'Man', age: 5, address: 'Shenzhen' },
                  { id: 10008, name: 'Test8', nickname: 'T8', role: 'Develop', sex: 'Man', age: 80, address: 'Shenzhen' }
                ]
              }
            }
          }
        }
        

  <vxe-grid ref="xTable"
            highlight-hover-row
            highlight-current-row
            :align="align"
            :round="true"
            :border="border"
            :stripe="stripe"
            :loading="loading"
            :show-overflow="showOverflow"
            :show-header="showHeader"
            :show-footer="showFooter"
            :toolbarConfig="toolbarConfig"
            :export-config="exportConfig"
            :edit-config="editConfig"
            :filter-config="filterConfig"
            :print-config="printConfig"
            :footer-method="footerMethod"
            :size="size"
            :height="height"
            :treeConfig="treeConfig"
            :seq-config="{startIndex: (tablePage.pageIndex - 1) * tablePage.pageSize}"
            :columns="columns"
            :data="data"
            :radio-config="radioConfig"
            :checkbox-config="checkboxConfig"
            :menu-config="tableMenu"
            @edit-closed="editClosed"
            @cell-click="rowClick"
            @cell-dblclick="rowdblClick"
            @menu-click="menuClick">
            
    <!-- 自定义工具栏 -->
    <template #toolbar_buttons>
      <slot></slot>
    </template>

    <!-- 分页组件 -->
    <template #pager>
      <vxe-pager v-if="tablePage.showPage"
                 background
                 :layouts="['Sizes', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'FullJump', 'Total']"
                 :current-page.sync="tablePage.pageIndex"
                 :page-size.sync="tablePage.pageSize"
                 :page-sizes="[10,20,50,100,200]"
                 :total="tablePage.total"
                 @page-change="handlePageChange">
      </vxe-pager>
    </template>
  </vxe-grid>
</template>
<script>

import '@/components/vxeTable/renderer.js'
export default {
  name: 'Table',
  props: {
    loading: {
      type: Boolean,
      default: false
    },
    height: {
      type: String || Number,
      default: () => {
        return 'auto'
      }
    },
    stripe: {
      type: Boolean,
      default: true
    },
    border: {
      type: String,
      default: 'full'
    },
    align: {
      type: String,
      default: 'center'
    },
    showOverflow: {
      type: String,
      default: () => { }
    },
    showHeader: {
      type: Boolean,
      default: true
    },
    showFooter: {
      type: Boolean,
      default: false
    },
    toolbarConfig: {
      type: Object,
      default: () => { }
    },
    size: {
      type: String,
      default: () => { }
    },
    tablePage: {
      type: Object,
      default: () => {
        return {
          showPage: false,
          pageIndex: 1,
          pageSize: 10,
          total: 0
        }
      }
    },
    columns: {
      type: Array,
      default: () => {
        return []
      }
    },
    data: {
      type: Array,
      default: () => {
        return []
      }
    },
    treeConfig: {
      type: Object,
      default: undefined
    },
    // 可编辑配置项
    editConfig: {
      type: Object,
      default: () => {
        return {
          enabled: false,
          trigger: 'click',
          mode: 'row',
          beforeEditMethod: true
        }
      }
    },
    // 复选框配置项
    checkboxConfig: {
      type: Object,
      default: () => {
        return { highlight: true, range: true, trigger: 'default' }
      }
    },
    // 右键快捷菜单配置
    tableMenu: {
      type: Object,
      default: () => {
        return {}
      }
    },
    // 表尾方法
    footerMethod: {
      type: Function,
      default: () => {
        return []
      }
    }
  },
  data() {
    return {
      // 导出配置项
      exportConfig: {
        // 默认选中类型
        type: 'xlsx',
        // 局部自定义类型
        types: ['xlsx', 'csv'],
        // 自定义数据量列表
        modes: ['current', 'selected', 'all'],
        filename: '',
        sheetName: '',
        isFooter: true,
        isHeader: true,
        isMerge: true,
        isColgroup: true
      },
      filterConfig: {
        remote: false
      },
      // 打印配置项
      printConfig: {
        mode: 'current',
        modes: ['current'],
        sheetName: '',
        isHeader: true,
        isColgroup: true,
        isFooter: true
      },
      // 单选框配置项
      radioConfig: {
        highlight: true,
        strict: false,
        trigger: 'row'
      }
    }
  },
  methods: {
    // 分页改变触发事件
    handlePageChange({ currentPage, pageSize }) {
      this.tablePage.pageIndex = currentPage
      this.tablePage.pageSize = pageSize
      this.$emit('pageChange', { pageIndex: currentPage, pageSize })
    },
    // 单元格点击事件
    rowClick({ row, rowIndex, columnIndex, column }) {
      Object.defineProperty(row, 'rowIndex', { // 给每一行添加不可枚举属性rowIndex来标识当前行
        value: rowIndex,
        writable: true,
        enumerable: false
      })
      // 筛除多选时单击触发操作
      if (!column.property) {
        return
      }
      this.$emit('rowClick', { row, rowIndex, columnIndex })
    },
    // 单元格双击事件
    rowdblClick({ row, rowIndex, column }) {
      // 筛除多选时单击触发操作
      if (!column.property) {
        return
      }
      this.$emit('rowdblClick', { row, rowIndex })
    },
    // 右键点击单元格菜单
    menuClick({ menu, row, column }) {
      this.$emit('menuClick', { menu, row, column })
    },
    // 获取表格数据集(包括插入的临时数据)
    getRecordset() {
      return this.$refs.xTable.getRecordset()
    },
    // 获取当前选中的行数据(用于单选框)
    getRadioRecord() {
      return this.$refs.xTable.getRadioRecord()
    },
    // 删除复选框选中的行数据
    removeCheckboxRow() {
      return this.$refs.xTable.removeCheckboxRow()
    },
    // 获取当前已选中的行数据(用于复选框)
    getCheckboxRecords() {
      return this.$refs.xTable.getCheckboxRecords()
    },
    // 只对 edit-config 配置时有效,单元格编辑状态下被关闭时会触发该事件
    editClosed(row, rowIndex, column) {
      this.$emit('editClosed', row, rowIndex, column)
    }
    // 多选操作勾选某行
    // setCheckboxRow(rows, checked) {
    //   if (rows.length === 0) { // 清除全部多选选择
    //     this.$refs.xTable.clearCheckboxRow()
    //   } else {
    //     this.$refs.xTable.setCheckboxRow(rows, checked)
    //   }
    // }
    // // 表格尾部
    // sumNum(list, field) {
    //   let count = 0
    //   let num = 0
    //   list.forEach(item => {
    //     count += Number(item[field])
    //     num = count.toFixed(0)
    //   })
    //   return num
    // },
    // meanNum(list, field) {
    //   let count = 0
    //   list.forEach(item => {
    //     count += Number(item[field])
    //   })
    //   return (count / list.length).toFixed(2)
    // },
    // // 表格尾部总计
    // footerMethod({ columns, data }) {
    //   // return this.footerData
    //   const sums = []
    //   columns.forEach((column, columnIndex) => {
    //     if (columnIndex === 0) {
    //       sums.push('总')
    //     } else {
    //       let sumCell = null
    //       switch (column.property) {
    //         case 'stCount':
    //         case 'Sales':
    //         case 'Trans':
    //         case 'lstCount':
    //         case 'lSales':
    //         case 'lTrans':
    //           sumCell = this.sumNum(data, column.property)
    //           break
    //         case 'AC':
    //         case 'lAC':
    //           sumCell = this.meanNum(data, column.property)
    //           break
    //       }
    //       sums.push(sumCell)
    //     }
    //   })
    //   // 返回一个二维数组的表尾合计
    //   return [sums]
    // }
  }
}
</script>
<style lang="less" scoped>
.my-menus {
  background-color: #ffffff;
}
.my-menus .vxe-ctxmenu--link {
  width: 300px;
}
.my-copy-item {
  font-weight: 700;
  font-style: oblique;
}
/deep/ .vxe-table--render-default .vxe-body--row.row--current {
  background: #fff3e0;
}
/deep/ .vxe-toolbar {
  padding: 0 15px;
  .vxe-buttons--wrapper {
    .vxe-button:not(.is--disabled):hover {
      color: #82bdf8;
    }
  }
}
</style>

rederer.js


import SelcetFilter from './component/selcetFilter.vue'

import SelectContent from './component/selectContent.vue'

import DataFilter from './component/dataFilter.vue'

import Vue from 'vue'

Vue.component('SelcetFilter', SelcetFilter)

Vue.component('SelectContent', SelectContent)

Vue.component('DataFilter', DataFilter)

  


// 创建支持列内容的筛选渲染器

VXETable.renderer.add('SelcetFilter', {

  // 不显示底部按钮,使用自定义的按钮

  isFooter: false,

  // 筛选模板

  renderFilter(h, renderOpts, params) {

    return [

      <selcet-filter params={ params }></selcet-filter>

    ]

  },

  // 重置数据方法

  filterResetMethod({ options }) {

    options.forEach(option => {

      option.data = { vals: [], sVal: '' }

    })

  },

  // 筛选数据方法

  filterMethod({ option, row, column }) {

    const { vals } = option.data

    const cellValue = row[column.field]

    return vals.includes(cellValue)

  }

})

  


// 创建支持列内容的筛选渲染器

VXETable.renderer.add('DataFilter', {

  // 不显示底部按钮,使用自定义的按钮

  isFooter: false,

  // 筛选模板

  renderFilter(h, renderOpts, params) {

    return [

      <data-filter params={ params }></data-filter>

    ]

  },

  // 重置数据方法

  filterResetMethod({ options }) {

    options.forEach(option => {

      option.data = { vals: [], sVal: '' }

    })

  },

  // 筛选数据方法

  filterMethod({ option, row, column }) {

    const { vals } = option.data

    const cellValue = row[column.field]

    return vals.includes(cellValue)

  }

})

  


 // 创建element-select下拉框的可编辑渲染器

 VXETable.renderer.add('SelectContent', {

  // 可编辑模板

  renderEdit(h, renderOpts, params) {

    return [

      <select-content params={ params } ></select-content>

    ]

  }

  // 可编辑显示模板

  // renderCell(h, renderOpts, params) {

  //   const {row, column} = params

  //   return [

  //     <span>{ row[column.field] }</span>

  //   ]

  // }

})

dataFilter.vue


  <div class="data-filter">

    <el-date-picker clearable

                    v-model="value1"

                    type="daterange"

                    range-separator="至"

                    start-placeholder="开始日期"

                    end-placeholder="结束日期"

                    @change="dataChage">

    </el-date-picker>

  </div>

</template>

  


<script>

  


export default {

  name: 'DataFilter',

  props: {

    params: Object

  },

  inject: {

    getTableData: { value: ' getTableData', default: null }

  },

  data() {

    return {

      value1: ''

    }

  },

  methods: {

    dataChage(val) {

      console.log('%c [ val ]-31', 'font-size:13px; background:pink; color:#bf2c9f;', val)

    }

  }

}

  


</script>

  


<style scoped>

</style>