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