一、需求
在后台管理项目中,可以显而易见的见到表格组件,为了方便我们会把表格组件进行二次封装,即方便了开发,也方便了维护。
2023年04月01日更新单元格编辑功能
注意事项
2023年4月25号
// LTable.vue组件中
// 在修改某一行数据的时候切记删除添加的 index 字段
// 否则会报这样的一个错误:caught (in promise) ER_BAD_FIELD_ERROR: Unknown column 'index' in 'field list'
// 就是这里这个index导致的,在你去请求接口把这个字段删除掉就可以了,
// 例子:delete form.index 下面接着去请求接口就行了
// 为每一行返回固定的className
function tableRowClassName({ row, rowIndex }) {
row.index = rowIndex;
}
// 为所有单元格设置一个固定的 className
function tableCellClassName({ column, columnIndex }) {
column.index = columnIndex;
}
二、组件功能
1、基础表格
2、表格分页
3、单条数据操作
4、表格中图片展示
5、根据需要格式化时间
6、单条数据操作 启用/禁用
7、根据数据状态展示不同样式
三、准备工作
在 components 下创建 LTable.vue 文件
接下来是我们的页面
分别需要一个 index.vue 和 table.ts 文件
接下来我们直接上代码
四、LTable.vue 完整代码
这里需要吧 LTable.vue 该组件全局组册 或者使用时单独引入(根据自己需求)
<template>
<div class="l-table">
<!-- 表格 -->
<el-table :data="props.tableModule.dataList" border height="100%" style="width: 100%; overflow-y: scroll"
v-loading="props.tableModule.loading" @selection-change="props.tableModule.selectChange"
:row-class-name="tableRowClassName" :cell-class-name="tableCellClassName" @cell-dblclick="cellDblClick">
<el-table-column type="selection" width="50" align="center" />
<template v-if="tableChildren == '1'">
<el-table-column v-for="(item, index) in props.tableModule.columns" :key="item.prop" :prop="item.prop"
:label="item.label" :align="item.align || 'left'" :width="item.width" :min-width="item.min_width"
:fixed="item.fixed">
<template slot-scope="scope" #default="scope">
<div v-if="item.type == 'switch'">
<el-switch v-model="scope.row[item.prop]" :active-value="item.activeValue"
:inactive-value="item.inactiveValue" @change="props.tableModule.switchChange(scope.row)">
</el-switch>
</div>
<div v-else-if="item.type == 'status'">
<el-tag :type="item.color ? item.color[scope.row[item.prop]] : ''">{{
props.tableModule.fieldChange(scope.row[item.prop], item.option) }}</el-tag>
</div>
<div v-else-if="item.type == 'image'">
<el-image style="width: 60px; height: 60px" :src="scope.row[item.prop]"
:preview-src-list="[scope.row[item.prop]]">
</el-image>
</div>
<div v-else-if="item.type == 'time'">{{ formatDate(scope.row[item.prop]) }}</div>
<div v-else-if="item.isEdit">
<el-input v-model="scope.row[item.prop]" :placeholder="'请输入' + item.label"
@blur="inputBlur(scope.row)" autofocus ref="inputRef"
v-if="scope.row['index'] == rowIndex && scope.column['index'] == columnIndex" />
<div v-else>{{ scope.row[item.prop] }}</div>
</div>
<div v-else>{{ scope.row[item.prop] }}</div>
</template>
</el-table-column>
</template>
<template v-else-if="tableChildren == '2'">
<el-table-column v-for="(one, index) in props.tableModule.columns" :key="index" :label="one.label">
<el-table-column v-for="item in props.tableModule.columns[index].children" :key="item.prop"
:prop="item.prop" :label="item.label" :align="item.align || 'left'" :width="item.width"
:min-width="item.min_width" :fixed="item.fixed">
<template slot-scope="scope" #default="scope">
<div v-if="item.type == 'switch'">
<el-switch v-model="scope.row[item.prop]" :active-value="item.activeValue"
:inactive-value="item.inactiveValue"
@change="props.tableModule.switchChange(scope.row)">
</el-switch>
</div>
<div v-else-if="item.type == 'status'">
<el-tag :type="item.color ? item.color[scope.row[item.prop]] : ''">{{
props.tableModule.fieldChange(scope.row[item.prop], item.option) }}</el-tag>
</div>
<div v-else-if="item.type == 'image'">
<el-image style="width: 60px; height: 60px" :src="scope.row[item.prop]"
:preview-src-list="[scope.row[item.prop]]">
</el-image>
</div>
<div v-else-if="item.type == 'time'">{{ formatDate(scope.row[item.prop]) }}</div>
<div v-else-if="item.isEdit">
<el-input v-model="scope.row[item.prop]" :placeholder="'请输入' + item.label"
@blur="inputBlur(scope.row)" autofocus ref="inputRef"
v-if="scope.row['index'] == rowIndex && scope.column['index'] == columnIndex" />
<div v-else>{{ scope.row[item.prop] }}</div>
</div>
<div v-else>{{ scope.row[item.prop] }}</div>
</template>
</el-table-column>
</el-table-column>
</template>
<slot name="event"></slot>
</el-table>
<div class="l-pages">
<!-- 分页 -->
<el-pagination :current-page="props.tableModule.pages.page" :page-size.sync="props.tableModule.pages.limit"
:page-sizes="pageSizes" :layout="layout" :total="props.tableModule.pages.total"
@size-change="props.tableModule.sizeChange" @current-change="props.tableModule.currentChange" />
</div>
</div>
</template>
<script setup>
import { defineProps, onMounted, reactive, toRefs } from 'vue'
import { formatDate } from '@/utils/index'
const props = defineProps({
tableModule: Object,
layout: {
type: String,
default: "total, sizes, prev, pager, next, jumper",
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50];
},
},
tableChildren: {
type: String,
default: '1'
}
})
const state = reactive({
rowIndex: 0,
columnIndex: 0
})
const { rowIndex, columnIndex } = toRefs(state)
onMounted(() => { })
// 编辑单元格
// 为每一行返回固定的className
function tableRowClassName({ row, rowIndex }) {
row.index = rowIndex;
}
// 为所有单元格设置一个固定的 className
function tableCellClassName({ column, columnIndex }) {
column.index = columnIndex;
}
// el-table单元格双击事件
function cellDblClick(row, column, cell, event) {
state.rowIndex = row.index
state.columnIndex = column.index
}
// input失去焦点
function inputBlur(row) {
state.rowIndex = 0
state.columnIndex = 0
props.tableModule.editInputBlur()
}
</script>
<style scoped lang="scss">
.l-table {
height: calc(100% - 32px);
padding-bottom: 10px;
.el-table {
height: calc(100% - 42px) !important;
}
.l-pages {
margin-top: 10px;
}
}
</style>
index.vue 完整代码
<template>
<div class="role">
<!-- :tableChildren="2" 开启二级表头 普通表格不穿 -->
<LTable :tableModule="tableModule" :tableChildren="2">
<template #event>
<el-table-column align="center" fixed="right" label="操作" width="120">
<template slot-scope="scope" #default="scope">
<el-button @click="userDelete(scope)">删除</el-button>
</template>
</el-table-column>
</template>
</LTable>
</div>
</template>
<script setup lang="ts">
import { reactive, toRefs, ref, onMounted } from 'vue'
import { columnsData } from './table'
import { ElMessage, ElMessageBox } from 'element-plus'
const state = reactive({
columns: columnsData,
selections: [],
loading: false,
dataList: [
{
username: '张三',
phone: '16688889999',
createDate: 1679489616000,
avatar: 'https://img1.baidu.com/it/u=2427424503,947601508&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
userStatus: 1,
}
],
pages: {
total: 0,
limit: 20,
page: 1,
}
})
const { loading, dataList, columns, pages } = toRefs(state)
onMounted(() => {
getDataList()
})
function getDataList() {
}
function userDelete(row: any) {
console.log(row)
}
// 表格分页相关
const tableModule = ref<Object>({
currentChange: currentChange,
selectChange: selectChange,
fieldChange: fieldChange,
sizeChange: sizeChange,
editInputBlur: editInputBlur,
columns: columns,
dataList: dataList,
loading: loading,
pages: pages
})
function selectChange(selection: any) {
state.selections = selection
}
function fieldChange(row: any, option: any) {
if (option[row]) {
return option[row]
}
}
function sizeChange(item: any) {
state.pages.limit = item
getDataList()
}
function currentChange(item: any) {
state.pages.page = item
getDataList()
}
function editInputBlur() {
console.log('可编辑单元格失去焦点')
}
</script>
<style scoped lang="scss"></style>
table.ts 完整代码
talbe.ts文件介绍
该文件是控制我们表格的列展示的
const columnsData: any = [
{
prop: 'username', // 数据字段对应
label: '姓名', // 列名称
min_width: 120, // 最小长度(这里建议每一个表格中最少有一个字段设置最小长度)设置这个就不用设置 widht 了,大家可以自己摸索
fixed: true // 固定表头(注意:二级表头时不可用)
},
{
prop: 'phone',
label: '手机号',
isEdit: true, // isEdit为 true 开启单元格编辑(可编辑的单元格由自己控制)
align: 'center', // 控制列对其方式(默认左对齐)
width: 150,
},
{
prop: 'createDate',
label: ' 创建时间',
align: 'center',
type: 'time', // 针对字段需要时间格式化
width: 180,
},
{
prop: 'avatar',
label: '头像',
align: 'center',
type: 'image', // 设置字段以图片展示
width: 80
},
{
prop: 'userStatus',
label: '用户状态',
align: 'center',
type: 'switch', // 编辑用户状态
activeValue: 1, // switch为打开时的值
inactiveValue: 2,// switch为关闭时的值
width: 100
},
{
prop: 'userStatus',
label: '用户状态',
align: 'center',
width: 100,
type: 'status', // 与 type 为 switch 不同(status主要是看的而不是对数据的操作)
option: {
'1': '启用',
'2': '禁用',
},
color: {
'1': 'success',
'2': 'info',
}
},
]
export {
columnsData
}
// 二级表头写法
const columnsData: any = [
{
label: '用户信息', // 表头名称
children: [ // 子级表头
{
prop: 'username',
label: '姓名',
min_width: 120
}
]
},
// 依次向下添加即可
]
export {
columnsData
}
这里附上一张效果图
二级表头效果图
可编辑单元格
表格增删改查请移步至 el-table表格查询封装