Vxe Table + Vue2封装及使用示例

一、前言

在这里插入图片描述
在前端开发中,表格展示数据是极为常见的需求。Vxe Table 作为一款功能强大的基于 Vue 的表格组件库,为我们高效构建表格提供了诸多便利。Vxe Table 具有丰富的特性,如支持复杂表头、单元格编辑、数据校验、排序、筛选等。它拥有简洁易用的 API,能帮助开发者快速搭建出符合业务需求的表格界面,大大提高开发效率。其强大的自定义能力,可以满足各种复杂场景下的表格展示需求。本文将详细介绍如何在 Vue2 项目中对 Vxe Table 进行封装,并给出实际使用示例。

二、Vxe Table 的封装

1 安装及引入流程见官网VxeTable v3

​ 注意版本适配,Vue2对应V3版本,Vue3对应V4版本。

2 封装自定义表格组件

​ 在项目/src/components目录下创建vxeTable目录,新建一个index.jsindex.vue。代码如下:

// index.js
import VxeUIAll from 'vxe-pc-ui'
import 'vxe-pc-ui/lib/style.css'
import VxeUITable from 'vxe-table'
import 'vxe-table/lib/style.css'

export const CustomVxeTable = (app) => {
  app.use(VxeUIAll)
  app.use(VxeUITable)
}
<template>
  <!-- 表格组件 -->
  <vxe-grid
    ref="vxeTable"
    v-bind="gridOptions"
    v-on="$listeners"
    :data="dataSource"
    :columns="columns"
    :border="bordered"
  >
    <!-- 自定义表单内容 -->
    <template #form>
      <slot name="form"></slot>
    </template>

    <!-- 自定义表格上方内容 -->
    <template #top>
      <slot name="top"></slot>
    </template>

    <!-- 自定义表头内容 -->
    <template v-for="header in slotsHeader" #[header]>
      <slot :name="header"></slot>
    </template>

    <!-- 自定义表格展开内容 -->
    <template #expandedRowRender="{ row }">
      <slot name="expandedRowRender" :record="row"></slot>
    </template>

    <!-- 自定义表格内容 -->
    <template v-for="slot in slotsDefault" #[slot]="{ row }">
      <slot :name="slot" :text="row[slot]" :record="row"></slot>
    </template>

    <!-- 自定义空数据内容 -->
    <template #empty>
      <slot v-if="$slots.empty" name="empty"></slot>
      <a-empty v-else />
    </template>

    <!-- 自定义表格下方内容 -->
    <template #bottom>
      <slot name="bottom"></slot>
    </template>

    <!-- 分页组件 -->
    <template #pager>
      <vxe-pager
        v-if="pagination"
        :loading="gridOptions.loading"
        :loyouts="pagerOptions.loyouts"
        :size="pagerOptions.size"
        :currentPage.sync="pagerOptions.current"
        :pageSize.sync="pagerOptions.pageSize"
        :pageSizes="pagerOptions.pageSizes"
        :total="pagerOptions.total"
        @page-change="handlePageChange">
      </vxe-pager>
    </template>
  </vxe-grid>
</template>

<script>
import defaultConf from './defaultConf'

/**
 * 根据插槽类型获取插槽列表
 * @param columns 列配置
 * @param slotType  插槽类型
 * @returns {*[]}
 */
function slotsByType(columns, slotType) {
  const slots = []

  const findSlots = (column) => {
    if (!column) return

    if (column.slots && column.slots[slotType]) {
      slots.push(column.slots[slotType])
    }

    if (column.children) {
      for (let child of column.children) {
        findSlots(child)
      }
    }
  }

  for (let column of columns) {
    findSlots(column)
  }

  return slots
}

