feat: 实现时间线拖拽排序功能及PWA支持
Some checks failed
test/timeline-frontend/pipeline/head Something is wrong with the build of this commit
Some checks failed
test/timeline-frontend/pipeline/head Something is wrong with the build of this commit
新增时间线节点的拖拽排序功能,使用dnd-kit库实现可排序网格布局。添加PWA支持,包括Service Worker注册和manifest配置。优化移动端适配,改进批量操作工具栏和撤销/重做功能。 重构用户登录和注册页面,修复登录跳转逻辑。调整画廊视图在不同设备上的显示效果。新增协作成员管理功能,支持批量修改权限。 修复请求错误处理中的跳转逻辑问题,避免重复跳转登录页。优化样式表,增强时间线卡片和图片展示的响应式布局。 新增多个API接口支持批量操作,包括排序、删除和时间修改。引入useBatchSelection和useHistory自定义Hook管理状态。添加UndoRedoToolbar组件提供撤销/重做功能。 实现Service Worker离线缓存策略,支持静态资源和API请求的缓存。新增PWA工具函数处理安装提示和更新检测。优化移动端交互,调整组件布局和操作按钮。
This commit is contained in:
159
src/pages/story/components/SortableTimelineGridItem.tsx
Normal file
159
src/pages/story/components/SortableTimelineGridItem.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* 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;
|
||||
Reference in New Issue
Block a user