基于Canvas+React的高性能Table表格

        本人之前擅长客户端GDI+,疫情期间偶然了解到Web前端的Canvas画布。突发奇想,遂一发不可收拾,于是就有了这么个产物--基于Canvas+React的高性能Table表格,可咸可甜

        语言:JavaScript、TypeScript、ES6语法;

        框架:React;

        技术:Canvas画布;

        性能:以装载并渲染18列、N行为标准,压测有以下性能指标(附,测试硬盘状态不是太好,08年的老盘)。

1如需自适应列宽,30万行(30万,耗时24S)
2若无需自适应列宽,可达百万级(30万,耗时5S)
3自适应列宽模式,14500行,耗时1.5S
4无自适应列宽,14500行,耗时忽略不计

        特色:高性能、渲染流畅、操控顺滑、高扩展性(自定义派生列、单元格)、装载灵活、ES6语法、全程面向对象(class)、样式全部属性化(颜色、字体、尺寸、高度……抛弃繁杂的CSS样式表,降低入手门槛、维护复杂度);

目前已经支持的功能:

1、序号列、颜色(矩形、圆形)、进度、复选框、复选框集、按钮集、金额线、普通单元格;

2、右键显示隐藏列;

3、拖拽列索引顺序;

4、拖拽列宽;

5、自适应列宽;

6、数据结构(列表结构、树结构);

7、自定义行、单元格背景色、前景色;

8、自定义选中行、单元格的背景色;

9、焦点行背景色;

10、展开、折叠父子行;

11、表格数据排序;

12、表格内数据查找、定位;

13、数据选择模式(单选、多选--Shift辅助、鼠标滑动拖拽);

14、快捷键:↑(向上选择单元格或行)、↓(向下选择单元格或行)、←(向左选择单元格)、→(向右选择单元格)、PageUp(上翻一个显示范围)、PageDown(下翻一个显示范围)、Home(定位首行)、End(定位末行)、Ctrl+C(复制选中内容)、Ctrl+F(表格内数据查找、定位);

15、表格数据打印(目前针对页边距,还有点小瑕疵,需要手工调整一次页边距。大数据打印,尚且存有性能缺陷);

16、表格数据导出(时间紧迫,目前仅支持Excel);

17、多表头(支持表头合并);

18、单元格合并;

19、表头数据筛选、过滤(类似Excel数据筛选);

20、表格内容动态编辑(目前仅支持文本内容编辑,后续补充支持下拉框、日期框、数字框……);

21、表内,行数据拖拽交换(同级交换、父子级交换);

22、表外,行数据拖拽交换(不同表格之间,拖拽移除、或插入、或交换);

23、自定义右键菜单;

24、行定位(定位可视范围外、未渲染区域的选中行,并主动更新滚动条,使目标行处于可视范围内);

25、丰富的事件行为,总有一款适合您。单元格单击事件、行单击事件、行选择改变事件、表格勾选事件(行、列、单元格)、表格失焦事件、右键上下文选项单击事件……;

列表结构:

树结构:

右键菜单:

显示、隐藏列:

 列头数据筛选:

 

案例一:绑定Column、Row、Cell对象

import React, { useEffect, useState } from 'react';
import ColumnCollection from '@/EniacComponents/Table/Collections/ColumnCollection';
import RowCollection from '@/EniacComponents/Table/Collections/RowCollection';
import TableCore, { IFieldData } from '@/EniacComponents/Table/TableCore';
import Table from '@/EniacComponents/Table/Index';

