This commit is contained in:
root 2025-07-11 01:22:52 +08:00
parent d6dd6109c0
commit c973aaa58c
2 changed files with 76 additions and 116 deletions

View File

@ -53,57 +53,7 @@ class ResourceCategoryManager:
def _create_default_categories(self) -> List[ResourceCategory]: def _create_default_categories(self) -> List[ResourceCategory]:
"""创建默认分类""" """创建默认分类"""
now = datetime.now().isoformat() default_categories = []
default_categories = [
ResourceCategory(
id=str(uuid.uuid4()),
title="人物视频",
ai_prompt="包含人物、人脸、人体的视频素材",
color="#FF6B6B",
created_at=now,
updated_at=now
),
ResourceCategory(
id=str(uuid.uuid4()),
title="风景视频",
ai_prompt="自然风景、城市景观、建筑物的视频素材",
color="#4ECDC4",
created_at=now,
updated_at=now
),
ResourceCategory(
id=str(uuid.uuid4()),
title="动物视频",
ai_prompt="动物、宠物、野生动物的视频素材",
color="#45B7D1",
created_at=now,
updated_at=now
),
ResourceCategory(
id=str(uuid.uuid4()),
title="音乐音效",
ai_prompt="背景音乐、音效、声音素材",
color="#96CEB4",
created_at=now,
updated_at=now
),
ResourceCategory(
id=str(uuid.uuid4()),
title="文字图片",
ai_prompt="文字、标题、图标、图形素材",
color="#FFEAA7",
created_at=now,
updated_at=now
),
ResourceCategory(
id=str(uuid.uuid4()),
title="特效素材",
ai_prompt="转场、特效、动画、滤镜素材",
color="#DDA0DD",
created_at=now,
updated_at=now
)
]
self._save_categories(default_categories) self._save_categories(default_categories)
return default_categories return default_categories
@ -123,7 +73,7 @@ class ResourceCategoryManager:
def get_all_categories(self) -> List[Dict]: def get_all_categories(self) -> List[Dict]:
"""获取所有分类""" """获取所有分类"""
return [asdict(category) for category in self.categories if category.is_active] return [asdict(category) for category in self.categories]
def get_category_by_id(self, category_id: str) -> Optional[Dict]: def get_category_by_id(self, category_id: str) -> Optional[Dict]:
"""根据ID获取分类""" """根据ID获取分类"""
@ -192,9 +142,7 @@ class ResourceCategoryManager:
results = [] results = []
for category in self.categories: for category in self.categories:
if (category.is_active and if (keyword in category.title.lower() or keyword in category.ai_prompt.lower()):
(keyword in category.title.lower() or
keyword in category.ai_prompt.lower())):
results.append(asdict(category)) results.append(asdict(category))
return results return results

View File

