refactor: 重构 QrCodePreview 组件,继承 HTMLDiv 属性并统一 shadcn 样式

- 组件改为继承 `React.HTMLAttributes<HTMLDivElement>`,支持外部传入 className 和事件
- 使用 `cn()` 函数合并外部类名
- 空白状态改用 `border border-dashed` 和更浅的 `bg-muted/40` 背景
- 正常状态改为纯白背景柔边圆角容器包裹二维码,确保暗黑模式下黑白对比度
- 下载/复制按钮完全对齐 shadcn 的 Outline 与 Default 样式规范
- 更新测试用例中的 alt 文本为 "QR Code Preview"
This commit is contained in:
雨霖铃
2026-05-22 20:48:14 +08:00
parent 1bfb7d4e6d
commit da667ccb13
2 changed files with 54 additions and 18 deletions
+52 -16
View File
@@ -1,7 +1,10 @@
import { Download, Copy } from 'lucide-react';
import React from 'react';
import { Copy, Download } from 'lucide-react';
import { useLazyTranslation } from '@/utils/useLazyTranslation';
import { cn } from '@/lib/utils'; // 1. 引入标准的 shadcn 工具函数
interface QrCodePreviewProps {
// 继承原生 HTML Div 属性,方便外部无缝扩充类名或监听事件
interface QrCodePreviewProps extends React.HTMLAttributes<HTMLDivElement> {
/** 二维码 Data URL */
qrCodeDataUrl: string;
/** 下载回调 */
@@ -10,39 +13,72 @@ interface QrCodePreviewProps {
onCopy: () => void;
}
const QrCodePreview = ({ qrCodeDataUrl, onDownload, onCopy }: QrCodePreviewProps) => {
const QrCodePreview = ({
qrCodeDataUrl,
onDownload,
onCopy,
className,
...props
}: QrCodePreviewProps) => {
const { t } = useLazyTranslation('qrCode');
// 空状态下的虚线骨架屏
if (!qrCodeDataUrl) {
return (
<div className="flex flex-col justify-center items-center min-h-[200px] border-2 border-dashed border-input rounded-xl p-4 bg-muted">
<span className="text-sm text-muted-foreground text-center">
{t('qrCode:qrCodeWillShow')}
</span>
<div
className={cn(
'flex flex-col justify-center items-center min-h-[200px] border border-dashed border-input rounded-xl p-4 bg-muted/40',
className,
)}
{...props}
>
<p className="text-sm text-muted-foreground text-center">{t('qrCode:qrCodeWillShow')}</p>
</div>
);
}
return (
<div className="flex flex-col justify-center items-center min-h-[200px] border-2 border-dashed border-input rounded-xl p-4 bg-muted">
<div className="flex flex-col items-center w-full">
<img src={qrCodeDataUrl} alt="QR Code" className="w-[250px] h-[250px] block" />
<div className="flex gap-2 mt-4">
<div
className={cn(
'flex flex-col justify-center items-center min-h-[200px] border border-input rounded-xl p-6 bg-muted/40',
className,
)}
{...props}
>
<div className="flex flex-col items-center w-full max-w-xs">
{/*
2. 二维码容器适配:
在暗黑模式下,纯黑白的二维码如果直接暴露在暗色背景下,会导致手机摄像头极难识别。
通过裹一层 bg-white 和 p-3,确保黑白对比度绝对安全,同时加入 shadow 增强卡片感。
*/}
<div className="p-3 bg-white rounded-lg shadow-sm border border-border/40">
<img
src={qrCodeDataUrl}
alt="QR Code Preview"
className="w-56 h-56 max-w-full object-contain block animate-in fade-in duration-300"
/>
</div>
{/* 3. 按钮群全面向 shadcn 官方 Button 视觉规范对齐 */}
<div className="flex w-full gap-2 mt-5">
{/* 下载按钮:使用标准的次要按钮风格 (Outline) */}
<button
type="button"
onClick={onDownload}
className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-green-600 bg-background border border-green-600 rounded-lg hover:bg-green-500/10 transition-colors"
className="flex-1 inline-flex h-9 items-center justify-center gap-2 px-3 text-sm font-medium rounded-md border border-input bg-background shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
>
<Download className="w-4 h-4" />
{t('qrCode:downloadButton')}
<Download className="w-4 h-4 text-muted-foreground" />
<span className="truncate">{t('qrCode:downloadButton')}</span>
</button>
{/* 复制按钮:使用标准的主要行动按钮风格 (Default) */}
<button
type="button"
onClick={onCopy}
className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-green-600 rounded-lg hover:bg-green-700 transition-colors"
className="flex-1 inline-flex h-9 items-center justify-center gap-2 px-3 text-sm font-medium rounded-md bg-primary text-primary-foreground shadow transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
>
<Copy className="w-4 h-4" />
{t('qrCode:copyQrButton')}
<span className="truncate">{t('qrCode:copyQrButton')}</span>
</button>
</div>
</div>