export default {
  name: 'VxeTable',
  props: {
    // 表格数据
    dataSource: {
      type: Array,
      required: true
    },
    // 表格列配置
    columns: {
      type: Array,
      required: true
    },
    // 边框
    bordered: {
      type: Boolean,
      default: true
    },
    // 分页配置
    pagination: {
      type: Object,
      default: null // 默认不启用分页
    }
  },
  data() {
    return {}
  },
  computed: {
    gridOptions() {
      const gridOptions = {
        ...defaultConf.grid,  // 默认配置
        ...this.$attrs,       // 用户传入的配置
        ...this.props,        // props传入的配置
        rowConfig: { ...defaultConf.rowConfig, useKey: Boolean(this.$attrs.rowKey), keyField: this.$attrs.rowKey }  // 特殊配置单独处理
      }
      const expiredAttribute = ['rowKey'] // 删除过期属性,避免传入到vxe-grid组件报错
      for (let attr of expiredAttribute) {
        delete gridOptions[attr]
      }
      return gridOptions
    },
    pagerOptions() {
      if (!this.pagination) return {}
      return {
        ...defaultConf.pager,   // 默认配置
        ...this.pagination,    // 用户传入的配置
        pageSizes: this.pagination.pageSizeOptions.map(item => parseInt(item)) // 特殊配置单独处理
      }
    },
    slotsDefault() {
      return slotsByType(this.columns, 'default')
    },
    slotsHeader() {
      return slotsByType(this.columns, 'header')
    }
  },
  mounted() {
    // 将子组件的方法复制到父组件
    Object.keys(this.$refs.vxeTable.$options.methods).forEach(method => {
      this[method] = this.$refs.vxeTable[method].bind(this.$refs.vxeTable)
    })
  },
  methods: {
    handlePageChange({ currentPage, pageSize }) {
      this.$emit('change', { ...this.pagination, current: currentPage, pageSize: pageSize })
    }
  }
}
</script>

<style scoped>
/* 可以根据需要添加样式 */
</style>

​ 此外,为了方便使用,新建一个默认配置文件defaultConf.js

export default {
  grid: {
    layouts: ['Form', 'Toolbar', 'Top', 'Table', 'Bottom', 'Pager'],
    height: null,  // %, px
    minHeight: null,
    maxHeight: null,
    stripe: true, // 斑马纹
    border: true, // 边框
    round: false, // 圆角边框
    size: 'small', // medium, small, mini
    showHeader: true, // 是否显示表头
    showFooter: false, // 是否显示表尾
    headerAlign: 'center',  // 表头对齐方式
    footerAlign: 'left',  // 表尾对齐方式
    keepSource: false,  // 保持原始值的状态,被某些功能所依赖,比如编辑状态、还原数据等
    showOverflow: false,  // 列超出宽度是否自动省略,推荐每列自主设置 'false' 'true/ellipsis' 'title' 'tooltip'
    showHeaderOverflow: 'title',  // 表头列超出宽度是否自动省略
    showFooterOverflow: 'title',  // 表尾列超出宽度是否自动省略
    autoResize: true, // 是否自动监听父容器变化去重新计算表格(对于固定高度的表格,可能会用到)
    resizeConfig: {
      refreshDelay: 250 // 只对 autoResize 有效,刷新延时,当父容器发生变化时,至少多少毫秒刷新布局
    },
    columnConfig: {
      isCurrent: false,  // 鼠标点击列头时是否高亮当前列
      isHover: false,  // 鼠标移到列时是否高亮当前列
      resizable: false // 是否允许拖动列宽调整大小,推荐每列自主设置
    },
    rowConfig: {
      // keyField: '_X_ROW_KEY', // 行数据的唯一主键字段名
      isCurrent: false, // 当鼠标点击行时,是否要高亮当前行
      isHover: false  // 当鼠标移到行时,是否要高亮当前行
    },
    cellConfig: {
      padding: true // 是否显示间距
      // height: 48, // number, 默认单元格高度
    },
    editConfig: {
      // mode: 'cell', // cell, row
      trigger: 'click',
      showIcon: true,
      showAsterisk: true
    },
    radioConfig: {
      // trigger: 'default'
      strict: true
    },
    checkboxConfig: {
      // trigger: 'default',
      strict: true
    },
    tooltipConfig: {
      // 所有单元格开启工具提示 boolean
      showAll: false,
      // tooltip 的主题颜色string  dark,light
      theme: 'dark',
      // 鼠标是否可进入到工具提示中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',
      indent: 20,
      showIcon: true,
      expandInit: false
    },
    expandConfig: {
      // trigger: 'default',
      showIcon: 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
    }
  },
  pager: {
    loyouts: ['Home', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'End', 'Sizes', 'FullJump', 'Total']
  }
}

