fix
This commit is contained in:
parent
61a6cdb5ba
commit
1778f99d41
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue