diff --git a/apps/desktop/src/components/ProjectCard.tsx b/apps/desktop/src/components/ProjectCard.tsx index 4b5960b..c6f113a 100644 --- a/apps/desktop/src/components/ProjectCard.tsx +++ b/apps/desktop/src/components/ProjectCard.tsx @@ -1,15 +1,25 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Project } from '../types/project'; +import { MaterialStats } from '../types/material'; import { formatDistanceToNow } from 'date-fns'; import { zhCN } from 'date-fns/locale'; -import { - Folder, - MoreVertical, - Edit3, - Trash2, +import { invoke } from '@tauri-apps/api/core'; +import { + Folder, + MoreVertical, + Edit3, + Trash2, ExternalLink, Calendar, - MapPin + MapPin, + FolderOpen, + FileVideo, + FileAudio, + FileImage, + File, + HardDrive, + BarChart3, + Hash } from 'lucide-react'; interface ProjectCardProps { @@ -19,6 +29,15 @@ interface ProjectCardProps { onDelete: (id: string) => void; } +// 格式化文件大小 +const formatFileSize = (bytes: number): string => { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; +}; + /** * 项目卡片组件 * 遵循简洁大方的设计风格 @@ -30,8 +49,30 @@ export const ProjectCard: React.FC = ({ onDelete }) => { const [showMenu, setShowMenu] = React.useState(false); + const [stats, setStats] = useState(null); + const [isLoadingStats, setIsLoadingStats] = useState(false); const menuRef = React.useRef(null); + // 加载项目统计信息 + useEffect(() => { + const loadStats = async () => { + setIsLoadingStats(true); + try { + const projectStats = await invoke('get_project_material_stats', { + projectId: project.id + }); + setStats(projectStats); + } catch (error) { + console.error('加载项目统计失败:', error); + // 静默失败,不影响卡片显示 + } finally { + setIsLoadingStats(false); + } + }; + + loadStats(); + }, [project.id]); + // 点击外部关闭菜单 React.useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -68,6 +109,37 @@ export const ProjectCard: React.FC = ({ return parts[parts.length - 1] || path; }; + // 打开项目文件夹 + const handleOpenFolder = async (e: React.MouseEvent) => { + e.stopPropagation(); + try { + const { openPath } = await import('@tauri-apps/plugin-opener'); + + // 处理 Windows 路径格式,移除 \\?\ 前缀 + let normalizedPath = project.path; + if (normalizedPath.startsWith('\\\\?\\')) { + normalizedPath = normalizedPath.substring(4); + } + + await openPath(normalizedPath); + } catch (error) { + console.error('打开文件夹失败:', error); + + // 如果 openPath 失败,尝试使用 revealItemInDir + try { + const { revealItemInDir } = await import('@tauri-apps/plugin-opener'); + let normalizedPath = project.path; + if (normalizedPath.startsWith('\\\\?\\')) { + normalizedPath = normalizedPath.substring(4); + } + await revealItemInDir(normalizedPath); + } catch (fallbackError) { + console.error('备用方法也失败:', fallbackError); + alert('无法打开文件夹,请检查路径是否存在'); + } + } + }; + return (
@@ -96,6 +168,16 @@ export const ProjectCard: React.FC = ({ 打开项目 +