​ 在上述代码中,我们通过 props 接收外部传递的主要属性(如:数据和表格列配置),其余属性通过this.$attrs接收,父组件未设定的属性值通过defaultConf进行默认配置。实现了一个具有基本功能的可复用表格组件。同时,绑定了一些常用的事件,如选择项变化和行双击事件,并通过$emit将事件传递给父组件。

三、Vxe Table 的使用示例

​ 在父组件中引入并使用封装的表格组件
在需要使用表格的父组件中,引入MyTable.vue组件,并传递相应的数据和配置。

<template>
  <div class="demo-page-wrapper">
    <!-- 使用二次封装的 VxeTable 组件 -->
    <div style="padding: 16px 0; display: flex;align-items: center;justify-content: space-between;">
      <a-space size="middle">
        <a-button @click="sortEvent('age', 'asc')">年龄 升序</a-button>
        <a-button @click="sortEvent('age', 'desc')">年龄 降序</a-button>
        <a-button @click="clearSortEvent" type="primary">清除排序</a-button>
      </a-space>
      <a-space size="middle">
        <div>
          总计:
          <span>1000</span>
          元
        </div>
        <a-button @click="getSelectEvent" type="primary">获取选中数据</a-button>
        <a-button @click="handleExport" type="primary">导出</a-button>
      </a-space>
    </div>

    <VxeTable
      ref="table"
      size="small"
      rowKey="id"
      border
      round
      stripe
      height="auto"
      :expandConfig="{ showIcon: false }"
      :loading="loading"
      :dataSource="dataSource"
      :columns="cols"
      :pagination="ipagination"
      :menuConfig="menuConfig"
      @cell-menu="cellMenuEvent"
      @menu-click="menuClickEvent"
      @cell-click="handleCellClick"
      @edit-closed="handleEditClosed"
      @change="handleTableChange"
      @form-submit="formSubmitEvent"
      @form-reset="formResetEvent"
    >
      <!-- 自定义表头内容 -->
      <template #name_header>
        <span>《名字名字名字名字名字》</span>
      </template>

      <template #expandedRowRender="{ record }">
        <vxe-grid :columns="childCols" :data="record.bookList"></vxe-grid>
      </template>

      <!-- 自定义表格内容 以下三种写法结果相同 -->
      <!--      <template v-slot:email="{ record }">-->
      <!--      <template #email="{ text,record }">-->
      <template slot="email" slot-scope="{ text,record }">
        {{ text }}
        <vxe-text :content="record.email" click-to-copy></vxe-text>
      </template>

      <template #action="{ record }">
        <a @click="handleEdit(record)">编辑</a>
      </template>

      <!-- 自定义空数据内容 -->
      <template #empty>
        <span style="color: red;">
          <img src="https://vxeui.com/resource/img/546.gif">
          <p>不用再看了,没有更多数据了!!!!!</p>
        </span>
      </template>
    </VxeTable>
  </div>
</template>

<script>
import XEUtils from 'xe-utils'
import VxeTable from '@/components/VxeTable/Index.vue'
import GridMixin from '@/mixins/GridMixin'
import { loadMockData } from '@views/demo/vxeTable/generateData'
import { VxeUI } from 'vxe-table'

// 自定义单元格渲染
const avatarUrlCellRender = {
  name: 'VxeImage',
  props: {
    circle: true,
    width: 36,
    height: 36
  }
}
const levelNumCellRender = {
  name: 'VxeRate',
  props: {
    readonly: false
  }
}
const flagCellRender = {
  name: 'VxeSwitch',
  props: {
    readonly: false
  },
  events: {
    change: (cellParams, targetParams) => {
      console.log('flag', targetParams.value, cellParams)
    }
  }
}
const nameEditRender = {
  name: 'VxeInput',
  props: {
    trim: true,
    clearable: true,
    maxLength: 5,
    placeholder: '请输入名字'
  },
  events: {
    blur: (cellParams, targetParams) => {
      console.log('name', targetParams.value)
    }
  }
}

