antd/fusion表格增加圈选复制功能

这篇具有很好参考价值的文章主要介绍了antd/fusion表格增加圈选复制功能。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景介绍

我们存在着大量在PC页面通过表格看数据业务场景,表格又分为两种,一种是 antd / fusion 这种基于 dom 元素的表格,另一种是通过 canvas 绘制的类似 excel 的表格。

基于 dom 的表格功能丰富较为美观,能实现多表头、合并单元格和各种自定义渲染(如表格中渲染图形 / 按钮 / 进度条 / 单选框 / 输入框),以展示为主,不提供圈选、整列复制等功能。

antd/fusion表格增加圈选复制功能

canvas 绘制的类 excel 外表朴素更为实用,大量数据渲染不卡顿,操作类似 excel,能行/列选中,圈选、复制等功能。

antd/fusion表格增加圈选复制功能

两者使用场景有所差异,各有利弊,但业务方不希望一套系统中出现两种类型的交互,期望能将两种表格的优缺点进行融合,在美观的dom表格中增加圈选、复制的功能。

圈选效果

业务方所期望的圈选效果和excel类似,鼠标按下即选中元素,然后滑动鼠标,鼠标所经过形成的四边形就是选中区域,此时鼠标右键点击复制按钮,或者键盘按下 ctrl + c 复制文本。

antd/fusion表格增加圈选复制功能

而dom表格经过如上操作,会把一整行数据都选上,不符合业务同学的使用预期。

antd/fusion表格增加圈选复制功能

实现过程

去除默认样式

我们需要自行定义鼠标事件、元素样式,需要先将无用的默认样式清除,包括上图中的 hover 和选中元素的背景色。

  • 禁用表格本身的鼠标点击选择功能,设置css,userSelect: none
<Table style={{ userSelect: 'none' }} ></Table>
  • 去除 hover 样式(这里使用的是 fusion 组件)
.next-table-row:hover {
  background-color: transparent !important;
}

鼠标按下,记录选中元素

为表格绑定鼠标按键时触发事件 mousedown

当鼠标按下时,这个元素就是中心元素,无论是向哪个方向旋转,所形成的区域一定会包含初始选中的元素。

getBoundingClientRect() 用于获得页面中某个元素的上下左右分别相对浏览器视窗的位置。

const onMouseDown = (event) => {
  const rect = event.target.getBoundingClientRect();

  // funsion 判断点击是否为表头元素,为否时才继续后面的逻辑。antd 不需要判断,因为点击表头不会触发该事件
  const isHeaderNode = event.target?.parentNode?.getAttribute('class')?.indexOf('next-table-header-node') > -1;
  if (isHeaderNode) return;

  originDir = {
    top: rect.top,
    left: rect.left,
    right: rect.right,
    bottom: rect.bottom,
  };
  // 渲染
  renderNodes(originDir);
};

<Table style={{ userSelect: 'none' }} onMouseDown={onMouseDown}></Table>

鼠标滑过

为表格绑定鼠标滑过时触发事件 mousemove

根据滑动元素的上下左右距离与鼠标按下时的位置进行判断,圈选元素存在四个方向,以第一次选中的元素为中心位置。滑动时元素位于鼠标按下的右下、左下、右上、左上方,根据不同的情况来设置四个角的方位。

antd/fusion表格增加圈选复制功能

