mxivideo/src/pages/ResourceCategoryPage.tsx

331 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react'
import { Plus, Edit, Trash2, Search, Save, X } from 'lucide-react'
import { ResourceCategoryService, ResourceCategory } from '../services/resourceCategoryService'
const ResourceCategoryPage: React.FC = () => {
const [categories, setCategories] = useState<ResourceCategory[]>([])
const [loading, setLoading] = useState(true)
const [searchTerm, setSearchTerm] = useState('')
const [editingCategory, setEditingCategory] = useState<ResourceCategory | null>(null)
const [showCreateForm, setShowCreateForm] = useState(false)
const [formData, setFormData] = useState({
title: '',
ai_prompt: '',
color: '#FF6B6B'
})
// 预设颜色选项
const presetColors = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD',
'#FF7675', '#74B9FF', '#00B894', '#FDCB6E', '#E17055', '#A29BFE',
'#FD79A8', '#6C5CE7', '#00CEC9', '#55A3FF', '#FF9F43', '#26DE81'
]
useEffect(() => {
loadCategories()
}, [])
const loadCategories = async () => {
try {
setLoading(true)
const response = await ResourceCategoryService.getAllCategories()
console.log(`loadCategories`, response)
if (response.status && response.data) {
setCategories(response.data)
} else {
console.error('Failed to load categories:', response.msg)
}
} catch (error) {
console.error('Failed to load categories:', error)
} finally {
setLoading(false)
}
}
const handleCreateCategory = async () => {
try {
const response = await ResourceCategoryService.createCategory(formData)
console.log(`handleCreateCategory`, response)
if (response.status && response.data) {
setCategories([...categories, response.data])
setShowCreateForm(false)
setFormData({ title: '', ai_prompt: '', color: '#FF6B6B' })
} else {
console.error('创建失败:', response.msg || '未知错误')
}
} catch (error) {
console.error('Failed to create category:', error)
}
}
const handleUpdateCategory = async () => {
if (!editingCategory) return
try {
const response = await ResourceCategoryService.updateCategory(editingCategory.id, formData)
if (response.status && response.data) {
const updatedCategories = categories.map(cat =>
cat.id === editingCategory.id ? response.data! : cat
)
setCategories(updatedCategories)
setEditingCategory(null)
setFormData({ title: '', ai_prompt: '', color: '#FF6B6B' })
} else {
console.error('更新失败:', response.msg || '未知错误')
}
} catch (error) {
console.error('Failed to update category:', error)
}
}
const handleDeleteCategory = async (categoryId: string) => {
if (!confirm('确定要删除这个分类吗?')) return
try {
const response = await ResourceCategoryService.deleteCategory(categoryId)
if (response.status) {
setCategories(categories.filter(cat => cat.id !== categoryId))
} else {
console.error('删除失败:', response.msg || '未知错误')
}
} catch (error) {
console.error('Failed to delete category:', error)
}
}
const startEdit = (category: ResourceCategory) => {
setEditingCategory(category)
setFormData({
title: category.title,
ai_prompt: category.ai_prompt,
color: category.color
})
setShowCreateForm(false)
}
const cancelEdit = () => {
setEditingCategory(null)
setShowCreateForm(false)
setFormData({ title: '', ai_prompt: '', color: '#FF6B6B' })
}
const filteredCategories = categories.filter(category =>
category.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
category.ai_prompt.toLowerCase().includes(searchTerm.toLowerCase())
)
if (loading) {
return (
<div className="flex items-center justify-center h-64">
<div className="text-gray-500">...</div>
</div>
)
}
return (
<div className="p-6">
{/* 页面标题 */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900"></h1>
<p className="text-gray-600 mt-2">AI识别提示词和展示颜色</p>
</div>
{/* 搜索和创建按钮 */}
<div className="flex items-center justify-between mb-6">
<div className="relative flex-1 max-w-md">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
<input
type="text"
placeholder="搜索分类..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
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"
/>
</div>
<button
onClick={() => {
setShowCreateForm(true)
setEditingCategory(null)
setFormData({ title: '', ai_prompt: '', color: '#FF6B6B' })
}}
className="ml-4 flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
<Plus size={20} className="mr-2" />
</button>
</div>
{/* 分类列表 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredCategories.map((category) => (
<div key={category.id} className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
{/* 分类标题和颜色 */}
<div className="flex items-center justify-between mb-4">
<div className="flex items-center">
<div
className="w-4 h-4 rounded-full mr-3"
style={{ backgroundColor: category.color }}
/>
<h3 className="text-lg font-semibold text-gray-900">{category.title}</h3>
</div>
<div className="flex items-center space-x-2">
<button
onClick={() => startEdit(category)}
className="p-2 text-gray-400 hover:text-blue-600 transition-colors"
title="编辑"
>
<Edit size={16} />
</button>
<button
onClick={() => handleDeleteCategory(category.id)}
className="p-2 text-gray-400 hover:text-red-600 transition-colors"
title="删除"
>
<Trash2 size={16} />
</button>
</div>
</div>
{/* AI提示词 */}
<div className="mb-4">
<p className="text-sm text-gray-600 leading-relaxed">{category.ai_prompt}</p>
</div>
{/* 创建时间 */}
<div className="text-xs text-gray-400">
{new Date(category.created_at).toLocaleDateString()}
</div>
</div>
))}
</div>
{/* 空状态 */}
{filteredCategories.length === 0 && (
<div className="text-center py-12">
<div className="text-gray-400 mb-4">
{searchTerm ? '没有找到匹配的分类' : '暂无分类'}
</div>
{!searchTerm && (
<button
onClick={() => setShowCreateForm(true)}
className="text-blue-600 hover:text-blue-700"
>
</button>
)}
</div>
)}
{/* 创建/编辑表单弹窗 */}
{(showCreateForm || editingCategory) && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
{/* 表单标题 */}
<div className="flex items-center justify-between p-6 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900">
{editingCategory ? '编辑分类' : '新建分类'}
</h2>
<button
onClick={cancelEdit}
className="text-gray-400 hover:text-gray-600"
>
<X size={24} />
</button>
</div>
{/* 表单内容 */}
<div className="p-6 space-y-4">
{/* 分类标题 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<input
type="text"
value={formData.title}
onChange={(e) => 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"
/>
</div>
{/* AI识别提示词 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
AI识别提示词
</label>
<textarea
value={formData.ai_prompt}
onChange={(e) => setFormData({ ...formData, ai_prompt: e.target.value })}
placeholder="描述这类素材的特征用于AI自动分类"
rows={3}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none"
/>
</div>
{/* 展示颜色 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<div className="flex items-center space-x-3 mb-3">
<div
className="w-8 h-8 rounded-lg border-2 border-gray-300"
style={{ backgroundColor: formData.color }}
/>
<input
type="color"
value={formData.color}
onChange={(e) => setFormData({ ...formData, color: e.target.value })}
className="w-16 h-8 border border-gray-300 rounded cursor-pointer"
/>
<span className="text-sm text-gray-600">{formData.color}</span>
</div>
{/* 预设颜色 */}
<div className="grid grid-cols-9 gap-2">
{presetColors.map((color) => (
<button
key={color}
onClick={() => setFormData({ ...formData, color })}
className={`w-8 h-8 rounded-lg border-2 transition-all ${
formData.color === color
? 'border-gray-800 scale-110'
: 'border-gray-300 hover:border-gray-400'
}`}
style={{ backgroundColor: color }}
title={color}
/>
))}
</div>
</div>
</div>
{/* 表单按钮 */}
<div className="flex items-center justify-end space-x-3 p-6 border-t border-gray-200">
<button
onClick={cancelEdit}
className="px-4 py-2 text-gray-600 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
>
</button>
<button
onClick={editingCategory ? handleUpdateCategory : handleCreateCategory}
disabled={!formData.title.trim() || !formData.ai_prompt.trim()}
className="flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
>
<Save size={16} className="mr-2" />
{editingCategory ? '保存' : '创建'}
</button>
</div>
</div>
</div>
)}
</div>
)
}
export default ResourceCategoryPage