关于vxe-table的使用心得及扩展2【vxe-table表格二次封装】(非插件)


前言

根据官网文档预设些自定义参数,二次封装了vxe-table。注意这不是vxe-grid哦!

1.添加了官方不支持的表格合计计算参数

2.使表格 全选复选框勾选变化时,能直接触发复选框的变化事件。即使联动逻辑和el-table保持相同

3.补充了表格高度动态计算参数,dynamicHeight,如果实在不会用 就留言要例子把,请先看我写的参数注释!


一、在components中创建vxe-table\index.vue文件

<template>
  <vxe-table
    ref="c-vxe-table"
    v-on="$listeners"
    v-bind="$attrs"
    :loading="loading"
    :loading-config="loadingOpts"
    :auto-resize="autoResize"
    :stripe="stripe"
    :align="align"
    :header-align="headerAlign"
    :merge-cells="mergeCells"
    :tree-config="treeOpts"
    :radio-config="radioOpts"
    :checkbox-config="checkboxOpts"
    :show-overflow="showOverflow"
    :show-header-overflow="showHeaderOverflow"
    :show-footer-overflow="showFooterOverflow"
    :keep-source="keepSource"
    :border="border"
    :height="height"
    :min-height="minHeight"
    :max-height="maxHeight"
    :data="tData"
    :show-footer="showFooter"
    :column-config="columnOpts"
    :resize-config="resizeOpts"
    :row-config="rowOpts"
    :menu-config="ctxMenuOpts"
    :edit-config="editOpts"
    :expand-config="expandConfig"
    :tooltip-config="tooltipOpts"
    :span-method="spanMethod"
    :row-class-name="rowClassName"
    :cell-class-name="cellClassName"
    :header-row-class-name="headerRowClassName"
    :header-cell-class-name="headerCellClassName"
    :footer-row-class-name="footerRowClassName"
    :footer-cell-class-name="footerCellClassName"
    :footer-data="footerData"
    :footer-align="footerAlign"
    :footer-span-method="footerSpanMethod"
    :footer-method="footerMethod"
    :cell-style="cellStyle"
    @header-cell-click="$emit('headerCellClick')"
    @header-cell-dblclick="$emit('headerCellDblclick')"
    @header-cell-menu="$emit('headerCellMenu')"
    @cell-click="$emit('cellClick')"
    @cell-dblclick="$emit('cellDBLClick')"
    @cell-mouseenter="$emit('cellMouseenter')"
    @cell-mouseleave="$emit('cellMouseleave')"
    @cell-menu="$emit('cellMenu')"
    @radio-change="$emit('radioChange')"
    @checkbox-change="checkboxChange"
    @checkbox-all="checkboxAll"
    @scroll="$emit('scroll')">
    <slot></slot>
  </vxe-table>
</template>

<script>

/* global XEUtils:false */
/* ==================== 说明开始 ==================== */

// 1. API详见 https://vxetable.cn/v3.8/#/table/api   当前使用版本为 "vxe-table": "^3.8.15",
// 2. 该表格属性若干,该版只重定义了部分常用属性,其它未重写的方法可直接在组件上按官网案例调用,其方法和属性穿透实现方式为:$listeners、$attrs
// 3. 公用属性已提取到同级conf.js中,补充属性请按此规则配置并补充对应参数说明
`表格底部求和及平局值调用示例:
<custom-vxe-table
  showFooter
  :summaryColKeyList="[['classify','allSum'],['allSum','agreementMoney']]"
  :sumConfig="[{columnIndex: 1, text: '合计(元)' },{columnIndex: 1, text: '平均值(元)', ruleName: 'avg' }]"
  :table-data="data"
  @selection-change="handleSelectionChange">
    <vxe-column type="seq" width="50"></vxe-column>
    <vxe-column field="classify" title="classify"></vxe-column>
    <vxe-column field="allSum" title="allSum"></vxe-column>
    <vxe-column field="agreementMoney" title="agreementMoney"></vxe-column>
</custom-vxe-table>`

/* ==================== 说明结束 ==================== */

import GlobalConfig from './conf'
import { accAdd } from '@/util/util'
import { getPageIndexHeight } from '@/util/tools'
import { validatenull } from '@/util/validate'

