- web/: React + Vite + TypeScript 前端 - backend/: Django + DRF + SimpleJWT 后端 - prototype/: HTML 设计原型 - docs/: PRD 和设计评审文档 - test: 单元测试 + E2E 极限测试
94 lines
2.8 KiB
TypeScript
94 lines
2.8 KiB
TypeScript
import { useRef, useCallback, type DragEvent } from 'react';
|
|
import { useInputBarStore } from '../store/inputBar';
|
|
import { UniversalUpload } from './UniversalUpload';
|
|
import { KeyframeUpload } from './KeyframeUpload';
|
|
import { PromptInput } from './PromptInput';
|
|
import { Toolbar } from './Toolbar';
|
|
import { showToast } from './Toast';
|
|
import styles from './InputBar.module.css';
|
|
|
|
export function InputBar() {
|
|
const mode = useInputBarStore((s) => s.mode);
|
|
const addReferences = useInputBarStore((s) => s.addReferences);
|
|
const setFirstFrame = useInputBarStore((s) => s.setFirstFrame);
|
|
const references = useInputBarStore((s) => s.references);
|
|
const barRef = useRef<HTMLDivElement>(null);
|
|
|
|
const handleDragOver = useCallback((e: DragEvent) => {
|
|
e.preventDefault();
|
|
if (barRef.current) {
|
|
barRef.current.style.borderColor = '#00b8e6';
|
|
}
|
|
}, []);
|
|
|
|
const handleDragLeave = useCallback(() => {
|
|
if (barRef.current) {
|
|
barRef.current.style.borderColor = '#2a2a38';
|
|
}
|
|
}, []);
|
|
|
|
const handleDrop = useCallback((e: DragEvent) => {
|
|
e.preventDefault();
|
|
if (barRef.current) {
|
|
barRef.current.style.borderColor = '#2a2a38';
|
|
}
|
|
const IMAGE_MAX = 20 * 1024 * 1024;
|
|
const VIDEO_MAX = 100 * 1024 * 1024;
|
|
const files = Array.from(e.dataTransfer.files).filter(
|
|
(f) => f.type.startsWith('image/') || f.type.startsWith('video/')
|
|
);
|
|
if (!files.length) return;
|
|
|
|
const valid: File[] = [];
|
|
for (const f of files) {
|
|
const limit = f.type.startsWith('video/') ? VIDEO_MAX : IMAGE_MAX;
|
|
if (f.size > limit) {
|
|
showToast(f.type.startsWith('video/') ? '视频文件不能超过100MB' : '图片文件不能超过20MB');
|
|
} else {
|
|
valid.push(f);
|
|
}
|
|
}
|
|
if (!valid.length) return;
|
|
|
|
if (mode === 'universal') {
|
|
const remaining = 5 - references.length;
|
|
if (remaining <= 0) {
|
|
showToast('最多上传5张参考内容');
|
|
return;
|
|
}
|
|
addReferences(valid);
|
|
if (valid.length > remaining) {
|
|
showToast('最多上传5张参考内容');
|
|
}
|
|
} else {
|
|
setFirstFrame(valid[0]);
|
|
}
|
|
}, [mode, references.length, addReferences, setFirstFrame]);
|
|
|
|
return (
|
|
<div className={styles.wrapper}>
|
|
<div className={styles.container}>
|
|
<div
|
|
ref={barRef}
|
|
className={styles.bar}
|
|
onDragOver={handleDragOver}
|
|
onDragLeave={handleDragLeave}
|
|
onDrop={handleDrop}
|
|
>
|
|
{/* Upper area: Upload + Prompt */}
|
|
<div className={styles.inputArea}>
|
|
{mode === 'universal' ? <UniversalUpload /> : <KeyframeUpload />}
|
|
<PromptInput />
|
|
</div>
|
|
|
|
{/* Divider */}
|
|
<div className={styles.divider} />
|
|
|
|
{/* Toolbar */}
|
|
<Toolbar />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|