Handsontable合并单元格案例

npm install handsontable @handsontable/react
import Handsontable from 'handsontable';
import 'handsontable/dist/handsontable.full.css';
import { registerLanguageDictionary, zhCN } from 'handsontable/i18n';
import React, { useEffect, useRef, useState } from 'react';
registerLanguageDictionary(zhCN);

const EditTable: React.FC = () => {
  const containerRef = useRef<HTMLDivElement>(null);
  const [tableData, setTableData] = useState<any>([
    [
      '厂内',
      '产品/应用',
      null,
      null,
      '软件',
      null,
      null,
      '算法',
      null,
      null,
      '应用工程',
      null,
      null,
      '机械',
      null,
      null,
      '研发总计\r\n(人天)',
      '应用总计\r\n(人天)',
      '产品+算法',
      '厂内\r\n总投入',
    ],
    [
      null,
      '人数',
      '时间',
      '小计\r\n(人天)',
      '人数',
      '时间',
      '小计\r\n(人天)',
      '人数',
      '时间',
      '小计\r\n(人天)',
      '人数',
      '时间',
      '小计\r\n(人天)',
      '人数',
      '时间',
      '小计\r\n(人天)',
      null,
      null,
      null,
      null,
    ],
    [
      '开发阶段',
      '1',
      '7',
      '7',
      '2',
      '10',
      '20',
      '0',
      '0',
      '0',
      '0',
      '0',
      '0',
      '0',
      '0',
      '0',
      '27',
      '0',
      '',
      '',
    ],
    [
      '厂内调试阶段',
      '',
      '',
      '0',
      '2',
      '15',
      '30',
      '',
      '',
      '0',
      '2',
      '35',
      '70',
      '',
      '',
      '0',
      '30',
      '70',
      '',
      '',
    ],
    ['合计', '', '', '7', '', '', '50', '', '', '0', '', '', '70', '', '', '0', '57', '70', '', ''],
    [
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      ' 57,000 ',
      ' 49,000 ',
      '',
      '106000',
    ],
    [
      '厂外',
      '产品',
      null,
      null,
      '软件',
      null,
      null,
      '算法',
      null,
      null,
      '应用工程',
      null,
      null,
      '机械设计',
      null,
      null,
      '研发总计\r\n(人天)',
      '应用总计\r\n(人天)',
      '产品+算法',
      '厂外\r\n总投入',
    ],
    [
      null,
      '人数',
      '时间',
      '小计\r\n(人天)',
      '人数',
      '时间',
      '小计\r\n(人天)',
      '人数',
      '时间',
      '小计\r\n(人天)',
      '人数',
      '时间',
      '小计\r\n(人天)',
      '人数',
      '时间',
      '小计\r\n(人天)',
      null,
      null,
      null,
      null,
    ],
    [
      'SETUP阶段',
      '1',
      '5',
      '5',
      '1',
      '15',
      '15',
      '0',
      '0',
      '0',
      '2',
      '35',
      '70',
      '0',
      '0',
      '0',
      '20',
      '70',
      '                                                                                                                                                                                                                     ',
      '',
    ],
    [
      '技术达标阶段',
      '1',
      '5',
      '5',
      '1',
      '40',
      '40',
      '0',
      '0',
      '0',
      '1',
      '45',
      '45',
      '0',
      '0',
      '0',
      '45',
      '45',
      '',
      '',
    ],
    [
      '验收阶段',
      '1',
      '0',
      '0',
      '1',
      '7',
      '7',
      '0',
      '0',
      '0',
      '1',
      '30',
      '30',
      '',
      '',
      '0',
      '7',
      '30',
      '',
      '',
    ],
    [
      '合计',
      '',
      '',
      '10',
      '',
      '',
      '62',
      '',
      '',
      '0',
      '',
      '',
      '145',
      '',
      '',
      '0',
      '72',
      '145',
      '',
      '',
    ],
    [
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      '',
      ' 72,000 ',
      ' 101,500 ',
      '0',
      '173500',
    ],
  ]);

  useEffect(() => {
    if (containerRef.current) {
      let hot = new Handsontable(containerRef.current, {
        data: tableData,
        rowHeaders: true,
        colHeaders: true,
        mergeCells: [
          {
            row: 0,
            col: 0,
            rowspan: 2,
            colspan: 1,
          },
          {
            row: 0,
            col: 4,
            rowspan: 1,
            colspan: 3,
          },
          {
            row: 0,
            col: 7,
            rowspan: 1,
            colspan: 3,
          },
          {
            row: 0,
            col: 10,
            rowspan: 1,
            colspan: 3,
          },
          {
            row: 0,
            col: 13,
            rowspan: 1,
            colspan: 3,
          },
          {
            row: 0,
            col: 16,
            rowspan: 2,
            colspan: 1,
          },
          {
            row: 0,
            col: 17,
            rowspan: 2,
            colspan: 1,
          },
          {
            row: 0,
            col: 18,
            rowspan: 2,
            colspan: 1,
          },
          {
            row: 0,
            col: 19,
            rowspan: 2,
            colspan: 1,
          },
          {
            row: 6,
            col: 0,
            rowspan: 2,
            colspan: 1,
          },
          {
            row: 6,
            col: 1,
            rowspan: 1,
            colspan: 3,
          },
          {
            row: 6,
            col: 4,
            rowspan: 1,
            colspan: 3,
          },
          {
            row: 6,
            col: 7,
            rowspan: 1,
            colspan: 3,
          },
          {
            row: 6,
            col: 10,
            rowspan: 1,
            colspan: 3,
          },
          {
            row: 6,
            col: 13,
            rowspan: 1,
            colspan: 3,
          },
          {
            row: 6,
            col: 16,
            rowspan: 2,
            colspan: 1,
          },
          {
            row: 6,
            col: 17,
            rowspan: 2,
            colspan: 1,
          },
          {
            row: 6,
            col: 18,
            rowspan: 2,
            colspan: 1,
          },
          {
            row: 6,
            col: 19,
            rowspan: 2,
            colspan: 1,
          },
          {
            row: 0,
            col: 1,
            rowspan: 1,
            colspan: 3,
          },
        ],
        height: 'auto',
        licenseKey: 'non-commercial-and-evaluation', // 非商业用途的免费license
        afterChange: (changes, source) => {
          if (source === 'edit') {
            const updatedData = hot.getData();
            console.log('Updated Data:', updatedData);
            setTableData(updatedData);
          }
        },
        afterMergeCells: function (mergedCellRange) {
          const mergedCells = this.getPlugin('mergeCells').mergedCellsCollection.mergedCells;
          console.log('Current Merged Cells:', mergedCells);
        },
        afterColumnResize: function (newSize, column) {
          console.log(`列 ${column} 的新宽度: ${newSize}px`);
          // 获取所有列宽
          const allColWidths = [];
          for (let i = 0; i < this.countCols(); i++) {
            allColWidths.push(this.getColWidth(i));
          }
          console.log('所有列宽:', allColWidths);
        },
        colWidths: [92, 50, 50, 62, 50, 50, 62, 50, 50, 62, 50, 50, 62, 50, 50, 62, 80, 75, 94, 61],
        manualColumnResize: true,
        manualRowResize: true,
        contextMenu: true,
        language: 'zh-CN', // 添加中文语言配置
        cells: function (row, col) {
          const className = 'htCenter htMiddle'; // 默认样式
          // 为特定行设置背景色
          if (row === 0 || row === 1 || row === 6 || row === 7) {
            return {
              className,
              renderer: function (instance, td) {
                Handsontable.renderers.TextRenderer.apply(this, arguments);
                td.style.backgroundColor = '#1890ff'; // 浅蓝色背景
                td.style.color = '#fff';
              },
            };
          }
          // 开发阶段-小计(人天)计算
          if (row === 2 && col === 3) {
            return {
              className,
              type: 'numeric',
              readOnly: true, // 禁止编辑
              renderer: function (instance, td) {
                // 计算前两行对应列的和
                const value =
                  Number(instance.getDataAtCell(2, 1)) * Number(instance.getDataAtCell(2, 2));
                // @ts-ignore
                td.textContent = value;
                td.style.backgroundColor = '#f5f5f5'; // 浅灰色背景
                td.style.textAlign = 'right'; // 浅灰色背景

                tableData[row][col] = value;
                setTableData([...tableData]);
              },
            };
          }
          // 厂内调试阶段-小计(人天)计算
          if (row === 3 && col === 3) {
            return {
              className,
              type: 'numeric',
              readOnly: true, // 禁止编辑
              renderer: function (instance, td) {
                // 计算前两行对应列的和
                const value =
                  Number(instance.getDataAtCell(3, 1)) * Number(instance.getDataAtCell(3, 2));
                // @ts-ignore
                td.textContent = value;
                td.style.backgroundColor = '#f5f5f5'; // 浅灰色背景
                td.style.textAlign = 'right'; // 浅灰色背景

                tableData[row][col] = value;
                setTableData([...tableData]);
              },
            };
          }
          // 合计-小计(人天)计算
          if (row === 4 && col === 3) {
            return {
              className,
              type: 'numeric',
              readOnly: true, // 禁止编辑
              renderer: function (instance, td) {
                // 计算前两行对应列的和
                const value =
                  Number(instance.getDataAtCell(2, 3)) + Number(instance.getDataAtCell(3, 3));
                // @ts-ignore
                td.textContent = value;
                td.style.backgroundColor = '#f5f5f5'; // 浅灰色背景
                td.style.textAlign = 'right'; // 浅灰色背景

                tableData[row][col] = value;
                setTableData([...tableData]);
              },
            };
          }
          return { className };
        },
      });
    }
  }, []);

  return <div ref={containerRef} />;
};

export default EditTable;
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容