export default {
  name: 'vxe-table-m',
  props: {
    tableData: {
      type: Array,
      default: () => [],
    },
    // tableHeight、minHeight、maxHeight 三者之间的关系见 https://www.cnblogs.com/xldbk/p/12114686.html
    tableHeight: {
      type: [Number, String],
    },
    minHeight: { type: [Number, String], default: () => GlobalConfig.table.minHeight },
    maxHeight: {
      type: [Number, String],
      default: () => GlobalConfig.table.maxHeight,
    },
    // 最终高度需要再减去的高度,常用来减去已知的自定义元素高度,此数值需要是1920*1080分辨率下的像素值(只有不传入其它任何高度限制时生效)
    dynamicHeight: {
      type: [Number, String],
      default: 0,
    },
    // 保持原始值的状态,被某些功能所依赖,比如编辑状态、还原数据等(开启后影响性能,具体取决于数据量)
    keepSource: { type: Boolean, default: () => GlobalConfig.table.keepSource },
    // 是否自动监听父容器变化去更新响应式表格宽高
    autoResize: {
      type: Boolean,
      default: () => {
        return false
      },
    },
    // 响应式布局配置项
    resizeConfig: {
      type: Object,
    },
    // 是否带有斑马纹
    stripe: { type: Boolean, default: () => GlobalConfig.table.stripe },
    border: { type: [Boolean, String], default: () => GlobalConfig.table.border },
    loading: {
      type: Boolean,
      default: () => {
        return false
      },
    },
    loadingConfig: {
      type: Object,
    },
    // 所有的列对其方式
    align: { type: String, default: () => GlobalConfig.table.align },
    // 所有的表头列的对齐方式
    headerAlign: { type: String, default: () => GlobalConfig.table.headerAlign },
    // 所有的表尾列的对齐方式
    footerAlign: { type: String, default: () => GlobalConfig.table.footerAlign },
    // 给行附加 className
    rowClassName: {
      type: [String, Function],
      default: () => {
        return ''
      },
    },
    // 给单元格附加 className
    cellClassName: [String, Function],
    // 给表头的行附加 className
    headerRowClassName: [String, Function],
    // 给表头的单元格附加 className
    headerCellClassName: [String, Function],
    rowConfig: {
      type: Object,
    },
    // 列默认配置项
    columnConfig: {
      type: Object,
      default: () => GlobalConfig.table.columnConfig,
    },
    // 快捷菜单配置项(右键)
    menuConfig: {
      type: Object,
    },
    // 展开行配置项
    expandConfig: {
      type: Object,
    },
    // 编辑配置项
    editConfig: {
      type: Object,
    },
    // 合并指定单元格
    mergeCells: {
      type: Array,
    },
    tooltipConfig: {
      type: Object,
    },
    // 树形结构配置项
    treeConfig: [Boolean, Object],
    radioConfig: {
      type: Object,
    },
    checkboxConfig: {
      type: Object,
    },
    // 设置所有内容过长时显示为省略号
    showOverflow: { type: [Boolean, String], default: () => GlobalConfig.table.showOverflow },
    // 设置表头所有内容过长时显示为省略号
    showHeaderOverflow: {
      type: [Boolean, String],
      default: () => GlobalConfig.table.showHeaderOverflow,
    },
    // 设置表尾所有内容过长时显示为省略号
    showFooterOverflow: {
      type: [Boolean, String],
      default: () => GlobalConfig.table.showFooterOverflow,
    },
    // 是否显示表尾合计
    showFooter: {
      type: Boolean,
    },
    // 表尾数据
    footerData: {
      type: Array,
    },
    // 自定义合并行或列的方法
    spanMethod: Function,
    // 表尾合并行或列
    footerSpanMethod: Function,
    // 给表尾的行附加 className
    footerRowClassName: [String, Function],
    // 给表尾的单元格附加 className
    footerCellClassName: [String, Function],
    // 给单元格附加样式
    cellStyle: [Object, Function],
    // 需要计算合计的列的key, 二维数组, key可以是无序的 [['第一行key1', '第一行key2'],['第二行key1','第二行key2']], 如果只传一个数组,后续计算则复用
    summaryColKeyList: Array,
    /**
     * 合计描述及计算规则, 对应footerMethod方法的返回值(多行合计数组),即:
     * [{columnIndex: '第一行合计行描述列下标', text: '描述', ruleName: '计算规则(求和)'},{columnIndex: '第二行合计行描述列下标',text: '描述', ruleName: '计算规则(平均值)'}]
     * 计算规则参数(ruleName): add 求和, avg 平均值 , 默认值:add。结果保留2位小数
     */
    sumConfig: {
      type: Array,
      required: true,
      default: () => {
        return [{ columnIndex: 1, text: '合计(元)', ruleName: 'add' }]
      },
    },
    /**
     * 如果不想自动求和,比如直接使用后端请求回来的数据,则需要手动设置summaryData。此时将不启用sumConfig中的(ruleName)计算规则进行计算
     * 对象数组(对应summaryColKeyList中的key) [{第一行key1:'第一行key1的值', 第一行key2:'第一行key2的值'},...]
     */
    summaryData: Array,
  },
  data() {
    return {
      height: '', // 表格高度
    }
  },
  watch: {},
  computed: {
    tData() {
      if (this.tableData.length > 0) {
        this.autoHeight()
      }
      return this.tableData
    },
    columnOpts() {
      return Object.assign({}, GlobalConfig.table.columnConfig, this.columnConfig)
    },
    rowOpts() {
      return Object.assign({}, GlobalConfig.table.rowConfig, this.rowConfig)
    },
    resizeOpts() {
      return Object.assign({}, GlobalConfig.table.resizeConfig, this.resizeConfig)
    },
    radioOpts() {
      return Object.assign({}, GlobalConfig.table.radioConfig, this.radioConfig)
    },
    checkboxOpts() {
      return Object.assign({}, GlobalConfig.table.checkboxConfig, this.checkboxConfig)
    },
    tooltipOpts() {
      return Object.assign({}, GlobalConfig.table.tooltipConfig, this.tooltipConfig)
    },
    editOpts() {
      return Object.assign({}, GlobalConfig.table.editConfig, this.editConfig)
    },
    treeOpts() {
      return Object.assign({}, GlobalConfig.table.treeConfig, this.treeConfig)
    },
    ctxMenuOpts() {
      return Object.assign({}, GlobalConfig.table.menuConfig, this.menuConfig)
    },
    loadingOpts() {
      return Object.assign({}, GlobalConfig.table.loadingConfig, this.loadingConfig)
    },
  },
  mounted() {
    let _self = this
    _self.autoHeight()
    // 监听页面缩放, 重新计算表格高度
    window.onresize = XEUtils.debounce(() => {
      return (() => {
        // _self.autoHeight()
      })()
    }, 200)
  },
  methods: {
    // 表尾合计,
    footerMethod({ columns, data }) {
      let _self = this
      let sumRows = []
      if (!validatenull(_self.sumConfig)) {
        _self.sumConfig.forEach((conf, index) => {
          sumRows.push(
            columns.map((column, columnIndex) => {
              // 设置表尾合计的描述
              if (columnIndex == conf.columnIndex) {
                return conf.text
              }
              // 对符合要求的列进行计算
              if (!validatenull(_self.summaryColKeyList)) {
                let keyList = _self.summaryColKeyList[index]
                  ? _self.summaryColKeyList[index]
                  : _self.summaryColKeyList[0]
                if (!validatenull(keyList) && keyList?.includes(column.field)) {
                  if (!validatenull(_self.summaryData?.[index])) {
                    return _self.summaryData[index][column.field] || null
                  }
                  return _self.sumNum(data, column.field, conf.ruleName || 'add')
                }
              }
              return null
            })
          )
        })
      }
      return sumRows
    },
    // 根据sumConfig中配置的计算规则进行计算
    sumNum(list, field, rule) {
      let count = 0
      if (rule == 'add') {
        list.forEach((item) => {
          count = accAdd(count, Number(item[field]))
        })
        return Math.round(count * 100) / 100
      }
      if (rule == 'avg') {
        // 求平均...
        list.forEach((item) => {
          count += Number(item[field])
        })
        return Math.round((count / list.length) * 100) / 100
      }
    },
    // checkbox选中状态变换. 兼容el-table
    checkboxChange(params) {
      let selections = params.records || []
      let selectionIds = selections.map((item) => {
        return item.id
      })
      this.$emit('selection-change', selections, selectionIds, params)
    },
    // 计算table页面高度: 如果传入自定义高度,则使用自定义高度,否则动态计算高度,其优先关系详见prop中文档说明
    autoHeight() {
      this.$nextTick(() => {
        if (this.maxHeight == null) {
          if (this.tableHeight) {
            this.height = this.tableHeight
          } else {
            /**
             * getPageIndexHeight 函数为动态计算表格高度函数,看业务需求。一般是减去处表格外的其它元素高度,比如搜索栏+底部分页等。
             * this.dynamicHeight 参数就是为了减去除getPageIndexHeight中预设元素高度的其它已知元素高度。再简单的描述就是:当你不想再往该函数中添加预设元素高度计算时,可以使用该参数在结果上直接减去该高度
             * this.tableData.length > 0 是为了兼容动态数据,如果数据为空,则不减去底部分页。因为默认无数据时不展示分页,当然你可能需要不到该逻辑,此处提示建设性想法
             */ 
            // this.height = getPageIndexHeight(this.dynamicHeight, this.tableData.length > 0)
            
          }
        }
      })
    },
    // checkbox全选状态变换. 兼容el-table
    checkboxAll(params) {
      let selections = params.records || []
      this.$emit('select-all', selections, params)
      // 兼容el-table 触发逻辑,select-all会联动selection-change
      let selectionIds = selections.map((item) => {
        return item.id
      })
      this.$emit('selection-change', selections, selectionIds, params)
    },
  },
}
</script>