const onMouseMove = (event) => {
  if (!originDir.top) return;
  const rect = event.target.getBoundingClientRect();

  let coordinates = {};

  // 鼠标按下后往右下方拖动
  if (
    rect.top <= originDir.top &&
    rect.left <= originDir.left &&
    rect.right <= originDir.left &&
    rect.bottom <= originDir.top
  ) {
    coordinates = {
      top: rect.top,
      left: rect.left,
      right: originDir.right,
      bottom: originDir.bottom,
    };
  }

  // 鼠标按下后往左下方拖动
  if (
    rect.top >= originDir.top &&
    rect.left <= originDir.left &&
    rect.right <= originDir.right &&
    rect.bottom >= originDir.bottom
  ) {
    coordinates = {
      top: originDir.top,
      left: rect.left,
      right: originDir.right,
      bottom: rect.bottom,
    };
  }
  
  
// 鼠标按下后往右上方拖动
   if (
    rect.top <= originDir.top &&
    rect.left >= originDir.left &&
    rect.right >= originDir.right &&
    rect.bottom <= originDir.bottom
    ) {
     coordinates = {
        top: rect.top,
        left: originDir.left,
        right: rect.right,
        bottom: originDir.bottom,
    };
}

  // 鼠标按下后往左上方拖动
  if (
    rect.top >= originDir.top &&
    rect.left >= originDir.left &&
    rect.right >= originDir.right &&
    rect.bottom >= originDir.bottom
  ) {
    coordinates = {
      top: originDir.top,
      left: originDir.left,
      right: rect.right,
      bottom: rect.bottom,
    };
  }

  renderNodes(coordinates);
};

<Table
    style={{ userSelect: 'none' }}
    onMouseDown={onMouseDown}
    onMouseMove={onMouseMove}
></Table>

渲染/清除样式

遍历表格中 dom 元素,如果该元素在圈选的区域内,为其添加选中的背景色,再为四边形区域增加边框。

这里无论是直接设置 style 还是添加 classname 都不是很好。直接添加 classname 时,antd 会在 hover 操作时重置 classname,原来设置的 classname 会被覆盖。直接设置 style 可能存在和其他设置冲突的情况,并且最后获取所有圈选元素时比较麻烦。

以上两种方法都尝试过,最后选择了直接往 dom 元素上面添加属性,分别用5个属性保存是否圈选,上下左右边框,这里没有进行合并是因为一个dom元素可能同时存在这五个属性。

const renderNodes = (coordinates) => {
  const nodes = document.querySelectorAll('.next-table-cell-wrapper');
  nodes.forEach((item) => {
    const target = item?.getBoundingClientRect();
    clearStyle(item);
    if (
      target?.top >= coordinates.top &&
      target?.right <= coordinates.right &&
      target?.left >= coordinates.left &&
      target?.bottom <= coordinates.bottom
    ) {
      item.setAttribute('data-brush', 'true');

      if (target.top === coordinates.top) {
        item.setAttribute('brush-border-top', 'true');
      }
      if (target.right === coordinates.right) {
        item.setAttribute('brush-border-right', 'true');
      }
      if (target.left === coordinates.left) {
        item.setAttribute('brush-border-left', 'true');
      }
      if (target.bottom === coordinates.bottom) {
        item.setAttribute('brush-border-bottom', 'true');
      }
    }
  });
};

const clearStyle = (item) => {
  item.hasAttribute('data-brush') && item.removeAttribute('data-brush');
  item.hasAttribute('brush-border-top') && item.removeAttribute('brush-border-top');
  item.hasAttribute('brush-border-right') && item.removeAttribute('brush-border-right');
  item.hasAttribute('brush-border-left') && item.removeAttribute('brush-border-left');
  item.hasAttribute('brush-border-bottom') && item.removeAttribute('brush-border-bottom');
};

使用 fusion 的 table 需要为每一个元素添加上透明的边框,不然会出现布局抖动的情况。(antd 不用)

 /* 为解决设置样式抖动而设置 */
 .next-table td .next-table-cell-wrapper {
  border: 1px solid transparent;
 }

[brush-border-top="true"] {
  border-top: 1px solid #b93d06 !important;
}
[brush-border-right="true"] {
  border-right: 1px solid #b93d06 !important;
}
[brush-border-left="true"] {
  border-left: 1px solid #b93d06 !important;
}
[brush-border-bottom="true"] {
  border-bottom: 1px solid #b93d06 !important;
}
[data-brush="true"] {
  background-color: #f5f5f5 !important;
}