// 格式化单元格
const formatSex = ({ cellValue }) => {
  if (cellValue) {
    return cellValue === '1' ? '男' : '女'
  }
  return ''
}
const cityOptions = [
  { label: '中华人民共和国广东省深圳市龙岗区人民法院', value: 'sz' },
  { label: '广东省广州市', value: 'gz' },
  { label: '北京市', value: 'bj' },
  { label: '上海市', value: 'sh' },
  { label: '浙江省杭州市', value: 'hz' }
]
const formatCity = ({ cellValue }) => {
  const item = cityOptions.find(item => item.value === cellValue)
  return item ? item.label : cellValue
}
const formatAmount = ({ cellValue }) => {
  if (cellValue) {
    return `¥${ XEUtils.commafy(cellValue, { digits: 2 }) }`
  }
  return ''
}

// 筛选条件
const sexFilterOptions = [
  { label: '女', value: '0' },
  { label: '男', value: '1' }
]
const ageFilterOptions = [
  { label: '(18,25]', value: '18-25' },
  { label: '(26,35]', value: '26-35' },
  { label: '(36,45]', value: '36-45' },
  { label: '(46,55]', value: '46-55' },
  { label: '(56,65]', value: '56-65' }
]
const ageFilterMethod = function({ value: filterVal, cellValue }) {
  filterVal = filterVal.split('-')
  const age = parseInt(cellValue)
  if (filterVal.length === 2) {
    return age >= parseInt(filterVal[0]) && age < parseInt(filterVal[1])
  }
}

// 右键菜单
const menuConfig = {
  header: {
    options: [
      [
        { code: 'exportAll', name: '导出所有.csv', prefixConfig: { icon: 'vxe-icon-download' }, visible: true, disabled: false }
      ]
    ]
  },
  body: {
    options: [
      [
        { code: 'copy', name: '复制内容(Ctrl+C)', prefixConfig: { icon: 'vxe-icon-copy' }, visible: true, disabled: false },
        { code: 'clear', name: '清除内容', visible: true, disabled: false },
        { code: 'reload', name: '刷新表格', visible: true, disabled: false }
      ],
      [
        {
          code: 'fixed',
          name: '冻结列',
          children: [
            { code: 'cancelFixed', name: '取消冻结' },
            { code: 'fixedLeft', name: '冻结在左侧', prefixConfig: { icon: 'vxe-icon-fixed-left' } },
            { code: 'fixedRight', name: '冻结在右侧', prefixConfig: { icon: 'vxe-icon-fixed-right' } }
          ]
        }
      ],
      [
        { code: 'myPrint', name: '打印(Ctrl+P)', prefixConfig: { icon: 'vxe-icon-print' }, visible: true, disabled: false },
        { code: 'myExport', name: '导出.csv', prefixConfig: { icon: 'vxe-icon-download' }, visible: true, disabled: false }
      ]
    ]
  }
}

const formConfig = {
  data: {
    name: '',
    role: '',
    sex: '',
    num: '',
    address: ''
  },
  items: [
    { field: 'name', title: '名称', span: 8, itemRender: { name: 'VxeInput' } },
    { field: 'email', title: '邮件', span: 8, itemRender: { name: 'VxeInput' } },
    { field: 'nickname', title: '昵称', span: 8, itemRender: { name: 'VxeInput' } },
    { field: 'role', title: '角色', span: 8, folding: true, itemRender: { name: 'VxeInput' } },
    { field: 'sex', title: '性别', span: 8, folding: true, itemRender: { name: 'VxeInput' } },
    { field: 'age', title: '年龄', span: 8, folding: true, itemRender: { name: 'VxeInput' } },
    {
      span: 24,
      collapseNode: true,
      align: 'center',
      itemRender: {
        name: 'VxeButtonGroup',
        options: [
          { type: 'submit', content: '搜索', status: 'primary' },
          { type: 'reset', content: '重置' }
        ]
      }
    }
  ]
}

