- Go backend (server/)
- Frontend (web/, server/static/)
- Database and deployment files
- Scripts and docs
Co-Authored-By: 狸花猫/Claude-Qwen3.6-Plus 🐾
652 lines
19 KiB
JavaScript
652 lines
19 KiB
JavaScript
import { __assign } from "tslib";
|
||
import { clone, debounce, deepMix } from '@antv/util';
|
||
import Global from '../global';
|
||
export default {
|
||
getDefaultCfg: function getDefaultCfg() {
|
||
return {
|
||
updateEdge: true,
|
||
delegateStyle: {},
|
||
// 是否开启delegate
|
||
enableDelegate: false,
|
||
// 拖动节点过程中是否只改变 Combo 的大小,而不改变其结构
|
||
onlyChangeComboSize: false,
|
||
// 拖动过程中目标 combo 状态样式
|
||
comboActiveState: '',
|
||
selectedState: 'selected',
|
||
enableOptimize: false,
|
||
enableDebounce: false,
|
||
enableStack: true
|
||
};
|
||
},
|
||
getEvents: function getEvents() {
|
||
return {
|
||
'node:mousedown': 'onMouseDown',
|
||
drag: 'onDragMove',
|
||
dragend: 'onDragEnd',
|
||
'combo:dragenter': 'onDragEnter',
|
||
'combo:dragleave': 'onDragLeave',
|
||
'combo:drop': 'onDropCombo',
|
||
'node:drop': 'onDropNode',
|
||
'canvas:drop': 'onDropCanvas',
|
||
touchstart: 'onTouchStart',
|
||
touchmove: 'onTouchMove',
|
||
touchend: 'onDragEnd',
|
||
afterchangedata: 'onDragEnd'
|
||
};
|
||
},
|
||
validationCombo: function validationCombo(item) {
|
||
if (!this.origin || !item || item.destroyed) {
|
||
return false;
|
||
}
|
||
var type = item.getType();
|
||
if (type !== 'combo') {
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
onTouchStart: function onTouchStart(evt) {
|
||
if (!evt.item) return;
|
||
var self = this;
|
||
try {
|
||
var touches = evt.originalEvent.touches;
|
||
var event1 = touches[0];
|
||
var event2 = touches[1];
|
||
if (event1 && event2) {
|
||
return;
|
||
}
|
||
evt.preventDefault();
|
||
} catch (e) {
|
||
console.warn('Touch original event not exist!');
|
||
}
|
||
this.mousedown = {
|
||
item: evt.item,
|
||
target: evt.target,
|
||
origin: {
|
||
x: evt.x,
|
||
y: evt.y
|
||
}
|
||
};
|
||
this.dragstart = true;
|
||
self.onDragStart(evt);
|
||
},
|
||
onTouchMove: function onTouchMove(e) {
|
||
var self = this;
|
||
try {
|
||
var touches = e.originalEvent.touches;
|
||
var event1 = touches[0];
|
||
var event2 = touches[1];
|
||
if (event1 && event2) {
|
||
self.onDragEnd(e);
|
||
return;
|
||
}
|
||
e.preventDefault();
|
||
} catch (e) {
|
||
console.warn('Touch original event not exist!');
|
||
}
|
||
self.onDrag(e);
|
||
},
|
||
/**
|
||
* cache the manipulated item and target, since drag and dragend are global events but not node:*
|
||
* @param evt event param
|
||
*/
|
||
onMouseDown: function onMouseDown(evt) {
|
||
this.mousedown = {
|
||
item: evt.item,
|
||
target: evt.target,
|
||
origin: {
|
||
x: evt.x,
|
||
y: evt.y
|
||
}
|
||
};
|
||
// 绑定浏览器监听,触发拖拽结束,结束拖拽时移除
|
||
if (typeof window !== 'undefined' && !this.windowEventBinded) {
|
||
this.windowEventBinded = true;
|
||
document.body.addEventListener('contextmenu', this.onDragEnd.bind(this));
|
||
document.body.addEventListener('mouseup', this.onDragEnd.bind(this));
|
||
}
|
||
},
|
||
/**
|
||
* trigger dragstart/drag by mousedown and drag events
|
||
* @param evt event param
|
||
*/
|
||
onDragMove: function onDragMove(evt) {
|
||
var _a, _b;
|
||
if (((_b = (_a = evt.item) === null || _a === void 0 ? void 0 : _a.getType) === null || _b === void 0 ? void 0 : _b.call(_a)) !== 'node') {
|
||
this.onDragEnd();
|
||
return;
|
||
}
|
||
if (!this.mousedown) return;
|
||
if (!this.dragstart) {
|
||
// dragstart
|
||
this.dragstart = true;
|
||
this.onDragStart(evt);
|
||
} else {
|
||
// drag
|
||
this.onDrag(__assign(__assign({}, evt), this.mousedown));
|
||
}
|
||
},
|
||
/**
|
||
* 开始拖动节点
|
||
* @param evt
|
||
*/
|
||
onDragStart: function onDragStart(evt) {
|
||
var _this = this;
|
||
this.currentShouldEnd = true;
|
||
if (!this.shouldBegin(__assign(__assign({}, evt), this.mousedown), this)) {
|
||
return;
|
||
}
|
||
var _a = this.mousedown,
|
||
item = _a.item,
|
||
target = _a.target;
|
||
if (!item || item.destroyed || item.hasLocked()) {
|
||
return;
|
||
}
|
||
// 拖动时,设置拖动元素的 capture 为false,则不拾取拖动的元素
|
||
var group = item.getContainer();
|
||
group.set('capture', false);
|
||
if (!this.cachedCaptureItems) this.cachedCaptureItems = [];
|
||
this.cachedCaptureItems.push(item);
|
||
// 如果拖动的target 是linkPoints / anchorPoints 则不允许拖动
|
||
if (target) {
|
||
var isAnchorPoint = target.get('isAnchorPoint');
|
||
if (isAnchorPoint) {
|
||
return;
|
||
}
|
||
}
|
||
var graph = this.graph;
|
||
this.targets = [];
|
||
// 将节点拖入到指定的 Combo
|
||
this.targetCombo = null;
|
||
// 获取所有选中的元素
|
||
var nodes = graph.findAllByState('node', this.selectedState);
|
||
var currentNodeId = item.get('id');
|
||
// 当前拖动的节点是否是选中的节点
|
||
var dragNodes = nodes.filter(function (node) {
|
||
var nodeId = node.get('id');
|
||
return currentNodeId === nodeId;
|
||
});
|
||
// 只拖动当前节点
|
||
if (dragNodes.length === 0) {
|
||
this.targets.push(item);
|
||
} else if (nodes.length > 1) {
|
||
// 拖动多个节点
|
||
nodes.forEach(function (node) {
|
||
var locked = node.hasLocked();
|
||
if (!locked) {
|
||
_this.targets.push(node);
|
||
}
|
||
});
|
||
} else {
|
||
this.targets.push(item);
|
||
}
|
||
if (this.graph.get('enabledStack') && this.enableStack) {
|
||
var beforeDragNodes_1 = [];
|
||
this.targets.forEach(function (t) {
|
||
var _a = t.getModel(),
|
||
x = _a.x,
|
||
y = _a.y,
|
||
id = _a.id;
|
||
beforeDragNodes_1.push({
|
||
x: x,
|
||
y: y,
|
||
id: id
|
||
});
|
||
});
|
||
this.set('beforeDragNodes', beforeDragNodes_1);
|
||
}
|
||
this.hidenEdge = {};
|
||
if (this.get('updateEdge') && this.enableOptimize && !this.enableDelegate) {
|
||
this.targets.forEach(function (node) {
|
||
var edges = node.getEdges();
|
||
edges.forEach(function (edge) {
|
||
if (!edge.isVisible()) return;
|
||
_this.hidenEdge[edge.getID()] = true;
|
||
edge.hide();
|
||
});
|
||
});
|
||
}
|
||
this.origin = this.mousedown.origin;
|
||
this.point = {};
|
||
this.originPoint = {};
|
||
},
|
||
/**
|
||
* 持续拖动节点
|
||
* @param evt
|
||
*/
|
||
onDrag: function onDrag(evt) {
|
||
var _this = this;
|
||
if (!this.mousedown || !this.origin) return;
|
||
if (!this.shouldUpdate(evt, this)) return;
|
||
if (this.get('enableDelegate')) {
|
||
this.updateDelegate(evt);
|
||
} else {
|
||
if (this.enableDebounce) {
|
||
this.debounceUpdate({
|
||
targets: this.targets,
|
||
graph: this.graph,
|
||
point: this.point,
|
||
origin: this.origin,
|
||
evt: evt,
|
||
updateEdge: this.get('updateEdge'),
|
||
onlyChangeComboSize: this.onlyChangeComboSize,
|
||
updateParentCombos: this.updateParentCombos
|
||
});
|
||
} else {
|
||
var parentComboMap_1 = {};
|
||
this.targets.map(function (target) {
|
||
_this.update(target, evt);
|
||
var parentComboId = target.getModel().comboId;
|
||
if (parentComboId) parentComboMap_1[parentComboId] = _this.graph.findById(parentComboId);
|
||
});
|
||
if (this.onlyChangeComboSize) {
|
||
// 拖动节点过程中,动态改变 Combo 的大小
|
||
this.updateParentCombos();
|
||
}
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* 拖动结束,设置拖动元素capture为true,更新元素位置,如果是拖动涉及到 combo,则更新 combo 结构
|
||
* @param evt
|
||
*/
|
||
onDragEnd: function onDragEnd(evt) {
|
||
var _this = this;
|
||
var _a;
|
||
this.mousedown = false;
|
||
this.dragstart = false;
|
||
// 移除浏览器监听
|
||
if (typeof window !== 'undefined' && this.windowEventBinded) {
|
||
this.windowEventBinded = false;
|
||
document.body.removeEventListener('contextmenu', this.onDragEnd.bind(this));
|
||
document.body.removeEventListener('mouseup', this.onDragEnd.bind(this));
|
||
}
|
||
if (!this.origin) {
|
||
return;
|
||
}
|
||
// 拖动结束后,设置拖动元素 group 的 capture 为 true,允许拾取拖动元素
|
||
(_a = this.cachedCaptureItems) === null || _a === void 0 ? void 0 : _a.forEach(function (item) {
|
||
var group = item.getContainer();
|
||
group.set('capture', true);
|
||
});
|
||
this.cachedCaptureItems = [];
|
||
if (this.delegateRect) {
|
||
this.delegateRect.remove();
|
||
this.delegateRect = null;
|
||
}
|
||
if (this.get('updateEdge') && this.enableOptimize && !this.enableDelegate) {
|
||
this.targets.forEach(function (node) {
|
||
var edges = node.getEdges();
|
||
edges.forEach(function (edge) {
|
||
if (_this.hidenEdge[edge.getID()]) edge.show();
|
||
edge.refresh();
|
||
});
|
||
});
|
||
}
|
||
this.hidenEdge = {};
|
||
var graph = this.graph;
|
||
// 拖动结束后,入栈
|
||
if (graph.get('enabledStack') && this.enableStack) {
|
||
var stackData_1 = {
|
||
before: {
|
||
nodes: [],
|
||
edges: [],
|
||
combos: []
|
||
},
|
||
after: {
|
||
nodes: [],
|
||
edges: [],
|
||
combos: []
|
||
}
|
||
};
|
||
this.get('beforeDragNodes').forEach(function (model) {
|
||
stackData_1.before.nodes.push(model);
|
||
});
|
||
this.targets.forEach(function (target) {
|
||
var _a = target.getModel(),
|
||
x = _a.x,
|
||
y = _a.y,
|
||
id = _a.id;
|
||
stackData_1.after.nodes.push({
|
||
x: x,
|
||
y: y,
|
||
id: id
|
||
});
|
||
});
|
||
graph.pushStack('update', clone(stackData_1));
|
||
}
|
||
// 拖动结束后emit事件,将当前操作的节点抛出去,目标节点为null
|
||
graph.emit('dragnodeend', {
|
||
items: this.targets,
|
||
targetItem: null
|
||
});
|
||
this.point = {};
|
||
this.origin = null;
|
||
this.originPoint = {};
|
||
this.targets.length = 0;
|
||
this.targetCombo = null;
|
||
},
|
||
/**
|
||
* 拖动过程中将节点放置到 combo 上
|
||
* @param evt
|
||
*/
|
||
onDropCombo: function onDropCombo(evt) {
|
||
var item = evt.item;
|
||
this.currentShouldEnd = this.shouldEnd(evt, item, this);
|
||
// 若不允许结束,则将节点位置设置回初识位置。后面的逻辑仍需要执行
|
||
this.updatePositions(evt, !this.currentShouldEnd);
|
||
if (!this.currentShouldEnd || !this.validationCombo(item)) return;
|
||
var graph = this.graph;
|
||
if (this.comboActiveState) {
|
||
graph.setItemState(item, this.comboActiveState, false);
|
||
}
|
||
this.targetCombo = item;
|
||
// 拖动结束后是动态改变 Combo 大小还是将节点从 Combo 中删除
|
||
if (this.onlyChangeComboSize) {
|
||
// 拖动节点结束后,动态改变 Combo 的大小
|
||
graph.updateCombos();
|
||
} else {
|
||
var targetComboModel_1 = item.getModel();
|
||
this.targets.map(function (node) {
|
||
var nodeModel = node.getModel();
|
||
if (nodeModel.comboId !== targetComboModel_1.id) {
|
||
graph.updateComboTree(node, targetComboModel_1.id);
|
||
}
|
||
});
|
||
graph.updateCombo(item);
|
||
}
|
||
// 将节点拖动到 combo 上面,emit事件抛出当前操作的节点及目标 combo
|
||
graph.emit('dragnodeend', {
|
||
items: this.targets,
|
||
targetItem: this.targetCombo
|
||
});
|
||
},
|
||
onDropCanvas: function onDropCanvas(evt) {
|
||
var graph = this.graph;
|
||
this.currentShouldEnd = this.shouldEnd(evt, undefined, this);
|
||
// 若不允许结束,则将节点位置设置回初识位置。后面的逻辑仍需要执行
|
||
this.updatePositions(evt, !this.currentShouldEnd);
|
||
if (!this.targets || this.targets.length === 0 || !this.currentShouldEnd) return;
|
||
if (this.onlyChangeComboSize) {
|
||
this.updateParentCombos();
|
||
} else {
|
||
this.targets.map(function (node) {
|
||
// 拖动的节点有 comboId,即是从其他 combo 中拖出时才处理
|
||
var model = node.getModel();
|
||
if (model.comboId) {
|
||
graph.updateComboTree(node);
|
||
}
|
||
});
|
||
}
|
||
},
|
||
/**
|
||
* 拖动放置到某个 combo 中的子 node 上
|
||
* @param evt
|
||
*/
|
||
onDropNode: function onDropNode(evt) {
|
||
if (!this.targets || this.targets.length === 0) return;
|
||
var self = this;
|
||
var item = evt.item;
|
||
var graph = self.graph;
|
||
var comboId = item.getModel().comboId;
|
||
var newParentCombo = comboId ? graph.findById(comboId) : undefined;
|
||
this.currentShouldEnd = this.shouldEnd(evt, newParentCombo, this);
|
||
// 若不允许结束,则将节点位置设置回初识位置。后面的逻辑仍需要执行
|
||
this.updatePositions(evt, !this.currentShouldEnd);
|
||
if (!this.currentShouldEnd) return;
|
||
if (this.onlyChangeComboSize) {
|
||
this.updateParentCombos();
|
||
} else if (comboId) {
|
||
var combo = graph.findById(comboId);
|
||
if (self.comboActiveState) {
|
||
graph.setItemState(combo, self.comboActiveState, false);
|
||
}
|
||
this.targets.map(function (node) {
|
||
var nodeModel = node.getModel();
|
||
if (comboId !== nodeModel.comboId) {
|
||
graph.updateComboTree(node, comboId);
|
||
}
|
||
});
|
||
graph.updateCombo(combo);
|
||
} else {
|
||
this.targets.map(function (node) {
|
||
var model = node.getModel();
|
||
if (model.comboId) {
|
||
graph.updateComboTree(node);
|
||
}
|
||
});
|
||
}
|
||
// 将节点拖动到另外个节点上面,emit 事件抛出当前操作的节点及目标节点
|
||
graph.emit('dragnodeend', {
|
||
items: this.targets,
|
||
targetItem: item
|
||
});
|
||
},
|
||
/**
|
||
* 将节点拖入到 Combo 中
|
||
* @param evt
|
||
*/
|
||
onDragEnter: function onDragEnter(evt) {
|
||
var item = evt.item;
|
||
if (!this.validationCombo(item)) return;
|
||
var graph = this.graph;
|
||
if (this.comboActiveState) {
|
||
graph.setItemState(item, this.comboActiveState, true);
|
||
}
|
||
},
|
||
/**
|
||
* 将节点从 Combo 中拖出
|
||
* @param evt
|
||
*/
|
||
onDragLeave: function onDragLeave(evt) {
|
||
var item = evt.item;
|
||
if (!this.validationCombo(item)) return;
|
||
var graph = this.graph;
|
||
if (this.comboActiveState) {
|
||
graph.setItemState(item, this.comboActiveState, false);
|
||
}
|
||
},
|
||
updatePositions: function updatePositions(evt, restore) {
|
||
var _this = this;
|
||
if (!this.targets || this.targets.length === 0) return;
|
||
// 当开启 delegate 时,拖动结束后需要更新所有已选中节点的位置
|
||
if (this.get('enableDelegate')) {
|
||
if (this.enableDebounce) this.debounceUpdate({
|
||
targets: this.targets,
|
||
graph: this.graph,
|
||
point: this.point,
|
||
origin: this.origin,
|
||
evt: evt,
|
||
updateEdge: this.get('updateEdge'),
|
||
onlyChangeComboSize: this.onlyChangeComboSize,
|
||
updateParentCombos: this.updateParentCombos
|
||
});else if (!restore) this.targets.map(function (node) {
|
||
return _this.update(node, evt);
|
||
});
|
||
} else this.targets.map(function (node) {
|
||
return _this.update(node, evt, restore);
|
||
});
|
||
},
|
||
/**
|
||
* 更新节点
|
||
* @param item 拖动的节点实例
|
||
* @param evt
|
||
*/
|
||
update: function update(item, evt, restore) {
|
||
var origin = this.origin;
|
||
var model = item.get('model');
|
||
var nodeId = item.get('id');
|
||
if (!this.point[nodeId]) {
|
||
this.point[nodeId] = {
|
||
x: model.x || 0,
|
||
y: model.y || 0
|
||
};
|
||
}
|
||
var x = evt.x - origin.x + this.point[nodeId].x;
|
||
var y = evt.y - origin.y + this.point[nodeId].y;
|
||
if (restore) {
|
||
x += origin.x - evt.x;
|
||
y += origin.y - evt.y;
|
||
}
|
||
var pos = {
|
||
x: x,
|
||
y: y
|
||
};
|
||
if (this.get('updateEdge')) {
|
||
this.graph.updateItem(item, pos, false);
|
||
} else {
|
||
item.updatePosition(pos);
|
||
}
|
||
},
|
||
/**
|
||
* 限流更新节点
|
||
* @param item 拖动的节点实例
|
||
* @param evt
|
||
*/
|
||
debounceUpdate: debounce(function (event) {
|
||
var targets = event.targets,
|
||
graph = event.graph,
|
||
point = event.point,
|
||
origin = event.origin,
|
||
evt = event.evt,
|
||
updateEdge = event.updateEdge,
|
||
onlyChangeComboSize = event.onlyChangeComboSize,
|
||
updateParentCombos = event.updateParentCombos;
|
||
targets.map(function (item) {
|
||
var model = item.get('model');
|
||
var nodeId = item.get('id');
|
||
if (!point[nodeId]) {
|
||
point[nodeId] = {
|
||
x: model.x || 0,
|
||
y: model.y || 0
|
||
};
|
||
}
|
||
var x = evt.x - origin.x + point[nodeId].x;
|
||
var y = evt.y - origin.y + point[nodeId].y;
|
||
var pos = {
|
||
x: x,
|
||
y: y
|
||
};
|
||
if (updateEdge) {
|
||
graph.updateItem(item, pos, false);
|
||
} else {
|
||
item.updatePosition(pos);
|
||
}
|
||
});
|
||
if (onlyChangeComboSize) {
|
||
updateParentCombos(graph, targets);
|
||
}
|
||
}, 50, true),
|
||
/**
|
||
* 更新拖动元素时的delegate
|
||
* @param {Event} evt 事件句柄
|
||
* @param {number} x 拖动单个元素时候的x坐标
|
||
* @param {number} y 拖动单个元素时候的y坐标
|
||
*/
|
||
updateDelegate: function updateDelegate(evt) {
|
||
var graph = this.graph;
|
||
if (!this.delegateRect) {
|
||
// 拖动多个
|
||
var parent_1 = graph.get('group');
|
||
var attrs = deepMix({}, Global.delegateStyle, this.delegateStyle);
|
||
var _a = this.calculationGroupPosition(evt),
|
||
cx = _a.x,
|
||
cy = _a.y,
|
||
width = _a.width,
|
||
height = _a.height,
|
||
minX = _a.minX,
|
||
minY = _a.minY;
|
||
this.originPoint = {
|
||
x: cx,
|
||
y: cy,
|
||
width: width,
|
||
height: height,
|
||
minX: minX,
|
||
minY: minY
|
||
};
|
||
// model上的x, y是相对于图形中心的,delegateShape是g实例,x,y是绝对坐标
|
||
this.delegateRect = parent_1.addShape('rect', {
|
||
attrs: __assign({
|
||
width: width,
|
||
height: height,
|
||
x: cx,
|
||
y: cy
|
||
}, attrs),
|
||
name: 'rect-delegate-shape'
|
||
});
|
||
this.delegate = this.delegateRect;
|
||
this.delegateRect.set('capture', false);
|
||
} else {
|
||
var clientX = evt.x - this.origin.x + this.originPoint.minX;
|
||
var clientY = evt.y - this.origin.y + this.originPoint.minY;
|
||
this.delegateRect.attr({
|
||
x: clientX,
|
||
y: clientY
|
||
});
|
||
}
|
||
},
|
||
/**
|
||
* 计算delegate位置,包括左上角左边及宽度和高度
|
||
* @memberof ItemGroup
|
||
* @return {object} 计算出来的delegate坐标信息及宽高
|
||
*/
|
||
calculationGroupPosition: function calculationGroupPosition(evt) {
|
||
var nodes = this.targets;
|
||
if (nodes.length === 0) {
|
||
nodes.push(evt.item);
|
||
}
|
||
var minx = Infinity;
|
||
var maxx = -Infinity;
|
||
var miny = Infinity;
|
||
var maxy = -Infinity;
|
||
// 获取已节点的所有最大最小x y值
|
||
for (var i = 0; i < nodes.length; i++) {
|
||
var element = nodes[i];
|
||
var bbox = element.getBBox();
|
||
var minX = bbox.minX,
|
||
minY = bbox.minY,
|
||
maxX = bbox.maxX,
|
||
maxY = bbox.maxY;
|
||
if (minX < minx) {
|
||
minx = minX;
|
||
}
|
||
if (minY < miny) {
|
||
miny = minY;
|
||
}
|
||
if (maxX > maxx) {
|
||
maxx = maxX;
|
||
}
|
||
if (maxY > maxy) {
|
||
maxy = maxY;
|
||
}
|
||
}
|
||
var x = Math.floor(minx);
|
||
var y = Math.floor(miny);
|
||
var width = Math.ceil(maxx) - Math.floor(minx);
|
||
var height = Math.ceil(maxy) - Math.floor(miny);
|
||
return {
|
||
x: x,
|
||
y: y,
|
||
width: width,
|
||
height: height,
|
||
minX: minx,
|
||
minY: miny
|
||
};
|
||
},
|
||
/**
|
||
* updates the parent combos' size and position
|
||
* @param paramGraph param for debounce function, where 'this' is not available
|
||
* @param paramTargets param for debounce function, where 'this' is not available
|
||
*/
|
||
updateParentCombos: function updateParentCombos(paramGraph, paramTargets) {
|
||
var graph = paramGraph || this.graph;
|
||
var targets = paramTargets || this.targets;
|
||
var comboParentMap = {};
|
||
targets === null || targets === void 0 ? void 0 : targets.forEach(function (target) {
|
||
var comboId = target.getModel().comboId;
|
||
if (comboId) comboParentMap[comboId] = graph.findById(comboId);
|
||
});
|
||
Object.values(comboParentMap).forEach(function (combo) {
|
||
if (combo) graph.updateCombo(combo);
|
||
});
|
||
}
|
||
}; |