Initial project baseline
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { useRef, useEffect, useState } from 'react';
|
||||
import { NameLocation, JobResult } from '../types';
|
||||
import { IconCloudy } from './Icons';
|
||||
|
||||
interface CanvasAreaProps {
|
||||
maskFile: File | null;
|
||||
jobResult: JobResult | null;
|
||||
apiBase: string;
|
||||
viewMode: '2d' | '3d';
|
||||
zoom: number;
|
||||
highlightLocation: NameLocation | null;
|
||||
}
|
||||
|
||||
export default function CanvasArea({
|
||||
maskFile, jobResult, apiBase, viewMode, zoom, highlightLocation
|
||||
}: CanvasAreaProps) {
|
||||
const [maskPreviewUrl, setMaskPreviewUrl] = useState<string | null>(null);
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!maskFile) { setMaskPreviewUrl(null); return; }
|
||||
const url = URL.createObjectURL(maskFile);
|
||||
setMaskPreviewUrl(url);
|
||||
return () => URL.revokeObjectURL(url);
|
||||
}, [maskFile]);
|
||||
|
||||
// 图片 URL 处理:
|
||||
// - 若已是完整 http URL 直接使用
|
||||
// - 若是 /api/... 路径则拼接 apiBase
|
||||
// - 否则回退到 /api/jobs/{id}/files/png
|
||||
const resolveImageUrl = (): string | null => {
|
||||
if (!jobResult) return maskPreviewUrl;
|
||||
const raw = jobResult.image_url;
|
||||
if (!raw || !jobResult.job_id) return maskPreviewUrl;
|
||||
if (raw.startsWith('http')) return raw;
|
||||
return `${apiBase}${raw}`;
|
||||
};
|
||||
|
||||
const displayUrl = resolveImageUrl();
|
||||
const isEmpty = !displayUrl;
|
||||
|
||||
return (
|
||||
<div className="canvas-inner" ref={wrapperRef}>
|
||||
{isEmpty ? (
|
||||
<div className="canvas-placeholder">
|
||||
<div className="canvas-placeholder-icon"><IconCloudy /></div>
|
||||
<div className="canvas-placeholder-text">导入底图或名单后可预览词云</div>
|
||||
</div>
|
||||
) : viewMode === '3d' ? (
|
||||
<div className="view-3d-container">
|
||||
<img src={displayUrl} alt="wordcloud 3D" className="view-3d-image" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="canvas-image-wrapper">
|
||||
<img
|
||||
src={displayUrl}
|
||||
alt="wordcloud"
|
||||
className="canvas-image"
|
||||
style={{ transform: `scale(${zoom})` }}
|
||||
/>
|
||||
{highlightLocation && jobResult && (
|
||||
<HighlightBox location={highlightLocation} zoom={zoom} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HighlightBox({ location, zoom }: { location: NameLocation; zoom: number }) {
|
||||
const x = location.box_x ?? location.x;
|
||||
const y = location.box_y ?? location.y;
|
||||
const width = location.box_width ?? location.width ?? location.font_size ?? 24;
|
||||
const height = location.box_height ?? location.height ?? location.font_size ?? 24;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="highlight-box"
|
||||
style={{
|
||||
left: x * zoom,
|
||||
top: y * zoom,
|
||||
width: width * zoom,
|
||||
height: height * zoom,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user