/**
 * 列常用配置说明:
 * 1. type:列类型,支持的类型有 seq(序号)、checkbox(复选框)、radio(单选框)、expand(展开行)、html(HTML片段)
 * 2. field:列字段名
 * 3. title:标题名
 * 4. width: 列宽,支持auto、固定像素、百分比、等比例分配等,如果不设置则按照表格的宽度进行均匀分配。该表格组件支持min-width,可适时使用
 * 5. 溢出省略:
 *  key的取值:showOverflow(单元格)、showHeaderOverflow(表头) 和 showFooterOverflow(表尾)
 *  value的取值:ellipsis(省略号)、title(省略号 + 原生title)、tooltip(省略号 + tooltip提示)
 * 5. resizable: 是否允许拖动列宽调整大小
 * 6. fixed: 列固定,支持 left(固定左侧)、right(固定右侧)
 * 7. align: 对齐方式,支持 left、center、right
 * 8. sortable: 是否允许排序
 * 9. filters: 筛选项。数组格式,元素结构:{ label: '选项', value: '值' ,checked:'是否默认选中'}。搭配filterMethod定义筛选方法,filterMultiple定义是否多选
 * 10. formatter: (({ cellValue, row, column }) => string) | any[] | string,格式化显示内容
 * 11. cellRender: 自定义单元格渲染
 * 12. editRender: 自定义编辑渲染
 * 13. contentRender: 自定义内容渲染
 * 13. slots: 自定义插槽
 */
const cols = [
  { type: 'expand', width: '40px', align: 'center', slots: { content: 'expandedRowRender' } },
  { field: 'seq', type: 'seq', width: '40px', align: 'center', resizable: false },
  { field: 'checkbox', type: 'checkbox', width: '40px', align: 'center' },
  { field: 'avatarUrl', title: '头像', width: '80px', align: 'center', titlePrefix: { content: '自定义前缀图标', icon: 'vxe-icon-question-circle-fill' }, cellRender: avatarUrlCellRender },
  { field: 'name', title: '名字', align: 'center', showHeaderOverflow: 'ellipsis', minWidth: '100px', dragSort: true, editRender: nameEditRender, slots: { header: 'name_header' } },
  {
    title: '基本信息',
    field: 'info',
    children: [
      { field: 'city', title: '所在地', width: '250px', showOverflow: 'tooltip', formatter: formatCity },
      { field: 'age', title: '年龄', width: '100px', align: 'center', sortable: true, filters: ageFilterOptions, filterMethod: ageFilterMethod, editRender: { name: 'input', attrs: { type: 'number' } } },
      { field: 'sex', title: '性别', width: '60px', align: 'center', formatter: formatSex, filters: sexFilterOptions, filterMultiple: true, editRender: { name: 'select', options: sexFilterOptions } },
      { field: 'email', title: '邮箱', width: '220px', slots: { default: 'email' } }
    ]
  },
  { field: 'flag', title: '是否启用', width: '80px', align: 'center', cellRender: flagCellRender },
  { field: 'levelNum', title: '评分', width: '200px', align: 'center', cellRender: levelNumCellRender },
  {
    title: '年度账单',
    field: 'annualStatement',
    children: [
      { field: 'annualStatement.m1', title: '一月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m2', title: '二月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m3', title: '三月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m4', title: '四月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m5', title: '五月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m6', title: '六月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m7', title: '七月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m8', title: '八月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m9', title: '九月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m10', title: '十月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m11', title: '十一月', width: '140px', formatter: formatAmount },
      { field: 'annualStatement.m12', title: '十二月', width: '140px', formatter: formatAmount }
    ]
  },
  { title: '操作', field: 'action', width: '100px', align: 'center', fixed: 'right', slots: { default: 'action' } }
]

const childCols = [
  { field: 'name', title: '书籍名称', align: 'center' },
  { field: 'author', title: '作者', align: 'center' },
  { field: 'price', title: '价格', align: 'center' }
]

