一、前言
在前端开发中,表格展示数据是极为常见的需求。Vxe Table 作为一款功能强大的基于 Vue 的表格组件库,为我们高效构建表格提供了诸多便利。Vxe Table 具有丰富的特性,如支持复杂表头、单元格编辑、数据校验、排序、筛选等。它拥有简洁易用的 API,能帮助开发者快速搭建出符合业务需求的表格界面,大大提高开发效率。其强大的自定义能力,可以满足各种复杂场景下的表格展示需求。本文将详细介绍如何在 Vue2 项目中对 Vxe Table 进行封装,并给出实际使用示例。
二、Vxe Table 的封装
1 安装及引入流程见官网VxeTable v3
注意版本适配,Vue2对应V3版本,Vue3对应V4版本。
2 封装自定义表格组件
在项目/src/components
目录下创建vxeTable
目录,新建一个index.js
和index.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 的封装及使用示例。通过合理的封装,能够提高代码的复用性和可维护性,更高效地实现项目中表格相关的功能需求。