Files
testing-tool/src/pages/QrCode/components/ParsePanel.tsx
T
雨霖铃 d38bafe237 refactor: 统一中文文案并简化组件结构,提升代码可读性
移除 chrome.i18n 后,将各功能页面、布局与工具模块的 UI 文案改为内联中文;
删除冗余注释与过度抽象(如 ToolCard、StatCard),精简 props 与状态管理;
同步优化 JsonTools、StorageCleaner、Timestamp、TestDataGenerator、Dashboard、
Base64Converter、RightClickRestorer、TextStatistics、TopBar、RouterProvider、
ThemeModeProvider 及生成器库与 utils 工具文件。
2026-06-27 17:26:19 +08:00

126 lines
4.3 KiB
TypeScript

import { useCallback, useEffect } from 'react';
import { RefreshCw } from 'lucide-react';
import { toast } from 'sonner';
import TextInputArea from '@/components/TextInputArea';
import ImageUploader from './ImageUploader';
import { useQrCodeContext } from '../contexts/QrCodeContext';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { cn } from '@/lib/utils';
export default function ParsePanel() {
const { parserState, setParserState, handleFileChange, handleClearFile } = useQrCodeContext();
const hasFile = parserState.selectedFile !== null;
const handlePaste = useCallback(
async (e: ClipboardEvent) => {
const items = e.clipboardData?.items;
if (!items) return;
for (let i = 0; i < items.length; i++) {
if (items[i].type.startsWith('image/')) {
e.preventDefault();
const file = items[i].getAsFile();
if (file) {
handleFileChange(file);
toast.success('图片粘贴成功,正在解析...');
}
return;
}
}
const text = e.clipboardData?.getData('text/plain');
if (text && text.startsWith('data:image/')) {
e.preventDefault();
try {
const response = await fetch(text);
const blob = await response.blob();
const file = new File([blob], 'pasted-image.png', { type: blob.type });
handleFileChange(file);
toast.success('图片粘贴成功,正在解析...');
} catch (error) {
console.error('处理 Base64 图片失败:', error);
toast.error('粘贴图片失败,请重试');
}
}
},
[handleFileChange],
);
useEffect(() => {
document.addEventListener('paste', handlePaste);
return () => {
document.removeEventListener('paste', handlePaste);
};
}, [handlePaste]);
if (!hasFile) {
return (
<div className="w-full select-none p-0.5">
<ImageUploader
selectedFile={parserState.selectedFile}
onFileChange={handleFileChange}
onClearFile={handleClearFile}
previewUrl={parserState.previewUrl}
dragging={parserState.dragging}
onDraggingChange={(dragging) => setParserState((prev) => ({ ...prev, dragging }))}
/>
</div>
);
}
return (
<div className="w-full select-none p-0.5 flex flex-col">
<div className="border border-border rounded-xl bg-card text-card-foreground shadow-sm p-3 mb-3">
<div className="flex items-center justify-between mb-1.5">
<Label className="text-[10px] font-bold text-muted-foreground/90 uppercase tracking-wider select-none pl-0.5">
已上传图片
</Label>
<Button variant="ghost" size="sm" onClick={handleClearFile} className="h-6 px-2 text-xs">
<RefreshCw className="w-3 h-3 mr-1" />
重新上传
</Button>
</div>
<div className="flex items-center gap-3 bg-muted/50 rounded-md p-2">
<img
src={parserState.previewUrl}
alt="Uploaded QR Code"
className="w-16 h-16 object-contain rounded"
/>
<div className="flex-1 min-w-0">
<p className="text-sm text-foreground truncate">{parserState.selectedFile?.name}</p>
{parserState.parsing && (
<p className="text-xs text-primary animate-pulse mt-1">解析中...</p>
)}
</div>
</div>
</div>
<div
className={cn(
'border border-border rounded-xl bg-card text-card-foreground shadow-sm flex flex-col p-4',
'focus-within:ring-1 focus-within:ring-ring focus-within:border-ring',
)}
>
<div className="flex flex-col space-y-2.5">
<Label className="text-[10px] font-bold text-muted-foreground/90 uppercase tracking-wider pl-0.5">
解析结果
</Label>
<TextInputArea
value={parserState.decodedResult}
readOnly={true}
showClear={false}
allowCopy={true}
placeholder={parserState.parsing ? '' : '解析结果将显示在此处'}
minRows={4}
maxRows={8}
externalError={parserState.parseError || undefined}
/>
</div>
</div>
</div>
);
}