二、同级创建配置文件conf.js

export default {
  table: {
    fit: true,
    align: 'left',
    headerAlign: 'left',
    footerAlign: 'left',
    showHeader: true,
    animat: true,
    delayHover: 250,
    autoResize: true,
    minHeight: null,
    maxHeight: null,
    keepSource: false,
    showOverflow: true,
    showHeaderOverflow: true,
    showFooterOverflow: true,
    // size: null,
    // zIndex: null,
    stripe: true,
    border: true,
    // round: false,
    // emptyText: '暂无数据',
    // emptyRender: {
    //   name: ''
    // },
    loadingConfig: { icon: 'el-icon-loading', text: '加载中...' },
    rowConfig: {
      // keyField: '_X_ROW_KEY', // 行数据的唯一主键字段名
      // 是否需要为每一行的 VNode 设置 key 属性
      boolean: false,
      // 自定义行数据唯一主键的字段名(默认自动生成)
      // 当鼠标点击行时,是否要高亮当前行
      isCurrent: false,
      // 当鼠标移到行时,是否要高亮当前行
      isHover: false,
    },
    columnConfig: {
      resizable: true,
    },
    resizeConfig: {
      refreshDelay: 250,
    },
    radioConfig: {
      // trigger: 'default'
      strict: true,
    },
    checkboxConfig: {
      // trigger: 'default',
      strict: true,
    },
    tooltipConfig: {
      // 所有单元格开启工具提示 boolean
      showAll: false,
      // tooltip 的主题颜色string  dark,light
      theme: 'light',
      // 鼠标是否可进入到工具提示中boolean
      enterable: false,
      // 鼠标移入后延时多少才显示工具提示number
      enterDelay: 500,
      // 鼠标移出后延时多少才隐藏工具提示number
      leaveDelay: 300,
    },
    validConfig: {
      showMessage: true,
      autoClear: true,
      autoPos: true,
      message: 'inline',
      msgMode: 'single',
    },
    menuConfig: {
      enabled: false, // 是否启用
    },
    customConfig: {
      allowVisible: true,
      allowResizable: true,
      allowFixed: true,
      allowSort: true,
      showFooter: true,
      placement: 'topRight',
      //  storage: false,
      //  checkMethod () {},
      modalOptions: {
        showZoom: true,
        mask: true,
        lockView: true,
        resize: true,
        escClosable: true,
      },
    },
    sortConfig: {
      // remote: false,
      // trigger: 'default',
      // orders: ['asc', 'desc', null],
      // sortMethod: null,
      showIcon: true,
      iconLayout: 'vertical',
    },
    filterConfig: {
      // remote: false,
      // filterMethod: null,
      showIcon: true,
    },
    treeConfig: {
      rowField: 'id',
      parentField: 'parentId',
      childrenField: 'children',
      hasChildField: 'hasChild',
      mapChildrenField: '_X_ROW_CHILD',
      indent: 20,
      showIcon: true,
    },
    expandConfig: {
      // trigger: 'default',
      showIcon: true,
    },
    editConfig: {
      // mode: 'cell',
      showIcon: true,
      showAsterisk: true,
    },
    importConfig: {
      _typeMaps: {
        csv: 1,
        html: 1,
        xml: 1,
        txt: 1,
      },
      modes: ['insert', 'covering'],
    },
    exportConfig: {
      _typeMaps: {
        csv: 1,
        html: 1,
        xml: 1,
        txt: 1,
      },
      modes: ['current', 'selected'],
    },
    printConfig: {
      modes: ['current', 'selected'],
    },
    mouseConfig: {
      extension: true,
    },
    keyboardConfig: {
      isEsc: true,
    },
    scrollX: {
      // enabled: false,
      gt: 60,
      // oSize: 0
    },
    scrollY: {
      // enabled: false,
      gt: 100,
      // oSize: 0
    },
  },
  export: {
    types: {},
  },

  grid: {
    // size: null,
    // zoomConfig: {
    //   escRestore: true
    // },
    formConfig: {
      enabled: true,
    },
    pagerConfig: {
      enabled: true,
      // perfect: false
    },
    toolbarConfig: {
      enabled: true,
      // perfect: false
    },
    proxyConfig: {
      enabled: true,
      autoLoad: true,
      message: true,
      props: {
        list: null,
        result: 'result',
        total: 'page.total',
        message: 'message',
      },
      // beforeItem: null,
      // beforeColumn: null,
      // beforeQuery: null,
      // afterQuery: null,
      // beforeDelete: null,
      // afterDelete: null,
      // beforeSave: null,
      // afterSave: null
    },
  },
  pager: {
    pageSizePlacement: 'top',
    // size: null,
    // autoHidden: false,
    // perfect: true,
    // pageSize: 10,
    // pagerCount: 7,
    // pageSizes: [10, 15, 20, 50, 100],
    // layouts: ['PrevJump', 'PrevPage', 'Jump', 'PageCount', 'NextPage', 'NextJump', 'Sizes', 'Total']
  },
}

