feat: 添加 husky 和 lint-staged 支持,配置 prettier 进行代码格式化

This commit is contained in:
雨霖铃
2026-01-28 20:26:48 +08:00
parent 4edb2f1a47
commit 710bffd2f8
10 changed files with 338 additions and 112 deletions
+32 -46
View File
@@ -1,5 +1,4 @@
import { useState, useCallback } from 'react';
import { copyToClipboard } from '@/utils/chromeUtils';
import { useEffect, useState, useCallback } from 'react';
const CopyButton = ({
text = '要复制的文本',
@@ -7,14 +6,13 @@ const CopyButton = ({
className = 'action-btn',
successMessage = '复制成功!',
errorMessage = '复制失败,请手动复制。',
onCopyOverride = null,
}) => {
const [status, setStatus] = useState('idle'); // 'idle' | 'copying' | 'success' | 'error'
useEffect(() => {
let timer;
let timer: number;
if (status === 'success' || status === 'error') {
timer = setTimeout(() => {
timer = window.setTimeout(() => {
setStatus('idle');
}, 2000);
}
@@ -30,29 +28,15 @@ const CopyButton = ({
const safeText = String(text);
// 优先使用传入的复制函数
if (onCopyOverride) {
return await onCopyOverride(safeText);
if (navigator.clipboard && navigator.clipboard.writeText) {
try {
await navigator.clipboard.writeText(safeText);
return true;
} catch (err) {
console.error('使用 Clipboard API 复制失败:', err);
return false;
}
}
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 () => {
@@ -65,35 +49,37 @@ const CopyButton = ({
console.error('复制时出错:', error);
setStatus('error');
}
}, [text, onCopyOverride]);
}, [text]);
// 根据状态计算当前显示的文本
const currentText = status === 'success' ? successMessage
: status === 'error' ? errorMessage
: buttonText;
const currentText =
status === 'success' ? successMessage : status === 'error' ? errorMessage : buttonText;
// 根据状态计算样式 (建议使用 CSS Module 或 Tailwind,这里为了演示保留内联)
const getBackgroundColor = () => {
switch (status) {
case 'success': return '#4CAF50';
case 'error': return '#f44336';
default: return '#4CAF50'; // 让 CSS 类控制默认颜色
// 动态样式:只在非默认状态下覆盖颜色,平时让 className 控制
const getStyle = () => {
const baseStyle = {
transition: 'all 0.3s ease',
cursor: 'pointer', // 确保有手型光标
// 这里去掉了 padding/border/radius 的硬编码,建议在 CSS 类中定义
// 除非你想强制覆盖
};
if (status === 'success') {
return { ...baseStyle, backgroundColor: '#4CAF50', color: 'white' };
}
if (status === 'error') {
return { ...baseStyle, backgroundColor: '#f44336', color: 'white' };
}
return baseStyle;
};
return (
<button
onClick={handleClick}
className={`${className} ${status} copy-button`}
style={{
backgroundColor: getBackgroundColor(),
color: status !== 'idle' ? 'white' : undefined,
transition: 'all 0.3s ease',
padding: '8px 16px',
border: 'none',
borderRadius: '8px',
cursor: 'pointer',
}}
style={getStyle()}
disabled={status === 'copying'}
>
{currentText}
</button>