.next-table-row:hover {
  background-color: transparent !important;
}

鼠标松开

为表格绑定鼠标松开时触发事件 mouseup

从鼠标按下,到滑动,最后松开,是一整个圈选流程,在鼠标按下时保存了初始的方位,滑动时判断是否存在方位再进行计算,松开时将初始方位置空。

const onMouseUp = () => {
  originDir = {};
};

 <Table
    style={{ userSelect: 'none' }}
    onMouseDown={onMouseDown}
    onMouseMove={onMouseMove}
    onMouseUp={onMouseUp}
    ></Table>

到这一步,就已经实现了鼠标圈选功能。

antd/fusion表格增加圈选复制功能

复制功能

表格圈选的交互效果其实是为复制功能做准备。

鼠标右键复制

原表格在选中元素时鼠标右键会出现【复制】按钮,点击后复制的效果是图中圈选到的元素每一个都换行展示,圈选行为不能满足使用需求,复制的内容也无法按照页面中展示的行列格式。

antd/fusion表格增加圈选复制功能

而当我们实现圈选功能之后,因为使用 css 属性 "user-select: none" 禁止用户选择文本,此时鼠标右键已经不会出现复制按钮。

antd/fusion表格增加圈选复制功能

为了实现鼠标右键出现复制按钮,我们需要覆盖原鼠标右键事件,自定义复制功能。

1、为表格绑定鼠标右键事件 contextMenu

<Table
    style={{ userSelect: 'none' }}
    onMouseDown={onMouseDown}
    onMouseMove={onMouseMove}
    onMouseUp={onMouseUp}
    onContextMenu={onContextMenu}
></Table>

2、创建一个包含复制按钮的自定义上下文菜单

<div id="contextMenu" className="context-menu" style={{ cursor: 'pointer' }}>
<div onClick={onClickCopy}>复制</div>
</div>

3、阻止默认的右键菜单弹出,将自定义上下文菜单添加到页面中,并定位在鼠标右键点击的位置。

const onContextMenu = (event) => {
  event.preventDefault(); // 阻止默认右键菜单弹出

  const contextMenu = document.getElementById('contextMenu');

  // 定位上下文菜单的位置
  contextMenu.style.left = `${event.clientX}px`;
  contextMenu.style.top = `${event.clientY}px`;

  // 显示上下文菜单
  contextMenu.style.display = 'block';
};

这里复制按钮没有调整样式,可根据自己项目情况进行一些美化。

antd/fusion表格增加圈选复制功能

4、点击复制按钮时,保存当前行列格式执行复制操作。

复制仍然保留表格的样式,这里想了很久,一直在想通过保存dom元素的样式来实现,这种方案存在两个问题,一是保存html样式的api,document.execCommand('copy') 不被浏览器支持,二是表格元素都是行内元素,即使复制了样式,也和页面上看到的布局不一样。

最后采取的方案还是自己对是否换行进行处理,遍历元素时判断当前元素的 top 属性和下一个点距离,如果相同则添加空字符串,不同则添加换行符 \n 。

const onClickCopy = () => {
    const contextMenu = document.getElementById('contextMenu');
    const copyableElements = document.querySelectorAll('[data-brush=true]');

    // 遍历保存文本
    let copiedContent = '';
    copyableElements.forEach((element, index) => {
       let separator = ' ';
       if (index < copyableElements.length - 1) {
          const next = copyableElements?.[index + 1];
          if (next?.getBoundingClientRect().top !== element.getBoundingClientRect().top) {
              separator = '\n';
           }
        }
        copiedContent += `${element.innerHTML}${separator}`;
    });

    // 执行复制操作
    navigator.clipboard.writeText(copiedContent).then(() => {
       console.log('已复制内容:', copiedContent);
    }) .catch((error) => {
        console.error('复制失败:', error);
    });

    // 隐藏上下文菜单
    contextMenu.style.display = 'none';
};

