自用存档,如需要依赖和使用说明可以私信
完整项目地址
功能描述
1、表格支持自适应页面的高度、宽度
2、支持行筛选、排序(服务端筛选排序)、支持前端底部汇总sum、avg
3、支持列宽度调整、顺序调整、字段隐藏/显示、固定左侧/右侧的配置,且可通过id保存到服务端、下次打开页面时自动从服务端加载配置
4、支持通过配置生成表格上的筛选表单,作为表格筛选条件的一部分传入服务端筛选
5、支持动态修改Locale切换国际化,支持动态切换light/dark两种色彩模式
6、支持行内编辑,行内编辑时可通过键盘快捷键切换下一个框,到尾部后可自动新增行
7、表头筛选支持按配置数据源的方式生成element的输入框、下拉框、日期选择控件来筛选
8、支持主表明细
9、支持弹窗高级筛选,筛选条件可通过接口保存为常用筛选
10、支持配置导出列后进行excel导出
组件代码
<template>
<div :style="isDark ? 'background-color: #000;' : 'background-color: #fff;'">
<vxe-pulldown v-model="showPull" style="position: fixed;top: 10px;right: 10px;" mode="text">
<template #default>
<vxe-button icon="vxe-icon-arrow-down" @click="toggleEvent">Language</vxe-button>
</template>
<template #dropdown>
<div>
<div class="my-dropdown-link" @click="clickEvent('zh-CN')">中文(简体)</div>
<div class="my-dropdown-link" @click="clickEvent('zh-HK')">中文(香港)</div>
<div class="my-dropdown-link" @click="clickEvent('zh-TW')">中文(繁体)</div>
<div class="my-dropdown-link" @click="clickEvent('zh-MO')">中文(澳门)</div>
<div class="my-dropdown-link" @click="clickEvent('en-US')">英语(美国)</div>
<div class="my-dropdown-link" @click="clickEvent('es-ES')">西班牙语(国际)</div>
<div class="my-dropdown-link" @click="clickEvent('hu-HU')">匈牙利语</div>
<div class="my-dropdown-link" @click="clickEvent('ja-JP')">日语</div>
<div class="my-dropdown-link" @click="clickEvent('ko-KR')">朝鲜语</div>
<div class="my-dropdown-link" @click="clickEvent('pt-BR')">葡萄牙语</div>
<div class="my-dropdown-link" @click="clickEvent('ru-RU')">俄语</div>
<div class="my-dropdown-link" @click="clickEvent('uk-UA')">乌克兰语</div>
</div>
</template>
</vxe-pulldown>
<div class="toolbar">
<vxe-button status="primary" icon="vxe-icon-save" circle @click="exportEvent"></vxe-button>
<vxe-button status="primary" icon="vxe-icon-setting" circle @click="openToolEvent"></vxe-button>
<vxe-button icon="vxe-icon-sunny" circle @click="toggleTheme"></vxe-button>
<vxe-button status="primary" icon="vxe-icon-search" circle @click="showFilter = true" />
</div>
<vxe-grid ref="gridRef" v-bind="gridOptions" v-on="gridEvents">
<template #expand_content="{ row }">
<div>Name:{{ row.name }}</div>
<div>Age:{{ row.age }}</div>
</template>
</vxe-grid>
<!-- 列配置弹窗 -->
<vxe-modal v-model="showTool" type="alert" title="列配置" showFooter width="1200px" @confirm="ToolClick">
<template #default>
<vxe-table :data="gridOptions.columns" show-overflow :edit-config="editToolConfig">
<vxe-column field="title" title="标题" :edit-render="{ name: 'input' }"></vxe-column>
<vxe-column field="width" title="宽度" :edit-render="{ name: 'input' }"></vxe-column>
<vxe-column field="align" title="对齐方式" :edit-render="{
name: 'select',
options: [{ label: '', value: '' }, { label: '左侧', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '右侧', value: 'right' }]
}"></vxe-column>
<vxe-column field="headerAlign" title="表头对齐方式" :edit-render="{
name: 'select',
options: [{ label: '', value: '' }, { label: '左侧', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '右侧', value: 'right' }]
}"></vxe-column>
<vxe-column field="footerAlign" title="表尾对齐方式" :edit-render="{
name: 'select',
options: [{ label: '', value: '' }, { label: '左侧', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '右侧', value: 'right' }]
}"></vxe-column>
<vxe-column field="fixed" title="冻结列" :edit-render="{
name: 'select',
options: [{ label: '', value: '' }, { label: '左侧', value: 'left' },
{ label: '右侧', value: 'right' }]
}"></vxe-column>
</vxe-table>
</template>
</vxe-modal>
<!-- 操作提示 -->
<div class="message" v-if="false">
<p style="min-width: 100%; text-indent: 2em;">" | Arrow Up ↑ | 移动到当前活动单元格上面的单元格 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Arrow Down ↓ | 移动到当前活动单元格下面的单元格 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Arrow Left ← | 移动到当前活动单元格左边的单元格 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Arrow Right → | 移动到当前活动单元格右边的单元格 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Tab | 移动到当前选中或活动单元格的右侧单元格,如果到最后一列且存在下一行,则从下一行开始移动 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Tab + Shift | 移动到当前选中或活动单元格的左侧单元格,如果到第一列且存在上一行,则从上一行开始移动 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Spacebar | 如果单元格是复选框或单选框则切换勾选状态 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Enter | 取消编辑并移动到当前活动单元格下面的单元格 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Delete | 清空内容 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Backspace | 清空内容并激活选中单元格为编辑状态 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | F2 | 激活单元格编辑 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Esc | 取消单元格编辑 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Page Up | 向上翻页滚动 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Page Down | 向下翻页滚动 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | Home | 滚动到顶部 |。"</p>
<p style="min-width: 100%; text-indent: 2em;">" | End | 滚动到底部 |。"</p>
</div>
<!-- 高级搜索弹窗 -->
<vxe-modal v-model="showFilter" type="alert" title="高级筛选" showFooter width="1200px" @confirm="filterConfirm">
<template #default>
<div class="dialog-header">
<div>
<vxe-button-group>
<vxe-button status="primary" @click="addFilterRule">新增筛选</vxe-button>
<vxe-button status="primary" @click="resetFilter">重置条件</vxe-button>
</vxe-button-group>
</div>
<div>
<vxe-button-group>
<vxe-button status="primary" @click="saveFilter(0)">保存方案</vxe-button>
<vxe-button status="primary" @click="saveFilter(2)">另存方案</vxe-button>
</vxe-button-group>
<vxe-select style="margin-left: 10px;" v-model="selectedFilterIndex" @change="selectFilter"
placeholder="选择方案">
<vxe-option v-for="(item, index) in filterConfigurations" :key="index" :label="item.name"
:value="index" />
</vxe-select>
</div>
</div>
<vxe-table ref="filterTableRef" :data="filterRules" style="width: 100%" :row-config="{ keyField: 'fieldCount' }"
:tree-config="treeConfig" :column-config="columnConfig">
<vxe-column field="field" title="列名" tree-node>
<template #default="{ row }">
<vxe-select v-model="row.field" @change="handleFieldChange(row)">
<vxe-option v-for="col in filteredColumns" :key="col.field" :label="col.title" :value="col.field" />
</vxe-select>
</template>
</vxe-column>
<vxe-column field="operator" title="比较运算符">
<template #default="{ row }">
<vxe-select v-model="row.operator" :disabled="!row.field" @change="handleOperatorChange(row)">
<vxe-option label="等于" value="eq" v-if="['number', 'string', 'date'].includes(row.type)" />
<vxe-option label="不等于" value="ne" v-if="['number', 'string', 'date'].includes(row.type)" />
<vxe-option label="大于" value="gt" v-if="['number', 'date'].includes(row.type)" />
<vxe-option label="大于或等于" value="ge" v-if="['number', 'date'].includes(row.type)" />
<vxe-option label="小于" value="lt" v-if="['number', 'date'].includes(row.type)" />
<vxe-option label="小于或等于" value="le" v-if="['number', 'date'].includes(row.type)" />
<vxe-option label="介于" value="between" v-if="row.type === 'date'" />
<vxe-option label="不介于" value="notBetween" v-if="row.type === 'date'" />
<vxe-option label="包含" value="like" v-if="row.type === 'string'" />
<vxe-option label="不包含" value="notLike" v-if="row.type === 'string'" />
<vxe-option label="以…开始" value="likeLeft" v-if="row.type === 'string'" />
<vxe-option label="以…结束" value="likeRight" v-if="row.type === 'string'" />
<vxe-option label="空" value="isNull" v-if="row.type === 'string'" />
<vxe-option label="非空" value="isNotNUll" v-if="row.type === 'string'" />
<vxe-option label="在列表之中" value="in" v-if="['number', 'string'].includes(row.type)" />
<vxe-option label="不在列表之中" value="notIn" v-if="['number', 'string'].includes(row.type)" />
</vxe-select>
</template>
</vxe-column>
<vxe-column field="value" title="值">
<template #default="{ row }">
<template v-if="!['in', 'notIn'].includes(row.operator)">
<vxe-input v-if="row.type === 'string'" v-model="row.value" :disabled="!row.field" />
<vxe-input v-if="row.type === 'number'" v-model="row.value" type="number" :disabled="!row.field" />
<vxe-date-range-picker v-if="row.type === 'date' && ['between', 'notBetween'].includes(row.operator)"
v-model="row.value" type="date" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
<vxe-date-picker v-if="row.type === 'date' && !['between', 'notBetween'].includes(row.operator)"
v-model="row.value" type="date" />
</template>
<vxe-select v-else v-model="row.value" multiple :options="row.inList || []" />
</template>
</vxe-column>
<vxe-column field="logic" title="逻辑运算符">
<template #default="{ row }">
<vxe-select v-model="row.logic" :disabled="!row.field">
<vxe-option label="并且" value="and" />
<vxe-option label="或者" value="or" />
</vxe-select>
</template>
</vxe-column>
<vxe-column title="操作" width="160">
<template #default="{ row }">
<vxe-button status="primary" icon="vxe-icon-add" @click="addChildFilter(row)" />
<vxe-button status="primary" icon="vxe-icon-minus" @click="removeFilter(row)" />
</template>
</vxe-column>
</vxe-table>
</template>
</vxe-modal>
<!-- 保存筛选方案弹窗 -->
<vxe-modal v-model="showSaveFilter" type="alert" title="请为该规则添加一个名字" showFooter @confirm="confirmSaveFilter">
<template #default>
<vxe-input v-model="filterName" placeholder="请输入方案名称" />
</template>
</vxe-modal>
</div>
</template>
<script lang="ts" setup>
import { defineProps, ref, reactive, computed, nextTick } from 'vue'
import { VxeUI } from 'vxe-table'
import type { VxeGridProps } from 'vxe-table'
// 定义组件属性
const props = defineProps({
tableData: {
type: Array,
required: true
},
tableConfig: {
type: Object,
required: true
},
ifForm: {
type: Boolean,
default: false
}
})
// 创建过滤后的列数组
const filteredColumns = computed(() => {
return props.tableConfig.columns.filter((column: { type: string }) =>
!['seq', 'checkbox', 'expand'].includes(column.type)
)
})
// 定义组件事件
const emit = defineEmits(['filter', 'search', 'reset'])
const filterTableRef = ref()
interface FilterRule {
id: number
parentId: number | null
logic: string
field: string
operator: string
value: string | number | any[]
fieldCount: number
type: string
filters?: FilterRule[]
hasChildren?: number
}
const gridRef = ref()
const gridOptions = reactive<VxeGridProps>({
border: true,
showFooter: true,
showOverflow: true,
keepSource: true,
mouseConfig: {
selected: true
},
keyboardConfig: {
isEdit: true,
isArrow: true,
isEnter: true,
isTab: true,
isDel: true,
isBack: true,
isEsc: true,
editMode: 'insert',
isLastEnterAppendRow: true
},
editConfig: {
trigger: 'click',
mode: 'cell',
showStatus: true
},
exportConfig: {
filename() {
return `导出文件名${Date.now()}`
},
sheetName() {
return `导出标题${Date.now()}`
},
excludeFields: [
'seq',
'checkbox',
'expand'
]
},
// 搜索配置
formConfig: props.ifForm ? {
data: props.tableConfig.columns.reduce((acc:any, col:any) => {
if (col.field) {
acc[col.field] = ''
}
return acc
}, {}),
items: [
...props.tableConfig.columns
.filter((col: { field: any; title: any }) => col.field && col.title)
.map((col: { field: any; title: any; editRender: any }) => ({
field: col.field,
title: col.title,
itemRender: col.editRender || { name: 'VxeInput' }
})),
{
itemRender: {
name: 'VxeButtonGroup',
options: [
{ type: 'submit', content: '搜索', status: 'primary' },
{ type: 'reset', content: '重置' }
]
}
}
]
} : undefined,
columns: props.tableConfig.columns,
data: props.tableData,
footerData: props.tableConfig.footerData
})
const exportEvent = () => {
gridOptions.columns = filteredColumns.value
ToolClick()
const $grid = gridRef.value
if ($grid) {
$grid.openExport()
}
// 屏蔽然后展示,绕过导出不能设置字段的缺陷
nextTick(() => {
gridOptions.columns = props.tableConfig.columns
ToolClick()
})
}
// 表头的返回
const gridEvents = {
formSubmit(params: any) {
emit('search', params)
},
formReset(params: any) {
emit('reset', params)
}
}
// 打开列布局
const showTool = ref(false)
const openToolEvent = () => {
showTool.value = true
}
const editToolConfig = ref({
trigger: 'click' as 'click' | 'manual' | 'dblclick',
mode: 'cell' as 'cell' | 'row'
})
// 触发列配置修改
const ToolClick = () => {
const $grid = gridRef.value
if ($grid) {
$grid.loadColumn(gridOptions.columns)
}
}
// 语言配置
import zhCN from 'vxe-table/lib/locale/lang/zh-CN' // 中文(简体)
import zhHK from 'vxe-table/lib/locale/lang/zh-HK' // 中文(香港)
import zhTW from 'vxe-table/lib/locale/lang/zh-TW' // 中文(繁体)
import zhMO from 'vxe-table/lib/locale/lang/zh-MO' // 中文(澳门)
import enUS from 'vxe-table/lib/locale/lang/en-US' // 英语(美国)
import esES from 'vxe-table/lib/locale/lang/es-ES' // 西班牙语(国际)
import huHU from 'vxe-table/lib/locale/lang/hu-HU' // 匈牙利语
import jaJP from 'vxe-table/lib/locale/lang/ja-JP' // 日语
import koKR from 'vxe-table/lib/locale/lang/ko-KR' // 朝鲜语
import ptBR from 'vxe-table/lib/locale/lang/pt-BR' // 葡萄牙语
import ruRU from 'vxe-table/lib/locale/lang/ru-RU' // 俄语
import ukUA from 'vxe-table/lib/locale/lang/uk-UA' // 乌克兰语
// 注册语言
VxeUI.setI18n('zh-CN', zhCN)
VxeUI.setI18n('zh-HK', zhHK)
VxeUI.setI18n('zh-TW', zhTW)
VxeUI.setI18n('zh-MO', zhMO)
VxeUI.setI18n('en-US', enUS)
VxeUI.setI18n('es-ES', esES)
VxeUI.setI18n('hu-HU', huHU)
VxeUI.setI18n('ja-JP', jaJP)
VxeUI.setI18n('ko-KR', koKR)
VxeUI.setI18n('pt-BR', ptBR)
VxeUI.setI18n('ru-RU', ruRU)
VxeUI.setI18n('uk-UA', ukUA)
// 下拉切换语言
const showPull = ref(false)
const i18Lang = ref('zhCN')
const toggleEvent = () => {
showPull.value = !showPull.value
}
const clickEvent = (num: any) => {
showPull.value = false
i18Lang.value = num
// 切换指定语言
VxeUI.setLanguage(num)
}
// 主题切换相关
const isDark = ref(false)
const toggleTheme = () => {
isDark.value = !isDark.value
VxeUI.setTheme(isDark.value ? 'dark' : 'light')
}
// 高级筛选相关
const showFilter = ref(false)
const showSaveFilter = ref(false)
const filterName = ref('')
const selectedFilterIndex = ref()
const filterRules = ref<FilterRule[]>([])
interface FilterConfiguration {
name: string
rules: FilterRule[]
}
const filterConfigurations = ref<FilterConfiguration[]>([])
// 从本地存储加载筛选配置
const loadFilterConfigurations = () => {
const storedConfig = localStorage.getItem('tableFilters')
if (storedConfig) {
filterConfigurations.value = JSON.parse(storedConfig)
}
}
const columnConfig = reactive({
resizable: true
})
const treeConfig = reactive({
transform: true,
rowField: 'id',
parentField: 'parentId',
})
// 添加筛选规则
const addFilterRule = () => {
filterRules.value.push({
id: Date.now(),
parentId: null,
logic: 'and',
field: '',
operator: '',
value: '',
fieldCount: Date.now(),
type: 'string'
})
}
// 重置筛选条件
const resetFilter = () => {
filterRules.value = []
}
// 处理字段变更
const handleFieldChange = (row: { field: string | undefined; type: string; value: string; operator: string }) => {
const column = gridOptions.columns?.find(col => col.field === row.field)
if (column) {
row.type = column.type || 'string'
row.value = ''
row.operator = ''
}
}
// 处理运算符变更
const handleOperatorChange = (row: { operator: string; value: string | never[] | null; type: string }) => {
if (['between', 'notBetween'].includes(row.operator)) {
row.value = []
} else if (row.type === 'string') {
row.value = ''
} else if (row.type === 'number') {
row.value = null
}
}
// 添加子筛选规则
const addChildFilter = (row: any) => {
const record = {
id: Date.now(),
parentId: row.id,
logic: 'and',
field: '',
operator: '',
value: '',
fieldCount: Date.now(),
type: 'string'
}
filterRules.value.push(record)
// filterTableRef.value.insert(record)
console.log(row, filterRules.value)
}
// 移除筛选规则
const removeFilter = (row: any) => {
if (row.hasChildren) {
const parent = filterRules.value.find(r => r.fieldCount === row.hasChildren)
if (parent && parent.filters) {
parent.filters = parent.filters.filter(f => f.fieldCount !== row.fieldCount)
}
} else {
filterRules.value = filterRules.value.filter(r => r.fieldCount !== row.fieldCount)
}
}
// 选择筛选方案
const selectFilter = () => {
if (selectedFilterIndex.value !== undefined) {
filterRules.value = JSON.parse(JSON.stringify(filterConfigurations.value[selectedFilterIndex.value].rules))
}
}
// 保存筛选方案
const saveFilter = (type: number) => {
if (type === 2) {
showSaveFilter.value = true
} else if (type === 0) {
if (selectedFilterIndex.value !== undefined) {
filterConfigurations.value[selectedFilterIndex.value].rules = JSON.parse(JSON.stringify(filterRules.value))
localStorage.setItem('tableFilters', JSON.stringify(filterConfigurations.value))
} else {
showSaveFilter.value = true
}
}
}
// 确认保存筛选方案
const confirmSaveFilter = () => {
if (filterName.value) {
const newConfig = {
name: filterName.value,
rules: JSON.parse(JSON.stringify(filterRules.value))
}
filterConfigurations.value.push(newConfig)
localStorage.setItem('tableFilters', JSON.stringify(filterConfigurations.value))
showSaveFilter.value = false
filterName.value = ''
}
}
// 确认筛选
const filterConfirm = () => {
const filterConfig = {
logic: 'and',
field: '',
operator: '',
value: '',
filters: filterRules.value
}
// 触发筛选事件
emit('filter', filterConfig)
showFilter.value = false
}
// 初始化时加载筛选配置
loadFilterConfigurations()
</script>
<style scoped>
.toolbar {
display: flex;
align-items: center;
justify-content: end;
margin-bottom: 6px;
}
.my-dropdown-link {
min-width: 100%;
cursor: pointer;
}
.message {
color: #999;
font-size: 14px;
text-align: left;
}
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.vxe-select {
width: 120px;
}
.vxe-input {
width: 200px;
}
</style>
组件引用
<script setup lang="ts">
import tableGrid from './components/tableGrid.vue';
import { ref, reactive } from 'vue';
// 表格数据
const tableData = ref([
{ id: 10001, name: 'Test1', date: '', role: 'Develop', sex: 'Man', age: 28, address: 'test abc' },
{ id: 10002, name: 'Test2', date: '', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' },
{ id: 10003, name: 'Test3', date: '', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' },
{ id: 10004, name: 'Test4', date: '', role: 'Designer', sex: 'Women', age: 24, address: 'Shanghai' }
])
// 表格配置
const tableConfig = reactive({
columns: [
{ field: 'seq', type: 'seq', width: 70, fixed: 'left' },
{ field: 'checkbox', type: 'checkbox', width: 70 },
{ type: 'expand', width: 80, slots: { content: 'expand_content' } },
{ field: 'name', title: 'Name', align: 'center', headerAlign: 'center', footerAlign: 'center', editRender: { name: 'input' } },
{ field: 'date', title: '日期', align: 'center', headerAlign: 'center', footerAlign: 'center', width: 180, editRender: { name: 'VxeDatePicker', props: { type: 'date' } } },
{
field: 'sex', title: 'Sex', align: 'center', headerAlign: 'center', footerAlign: 'center', editRender: {
name: 'select',
options: [{ label: '未知', value: '' },
{ label: '女', value: 'Women' },
{ label: '男', value: 'Man' }]
}
},
{ field: 'age', title: 'Age', align: 'center', headerAlign: 'center', footerAlign: 'center', editRender: { name: 'VxeNumberInput', props: { type: 'integer' } } }
],
footerData: [
{ seq: '合计', sex: '666', age: '999' },
{ seq: '平均', sex: '888', age: '333' }
]
})
// 处理筛选事件
const handleFilter = (filterConfig: any) => {
console.log('筛选配置:', filterConfig)
// 这里可以根据筛选配置处理数据
}
// 处理查询事件
const handleSearch = (filterConfig: any) => {
console.log('查询:', filterConfig)
// 这里可以处理数据
}
// 处理重置事件
const handleResetr = (filterConfig: any) => {
console.log('重置:', filterConfig)
// 这里可以处理数据
}
</script>
<template>
<div>
<tableGrid
:table-data="tableData"
:table-config="tableConfig"
:if-form="true"
@filter="handleFilter"
@search="handleSearch"
@reset="handleResetr"
></tableGrid>
</div>
</template>
<style>
body {
margin: 0;
width: 100%;
height: 100%;
}
</style>
readme.md
TableGrid 组件使用说明
TableGrid 是一个基于Vxe-Table的高级表格组件,支持数据展示、编辑、筛选、导出等功能。
Vxe-Table文档地址:https://vxetable.cn/#/demo/list
功能特性
- 数据展示与编辑
- 高级筛选功能
- 列配置功能
- 数据导出功能
- 主题切换
- 多语言支持
- 搜索表单(可选)
依赖
npm install vxe-pc-ui@4.5.36 vxe-table@4.13.20
# 或者
yarn add vxe-pc-ui@4.5.36 vxe-table@4.13.20
# 或者
pnpm add vxe-pc-ui@4.5.36 vxe-table@4.13.20
基础用法
<template>
<tableGrid
:table-data="tableData"
:table-config="tableConfig"
:if-form="true"
@filter="handleFilter"
></tableGrid>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
// 表格数据
const tableData = ref([
{
id: 10001,
name: "Test1",
date: "",
role: "Develop",
sex: "Man",
age: 28,
address: "test abc",
},
{
id: 10002,
name: "Test2",
date: "",
role: "Test",
sex: "Women",
age: 22,
address: "Guangzhou",
},
]);
// 表格配置
const tableConfig = reactive({
columns: [
// 序号列配置
{
field: "seq", // 字段名称
type: "seq", // 字段类型为序号列
width: 70, // 列宽度70px
fixed: "left", // 固定在表格左侧
},
// 复选框列配置
{
field: "checkbox", // 字段名称
type: "checkbox", // 字段类型为复选框
width: 70, // 列宽度70px
},
// 展开列配置
{
type: "expand", // 字段类型为展开列
width: 80, // 列宽度80px
slots: { content: "expand_content" }, // 展开内容插槽
},
// 姓名列配置
{
field: "name", // 字段名称
title: "Name", // 列标题
align: "center", // 内容居中
headerAlign: "center", // 表头居中
footerAlign: "center", // 表尾居中
editRender: {
// 编辑配置
name: "input", // 使用input输入框进行编辑
},
},
// 日期列配置
{
field: "date", // 字段名称
title: "日期", // 列标题
align: "center", // 内容居中
headerAlign: "center", // 表头居中
footerAlign: "center", // 表尾居中
width: 180, // 列宽度180px
editRender: {
// 编辑配置
name: "VxeDatePicker", // 使用日期选择器
props: { type: "date" }, // 日期选择器类型为日期
},
},
// 性别列配置
{
field: "sex", // 字段名称
title: "Sex", // 列标题
align: "center", // 内容居中
headerAlign: "center", // 表头居中
footerAlign: "center", // 表尾居中
editRender: {
// 编辑配置
name: "select", // 使用下拉选择器
options: [
// 下拉选项配置
{ label: "", value: "" },
{ label: "女", value: "Women" },
{ label: "男", value: "Man" },
],
},
},
// 年龄列配置
{
field: "age", // 字段名称
title: "Age", // 列标题
align: "center", // 内容居中
headerAlign: "center", // 表头居中
footerAlign: "center", // 表尾居中
editRender: {
// 编辑配置
name: "VxeNumberInput", // 使用数字输入框
props: { type: "integer" }, // 限制为整数类型
},
},
],
// 表尾数据配置
footerData: [
{ seq: "合计", sex: "666", age: "999" },
{ seq: "平均", sex: "888", age: "333" },
],
});
// 处理筛选事件
const handleFilter = (filterConfig: any) => {
console.log("筛选配置:", filterConfig);
// 这里可以根据筛选配置处理数据
};
</script>
Props 说明
属性名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
tableData | Array | 是 | - | 表格数据源 |
tableConfig | Object | 是 | - | 表格配置对象 |
ifForm | Boolean | 否 | false | 是否显示搜索表单 |
tableConfig 配置说明
interface TableConfig {
columns: Array<{
field?: string; // 字段名
title?: string; // 列标题
type?: string; // 列类型
width?: number; // 列宽度
fixed?: "left" | "right"; // 固定列
align?: string; // 对齐方式
headerAlign?: string; // 表头对齐方式
footerAlign?: string; // 表尾对齐方式
editRender?: {
// 编辑配置
name: string; // 编辑组件名称
props?: object; // 编辑组件属性
options?: Array<{
// 选项(用于下拉框等)
label: string;
value: any;
}>;
};
}>;
footerData?: Array<any>; // 表尾数据
}
事件说明
事件名 | 参数 | 说明 |
---|---|---|
filter | filterConfig: object | 高级筛选事件,返回筛选配置 |
search | params: object | 搜索表单提交事件,返回搜索参数 |
reset | params: object | 搜索表单重置事件,返回重置后的参数 |
功能说明
1. 搜索表单
- 当
ifForm
为true
时,会自动根据tableConfig.columns
生成搜索表单 - 搜索表单的字段类型会自动继承表格列的编辑类型
- 支持搜索和重置功能
2. 高级筛选
- 支持多条件组合筛选
- 支持保存筛选方案
- 支持筛选方案的导入导出
3. 列配置
- 支持自定义列宽
- 支持列对齐方式设置
- 支持列固定
4. 数据导出
- 支持导出为 Excel
- 支持自定义导出文件名和表名
5. 主题切换
- 支持明暗主题切换
- 支持自定义主题
6. 多语言
- 支持多种语言切换
- 支持自定义语言包
注意事项
- 表格列配置中的
editRender
会同时影响编辑和搜索表单的输入组件 - 高级筛选功能需要自行实现数据过滤逻辑
- 导出功能需要配置相应的导出权限
- 多语言切换需要预先注册语言包
示例代码
基础表格
<tableGrid :table-data="tableData" :table-config="tableConfig"></tableGrid>
带搜索表单的表格
<tableGrid
:table-data="tableData"
:table-config="tableConfig"
:if-form="true"
></tableGrid>
带筛选功能的表格
<tableGrid
:table-data="tableData"
:table-config="tableConfig"
@filter="handleFilter"
></tableGrid>
最后:谴责深圳【普盛网络】有偿定制功能却要了源码就跑的行为