1. 重构CopyButton
2. 修改Router特性
3. 修改CopyButton传参
4. 添加options页面
This commit is contained in:
雨霖铃
2026-01-28 00:42:05 +08:00
parent b33194bd2d
commit 4edb2f1a47
5 changed files with 135 additions and 90 deletions
+72 -79
View File
@@ -1,108 +1,101 @@
import {useState, useCallback} from "react"; import { useState, useCallback } from 'react';
import { copyToClipboard } from '@/utils/chromeUtils';
const CopyButton = ({ const CopyButton = ({
text = '要复制的文本', text = '要复制的文本',
buttonText = '复制文本', buttonText = '复制文本',
className = 'action-btn', className = 'action-btn',
}) => { successMessage = '复制成功!',
const [btnText, setBtnText] = useState(buttonText); errorMessage = '复制失败,请手动复制。',
const [copyStatus, setCopyStatus] = useState(''); onCopyOverride = null,
}) => {
const [status, setStatus] = useState('idle'); // 'idle' | 'copying' | 'success' | 'error'
// 重置按钮状态的函数 useEffect(() => {
const resetButton = useCallback(() => { let timer;
setBtnText(buttonText); if (status === 'success' || status === 'error') {
setCopyStatus(''); timer = setTimeout(() => {
}, [buttonText]); setStatus('idle');
// 复制成功的处理
const handleCopySuccess = useCallback(() => {
setCopyStatus('success');
setBtnText('复制成功!');
// 2秒后恢复
setTimeout(() => {
resetButton();
}, 2000); }, 2000);
}, [text, resetButton]); }
// 复制失败的处理 return () => clearTimeout(timer);
const handleCopyError = useCallback(() => { }, [status]);
setCopyStatus('error');
// 2秒后恢复 const performCopy = async () => {
setTimeout(() => { if (!text) {
resetButton(); console.warn('没有提供要复制的文本');
}, 2000); return false;
}, [resetButton]); }
const safeText = String(text);
// 优先使用传入的复制函数
if (onCopyOverride) {
return await onCopyOverride(safeText);
}
const textArea = document.createElement("textarea");
textArea.value = safeText;
// 确保元素存在但不可见,且不影响布局
textArea.style.position = "fixed";
textArea.style.left = "-9999px";
textArea.style.top = "0";
textArea.style.opacity = "0";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
const successful = document.execCommand('copy');
document.body.removeChild(textArea);
// 使用通用的复制函数
return true;
};
const handleClick = useCallback(async () => {
setStatus('copying');
const handleCopy = async () => {
try { try {
console.log('开始复制:', text); const isSuccess = await performCopy();
setStatus(isSuccess ? 'success' : 'error');
// 首先尝试直接使用navigator.clipboard(在popup页面中可能可用) } catch (error) {
try { console.error('复制时出错:', error);
console.log('尝试直接使用navigator.clipboard复制'); setStatus('error');
await navigator.clipboard.writeText(text.toString());
console.log('直接复制成功');
handleCopySuccess();
return;
} catch (directError) {
console.log('直接复制失败,尝试使用Chrome扩展API:', directError);
} }
}, [text, onCopyOverride]);
// 如果直接复制失败,尝试使用Chrome扩展API // 根据状态计算当前显示的文本
if (chrome && chrome.runtime && chrome.runtime.sendMessage) { const currentText = status === 'success' ? successMessage
console.log('使用Chrome扩展API复制'); : status === 'error' ? errorMessage
: buttonText;
// 使用Chrome扩展API复制 // 根据状态计算样式 (建议使用 CSS Module 或 Tailwind,这里为了演示保留内联)
chrome.runtime.sendMessage({ const getBackgroundColor = () => {
action: 'copy', switch (status) {
text: text case 'success': return '#4CAF50';
}, (response) => { case 'error': return '#f44336';
console.log('收到background响应:', response, 'lastError:', chrome.runtime.lastError); default: return '#4CAF50'; // 让 CSS 类控制默认颜色
// 检查是否有运行时错误
if (chrome.runtime.lastError) {
console.error('Chrome运行时错误:', chrome.runtime.lastError.message);
handleCopyError();
return;
}
// 检查响应
if (response && response.success) {
console.log('通过background复制成功');
handleCopySuccess();
} else {
console.error('通过background复制失败:', response?.error);
handleCopyError();
}
});
} else {
console.error('没有可用的复制方法');
handleCopyError();
}
} catch (err) {
console.error('复制过程中发生错误:', err);
handleCopyError();
} }
}; };
return ( return (
<button <button
onClick={handleCopy} onClick={handleClick}
className={className} className={`${className} ${status} copy-button`}
style={{ style={{
backgroundColor: copyStatus === 'success' ? '#4CAF50' : backgroundColor: getBackgroundColor(),
copyStatus === 'error' ? '#f44336' : '', color: status !== 'idle' ? 'white' : undefined,
color: copyStatus ? 'white' : '',
transition: 'all 0.3s ease', transition: 'all 0.3s ease',
padding: '8px 16px', padding: '8px 16px',
border: 'none', border: 'none',
borderRadius: '8px', borderRadius: '8px',
cursor: 'pointer' cursor: 'pointer',
}} }}
> >
{btnText} {currentText}
</button> </button>
); );
}; };
+1 -1
View File
@@ -127,7 +127,7 @@ export function TimestampExecution() {
</button> </button>
<CopyButton <CopyButton
text={currentTimestamp.toString()} text={String(currentTimestamp)}
buttonText="复制时间戳" buttonText="复制时间戳"
aria-label="复制当前时间戳到剪贴板" aria-label="复制当前时间戳到剪贴板"
/> />
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
Hello Options Page
</body>
</html>
+1 -1
View File
@@ -43,7 +43,7 @@ function App() {
const collapsedNavItems = navItems.slice(visibleItems); const collapsedNavItems = navItems.slice(visibleItems);
return ( return (
<Router> <Router future={{v7_startTransition: true, v7_relativeSplatPath: true}}>
<div className="app"> <div className="app">
<nav className="nav"> <nav className="nav">
<div className="nav-content"> <div className="nav-content">
+41
View File
@@ -0,0 +1,41 @@
/**
* 使用Chrome扩展API复制文本到剪贴板
* @param text 复制文本
* @returns
* - false: 复制失败
* - true: 复制成功
*/
export const copyToClipboard = async (text: string) => {
console.log('尝试复制文本:', text);
if (chrome && chrome.runtime && chrome.runtime.sendMessage) {
console.log('使用Chrome扩展API复制');
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage(
{
action: 'copy',
text: text,
},
(response) => {
console.log('收到background响应:', response, 'lastError:', chrome.runtime.lastError);
// 检查是否有运行时错误
if (chrome.runtime.lastError) {
console.error('Chrome运行时错误:', chrome.runtime.lastError.message);
return reject(false);
}
// 检查响应
if (response && response.success) {
console.log('通过background复制成功');
return resolve(true);
} else {
console.error('通过background复制失败:', response?.error);
return reject(false);
}
}
);
});
}
return Promise.reject(false);
};