封装标记📌前后数据比较的表格

需求背景:

日常开发中会碰到历史记录展示的弹窗,有时表格列比较多,数据前后基本变化不大,但这时找需要从大量表格数据中对比数据,有都有那些修改,比较费眼力了,如果标记📌出来前后变更的操作字段,将会省不少眼药水🐶。

实现思路:

基本思路:先对比获取哪条数据的那个字段需要被渲染,然后根据对比数据结果结果进行渲染。

1、 对比时应该永远标记最新修改的那一条数据:

历史记录排序可以分为正序和倒序,一般默认情况下第一条记录应该是最新一条即倒序排序,所以如果是第一条和第二条进行比较的话,默认我们应该把最新的记录给标记出来。这里以倒序为例;

数据决定页面渲染,遍历相邻的两条数据对它们每个展示字段进行比较,如果值不相同,存储值字段名,这时需要有一个地方存放值变化字段名,在这条记录对象上开辟一个新字段changeList(存放需要标记的字段名)

ts 复制代码
    datasource =[{
        operationType: 'add',
        operator: 'zhangsan',
        params1: 'BA',
        params2: 'BZ'
        ...
        _changeList: ['operator','params1']
    },{
        operationType: 'add',
        operator: 'lisi',
        params1:'CB',
        params2: 'BZ'
    }]
  1. 渲染记录的时候,就清楚知道哪条记录的哪些字段列需要标记📌了。