5、对鼠标按下事件 onMouseDown 的处理

  • 鼠标点击右键也会触发 onMouseDown ,这时会造成选中区域错乱,需要通过 event.button 判断当前事件触发的鼠标位置。
  • 鼠标右键后如果没有点击复制按钮而是滑走或者使用鼠标左键选中,这时候相当于执行取消复制操作,复制按钮的上下文需要清除。
const onMouseDown = (event) => {
  //  0:表示鼠标左键。2:表示鼠标右键。1:表示鼠标中键或滚轮按钮
  if (event.button !== 0) return;
  
  // 隐藏复制按钮
  const contextMenu = document.getElementById('contextMenu');
  contextMenu.style.display = 'none';
};

到这里,就已经实现了圈选鼠标右键复制的功能。

antd/fusion表格增加圈选复制功能

ctrl+s / command+s 复制

使用 event.ctrlKey 来检查 Ctrl 键是否按下,使用 event.metaKey 来检查 Command 键是否按下,并使用 event.key 来检查按下的键是否是 c 键。

useEffect(() => {
    const clickSave = (event) => {
      if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
        onClickCopy();
        event.preventDefault(); // 阻止默认的保存操作
      }
    };

    document.addEventListener('keydown', clickSave);

    return () => {
      document.removeEventListener('keydown', clickSave);
    };
}, []);

antd 也可以使用

以上功能是在 fusion design 中实现的,在 antd 中也可以使用,语法稍有不同。

表格中鼠标事件需要绑定在 onRow 函数中

 <Table
  style={{ userSelect: 'none' }}
  onRow={() => {
    return {
      onContextMenu,
      onMouseDown,
      onMouseMove,
      onMouseUp,
    };
  }}
>

获取所有表格 dom 元素的类名替换一下

 const nodes = document.querySelectorAll('.ant-table-cell');

覆盖表格 hover 时样式

 .ant-table-cell-row-hover {
    background: transparent;
  }

  .ant-table-wrapper .ant-table .ant-table-tbody > tr.ant-table-row:hover > td,
  .ant-table-wrapper .ant-table .ant-table-tbody > tr > td.ant-table-cell-row-hover {
    background: transparent;
  }

实现效果是这样的

antd/fusion表格增加圈选复制功能

完整代码

完整代码在这里 table-brush-copy,包括 fusion design 和 ant design 两个版本。

总结

表格圈选复制功能的实现主要是以下五步

  • mousedown 按下鼠标,记录初始坐标
  • mousemove 滑动鼠标,计算所形成的四边形区域
  • mouseup 松开鼠标,清空初始坐标
  • contextmenu 自定义鼠标右键事件,定位上下文事件
  • keydown 监听键盘按下位置,判断是否为复制操作

集合了较多的鼠标、键盘事件,以及 javascript 获取属性、元素。文章来源地址https://www.toymoban.com/news/detail-710098.html

