新功能: - 项目创建:支持项目名称和本地路径绑定 - 项目列表:简洁大方的卡片式布局展示 - 项目编辑:支持项目信息修改 - 项目删除:支持项目软删除 - 路径选择:集成系统文件夹选择对话框 - 路径验证:实时验证项目路径有效性 架构设计: - 遵循 Tauri 开发规范的四层架构设计 - 基础设施层:数据库管理、文件系统操作 - 数据访问层:项目仓库模式、SQLite 集成 - 业务逻辑层:项目服务、数据验证 - 表示层:Tauri 命令、前端组件 UI/UX: - 使用 Tailwind CSS 实现简洁大方的设计风格 - 响应式布局适配不同屏幕尺寸 - 流畅的动画效果和交互反馈 - 完整的错误处理和用户提示 技术栈: - 后端:Rust + Tauri + SQLite + 四层架构 - 前端:React + TypeScript + Tailwind CSS + Zustand - 测试:Rust 单元测试 + Vitest 前端测试 - 工具:pnpm 包管理 + 类型安全保证 质量保证: - Rust 单元测试覆盖核心业务逻辑 - 前端组件测试覆盖主要 UI 组件 - TypeScript 严格模式确保类型安全 - 遵循开发规范的代码质量标准 核心特性: - 项目管理:创建、查看、编辑、删除项目 - 路径管理:自动验证、绝对路径转换 - 数据持久化:SQLite 本地数据库存储 - 状态管理:Zustand 响应式状态管理 - 错误处理:完整的错误捕获和用户反馈
145 lines
4.0 KiB
TypeScript
145 lines
4.0 KiB
TypeScript
import React, { useEffect } from 'react';
|
|
import { useProjectStore } from '../store/projectStore';
|
|
import { useUIStore } from '../store/uiStore';
|
|
import { ProjectCard } from './ProjectCard';
|
|
import { EmptyState } from './EmptyState';
|
|
import { LoadingSpinner } from './LoadingSpinner';
|
|
import { ErrorMessage } from './ErrorMessage';
|
|
import { Plus } from 'lucide-react';
|
|
|
|
/**
|
|
* 项目列表组件
|
|
* 遵循 Tauri 开发规范的组件设计模式
|
|
*/
|
|
export const ProjectList: React.FC = () => {
|
|
const {
|
|
projects,
|
|
isLoading,
|
|
error,
|
|
loadProjects,
|
|
deleteProject,
|
|
setCurrentProject,
|
|
clearError
|
|
} = useProjectStore();
|
|
|
|
const {
|
|
openCreateProjectModal,
|
|
openEditProjectModal
|
|
} = useUIStore();
|
|
|
|
// 组件挂载时加载项目
|
|
useEffect(() => {
|
|
loadProjects();
|
|
}, [loadProjects]);
|
|
|
|
// 处理项目打开
|
|
const handleProjectOpen = (project: any) => {
|
|
setCurrentProject(project);
|
|
// TODO: 导航到项目详情页面
|
|
console.log('打开项目:', project);
|
|
};
|
|
|
|
// 处理项目编辑
|
|
const handleProjectEdit = (project: any) => {
|
|
openEditProjectModal(project);
|
|
};
|
|
|
|
// 处理项目删除
|
|
const handleProjectDelete = async (id: string) => {
|
|
if (window.confirm('确定要删除这个项目吗?此操作不可撤销。')) {
|
|
try {
|
|
await deleteProject(id);
|
|
} catch (error) {
|
|
console.error('删除项目失败:', error);
|
|
}
|
|
}
|
|
};
|
|
|
|
// 错误状态
|
|
if (error) {
|
|
return (
|
|
<div className="w-full animate-fade-in">
|
|
<div className="flex items-center justify-between mb-8 pb-4 border-b border-gray-200">
|
|
<h1 className="text-3xl font-bold text-gray-900">我的项目</h1>
|
|
<button
|
|
className="btn btn-primary"
|
|
onClick={openCreateProjectModal}
|
|
>
|
|
<Plus size={20} />
|
|
新建项目
|
|
</button>
|
|
</div>
|
|
<ErrorMessage
|
|
message={error}
|
|
onRetry={loadProjects}
|
|
onDismiss={clearError}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 加载状态
|
|
if (isLoading && projects.length === 0) {
|
|
return (
|
|
<div className="w-full animate-fade-in">
|
|
<div className="flex items-center justify-between mb-8 pb-4 border-b border-gray-200">
|
|
<h1 className="text-3xl font-bold text-gray-900">我的项目</h1>
|
|
<button
|
|
className="btn btn-primary"
|
|
onClick={openCreateProjectModal}
|
|
>
|
|
<Plus size={20} />
|
|
新建项目
|
|
</button>
|
|
</div>
|
|
<div className="flex items-center justify-center py-16">
|
|
<LoadingSpinner text="加载项目中..." />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="w-full">
|
|
<div className="flex items-center justify-between mb-8 pb-4 border-b border-gray-200">
|
|
<h1 className="text-3xl font-bold text-gray-900">我的项目</h1>
|
|
<button
|
|
className="btn btn-primary"
|
|
onClick={openCreateProjectModal}
|
|
disabled={isLoading}
|
|
>
|
|
<Plus size={20} />
|
|
新建项目
|
|
</button>
|
|
</div>
|
|
|
|
{projects.length === 0 ? (
|
|
<EmptyState
|
|
title="还没有项目"
|
|
description="创建您的第一个项目开始使用 MixVideo"
|
|
actionText="新建项目"
|
|
onAction={openCreateProjectModal}
|
|
/>
|
|
) : (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
|
{projects.map((project) => (
|
|
<ProjectCard
|
|
key={project.id}
|
|
project={project}
|
|
onOpen={handleProjectOpen}
|
|
onEdit={handleProjectEdit}
|
|
onDelete={handleProjectDelete}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{isLoading && projects.length > 0 && (
|
|
<div className="fixed inset-0 bg-black bg-opacity-20 flex items-center justify-center z-40">
|
|
<LoadingSpinner size="small" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|