ts 复制代码
 const columns = [
   {
    title: 'Operation Type',
    dataIndex:'operationType'
   },
    {
     title: 'Operator',
     dataIndex:'operator'
   },
   {
    title: 'Params1',
    dataIndex:'params1'
   },
   {
    title: 'Params2',
    dataIndex:'params2'
   }
].map(columnItem=>{ 
   const {render,dataIndex} = columnItem
     
   return
       { ...columnItem,
       render: (_,record)=> {
           const { _changeList } = record
           return _changeList.includes(dataIndex) ?   <Typography.Text mark>
               {render()}
           </Typography.Text> : render()
       }}}

对比时包含引用类型比较

  • 考虑dataIndex 可能是数组的情况: dataIndex 除了是字符串还有可能是数组对应对象里的字段。这样_changeList 可能存储这样结果:[['company','name'],'operator','params1' ], 对于原生类型是赋值是值传递,而对于引用类型(数组、对象)则是地址传递;所以下面比较也不会有问题,但是操作时千万不要修改_changeList,因为它可能会影响columns dataIndex
  • value也可能是引用类型:比较的两个字段除了是原始类型以外,它们也可能是引用类型,通过render函数重新渲染。比较引用类型可以使用isEqual函数来判断。
ts 复制代码
const test = () => {
    const columns = [
       {
      title: '服务器',
      dataIndex: ['info', 'server'], // 嵌套对象使用数组路径
    }
    ];

    let list: (string | string[])[] = [];
    list.push(columns[0].dataIndex);
    console.log(list.includes(columns[0].dataIndex)); // true
};

考虑一下可以忽略的列: 对于一些创建日期的列,因为可能很大概率都不同,所以这种列没有必要标记📌出来。

完整实现

tsx 复制代码
import { Table, Typography } from 'antd'
import type { ColumnProps } from 'antd/es/table'
import dayjs from 'dayjs'
import { map, get, isArray, isEqual } from 'lodash'

const CompareTable = () => {
  const dataSource = [
    {
      id: '1',
      action: '部署服务',
      operator: '赵六',
      createTime: '2023-06-03T16:20:00Z', // 最新时间
      details: '部署了v2.3.0版本',
      status: 'success',
      department: '运维部',
      cost: 3200,
      approval: true,
      tags: ['发布', '生产环境'],
      info: {
        server: 'SI-3344',
        location: '深圳机房',
      },
    },
    ...
  ]

  const columns = [
    {
      title: 'ID',
      dataIndex: 'id',
    },
    {
      title: '操作类型',
      dataIndex: 'action',
    },
    {
      title: '操作人',
      dataIndex: 'operator',
    },
    {
      title: '操作时间',
      dataIndex: 'createTime',
      render: (time: string) => dayjs(time).format('YYYY-MM-DD HH:mm:ss'),
    },
    {
      title: '操作详情',
      dataIndex: 'details',
    },
    {
      title: '状态',
      dataIndex: 'status',
    },
    {
      title: '部门',
      dataIndex: 'department',
    },
    {
      title: '成本',
      dataIndex: 'cost',
    },
    {
      title: '审批状态',
      dataIndex: 'approval',
    },
    {
      title: '标签',
      dataIndex: 'tags',
      render: (ts) => {
        return Array.isArray(ts) ? ts.join(',') : '--'
      },
    },
    {
      title: '服务器',
      dataIndex: ['info', 'server'], // 嵌套对象使用数组路径
    },
    {
      title: '位置',
      dataIndex: ['info', 'location'], // 嵌套对象使用数组路径
    },
  ].map((item) => {
    const { dataIndex, render = (text) => text ?? '--' } = item

    return {
      ...item,
      render: (text, record) => {
        const { changeList } = record

        return changeList?.includes(dataIndex) ? (
          <Typography.Text mark>{render(text)}</Typography.Text>
        ) : (
          render(text) ?? text
        )
      },
    }
  })

  /**
   * @description 生成前后比较dataSource
   * @param config:{dataSource 数据源, columns: 数据列 , ignoreDataIndexList: 忽略数据列}
   * @returns 变更后的dataSource
   */
  const compareTable = (config: {
    dataSource: Record<string, any>[]
    columns: ColumnProps[]
    ignoreDataIndexList?: (string | string[])[]
  }) => {
    const { dataSource, columns, ignoreDataIndexList = [] } = config
    const columnList = map(columns, 'dataIndex')

    // 从下往上每行遍历最新值
    dataSource.reduceRight((nextItem, preItem) => {
      const changeList = []
      // 每行取每列比较
      columnList.forEach((dataIndex) => {
        // 跳过忽略列属性
        if (
          ignoreDataIndexList.some((item) =>
            // 比较引用类型可以使用isEqual
            isArray(dataIndex) ? isEqual(item, dataIndex) : item === dataIndex
          )
        ) {
          return
        }
        const nextValue = get(nextItem, dataIndex)
        const currentValue = get(preItem, dataIndex)
        if (
          typeof nextValue === 'object'
            ? !isEqual(nextValue, currentValue)
            : nextValue !== currentValue
        ) {
          changeList.push(dataIndex)
        }
      })
      preItem.changeList = changeList

      return preItem
    })

    return dataSource
  }

  compareTable({
    dataSource,
    columns,
    ignoreDataIndexList: ['createTime', 'id', ['info', 'server']],
  })

  return (
    <Table
      rowKey="id"
      dataSource={compareDataSource}
      columns={columns}
      bordered
      pagination={{ pageSize: 10 }}
      scroll={{ x: 'max-content' }}
    />
  )
}

export default CompareTable

上面实现实际会原地修改dataSource,如果后续对dataSource其他计算时需要注意,另外对compareTable调用,放到组件每次渲染时这里只是作为例子,现实中对compareTable调用只有依赖参数变化才会去调用。

如果你有其他好的想法欢迎评论和指正~

相关推荐
努力学习的小刘1 分钟前
如何使用react-router实现动态路由
前端·javascript
PasserbyX1 分钟前
JS原型链
前端·javascript
curdcv_po1 分钟前
你知道Cookie的弊端吗?
前端
curdcv_po3 分钟前
前端CSS高频面试题详解
前端
Danta7 分钟前
从0开始学习three.js(1)😁
前端·javascript·three.js
我的心巴7 分钟前
vue-print-nb 打印相关问题
前端·vue.js·elementui
coderYYY26 分钟前
element树结构el-tree,默认选中当前setCurrentKey无效
前端·javascript·vue.js
GISer_Jing36 分钟前
Three.js中AR实现详解并详细介绍基于图像标记模式AR生成的详细步骤
开发语言·javascript·ar
GISer_Jing1 小时前
[总结篇]个人网站
前端·javascript
ss.li1 小时前
TripGenie:畅游济南旅行规划助手:个人工作纪实(二十二)
javascript·人工智能·python