fix: UI 样式优化

This commit is contained in:
imeepos
2025-07-15 19:42:39 +08:00
parent 888675dbf1
commit fed5cdd73d
10 changed files with 318 additions and 191 deletions

View File

@@ -242,14 +242,16 @@ const ModelCard: React.FC<ModelCardProps> = ({
); );
} }
// 网格视图 - 现代化设计 // 网格视图 - 增强的现代化设计
return ( return (
<div <div
className="group relative bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden hover:shadow-xl hover:border-primary-200 transition-all duration-300 hover:-translate-y-1 hover-lift animate-scale-in" className="group relative bg-gradient-to-br from-white to-gray-50/50 hover:from-white hover:to-primary-50/30 rounded-2xl shadow-sm border border-gray-100/50 overflow-hidden hover:shadow-xl hover:border-primary-200 transition-all duration-500 hover:-translate-y-2 hover:scale-[1.02] animate-scale-in"
onMouseEnter={() => setIsHovered(true)} onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)} onMouseLeave={() => setIsHovered(false)}
onClick={onSelect} onClick={onSelect}
> >
{/* 装饰性背景 */}
<div className="absolute top-0 right-0 w-20 h-20 bg-gradient-to-br from-primary-100/30 to-primary-200/30 rounded-full -translate-y-10 translate-x-10 opacity-0 group-hover:opacity-100 transition-all duration-500"></div>
{/* 封面图片容器 */} {/* 封面图片容器 */}
<div className="relative aspect-[4/5] bg-gradient-to-br from-gray-50 to-gray-100 overflow-hidden"> <div className="relative aspect-[4/5] bg-gradient-to-br from-gray-50 to-gray-100 overflow-hidden">
{/* 图片 */} {/* 图片 */}

View File

@@ -250,12 +250,13 @@ const ModelList: React.FC<ModelListProps> = ({ onModelSelect }) => {
return ( return (
<div className="space-y-8 animate-fade-in"> <div className="space-y-8 animate-fade-in">
{/* 头部工具栏 */} {/* 美观的头部工具栏 */}
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-4"> <div className="bg-gradient-to-r from-white via-primary-50/30 to-white rounded-xl shadow-sm border border-gray-200/50 p-6 relative overflow-hidden">
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4"> <div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-primary-100/30 to-primary-200/30 rounded-full -translate-y-16 translate-x-16 opacity-50"></div>
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4 relative z-10">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="w-10 h-10 bg-gradient-to-br from-primary-500 to-primary-600 rounded-lg flex items-center justify-center"> <div className="w-12 h-12 bg-gradient-to-br from-primary-500 to-primary-600 rounded-xl flex items-center justify-center shadow-sm">
<SparklesIcon className="h-5 w-5 text-white" /> <SparklesIcon className="h-6 w-6 text-white" />
</div> </div>
<div> <div>
<h1 className="text-xl font-bold text-gray-900 mb-0.5"></h1> <h1 className="text-xl font-bold text-gray-900 mb-0.5"></h1>

View File

@@ -52,13 +52,18 @@ const Navigation: React.FC = () => {
}; };
return ( return (
<nav className="bg-white shadow-sm border-b border-gray-200"> <nav className="bg-white/95 backdrop-blur-sm shadow-sm border-b border-gray-200/50 sticky top-0 z-50">
<div className="px-4 sm:px-6 lg:px-8"> <div className="px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16"> <div className="flex items-center justify-between h-16">
{/* Logo */} {/* 增强的 Logo */}
<div className="flex items-center"> <div className="flex items-center">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<h1 className="text-xl font-bold text-gray-900">MixVideo</h1> <div className="flex items-center gap-2">
<div className="w-8 h-8 bg-gradient-to-br from-primary-500 to-primary-600 rounded-lg flex items-center justify-center shadow-sm">
<span className="text-white font-bold text-sm">M</span>
</div>
<h1 className="text-xl font-bold bg-gradient-to-r from-gray-900 to-gray-700 bg-clip-text text-transparent">MixVideo</h1>
</div>
</div> </div>
</div> </div>
@@ -73,17 +78,20 @@ const Navigation: React.FC = () => {
<Link <Link
key={item.name} key={item.name}
to={item.href} to={item.href}
className={`group flex items-center px-3 py-2 rounded-md text-sm font-medium transition-colors ${ className={`group flex items-center px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200 relative overflow-hidden ${
active active
? 'bg-blue-100 text-blue-700' ? 'bg-gradient-to-r from-primary-100 to-primary-200 text-primary-700 shadow-sm'
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-50' : 'text-gray-600 hover:text-gray-900 hover:bg-gradient-to-r hover:from-gray-50 hover:to-gray-100'
}`} }`}
title={item.description} title={item.description}
> >
<Icon className={`mr-2 h-5 w-5 ${ {active && (
active ? 'text-blue-600' : 'text-gray-400 group-hover:text-gray-600' <div className="absolute inset-0 bg-gradient-to-r from-primary-500/10 to-primary-600/10"></div>
)}
<Icon className={`mr-2 h-5 w-5 relative z-10 transition-all duration-200 ${
active ? 'text-primary-600' : 'text-gray-400 group-hover:text-gray-600 group-hover:scale-110'
}`} /> }`} />
{item.name} <span className="relative z-10">{item.name}</span>
</Link> </Link>
); );
})} })}