@ -203,70 +203,79 @@ const ProjectManagePage: React.FC = () => {
{/* 项目列表 */} {/* 项目列表 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredProjects.map((project) => ( {filteredProjects.map((project) => (
<div key={project.id} className="bg-white rounded-lg shadow-sm border border-gray-200 p-6"> <div key={project.id} className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
{/* 项目标题 */} {/* 商品图片 */}
<div className="flex items-center justify-between mb-4"> <div className="h-32 bg-gray-100 overflow-hidden">
<h3 className="text-lg font-semibold text-gray-900">{project.name}</h3> {project.product_image ? (
<>
<div className="flex items-center space-x-2"> <img
<button src={project.product_image.startsWith('http') ? project.product_image : `file://${project.product_image}`}
onClick={() => handleOpenDirectory(project.id)} alt={project.product_name || project.name}
className="p-2 text-gray-400 hover:text-green-600 transition-colors" className="w-full h-full object-cover"
title="打开目录" onError={(e) => {
> const target = e.target as HTMLImageElement
<FolderOpen size={16} /> target.style.display = 'none'
</button> const fallback = target.parentElement?.querySelector('.image-fallback') as HTMLElement
<button if (fallback) {
onClick={() => startEdit(project)} fallback.style.display = 'flex'
className="p-2 text-gray-400 hover:text-blue-600 transition-colors" }
title="编辑" }}
> />
<Edit size={16} /> <div className="image-fallback hidden w-full h-full bg-gray-200 flex items-center justify-center">
</button> <Package size={32} className="text-gray-400" />
<button </div>
onClick={() => handleDeleteProject(project.id)} </>
className="p-2 text-gray-400 hover:text-red-600 transition-colors" ) : (
title="删除" <div className="w-full h-full bg-gray-200 flex items-center justify-center">
> <Package size={32} className="text-gray-400" />
<Trash2 size={16} /> </div>
</button> )}
</div>
</div> </div>
{/* 项目信息 */} <div className="p-6">
<div className="space-y-3"> {/* 项目标题 */}
{/* 本地目录 */} <div className="flex items-center justify-between mb-4">
<div> <h3 className="text-lg font-semibold text-gray-900">{project.name}</h3>
<p className="text-sm text-gray-600">:</p>
<p className="text-sm text-gray-900 truncate" title={project.local_directory}> <div className="flex items-center space-x-2">
{project.local_directory} <button
</p> onClick={() => handleOpenDirectory(project.id)}
className="p-2 text-gray-400 hover:text-green-600 transition-colors"
title="打开目录"
>
<FolderOpen size={16} />
</button>
<button
onClick={() => startEdit(project)}
className="p-2 text-gray-400 hover:text-blue-600 transition-colors"
title="编辑"
>
<Edit size={16} />
</button>
<button
onClick={() => handleDeleteProject(project.id)}
className="p-2 text-gray-400 hover:text-red-600 transition-colors"
title="删除"
>
<Trash2 size={16} />
</button>
</div>
</div> </div>
{/* 商品信息 */} {/* 项目信息 */}
{project.product_name && ( <div className="space-y-3">
<div> {/* 商品信息 */}
<p className="text-sm text-gray-600">:</p> {project.product_name && (
<p className="text-sm text-gray-900">{project.product_name}</p> <div>
</div> <p className="text-sm text-gray-600">:</p>
)} <p className="text-sm text-gray-900">{project.product_name}</p>
{/* 商品图片 */}
{project.product_image && (
<div>
<p className="text-sm text-gray-600">:</p>
<div className="flex items-center space-x-2">
<Package size={16} className="text-gray-400" />
<p className="text-sm text-gray-900 truncate flex-1" title={project.product_image}>
{project.product_image.split('/').pop()}
</p>
</div> </div>
</div> )}
)}
{/* 创建时间 */} {/* 创建时间 */}
<div className="text-xs text-gray-400 pt-2 border-t border-gray-100"> <div className="text-xs text-gray-400 pt-2 border-t border-gray-100">
{new Date(project.created_at).toLocaleDateString()} {new Date(project.created_at).toLocaleDateString()}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -391,16 +400,19 @@ const ProjectManagePage: React.FC = () => {
<p className="text-xs text-gray-500 mb-1">:</p> <p className="text-xs text-gray-500 mb-1">:</p>
<div className="w-20 h-20 border border-gray-200 rounded-lg overflow-hidden bg-gray-50 flex items-center justify-center"> <div className="w-20 h-20 border border-gray-200 rounded-lg overflow-hidden bg-gray-50 flex items-center justify-center">
<img <img
src={`file://${formData.product_image}`} src={formData.product_image.startsWith('http') ? formData.product_image : `file://${formData.product_image}`}
alt="商品图片预览" alt="商品图片预览"
className="w-full h-full object-cover" className="w-full h-full object-cover"
onError={(e) => { onError={(e) => {
const target = e.target as HTMLImageElement const target = e.target as HTMLImageElement
target.style.display = 'none' target.style.display = 'none'
target.nextElementSibling!.classList.remove('hidden') const fallback = target.parentElement?.querySelector('.fallback-text') as HTMLElement
if (fallback) {
fallback.style.display = 'block'
}
}} }}
/> />
<div className="hidden text-xs text-gray-400 text-center p-2"> <div className="fallback-text hidden text-xs text-gray-400 text-center p-2">
</div> </div>
</div> </div>