<template>
|
<el-table v-loading="loading" :data="tableData" :border="border" :show-selection="showSelection" :height="height" :max-height="maxHeight"
|
:header-cell-style="{ background: '#EBEEF5', color: '#3D3D3D' }" @selection-change="handleSelectionChange"
|
@row-click="handleRowClick" @row-dblclick="handleRowDblClick" :row-class-name="tableRowClassName" @cell-click="handleCellClick" :max-width="maxWidth"
|
@export="handleExport">
|
<el-table-column v-if="showSelection" type="selection" width="55" align="center" />
|
<el-table-column v-if="showIndex" label="序号" type="index" width="60" align="center" /> <template
|
v-for="col in columns" :key="col.prop">
|
<el-table-column v-bind="col" :show-overflow-tooltip="shouldShowTooltip(col, tableData)" align="center"> <template
|
#default="scope">
|
<template v-if="col.slot">
|
<slot></slot>
|
</template>
|
<template v-else>
|
<div class="cell-edit" @dblclick="handleCellEdit(scope.row, col.prop)"
|
:class="{'editable': isColumnEditable(col.prop)}"> <span
|
v-if="!scope.row.editing || !scope.row.editing[col.prop]" class="cell-text">
|
{{ scope.row[col.prop] == null || scope.row[col.prop] === '' ? '--' : scope.row[col.prop] }}
|
</span>
|
<el-input v-else v-model="scope.row[col.prop]" size="small"
|
@focus="handleCellFocus(scope.row, col.prop, $event)" @blur="handleCellSave(scope.row, col.prop)"
|
@keyup.enter="handleCellSave(scope.row, col.prop)" class="cell-input" />
|
</div>
|
</template>
|
</template>
|
</el-table-column>
|
</template>
|
<!-- 操作列 -->
|
<el-table-column v-if="showOperations" :label="operationsLabel" :width="operationsWidth" fixed="right" align="center">
|
<template #default="scope">
|
<slot name="operations" :row="scope.row">
|
<el-button v-if="operations.includes('edit')" link type="primary" size="small"
|
@click="handleEdit(scope.row)">编辑</el-button>
|
<!-- <el-button-->
|
<!-- v-if="operations.includes('delete')"-->
|
<!-- link-->
|
<!-- type="danger"-->
|
<!-- size="small"-->
|
<!-- @click="handleDelete(scope.row)"-->
|
<!-- >删除</el-button>-->
|
</slot>
|
</template>
|
</el-table-column>
|
</el-table>
|
</template>
|
|
<script setup>
|
import { defineEmits, nextTick } from 'vue'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
const props = defineProps({
|
// 获取行样式
|
tableRowClassName: {
|
type: Function,
|
default: () => ''
|
},
|
// 获取高度
|
height: {
|
type: [String, Number],
|
default: 'auto'
|
},
|
// 是否允许编辑单元格
|
editableCells: {
|
type: Boolean,
|
default: true
|
},
|
// 指定可编辑的列,如果为空数组则表示所有列都不可编辑
|
editableColumns: {
|
type: Array,
|
default: () => []
|
},
|
// 最大宽度
|
maxWidth: {
|
type: [String, Number],
|
default: 'auto'
|
},
|
handleCellClick: {
|
type: Function,
|
default: () => { }
|
},
|
handleRowClick: {
|
type: Function,
|
default: () => { }
|
},
|
handleExport: {
|
type: Function,
|
default: () => { }
|
},
|
handleRowDblClick: {
|
type: Function,
|
default: () => { }
|
},
|
// 高度
|
maxHeight: {
|
type: [String, Number],
|
default: 'auto'
|
},
|
// 加载状态
|
loading: {
|
type: Boolean,
|
default: false
|
},
|
// border
|
border: {
|
type: Boolean,
|
default: false
|
},
|
// 表格数据
|
tableData: {
|
type: Array,
|
default: () => []
|
},
|
// 是否显示选择列
|
showSelection: {
|
type: Boolean,
|
default: false
|
},
|
// 是否显示序号列
|
showIndex: {
|
type: Boolean,
|
default: true
|
},
|
// 列配置
|
columns: {
|
type: Array,
|
default: () => []
|
},
|
// 是否显示操作列
|
showOperations: {
|
type: Boolean,
|
default: true
|
},
|
// 操作列标签
|
operationsLabel: {
|
type: String,
|
default: '操作'
|
},
|
// 操作列宽度
|
operationsWidth: {
|
type: [String, Number],
|
default: 100
|
},
|
// 显示哪些操作按钮
|
operations: {
|
type: Array,
|
default: () => ['edit', 'delete', 'export']
|
},
|
// 删除确认信息
|
deleteConfirmText: {
|
type: String,
|
default: '确认删除该记录?'
|
}
|
})
|
// 检查列是否需要显示tooltip
|
const shouldShowTooltip = (col, data) => {
|
// 如果没有prop,直接返回false
|
if (!col.prop) return false;
|
// 检查该列在所有数据中是否有非空值
|
return data.some(row => row[col.prop] != null && row[col.prop] !== '');
|
};
|
|
// 处理单元格编辑
|
const handleCellEdit = (row, prop) => {
|
// 如果不允许编辑单元格,直接返回
|
if (!props.editableCells) return;
|
|
// 如果指定了可编辑列,且当前列不在可编辑列中,则不允许编辑
|
if (props.editableColumns.length > 0 && !props.editableColumns.includes(prop)) return;
|
|
// 初始化editing对象
|
if (!row.editing) {
|
row.editing = {};
|
}
|
// 设置当前单元格为编辑状态
|
row.editing[prop] = true;
|
|
// 在下一个DOM更新周期,让输入框获得焦点并选中内容
|
setTimeout(() => {
|
const inputElement = document.querySelector('.cell-edit .el-input__inner');
|
if (inputElement) {
|
inputElement.focus();
|
inputElement.select();
|
}
|
}, 10);
|
}
|
|
// 处理单元格保存
|
const handleCellSave = (row, prop) => {
|
// 关闭编辑状态
|
row.editing[prop] = false;
|
// 触发单元格编辑完成事件
|
emit('cell-edit', row, prop, row[prop]);
|
}
|
// 处理单元格聚焦事件
|
const handleCellFocus = (row, prop, event) => {
|
// 如果不允许编辑单元格,直接返回
|
if (!props.editableCells) return;
|
|
// 如果指定了可编辑列,且当前列不在可编辑列中,则不允许编辑
|
if (props.editableColumns.length > 0 && !props.editableColumns.includes(prop)) return;
|
|
// 初始化editing对象
|
if (!row.editing) {
|
row.editing = {};
|
}
|
// 设置当前单元格为编辑状态
|
row.editing[prop] = true;
|
|
// 自动全选输入框内容
|
if (event && event.target) {
|
event.target.select();
|
}
|
}
|
// 判断列是否可编辑
|
const isColumnEditable = (prop) => {
|
if (props.editableColumns.length === 0) {
|
return props.editableCells;
|
}
|
return props.editableColumns.includes(prop);
|
}
|
|
// 处理选择变化、编辑、删除和导出操作
|
const emit = defineEmits(['selection-change', 'edit', 'delete', 'export', 'cell-edit'])
|
const handleSelectionChange = (selection) => {
|
emit('selection-change', selection)
|
}
|
const handleEdit = (row) => {
|
emit('edit', row)
|
}
|
const handleExport = (row) => {
|
emit('export', row)
|
}
|
</script>
|
<style scoped lang="scss">
|
.el-table {
|
margin: 20px 0 !important;
|
}
|
|
:deep(.el-table th) {
|
background-color: #f5f7fa;
|
}
|
|
:deep(.cell-edit) {
|
width: 100%;
|
height: 100%;
|
position: relative;
|
}
|
|
:deep(.cell-edit .cell-text) {
|
width: 100%;
|
display: block;
|
}
|
|
:deep(.cell-edit.editable:hover .cell-text) {
|
color: #409EFF;
|
cursor: pointer;
|
}
|
|
:deep(.cell-input) {
|
width: 80%;
|
max-width: 120px;
|
min-width: 60px;
|
}
|
|
:deep(.cell-input .el-input__inner) {
|
border-radius: 4px;
|
text-align: center;
|
transition: all 0.2s;
|
}
|
|
|
/* 响应式样式 */
|
@media screen and (max-width: 768px) {
|
:deep(.el-table) {
|
width: 100%;
|
overflow-x: auto;
|
}
|
}
|
</style>
|