三. 目录结构

仅供参考(示例):
在这里插入图片描述


四. 注册调用, 可使用其它方式注册

注册(示例):

import CustomVxeTable from '@/components/vxe-table'
Vue.component('CustomVxeTable', CustomVxeTable)

调用(示例):

<custom-vxe-table
      border
      :max-height="475"
      show-footer
      :merge-footer-items="[{ row: 0, col: 1, rowspan: 1, colspan: 3 }]"
      :summaryColKeyList="[['num1', 'age'], ['rate']]"
      :sumConfig="[{columnIndex: 1, text: '合计(元)' },{columnIndex: 1, text: '平均值(元)', ruleName: 'avg' }]"
      :table-data="tableData"
      @selection-change="handleSelectionChange">
      <template v-slot:column>
        <vxe-column
          type="seq"
          title="序号"
          width="60"></vxe-column>
        <vxe-colgroup title="统计信息">
          <vxe-column
            field="name"
            title="Name"
            :edit-render="{}">
            <template #edit="{ row }">
              <vxe-input
                v-model="row.name"
                type="text"></vxe-input>
            </template>
          </vxe-column>
          <vxe-column
            field="age"
            title="Age"
            :edit-render="{ autofocus: '.vxe-input--inner' }">
            <template #edit="{ row }">
              <vxe-input
                v-model="row.age"
                type="text"
                :min="1"
                :max="120"
                @change="updateFooterEvent"></vxe-input>
            </template>
          </vxe-column>
          <vxe-column
            field="num1"
            title="Num"
            :edit-render="{ autofocus: '.vxe-input--inner' }">
            <template #edit="{ row }">
              <vxe-input
                v-model="row.num1"
                type="text"
                @input="updateFooterEvent"></vxe-input>
            </template>
          </vxe-column>
          <vxe-column
            field="rate"
            title="Rate"
            :edit-render="{}">
            <template #edit="{ row }">
              <vxe-input
                v-model="row.rate"
                type="text"
                @input="updateFooterEvent"></vxe-input>
            </template>
          </vxe-column>
        </vxe-colgroup>
      </template>
    </custom-vxe-table>