View File

@@ -139,16 +139,20 @@ export const ProjectCard: React.FC<ProjectCardProps> = ({
}; };
return ( return (
<div className="card card-interactive group cursor-pointer animate-fade-in-up relative overflow-hidden"> <div className="card card-interactive group cursor-pointer animate-fade-in-up relative overflow-hidden bg-gradient-to-br from-white to-gray-50/50 hover:from-white hover:to-primary-50/30 transition-all duration-500">
{/* 背景装饰 */} {/* 增强的背景装饰 */}
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-primary-100 to-blue-100 rounded-full -translate-y-16 translate-x-16 opacity-50 group-hover:opacity-70 transition-opacity duration-300"></div> <div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-primary-100 to-primary-200 rounded-full -translate-y-16 translate-x-16 opacity-40 group-hover:opacity-60 transition-all duration-500 group-hover:scale-110"></div>
{/* 光晕效果 */}
<div className="absolute inset-0 bg-gradient-to-br from-transparent via-transparent to-primary-50/20 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
{/* 卡片头部 */} {/* 卡片头部 */}
<div className="relative p-6 pb-4"> <div className="relative p-6 pb-4">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="p-3 bg-primary-100 text-primary-600 rounded-xl group-hover:bg-primary-200 group-hover:scale-110 transition-all duration-300"> <div className="p-3 bg-gradient-to-br from-primary-100 to-primary-200 text-primary-600 rounded-xl group-hover:from-primary-200 group-hover:to-primary-300 group-hover:scale-110 group-hover:shadow-lg transition-all duration-300 relative overflow-hidden">
<Folder size={20} /> <div className="absolute inset-0 bg-white opacity-0 group-hover:opacity-20 transition-opacity duration-300"></div>
<Folder size={20} className="relative z-10" />
</div> </div>
<div> <div>
<h3 className="text-lg font-semibold text-gray-900 line-clamp-1 group-hover:text-primary-700 transition-colors duration-200"> <h3 className="text-lg font-semibold text-gray-900 line-clamp-1 group-hover:text-primary-700 transition-colors duration-200">
@@ -238,14 +242,15 @@ export const ProjectCard: React.FC<ProjectCardProps> = ({
</div> </div>
</div> </div>
) : stats && ( ) : stats && (
<div className="bg-gradient-to-r from-primary-50 to-blue-50 rounded-xl p-4 mb-4 border border-primary-100"> <div className="bg-gradient-to-r from-primary-50 via-blue-50 to-indigo-50 rounded-xl p-4 mb-4 border border-primary-200/50 shadow-sm hover:shadow-md transition-all duration-300 relative overflow-hidden">
<div className="flex items-center justify-between mb-3"> <div className="absolute inset-0 bg-gradient-to-r from-white/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="flex items-center justify-between mb-3 relative z-10">
<div className="flex items-center space-x-2 text-primary-700 font-semibold text-sm"> <div className="flex items-center space-x-2 text-primary-700 font-semibold text-sm">
<BarChart3 className="w-4 h-4" /> <BarChart3 className="w-4 h-4 text-primary-600" />
<span></span> <span></span>
</div> </div>
{stats.total_materials > 0 && ( {stats.total_materials > 0 && (
<div className="badge badge-primary"> <div className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-primary-100 text-primary-800 border border-primary-200">
{stats.total_materials} {stats.total_materials}
</div> </div>
)} )}
@@ -308,28 +313,28 @@ export const ProjectCard: React.FC<ProjectCardProps> = ({
</div> </div>
</div> </div>
{/* 卡片底部操作区 */} {/* 增强的底部操作区 */}
<div className="relative px-6 pb-6"> <div className="relative px-6 pb-6">
<div className="flex gap-2"> <div className="flex gap-2">
<button <button
className="btn btn-ghost btn-sm p-2" className="p-2.5 rounded-lg text-gray-500 hover:text-primary-600 hover:bg-primary-50 transition-all duration-200 hover:scale-105"
onClick={handleOpenFolder} onClick={handleOpenFolder}
title="打开项目文件夹" title="打开项目文件夹"
> >
<FolderOpen size={16} /> <FolderOpen size={16} />
</button> </button>
<button <button
className="btn btn-secondary btn-sm flex-1" className="flex-1 flex items-center justify-center gap-2 px-4 py-2.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-all duration-200 hover:scale-105 font-medium text-sm"
onClick={() => onEdit(project)} onClick={() => onEdit(project)}
> >
<Edit3 size={16} /> <Edit3 size={14} />
</button> </button>
<button <button
className="btn btn-primary btn-sm flex-1 shadow-glow" className="flex-1 flex items-center justify-center gap-2 px-4 py-2.5 bg-gradient-to-r from-primary-600 to-primary-700 hover:from-primary-700 hover:to-primary-800 text-white rounded-lg transition-all duration-200 hover:scale-105 hover:shadow-lg font-medium text-sm"
onClick={() => onOpen(project)} onClick={() => onOpen(project)}
> >
<ExternalLink size={16} /> <ExternalLink size={14} />
</button> </button>
</div> </div>

View File

@@ -257,13 +257,13 @@ export const ProjectList: React.FC = () => {
</div> </div>
) : ( ) : (
<div className="space-y-6"> <div className="space-y-6">
{/* 项目网格 */} {/* 优化的项目网格 - 更好的响应式布局 */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-5">
{projects.map((project, index) => ( {projects.map((project, index) => (
<div <div
key={project.id} key={project.id}
className="animate-fade-in-up" className="animate-fade-in-up"
style={{ animationDelay: `${index * 0.1}s` }} style={{ animationDelay: `${index * 0.08}s` }}
> >
<ProjectCard <ProjectCard
project={project} project={project}

View File

@@ -7,7 +7,8 @@ import {
ArrowUpIcon, ArrowUpIcon,
ArrowDownIcon, ArrowDownIcon,
CheckIcon, CheckIcon,
XMarkIcon XMarkIcon,
CpuChipIcon
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import { AiClassificationService } from '../services/aiClassificationService'; import { AiClassificationService } from '../services/aiClassificationService';
import { import {
@@ -282,31 +283,41 @@ const AiClassificationSettings: React.FC = () => {
return ( return (
<div className="min-h-screen bg-gray-50/30"> <div className="min-h-screen bg-gray-50/30">
<div className="max-w-6xl mx-auto px-4 py-6"> <div className="max-w-6xl mx-auto px-4 py-6">
{/* 页面标题和操作栏 - 优化布局和视觉层次 */} {/* 美观的页面头部 */}
<div className="mb-8"> <div className="mb-6">
<div className="flex items-center justify-between"> <div className="bg-gradient-to-r from-white via-purple-50/30 to-white rounded-xl shadow-sm border border-gray-200/50 p-6 relative overflow-hidden">
<div className="space-y-1"> <div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-purple-100/30 to-pink-100/30 rounded-full -translate-y-16 translate-x-16 opacity-50"></div>
<h1 className="text-xl font-semibold text-gray-900 tracking-tight">AI分类设置</h1>
<p className="text-sm text-gray-500">AI分类规则和提示词</p> <div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 relative z-10">
<div className="flex items-center gap-4">
<div className="w-12 h-12 bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl flex items-center justify-center shadow-sm">
<CpuChipIcon className="h-6 w-6 text-white" />
</div> </div>
<div className="flex items-center gap-2"> <div>
<h1 className="text-2xl font-bold text-gray-900 mb-1">AI分类设置</h1>
<p className="text-sm text-gray-600">AI分类规则和提示词</p>
</div>
</div>
<div className="flex items-center gap-3">
<button <button
onClick={openPreviewDialog} onClick={openPreviewDialog}
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-gray-600 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-300 transition-all duration-200" className="inline-flex items-center gap-2 px-4 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 hover:border-gray-300 transition-all duration-200 hover:scale-105"
> >
<EyeIcon className="h-3.5 w-3.5" /> <EyeIcon className="h-4 w-4" />
</button> </button>
<button <button
onClick={openCreateDialog} onClick={openCreateDialog}
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-white bg-blue-600 border border-blue-600 rounded-lg hover:bg-blue-700 hover:border-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-300 transition-all duration-200 shadow-sm" className="inline-flex items-center gap-2 px-4 py-2.5 text-sm font-medium text-white bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 rounded-lg transition-all duration-200 hover:scale-105 shadow-sm hover:shadow-md"
> >
<PlusIcon className="h-3.5 w-3.5" /> <PlusIcon className="h-4 w-4" />
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div>
{/* 分类列表 - 优化卡片设计和布局 */} {/* 分类列表 - 优化卡片设计和布局 */}
{classifications.length === 0 ? ( {classifications.length === 0 ? (
@@ -325,28 +336,30 @@ const AiClassificationSettings: React.FC = () => {
</h2> </h2>
</div> </div>
{/* 分类卡片网格 */} {/* 美观的分类卡片网格 */}
<div className="grid gap-3"> <div className="grid gap-4">
{classifications.map((classification, index) => ( {classifications.map((classification, index) => (
<div <div
key={classification.id} key={classification.id}
className="group relative bg-white border border-gray-200 rounded-xl p-4 hover:border-gray-300 hover:shadow-sm transition-all duration-200" className="group relative bg-gradient-to-br from-white to-gray-50/30 border border-gray-200/50 rounded-xl p-5 hover:border-purple-200 hover:shadow-md hover:-translate-y-1 transition-all duration-300 overflow-hidden"
> >
{/* 卡片头部 */} {/* 装饰性背景 */}
<div className="flex items-start justify-between mb-3"> <div className="absolute top-0 right-0 w-20 h-20 bg-gradient-to-br from-purple-100/20 to-pink-100/20 rounded-full -translate-y-10 translate-x-10 opacity-0 group-hover:opacity-100 transition-all duration-300"></div>
{/* 美观的卡片头部 */}
<div className="flex items-start justify-between mb-4 relative z-10">
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1"> <div className="flex items-center gap-2 mb-2">
<h3 className="text-sm font-medium text-gray-900 truncate"> <h3 className="text-base font-semibold text-gray-900 truncate">
{classification.name} {classification.name}
</h3> </h3>
<span className={`inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium ${ <span className={`inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium ${
classification.is_active classification.is_active
? 'bg-emerald-50 text-emerald-700 border border-emerald-200' ? 'bg-gradient-to-r from-emerald-50 to-emerald-100 text-emerald-700 border border-emerald-200'
: 'bg-gray-50 text-gray-600 border border-gray-200' : 'bg-gradient-to-r from-gray-50 to-gray-100 text-gray-600 border border-gray-200'
}`}> }`}>
{classification.is_active ? '激活' : '禁用'} {classification.is_active ? '激活' : '禁用'}
</span> </span>
<span className="text-xs text-gray-400 bg-gray-50 px-1.5 py-0.5 rounded"> <span className="text-xs text-purple-600 bg-purple-50 px-2 py-1 rounded-full border border-purple-200">
#{classification.sort_order} #{classification.sort_order}
</span> </span>
</div> </div>

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { ArrowLeft, FolderOpen, Upload, FileVideo, FileAudio, FileImage, HardDrive, Brain, Loader2, Link, Layers } from 'lucide-react'; import { ArrowLeft, FolderOpen, Upload, FileVideo, FileAudio, FileImage, HardDrive, Brain, Loader2, Link, Layers, Calendar, MapPin } from 'lucide-react';
import { invoke } from '@tauri-apps/api/core'; import { invoke } from '@tauri-apps/api/core';
import { useProjectStore } from '../store/projectStore'; import { useProjectStore } from '../store/projectStore';
import { useMaterialStore } from '../store/materialStore'; import { useMaterialStore } from '../store/materialStore';
@@ -29,6 +29,27 @@ import {
import { MaterialMatchingService } from '../services/materialMatchingService'; import { MaterialMatchingService } from '../services/materialMatchingService';
import { MaterialMatchingResult, MaterialMatchingRequest } from '../types/materialMatching'; import { MaterialMatchingResult, MaterialMatchingRequest } from '../types/materialMatching';
import { MaterialSegmentView } from '../components/MaterialSegmentView'; import { MaterialSegmentView } from '../components/MaterialSegmentView';
import { formatDistanceToNow } from 'date-fns';
import { zhCN } from 'date-fns/locale';
// 格式化时间
const formatTime = (dateString: string) => {
try {
const date = new Date(dateString);
return formatDistanceToNow(date, {
addSuffix: true,
locale: zhCN
});
} catch {
return '未知时间';
}
};
// 获取项目目录名
const getDirectoryName = (path: string) => {
const parts = path.split(/[/\\]/);
return parts[parts.length - 1] || path;
};
/** /**
* 项目详情页面组件 * 项目详情页面组件
@@ -427,111 +448,146 @@ export const ProjectDetails: React.FC = () => {
} }
return ( return (
<div className="max-w-6xl mx-auto"> <div className="max-w-7xl mx-auto">
{/* 页面头部 */} {/* 美观的页面头部 */}
<div className="mb-8"> <div className="mb-6">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between space-y-4 sm:space-y-0 mb-4"> {/* 面包屑导航 */}
<div className="mb-4">
<button <button
onClick={handleBack} onClick={handleBack}
className="inline-flex items-center text-gray-600 hover:text-gray-900 transition-colors self-start" className="inline-flex items-center text-gray-500 hover:text-primary-600 transition-all duration-200 hover:scale-105"
> >
<ArrowLeft className="w-5 h-5 mr-2" /> <ArrowLeft className="w-4 h-4 mr-1.5" />
<span className="hidden sm:inline"></span> <span className="text-sm font-medium"></span>
<span className="sm:hidden"></span>
</button> </button>
</div>
<div className="flex flex-wrap items-center gap-2 sm:gap-3"> {/* 项目标题和操作区 */}
<div className="bg-gradient-to-r from-white via-primary-50/30 to-white rounded-2xl border border-gray-200/50 p-6 shadow-sm hover:shadow-md transition-all duration-300 relative overflow-hidden">
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-primary-100/50 to-primary-200/50 rounded-full -translate-y-16 translate-x-16 opacity-50"></div>
<div className="relative z-10">
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
<div className="flex-1 min-w-0">
<h1 className="text-2xl lg:text-3xl font-bold text-gray-900 mb-2 truncate">
{project.name}
</h1>
{project.description && (
<p className="text-gray-600 text-sm lg:text-base mb-3 line-clamp-2">
{project.description}
</p>
)}
<div className="flex items-center gap-4 text-sm text-gray-500">
<div className="flex items-center gap-1.5">
<Calendar className="w-4 h-4" />
<span> {formatTime(project.updated_at)}</span>
</div>
<div className="flex items-center gap-1.5">
<MapPin className="w-4 h-4" />
<span className="truncate max-w-xs" title={project.path}>
{getDirectoryName(project.path)}
</span>
</div>
</div>
</div>
<div className="flex flex-wrap items-center gap-2">
<button <button
onClick={handleOpenFolder} onClick={handleOpenFolder}
className="inline-flex items-center px-3 sm:px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors text-sm" className="inline-flex items-center px-4 py-2.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-all duration-200 hover:scale-105 text-sm font-medium"
> >
<FolderOpen className="w-4 h-4" /> <FolderOpen className="w-4 h-4 mr-2" />
<span className="hidden sm:inline ml-2"></span>
</button> </button>
<button <button
onClick={handleMaterialImport} onClick={handleMaterialImport}
className="inline-flex items-center px-3 sm:px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm" className="inline-flex items-center px-4 py-2.5 bg-gradient-to-r from-primary-600 to-primary-700 hover:from-primary-700 hover:to-primary-800 text-white rounded-lg transition-all duration-200 hover:scale-105 shadow-sm hover:shadow-md text-sm font-medium"
> >
<Upload className="w-4 h-4" /> <Upload className="w-4 h-4 mr-2" />
<span className="hidden sm:inline ml-2"></span>
</button> </button>
<button <button
onClick={handleBatchClassification} onClick={handleBatchClassification}
disabled={classificationLoading} disabled={classificationLoading}
className="inline-flex items-center px-3 sm:px-4 py-2 bg-gradient-to-r from-purple-500 to-pink-500 text-white rounded-lg hover:from-purple-600 hover:to-pink-600 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-sm hover:shadow-md text-sm" className="inline-flex items-center px-4 py-2.5 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 disabled:opacity-50 disabled:cursor-not-allowed text-white rounded-lg transition-all duration-200 hover:scale-105 shadow-sm hover:shadow-md text-sm font-medium"
> >
{classificationLoading ? ( {classificationLoading ? (
<Loader2 className="w-4 h-4 animate-spin" /> <Loader2 className="w-4 h-4 mr-2 animate-spin" />
) : ( ) : (
<Brain className="w-4 h-4" /> <Brain className="w-4 h-4 mr-2" />
)} )}
<span className="hidden sm:inline ml-2">
{classificationLoading ? '分类中...' : '一键AI分类'} {classificationLoading ? '分类中...' : '一键AI分类'}
</span>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
{/* 项目统计概览 */} {/* 美观的项目统计概览 */}
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 md:gap-6 mb-6"> <div className="grid grid-cols-2 md:grid-cols-5 gap-3 md:gap-4 mb-6">
{/* 总素材数 */} {/* 总素材数 */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 md:p-6"> <div className="bg-gradient-to-br from-white to-primary-50/30 rounded-xl shadow-sm border border-gray-200/50 p-4 md:p-5 hover:shadow-md transition-all duration-300 hover:-translate-y-1 relative overflow-hidden">
<div className="flex items-center justify-between"> <div className="absolute top-0 right-0 w-16 h-16 bg-gradient-to-br from-primary-100/50 to-primary-200/50 rounded-full -translate-y-8 translate-x-8 opacity-50"></div>
<div className="flex items-center justify-between relative z-10">
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<p className="text-xs md:text-sm font-medium text-gray-600 truncate"></p> <p className="text-xs md:text-sm font-medium text-gray-600 truncate"></p>
<p className="text-xl md:text-2xl font-bold text-gray-900">{stats?.total_materials || 0}</p> <p className="text-xl md:text-2xl font-bold text-gray-900">{stats?.total_materials || 0}</p>
</div> </div>
<div className="w-10 h-10 md:w-12 md:h-12 bg-blue-100 rounded-lg flex items-center justify-center ml-2"> <div className="w-10 h-10 md:w-12 md:h-12 bg-gradient-to-br from-primary-100 to-primary-200 rounded-xl flex items-center justify-center ml-2 shadow-sm">
<FolderOpen className="w-5 h-5 md:w-6 md:h-6 text-blue-600" /> <FolderOpen className="w-5 h-5 md:w-6 md:h-6 text-primary-600" />
</div> </div>
</div> </div>
</div> </div>
{/* 视频文件 */} {/* 视频文件 */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 md:p-6"> <div className="bg-gradient-to-br from-white to-green-50/30 rounded-xl shadow-sm border border-gray-200/50 p-4 md:p-5 hover:shadow-md transition-all duration-300 hover:-translate-y-1 relative overflow-hidden">
<div className="flex items-center justify-between"> <div className="absolute top-0 right-0 w-16 h-16 bg-gradient-to-br from-green-100/50 to-green-200/50 rounded-full -translate-y-8 translate-x-8 opacity-50"></div>
<div className="flex items-center justify-between relative z-10">
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<p className="text-xs md:text-sm font-medium text-gray-600 truncate"></p> <p className="text-xs md:text-sm font-medium text-gray-600 truncate"></p>
<p className="text-xl md:text-2xl font-bold text-gray-900">{stats?.video_count || 0}</p> <p className="text-xl md:text-2xl font-bold text-gray-900">{stats?.video_count || 0}</p>
</div> </div>
<div className="w-10 h-10 md:w-12 md:h-12 bg-green-100 rounded-lg flex items-center justify-center ml-2"> <div className="w-10 h-10 md:w-12 md:h-12 bg-gradient-to-br from-green-100 to-green-200 rounded-xl flex items-center justify-center ml-2 shadow-sm">
<FileVideo className="w-5 h-5 md:w-6 md:h-6 text-green-600" /> <FileVideo className="w-5 h-5 md:w-6 md:h-6 text-green-600" />
</div> </div>
</div> </div>
</div> </div>
{/* 音频文件 */} {/* 音频文件 */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 md:p-6"> <div className="bg-gradient-to-br from-white to-purple-50/30 rounded-xl shadow-sm border border-gray-200/50 p-4 md:p-5 hover:shadow-md transition-all duration-300 hover:-translate-y-1 relative overflow-hidden">
<div className="flex items-center justify-between"> <div className="absolute top-0 right-0 w-16 h-16 bg-gradient-to-br from-purple-100/50 to-purple-200/50 rounded-full -translate-y-8 translate-x-8 opacity-50"></div>
<div className="flex items-center justify-between relative z-10">
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<p className="text-xs md:text-sm font-medium text-gray-600 truncate"></p> <p className="text-xs md:text-sm font-medium text-gray-600 truncate"></p>
<p className="text-xl md:text-2xl font-bold text-gray-900">{stats?.audio_count || 0}</p> <p className="text-xl md:text-2xl font-bold text-gray-900">{stats?.audio_count || 0}</p>
</div> </div>
<div className="w-10 h-10 md:w-12 md:h-12 bg-purple-100 rounded-lg flex items-center justify-center ml-2"> <div className="w-10 h-10 md:w-12 md:h-12 bg-gradient-to-br from-purple-100 to-purple-200 rounded-xl flex items-center justify-center ml-2 shadow-sm">
<FileAudio className="w-5 h-5 md:w-6 md:h-6 text-purple-600" /> <FileAudio className="w-5 h-5 md:w-6 md:h-6 text-purple-600" />
</div> </div>
</div> </div>
</div> </div>
{/* 图片文件 */} {/* 图片文件 */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 md:p-6"> <div className="bg-gradient-to-br from-white to-orange-50/30 rounded-xl shadow-sm border border-gray-200/50 p-4 md:p-5 hover:shadow-md transition-all duration-300 hover:-translate-y-1 relative overflow-hidden">
<div className="flex items-center justify-between"> <div className="absolute top-0 right-0 w-16 h-16 bg-gradient-to-br from-orange-100/50 to-orange-200/50 rounded-full -translate-y-8 translate-x-8 opacity-50"></div>
<div className="flex items-center justify-between relative z-10">
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<p className="text-xs md:text-sm font-medium text-gray-600 truncate"></p> <p className="text-xs md:text-sm font-medium text-gray-600 truncate"></p>
<p className="text-xl md:text-2xl font-bold text-gray-900">{stats?.image_count || 0}</p> <p className="text-xl md:text-2xl font-bold text-gray-900">{stats?.image_count || 0}</p>
</div> </div>
<div className="w-10 h-10 md:w-12 md:h-12 bg-orange-100 rounded-lg flex items-center justify-center ml-2"> <div className="w-10 h-10 md:w-12 md:h-12 bg-gradient-to-br from-orange-100 to-orange-200 rounded-xl flex items-center justify-center ml-2 shadow-sm">
<FileImage className="w-5 h-5 md:w-6 md:h-6 text-orange-600" /> <FileImage className="w-5 h-5 md:w-6 md:h-6 text-orange-600" />
</div> </div>
</div> </div>
</div> </div>
{/* AI分类状态 */} {/* AI分类状态 */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 md:p-6"> <div className="bg-gradient-to-br from-white to-purple-50/30 rounded-xl shadow-sm border border-gray-200/50 p-4 md:p-5 hover:shadow-md transition-all duration-300 hover:-translate-y-1 relative overflow-hidden">
<div className="flex items-center justify-between"> <div className="absolute top-0 right-0 w-16 h-16 bg-gradient-to-br from-purple-100/50 to-pink-100/50 rounded-full -translate-y-8 translate-x-8 opacity-50"></div>
<div className="flex items-center justify-between relative z-10">
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<p className="text-xs md:text-sm font-medium text-gray-600 truncate">AI分类队列</p> <p className="text-xs md:text-sm font-medium text-gray-600 truncate">AI分类队列</p>
<div className="flex items-center space-x-1"> <div className="flex items-center space-x-1">
@@ -541,12 +597,12 @@ export const ProjectDetails: React.FC = () => {
<span className="text-xs text-gray-500"></span> <span className="text-xs text-gray-500"></span>
</div> </div>
{queueStats?.processing_tasks && queueStats.processing_tasks > 0 && ( {queueStats?.processing_tasks && queueStats.processing_tasks > 0 && (
<p className="text-xs text-blue-600 mt-1"> <p className="text-xs text-purple-600 mt-1 font-medium">
{queueStats.processing_tasks} {queueStats.processing_tasks}
</p> </p>
)} )}
</div> </div>
<div className="w-10 h-10 md:w-12 md:h-12 bg-gradient-to-br from-purple-100 to-pink-100 rounded-lg flex items-center justify-center ml-2"> <div className="w-10 h-10 md:w-12 md:h-12 bg-gradient-to-br from-purple-100 to-pink-100 rounded-xl flex items-center justify-center ml-2 shadow-sm">
<Brain className="w-5 h-5 md:w-6 md:h-6 text-purple-600" /> <Brain className="w-5 h-5 md:w-6 md:h-6 text-purple-600" />
</div> </div>
</div> </div>
@@ -555,16 +611,16 @@ export const ProjectDetails: React.FC = () => {
{/* 主要内容区域 */} {/* 主要内容区域 */}
<div className="space-y-6"> <div className="space-y-6">
{/* 选项卡导航 */} {/* 美观的选项卡导航 */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200"> <div className="bg-white rounded-xl shadow-sm border border-gray-200/50 overflow-hidden">
<div className="border-b border-gray-200"> <div className="border-b border-gray-100">
<nav className="flex space-x-4 md:space-x-8 px-4 md:px-6 overflow-x-auto" aria-label="Tabs"> <nav className="flex space-x-1 px-4 md:px-6 overflow-x-auto" aria-label="Tabs">
<button <button
onClick={() => setActiveTab('materials')} onClick={() => setActiveTab('materials')}
className={`py-4 px-1 border-b-2 font-medium text-sm transition-colors whitespace-nowrap ${ className={`py-3 px-4 font-medium text-sm transition-all duration-200 whitespace-nowrap rounded-t-lg relative ${
activeTab === 'materials' activeTab === 'materials'
? 'border-blue-500 text-blue-600' ? 'text-primary-600 bg-primary-50 border-b-2 border-primary-500'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
}`} }`}
> >
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
@@ -575,10 +631,10 @@ export const ProjectDetails: React.FC = () => {
</button> </button>
<button <button
onClick={() => setActiveTab('segments')} onClick={() => setActiveTab('segments')}
className={`py-4 px-1 border-b-2 font-medium text-sm transition-colors whitespace-nowrap ${ className={`py-3 px-4 font-medium text-sm transition-all duration-200 whitespace-nowrap rounded-t-lg relative ${
activeTab === 'segments' activeTab === 'segments'
? 'border-blue-500 text-blue-600' ? 'text-primary-600 bg-primary-50 border-b-2 border-primary-500'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
}`} }`}
> >
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
@@ -589,10 +645,10 @@ export const ProjectDetails: React.FC = () => {
</button> </button>
<button <button
onClick={() => setActiveTab('templates')} onClick={() => setActiveTab('templates')}
className={`py-4 px-1 border-b-2 font-medium text-sm transition-colors whitespace-nowrap ${ className={`py-3 px-4 font-medium text-sm transition-all duration-200 whitespace-nowrap rounded-t-lg relative ${
activeTab === 'templates' activeTab === 'templates'
? 'border-blue-500 text-blue-600' ? 'text-primary-600 bg-primary-50 border-b-2 border-primary-500'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
}`} }`}
> >
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
@@ -603,10 +659,10 @@ export const ProjectDetails: React.FC = () => {
</button> </button>
<button <button
onClick={() => setActiveTab('ai-logs')} onClick={() => setActiveTab('ai-logs')}
className={`py-4 px-1 border-b-2 font-medium text-sm transition-colors whitespace-nowrap ${ className={`py-3 px-4 font-medium text-sm transition-all duration-200 whitespace-nowrap rounded-t-lg relative ${
activeTab === 'ai-logs' activeTab === 'ai-logs'
? 'border-blue-500 text-blue-600' ? 'text-primary-600 bg-primary-50 border-b-2 border-primary-500'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
}`} }`}
> >
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">

View File

@@ -186,54 +186,66 @@ const TemplateManagement: React.FC = () => {
return ( return (
<div className="min-h-screen bg-gray-50 p-6"> <div className="min-h-screen bg-gray-50 p-6">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
{/* 页面标题和操作栏 */} {/* 美观的页面头部 */}
<div className="mb-8"> <div className="mb-6">
<div className="flex items-center justify-between mb-6"> <div className="bg-gradient-to-r from-white via-indigo-50/30 to-white rounded-xl shadow-sm border border-gray-200/50 p-6 mb-6 relative overflow-hidden">
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-indigo-100/30 to-purple-100/30 rounded-full -translate-y-16 translate-x-16 opacity-50"></div>
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 relative z-10">
<div className="flex items-center gap-4">
<div className="w-12 h-12 bg-gradient-to-br from-indigo-500 to-purple-500 rounded-xl flex items-center justify-center shadow-sm">
<FileText className="h-6 w-6 text-white" />
</div>
<div> <div>
<h1 className="text-3xl font-bold text-gray-900"></h1> <h1 className="text-2xl font-bold text-gray-900 mb-1"></h1>
<p className="text-gray-600 mt-2"></p> <p className="text-sm text-gray-600"></p>
</div>
</div> </div>
<div className="flex items-center space-x-3"> <div className="flex items-center gap-3">
<button <button
onClick={() => setShowImportModal(true)} onClick={() => setShowImportModal(true)}
className="flex items-center gap-1.5 px-4 py-2 bg-gradient-to-r from-primary-500 to-primary-600 text-white rounded-lg hover:from-primary-600 hover:to-primary-700 transition-all duration-200 shadow-sm hover:shadow-md text-sm font-medium" className="inline-flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-primary-600 to-primary-700 hover:from-primary-700 hover:to-primary-800 text-white rounded-lg transition-all duration-200 hover:scale-105 shadow-sm hover:shadow-md text-sm font-medium"
> >
<Plus className="w-4 h-4 mr-2" /> <Plus className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => setShowBatchImportModal(true)} onClick={() => setShowBatchImportModal(true)}
className="inline-flex items-center px-3 sm:px-4 py-2 bg-gradient-to-r from-purple-500 to-pink-500 text-white rounded-lg hover:from-purple-600 hover:to-pink-600 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-sm hover:shadow-md text-sm" className="inline-flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white rounded-lg transition-all duration-200 hover:scale-105 shadow-sm hover:shadow-md text-sm font-medium"
> >
<Upload className="w-4 h-4 mr-2" /> <Upload className="w-4 h-4" />
</button> </button>
</div> </div>
</div> </div>
</div>
{/* 搜索和筛选栏 */} {/* 美观的搜索和筛选栏 */}
<div className="flex items-center space-x-4 bg-white p-4 rounded-lg shadow-sm"> <div className="flex items-center space-x-4 bg-white p-5 rounded-xl shadow-sm border border-gray-200/50">
<div className="flex-1 relative"> <div className="flex-1 relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<input <input
type="text" type="text"
placeholder="搜索模板名称..." placeholder="搜索模板名称..."
value={searchKeyword} value={searchKeyword}
onChange={(e) => handleSearch(e.target.value)} onChange={(e) => handleSearch(e.target.value)}
className="w-full pl-10 pr-3 py-2 border border-gray-200 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all duration-200 placeholder-gray-400 text-sm text-gray-900 hover:border-gray-300" className="w-full pl-11 pr-4 py-3 border border-gray-200 rounded-xl bg-white focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all duration-200 placeholder-gray-400 text-sm text-gray-900 hover:border-gray-300 shadow-sm"
/> />
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-3">
<Filter className="w-4 h-4 text-gray-500" /> <div className="flex items-center gap-2 text-gray-500">
<Filter className="w-4 h-4" />
<span className="text-sm font-medium"></span>
</div>
<CustomSelect <CustomSelect
value={statusFilter} value={statusFilter}
onChange={handleStatusFilter} onChange={handleStatusFilter}
options={statusOptions} options={statusOptions}
placeholder="筛选状态" placeholder="筛选状态"
className="w-40" className="w-44"
/> />
</div> </div>
</div> </div>

View File

@@ -3,11 +3,23 @@
* 遵循前端开发规范的动画设计原则 * 遵循前端开发规范的动画设计原则
*/ */
/* 淡入动画 */ /* 增强的淡入动画 */
@keyframes fadeIn { @keyframes fadeIn {
from { from {
opacity: 0; opacity: 0;
transform: translateY(10px); transform: translateY(20px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* 优雅的淡入上升动画 */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
} }
to { to {
opacity: 1; opacity: 1;
@@ -117,7 +129,25 @@
/* 动画类 */ /* 动画类 */
.animate-fade-in { .animate-fade-in {
animation: fadeIn 0.3s ease-out forwards; animation: fadeInUp 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
/* 优雅的悬停效果 */
.hover-glow {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.hover-glow:hover {
box-shadow: 0 10px 25px -5px rgba(59, 130, 246, 0.1), 0 10px 10px -5px rgba(59, 130, 246, 0.04);
transform: translateY(-2px);
}
/* 渐变文字效果 */
.text-gradient-primary {
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
} }
.animate-fade-out { .animate-fade-out {

View File

@@ -2,17 +2,17 @@
/* 设计令牌 - 颜色系统 */ /* 设计令牌 - 颜色系统 */
:root { :root {
/* 主色调 - 优雅的紫色系 */ /* 主色调 - 现代蓝色系与Tailwind配置一致 */
--primary-50: #faf5ff; --primary-50: #eff6ff;
--primary-100: #f3e8ff; --primary-100: #dbeafe;
--primary-200: #e9d5ff; --primary-200: #bfdbfe;
--primary-300: #d8b4fe; --primary-300: #93c5fd;
--primary-400: #c084fc; --primary-400: #60a5fa;
--primary-500: #a855f7; --primary-500: #3b82f6;
--primary-600: #9333ea; --primary-600: #2563eb;
--primary-700: #7c3aed; --primary-700: #1d4ed8;
--primary-800: #6b21a8; --primary-800: #1e40af;
--primary-900: #581c87; --primary-900: #1e3a8a;
/* 中性色 */ /* 中性色 */
--gray-50: #f9fafb; --gray-50: #f9fafb;