Files
Cyrene_v2/frontend/web/src/modes/VisionMode.tsx
T

83 lines
2.6 KiB
TypeScript

import { useRef, useState } from "react";
import { Button, Surface, EmptyState, Tag } from "../components/primitives";
export function VisionMode(): React.ReactNode {
const [preview, setPreview] = useState<string | null>(null);
const [analyzing, setAnalyzing] = useState(false);
const [tags, setTags] = useState<string[]>([]);
const fileRef = useRef<HTMLInputElement>(null);
const onFile = (file: File | undefined) => {
if (!file) return;
setPreview(URL.createObjectURL(file));
setTags([]);
};
const analyze = () => {
if (!preview) return;
setAnalyzing(true);
// 占位分析:真实实现会调用视觉模型,此处演示加载态并给出结果。
window.setTimeout(() => {
setAnalyzing(false);
setTags(["图像", "占位分析", "视觉模型待接入"]);
}, 900);
};
return (
<div className="mx-auto max-w-3xl space-y-5 p-4">
<div>
<h2 className="text-lg font-semibold">视觉</h2>
<p className="text-sm text-[var(--mu-text-lo)]">图像分析</p>
</div>
<input
ref={fileRef}
type="file"
accept="image/*"
className="hidden"
onChange={(e) => onFile(e.target.files?.[0])}
/>
<button
onClick={() => fileRef.current?.click()}
className="flex min-h-[16rem] w-full flex-col items-center justify-center gap-3 rounded-[var(--mu-radius-lg)] border border-dashed border-[var(--mu-glass-border)] bg-[var(--mu-surface-2)]/40 p-4"
>
{preview ? (
<img src={preview} alt="预览" className="max-h-64 rounded-[var(--mu-radius)] object-contain" />
) : (
<EmptyState icon="👁" title="点击上传图片" hint="支持拖拽/点选,占位实现,视觉模型待接入" />
)}
</button>
<div className="flex items-center gap-2">
<Button variant="primary" onClick={analyze} disabled={!preview || analyzing}>
{analyzing ? "分析中…" : "开始分析"}
</Button>
{preview ? (
<Button
variant="ghost"
onClick={() => {
if (preview) URL.revokeObjectURL(preview);
setPreview(null);
setTags([]);
}}
>
清除
</Button>
) : null}
</div>
{tags.length > 0 ? (
<Surface className="flex flex-wrap items-center gap-2">
<span className="text-xs text-[var(--mu-text-lo)]">识别结果</span>
{tags.map((t) => (
<Tag key={t} tone="accent">
{t}
</Tag>
))}
</Surface>
) : null}
</div>
);
}