表格测试数据

tableData: [
        {
          id: 10001,
          name: 'Test1',
          nickname: 'T1',
          role: 'Develop',
          sex: '0',
          sex2: ['0'],
          num1: 40,
          age: 28.2,
          rate: 22,
        },
        {
          id: 10002,
          name: 'Test2',
          nickname: 'T2',
          role: 'Designer',
          sex: '1',
          sex2: ['0', '1'],
          num1: 23,
          age: 22.1,
          rate: 34,
        },
        {
          id: 10003,
          name: 'Test3',
          nickname: 'T3',
          role: 'Test',
          sex: '0',
          sex2: ['1'],
          num1: 200,
          age: 32.1,
          rate: 18,
        },
        {
          id: 10004,
          name: 'Test4',
          nickname: 'T4',
          role: 'Designer',
          sex: '1',
          sex2: ['1'],
          num1: 30,
          age: 23.3,
          rate: 13,
        },
        {
          id: 10005,
          name: 'Test5',
          nickname: 'T5',
          role: 'Develop',
          sex: '0',
          sex2: ['1', '0'],
          num1: 20,
          age: 30.01,
          rate: 6,
        },
        {
          id: 10006,
          name: 'Test6',
          nickname: 'T6',
          role: 'Designer',
          sex: '1',
          sex2: ['0'],
          num1: 10,
          age: 21.03,
          rate: 33,
        },
        {
          id: 10007,
          name: 'Test7',
          nickname: 'T7',
          role: 'Develop',
          sex: '0',
          sex2: ['0'],
          num1: 5,
          age: 29,
          rate: 4,
        },
        {
          id: 10008,
          name: 'Test8',
          nickname: 'T8',
          role: 'PM',
          sex: '1',
          sex2: ['0'],
          num1: 2,
          age: 35,
          rate: 55,
        },
      ],

