某财税系统高性能表格实践总结

关系型数据库数据安全图像处理

‍本文来自 @visactor/VTable 用户投稿,该用户获得VisActor奖励的精美礼品一份!


背景

我们项目采用了 AngularJS 作为主要的技术框架,团队自行开发的表格列表及扩展功能,存在性能问题,而且扩展能力差,难以实现丰富表格功能。

我们调研各种开源解决方案,希望解决如下问题:

  1. 高性能,期望在十万数据级别,能够快速渲染,交互无卡顿,无白屏。
  2. 扩展能力强,在单元格渲染上支持一定程度的自定义。同时提供默认可用的丰富可视化效果
  3. 支持多维数据分析与展示
  4. 可以很方便的同目前技术框架(AngularJS)融合。

同时希望新表格的应用能做到如下开发体验:

  1. 功能一致性: 替换后的表格与原表格功能基本一致。
  2. 交互体验: 交互功能和使用体验得到显著提升。
VTable 调研

我们调研了多个开源表格库,从实现方式上整体分为两类:

  1. 基于dom的表格。优点是自定义能力强,可以基于dom节点做灵活的自定义渲染和扩展;缺点是大数据渲染场景下,性能劣化明显。
  2. 基于Canvas的表格。优点是具有非常高的渲染性能;缺点是扩展能力普遍较弱,需要使用原生Canvas 绘图api进行自定义渲染。

为彻底解决我们的性能问题,我们决定采用Canvas 表格,在为数不多的Canvas表格中,我们最终选择了VTable,原因如下:

  1. 超高的性能表现,支持百万级数据的渲染与流程交互。

