This commit is contained in:
root 2025-07-10 23:48:08 +08:00
parent 562e49f028
commit 538f223217
2 changed files with 78 additions and 9 deletions

View File

@ -27,6 +27,7 @@ interface TrackTimelineProps {
currentTime: number currentTime: number
onSegmentClick?: (segment: TrackSegment) => void onSegmentClick?: (segment: TrackSegment) => void
onSegmentHover?: (segment: TrackSegment | null) => void onSegmentHover?: (segment: TrackSegment | null) => void
onSegmentNameChange?: (segmentId: string, newName: string) => void
} }
export const TrackTimeline: React.FC<TrackTimelineProps> = ({ export const TrackTimeline: React.FC<TrackTimelineProps> = ({
@ -34,8 +35,11 @@ export const TrackTimeline: React.FC<TrackTimelineProps> = ({
totalDuration, totalDuration,
currentTime, currentTime,
onSegmentClick, onSegmentClick,
onSegmentHover onSegmentHover,
onSegmentNameChange
}) => { }) => {
const [editingSegmentId, setEditingSegmentId] = React.useState<string | null>(null)
const [editingName, setEditingName] = React.useState('')
const getSegmentColor = (type: string) => { const getSegmentColor = (type: string) => {
switch (type) { switch (type) {
case 'video': return 'bg-blue-500 hover:bg-blue-600' case 'video': return 'bg-blue-500 hover:bg-blue-600'
@ -73,6 +77,38 @@ export const TrackTimeline: React.FC<TrackTimelineProps> = ({
return `${minutes}:${secs.padStart(5, '0')}` return `${minutes}:${secs.padStart(5, '0')}`
} }
const handleSegmentDoubleClick = (segment: TrackSegment) => {
setEditingSegmentId(segment.id)
setEditingName(segment.name)
}
const handleSegmentRightClick = (e: React.MouseEvent, segment: TrackSegment) => {
e.preventDefault()
setEditingSegmentId(segment.id)
setEditingName(segment.name)
}
const handleNameSubmit = (segmentId: string) => {
if (onSegmentNameChange && editingName.trim()) {
onSegmentNameChange(segmentId, editingName.trim())
}
setEditingSegmentId(null)
setEditingName('')
}
const handleNameCancel = () => {
setEditingSegmentId(null)
setEditingName('')
}
const handleKeyDown = (e: React.KeyboardEvent, segmentId: string) => {
if (e.key === 'Enter') {
handleNameSubmit(segmentId)
} else if (e.key === 'Escape') {
handleNameCancel()
}
}
return ( return (
<div className="border border-gray-200 rounded-lg overflow-hidden"> <div className="border border-gray-200 rounded-lg overflow-hidden">
{/* Track Header */} {/* Track Header */}
@ -122,16 +158,28 @@ export const TrackTimeline: React.FC<TrackTimelineProps> = ({
minWidth: '80px' minWidth: '80px'
}} }}
onClick={() => onSegmentClick?.(segment)} onClick={() => onSegmentClick?.(segment)}
onDoubleClick={() => handleSegmentDoubleClick(segment)}
onContextMenu={(e) => handleSegmentRightClick(e, segment)}
onMouseEnter={() => onSegmentHover?.(segment)} onMouseEnter={() => onSegmentHover?.(segment)}
onMouseLeave={() => onSegmentHover?.(null)} onMouseLeave={() => onSegmentHover?.(null)}
title={`${segment.name}\n类型: ${segment.type}\n开始: ${formatTime(segment.start_time)}\n结束: ${formatTime(segment.end_time)}\n时长: ${formatTime(segment.duration)}${segment.resource_path ? `\n资源: ${segment.resource_path}` : ''}`} title={`${segment.name}\n类型: ${segment.type}\n开始: ${formatTime(segment.start_time)}\n结束: ${formatTime(segment.end_time)}\n时长: ${formatTime(segment.duration)}${segment.resource_path ? `\n资源: ${segment.resource_path}` : ''}`}
> >
<div className="truncate flex-1"> {editingSegmentId === segment.id ? (
{segment.name} <input
</div> type="text"
<div className="text-xs opacity-75 ml-2"> value={editingName}
{formatTime(segment.duration)} onChange={(e) => setEditingName(e.target.value)}
</div> onBlur={() => handleNameSubmit(segment.id)}
onKeyDown={(e) => handleKeyDown(e, segment.id)}
className="w-full bg-transparent text-inherit border-none outline-none"
autoFocus
onClick={(e) => e.stopPropagation()}
/>
) : (
<div className="truncate flex-1">
{segment.name}
</div>
)}
</div> </div>
) )
})} })}

View File

@ -108,6 +108,26 @@ const TemplateDetailPage: React.FC = () => {
} }
} }
const handleSegmentNameChange = (segmentId: string, newName: string) => {
if (!templateDetail) return
// Update the segment name in the local state
const updatedDetail = {
...templateDetail,
tracks: templateDetail.tracks.map(track => ({
...track,
segments: track.segments.map(segment =>
segment.id === segmentId ? { ...segment, name: newName } : segment
)
}))
}
setTemplateDetail(updatedDetail)
// TODO: Save to backend
console.log(`Segment ${segmentId} renamed to: ${newName}`)
}
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center"> <div className="min-h-screen bg-gray-50 flex items-center justify-center">
@ -266,6 +286,7 @@ const TemplateDetailPage: React.FC = () => {
// Could show segment details in a tooltip // Could show segment details in a tooltip
console.log('Hovering segment:', segment?.name) console.log('Hovering segment:', segment?.name)
}} }}
onSegmentNameChange={handleSegmentNameChange}
/> />
))} ))}
</div> </div>