表单设计器
This commit is contained in:
parent
03d4d21424
commit
1058487821
@ -37,7 +37,6 @@ import {
|
||||
useSensor,
|
||||
useSensors,
|
||||
pointerWithin,
|
||||
rectIntersection,
|
||||
closestCenter
|
||||
} from '@dnd-kit/core';
|
||||
import { arrayMove } from '@dnd-kit/sortable';
|
||||
@ -135,35 +134,84 @@ const FormDesigner: React.FC<FormDesignerProps> = ({
|
||||
})
|
||||
);
|
||||
|
||||
// 自定义碰撞检测策略:优先使用指针检测,避免栅格内部区域干扰排序
|
||||
// 自定义碰撞检测策略:基于边界判断,区分"插入"和"拖入"
|
||||
const customCollisionDetection = useCallback((args: any) => {
|
||||
// 首先尝试使用 pointerWithin - 只有指针真正进入区域时才触发
|
||||
const pointerCollisions = pointerWithin(args);
|
||||
|
||||
if (pointerCollisions.length > 0) {
|
||||
// 过滤掉栅格列(grid-xxx-col-xxx),优先选择画布或字段
|
||||
const nonGridCollisions = pointerCollisions.filter((collision: any) => {
|
||||
const id = collision.id.toString();
|
||||
return !id.startsWith('grid-') || id === 'canvas' || id === 'canvas-bottom';
|
||||
});
|
||||
|
||||
if (nonGridCollisions.length > 0) {
|
||||
return nonGridCollisions;
|
||||
if (pointerCollisions.length === 0) {
|
||||
return closestCenter(args);
|
||||
}
|
||||
|
||||
// 查找栅格字段的碰撞
|
||||
const gridFieldCollisions = pointerCollisions.filter((collision: any) => {
|
||||
const fieldId = collision.id.toString();
|
||||
return fields.some(f => f.id === fieldId && f.type === 'grid');
|
||||
});
|
||||
|
||||
// 如果拖到栅格字段上,使用边界检测
|
||||
if (gridFieldCollisions.length > 0 && args.pointerCoordinates) {
|
||||
const gridCollision = gridFieldCollisions[0];
|
||||
const droppableContainer = args.droppableContainers.find(
|
||||
(container: any) => container.id === gridCollision.id
|
||||
);
|
||||
|
||||
if (droppableContainer?.rect?.current) {
|
||||
const rect = droppableContainer.rect.current;
|
||||
const pointer = args.pointerCoordinates;
|
||||
|
||||
// 计算鼠标在栅格字段中的相对垂直位置(0-1)
|
||||
const relativeY = (pointer.y - rect.top) / rect.height;
|
||||
|
||||
// 边缘阈值:上下各 20%
|
||||
const edgeThreshold = 0.2;
|
||||
|
||||
if (relativeY < edgeThreshold || relativeY > (1 - edgeThreshold)) {
|
||||
// 在边缘区域 → 返回栅格字段本身(插入到栅格前后)
|
||||
return [gridCollision];
|
||||
} else {
|
||||
// 在中心区域 → 查找栅格列(拖入栅格内部)
|
||||
const gridColumnCollisions = pointerCollisions.filter((collision: any) => {
|
||||
const id = collision.id.toString();
|
||||
return id.startsWith('grid-') && id.includes('-col-');
|
||||
});
|
||||
|
||||
if (gridColumnCollisions.length > 0) {
|
||||
return gridColumnCollisions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果只有栅格列,说明确实是要拖入栅格
|
||||
return pointerCollisions;
|
||||
}
|
||||
|
||||
// 如果 pointerWithin 没有结果,使用 rectIntersection 作为后备
|
||||
const rectCollisions = rectIntersection(args);
|
||||
if (rectCollisions.length > 0) {
|
||||
return rectCollisions;
|
||||
|
||||
// 非栅格字段:优先级排序
|
||||
// 1. 优先选择非栅格字段
|
||||
const nonGridFields = pointerCollisions.filter((collision: any) => {
|
||||
const id = collision.id.toString();
|
||||
return id.startsWith('field_') && !fields.some(f => f.id === id && f.type === 'grid');
|
||||
});
|
||||
if (nonGridFields.length > 0) {
|
||||
return nonGridFields;
|
||||
}
|
||||
|
||||
// 最后使用 closestCenter 作为兜底
|
||||
return closestCenter(args);
|
||||
}, []);
|
||||
|
||||
// 2. 画布或底部区域
|
||||
const canvasCollisions = pointerCollisions.filter((collision: any) => {
|
||||
const id = collision.id.toString();
|
||||
return id === 'canvas' || id === 'canvas-bottom';
|
||||
});
|
||||
if (canvasCollisions.length > 0) {
|
||||
return canvasCollisions;
|
||||
}
|
||||
|
||||
// 3. 栅格列
|
||||
const gridColumns = pointerCollisions.filter((collision: any) => {
|
||||
const id = collision.id.toString();
|
||||
return id.startsWith('grid-') && id.includes('-col-');
|
||||
});
|
||||
if (gridColumns.length > 0) {
|
||||
return gridColumns;
|
||||
}
|
||||
|
||||
return pointerCollisions;
|
||||
}, [fields]);
|
||||
|
||||
// 生成唯一 ID
|
||||
const generateId = () => `field_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
@ -96,9 +96,12 @@ const DesignCanvas: React.FC<DesignCanvasProps> = ({
|
||||
<div className="form-designer-field-list">
|
||||
{fields.map((field) => (
|
||||
<React.Fragment key={field.id}>
|
||||
{/* 插入指示器 - 显示在拖拽悬停的字段上方 */}
|
||||
{activeId && overId === field.id && activeId !== field.id && (
|
||||
<DropIndicator text="释放以插入到此处" />
|
||||
{/* 插入指示器 - 显示在字段上方(栅格字段会根据边界自动判断) */}
|
||||
{activeId &&
|
||||
overId === field.id &&
|
||||
activeId !== field.id &&
|
||||
!overId?.toString().includes('-col-') && (
|
||||
<DropIndicator text={field.type === 'grid' ? "释放以插入到栅格前/后" : "释放以插入到此处"} />
|
||||
)}
|
||||
|
||||
<FieldItem
|
||||
|
||||
Loading…
Reference in New Issue
Block a user