Files
timeline-frontend/src/pages/story/components/SortableTimelineGridItem.tsx
jianghao 97a5ad3a00
Some checks failed
test/timeline-frontend/pipeline/head Something is wrong with the build of this commit
feat: 实现时间线拖拽排序功能及PWA支持
新增时间线节点的拖拽排序功能,使用dnd-kit库实现可排序网格布局。添加PWA支持,包括Service Worker注册和manifest配置。优化移动端适配,改进批量操作工具栏和撤销/重做功能。

重构用户登录和注册页面,修复登录跳转逻辑。调整画廊视图在不同设备上的显示效果。新增协作成员管理功能,支持批量修改权限。

修复请求错误处理中的跳转逻辑问题,避免重复跳转登录页。优化样式表,增强时间线卡片和图片展示的响应式布局。

新增多个API接口支持批量操作,包括排序、删除和时间修改。引入useBatchSelection和useHistory自定义Hook管理状态。添加UndoRedoToolbar组件提供撤销/重做功能。

实现Service Worker离线缓存策略,支持静态资源和API请求的缓存。新增PWA工具函数处理安装提示和更新检测。优化移动端交互,调整组件布局和操作按钮。
2026-02-24 10:33:10 +08:00

160 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* SortableTimelineGridItem - 可排序的时间线网格项组件
*
* 功能描述:
* 该组件包装 TimelineGridItem为其添加拖拽排序能力。
* 使用 dnd-kit 的 useSortable Hook 实现拖拽功能。
*
* 设计思路:
* 1. 使用 useSortable Hook 获取拖拽属性
* 2. 通过 CSS transform 实现拖拽动画
* 3. 拖拽时显示占位符和视觉反馈
* 4. 支持禁用拖拽(移动端默认禁用)
*
* @author Timeline Team
* @date 2024
*/
import { StoryItem } from '@/pages/story/data';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { DragOutlined } from '@ant-design/icons';
import React, { memo, CSSProperties } from 'react';
import TimelineGridItem from './TimelineGridItem';
/**
* 组件属性接口
* @property item - 时间线节点数据
* @property disabled - 是否禁用拖拽
* @property handleOption - 操作回调
* @property onOpenDetail - 打开详情回调
* @property refresh - 刷新数据回调
* @property disableEdit - 是否禁用编辑
*/
interface SortableTimelineGridItemProps {
item: StoryItem;
disabled?: boolean;
handleOption: (item: StoryItem, option: 'add' | 'edit' | 'addSubItem' | 'editSubItem') => void;
onOpenDetail: (item: StoryItem) => void;
refresh: () => void;
disableEdit?: boolean;
}
/**
* SortableTimelineGridItem 组件
* 为时间线节点添加拖拽排序能力
*/
const SortableTimelineGridItem: React.FC<SortableTimelineGridItemProps> = memo(({
item,
disabled = false,
handleOption,
onOpenDetail,
refresh,
disableEdit = false,
}) => {
/**
* useSortable Hook 核心功能:
* - attributes: 可访问性属性(如 tabindex
* - listeners: 拖拽事件监听器
* - setNodeRef: 设置 DOM 节点引用
* - transform: CSS transform 值
* - transition: CSS transition 值
* - isDragging: 是否正在拖拽
*/
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({
id: item.instanceId,
disabled: disabled,
data: {
type: 'timeline-item',
item,
},
});
/**
* 构建拖拽样式
* - transform: 应用拖拽位移和缩放
* - transition: 平滑过渡动画
* - opacity: 拖拽时降低透明度
* - zIndex: 拖拽时提升层级
*/
const style: CSSProperties = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : 1,
zIndex: isDragging ? 1000 : 'auto',
position: 'relative' as const,
};
return (
<div
ref={setNodeRef}
style={style}
className="sortable-timeline-item-wrapper"
>
{/* 拖拽手柄 - 仅在非禁用状态显示 */}
{!disabled && !disableEdit && (
<div
className="drag-handle"
{...attributes}
{...listeners}
style={{
position: 'absolute',
top: 8,
left: 8,
zIndex: 20,
cursor: 'grab',
padding: '4px 8px',
background: 'rgba(0, 0, 0, 0.6)',
borderRadius: 4,
opacity: 0,
transition: 'opacity 0.2s ease',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
onClick={(e) => e.stopPropagation()}
>
<DragOutlined style={{ color: '#fff', fontSize: 14 }} />
</div>
)}
{/* 原有组件 */}
<TimelineGridItem
item={item}
handleOption={handleOption}
onOpenDetail={onOpenDetail}
refresh={refresh}
disableEdit={disableEdit}
/>
{/* 拖拽时的视觉反馈 */}
{isDragging && (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
border: '2px dashed #1890ff',
borderRadius: 8,
pointerEvents: 'none',
background: 'rgba(24, 144, 255, 0.1)',
}}
/>
)}
</div>
);
});
SortableTimelineGridItem.displayName = 'SortableTimelineGridItem';
export default SortableTimelineGridItem;