五. 链接指引

💡 关于vxe-table的使用心得及扩展【表格虚拟滚动】(非插件)
💡💡 关于vxe-table的使用心得及扩展3【vxe-table二次封装组件应用】(非插件)

vxe-table是一个基于Vue.js的强大的表格组件库,它提供了丰富的功能和灵活的配置选项,可以用于展示和处理大量的数据。而vxe-table二次封装是指在vxe-table的基础上进行进一步的封装和定制,以满足特定项目或业务需求。 在进行vxe-table二次封装时,可以根据具体需求进行以下操作: 1. 封装常用的表格组件:根据项目需求,可以将常用的表格组件进行封装,以便在不同页面或模块中复用。例如,可以封装一个通用的表格组件,包含常用的列配置、分页、排序等功能。 2. 定制样式和主题:通过修改vxe-table的样式文件或者使用自定义主题,可以使表格组件与项目整体风格保持一致。这样可以提升用户体验,并增加项目的专属感。 3. 扩展功能和事件:根据项目需求,可以通过扩展vxe-table的功能和事件来满足特定的业务逻辑。例如,可以添加自定义的筛选、导出、编辑等功能,并监听相应的事件进行处理。 4. 封装数据请求和处理逻辑:在二次封装中,可以将数据请求和处理逻辑进行封装,以便在使用表格组件时更加方便地进行数据的获取和展示。可以通过封装API接口、数据转换等方式来实现。 5. 提供文档和示例:在进行vxe-table二次封装后,可以编写相应的文档和示例,以便其他开发人员能够更好地理解和使用封装后的表格组件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值