/** * 项目列表页面 - 采用卡片网格布局 * * 设计理念: * - 不使用传统列表,而是卡片网格展示 * - 每个项目卡片显示项目状态和进度 * - 支持快速操作:继续编辑、查看详情、删除 */ import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import { Button, Space, Tag, Card, message, Empty, Row, Col, Progress, Tooltip, Badge } from 'antd' import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined, ClockCircleOutlined, CheckCircleOutlined, LoadingOutlined, FileTextOutlined, SettingOutlined } from '@ant-design/icons' import { useProjectStore } from '@/stores/projectStore' import { SeriesProject } from '@/services/projectService' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import 'dayjs/locale/zh-cn' dayjs.extend(relativeTime) dayjs.locale('zh-cn') type ProjectStatus = 'draft' | 'in_progress' | 'completed' // 项目状态配置 const STATUS_CONFIG: Record = { draft: { text: '草稿', color: 'default', icon: }, in_progress: { text: '创作中', color: 'processing', icon: }, completed: { text: '已完成', color: 'success', icon: } } // 计算项目完成度 const calculateCompletion = (project: SeriesProject): number => { let completed = 0 let total = 3 // 检查世界观设定 if (project.globalContext?.worldSetting) completed += 1 // 检查人物设定 if (project.globalContext?.characterProfiles && Object.keys(project.globalContext.characterProfiles).length > 0) { completed += 1 } // 检查大纲 if (project.globalContext?.overallOutline) completed += 1 // 检查剧集完成度 if (project.totalEpisodes && project.episodes) { total = 3 + project.totalEpisodes completed += project.episodes.length } return Math.min(100, Math.round((completed / total) * 100)) } // 获取项目状态 const getProjectStatus = (project: SeriesProject): ProjectStatus => { const completion = calculateCompletion(project) if (completion === 0) return 'draft' if (completion >= 100) return 'completed' return 'in_progress' } // 项目卡片组件 const ProjectCard = ({ project, onEdit, onDelete, onView }: { project: SeriesProject onEdit: (id: string) => void onDelete: (id: string) => void onView: (id: string) => void }) => { const status = getProjectStatus(project) const statusConfig = STATUS_CONFIG[status] const completion = calculateCompletion(project) return ( {/* 项目标题 */}

{project.name}

}> {project.totalEpisodes || 0} 集 {statusConfig.text}
{/* 项目描述/内容预览 */}
{project.globalContext?.overallOutline ? (
{project.globalContext.overallOutline}
) : project.globalContext?.worldSetting ? (
{project.globalContext.worldSetting}
) : (
暂无内容描述
)}
{/* 进度条 */}
完成度 {completion}%
{/* 时间信息 */}
{dayjs(project.createdAt).fromNow()}
{/* 操作按钮 */}
{status === 'draft' || status === 'in_progress' ? ( ) : ( )}
) } export const ProjectList = () => { const navigate = useNavigate() const { projects, loading, fetchProjects, deleteProject } = useProjectStore() const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') useEffect(() => { fetchProjects() }, []) const handleDelete = async (id: string) => { try { await deleteProject(id) message.success('项目已删除') } catch (error) { message.error(`删除失败: ${(error as Error).message}`) } } const handleContinueEdit = (id: string) => { // 跳转到项目详情页继续编辑 navigate(`/projects/${id}`) } const handleView = (id: string) => { navigate(`/projects/${id}`) } // 按状态分组项目 const draftProjects = projects.filter(p => getProjectStatus(p) === 'draft') const inProgressProjects = projects.filter(p => getProjectStatus(p) === 'in_progress') const completedProjects = projects.filter(p => getProjectStatus(p) === 'completed') return (
{/* 头部 */}

我的项目

共 {projects.length} 个项目 {inProgressProjects.length > 0 && ` · ${inProgressProjects.length} 个创作中`} {completedProjects.length > 0 && ` · ${completedProjects.length} 个已完成`}

{/* 项目列表 */} {loading ? (

加载中...

) : projects.length === 0 ? (

暂无项目

创建您的第一个项目开始创作

} > ) : (
{/* 创作中的项目 */} {inProgressProjects.length > 0 && (

创作中 ({inProgressProjects.length})

{inProgressProjects.map(project => ( ))}
)} {/* 草稿 */} {draftProjects.length > 0 && (

草稿 ({draftProjects.length})

{draftProjects.map(project => ( ))}
)} {/* 已完成的项目 */} {completedProjects.length > 0 && (

已完成 ({completedProjects.length})

{completedProjects.map(project => ( ))}
)}
)} ) }