在react中使用最新的navigator.clipboard.writeText(url)方法来实现复制的功能
navigator.clipboard.writeText() 是现代 Web 开发中用于将文本写入系统剪贴板的标准 API。相比旧的 document.execCommand('copy') 方法,它更安全、更高效,且不依赖 DOM 元素。
javascript
import React, { FC, useRef, useMemo } from 'react'
import { Space, Button, Typography, Input, Tooltip, message, Popover } from 'antd'
import type { InputRef } from 'antd'
import { LeftOutlined, CopyOutlined, QrcodeOutlined } from '@ant-design/icons'
import { useNavigate, useParams } from 'react-router-dom'
import QRCode from 'qrcode.react'
import useGetPageInfo from '../../../hooks/useGetPageInfo'
import styles from './StatHeader.module.scss'
const { Title } = Typography
/**
* 统计页头部组件
* 负责展示问卷标题、返回按钮、分享链接/二维码以及编辑入口
*/
const StatHeader: FC = () => {
// 获取路由跳转函数
const nav = useNavigate()
// 获取路由参数中的问卷 ID
const { id } = useParams()
// 使用自定义 Hook 获取问卷页面信息(标题、是否已发布)
const { title, isPublished } = useGetPageInfo()
// 创建一个 ref 来引用链接输入框,用于获取 URL 值(虽然这里直接拼接了,但保留了通过 DOM 获取的逻辑)
const urlInputRef = useRef<InputRef>(null)
/**
* 复制链接到剪贴板的异步函数
* 使用现代 Clipboard API 实现
*/
async function copy() {
const elem = urlInputRef.current
if (elem == null) return
// 从输入框元素中获取 URL 值
// 注意:这里使用了可选链操作符,防止 elem.input 为 undefined
const url = elem.input?.value || ''
try {
// 调用浏览器原生 API 写入剪贴板
await navigator.clipboard.writeText(url)
message.success('拷贝成功')
} catch (err) {
// 捕获并处理写入失败的情况(如权限被拒、非安全上下文等)
message.error('拷贝失败')
console.error('Failed to copy: ', err)
}
}
// 使用 useMemo 进行性能优化:仅当 id 或 isPublished 变化时重新计算
// 目的:避免每次渲染都重新创建复杂的 JSX 元素(包含 Input, Button, QRCode),减少重渲染开销
const LinkAndQRCodeElem = useMemo(() => {
// 如果问卷未发布,则不显示分享组件
if (!isPublished) return null
// 拼接问卷的访问 URL (基于 C 端访问规则)
// 注意:此处为本地开发环境地址,生产环境需替换为正式域名
const url = `http://localhost:3000/question/${id}`
// 定义二维码弹窗的内容组件
const QRCodeElem = (
<div style={{ textAlign: 'center' }}>
{/* 使用 qrcode.react 库生成二维码,指定大小为 150px */}
<QRCode value={url} size={150} />
</div>
)
// 返回包含链接输入框、复制按钮和二维码按钮的工具栏
return (
<Space>
{/* 只读输入框显示链接 */}
<Input
value={url}
style={{ width: '300px' }}
ref={urlInputRef}
/>
{/* 复制链接按钮,带 Tooltip 提示 */}
<Tooltip title="拷贝链接">
<Button
icon={<CopyOutlined />}
onClick={copy} // 点击触发复制逻辑
/>
</Tooltip>
{/* 二维码弹窗按钮 */}
<Popover content={QRCodeElem}>
<Button icon={<QrcodeOutlined />} />
</Popover>
</Space>
)
}, [id, isPublished]) // 依赖数组:只有当问卷 ID 或发布状态改变时才重新执行
return (
<div className={styles['header-wrapper']}>
<div className={styles.header}>
{/* 左侧:返回按钮和问卷标题 */}
<div className={styles.left}>
<Space>
<Button
type="link"
icon={<LeftOutlined />}
onClick={() => nav(-1)} // 点击返回上一页
>
返回
</Button>
<Title level={3} style={{ margin: 0 }}>{title}</Title>
</Space>
</div>
{/* 中间:链接与二维码分享区域(仅在发布后显示) */}
<div className={styles.main}>{LinkAndQRCodeElem}</div>
{/* 右侧:编辑问卷入口 */}
<div className={styles.right}>
<Button
type="primary"
onClick={() => nav(`/question/edit/${id}`)} // 跳转到编辑页面
>
编辑问卷
</Button>
</div>
</div>
</div>
)
}
export default StatHeader