diff --git a/src/pages/ResourceCategoryPageV2.tsx b/src/pages/ResourceCategoryPageV2.tsx new file mode 100644 index 0000000..a19329a --- /dev/null +++ b/src/pages/ResourceCategoryPageV2.tsx @@ -0,0 +1,700 @@ +import React, { useState, useEffect } from 'react' +import { Plus, Edit, Trash2, Search, Save, X, Cloud, Users, Palette, BarChart3, Download, Upload } from 'lucide-react' +import { + ResourceCategoryServiceV2, + ResourceCategoryV2, + CategoryBatchItem +} from '../services/resourceCategoryServiceV2' + +const ResourceCategoryPageV2: React.FC = () => { + const [categories, setCategories] = useState([]) + const [loading, setLoading] = useState(true) + const [searchTerm, setSearchTerm] = useState('') + const [editingCategory, setEditingCategory] = useState(null) + const [showCreateForm, setShowCreateForm] = useState(false) + const [showBatchImport, setShowBatchImport] = useState(false) + const [includeCloud, setIncludeCloud] = useState(true) + const [showDisabled, setShowDisabled] = useState(true) + const [selectedColor, setSelectedColor] = useState('') + const [categoryCount, setCategoryCount] = useState(0) + const [formData, setFormData] = useState({ + title: '', + ai_prompt: '', + color: '#FF6B6B', + is_cloud: false + }) + const [batchData, setBatchData] = useState('') + + // 预设颜色选项(扩展版) + const presetColors = [ + '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', + '#FF7675', '#74B9FF', '#00B894', '#FDCB6E', '#E17055', '#A29BFE', + '#FD79A8', '#6C5CE7', '#00CEC9', '#55A3FF', '#FF9F43', '#26DE81', + '#FF3838', '#FF9500', '#FFDD00', '#48CAE4', '#9B59B6', '#E74C3C', + '#F39C12', '#27AE60', '#3498DB', '#8E44AD', '#34495E', '#95A5A6' + ] + + useEffect(() => { + loadCategories() + loadCategoryCount() + }, [includeCloud]) + + const loadCategories = async () => { + try { + setLoading(true) + const result = await ResourceCategoryServiceV2.getAllCategories({ + include_cloud: includeCloud, + limit: 200 + }) + setCategories(result) + } catch (error) { + console.error('Failed to load categories:', error) + } finally { + setLoading(false) + } + } + + const loadCategoryCount = async () => { + try { + const count = await ResourceCategoryServiceV2.getCategoryCount(includeCloud) + setCategoryCount(count) + } catch (error) { + console.error('Failed to load category count:', error) + } + } + + const handleCreateCategory = async () => { + try { + const newCategory = await ResourceCategoryServiceV2.createCategory( + formData.title, + formData.ai_prompt, + formData.color, + formData.is_cloud + ) + + if (newCategory) { + setCategories([...categories, newCategory]) + setShowCreateForm(false) + setFormData({ title: '', ai_prompt: '', color: '#FF6B6B', is_cloud: false }) + await loadCategoryCount() + } + } catch (error) { + console.error('Failed to create category:', error) + alert('创建分类失败: ' + (error instanceof Error ? error.message : '未知错误')) + } + } + + const handleUpdateCategory = async () => { + if (!editingCategory) return + + try { + const updatedCategory = await ResourceCategoryServiceV2.updateCategory( + editingCategory.id, + { + title: formData.title, + ai_prompt: formData.ai_prompt, + color: formData.color + } + ) + + if (updatedCategory) { + const updatedCategories = categories.map(cat => + cat.id === editingCategory.id ? updatedCategory : cat + ) + setCategories(updatedCategories) + setEditingCategory(null) + setFormData({ title: '', ai_prompt: '', color: '#FF6B6B', is_cloud: false }) + } + } catch (error) { + console.error('Failed to update category:', error) + alert('更新分类失败: ' + (error instanceof Error ? error.message : '未知错误')) + } + } + + const handleDeleteCategory = async (categoryId: string) => { + if (!confirm('确定要删除这个分类吗?此操作不可恢复。')) return + + try { + await ResourceCategoryServiceV2.deleteCategory(categoryId, true) + setCategories(categories.filter(cat => cat.id !== categoryId)) + await loadCategoryCount() + } catch (error) { + console.error('Failed to delete category:', error) + alert('删除分类失败: ' + (error instanceof Error ? error.message : '未知错误')) + } + } + + const handleToggleCategory = async (categoryId: string, isActive: boolean) => { + try { + if (isActive) { + await ResourceCategoryServiceV2.activateCategory(categoryId) + } else { + await ResourceCategoryServiceV2.deactivateCategory(categoryId) + } + + const updatedCategories = categories.map(cat => + cat.id === categoryId ? { ...cat, is_active: isActive } : cat + ) + setCategories(updatedCategories) + } catch (error) { + console.error('Failed to toggle category:', error) + alert('切换分类状态失败: ' + (error instanceof Error ? error.message : '未知错误')) + } + } + + const handleBatchImport = async () => { + try { + const batchItems: CategoryBatchItem[] = JSON.parse(batchData) + const result = await ResourceCategoryServiceV2.batchCreateCategories(batchItems) + + alert(`批量导入完成: 成功 ${result.success} 个,失败 ${result.failed} 个`) + setShowBatchImport(false) + setBatchData('') + await loadCategories() + await loadCategoryCount() + } catch (error) { + console.error('Failed to batch import:', error) + alert('批量导入失败: ' + (error instanceof Error ? error.message : '数据格式错误')) + } + } + + const handleSearchByColor = async (color: string) => { + if (color === selectedColor) { + // 取消颜色筛选 + setSelectedColor('') + await loadCategories() + } else { + // 按颜色筛选 + setSelectedColor(color) + try { + const result = await ResourceCategoryServiceV2.getCategoriesByColor(color, { + include_cloud: includeCloud + }) + setCategories(result) + } catch (error) { + console.error('Failed to search by color:', error) + } + } + } + + const handleSearch = async () => { + if (!searchTerm.trim()) { + await loadCategories() + return + } + + try { + const result = await ResourceCategoryServiceV2.searchCategories(searchTerm, { + include_cloud: includeCloud + }) + setCategories(result) + } catch (error) { + console.error('Failed to search categories:', error) + } + } + + const startEdit = (category: ResourceCategoryV2) => { + setEditingCategory(category) + setFormData({ + title: category.title, + ai_prompt: category.ai_prompt, + color: category.color, + is_cloud: category.is_cloud + }) + setShowCreateForm(false) + } + + const cancelEdit = () => { + setEditingCategory(null) + setShowCreateForm(false) + setShowBatchImport(false) + setFormData({ title: '', ai_prompt: '', color: '#FF6B6B', is_cloud: false }) + setBatchData('') + } + + const exportCategories = () => { + const exportData = categories.map(cat => ({ + title: cat.title, + ai_prompt: cat.ai_prompt, + color: cat.color, + is_cloud: cat.is_cloud + })) + + const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = 'resource_categories.json' + a.click() + URL.revokeObjectURL(url) + } + + const filteredCategories = categories.filter(category => { + // 搜索过滤 + const matchesSearch = !searchTerm || + category.title.toLowerCase().includes(searchTerm.toLowerCase()) || + category.ai_prompt.toLowerCase().includes(searchTerm.toLowerCase()) + + // 状态过滤 + const matchesStatus = showDisabled || category.is_active + + return matchesSearch && matchesStatus + }) + + // 统计信息 + const stats = { + total: categories.length, + active: categories.filter(c => c.is_active).length, + cloud: categories.filter(c => c.is_cloud).length, + user: categories.filter(c => !c.is_cloud).length + } + + if (loading) { + return ( +
+
加载中...
+
+ ) + } + + return ( +
+ {/* 页面标题和统计 */} +
+
+
+

素材分类管理 V2

+

基于最新API的增强版分类管理,支持云端分类、批量操作等高级功能

+
+ + {/* 统计卡片 */} +
+
+
{stats.total}
+
总数
+
+
+
{stats.active}
+
启用
+
+
+
{stats.cloud}
+
云端
+
+
+
{stats.user}
+
用户
+
+
+
+
+ + {/* 搜索和操作栏 */} +
+
+ {/* 搜索框 */} +
+ + setSearchTerm(e.target.value)} + onKeyPress={(e) => e.key === 'Enter' && handleSearch()} + className="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent w-full" + /> +
+ + {/* 筛选选项 */} +
+ + + +
+
+ + {/* 操作按钮 */} +
+ + + + + +
+
+ + {/* 颜色筛选栏 */} +
+
+ + 按颜色筛选: +
+ {presetColors.slice(0, 12).map((color) => ( + + )} +
+
+
+ + {/* 分类列表 */} +
+ {filteredCategories.map((category) => ( +
+ {/* 分类标题和标识 */} +
+
+
+

+ {category.title} +

+ + {/* 状态标识 */} +
+ {category.is_cloud && ( + + + 云端 + + )} + {!category.is_active && ( + + 已禁用 + + )} +
+
+ +
+ {/* 启用/禁用开关 */} + + + + +
+
+ + {/* AI提示词 */} +
+

{category.ai_prompt}

+
+ + {/* 分类信息 */} +
+
+ + {category.user_id} +
+
+ 创建于 {new Date(category.created_at).toLocaleDateString()} +
+
+
+ ))} +
+ + {/* 空状态 */} + {filteredCategories.length === 0 && ( +
+
+ {searchTerm || selectedColor ? '没有找到匹配的分类' : '暂无分类'} +
+ {!searchTerm && !selectedColor && ( + + )} +
+ )} + + {/* 创建/编辑表单弹窗 */} + {(showCreateForm || editingCategory) && ( +
+
+ {/* 表单标题 */} +
+

+ {editingCategory ? '编辑分类' : '新建分类'} +

+ +
+ + {/* 表单内容 */} +
+ {/* 分类标题 */} +
+ + setFormData({ ...formData, title: e.target.value })} + placeholder="请输入分类标题" + className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" + /> +
+ + {/* AI识别提示词 */} +
+ +