双击可触发input框模式,可修改当前单元格内容,点击其他地方失去焦点input状态隐藏
修改过的行背景色变红,点击保存则取消背景色
单页面所有代码假数据测试看效果版:
<template>
<div class="custom-tree-table">
<el-button style="margin: 2px 0;" size="mini" type="primary" @click="onAdd">+ 新增</el-button>
<el-button style="margin: 2px 0 0 10px;" size="mini" type="success" @click="onBatchSave">保存</el-button>
<el-button style="margin: 2px 0 0 10px;" size="mini" type="danger" @click="onBatchDelete" class="el-icon-delete"> 批量删除</el-button>
<el-table
:data="currentPageData"
row-key="id"
border
default-expand-all
height="508"
:row-style="rowStyle"
@selection-change="onSelectionChange">
<!-- 复选框 -->
<el-table-column width="62" align="center" type="selection" label="选择" />
<!-- 参数ID -->
<el-table-column label="参数ID" prop="key" min-width="250">
<template #default="{ row }">
<el-input
:value="row.key"
@click="onEdit(row, 'key')"
@blur="onBlur(row, 'key')"
v-if="row.isEditKey || row.key === ''"
v-model="row.key"
placeholder="请输入"/>
<span v-else @click="onEdit(row, 'key')">{{ row.key }}</span>
</template>
</el-table-column>
<!-- 参数名称 -->
<el-table-column label="参数名称" prop="type" min-width="200">
<template #default="{ row }">
<el-input
:value="row.type"
@click="onEdit(row, 'type')"
@blur="onBlur(row, 'type')"
v-if="row.isEditType || row.type === ''"
v-model="row.type"
placeholder="请输入"/>
<span v-else @click="onEdit(row, 'type')">{{ row.type }}</span>
</template>
</el-table-column>
<!-- 参数描述 -->
<el-table-column label="参数描述" prop="description" min-width="220">
<template #default="{ row }">
<el-input
:value="row.description"
@click="onEdit(row, 'description')"
@blur="onBlur(row, 'description')"
v-if="row.isEditDescription || row.description === ''"
v-model="row.description"
placeholder="请输入"/>
<span v-else @click="onEdit(row, 'description')">{{ row.description }}</span>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
:total="totalDataCount"
layout="total, sizes, prev, pager, next, jumper"/>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
tableData: [
{ id: '1', key: 'ufnak', type: '按钮', description: 'Name of the person', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '2', key: 'fbunkjn', type: '按钮2', description: 'Age of the person', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '3', key: 'fain', type: '安牛3', description: 'Active status', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '4', key: 'key4', type: 'Type 4', description: 'Description 4', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '5', key: 'key5', type: 'Type 5', description: 'Description 5', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '6', key: 'key6', type: 'Type 6', description: 'Description 6', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '7', key: 'key7', type: 'Type 7', description: 'Description 7', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '8', key: 'key8', type: 'Type 8', description: 'Description 8', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '9', key: 'key9', type: 'Type 9', description: 'Description 9', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '10', key: 'key10', type: 'Type 10', description: 'Description 10', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '11', key: 'key11', type: 'Type 11', description: 'Description 11', isEditKey: false, isEditType: false, isEditDescription: false },
{ id: '12', key: 'key12', type: 'Type 12', description: 'Description 12', isEditKey: false, isEditType: false, isEditDescription: false },
],
selectedRows: [], // 复选框
modifiedRows: [], // 已经被修改的行的id/背景色
// 分页
currentPage: 1, // 前往当前页
pageSize: 10, // 每页显示多少条
originalValue: '', //一个变量用于记录编辑前的值
}
},
computed: {
totalDataCount() { // 计算总数据量
return this.tableData.length;
},
currentPageData() { // 基于分页计算当前页面数据
const startIndex = (this.currentPage - 1) * this.pageSize;
const endIndex = startIndex + this.pageSize;
return this.tableData.slice(startIndex, endIndex);
},
},
methods: {
// +新增
onAdd() {
const id = String(Date.now());
const newRecord = { id, key: '', type: '', description: '', isEditKey: true, isEditType: true, isEditDescription: true };
this.tableData.unshift(newRecord);
},
// 批量删除
onBatchDelete() {
if (this.selectedRows.length > 0) {
this.tableData = this.tableData.filter(item => !this.selectedRows.some(row => row.id === item.id));
this.selectedRows = []; // 清空选中的行
}
},
onSelectionChange(selection) { // 复选框选择变更时触发,批量删除
this.selectedRows = selection;
},
// 保存
onBatchSave() {
// 收集表格中所有数据
const allData = this.tableData;
// 发送数据给后端
axios.post('/your-backend-endpoint', allData)
.then(response => {
// 处理后端返回的响应
console.log(response.data);
})
.catch(error => {
// 处理错误
console.error('Error:', error);
});
// 清空已修改的行数组
this.modifiedRows = [];
},
// 点击单元格进入编辑状态
onEdit(row, field) {
// 在进入编辑状态时记录编辑前的值
this.originalValue = row[field];
if (field === 'key') {
row.isEditKey = true; // 参数ID
row.isEditType = false; // 参数名称
row.isEditDescription = false; // 参数描述
} else if (field === 'type') {
row.isEditKey = false;
row.isEditType = true;
row.isEditDescription = false;
} else if (field === 'description') {
row.isEditKey = false;
row.isEditType = false;
row.isEditDescription = true;
}
},
// 根据编辑状态设置行样式
rowStyle({ row }) {
return { backgroundColor: this.modifiedRows.includes(row.id) ? '#ffcccc' : '' };
},
// 失去焦点时触发,隐藏输入框
onBlur(row, field) {
if (this.originalValue !== row[field]) {
// 只有在值发生变化时才将当前行的 ID 添加到 modifiedRows 数组中
if (!this.modifiedRows.includes(row.id)) {
this.modifiedRows.push(row.id);
}
}
//
if (field === 'key') {
row.isEditKey = false;
} else if (field === 'type') {
row.isEditType = false;
} else if (field === 'description') {
row.isEditDescription = false;
}
// 如果有已修改的行,并且当前行不在已修改的行中,则隐藏已修改的行的编辑状态
this.tableData.forEach(item => {
if (this.modifiedRows.includes(item.id) && item.id !== row.id) {
item.isEditKey = false;
item.isEditType = false;
item.isEditDescription = false;
}
});
},
// 分页每页多少条
handleSizeChange(val) {
this.pageSize = val;
},
// 前往当前页
handleCurrentChange(val) {
this.currentPage = val;
},
}
}
</script>
<style scoped>
.custom-tree-table {
height: 100%;
background-color: #fff;
padding: 15px;
}
/*新增按钮宽高*/
.el-button--mini{
padding: 7px 12px;
}
/*表格每行高度*/
.custom-tree-table /deep/ .el-table__cell {
padding: 8px;
}
/* input高度 */
::v-deep .el-input__inner{
height: 26px
}
/* 分页器位置 */
::v-deep .el-pagination{
display: flex;
justify-content: flex-end;
}
/* 设置按钮颜色 */
.el-button--primary {
background-color: #7184FF;
border-color: #7184FF;
color: #fff;
}
.el-button--success {
background-color: #7184FF;
border-color: #7184FF;
color: #fff;
}
.el-button--danger {
background-color: #7184FF;
border-color: #7184FF;
color: #fff;
}
</style>
单页面代码数据改成接口渲染联动版:
<template>
<div class="custom-tree-table">
<el-button style="margin: 2px 0;" size="mini" type="primary" @click="onAdd">+ 新增</el-button>
<el-button style="margin: 2px 0 0 10px;" size="mini" type="success" @click="onBatchSave">保存</el-button>
<el-button style="margin: 2px 0 0 10px;" size="mini" type="danger" @click="onBatchDelete" class="el-icon-delete"> 批量删除</el-button>
<el-table
:data="currentPageData"
row-key="id"
border
default-expand-all
height="508"
:row-style="rowStyle"
@selection-change="onSelectionChange">
<!-- 复选框 -->
<el-table-column width="62" align="center" type="selection" label="选择" />
<!-- 参数ID -->
<el-table-column label="参数ID" prop="key" min-width="250">
<template #default="{ row }">
<el-input
:value="row.key"
@click="onEdit(row, 'key')"
@blur="onBlur(row, 'key')"
v-if="row.isEditKey || row.key === ''"
v-model="row.key"
placeholder="请输入"/>
<span v-else @click="onEdit(row, 'key')">{{ row.key }}</span>
</template>
</el-table-column>
<!-- 参数名称 -->
<el-table-column label="参数名称" prop="type" min-width="200">
<template #default="{ row }">
<el-input
:value="row.type"
@click="onEdit(row, 'type')"
@blur="onBlur(row, 'type')"
v-if="row.isEditType || row.type === ''"
v-model="row.type"
placeholder="请输入"/>
<span v-else @click="onEdit(row, 'type')">{{ row.type }}</span>
</template>
</el-table-column>
<!-- 参数描述 -->
<el-table-column label="参数描述" prop="description" min-width="220">
<template #default="{ row }">
<el-input
:value="row.description"
@click="onEdit(row, 'description')"
@blur="onBlur(row, 'description')"
v-if="row.isEditDescription || row.description === ''"
v-model="row.description"
placeholder="请输入"/>
<span v-else @click="onEdit(row, 'description')">{{ row.description }}</span>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
:total="totalDataCount"
layout="total, sizes, prev, pager, next, jumper"/>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
tableData: [],
selectedRows: [], // 复选框
modifiedRows: [], // 已经被修改的行的id/背景色
// 分页
currentPage: 1, // 前往当前页
pageSize: 10, // 每页显示多少条
originalValue: '', //一个变量用于记录编辑前的值
}
},
computed: {
totalDataCount() { // 计算总数据量
return this.tableData.length;
},
currentPageData() { // 基于分页计算当前页面数据
const startIndex = (this.currentPage - 1) * this.pageSize;
const endIndex = startIndex + this.pageSize;
return this.tableData.slice(startIndex, endIndex);
},
},
created() {
this.fetchData();
},
methods: {
// 获取数据后端数据表格
async fetchData() {
try {
// 调用后端获取数据的接口
const response = await axios.get('/api/getData');
// 将返回的数据赋值给tableData
this.tableData = response.data;
} catch (error) {
console.error('Error fetching data:', error);
}
},
// +新增
async onAdd() {
try {
// 调用后端新增数据的接口
const response = await axios.post('/api/addData', { key: '', type: '', description: '', isEditKey: true, isEditType: true, isEditDescription: true});
// 将返回的新数据插入到表格的最前面
const newRecord = response.data;
this.tableData.unshift(newRecord);
} catch (error) {
console.error('新增数据时发生错误:', error);
}
},
// 批量删除
async onBatchDelete() {
if (this.selectedRows.length > 0) {
try {
// 获取选中行的id数组
const selectedIds = this.selectedRows.map(row => row.id);
// 调用后端删除数据的接口
await axios.delete('/api/deleteData', { data: { ids: selectedIds } });
// 从tableData中移除选中的行
this.tableData = this.tableData.filter(item => !selectedIds.includes(item.id));
// 清空选中的行
this.selectedRows = [];
} catch (error) {
console.error('批量删除时发生错误:', error);
}
}
},
onSelectionChange(selection) { // 复选框选择变更时触发,批量删除
this.selectedRows = selection;
},
// 保存
async onBatchSave() {
try {
// 收集表格中所有数据
const allData = this.tableData;
// 发送数据给后端
const response = await axios.post('/your-backend-endpoint', allData);
// 处理后端返回的响应
console.log(response.data);
// 清空已修改的行数组
this.modifiedRows = [];
} catch (error) {
// 处理错误
console.error('保存时发生错误:', error);
}
},
// 点击单元格进入编辑状态
onEdit(row, field) {
// 在进入编辑状态时记录编辑前的值
this.originalValue = row[field];
if (field === 'key') {
row.isEditKey = true; // 参数ID
row.isEditType = false; // 参数名称
row.isEditDescription = false; // 参数描述
} else if (field === 'type') {
row.isEditKey = false;
row.isEditType = true;
row.isEditDescription = false;
} else if (field === 'description') {
row.isEditKey = false;
row.isEditType = false;
row.isEditDescription = true;
}
},
// 根据编辑状态设置行样式
rowStyle({ row }) {
return { backgroundColor: this.modifiedRows.includes(row.id) ? '#ffcccc' : '' };
},
// 失去焦点时触发,隐藏输入框
onBlur(row, field) {
if (this.originalValue !== row[field]) {
// 只有在值发生变化时才将当前行的 ID 添加到 modifiedRows 数组中
if (!this.modifiedRows.includes(row.id)) {
this.modifiedRows.push(row.id);
}
}
//
if (field === 'key') {
row.isEditKey = false;
} else if (field === 'type') {
row.isEditType = false;
} else if (field === 'description') {
row.isEditDescription = false;
}
// 如果有已修改的行,并且当前行不在已修改的行中,则隐藏已修改的行的编辑状态
this.tableData.forEach(item => {
if (this.modifiedRows.includes(item.id) && item.id !== row.id) {
item.isEditKey = false;
item.isEditType = false;
item.isEditDescription = false;
}
});
},
// 分页每页多少条
handleSizeChange(val) {
this.pageSize = val;
},
// 前往当前页
handleCurrentChange(val) {
this.currentPage = val;
},
}
}
</script>
<style scoped>
.custom-tree-table {
height: 100%;
background-color: #fff;
padding: 15px;
}
/*新增按钮宽高*/
.el-button--mini{
padding: 7px 12px;
}
/*表格每行高度*/
.custom-tree-table /deep/ .el-table__cell {
padding: 8px;
}
/* input高度 */
::v-deep .el-input__inner{
height: 26px
}
/* 分页器位置 */
::v-deep .el-pagination{
display: flex;
justify-content: flex-end;
}
/* 设置按钮颜色 */
.el-button--primary {
background-color: #7184FF;
border-color: #7184FF;
color: #fff;
}
.el-button--success {
background-color: #7184FF;
border-color: #7184FF;
color: #fff;
}
.el-button--danger {
background-color: #7184FF;
border-color: #7184FF;
color: #fff;
}
</style>