export default {
  components: {
    VxeTable
  },
  mixins: [GridMixin],
  data() {
    return {
      cols,
      childCols,
      menuConfig,
      formConfig,
      disableMixinCreated: true
    }
  },
  mounted() {
    this.loadData()
  },
  methods: {
    formSubmitEvent() {
      console.log('form submit')
    },
    formResetEvent() {
      console.log('form reset')
    },
    cellMenuEvent({ row }) {
      const $grid = this.$refs.table
      if ($grid) {
        $grid.setCurrentRow(row)
      }
    },
    menuClickEvent({ menu, row, column }) {
      const $grid = this.$refs.table
      if ($grid) {
        switch (menu.code) {
          case 'copy':
            if (row && column) {
              if (VxeUI.clipboard.copy(row[column.field])) {
                VxeUI.modal.message({ content: '已复制到剪贴板!', status: 'success' })
              }
            }
            break
          case 'clear':
            $grid.clearData(row, column.field)
            break
          case 'myPrint':
            $grid.print()
            break
          case 'myExport':
            $grid.exportData()
            break
          default:
            VxeUI.modal.message({ content: `点击了 ${ menu.code }`, status: 'success' })
            break
        }
      }
    },
    handleExport() {
      const $grid = this.$refs.table
      if ($grid) {
        $grid.openExport()
      }
    },
    getSelectEvent() {
      const $table = this.$refs.table
      if ($table) {
        const selectRecords = $table.getCheckboxRecords()
        console.log('选中数据:', selectRecords)
      }
    },
    sortEvent(field, order) {
      const $table = this.$refs.table
      if ($table) {
        $table.sort({ field, order })
      }
    },
    clearSortEvent() {
      const $table = this.$refs.table
      if ($table) {
        $table.clearSort()
      }
    },
    handleEdit(record) {
      console.log('Edit:', record)
      this.$refs.table.setEditRow(record)
    },
    loadData(arg) {
      // if (!this.url.list) {
      //   this.$message.error({
      //     key: this.messageKey,
      //     content: '请设置url.list属性!'
      //   })
      //   return
      // }
      // 加载数据,若传入参数1则加载第一页的内容
      if (arg === 1) {
        if (false != this.ipagination) {
          this.ipagination.current = 1
        }
      }
      this.selectedRowKeys = []
      this.selectedRows = []
      // 查询条件
      let params = this.getQueryParams()
      this.loading = true
      // get(this.url.list, params).then(res=> {
      loadMockData(this.ipagination.current, this.ipagination.pageSize).then(res => {
        if (res.success) {
          if (this.rowKey) {
            this.initRowKey(res.data.records)
          }
          this.dataSource = res.data.records
          if (false !== this.ipagination) {
            if (this.dataSource.length <= 0 && this.ipagination.current > 1) {
              this.ipagination.current--
              this.loadData()
              return
            } else if (this.dataSource.length > 0 && this.ipagination.current < 1) {
              this.ipagination.current = 1
            }
            this.ipagination.total = 100
          }
          this.loadDataSuccess()
        } else {
          this.$message.warning({
            key: this.messageKey,
            content: (res.msg && res.msg !== '操作成功' ? res.msg : '操作失败!')
          })
        }
        // setTimeout(() => {
        this.loading = false
        // }, 5000)
      })
    },
    handleEditClosed(params) {
      const { row, rowIndex, column, columnIndex } = params
      console.log('Row edit closed:(' + rowIndex + ', ' + columnIndex + ')', row, row[column.field])
    },
    handleCellClick(params) {
      console.log('Cell click:', params)
    }
  }
}
</script>

<style scoped>
/* 可以根据需要添加样式 */
.demo-page-wrapper {
  height: calc(100vh - 158px);
}
</style>	

附件:模拟数据生成方法js文件generateData.js

import XEUtils from 'xe-utils'