到了这里,关于antd/fusion表格增加圈选复制功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue3 - element-plus表格组件el-table实现鼠标拖曳排序功能,vue3 Table表格拖拽排序,表格每行使用鼠标拖动进行排序功能,表格拖拽排序实现(详细示例代码,一键复制开箱即用

    在vue3+elementPlus网站开发中,详细完成el-table表格的鼠标拖拽/拖曳/拖动排序,vue3使用element plus表格组件进行表格每行的拖动换位置排序功能(支持一键开启和关闭鼠标是否可拖动排序,代码易改造灵活),稍加改造可支持【树形复杂表格的排序】! 详细示例源代码,复制运行

    2024年04月09日
    浏览(61)
  • ELK技术介绍:背景、功能及应用场景全面解析

            ELK是由Elasticsearch、Logstash和Kibana三个开源软件组成的日志管理解决方案,这一组合在近年来得到了广泛的关注和应用。ELK的出现,源于大数据和云计算技术的快速发展,以及对高效日志管理的迫切需求。         随着企业信息化程度的提高,各类应用系统和服

    2024年04月28日
    浏览(37)
  • Antd List组件增加gutter属性后出现横向滚动,如何解决

    第一次使用ant design的List列表组件,设置gutter间隔属性后,页面出现了横向滚动条,查阅文档发现是由于加间隔后导致容器宽度被撑开,ant design官方默认给外层容器加了margin-left和margin-right  解决方法是在外层容器预留一定的padding,代码如下: 到此,完美解决~

    2024年02月16日
    浏览(29)
  • Vue3 Antd 父子嵌套子表格

    父子嵌套子表格 目标1:可以点击多个父节点表格,正确显示子表格数据 目标2:父表格数据刷新重载,解决子表格数据不刷新问题 官方示例代码,以及效果 https://www.antdv.com/components/table-cn#components-table-demo-nested-table 可以看到官方示例十分简单,使用了 template #expandedRowRender

    2024年02月01日
    浏览(29)
  • react antd阻止Checkbox事件冒泡(折叠面板标题中增加复选框,阻止点击复选框折叠面板展开/折叠)

    背景 折叠面板Collapse标题中增加复选框,点击复选框,会触发折叠面板的展开和折叠。 我们希望勾选复选框的时候,不能影响到折叠面板的展开和折叠。 最开始使用 onChange 自带的event来阻止事件冒泡,这种方式是无效的,代码如下: 解决 监听复选框的 onClick 事件,用 e.st

    2024年02月11日
    浏览(54)
  • 前端开发中ui(antd)表格导出为excel。详细步骤一看就会

    我们在开发的时候往往有这样的需求,将页面上的表格导出为excel文件。以随手写的demo为例。   1、首先我们需要下载一个插件,用于将页面中的数据转为excel文件            可以通过我们的package.json文件去查看是否安装好 2、在我们需要用到的页面去引用  3、定义触发

    2024年02月15日
    浏览(40)
  • vue3 antd项目实战——table表格的自定义筛选【纯前端filters过滤、自定义筛选table表格数据】

    文章内容 文章链接 vue3 antd table 表格的基础搭建 https://blog.csdn.net/XSL_HR/article/details/128072745 ant design vue 组件库的引入与使用 https://blog.csdn.net/XSL_HR/article/details/127396384 在 后台管理系统 中,我们需要 对大量的数据进行展示、处理和操作 ,table表格也因此无处不在。而 ant design

    2024年01月25日
    浏览(54)
  • Android 13.0 SystemUI下拉状态栏背景增加高斯模糊背景

    在13.0的产品开发中,发现现在很多产品都是高斯模糊背景的,这种高斯模糊背景看起来效果很不错,产品开发需要SystemUI下拉状态栏背景也是高斯模糊背景,所以就要来实现下拉状态栏高斯模糊背景 在系统systemui,其实它的布局结构是比较复杂,里面管理各种服务,导航栏,

    2024年02月05日
    浏览(153)
  • es索引数据复制并增加条件和修改目标数据值

    es操作同一个索引里数据的复制语法 复制数据:     字段值修改: 可以通过在  source  中添加  query  来设置条件,只有满足条件的文档才会被复制到目标索引中。例如: 上述代码将只复制  source_index  中  field_name  字段值为  value  的文档到  destination_index  中。 可以在

    2024年02月08日
    浏览(34)
  • 首屏优化,给以图片为背景的元素增加相似背景,优化用户体验,background-image 绘制规则

    每日鸡汤:每个你想要学习的瞬间都是未来的你向自己求救 假设你的项目首页有个大大的图片作为背景,那么这个图片肯定会在网络不好的时候加载出来很慢,导致用户回看到一大片白屏,这样很影响体验。这也是老生常谈的首屏优化的问题。例如 我们可以给这个首屏元素

    2024年02月14日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包