本文基于react项目实现。
起因
接了一个需求,要求实现点击页面上的一个按钮,复制某个表格数据,然后ctrl+V到Excel表格中。
思考
其实我觉着这个功能用导出Excel的方式实现不就好了么。不过本着好奇的心思,还是试着做做看吧。
首先,点击按钮的时候要拿到要复制的数据。最初是想通过获取DOM元素的方式来取得表格,可是这样的话,得到的就是含有html标签的一堆字符串。那么只能自己来拼装数据了。我把表格部分的数据通过复制、黏贴到编辑器看下他是什么,结果如下图:
而页面上的表格是这样的:
对比来看:跟页面上的表格差别不大。
其次,要把数据组装好之后,要通过JS的方式放到剪切板中。这个有点难度了。通过查资料下面这种方式可以改变剪切板的内容。
document.addEventListener("copy", (event) => {
if (copyEvent) {
if (event.clipboardData || event.originalEvent) {
var clipboardData = (event.clipboardData || event.originalEvent.clipboardData);
const selection = "好激动12312412\nhahahha";
clipboardData.setData('text/plain', selection.toString());
event.preventDefault();
}
}
});
那么,问题来了,这里是通过监听copy事件来改变剪切板的内容的,但是我们点击按钮到复制到Excel里面,这过程中并没有触发ctrl+C的操作啊?
那么只能点击按钮的时候来触发了。
document.execCommand('copy')
可以实现。
然后,由于是在document上做的监听,那么页面上所有的复制操作都被拦截了,换成我们拼接的内容啦。显然这不是我们想要的。于是我想到了开关,在点击按钮的时候打开开关,然后触发copy事件,在监听事件中判断开关状态,这样就能过滤掉其他位置的复制的操作了。
实现
• 先准备环境、数据
import { Button, Table } from 'antd';
import { useEffect } from 'react'
import './App.css';
const dataSource = [
{
key: '1',
name: '胡彦斌',
age: 32,
address: '西湖区湖底公园1号',
},
{
key: '2',
name: '胡彦祖',
age: 42,
address: '西湖区湖底公园1号',
},
];
const columns = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '年龄',
dataIndex: 'age',
key: 'age',
},
{
title: '住址',
dataIndex: 'address',
key: 'address',
},
]
const btnClick = () => {
}
function App() {
return (
<div className="App">
<header className="App-header">
<Button type="primary" onClick={btnClick}>复制</Button>
<Table dataSource={dataSource} columns={columns} />
</header>
</div>
);
}
export default App;
• 监听copy事件,点击按钮的时候触发copy事件
const btnClick = () => {
document.execCommand('copy')
}
useEffect(() => {
document.addEventListener("copy", (event) => {
if (event.clipboardData || event.originalEvent) {
var clipboardData = (event.clipboardData || event.originalEvent.clipboardData);
const selection = "好激动12312412\nhahahha";
clipboardData.setData('text/plain', selection.toString());
event.preventDefault();
copyEvent = false
}
});
}, []);
• 添加开关、拼接数据
let copyEvent = false // 开关
const btnClick = () => {
copyEvent = true
document.execCommand('copy')
}
useEffect(() => {
document.addEventListener("copy", (event) => {
if (copyEvent) {
if (event.clipboardData || event.originalEvent) {
var clipboardData = (event.clipboardData || event.originalEvent.clipboardData);
// 拼接数据
const first = columns.map(item=>item.title).join('\t')
const sec = dataSource.map(item=> `${item.name}\t${item.age} \t${item.address}`).join('\n')
const selection = `${first}\n${sec}`
clipboardData.setData('text/plain', selection.toString());
event.preventDefault();
copyEvent = false // 关掉开关
}
}
});
}, []);
\t是制表符,在Excel中就是单元格分隔。\n是换行符,对应Excel就是不同行。
复制到Excel中的效果:
完整代码
import { Button, Table } from 'antd';
import { useEffect } from 'react'
import './App.css';
const dataSource = [
{
key: '1',
name: '胡彦斌',
age: 32,
address: '西湖区湖底公园1号',
},
{
key: '2',
name: '胡彦祖',
age: 42,
address: '西湖区湖底公园1号',
},
];
const columns = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '年龄',
dataIndex: 'age',
key: 'age',
},
{
title: '住址',
dataIndex: 'address',
key: 'address',
},
]
let copyEvent = false // 开关
const btnClick = () => {
copyEvent = true
document.execCommand('copy')
}
function App() {
useEffect(() => {
document.addEventListener("copy", (event) => {
if (copyEvent) {
if (event.clipboardData || event.originalEvent) {
var clipboardData = (event.clipboardData || event.originalEvent.clipboardData);
// 拼接数据
const first = columns.map(item=>item.title).join('\t')
const sec = dataSource.map(item=> `${item.name}\t${item.age} \t${item.address}`).join('\n')
const selection = `${first}\n${sec}`
clipboardData.setData('text/plain', selection.toString());
event.preventDefault();
copyEvent = false // 关掉开关
}
}
});
}, []);
return (
<div className="App">
<header className="App-header">
<Button type="primary" onClick={btnClick}>复制</Button>
<Table dataSource={dataSource} columns={columns} />
</header>
</div>
);
}
export default App;
参考
• execCommand
• JS设置获取剪切板内容