首先是vue代码
<template>
//这个是表格,后面的js方法会往这个div里面插入表格
<div class="box-container" >
<div ref="tableContainer" class="lsb-table-box">
<div class="table-container" id="lsb-table">
</div>
</div>
</div>
//这个是弹窗
<div class="tanchuang move-win1"
style="width: 160px;height: 110px"
id="tanchuang1">
<el-button style="margin-left: 12px" data-action="above">在上方插入一行</el-button>
<el-button style="margin-top: 5px" data-action="below">在下方插入一行</el-button>
<el-button style="margin-top: 5px" data-action="remove">删除当前行</el-button>
</div>
</template>
然后是js方法
/**
* 渲染表格
*/
function tableRenderCs() {
const data=[{
"one": "测试1",
"two": "测试2",
"three": "测试3",
"four": "测试4",
"five": "测试5",
"six": "测试6",
"seven": "测试7",
"eight": "测试8",
"nine": "测试9"
},{
"one": "测试1",
"two": "测试2",
"three": "测试3",
"four": "测试4",
"five": "测试5",
"six": "测试6",
"seven": "测试7",
"eight": "测试8",
"nine": "测试9"
},{
"one": "测试1",
"two": "测试2",
"three": "测试3",
"four": "测试4",
"five": "测试5",
"six": "测试6",
"seven": "测试7",
"eight": "测试8",
"nine": "测试9"
}]
let html = '<table class="table-wj JZ-A" border="1" cellspacing="0">' +
'<thead>' +
'<tr>' +
'<th rowspan="2" colspan="3">第一列</th>' +
'<th rowspan="2" colspan="1" style="width: 50px;">第二列</th>' +
'<th rowspan="2" colspan="1" style="width: 100px;">第三列</th>' +
'<th rowspan="2" colspan="1" style="width: 50px;">第四列</th>' +
'<th rowspan="2" colspan="1" style="width: 80px;">第五列</th>' +
'<th rowspan="1" colspan="3">第六列</th>' +
'</tr>' +
'<tr>' +
'<th colspan="1" style="width: 50px;">第六列的第一列</th>' +
'<th colspan="1" style="width: 90px;">第六列的第二列</th>' +
'</tr>' +
'</thead>' +
'<tbody>';
for (let i = 0; i < data.length; i++) {
const item = data[i];
html +=
`<tr>` +
`<td>${item.one}</td>` +
`<td>${item.two}</td>` +
`<td>${item.three}</td>` +
`<td>${item.four}</td>` +
`<td>${item.five}</td>` +
`<td>${item.six}</td>` +
`<td>${item.seven}</td>` +
`<td>${item.eight}</td>` +
`<td>${item.nine}</td>` +
`</tr>`;
}
html += '</tbody></table>';
let _$ = $(".lsb-table-box .table-container");
_$.append(html);
//存储列名对应的字段值,方便后面计算
let rowName = {
'0': 'one',
'1': 'two',
'2': 'three',
'3': 'four',
'4': 'five',
'5': 'six',
'6': 'seven',
'7': 'eight',
'8': 'nine',
}
editInput(_$, data, "id", rowName); //编辑单元格
addBindAction(24, data, rowName) //为按钮绑定点击事件
}
const numCols这里给出了两种合并表格的逻辑,第一个是只会对前三列执行合并逻辑,而第二个会对所有列执行合并逻辑。下面是两种逻辑的合并效果图。
接下来是完整的js方法,实现了点击编辑单元格内容,新增单元行,删除单元行,以及还原被合并的单元格。
之所以要还原单元格是为了获取每一行的数据,而被合并的单元格会获取到空数据
let currentRow = null;
/**
* 设置单元格可编辑
* @param _$ 表对象
* @param data 表数据
* @param fieldName 编辑后要获取的值对应的列名
* @param rowName 列的索引与数据库字段相对应的集合
*/
export function editInput(_$, data, fieldName, rowName) {
// 获取所有表格单元格
let cells = _$.find('td');
// 为每个单元格添加点击事件
cells.each(function (index) {
let column = $(this).index();
let row = $(this).closest('tr').index();
let columnName = Object.keys(data[0])[column]; // 获取对应列的字段名
$(this).data('columnName', columnName); // 存储列名为数据属性
//添加双击击事件
$(this).on('dblclick', function (event) {
// 如果是鼠标右键点击,不进行操作
if (event.which === 3) return;
let $input = $(this).find('input'); // 检查单元格内是否已有输入框
if ($input.length === 0) { // 如果没有输入框,则进行以下操作
let currentValue = $(this).text();
// 创建一个输入框元素,并将当前单元格内容设置为其值
$input = $('<input type="text">');
$input.val(currentValue);
// 设置输入框的样式
$input.css({
'width': '100%', // 设置宽度为100%,以占满<td>的宽度
'height': '90px', // 设置高度为100%,以占满<td>的高度
'box-sizing': 'border-box', // 确保边框和填充不会增加元素的尺寸
'padding': '0', // 移除内边距
'margin': '0', // 移除外边距
'background-color': 'green', // 设置背景色为透明
'color': 'white', // 设置文字颜色为白色
'border': 'none', // 移除边框
'outline': 'none', // 移除外边框
});
// 清空单元格并将输入框添加到单元格中
$(this).empty().append($input);
// 焦点定位到输入框
$input.focus();
// 阻止默认行为
event.preventDefault();
// 处理输入框失去焦点事件
$input.on('blur', function () {
let newValue = $input.val();
let valueName = rowName[column]; // 获取列索引对应的字段名称
let columnValue = data[row][fieldName]; // 获取对应字段的值
$(this).parent('td').text(newValue);
console.log(`更改了${fieldName}为${columnValue}的字段${valueName}的值为${newValue}`)
let updateData = {};
updateData[valueName] = newValue; // 创建对象,属性名为 valueName,值为 newValue
restoreTable()
mergeColumns()
});
}
});
// 右键点击事件
$(this).on('contextmenu', function(event) {
event.preventDefault(); // 阻止默认的右键菜单
// 获取当前行的引用
currentRow =$(this).closest('tr');
// 调用右键点击事件的方法
showContextMenu(event);
});
});
}
//显示弹窗
function showContextMenu(event) {
let tanchuang = document.getElementById('tanchuang1');
// 设置弹窗的位置
tanchuang.style.display = 'block';
tanchuang.style.left = event.pageX + 'px';
tanchuang.style.top = event.pageY + 'px';
document.addEventListener('click', function(event) {
if (event.target !== tanchuang && !tanchuang.contains(event.target)) {
tanchuang.style.display = 'none'; // 隐藏弹窗
}
});
}
export function addBindAction(rows, data, rowName){
// 为按钮绑定点击事件
$(document).ready(function() {
$(document).on('click', '[data-action]', function() {
// 获取按钮的动作
let action = $(this).data('action');
// 处理行操作
handleRowAction(action, currentRow, rows, data, rowName);
});
});
}
// 定义插入新行的方法
function handleRowAction(action, $currentRow, rows, data, rowName) {
restoreTable()
// 创建新的行
let newRow = $('<tr>').append(
$('<td style="height: 90px;"></td>')
);
// 使用for循环添加剩余的单元格
for (let i = 0; i < rows; i++) {
newRow.append($('<td style="height: 90px;"></td>'));
}
bindCellEvents(newRow);
if (action === 'above') {
$currentRow.before(newRow);
} else if (action === 'below') {
$currentRow.after(newRow);
} else if (action === 'remove') {
$currentRow.remove();
}
let _$ = $(".lsb-table-box .table-container");
editInput(_$, data, "id", rowName);
mergeColumns()
}
// 新插入行绑定点击事件
function bindCellEvents($row) {
//添加双击击事件
//右键点击事件
$row.find('td').on('contextmenu', function(event) {
event.preventDefault(); // 阻止默认的右键菜单
// 获取当前行的引用
currentRow = $(this).closest('tr');
// 调用右键点击事件的方法
showContextMenu(event);
});
}
// 获取所有行内容的函数
export function getAllRowContents(tableId, type) {
let table = restoreTable();
// let table = document.getElementById(tableId);
let rows = table.rows; // 获取所有行
let list = []; // 创建一个空列表来存储行数据
for (let i = 0; i < rows.length; i++) {
let row = rows[i]; // 获取行
let rowData = []; // 创建一个空数组来存储当前行的数据
// 遍历行中的所有单元格
for (let j = 0; j < row.cells.length; j++) {
let cell = row.cells[j];
let cellValue = cell.textContent || cell.innerText; // 获取单元格的内容
rowData.push(cellValue); // 将单元格内容添加到行数据数组中
}
// 将行数据添加到列表中
list.push(rowData);
}
console.log(list)
let data = {
lsbList: list,
stationId: localStorage.getItem('stationId'),
type: type,
}
saveLcJlLSBList(data).then(res => {
if (res.code === 200){
ElMessage.success('保存成功')
}else {
ElMessage.error('保存失败')
}
})
mergeColumns();
}
/**
* 合并方法只有前三列执行合并逻辑
*/
export function mergeColumns() {
let $table = $('table.table-wj');
let $rows = $table.find('tbody tr');
const numCols = Math.min(3, $rows.eq(0).find('td').length); // 仅考虑前三列
//const numCols = $rows.eq(0).find('td').length; // 考虑所有列
// 遍历每列
for (let col = 1; col <= numCols; col++) {
let $currentColumn = $table.find(`td:nth-child(${col})`);
let prevContent = null;
let rowspan = 1;
for (let i = 0; i < $currentColumn.length; i++) {
let $currentCell = $($currentColumn[i]);
let currentContent = $currentCell.text();
if (currentContent === prevContent) {
rowspan++;
$currentCell.addClass('hidden');
} else {
if (rowspan > 1) {
$currentColumn.eq(i - rowspan).attr('rowspan', rowspan);
}
prevContent = currentContent;
rowspan = 1;
}
}
if (rowspan > 1) {
$currentColumn.eq($currentColumn.length - rowspan).attr('rowspan', rowspan);
}
}
// 清除被隐藏的单元格
$table.find('.hidden').remove();
}
//还原被合并的单元格
export function restoreTable() {
// 获取到包含表格的foreignObject元素
const foreignObject = document.getElementById('publicSvg');
if (!foreignObject){
return '';
}
// 获取到表格元素
let oldTable = foreignObject.querySelector('table');
if (!oldTable){
return '';
}
// 复制表格元素
// let newTable = oldTable.cloneNode(true);
let newTable = oldTable
// 将新表格添加到页面中
// foreignObject.appendChild(newTable);
//以下方法将合并后的表格还原为未合并的状态
let rows = newTable.rows;
for (let i = 2; i < rows.length; i++) {
let cells = rows[i].cells;
for (let j = 0; j < cells.length; j++) {
let cell = cells[j];
let rowspan = cell.rowSpan;
let colspan = cell.colSpan;
cell.setAttribute('data-original-rowspan', rowspan);
cell.setAttribute('data-original-colspan', colspan);
// 如果单元格有 rowspan 或 colspan
if (rowspan > 1 || colspan > 1) {
// 记录单元格的内容
let cellContent = cell.innerHTML;
// 还原 rowspan
for (let r = 0; r < rowspan; r++) {
for (let c = 0; c < colspan; c++) {
if (r !== 0 || c !== 0) {
// 创建一个新的单元格元素,保持原有的类型
let newCell = document.createElement(cell.tagName.toLowerCase());
newCell.innerHTML = cellContent;
newCell.setAttribute('data-remove', true);
// 在新位置插入单元格
let targetRow = rows[i + r];
if (targetRow) { // 确保目标行存在
targetRow.insertBefore(newCell, targetRow.cells[j + c]);
}
}
}
}
// 重置原单元格的 rowspan 和 colspan
cell.rowSpan = 1;
cell.colSpan = 1;
}
}
}
// 检查表格元素是否存在
if (newTable) {
// 删除表格元素
// foreignObject.removeChild(newTable);
}
return newTable;
}
实现效果如下,同时还获取了当前单元格所在行里面指定的某一列的数据内容(比如可以获取当前行的id,以此来给后端修改数据库中的数据),注意行是从表头下面开始的,行和列的下标都是从0开始