This commit is contained in:
root
2025-07-12 23:15:15 +08:00
parent 4994734ca1
commit 9c83676d3e
2 changed files with 1167 additions and 0 deletions

View File

@@ -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<ResourceCategoryV2[]>([])
const [loading, setLoading] = useState(true)
const [searchTerm, setSearchTerm] = useState('')
const [editingCategory, setEditingCategory] = useState<ResourceCategoryV2 | null>(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<string>('')
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 (
<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">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-gray-900"> V2</h1>
<p className="text-gray-600 mt-2">API的增强版分类管理</p>
</div>
{/* 统计卡片 */}
<div className="flex space-x-4">
<div className="bg-blue-50 rounded-lg p-3 text-center min-w-[80px]">
<div className="text-2xl font-bold text-blue-600">{stats.total}</div>
<div className="text-xs text-blue-500"></div>
</div>
<div className="bg-green-50 rounded-lg p-3 text-center min-w-[80px]">
<div className="text-2xl font-bold text-green-600">{stats.active}</div>
<div className="text-xs text-green-500"></div>
</div>
<div className="bg-purple-50 rounded-lg p-3 text-center min-w-[80px]">
<div className="text-2xl font-bold text-purple-600">{stats.cloud}</div>
<div className="text-xs text-purple-500"></div>
</div>
<div className="bg-orange-50 rounded-lg p-3 text-center min-w-[80px]">
<div className="text-2xl font-bold text-orange-600">{stats.user}</div>
<div className="text-xs text-orange-500"></div>
</div>
</div>
</div>
</div>
{/* 搜索和操作栏 */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center space-x-4 flex-1">
{/* 搜索框 */}
<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)}
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"
/>
</div>
{/* 筛选选项 */}
<div className="flex items-center space-x-3">
<label className="flex items-center text-sm text-gray-600">
<input
type="checkbox"
checked={includeCloud}
onChange={(e) => setIncludeCloud(e.target.checked)}
className="mr-2"
/>
<Cloud size={16} className="mr-1" />
</label>
<label className="flex items-center text-sm text-gray-600">
<input
type="checkbox"
checked={showDisabled}
onChange={(e) => setShowDisabled(e.target.checked)}
className="mr-2"
/>
</label>
</div>
</div>
{/* 操作按钮 */}
<div className="flex items-center space-x-3">
<button
onClick={exportCategories}
className="flex items-center px-3 py-2 text-gray-600 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
title="导出分类"
>
<Download size={16} className="mr-2" />
</button>
<button
onClick={() => setShowBatchImport(true)}
className="flex items-center px-3 py-2 text-gray-600 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
title="批量导入"
>
<Upload size={16} className="mr-2" />
</button>
<button
onClick={() => {
setShowCreateForm(true)
setEditingCategory(null)
setFormData({ title: '', ai_prompt: '', color: '#FF6B6B', is_cloud: false })
}}
className="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>
{/* 颜色筛选栏 */}
<div className="mb-6">
<div className="flex items-center space-x-2">
<Palette size={16} className="text-gray-500" />
<span className="text-sm text-gray-600">:</span>
<div className="flex space-x-2">
{presetColors.slice(0, 12).map((color) => (
<button
key={color}
onClick={() => handleSearchByColor(color)}
className={`w-6 h-6 rounded-full border-2 transition-all ${
selectedColor === color
? 'border-gray-800 scale-110'
: 'border-gray-300 hover:border-gray-400'
}`}
style={{ backgroundColor: color }}
title={`筛选颜色: ${color}`}
/>
))}
{selectedColor && (
<button
onClick={() => handleSearchByColor('')}
className="px-2 py-1 text-xs bg-gray-100 text-gray-600 rounded hover:bg-gray-200"
>
</button>
)}
</div>
</div>
</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 p-6 transition-all ${
category.is_active
? 'border-gray-200'
: 'border-gray-300 bg-gray-50 opacity-75'
}`}>
{/* 分类标题和标识 */}
<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 ${category.is_active ? 'text-gray-900' : 'text-gray-400'}`}>
{category.title}
</h3>
{/* 状态标识 */}
<div className="flex items-center ml-2 space-x-1">
{category.is_cloud && (
<span className="px-2 py-1 text-xs bg-purple-100 text-purple-600 rounded flex items-center">
<Cloud size={12} className="mr-1" />
</span>
)}
{!category.is_active && (
<span className="px-2 py-1 text-xs bg-gray-100 text-gray-500 rounded">
</span>
)}
</div>
</div>
<div className="flex items-center space-x-2">
{/* 启用/禁用开关 */}
<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={category.is_active}
onChange={(e) => handleToggleCategory(category.id, e.target.checked)}
className="sr-only peer"
/>
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
<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="删除"
disabled={category.is_cloud}
>
<Trash2 size={16} />
</button>
</div>
</div>
{/* AI提示词 */}
<div className="mb-4">
<p className="text-sm text-gray-600 leading-relaxed line-clamp-3">{category.ai_prompt}</p>
</div>
{/* 分类信息 */}
<div className="flex items-center justify-between text-xs text-gray-400">
<div className="flex items-center space-x-2">
<Users size={12} />
<span>{category.user_id}</span>
</div>
<div>
{new Date(category.created_at).toLocaleDateString()}
</div>
</div>
</div>
))}
</div>
{/* 空状态 */}
{filteredCategories.length === 0 && (
<div className="text-center py-12">
<div className="text-gray-400 mb-4">
{searchTerm || selectedColor ? '没有找到匹配的分类' : '暂无分类'}
</div>
{!searchTerm && !selectedColor && (
<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-8 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>
{/* 云端分类选项 */}
{!editingCategory && (
<div>
<label className="flex items-center text-sm text-gray-700">
<input
type="checkbox"
checked={formData.is_cloud}
onChange={(e) => setFormData({ ...formData, is_cloud: e.target.checked })}
className="mr-2"
/>
<Cloud size={16} className="mr-1" />
</label>
<p className="text-xs text-gray-500 mt-1"></p>
</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()}
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>
)}
{/* 批量导入弹窗 */}
{showBatchImport && (
<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-2xl 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"></h2>
<button
onClick={cancelEdit}
className="text-gray-400 hover:text-gray-600"
>
<X size={24} />
</button>
</div>
{/* 导入说明 */}
<div className="p-6">
<div className="mb-4">
<h3 className="text-sm font-medium text-gray-700 mb-2"></h3>
<div className="bg-gray-50 rounded-lg p-4 text-sm">
<p className="mb-2">JSON格式的分类数据</p>
<pre className="text-xs text-gray-600 bg-white p-2 rounded border overflow-x-auto">
{`[
{
"title": "视频素材",
"ai_prompt": "用于识别视频文件",
"color": "#FF6B6B",
"is_cloud": false
},
{
"title": "音频素材",
"ai_prompt": "用于识别音频文件",
"color": "#4ECDC4"
}
]`}
</pre>
</div>
</div>
{/* 数据输入 */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-2">
(JSON格式)
</label>
<textarea
value={batchData}
onChange={(e) => setBatchData(e.target.value)}
placeholder="请输入JSON格式的分类数据..."
rows={10}
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 font-mono text-sm"
/>
</div>
{/* 操作按钮 */}
<div className="flex items-center justify-end space-x-3">
<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={handleBatchImport}
disabled={!batchData.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"
>
<Upload size={16} className="mr-2" />
</button>
</div>
</div>
</div>
</div>
)}
</div>
)
}
export default ResourceCategoryPageV2

View File

@@ -0,0 +1,467 @@
import { invoke } from '@tauri-apps/api/core'
// 扩展的资源分类接口,包含新字段
export interface ResourceCategoryV2 {
id: string
title: string
ai_prompt: string
color: string
created_at: string
updated_at: string
is_active: boolean
is_cloud: boolean
user_id: string
}
// 新的响应格式
export interface ResourceCategoryResponse {
success: boolean
message: string
data?: any
output?: string
error?: string
}
// 请求接口
export interface CategoryListRequest {
user_id?: string
include_cloud?: boolean
limit?: number
verbose?: boolean
json_output?: boolean
}
export interface CategoryGetRequest {
category_id: string
user_id?: string
verbose?: boolean
json_output?: boolean
}
export interface CategoryCreateRequest {
title: string
ai_prompt?: string
color?: string
is_cloud?: boolean
user_id?: string
verbose?: boolean
json_output?: boolean
}
export interface CategoryUpdateRequest {
category_id: string
title?: string
ai_prompt?: string
color?: string
is_active?: boolean
user_id?: string
verbose?: boolean
json_output?: boolean
}
export interface CategoryDeleteRequest {
category_id: string
hard_delete?: boolean
user_id?: string
verbose?: boolean
json_output?: boolean
}
export interface CategorySearchRequest {
query: string
user_id?: string
include_cloud?: boolean
limit?: number
verbose?: boolean
json_output?: boolean
}
export interface CategoryColorRequest {
color: string
user_id?: string
include_cloud?: boolean
limit?: number
verbose?: boolean
json_output?: boolean
}
export interface CategoryBatchItem {
title: string
ai_prompt?: string
color?: string
is_cloud?: boolean
}
export interface CategoryBatchCreateRequest {
categories: CategoryBatchItem[]
user_id?: string
verbose?: boolean
json_output?: boolean
}
export interface BatchCreateResult {
total: number
success: number
failed: number
error?: string
}
export class ResourceCategoryServiceV2 {
private static defaultUserId = 'default'
/**
* 获取所有分类(新版本)
*/
static async getAllCategories(request?: Partial<CategoryListRequest>): Promise<ResourceCategoryV2[]> {
try {
const params: CategoryListRequest = {
user_id: this.defaultUserId,
include_cloud: true,
limit: 100,
verbose: false,
json_output: true,
...request
}
const response = await invoke<ResourceCategoryResponse>('get_all_resource_categories_cli', { request: params })
if (response.success && response.data) {
return Array.isArray(response.data) ? response.data : []
} else {
console.error('Failed to get categories:', response.error || response.message)
return []
}
} catch (error) {
console.error('Failed to get all categories:', error)
return []
}
}
/**
* 根据ID获取分类
*/
static async getCategoryById(categoryId: string, userId?: string): Promise<ResourceCategoryV2 | null> {
try {
const params: CategoryGetRequest = {
category_id: categoryId,
user_id: userId || this.defaultUserId,
verbose: false,
json_output: true
}
const response = await invoke<ResourceCategoryResponse>('get_resource_category_by_id_cli', { request: params })
if (response.success && response.data) {
return response.data
} else {
console.error('Failed to get category:', response.error || response.message)
return null
}
} catch (error) {
console.error('Failed to get category by id:', error)
return null
}
}
/**
* 创建新分类
*/
static async createCategory(
title: string,
aiPrompt?: string,
color?: string,
isCloud?: boolean,
userId?: string
): Promise<ResourceCategoryV2 | null> {
try {
const params: CategoryCreateRequest = {
title,
ai_prompt: aiPrompt || '',
color: color || '#FF5733',
is_cloud: isCloud || false,
user_id: userId || this.defaultUserId,
verbose: false,
json_output: true
}
const response = await invoke<ResourceCategoryResponse>('create_resource_category_cli', { request: params })
if (response.success && response.data) {
return response.data
} else {
console.error('Failed to create category:', response.error || response.message)
throw new Error(response.error || response.message || 'Failed to create category')
}
} catch (error) {
console.error('Failed to create category:', error)
throw error
}
}
/**
* 更新分类
*/
static async updateCategory(
categoryId: string,
updates: {
title?: string
ai_prompt?: string
color?: string
is_active?: boolean
},
userId?: string
): Promise<ResourceCategoryV2 | null> {
try {
const params: CategoryUpdateRequest = {
category_id: categoryId,
title: updates.title,
ai_prompt: updates.ai_prompt,
color: updates.color,
is_active: updates.is_active,
user_id: userId || this.defaultUserId,
verbose: false,
json_output: true
}
const response = await invoke<ResourceCategoryResponse>('update_resource_category_cli', { request: params })
if (response.success && response.data) {
return response.data
} else {
console.error('Failed to update category:', response.error || response.message)
throw new Error(response.error || response.message || 'Failed to update category')
}
} catch (error) {
console.error('Failed to update category:', error)
throw error
}
}
/**
* 删除分类
*/
static async deleteCategory(categoryId: string, hardDelete = true, userId?: string): Promise<boolean> {
try {
const params: CategoryDeleteRequest = {
category_id: categoryId,
hard_delete: hardDelete,
user_id: userId || this.defaultUserId,
verbose: false,
json_output: true
}
const response = await invoke<ResourceCategoryResponse>('delete_resource_category_cli', { request: params })
if (response.success) {
return true
} else {
console.error('Failed to delete category:', response.error || response.message)
throw new Error(response.error || response.message || 'Failed to delete category')
}
} catch (error) {
console.error('Failed to delete category:', error)
throw error
}
}
/**
* 搜索分类
*/
static async searchCategories(query: string, request?: Partial<CategorySearchRequest>): Promise<ResourceCategoryV2[]> {
try {
const params: CategorySearchRequest = {
query,
user_id: this.defaultUserId,
include_cloud: true,
limit: 50,
verbose: false,
json_output: true,
...request
}
const response = await invoke<ResourceCategoryResponse>('search_resource_categories_cli', { request: params })
if (response.success && response.data) {
return Array.isArray(response.data) ? response.data : []
} else {
console.error('Failed to search categories:', response.error || response.message)
return []
}
} catch (error) {
console.error('Failed to search categories:', error)
return []
}
}
/**
* 按颜色获取分类
*/
static async getCategoriesByColor(color: string, request?: Partial<CategoryColorRequest>): Promise<ResourceCategoryV2[]> {
try {
const params: CategoryColorRequest = {
color,
user_id: this.defaultUserId,
include_cloud: true,
limit: 50,
verbose: false,
json_output: true,
...request
}
const response = await invoke<ResourceCategoryResponse>('get_resource_categories_by_color_cli', { request: params })
if (response.success && response.data) {
return Array.isArray(response.data) ? response.data : []
} else {
console.error('Failed to get categories by color:', response.error || response.message)
return []
}
} catch (error) {
console.error('Failed to get categories by color:', error)
return []
}
}
/**
* 获取云端分类
*/
static async getCloudCategories(limit = 100): Promise<ResourceCategoryV2[]> {
try {
const params: CategoryListRequest = {
limit,
verbose: false,
json_output: true
}
const response = await invoke<ResourceCategoryResponse>('get_cloud_resource_categories_cli', { request: params })
if (response.success && response.data) {
return Array.isArray(response.data) ? response.data : []
} else {
console.error('Failed to get cloud categories:', response.error || response.message)
return []
}
} catch (error) {
console.error('Failed to get cloud categories:', error)
return []
}
}
/**
* 批量创建分类
*/
static async batchCreateCategories(categories: CategoryBatchItem[], userId?: string): Promise<BatchCreateResult> {
try {
const params: CategoryBatchCreateRequest = {
categories,
user_id: userId || this.defaultUserId,
verbose: false,
json_output: true
}
const response = await invoke<ResourceCategoryResponse>('batch_create_resource_categories_cli', { request: params })
if (response.success && response.data) {
return response.data
} else {
console.error('Failed to batch create categories:', response.error || response.message)
throw new Error(response.error || response.message || 'Failed to batch create categories')
}
} catch (error) {
console.error('Failed to batch create categories:', error)
throw error
}
}
/**
* 获取分类数量
*/
static async getCategoryCount(includeCloud = true, userId?: string): Promise<number> {
try {
const params: CategoryListRequest = {
user_id: userId || this.defaultUserId,
include_cloud: includeCloud,
verbose: false,
json_output: true
}
const response = await invoke<ResourceCategoryResponse>('get_resource_category_count_cli', { request: params })
if (response.success && response.data && typeof response.data.count === 'number') {
return response.data.count
} else {
console.error('Failed to get category count:', response.error || response.message)
return 0
}
} catch (error) {
console.error('Failed to get category count:', error)
return 0
}
}
/**
* 激活分类
*/
static async activateCategory(categoryId: string, userId?: string): Promise<boolean> {
try {
const params: CategoryGetRequest = {
category_id: categoryId,
user_id: userId || this.defaultUserId,
verbose: false,
json_output: true
}
const response = await invoke<ResourceCategoryResponse>('activate_resource_category_cli', { request: params })
if (response.success) {
return true
} else {
console.error('Failed to activate category:', response.error || response.message)
throw new Error(response.error || response.message || 'Failed to activate category')
}
} catch (error) {
console.error('Failed to activate category:', error)
throw error
}
}
/**
* 停用分类
*/
static async deactivateCategory(categoryId: string, userId?: string): Promise<boolean> {
try {
const params: CategoryGetRequest = {
category_id: categoryId,
user_id: userId || this.defaultUserId,
verbose: false,
json_output: true
}
const response = await invoke<ResourceCategoryResponse>('deactivate_resource_category_cli', { request: params })
if (response.success) {
return true
} else {
console.error('Failed to deactivate category:', response.error || response.message)
throw new Error(response.error || response.message || 'Failed to deactivate category')
}
} catch (error) {
console.error('Failed to deactivate category:', error)
throw error
}
}
/**
* 设置默认用户ID
*/
static setDefaultUserId(userId: string) {
this.defaultUserId = userId
}
/**
* 获取默认用户ID
*/
static getDefaultUserId(): string {
return this.defaultUserId
}
}