handsontable实现新增删除行(双行)

news/2025/10/29 16:59:32/文章来源:https://www.cnblogs.com/MrLie/p/19174911

// 配置方法

const tableSettings = computed(() => {
   return {
      ...hotTableParams,
      nestedHeaders: false,
      filters: false,
      columnSorting: false,
      height: 358,
      rowHeaders: false,
      colHeaders: [
         '企业名称',
         '类型',
         '1月',
         '2月',
         '3月',
         '4月',
         '5月',
         '6月',
         '7月',
         '8月',
         '9月',
         '10月',
         '11月',
         '12月',
         '操作',
      ],
      columns: [
         {
            data: 'name',
            className: 'htCenter htMiddle',
            readOnly: false, // 企业名称固定只读
         },
         {
            data: 'type',
            className: 'htCenter htMiddle',
            readOnly: true, // 类型(电量/电价)固定只读
         },
         // 月份列(可编辑)
         { data: 'one', type: 'numeric', className: 'htRight' },
         { data: 'two', type: 'numeric', className: 'htRight' },
         { data: 'three', type: 'numeric', className: 'htRight' },
         { data: 'four', type: 'numeric', className: 'htRight' },
         { data: 'five', type: 'numeric', className: 'htRight' },
         { data: 'six', type: 'numeric', className: 'htRight' },
         { data: 'seven', type: 'numeric', className: 'htRight' },
         { data: 'eight', type: 'numeric', className: 'htRight' },
         { data: 'nine', type: 'numeric', className: 'htRight' },
         { data: 'ten', type: 'numeric', className: 'htRight' },
         { data: 'eleven', type: 'numeric', className: 'htRight' },
         { data: 'twelve', type: 'numeric', className: 'htRight' },
         // 操作列(自定义渲染+合并)
         {
            renderer: (instance, td, row) => {
               td.innerHTML = '';
               if(props.isEdit) return td;
               if(row === 0 || row === 1) return td;
               if(row % 2 === 0) {
                  const container = document.createElement('div');
                  container.style.width = '100%';
                  container.style.height = '100%';
                  container.style.display = 'flex';
                  container.style.alignItems = 'center';
                  container.style.justifyContent = 'center';
                  container.style.gap = '8px';

                  // 新增按钮
                  const addBtn = document.createElement('span');
                  addBtn.className = 'mdi mdi-plus';
                  addBtn.style.fontSize = '18px';
                  addBtn.style.width = '18px';
                  addBtn.style.height = '18px';
                  addBtn.style.cursor = 'pointer';
                  addBtn.style.display = 'flex';
                  addBtn.style.alignItems = 'center';
                  addBtn.style.justifyContent = 'center';

                  addBtn.onclick = (e) => {
                     e.stopPropagation();
                     const hot = instance;
                     const currentRow = row;

                     hot.alter('insert_row_below', currentRow + 1, 2);

                     hot.setDataAtCell(currentRow + 2, 0, '');
                     hot.setDataAtCell(currentRow + 2, 1, '电量');
                     hot.setDataAtCell(currentRow + 3, 0, '');
                     hot.setDataAtCell(currentRow + 3, 1, '电价');

                     const currentMergedCells = [
                        ...hot.getPlugin('mergeCells').mergedCellsCollection
                           .mergedCells,
                     ];
                     currentMergedCells.push(
                        { row: currentRow + 2, col: 0, rowspan: 2, colspan: 1 },
                        { row: currentRow + 2, col: 14, rowspan: 2, colspan: 1 },
                     );
                     hot.updateSettings({ mergeCells: currentMergedCells });
                  };

                  // 删除按钮
                  const delBtn = document.createElement('span');
                  delBtn.className = 'mdi mdi-trash-can-outline';
                  delBtn.style.fontSize = '18px';
                  delBtn.style.width = '18px';
                  delBtn.style.height = '18px';
                  delBtn.style.cursor = 'pointer';
                  delBtn.style.display = 'flex';
                  delBtn.style.alignItems = 'center';
                  delBtn.style.justifyContent = 'center';

                  delBtn.onclick = (e) => {
                     e.stopPropagation();
                     const hot = instance;
                     if(hot.countRows() <= 4) return;

                     const currentRow = row;
                     const rowsToRemove = 2;

                     // 1. 获取原始合并规则并深拷贝
                     const mergeCellsPlugin = hot.getPlugin('mergeCells');
                     const originalMerged = JSON.parse(
                        JSON.stringify(
                           mergeCellsPlugin.mergedCellsCollection.mergedCells,
                        ),
                     );

                     // 2. 删除2行
                     hot.alter('remove_row', currentRow, rowsToRemove);

                     // 3. 处理合并规则:过滤重复 + 位移行索引
                     const updatedMerged = originalMerged
                        .filter(
                           (cell) =>
                              !(
                                 cell.row >= currentRow &&
                                 cell.row < currentRow + rowsToRemove
                              ),
                        ) // 过滤已删除行的规则
                        .map((cell) => {
                           if(cell.row > currentRow) {
                              return { ...cell, row: cell.row - rowsToRemove }; // 后续规则行索引减2
                           }

                           return cell;
                        })
                        // 去重:确保每个(row, col)组合唯一
                        .filter((cell, index, self) => {
                           return (
                              self.findIndex(
                                 (c) => c.row === cell.row && c.col === cell.col,
                              ) === index
                           );
                        });

                     // 4. 重新设置合并规则
                     hot.updateSettings({ mergeCells: updatedMerged });
                  };

                  container.appendChild(addBtn);
                  container.appendChild(delBtn);
                  td.appendChild(container);
               }
               else {
                  td.style.visibility = 'visible';
                  td.style.pointerEvents = 'none';
               }

               return td;
            },
         },
      ],
      dropdownMenu: false,
      cells: (row, col) => {
         // 合计行(0、1行)所有单元格只读
         if(row === 0 || row === 1 || col === 14) {
            return { readOnly: true };
         }

         // 企业名称列(0列)和类型列(1列)只读
         if(
            ((col === 0 || col === 1) && (row === 0 || row === 1)) ||
            col === 1 ||
            col === 14
         ) {
            return { readOnly: true };
         }

         return { readOnly: props.isEdit };
      },
      mergeCells: true,
      async afterChange(changes) {
         if(!changes || changes.length === 0) return;

         const hot = this; // Handsontable实例
         const data = hot.getSourceData(); // 表格原始数据

         // 定义月份字段映射(与columns中“1月-12月”的data属性对应)
         const monthFields = [
            'one',
            'two',
            'three',
            'four',
            'five',
            'six',
            'seven',
            'eight',
            'nine',
            'ten',
            'eleven',
            'twelve',
         ];

         // 遍历每个月份,分别计算“电量合计”和“电价合计”
         monthFields.forEach((field) => {
            let elecSum = 0; // 电量合计
            let priceSum = 0; // 电价合计

            // 遍历所有企业行(跳过前2行“合计行”)
            for(let i = 2; i < data.length; i += 2) {
               // 电量行(类型为“电量”)
               const elecRow = data[i];
               elecSum += Number(elecRow[field] || 0); // 确保转为数字

               // 电价行(类型为“电价”)
               const priceRow = data[i + 1];
               priceSum += Number(priceRow[field] || 0); // 确保转为数字
            }

            // 更新“合计行”的对应月份值
            data[0][field] = elecSum; // 电量合计行(第0行)
            data[1][field] = priceSum; // 电价合计行(第1行)
         });

         let totalElecSum = 0; // 总电量合计(所有月份电量合计的总和)
         let weightedPriceSum = 0; // 加权电价合计

         // 1. 计算总电量(合计行的电量总和)
         monthFields.forEach((field) => {
            totalElecSum += Number(data[0]?.[field] || 0); // 用可选链操作符?.避免data[0]为undefined
         });

         // 2. 遍历所有企业行,计算加权电价
         for(let i = 2; i < data.length; i += 2) { // 企业行是“电量行+电价行”,每次跳2行
            monthFields.forEach((field) => {
               const enterpriseElec = Number(data[i]?.[field] || 0); // 企业当月电量(防undefined)
               const enterprisePrice = Number(data[i + 1]?.[field] || 0); // 企业当月电价(防undefined)

               if(totalElecSum > 0) {
                  const weight = enterpriseElec / totalElecSum; // 企业电量占总电量的权重
                  weightedPriceSum += enterprisePrice * weight; // 累加该企业的加权电价
               }
            });
         }

         // 更新总合计值
         totalElec.value = totalElecSum;
         totalPrice.value = weightedPriceSum.toFixed(2);

         // 重新渲染表格(确保合计值显示)
         hot.loadData(data);
      },
   };
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/950069.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年国产角接触球轴承厂家推荐 一文了解轴承厂家选择标准

角接触球轴承在机械设备中的应用广泛,特别是精密机床、高速电机、电主轴等运行要求高的场合,更需要质量品质好一点的角接触轴承。想要轴承用的好,就得找到合适的生产厂家,下面就来推荐下2025年值得信任的国产角接触…

vxe-table 树形表格显示连接线的方式

vxe-table 树形表格显示连接线的方式 完整连接线 通过 tree-config.showLine 来启用是否显示连接线<template><div><vxe-grid v-bind="gridOptions"></vxe-grid></div> </…

2025年上海衣帽间定制机构权威推荐榜单:衣帽间设计/衣帽间十大品牌/衣帽间装修源头公司精选

在上海,一个规划合理的衣帽间正成为新兴住宅的标配。数据显示,2024年中国家装行业市场规模已突破860亿元,其中定制家具份额持续增长,而衣帽间作为定制家具的重要组成部分,正受到越来越多消费者的青睐。 01 行业趋…

在Web应用开发中状态到底是什么?

在计算机科学中,“状态”(State)这个词经常出现在讨论有状态(Stateful)和无状态(Stateless)系统、服务或组件时。要理解“状态”到底是什么,我们可以从最基本的层面来解释。一、什么是“状态”? 简单来说,“…

前后端不分离的springboot应用,静态文件修改了不更新的问题

当然,还有不依赖idea的解决方案,就是静态文件通过nginx来代理,直接将js和css这些文件代理到我们的代码目录,这样我们修改了代码目录后,配置就立马生效了。这样可以不依赖idea的版本,假如你的idea怎么设置热更新都…

Cookie与缓存的区别

一、本质定义 Cookie:客户端轻量化文本存储,存用户身份、网站偏好等会话相关数据,容量约4KB。 缓存:客户端/服务器临时存储,存网页静态资源(图、JS/CSS),容量几MB到几十GB。 二、3大核心区别 1. 存储内容:Coo…

2025 年铝卷厂家最新推荐榜,聚焦企业技术实力与市场口碑深度解析铝板铝卷/铝卷板/橘皮铝卷/压花铝卷/防锈铝卷/花纹铝卷公司推荐

引言 本次 2025 年铝卷产品推荐榜,由有色金属工业协会联合行业权威检测机构共同测评制定,测评过程严格遵循《铝及铝合金轧制卷材行业质量评价标准》。测评团队从企业生产实力、产品品质、服务能力三大维度入手,涵盖…

无人机航测界的强者——Pix4Dmapper 4.5.6使用教程+图文步骤

软件介绍 Pix4Dmapper 4.5.6是一款专业的无人机航空三维建模软件。它可以将通过无人机拍摄的照片转化为三维地图、模型、点云、高精度的数字高程模型(DEM)、数字表面模型(DSM)和正射影像(DOM)等。该软件具有自动…

qml与html通信

1. 在qml显示html并通讯main.qmlimport QtQuick 2.12 import QtQuick.Window 2.12 import QtWebEngine 1.2 import QtQuick.Controls Window {id:mainWindowwidth: 640height: 480visible: truetitle: qsTr("WebE…

2025 年排烟风机厂家最新推荐榜,技术实力与市场口碑深度解析,筛选高性能低噪音优质企业屋顶/双速/离心式/防排烟风机公司推荐

引言 为助力建筑行业精准选购排烟风机,本次榜单由暖通空调工业协会联合消防设备质量监督检验中心共同测评发布。测评采用 “三维九项” 评估体系,从技术实力(耐高温性能、噪音控制、节能效率)、市场口碑(客户满意…

2025 年建筑模型公司最新推荐榜,技术实力与市场口碑深度解析含沙盘、微缩、高端模型品牌

引言 随着建筑行业数字化升级,建筑模型需求向高精度、智能化、定制化加速迭代,据建筑装饰协会 2025 年行业测评报告显示,超 68% 的高端项目对模型精度要求达 0.1mm 级,且 82% 的客户将 “技术创新能力” 列为选择合…

Session、Cookie、Token 区别

一、本质理解 1. Cookie:就是浏览器里的“小记事本”,只能存少量东西(约4KB),比如记个“身份编号”,本身不是身份,只是个装信息的小本子。 2. Session:相当于服务器里的“用户档案柜”。你登录后,服务器给你编…

2025 年聚脲厂家最新推荐榜,技术实力与市场口碑深度解析,精选行业优质企业聚脲防腐/单组分双组分聚脲/MUL 聚脲/聚脲防水公司推荐

引言 在材料保护领域,聚脲产品因卓越性能需求持续攀升,为精准筛选优质企业,建筑材料联合会防腐保温材料分会联合行业权威机构开展 2025 年聚脲企业测评。测评涵盖生产规模(年产能、厂房面积)、技术实力(专利数量…

Flask零基础入门:5步搭建你的第一个Web应用

本文详细介绍了Flask框架的入门使用方法,包括环境搭建、路由配置、模板渲染和数据库集成等核心内容,通过完整代码示例帮助读者快速掌握Web开发基础。你想快速搭建一个Web应用,却总被复杂框架吓退?😫 惊人事实:F…

2025 年红外测温仪厂家最新推荐榜,技术实力与市场口碑深度解析比色/感应加热/高性价比/单晶炉红外测温仪公司推荐

引言 在工业制造、光伏能源、半导体等核心领域的智能化升级进程中,红外测温仪作为关键检测设备,其精度与稳定性直接决定生产安全与产品合格率。据中国仪器仪表行业协会 2025 年《工业测温设备专项测评报告》显示,国…

2025 年真空计厂家最新推荐榜,技术实力与市场口碑深度解析,涵盖压阻硅、薄膜硅等多类型产品皮拉尼真空计/单晶炉真空计公司推荐

引言 在半导体、光伏等高端制造领域,真空计作为核心测量设备,直接影响生产精度与效率。据仪器仪表行业协会 2025 年报告显示,国内真空计市场规模已达 86.3 亿元,国产化率提升至 45%,但市场产品良莠不齐,37% 的企…

2025年10月企业网站建设开发公司排行榜:前十名精选

摘要 随着数字化转型加速,2025年企业网站建设行业持续增长,中小企业对高效、可靠的建站服务需求激增。本文基于市场调研和用户口碑,整理出排名前十的企业网站建设服务商列表,并提供详细比较和选择指南,帮助企业主…

2025年企业网站建设开发公司口碑排行榜Top 10

摘要 企业网站建设行业在2025年持续蓬勃发展,随着数字化转型加速,中小企业对高效、可靠的建站服务需求激增。行业趋势显示,集成AI技术、移动端优化和全渠道营销成为主流。本文基于市场调研和用户反馈,整理了2025年…

基于四元数的航天器自适应滑模控制(ASMC)设计

一、系统建模与问题描述 1. 航天器动力学模型(四元数描述)\(q=[q0,q1,q2,q3]^T\):单位四元数(姿态描述) \(ω\):角速度误差(本体坐标系) \(d\):总干扰(含转动惯量不确定性和外界扰动)2. 控制目标跟踪期望四…

浅记线性同余方程(组)

线性同余方程 定义 线性同余方程就是形如 \(ax\equiv b\pmod m\) 其中 \(a,b,m\) 是给定的整数。 解法 由同余的性质可知 \(m\mid ax-b\) 即 \(ax-b=km\) 其中 \(k\in \mathbb{Z}\)。 如果我们设 \(k=-y\) 的话,就有 …