const arList = XEUtils.shuffle(XEUtils.range(1, 21).map(num => `https://vxeui.com/resource/avatarImg/avatar${ num }.jpeg`))
const neList = XEUtils.shuffle(['张三', '李四', '王五', '小徐', '老张', '老六', '小明', '老徐', '小张', '小赵', '老高', '老铁', '赵高', '小王', '老王'])
const cyList = XEUtils.shuffle(['sz', 'gz', 'bj', 'sh', 'hz'])
const sxList = XEUtils.shuffle(XEUtils.range(1, 60).map(num => `${ num % 2 }`))
const aeList = XEUtils.shuffle(XEUtils.range(18, 66))
const elList = XEUtils.range(1, 60).map(() => `${ XEUtils.sample('qwertyuiopasdfghjklzxcvbnm'.split(''), XEUtils.random(6, 16)).join('') }@163.com`)
const lnList = XEUtils.shuffle(XEUtils.range(0, 5))
const asmMpas = {
  m1: XEUtils.shuffle(XEUtils.range(1000, 1500)),
  m2: XEUtils.shuffle(XEUtils.range(1100, 1400)),
  m3: XEUtils.shuffle(XEUtils.range(800, 1200)),
  m4: XEUtils.shuffle(XEUtils.range(3000, 3600)),
  m5: XEUtils.shuffle(XEUtils.range(2000, 2100)),
  m6: XEUtils.shuffle(XEUtils.range(1600, 1700)),
  m7: XEUtils.shuffle(XEUtils.range(1200, 1300)),
  m8: XEUtils.shuffle(XEUtils.range(1100, 1200)),
  m9: XEUtils.shuffle(XEUtils.range(1700, 1800)),
  m10: XEUtils.shuffle(XEUtils.range(1300, 1700)),
  m11: XEUtils.shuffle(XEUtils.range(1000, 1300)),
  m12: XEUtils.shuffle(XEUtils.range(800, 1200))
}
const fgList = XEUtils.shuffle(XEUtils.range(1, 60).map(num => (num % 2) === 0))
const cacheList = []

export function loadMockData(page, rows) {
  // 计算需要生成的总数据量
  const rSize = page * rows
  return new Promise(resolve => {
    setTimeout(() => {
      // 生成足够的数据到 cacheList
      for (let i = cacheList.length; i < rSize; i++) {
        const item = {
          id: 1000000 + i,
          name: neList[i % neList.length],
          nickname: '',
          sex: sxList[i % sxList.length],
          age: aeList[i % aeList.length],
          email: elList[i % elList.length],
          city: cyList[i % cyList.length],
          avatarUrl: arList[i % arList.length],
          levelNum: lnList[i % lnList.length],
          annualStatement: {
            m1: asmMpas.m1[i % asmMpas.m1.length],
            m2: asmMpas.m2[i % asmMpas.m2.length],
            m3: asmMpas.m3[i % asmMpas.m3.length],
            m4: asmMpas.m4[i % asmMpas.m4.length],
            m5: asmMpas.m5[i % asmMpas.m5.length],
            m6: asmMpas.m6[i % asmMpas.m6.length],
            m7: asmMpas.m7[i % asmMpas.m7.length],
            m8: asmMpas.m8[i % asmMpas.m8.length],
            m9: asmMpas.m9[i % asmMpas.m9.length],
            m10: asmMpas.m10[i % asmMpas.m10.length],
            m11: asmMpas.m11[i % asmMpas.m11.length],
            m12: asmMpas.m12[i % asmMpas.m12.length]
          },
          flag: fgList[i % fgList.length],
          bookList: [
            { id: 1000000 + i + 1, name: '书籍名称1', author: '作者1', price: 100 },
            { id: 1000000 + i + 2, name: '书籍名称2', author: '作者2', price: 200 },
            { id: 1000000 + i + 3, name: '书籍名称3', author: '作者3', price: 300 },
            { id: 1000000 + i + 4, name: '书籍名称4', author: '作者4', price: 400 },
            { id: 1000000 + i + 5, name: '书籍名称5', author: '作者5', price: 500 }
          ]
        }
        cacheList.push(item)
      }
      // 计算当前页数据的起始和结束索引
      const startIndex = (page - 1) * rows
      const endIndex = startIndex + rows
      // 截取当前页的数据
      const data = cacheList.slice(startIndex, endIndex)
      resolve({
        success: true,
        data: {
          records: data
        }
      })
    }, 150)
  })
}

​ 通过以上步骤,我们完成了在 Vue2 项目中对 Vxe Table 的封装及使用示例。通过合理的封装,能够提高代码的复用性和可维护性,更高效地实现项目中表格相关的功能需求。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值