picture.image

  1. 较好的扩展能力,支持JSX自定义单元格;React-VTable 支持自定义React组件的接入(https://www.visactor.io/vtable/guide/Developer\_Ecology/react-custom-component)
  2. 和VChart 完美融合,支持在一张画布上渲染成百上千个图表,极大提升可视化能力及渲染性能(单元格显示图表)

另外VTable虽然是开源项目,但是提供了多种沟通渠道,可以直接和开发者进行需求沟通,快速响应,确保我们项目的上线周期。

功能实现

由于VTable默认功能比较完善,整个升级过程中基本没有技术上的障碍,下面将我们开发过程中自定义的几个点,简单总结一下。

全局基础配置

项目中使用表格的场景很多,但是基本风格一致,我们定义了一份全局配置,用来规定项目整体表格的一个基础风格,每个列表可以单独写个性化的配置,通过复制基础配置信息产生一个新的配置。

基础配置大致如下:


        
          
vtableOption = {  
    columns: [],  
    records: [],  
    frozenColCount: 5, //冻结列数  
    //rightFrozenColCount: 右侧冻结列数,默认为 0。  
    //bottomFrozenRowCount: 底部冻结行,默认为 0。  
    //autoWrapText: boolean:全局配置是否允许自动换行,默认值为false。如果设置为true,则当前列的单元格会根据其内容自动换行。  
    showFrozenIcon: false, // 是否显示固定列图标  
    widthMode: 'standard', //表格列宽度的计算模式,可以是 'standard'(标准模式)、'adaptive'(自适应容器宽度模式)或 'autoWidth'(自动宽度模式),默认为 'standard'。  
    dragHeaderMode: 'none', //拖拽表头换位置  'all' 所有表头均可换位 'none' 不可换位 'column' 只有换列表头可换位 'row' 只有换行表头可换位  
    // heightMode: 'autoHeight', //'standard'(标准模式)、'adaptive'(自适应容器高度模式)或 'autoHeight'(自动行高模式),默认为 'standard'。  
    // autoWrapText: true, //是否自动换行 heightMode需要设置为autoHeight  
    emptyTip: {  
        text: '数据为空,请重新设置搜索条件',  
        icon: {  
            width: 205,  
            height: 113,  
            image: '../assets/images/Group.png',  
        },  
    },  
    tooltip: {  
        //省略提示  
        isShowOverflowTextTooltip: true,  
    },  
    keyboardOptions: {  
        copySelected: true,  
    },  
    hover: {  
        highlightMode: 'cross', //四种hover交互模式,分别为:十字交叉('cross')、整('column')、整行('row')和单个单元格('cell')  
    },  
    menu: {  
        contextMenuItems: ['复制'],  
    },  
    theme: VTable.themes.DEFAULT.extends({  
        headerStyle: {  
            fontSize: 14,  
            color: '#666',  
            fontWeight: 'normal',  
        },  
        bodyStyle: {  
            color: '#666',  
        },  
        scrollStyle: {  
            visible: 'always',  
        },  
        checkboxStyle: {  
            size: 18,  
        },  
    }),  
    select: {  
        disableSelect: true,  
    },  
}  

      

下拉 菜单自定义

VTable默认提供了下拉菜单。

picture.image

但是个人感觉不够灵活,得益于VTable提供了灵活的扩展能力,于是自己写了一个自定义下拉菜单。

首先自定义下拉菜单的icon,核心代码如下:


        
          
{  
    field: 'dropdown\_menu',  
    title: '操作',  
    width: 60,  
    disableHeaderSelect: true,  
    disableSelect: true,  
    disableColumnResize: true,  
    customLayout: (args) => {  
        const { table, row, col, rect } = args  
        var _rect  
        var _ref = (_rect = rect) !== null && _rect !== void 0 ? _rect : table.getCellRect(col, row)  
        const { height, width } = _ref  
        const container = new VTable.CustomLayout.Group({  
            height,  
            width,  
            display: 'flex',  
            flexDirection: 'row',  
            flexWrap: 'nowrap',  
            alignItems: 'center',  
            justifyContent: 'center',  
        })  
        const containerCenter = new VTable.CustomLayout.Group({  
            height: 20,  
            width: 20,  
            display: 'flex',  
            alignItems: 'center',  
            justifyContent: 'center',  
        })  
        container.add(containerCenter)  
        const bloggerName = new VTable.CustomLayout.Image({  
            height: 16,  
            width: 16,  
            image: '../../../../assets/images/dropdown.png',  
        })  
        containerCenter.add(bloggerName)  
        return {  
            rootContainer: container,  
        }  
    },  
}  

      

上面代码中,图标是通过自定义渲染customLayout方式实现的(好像通过VTable本身icon能力实现会比较简单)。

最后通过点击单元格,获取单元格位置的方式,将组件定位到相应位置,进而展现整个菜单,菜单是用html实现的,这里就不列详细代码了。


        
          
tableInstance.on('click\_cell', (args) => {  
    const { col, row, field } = args  
    let currentRowData = tableInstance.getRecordByCell(col, row) //获取当前行数据  
                   if (currentRowData && field === 'dropdown\_menu') {  
        let rect = tableInstance.getCellRelativeRect(col, row)  
        //获取单元格坐标,展示下拉菜单  
        // 显示 自定义的菜单  
})  

      

最终效果如下:

picture.image

右键复制单元格信息功能

右键复制单元格信息功能,与官网上demo实现的功能有所区别(https://www.visactor.io/vtable/demo/interaction/context-menu)。

官网demo没有禁用单元格选中,需要选中单元格才能复制单元格内的信息,好处是可以拖拽选中多个单元格进行复制,不灵活的地方是,单个单元格也必须要先单击选中,才能右键复制内容。我的项目禁用了单元格选中功能,通过监听右键点击事件,直接进行单元格内容的复制,不能拖拽选中多个单元格进行复制。应该可以有兼容两种方式的处理方式,个人觉得处理交互太麻烦了,就暂时简单粗暴的处理了。

基本思路是通过VTable实例获取要复制的数据。

let copyData = tableInstance.getCellValue(col, row)

然后将数据写入剪贴板,如果浏览器支持clipboard接口,直接写入:


        
          
if (navigator.clipboard && window.isSecureContext) {  
              // navigator clipboard 向剪贴板写文本  
              return navigator.clipboard.writeText(copyData)  
          }  

      

如果不支持,则通过创建隐藏textArea的方式,将内容先写入textArea,


        
          
 let textArea = document.createElement('textarea')  
              textArea.value = copyData  

      

然后通过 copy 命令写入剪贴板。


        
          
 textArea.focus()  
              textArea.select()  
              return new Promise((res, rej) => {  
                  // 执行复制命令并移除文本框  
                  document.execCommand('copy') ? res() : rej()  
                  textArea.remove()  
              })  

      

完整代码如下:


        
          
  tableInstance.on('dropdown\_menu\_click', (args) => {  
      const { col, row } = args  
      if (args.menuKey === '复制') {  
          let copyData = tableInstance.getCellValue(col, row)  
          if (navigator.clipboard && window.isSecureContext) {  
              // navigator clipboard 向剪贴板写文本  
              return navigator.clipboard.writeText(copyData)  
          } else {  
              // 创建text area  
              let textArea = document.createElement('textarea')  
              textArea.value = copyData  
              // 使text area不在viewport,同时设置不可见  
              textArea.style.position = 'absolute'  
              textArea.style.opacity = 0  
              textArea.style.left = '-999999px'  
              textArea.style.top = '-999999px'  
              document.body.appendChild(textArea)  
              textArea.focus()  
              textArea.select()  
              return new Promise((res, rej) => {  
                  // 执行复制命令并移除文本框  
                  document.execCommand('copy') ? res() : rej()  
                  textArea.remove()  
              })  
          }  
      }  
  })  

      
最终效果

最终使用VTable 还原了原有表格功能:

基本表格样式:

picture.image

下拉菜单:

picture.image

同时性能得到了极大提升:

picture.image

欢迎交流

欢迎更多使用VisActor的用户联系我们,给我们投稿,交流业务场景,提建议,贡献代码,谢谢大家!

官方网站:www.visactor.io/

Discord:discord.gg/3wPyxVyH6m

Twiter:twitter.com/xuanhun1

github:github.com/VisActor

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论