- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
我们存在着大量在PC页面通过表格看数据业务场景,表格又分为两种,一种是 antd / fusion 这种基于 dom 元素的表格,另一种是通过 canvas 绘制的类似 excel 的表格.
基于 dom 的表格功能丰富较为美观,能实现多表头、合并单元格和各种自定义渲染(如表格中渲染图形 / 按钮 / 进度条 / 单选框 / 输入框),以展示为主,不提供圈选、整列复制等功能.
canvas 绘制的类 excel 外表朴素更为实用,大量数据渲染不卡顿,操作类似 excel,能行/列选中,圈选、复制等功能.
两者使用场景有所差异,各有利弊,但业务方不希望一套系统中出现两种类型的交互,期望能将两种表格的优缺点进行融合,在美观的dom表格中增加圈选、复制的功能.
业务方所期望的圈选效果和excel类似,鼠标按下即选中元素,然后滑动鼠标,鼠标所经过形成的四边形就是选中区域,此时鼠标右键点击复制按钮,或者键盘按下 ctrl + c 复制文本.
而dom表格经过如上操作,会把一整行数据都选上,不符合业务同学的使用预期.
我们需要自行定义鼠标事件、元素样式,需要先将无用的默认样式清除,包括上图中的 hover 和选中元素的背景色.
<Table style={{ userSelect: 'none' }} ></Table>
.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 .
根据滑动元素的上下左右距离与鼠标按下时的位置进行判断,圈选元素存在四个方向,以第一次选中的元素为中心位置。滑动时元素位于鼠标按下的右下、左下、右上、左上方,根据不同的情况来设置四个角的方位.
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>
到这一步,就已经实现了鼠标圈选功能.
表格圈选的交互效果其实是为复制功能做准备.
原表格在选中元素时鼠标右键会出现【复制】按钮,点击后复制的效果是图中圈选到的元素每一个都换行展示,圈选行为不能满足使用需求,复制的内容也无法按照页面中展示的行列格式.
而当我们实现圈选功能之后,因为使用 css 属性 "user-select: none" 禁止用户选择文本,此时鼠标右键已经不会出现复制按钮.
为了实现鼠标右键出现复制按钮,我们需要覆盖原鼠标右键事件,自定义复制功能.
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';
};
这里复制按钮没有调整样式,可根据自己项目情况进行一些美化.
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 的处理 。
const onMouseDown = (event) => {
// 0:表示鼠标左键。2:表示鼠标右键。1:表示鼠标中键或滚轮按钮
if (event.button !== 0) return;
// 隐藏复制按钮
const contextMenu = document.getElementById('contextMenu');
contextMenu.style.display = 'none';
};
到这里,就已经实现了圈选鼠标右键复制的功能.
使用 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);
};
}, []);
以上功能是在 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;
}
实现效果是这样的 。
完整代码在这里 table-brush-copy ,包括 fusion design 和 ant design 两个版本.
表格圈选复制功能的实现主要是以下五步 。
集合了较多的鼠标、键盘事件,以及 javascript 获取属性、元素.
最后此篇关于antd/fusion表格增加圈选复制功能的文章就讲到这里了,如果你想了解更多关于antd/fusion表格增加圈选复制功能的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
好的,这听起来很简单,但我已经花了几个小时在谷歌上搜索,我只是找不到解决方案,这并不复杂。 我想创建一个包含图像和文本的表格。我希望表格的每一行都具有相同的高度。我希望文本始终从顶部开始。 IE。 \
在我的网站表单上 - 我的出生日期、月份和年份菜单显示在两行上,我希望它们都显示在同一行上。 当我测试代码时,它显示在一行中,所以我相信一定存在宽度问题。 您可以在右侧表格 (incomeprotec
我们需要跟踪和审核生产,本质上我们有很多订单,但我们似乎在途中丢失了一些产品(废品等)。 为了阻止这种情况,我们现在已在 Google 表格上下了订单,并列出了应有的数量,然后员工会写下收到的数量。
我正在转换我的应用程序,以便它适用于 iOS 7。在应用程序的一部分,我有两个搜索栏,每个搜索栏都有一个与之关联的 UISearchDisplayController。当我搜索 UISearchDis
正如标题所说,非固定表格布局是否与类似的 HTML 表格具有相同的性能问题? 最佳答案 非固定表格的问题在于,要确定一列的宽度,必须加载该列的所有单元格。这仅在...... …您有一个包含几千字节或几
我在使用 Javascript 遍历表格并从一行的第一个单元格获取文本时遇到问题。我想获取此单元格的文本,以便我可以将它与其他内容进行比较,如果文本匹配则删除该行。但是,当我尝试获取文本时,实际出现的
我经常发现自己想要制作一个表格表格——一堆行,每一行都是一个单独的表格,有自己的字段和提交按钮。例如,这是一个宠物店应用程序示例——假设这是一个结帐屏幕,您可以选择更新所选宠物的数量和属性,并在结帐前
看过许多UBB代码,包括JS,ASP,JSP的,一直没发现表格的UBB,虽然可以直接用HTML模式实现相同表格功能,但对于某些开放的站点来说开放HTML模式终究是不合适的,故一直想实现表格的UBB。
表格由 table 标签来定义。每个表格均有若干行(由 tr 标签定义),每行被分割为若干单元格(由 td 标签定义)。字母 td 指表格数据(table data),即数据单元格的内容。数据单元格
我有一个 HTML 与 border-radius和使用 position: sticky 的粘性标题看起来像这样: https://codepen.io/muhammadrehansaeed/pen
对于 iPhone 应用程序,我需要以网格格式显示只读表格数据。该数据可能有许多行和列。 我可以使用 UITableView,但问题是数据很可能会非常宽并且需要滚动。 有没有办法将 UITableVi
我知道这里有类似的问题,但我找不到适合我的答案。 我想要的是显示表单“默认”是选择了某些选项(在这种情况下,除了“Ban Appeal”或“Ban Appeal(西类牙语)”之外的所有内容,我希望仅在
天啊! 我想在Flutter中创建以下非常简单的表。基本上是两列文字,左列右对齐,右列左对齐。如果右列具有多个名称,则每一行都将顶部对齐。 左列应自动调整为最大项目的大小(因为每个标题都有翻译字符串)
我们开始构建 SSAS 表格模型,并想知道大多数人是否拥有一个或多个模型。如果有多个,您是否复制每个所需的表,或者是否有办法在模型之间共享表?我想我知道答案,但我希望那些有更多经验的人能够证实我们的发
tl;博士 如何将任意数量的单词分成两列,总是在最后一列中只有最后一个单词,在第一列中包含所有其他单词? =IFS( LEN(C2)-LEN(SUBSTITUTE(C2," ",""))=1, SP
你们知道一个图表或dable,它可以提供一个简短而简洁但仍然完整且相对最新的现有协议(protocol)及其细节的 View ? (即:ZeroMQ、Rendez-Vous、EMS、...所有这些!:
我才刚刚开始开发MFC应用程序,我希望对整个“控件”概念更加熟悉。我在Visual Studio中使用对话框编辑器,到目前为止,我无法找到添加简单表/网格的功能。这对我来说似乎很基础,但是我什至找不到
我需要对一个非常大的表或矩阵执行计算和操作,大约有 7500 行和 30000 列。 矩阵数据将如下所示: 文件编号|字1 |字 2 |字 3 |... |字 30000 |文档类 0032 1 0
我正在使用设计非常糟糕的数据库,我需要在编写查询之前重新调整表格。 以下是我的常见问题: 时间戳已分为两列(一列用于日期,另一列用于时间)。 一些字符串列也被拆分成多个列。 大多数字符串都有固定长度和
我正在尝试显示 $row["name"] 通过 HTML Table 的形式,如下所示: echo " ".$row["name"]." "; 我也从这里获取行变量: $que
我是一名优秀的程序员,十分优秀!