This commit is contained in:
root 2025-07-13 00:12:02 +08:00
parent 61a6cdb5ba
commit 1778f99d41
2 changed files with 185 additions and 39 deletions

View File

@ -1,7 +1,7 @@
import React from 'react'
import { SegmentContextMenu } from './SegmentContextMenu'
import { SegmentTooltip } from './SegmentTooltip'
import { ResourceCategoryServiceV2, ResourceCategoryV2 } from '../../services/resourceCategoryServiceV2'
import { useCategoriesData, useCategoryActions, useActiveCategoriesOnly } from '../../stores/useCategoryStore'
export interface TrackSegment {
id: string
@ -48,27 +48,15 @@ export const TrackTimeline: React.FC<TrackTimelineProps> = ({
}>({ isOpen: false, position: { x: 0, y: 0 }, segment: null })
const [hoveredSegment, setHoveredSegment] = React.useState<TrackSegment | null>(null)
const [mousePosition, setMousePosition] = React.useState({ x: 0, y: 0 })
const [categories, setCategories] = React.useState<ResourceCategoryV2[]>([])
const [loadingCategories, setLoadingCategories] = React.useState(false)
// 加载分类数据
const loadCategories = React.useCallback(async () => {
try {
setLoadingCategories(true)
const result = await ResourceCategoryServiceV2.getAllCategories({
include_cloud: true
})
setCategories(result)
} catch (error) {
console.error('Failed to load categories:', error)
} finally {
setLoadingCategories(false)
}
}, [])
// 使用 Zustand store 管理分类数据
const { loading: loadingCategories } = useCategoriesData()
const activeCategories = useActiveCategoriesOnly()
const { loadCategories } = useCategoryActions()
// 组件挂载时加载分类
// 组件挂载时加载分类(如果还没有加载)
React.useEffect(() => {
loadCategories()
loadCategories() // store 内部会处理缓存和重复加载
}, [])
const getSegmentColor = (type: string) => {
@ -108,13 +96,9 @@ export const TrackTimeline: React.FC<TrackTimelineProps> = ({
return `${minutes}:${secs.padStart(5, '0')}`
}
const handleSegmentDoubleClick = async (segment: TrackSegment) => {
const handleSegmentDoubleClick = (segment: TrackSegment) => {
setEditingSegmentId(segment.id)
// 确保分类数据已加载
if (categories.length === 0 && !loadingCategories) {
await loadCategories()
}
// Zustand store 会自动处理分类数据的加载和缓存
}
const handleSegmentRightClick = (e: React.MouseEvent, segment: TrackSegment) => {
@ -156,14 +140,10 @@ export const TrackTimeline: React.FC<TrackTimelineProps> = ({
setContextMenu({ isOpen: false, position: { x: 0, y: 0 }, segment: null })
}
const handleContextMenuEdit = async () => {
const handleContextMenuEdit = () => {
if (contextMenu.segment) {
setEditingSegmentId(contextMenu.segment.id)
// 确保分类数据已加载
if (categories.length === 0 && !loadingCategories) {
await loadCategories()
}
// Zustand store 会自动处理分类数据的加载和缓存
}
}
@ -240,19 +220,17 @@ export const TrackTimeline: React.FC<TrackTimelineProps> = ({
onBlur={() => setEditingSegmentId(null)}
className="w-full bg-white text-gray-900 border border-gray-300 rounded px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer"
autoFocus
size={Math.min(categories.length + 2, 8)} // 限制下拉框高度
size={Math.min(activeCategories.length + 2, 8)} // 限制下拉框高度
>
<option value="">...</option>
<option value={segment.name} className="text-gray-600">
: {segment.name}
</option>
{categories
.filter(category => category.is_active)
.map((category) => (
<option key={category.id} value={category.title} className="flex items-center">
{category.title}
</option>
))}
{activeCategories.map((category) => (
<option key={category.id} value={category.title} className="flex items-center">
{category.title}
</option>
))}
</select>
)}
</div>

View File

@ -0,0 +1,168 @@
import { create } from 'zustand'
import { ResourceCategoryServiceV2, ResourceCategoryV2 } from '../services/resourceCategoryServiceV2'
interface CategoryState {
// 状态
categories: ResourceCategoryV2[]
loading: boolean
error: string | null
lastLoadTime: number
// 缓存控制
cacheTimeout: number // 缓存超时时间(毫秒)
// 操作方法
loadCategories: (force?: boolean) => Promise<void>
refreshCategories: () => Promise<void>
addCategory: (category: ResourceCategoryV2) => void
updateCategory: (categoryId: string, updates: Partial<ResourceCategoryV2>) => void
removeCategory: (categoryId: string) => void
clearError: () => void
// 查询方法
getCategoryById: (id: string) => ResourceCategoryV2 | undefined
getActiveCategoriesOnly: () => ResourceCategoryV2[]
getCategoriesByColor: (color: string) => ResourceCategoryV2[]
searchCategories: (query: string) => ResourceCategoryV2[]
}
export const useCategoryStore = create<CategoryState>((set, get) => ({
// 初始状态
categories: [],
loading: false,
error: null,
lastLoadTime: 0,
cacheTimeout: 5 * 60 * 1000, // 5分钟缓存
// 加载分类数据
loadCategories: async (force = false) => {
const state = get()
const now = Date.now()
// 检查是否需要重新加载
if (!force &&
state.categories.length > 0 &&
(now - state.lastLoadTime) < state.cacheTimeout &&
!state.loading) {
return // 使用缓存数据
}
// 防止重复加载
if (state.loading) {
return
}
set({ loading: true, error: null })
try {
const result = await ResourceCategoryServiceV2.getAllCategories({
include_cloud: true
})
set({
categories: result,
loading: false,
error: null,
lastLoadTime: now
})
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '加载分类失败'
console.error('Failed to load categories:', error)
set({
loading: false,
error: errorMessage
})
}
},
// 强制刷新分类数据
refreshCategories: async () => {
await get().loadCategories(true)
},
// 添加新分类
addCategory: (category: ResourceCategoryV2) => {
set(state => ({
categories: [...state.categories, category]
}))
},
// 更新分类
updateCategory: (categoryId: string, updates: Partial<ResourceCategoryV2>) => {
set(state => ({
categories: state.categories.map(category =>
category.id === categoryId
? { ...category, ...updates }
: category
)
}))
},
// 删除分类
removeCategory: (categoryId: string) => {
set(state => ({
categories: state.categories.filter(category => category.id !== categoryId)
}))
},
// 清除错误
clearError: () => {
set({ error: null })
},
// 根据ID获取分类
getCategoryById: (id: string) => {
return get().categories.find(category => category.id === id)
},
// 获取激活的分类
getActiveCategoriesOnly: () => {
return get().categories.filter(category => category.is_active)
},
// 根据颜色获取分类
getCategoriesByColor: (color: string) => {
return get().categories.filter(category => category.color === color)
},
// 搜索分类
searchCategories: (query: string) => {
const lowerQuery = query.toLowerCase()
return get().categories.filter(category =>
category.title.toLowerCase().includes(lowerQuery) ||
category.ai_prompt.toLowerCase().includes(lowerQuery)
)
}
}))
// 导出便捷的 hooks
export const useCategoriesData = () => {
const { categories, loading, error } = useCategoryStore()
return { categories, loading, error }
}
export const useActiveCategoriesOnly = () => {
const getActiveCategoriesOnly = useCategoryStore(state => state.getActiveCategoriesOnly)
return getActiveCategoriesOnly()
}
export const useCategoryActions = () => {
const {
loadCategories,
refreshCategories,
addCategory,
updateCategory,
removeCategory,
clearError
} = useCategoryStore()
return {
loadCategories,
refreshCategories,
addCategory,
updateCategory,
removeCategory,
clearError
}
}