修复编译问题

This commit is contained in:
北枳 2025-07-04 17:06:26 +08:00
parent 94acd18075
commit 7603a1a26e
7 changed files with 478 additions and 356 deletions

View File

@ -2,7 +2,21 @@
import { useState } from 'react';
import { motion } from 'framer-motion';
import { DragDropContext, DropResult } from 'react-beautiful-dnd';
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import type { DragEndEvent } from '@dnd-kit/core';
import { ScriptMetaInfo } from '../script-overview/script-meta-info';
import { SceneFilmstrip } from '../script-overview/scene-filmstrip';
import { SceneCardList } from '../script-overview/scene-card-list';
@ -30,6 +44,14 @@ export interface ScriptMeta {
}
export default function ScriptOverview() {
// Configure drag sensors
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
);
// Example Data
const [scriptMeta] = useState<ScriptMeta>({
title: "Lost Stars",
@ -164,14 +186,21 @@ export default function ScriptOverview() {
const [selectedSceneId, setSelectedSceneId] = useState<string>();
// Handle scene drag and drop sorting
const handleDragEnd = (result: DropResult) => {
if (!result.destination) return;
const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event;
const items = Array.from(scenes);
const [reorderedItem] = items.splice(result.source.index, 1);
items.splice(result.destination.index, 0, reorderedItem);
if (active.id === over?.id) {
return;
}
setScenes(items);
const oldIndex = scenes.findIndex(scene => scene.id === active.id);
const newIndex = scenes.findIndex(scene => scene.id === over?.id);
if (oldIndex === -1 || newIndex === -1) {
return;
}
setScenes(arrayMove(scenes, oldIndex, newIndex));
};
// Handle scene updates
@ -215,7 +244,15 @@ export default function ScriptOverview() {
<div className="flex-grow min-w-0 h-full overflow-hidden flex flex-col">
{/* Scene Card List */}
<div className="flex-grow overflow-y-auto px-8">
<DragDropContext onDragEnd={handleDragEnd}>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext
items={scenes.map(scene => scene.id)}
strategy={verticalListSortingStrategy}
>
<SceneCardList
scenes={scenes}
selectedSceneId={selectedSceneId}
@ -223,7 +260,8 @@ export default function ScriptOverview() {
onSceneDelete={handleSceneDelete}
onSceneDuplicate={handleSceneDuplicate}
/>
</DragDropContext>
</SortableContext>
</DndContext>
</div>
{/* Filmstrip Preview */}

View File

@ -2,7 +2,21 @@
import { useState } from 'react';
import { motion } from 'framer-motion';
import { DragDropContext, DropResult } from 'react-beautiful-dnd';
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import type { DragEndEvent } from '@dnd-kit/core';
import { ScriptMetaInfo } from '../script-overview/script-meta-info';
import { SceneFilmstrip } from '../script-overview/scene-filmstrip';
import { StoryboardCardList } from '../storyboard/storyboard-card-list';
@ -145,14 +159,21 @@ export default function StoryboardView() {
const [selectedSceneId, setSelectedSceneId] = useState<string>();
// 处理场景拖拽排序
const handleDragEnd = (result: DropResult) => {
if (!result.destination) return;
const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event;
const items = Array.from(scenes);
const [reorderedItem] = items.splice(result.source.index, 1);
items.splice(result.destination.index, 0, reorderedItem);
if (active.id === over?.id) {
return;
}
setScenes(items);
const oldIndex = scenes.findIndex(scene => scene.id === active.id);
const newIndex = scenes.findIndex(scene => scene.id === over?.id);
if (oldIndex === -1 || newIndex === -1) {
return;
}
setScenes(arrayMove(scenes, oldIndex, newIndex));
};
// 处理场景更新
@ -209,7 +230,20 @@ export default function StoryboardView() {
<div className="flex-grow min-w-0 h-full overflow-hidden flex flex-col">
{/* Scene Card List */}
<div className="flex-grow overflow-y-auto">
<DragDropContext onDragEnd={handleDragEnd}>
<DndContext
sensors={useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
)}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext
items={scenes.map(scene => scene.id)}
strategy={verticalListSortingStrategy}
>
<StoryboardCardList
scenes={scenes}
selectedSceneId={selectedSceneId}
@ -217,7 +251,8 @@ export default function StoryboardView() {
onSceneDelete={handleSceneDelete}
onSceneDuplicate={handleSceneDuplicate}
/>
</DragDropContext>
</SortableContext>
</DndContext>
</div>
{/* Filmstrip Preview */}

View File

@ -1,4 +1,3 @@
import { Droppable } from 'react-beautiful-dnd';
import { Scene } from '../pages/script-overview';
import { SceneCard } from './scene-card';
@ -18,17 +17,7 @@ export function SceneCardList({
onSceneDuplicate
}: SceneCardListProps) {
return (
<Droppable droppableId="scenes" direction="horizontal">
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className={`
flex gap-6 overflow-x-auto hide-scrollbar h-full overflow-y-hidden
${snapshot.isDraggingOver ? 'bg-white/5' : ''}
transition-colors duration-300 rounded-xl p-2
`}
>
<div className="flex gap-6 overflow-x-auto hide-scrollbar h-full overflow-y-hidden transition-colors duration-300 rounded-xl p-2">
{scenes.map((scene, index) => (
<SceneCard
key={scene.id}
@ -40,9 +29,6 @@ export function SceneCardList({
onDuplicate={() => onSceneDuplicate(scene.id)}
/>
))}
{provided.placeholder}
</div>
)}
</Droppable>
);
}

View File

@ -1,6 +1,7 @@
import { useState, useRef, useEffect } from 'react';
import { motion } from 'framer-motion';
import { Draggable } from 'react-beautiful-dnd';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { Trash2, Copy, RefreshCw, GripVertical } from 'lucide-react';
import { Scene } from '../pages/script-overview';
import Image from 'next/image';
@ -29,6 +30,20 @@ export function SceneCard({
const cardRef = useRef<HTMLDivElement>(null);
const timeoutRef = useRef<NodeJS.Timeout>();
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id: scene.id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
// 处理自动保存
const handleContentChange = (
field: keyof Scene,
@ -67,12 +82,10 @@ export function SceneCard({
}, [isSelected]);
return (
<Draggable draggableId={scene.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={setNodeRef}
style={style}
{...attributes}
>
<motion.div
id={`scene-card-${scene.id}`}
@ -80,9 +93,9 @@ export function SceneCard({
animate={{
opacity: 1,
scale: 1,
x: snapshot.isDragging ? 5 : 0,
y: snapshot.isDragging ? 5 : 0,
rotate: snapshot.isDragging ? 2 : 0
x: isDragging ? 5 : 0,
y: isDragging ? 5 : 0,
rotate: isDragging ? 2 : 0
}}
transition={{
type: "spring",
@ -92,7 +105,7 @@ export function SceneCard({
className={`
relative flex-shrink-0 w-[400px] bg-white/5 backdrop-blur-sm rounded-xl overflow-hidden h-full
flex flex-col group cursor-grab active:cursor-grabbing
${snapshot.isDragging ? 'ring-2 ring-blue-500/50 shadow-lg z-50' : ''}
${isDragging ? 'ring-2 ring-blue-500/50 shadow-lg z-50' : ''}
${isEditing ? 'ring-2 ring-yellow-500/50' : ''}
${isSelected ? 'ring-2 ring-purple-500/50' : ''}
transition-all duration-300
@ -103,6 +116,14 @@ export function SceneCard({
setShowDeleteConfirm(false);
}}
>
{/* Drag Handle */}
<div
{...listeners}
className="absolute top-2 right-2 z-10 p-2 rounded-lg bg-black/20 opacity-0 group-hover:opacity-100 transition-opacity cursor-grab active:cursor-grabbing"
>
<GripVertical className="w-4 h-4 text-white/60" />
</div>
{/* Scene Image */}
<div className="relative w-full h-[200px] flex-shrink-0">
<Image
@ -304,7 +325,5 @@ export function SceneCard({
</motion.div>
</motion.div>
</div>
)}
</Draggable>
);
}

View File

@ -1,4 +1,3 @@
import { Droppable } from 'react-beautiful-dnd';
import { StoryboardScene } from '../pages/storyboard-view';
import { StoryboardCard } from './storyboard-card';
@ -18,27 +17,18 @@ export function StoryboardCardList({
onSceneDuplicate
}: StoryboardCardListProps) {
return (
<Droppable droppableId="storyboard-scenes">
{(provided) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className="flex gap-6 overflow-x-auto hide-scrollbar h-full overflow-y-hidden p-8"
>
<div className="flex gap-6 overflow-x-auto hide-scrollbar h-full overflow-y-hidden transition-colors duration-300 rounded-xl p-2">
{scenes.map((scene, index) => (
<StoryboardCard
key={scene.id}
scene={scene}
index={index}
isSelected={selectedSceneId === scene.id}
isSelected={scene.id === selectedSceneId}
onUpdate={(updates) => onSceneUpdate(scene.id, updates)}
onDelete={() => onSceneDelete(scene.id)}
onDuplicate={() => onSceneDuplicate(scene.id)}
/>
))}
{provided.placeholder}
</div>
)}
</Droppable>
);
}

View File

@ -1,7 +1,8 @@
import { useState, useRef, useEffect } from 'react';
import { motion } from 'framer-motion';
import { Draggable } from 'react-beautiful-dnd';
import { Trash2, Copy, RefreshCw } from 'lucide-react';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { Trash2, Copy, RefreshCw, GripVertical } from 'lucide-react';
import { StoryboardScene } from '../pages/storyboard-view';
import Image from 'next/image';
@ -29,6 +30,20 @@ export function StoryboardCard({
const cardRef = useRef<HTMLDivElement>(null);
const timeoutRef = useRef<NodeJS.Timeout>();
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id: scene.id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
// 处理自动保存
const handleContentChange = (
field: keyof StoryboardScene,
@ -67,32 +82,13 @@ export function StoryboardCard({
}, [isSelected]);
return (
<Draggable draggableId={scene.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<motion.div
id={`scene-card-${scene.id}`}
initial={{ opacity: 0, scale: 0.8 }}
animate={{
opacity: 1,
scale: 1,
x: snapshot.isDragging ? 5 : 0,
y: snapshot.isDragging ? 5 : 0,
rotate: snapshot.isDragging ? 2 : 0
}}
transition={{
type: "spring",
stiffness: 200,
damping: 20
}}
ref={setNodeRef}
style={style}
className={`
relative flex-shrink-0 w-[400px] bg-white/5 backdrop-blur-sm rounded-xl overflow-hidden h-full
flex flex-col group cursor-grab active:cursor-grabbing
${snapshot.isDragging ? 'ring-2 ring-blue-500/50 shadow-lg z-50' : ''}
${isDragging ? 'ring-2 ring-blue-500/50 shadow-lg z-50' : ''}
${isEditing ? 'ring-2 ring-yellow-500/50' : ''}
${isSelected ? 'ring-2 ring-purple-500/50' : ''}
transition-all duration-300
@ -103,6 +99,44 @@ export function StoryboardCard({
setShowDeleteConfirm(false);
}}
>
<motion.div
id={`scene-card-${scene.id}`}
initial={{ opacity: 0, scale: 0.8 }}
animate={{
opacity: 1,
scale: 1,
x: isDragging ? 5 : 0,
y: isDragging ? 5 : 0,
rotate: isDragging ? 2 : 0
}}
transition={{
type: "spring",
stiffness: 200,
damping: 20
}}
className={`
relative flex-shrink-0 w-[400px] bg-white/5 backdrop-blur-sm rounded-xl overflow-hidden h-full
flex flex-col group cursor-grab active:cursor-grabbing
${isDragging ? 'ring-2 ring-blue-500/50 shadow-lg z-50' : ''}
${isEditing ? 'ring-2 ring-yellow-500/50' : ''}
${isSelected ? 'ring-2 ring-purple-500/50' : ''}
transition-all duration-300
`}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => {
setIsHovered(false);
setShowDeleteConfirm(false);
}}
>
{/* Drag Handle */}
<div
{...listeners}
{...attributes}
className="absolute top-2 right-2 z-10 p-2 rounded-lg bg-black/20 opacity-0 group-hover:opacity-100 transition-opacity cursor-grab active:cursor-grabbing"
>
<GripVertical className="w-4 h-4 text-white/60" />
</div>
{/* Scene Image */}
<div className="relative w-full h-[200px] flex-shrink-0">
<Image
@ -304,7 +338,5 @@ export function StoryboardCard({
</motion.div>
</motion.div>
</div>
)}
</Draggable>
);
}

View File

@ -6,12 +6,28 @@ import {
} from './constants';
// 当前选择的mock数据
let selectedMockData = getRandomMockData();
let selectedMockData: any = null;
// 加载mock数据的辅助函数
const loadMockData = async () => {
if (!selectedMockData) {
try {
selectedMockData = await getRandomMockData();
} catch (error) {
// 如果API失败使用本地fallback数据
const { MOCK_DATA } = await import('./constants');
const randomIndex = Math.floor(Math.random() * MOCK_DATA.length);
selectedMockData = MOCK_DATA[randomIndex];
console.log('使用本地fallback数据:', selectedMockData);
}
}
return selectedMockData;
};
// 模拟接口请求 获取任务详情
export const getTaskDetail = async (taskId: string): Promise<TaskObject> => {
// 每次获取任务详情时重新随机选择数据
selectedMockData = getRandomMockData();
// 确保已经加载了数据
await loadMockData();
const data: TaskObject = {
projectId: selectedMockData.detail.projectId,
@ -32,6 +48,9 @@ export const getTaskSketch = async (
taskId: string,
onProgress: (sketch: SketchItem, index: number) => void
): Promise<void> => {
// 确保已经加载了数据
await loadMockData();
const sketchData = selectedMockData.sketch;
const totalSketches = sketchData.length;
@ -71,6 +90,9 @@ export const getTaskVideo = async (
sketchCount: number,
onProgress: (video: VideoItem, index: number) => void
): Promise<void> => {
// 确保已经加载了数据
await loadMockData();
const videoData = selectedMockData.video;
const totalVideos = videoData.length;