const PageTableBindColumns = () => {
  const [columns, setColumns] = useState(new ColumnCollection());
  const [rows, setRows] = useState(new RowCollection());

  const BuildColumns = () => {
    let loColumns = columns;
    if (!loColumns || loColumns.Count === 0) {
      let loFieldList: IFieldData[] = [
        { Name: 'colSequenceColumn', Text: '', Type: 'SequenceColumn' },
        { Name: 'colCheckBoxColumn', Text: '', Type: 'CheckBoxColumn' },
        { Name: 'name', Text: '姓名', Type: 'Column' },
        { Name: 'age', Text: '年龄', Type: 'Column' },
        { Name: 'sex', Text: '性别', Type: 'Column' },
        { Name: 'items', Text: '功能清单', Type: 'CheckBoxListColumn' },
        { Name: 'address', Text: '地址', Type: 'Column' },
        { Name: 'assets_b', Text: '资产-本币', Type: 'AmountColumn' },
        { Name: 'assets_y', Text: '资产-原币', Type: 'AmountColumn' },
        { Name: 'controls', Text: '操作列', Type: 'ButtonColumn' },
      ];

      loColumns = new ColumnCollection();
      for (let liIndex = 0; liIndex < loFieldList.length; liIndex++) {
        let loCulumnData = loFieldList[liIndex];
        let loColumn = TableCore.CreateColumn(loCulumnData);
        loColumns.Add(loColumn);
      }

      setColumns(loColumns);
    }

    return loColumns;
  };

  const BuildRows = (poColumns: ColumnCollection) => {
    if (!rows || rows.length === 0) {
      let loDataList = [
        {
          name: '辛弃疾',
          age: 37,
          items: '喝酒,true;文臣,true;武将,true',
          sex: '男',
          address: '南宋',
          assets_b: 56832.215,
          assets_y: 56832.215,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '苏洵',
          age: 62,
          items: '喝酒,true;文臣,true;武将,false',
          sex: '男',
          address: '北宋',
          assets_b: 65912.32,
          assets_y: 65912.32,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '苏轼',
          age: 46,
          items: '喝酒,true;文臣,true;武将,true',
          sex: '男',
          address: '北宋',
          assets_b: 92615.68,
          assets_y: 92615.68,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '苏辙',
          age: 43,
          items: '喝酒,true;文臣,true;武将,true',
          sex: '男',
          address: '北宋',
          assets_b: 4689.36,
          assets_y: 4689.36,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '欧阳修',
          age: 78,
          items: '喝酒,true;文臣,true;武将,false',
          sex: '男',
          address: '北宋',
          assets_b: 9956592.36,
          assets_y: 9956592.36,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '王维',
          age: 66,
          items: '喝酒,true;文臣,true;武将,false',
          sex: '男',
          address: '大唐',
          assets_b: 6588.66,
          assets_y: 6588.66,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '杜甫',
          age: 48,
          items: '喝酒,true;文臣,true;武将,false',
          sex: '男',
          address: '大唐',
          assets_b: 6523698.99,
          assets_y: 6523698.99,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '李白',
          age: 63,
          items: '喝酒,true;文臣,true;武将,true',
          sex: '男',
          address: '大唐',
          assets_b: 98217365.922,
          assets_y: 98217365.922,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '李清照',
          age: 53,
          items: '喝酒,true;文臣,true;武将,true',
          sex: '女',
          address: '大宋',
          assets_b: 16591.52,
          assets_y: 16591.52,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '施耐庵',
          age: 56,
          items: '喝酒,true;文臣,true;武将,true',
          sex: '男',
          address: '大明',
          assets_b: 32336.12,
          assets_y: 32336.12,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '曹雪芹',
          age: 36,
          items: '喝酒,true;文臣,true;武将,true',
          sex: '男',
          address: '大清',
          assets_b: 615292.77,
          assets_y: 615292.77,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
        {
          name: '罗贯中',
          age: 68,
          items: '喝酒,true;文臣,true;武将,true',
          sex: '男',
          address: '元末明初',
          assets_b: 6668214.53,
          assets_y: 6668214.53,
          controls: '新增;修改;复制;审核;反审核;删除',
        },
      ];

      let loRows = new RowCollection();
      loDataList.forEach((loRowData: object) => {
        let loRow = TableCore.CreateRow(poColumns, loRowData);
        if (loRow) {
          loRows.Add(loRow);
        }
      });

      setRows(loRows);
    }
  };

  const BuildTableData = () => {
    let loColumns = BuildColumns();
    BuildRows(loColumns);
  };

  useEffect(() => {
    BuildTableData();
  }, [columns, rows]);

  return (
    <Table
      Width={'100%'}
      Height={'100%'}
      Properties={{
        Draggable: true,
        ShowCheckBoxColumn: true,
        ShowSequenceColumn: true,
        ContextMenuVisible: true,
        ColumnHeight: 40,
      }}
      DataSource={null}
      Items={{ Columns: columns, Rows: rows }}
    ></Table>
  );
};

export default PageTableBindColumns;

案例二:绑定数据源、事件的使用

import React, { useEffect, useState } from 'react';
import Table from '@/EniacComponents/Table/Index';
import ToolStripView from '../Components/ToolStrip/ToolStripView';
import { IFieldData } from '@/EniacComponents/Table/TableCore';

const PageTableBindFields = () => {
  const [fieldList, setFieldList] = useState([] as IFieldData[]);
  const [dataList, setDataList] = useState([] as object[]);

  const BuildFieldList = () => {
    if (!fieldList || fieldList.length === 0) {
      let loFieldList: IFieldData[] = [
        { Name: 'name', Text: '姓名', Type: 'Column' },
        { Name: 'age', Text: '年龄', Type: 'Column' },
        { Name: 'items', Text: '功能清单', Type: 'CheckBoxListColumn' },
        { Name: 'sex', Text: '性别', Type: 'Column' },
        { Name: 'address', Text: '地址', Type: 'Column' },
      ];

      setFieldList(loFieldList);
    }
  };

  const BuildDataList = () => {
    if (!dataList || dataList.length === 0) {
      let loDataList: any[] = [
        { name: '辛弃疾', age: 37, items: '新增;修改;复制;删除;', sex: '男', address: '南宋' },
        { name: '苏洵', age: 62, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '苏轼', age: 46, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '苏辙', age: 43, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '欧阳修', age: 78, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '王维', age: 66, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '杜甫', age: 48, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '李白', age: 63, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '李清照', age: 53, items: '新增;修改;复制;删除;', sex: '女', address: '大宋' },
        { name: '施耐庵', age: 56, items: '新增;修改;复制;删除;', sex: '男', address: '大明' },
        { name: '曹雪芹', age: 36, items: '新增;修改;复制;删除;', sex: '男', address: '大清' },
        { name: '罗贯中', age: 68, items: '新增;修改;复制;删除;', sex: '男', address: '元末明初' },
        { name: '辛弃疾', age: 37, items: '新增;修改;复制;删除;', sex: '男', address: '南宋' },
        { name: '苏洵', age: 62, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '苏轼', age: 46, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '苏辙', age: 43, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '欧阳修', age: 78, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '王维', age: 66, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '杜甫', age: 48, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '李白', age: 63, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '李清照', age: 53, items: '新增;修改;复制;删除;', sex: '女', address: '大宋' },
        { name: '施耐庵', age: 56, items: '新增;修改;复制;删除;', sex: '男', address: '大明' },
        { name: '曹雪芹', age: 36, items: '新增;修改;复制;删除;', sex: '男', address: '大清' },
        { name: '罗贯中', age: 68, items: '新增;修改;复制;删除;', sex: '男', address: '元末明初' },
        { name: '辛弃疾', age: 37, items: '新增;修改;复制;删除;', sex: '男', address: '南宋' },
        { name: '苏洵', age: 62, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '苏轼', age: 46, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '苏辙', age: 43, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '欧阳修', age: 78, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '王维', age: 66, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '杜甫', age: 48, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '李白', age: 63, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '李清照', age: 53, items: '新增;修改;复制;删除;', sex: '女', address: '大宋' },
        { name: '施耐庵', age: 56, items: '新增;修改;复制;删除;', sex: '男', address: '大明' },
        { name: '曹雪芹', age: 36, items: '新增;修改;复制;删除;', sex: '男', address: '大清' },
        { name: '罗贯中', age: 68, items: '新增;修改;复制;删除;', sex: '男', address: '元末明初' },
        { name: '辛弃疾', age: 37, items: '新增;修改;复制;删除;', sex: '男', address: '南宋' },
        { name: '苏洵', age: 62, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '苏轼', age: 46, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '苏辙', age: 43, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '欧阳修', age: 78, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '王维', age: 66, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '杜甫', age: 48, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '李白', age: 63, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '李清照', age: 53, items: '新增;修改;复制;删除;', sex: '女', address: '大宋' },
        { name: '施耐庵', age: 56, items: '新增;修改;复制;删除;', sex: '男', address: '大明' },
        { name: '曹雪芹', age: 36, items: '新增;修改;复制;删除;', sex: '男', address: '大清' },
        { name: '罗贯中', age: 68, items: '新增;修改;复制;删除;', sex: '男', address: '元末明初' },
        { name: '辛弃疾', age: 37, items: '新增;修改;复制;删除;', sex: '男', address: '南宋' },
        { name: '苏洵', age: 62, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '苏轼', age: 46, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '苏辙', age: 43, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '欧阳修', age: 78, items: '新增;修改;复制;删除;', sex: '男', address: '北宋' },
        { name: '王维', age: 66, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '杜甫', age: 48, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '李白', age: 63, items: '新增;修改;复制;删除;', sex: '男', address: '大唐' },
        { name: '李清照', age: 53, items: '新增;修改;复制;删除;', sex: '女', address: '大宋' },
        { name: '施耐庵', age: 56, items: '新增;修改;复制;删除;', sex: '男', address: '大明' },
        { name: '曹雪芹', age: 36, items: '新增;修改;复制;删除;', sex: '男', address: '大清' },
        { name: '罗贯中', age: 68, items: '新增;修改;复制;删除;', sex: '男', address: '元末明初' },
      ];

      setDataList(loDataList);
    }
  };

  useEffect(() => {
    BuildFieldList();
    BuildDataList();
  }, [fieldList, dataList]);

  const table_CellClick = (e: any) => {
    console.log('table_CellClick', e.Cell.Text);
  };

  const table_RowClick = (e: any) => {
    console.log('table_RowClick', e.Row);
  };

  const table_SelectionChanged = (e: any) => {
    console.log('table_SelectionChanged', e.Row);
  };

  const table_CheckStateChanged = (e: any) => {
    if (e.Cell) {
      console.log('勾选单元格', e.Cell);
    } else if (e.Row) {
      console.log('勾选行', e.Row);
    } else if (e.Column) {
      console.log('勾选列', e.Column);
    }
  };

  const table_ContextItemClick = (e: any) => {
    console.log('table_ContextMenuItemClick', e.Item);
  };

  const table_GotFocus = (e: any) => {
    console.log('table_GotFocus', '获得焦点');
  };

  const table_LostFocus = (e: any) => {
    console.log('table_LostFocus', '失去焦点');
  };

  return (
    <div style={{ width: '100%', height: '100%' }}>
      <div>
        <ToolStripView ItemStyle={'ImageBeforeText'}></ToolStripView>
      </div>
      <div style={{ width: '100%', height: 'calc(100% - 43px)' }}>
        <Table
          Width={'100%'}
          Height={'100%'}
          Properties={{
            AllowDragRow: true,
            ShowCheckBoxColumn: true,
            ShowSequenceColumn: true,
            ContextMenuVisible: true,
            CustomContextItems: [
              { Name: 'CustomItem1', Text: '自定义选项1号' },
              { Name: 'CustomItem2', Text: '自定义选项2号' },
            ],
          }}
          DataSource={{ FieldList: fieldList, DataList: dataList }}
          Items={null}
          OnCellClick={table_CellClick}
          OnRowClick={table_RowClick}
          OnSelectionChanged={table_SelectionChanged}
          OnCheckStateChanged={table_CheckStateChanged}
          OnContextItemClick={table_ContextItemClick}
          OnGotFocus={table_GotFocus}
          OnLostFocus={table_LostFocus}
        ></Table>
      </div>
    </div>
  );
};

export default PageTableBindFields;

本人对GDI+、Canvas相当痴迷,并小有成就。欢迎咨询、洽谈,相互学习、互相进步。 

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值