• 作者:老汪软件技巧
  • 发表时间:2024-09-13 00:04
  • 浏览量:

前言

老板:xxx, 你能不能把表格里面的数据弄一个复制的,我每次都要选中去cv很麻烦的。

我:让我想想。

略做思考

我用的是Antd组件库,在表格每一列render的时候通过一个通用自定义组件CopyText去复制,以便在任何可以复制的地方都可以使用,我还能再细化一哈,将复制的逻辑可以再次抽离出来,封装一个成一个hook函数useCopy。

实现封装useCopyhook函数

web中实现复制效果的方法有两种。

优点:

现代化:这是现代浏览器推荐的方式,符合最新的 Web 标准。异步操作:使用Promise,可以更好地处理异步操作和错误处理。更安全:需要用户的明确权限,减少了安全风险。更简洁:代码更简洁,易于理解和维护。

缺点:

兼容性:并非所有浏览器都支持,尤其是一些旧版浏览器。权限问题:需要用户授予剪贴板权限,可能会导致用户体验问题。

优点:

兼容性好:支持较早版本的浏览器,包括一些老旧的浏览器。无需权限:不需要用户授予剪贴板权限,操作更直接。

缺点:

过时:这是一个过时的 API,未来可能会被移除。同步操作:是同步操作,可能会导致阻塞 UI 线程。安全性:存在一定的安全风险,因为它不需要用户的明确权限。

综上,我们navigator.clipboard.writeText(v)的缺点恰好就是doucment.execCommand('copy')的优点,所以我们可以将两种方法其进行结合,首先使用navigator.clipboard.writeText(v)进行复制,若是复制失败后在使用doucment.execCommand('copy')进行复制,最后将复制方法导出,代码如下所示:

import { message } from 'antd';
import { useCallback } from 'react';
function useCopy(): [(str: string) => void] {
  const copy = useCallback(async (v: string): Promise<void> => {
    if (!v) {
      message.warning('复制内容为空!');
      return;
    }
    try {
      await navigator.clipboard.writeText(v);
      message.success('复制成功!');
    } catch (error) { // 复制失败后在使用execCommand复制一次
      const textareaC = document.createElement('textarea');
      textareaC.setAttribute('readonly', 'readonly'); //设置只读属性防止手机上弹出软键盘
      textareaC.value = v;
      document.body.appendChild(textareaC); //将textarea添加为body子元素
      textareaC.select();
      const successful = document.execCommand('copy'); // 执行 copy 操作
      if (successful) {
        message.success('复制成功!');
        document.body.removeChild(textareaC); //移除DOM元素
      } else {
        message.warning('复制失败,请手动复制!');
      }
    }
  }, []);
  return [copy];
}
export default useCopy;

实现CopyText组件

CopyText组件的复制逻辑已经在useCopy函数里面封装好了,然后我们就只需要考虑如何进行组件的展示效果了。

当鼠标指向一个需要复制的元素的时候,最好需要一个hover效果展示具体的内容,点击此元素时候也能直接复制相关内容。

import useCopy from '@/hooks/useCopy';
import { Popover } from 'antd';
type CopyInfo = {
  text: string;
  title?: string;
  children?: React.ReactNode;
};
export default function Index(props: CopyInfo) {
  const { text, title, children } = props;
  const [copy] = useCopy();
  const copyAccessKey = () => {
    copy(text);
  };
  const style = {
    cursor: 'pointer',
  };
  return (
    <Popover
      content={
        <div onClick={copyAccessKey} style={style}>
          {text}
        div>
      }
      title={title ?? '复制信息'}
    >
      <span onClick={copyAccessKey} style={style}>
        {children ?? text}
      span>
    Popover>
  );
}

使用及效果

结语

就这样,我们实现了一个复制组件的封装。在这里我们主要考虑的主要是浏览器原生复制的兼容程度,自定义复制逻辑,其次在复制组件中考虑如何需要如何展示数据。