diff --git a/doc/docs/guide/community/JiangHH/AJ_Report大屏设计时多组件对齐和拖拽移动实现.md b/doc/docs/guide/community/JiangHH/AJ_Report大屏设计时多组件对齐和拖拽移动实现.md new file mode 100644 index 00000000..b9cba901 --- /dev/null +++ b/doc/docs/guide/community/JiangHH/AJ_Report大屏设计时多组件对齐和拖拽移动实现.md @@ -0,0 +1,81 @@ +## 多组件对齐和拖拽移动功能 + +注意前端版本 vue版本 + +1.多组件选中实现 +2.对齐实现 +3.拖拽实现 + +### 1、多组件选中 +1. Ctrl键 +2. 鼠标框选 +3. 组合 + +#### 方式1 Ctrl键实现多选 +说明: + +1. 第一次单击组件,会默认把选中的组件加入到已选中的组件集合中. +2. 按住Ctrl键选中的组件,会加入到已选中的组件集合中. +3. 按住Ctrl键选中的组件,如果已选中的组件中包含该组件,则该组件取消选中. +4. 点击大屏其他位置(非组件),会把选中的组件清空. + +测试截图: + +![](.\picture\img_01.png) + +#### 方式2 鼠标框选实现多选 +说明: + +1. 鼠标 (按下,移动,释放)生成矩形框,跟矩形框相交的组件会被选中. +2. 组合方式多选的情况下,框选之前已选中的组件不会加入(除非框选也有). +3. 框选的组件,也支持按住Ctrl键取消选中. +4. 点击大屏其他位置(非组件),会把选中的组件清空. + +1. ​ 鼠标按下,移动,释放,会生成一个矩形框,跟矩形框相交的组件爱你,会被选中. +2. ​ 2.组合方式多选的情况下,框选之前已选中的组件不会加入(除非框选也有). +3. ​ 3.框选的组件,也支持按住Ctrl键取消选中. +4. ​ 4.点击大屏其他位置(非组件),会把选中的组件清空. + +- ​ 1.鼠标按下,移动,释放,会生成一个矩形框,跟矩形框相交的组件爱你,会被选中. + +测试截图: + +![](.\picture\img_02.png) + +![](.\picture\img_03.png) + +![](.\picture\img_04.png) + +### 2、多组件对齐 + +单选右键菜单 + +![](.\picture\img_05.png) + +多选右键菜单 + +![](.\picture\img_06.png) + +#### 左对齐/右对齐/居中对齐 + +选择左对齐 (以最上边的组件为标准对齐)---不合适自己可以修改代码 + +![](.\picture\img_07.png) + +#### 上对齐/下对齐/居中对齐 + +选择上对齐(以最左边的组件为标准对齐)----不合适自己可以修改代码 + +![](.\picture\img_08.png) + +![](.\picture\img_09.png) + +### 3、多组件移动拖拽 + +#### 多选状态 + +![](.\picture\img_10.png) + +#### 拖拽后 + +![](.\picture\img_11.png) diff --git a/doc/docs/guide/community/JiangHH/picture/img_01.png b/doc/docs/guide/community/JiangHH/picture/img_01.png new file mode 100644 index 00000000..1feac87e Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_01.png differ diff --git a/doc/docs/guide/community/JiangHH/picture/img_02.png b/doc/docs/guide/community/JiangHH/picture/img_02.png new file mode 100644 index 00000000..c033968c Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_02.png differ diff --git a/doc/docs/guide/community/JiangHH/picture/img_03.png b/doc/docs/guide/community/JiangHH/picture/img_03.png new file mode 100644 index 00000000..033f3c18 Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_03.png differ diff --git a/doc/docs/guide/community/JiangHH/picture/img_04.png b/doc/docs/guide/community/JiangHH/picture/img_04.png new file mode 100644 index 00000000..2759e8ea Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_04.png differ diff --git a/doc/docs/guide/community/JiangHH/picture/img_05.png b/doc/docs/guide/community/JiangHH/picture/img_05.png new file mode 100644 index 00000000..b6ec0033 Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_05.png differ diff --git a/doc/docs/guide/community/JiangHH/picture/img_06.png b/doc/docs/guide/community/JiangHH/picture/img_06.png new file mode 100644 index 00000000..abaf99a9 Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_06.png differ diff --git a/doc/docs/guide/community/JiangHH/picture/img_07.png b/doc/docs/guide/community/JiangHH/picture/img_07.png new file mode 100644 index 00000000..cb12cef5 Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_07.png differ diff --git a/doc/docs/guide/community/JiangHH/picture/img_08.png b/doc/docs/guide/community/JiangHH/picture/img_08.png new file mode 100644 index 00000000..ee1981da Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_08.png differ diff --git a/doc/docs/guide/community/JiangHH/picture/img_09.png b/doc/docs/guide/community/JiangHH/picture/img_09.png new file mode 100644 index 00000000..e8e568f6 Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_09.png differ diff --git a/doc/docs/guide/community/JiangHH/picture/img_10.png b/doc/docs/guide/community/JiangHH/picture/img_10.png new file mode 100644 index 00000000..a4aed1fd Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_10.png differ diff --git a/doc/docs/guide/community/JiangHH/picture/img_11.png b/doc/docs/guide/community/JiangHH/picture/img_11.png new file mode 100644 index 00000000..bbcfa8a9 Binary files /dev/null and b/doc/docs/guide/community/JiangHH/picture/img_11.png differ diff --git a/report-ui/src/utils/screenMixins.js b/report-ui/src/utils/screenMixins.js index a1df6f8c..fd709e79 100644 --- a/report-ui/src/utils/screenMixins.js +++ b/report-ui/src/utils/screenMixins.js @@ -258,6 +258,13 @@ const mixin = { display: "block", }; } + //设置多选和单选的菜单项展示 + document.getElementsByName("singleSelect").forEach(e=>{ + e.style.display= this.selectedWidgets.length >= 2 ?"none":"block"; + }); + document.getElementsByName("mulSelect").forEach(e=>{ + e.style.display= this.selectedWidgets.length >= 2 ?"block":"none"; + }) this.visibleContentMenu = true; return false; }, @@ -268,7 +275,11 @@ const mixin = { }, // 删除 deletelayer() { + let _this = this; this.widgets.splice(this.rightClickIndex, 1); + this.selectedWidgets.forEach(sw=>{ + _this.widgets = _this.widgets.filter(w=>w.value.widgetId !== sw.value.widgetId); + }) }, // 锁定 lockLayer() { @@ -329,6 +340,69 @@ const mixin = { } else { this.widgets.unshift(this.widgets.splice(this.rightClickIndex, 1)[0]); } + }, + //对齐 + alignment(align) { + if(this.selectedWidgets.length <= 1) { + this.$message.error("请至少选择两个组件对齐"); + return; + } + console.log("对齐方式:" + align); + let topWidget = this.selectedWidgets[0]; //最上组件(左右对齐使用) + let leftWidget = this.selectedWidgets[0]; //最左组件(上下对齐使用) + let minTop = this.selectedWidgets[0].value.position.top; + let minLeft = this.selectedWidgets[0].value.position.left; + //如果是框选的话,以【最上组件】【最左组件】为标准对齐;如果是Ctrl多选方式,则以第一个选中的组件为标准对齐,组合多选以框选逻辑为准 + if(this.kuangSelectFlag){ + for(let i = 0; i< this.selectedWidgets.length; i++){ + let widget = this.selectedWidgets[i]; + if( minTop > widget.value.position.top){ + minTop = widget.value.position.top; + topWidget = widget; + } + if( minLeft > widget.value.position.left){ + minLeft = widget.value.position.left; + leftWidget = widget; + } + } + } + for(let i = 0; i< this.selectedWidgets.length; i++){ + let widget = this.selectedWidgets[i]; + this.$refs.widgets.forEach(w=>{ + if(w.value.widgetId === widget.value.widgetId){ + w.$refs.draggable.setActive(false); + } + }); + this.widgets.forEach(w=>{ + if(w.value.widgetId === widget.value.widgetId){ + switch (align){ + case "left": //左对齐 + w.value.position.left = topWidget.value.position.left; + break; + case "right": //右对齐 + w.value.position.left = topWidget.value.position.left + topWidget.value.position.width - w.value.position.width; + break; + case "horizontal_center": //左右居中对齐 + w.value.position.left = topWidget.value.position.left + topWidget.value.position.width/2 - w.value.position.width /2; + break; + case "top": //上对齐 + w.value.position.top = leftWidget.value.position.top; + break; + case "bottom": //下对齐 + w.value.position.top = leftWidget.value.position.top + leftWidget.value.position.height - w.value.position.height; + break; + case "vertical_center": //上下居中对齐 + w.value.position.top = leftWidget.value.position.top + leftWidget.value.position.height/2 - w.value.position.height /2; + break; + } + } + }); + } + this.selectedWidgets = []; + if(this.rect){ + document.getElementById("workbench").removeChild(this.rect); + } + this.kuangSelectFlag = false; } } } diff --git a/report-ui/src/views/bigscreenDesigner/designer/components/contentMenu.vue b/report-ui/src/views/bigscreenDesigner/designer/components/contentMenu.vue index ecfbc0f5..84fd7759 100644 --- a/report-ui/src/views/bigscreenDesigner/designer/components/contentMenu.vue +++ b/report-ui/src/views/bigscreenDesigner/designer/components/contentMenu.vue @@ -10,27 +10,45 @@
删除图层
-
+
锁定图层
-
+
解除锁定
-
+
复制图层
-
+
置顶图层
-
+
置底图层
-
+
上移一层
-
+
下移一层
+
+ 左对齐 +
+
+ 右对齐 +
+
+ 左右居中对齐 +
+
+ 上对齐 +
+
+ 下对齐 +
+
+ 上下居中对齐 +
diff --git a/report-ui/src/views/bigscreenDesigner/designer/index.vue b/report-ui/src/views/bigscreenDesigner/designer/index.vue index 5300c3b1..e9da8b90 100644 --- a/report-ui/src/views/bigscreenDesigner/designer/index.vue +++ b/report-ui/src/views/bigscreenDesigner/designer/index.vue @@ -54,7 +54,7 @@ :key="'item' + index" class="tools-item" :class="widgetIndex == index ? 'is-active' : ''" - @click="layerClick(index)" + @click="layerClick($event,index)" > @@ -275,6 +275,9 @@ @click.self="setOptionsOnClickScreen" @drop="widgetOnDragged($event)" @dragover="dragOver($event)" + @mousedown.self="downEvent($event)" + @mouseup="upEvent($event)" + @mousemove="moveEvent($event)" >
@@ -351,6 +354,7 @@ @setlowLayer="setlowLayer" @moveupLayer="moveupLayer" @movedownLayer="movedownLayer" + @alignment="alignment($event)" />
@@ -416,6 +420,18 @@ export default { currentSizeRangeIndex: -1, // 当前是哪个缩放比分比, currentWidgetTotal: 0, widgetParamsConfig: [], // 各组件动态数据集的参数配置情况 + + selectedWidgets: [], //多选组件集合 + moveTimes: 0, //鼠标移动次数 + selectFlag: false, //选择标识 + kuangSelectFlag: false, //框选标识 + downX: 0, //移动开始X坐标 + downY: 0, //移动开始Y坐标 + downX2: 0, //移动结束X坐标 + downY2: 0, //移动结束Y坐标 + rect : null, //框选矩形对象 + openMulDrag: false, //批量拖拽开关 + moveWidgets:{}, //记录移动的组件的起始left和top属性 }; }, computed: { @@ -621,12 +637,20 @@ export default { }); } }, - layerClick(index) { + layerClick(event,index) { this.widgetIndex = index; - this.widgetsClick(index); + this.widgetsClick(event,index); }, // 如果是点击大屏设计器中的底层,加载大屏底层属性 setOptionsOnClickScreen() { + console.log("setOptionsOnClickScreen"); + if(this.selectedWidgets.length > 0 && this.kuangSelectFlag){ + //如果Ctrl多选过程中,点击了大屏底层,就清空 this.selectedWidgets + return; + } + this.selectFlag = false; + this.kuangSelectFlag = false; + this.selectedWidgets = []; this.screenCode = "screen"; // 选中不同的组件 右侧都显示第一栏 this.activeName = "first"; @@ -653,7 +677,42 @@ export default { }); this.widgetOptions = this.deepClone(this.widgets[obj.index]["options"]); }, - widgetsClick(index) { + widgetsClick(event,index) { + console.log("widgetsClick"); + //判断是否按住了Ctrl按钮,表示Ctrl多选 + let _this = this; + let eventWidget = event.currentTarget.__vue__.$parent;//vue3已经弃用__vue__ + if(event.ctrlKey){ //Ctrl左键选中或者取消选中 + if(this.selectedWidgets.includes(eventWidget)){ + this.selectedWidgets = this.selectedWidgets.filter(w=> w!== eventWidget); + this.$refs.widgets.forEach(w=>{ + if(eventWidget.value.widgetId === w.value.widgetId){ + setTimeout(function (){ + _this.$refs.widgets[index].$refs.draggable.setActive(false); + console.log("触发取消选中, eventWidget.value.widgetId = " + eventWidget.value.widgetId +", w.value.widgetId= "+ w.value.widgetId); + },200); //设置超时,防止效果被覆盖 + } + }) + return; + } + this.widgetsClickAndCtrl(event, index); + return; + } + if(this.selectedWidgets.includes(eventWidget)){ //右键点击菜单的时候 , 批量拖拽的时候 + this.openMulDrag = true; + this.moveWidgets = {}; + for (let i = 0; i < this.$refs.widgets.length; i++) { + let widget = { + left: this.$refs.widgets[i].value.position.left, + top: this.$refs.widgets[i].value.position.top + }; + this.moveWidgets[this.$refs.widgets[i].value.widgetId] = widget; + } + this.calculateMousePosition(event, true); + return; + } + this.selectedWidgets = []; //单选的时候需要清空 + this.selectedWidgets.push(this.$refs.widgets[index]); //确保第一个选中的组件添加到集合,不需要按住Ctrl键 const draggableArr = this.$refs.widgets; for (let i = 0; i < draggableArr.length; i++) { if (i == index) { @@ -665,6 +724,15 @@ export default { this.setOptionsOnClickWidget(index); this.grade = true; }, + //Ctrl鼠标点击事件 + widgetsClickAndCtrl(event, index) { + const draggableArr = this.$refs.widgets; + for (let i = 0; i < draggableArr.length; i++) { + if (i === index && ! this.selectedWidgets.includes(this.$refs.widgets[i])) { + this.selectedWidgets.push(this.$refs.widgets[i]); //选中的添加到集合 + } + } + }, handleMouseDown() { const draggableArr = this.$refs.widgets; for (let i = 0; i < draggableArr.length; i++) { @@ -734,6 +802,144 @@ export default { evt.preventDefault(); this.widgets = this.swapArr(this.widgets, evt.oldIndex, evt.newIndex); }, + //计算鼠标坐标 + calculateMousePosition(event, isStart){ + let workbenchPosition = this.getDomTopLeftById("workbench"); + let widgetTopInWorkbench = event.clientY - workbenchPosition.top; + let widgetLeftInWorkbench = event.clientX - workbenchPosition.left; + const targetScale = + this.currentSizeRangeIndex === this.defaultSize.index + ? this.bigscreenScaleInWorkbench + : this.sizeRange[this.currentSizeRangeIndex] / 100; + const x = widgetLeftInWorkbench / targetScale; + const y = widgetTopInWorkbench / targetScale; + if(isStart){ + this.downX = x; + this.downY = y; + }else{ + this.downX2 = x; + this.downY2 = y; + } + }, + //鼠标按下事件 + downEvent(event){ + console.log("downEvent") + this.moveTimes = 0; + this.selectedWidgets = []; + this.openMulDrag = false; + this.selectFlag = true; + this.kuangSelectFlag = false; //框选标志 + //鼠标位置 + this.calculateMousePosition(event, true) + + if(this.rect != null){ + document.getElementById("workbench").removeChild(this.rect); + this.rect = null; + } + }, + //鼠标移动事件 + moveEvent(event){ + console.log("moveEvent"); + //测试的时候发现,每次点击组件,再次点击大屏的时候,偶尔会触发一次moveEvent,导致会生成rect,所以加了移动次数moveTimes 变量控制一下,只有移动多次的情况下,才能说明是框选多选 + if(this.selectFlag && this.selectedWidgets.length <= 1 && this.moveTimes >= 1){ + if(this.rect === null){ + //这里说明一下,为啥不在downEvent方法中创建,是因为 + this.rect = document.createElement("div"); + this.rect.style.cssText = "position:absolute; width:0px; height:0px; font-size:0px; margin:0px; border: 1px dashed #0099FF; background-color: #C3D5ED"; + this.rect.id = "selectedDiv"; + this.rect.style.left = this.downX +"px"; + this.rect.style.top = this.downY+"px"; + this.rect.style.left = this.downX; + this.rect.style.top = this.downY; + } + document.getElementById("workbench").appendChild(this.rect); + this.calculateMousePosition(event, false); + + if(this.rect.style.display === "none"){ + this.rect.style.display = ""; + } + this.rect.style.left = Math.min(this.downX, this.downX2) + "px"; + this.rect.style.top = Math.min(this.downY, this.downY2) + "px"; + this.rect.style.width = Math.abs(this.downX - this.downX2) + "px"; + this.rect.style.height = Math.abs(this.downY - this.downY2) + "px"; + if(this.downX2 < this.downX && this.downY2 < this.downY){ + this.rect.style.left = this.downX2; + this.rect.style.top = this.downY2; + } + if(this.downX2 > this.downX2 && this.downY2 < this.downY){ + this.rect.style.left = this.downX; + this.rect.style.top = this.downY2; + } + if(this.downX2 < this.downX && this.downY2 > this.downY){ + this.rect.style.left = this.downX2; + this.rect.style.top = this.downY; + } + if(this.downX2 > this.downX2 && this.downY2 > this.downY){ + this.rect.style.left = this.downX; + this.rect.style.top = this.downY; + } + } + if (this.openMulDrag) { + this.mulWidgetMove(event); + } + this.moveTimes++; + }, + //批量拖拽移动 + mulWidgetMove(event){ + let _this = this; + if(this.openMulDrag && this.selectedWidgets.length >= 2){ + this.calculateMousePosition(event, false); + setTimeout(function (){ + _this.selectedWidgets.forEach(sw=>{ + for (let i = 0; i < _this.$refs.widgets.length; i++) { + if(sw.value.widgetId === _this.$refs.widgets[i].value.widgetId){ + _this.$refs.widgets[i].value.position.left = _this.moveWidgets[_this.$refs.widgets[i].value.widgetId].left + (_this.downX2 - _this.downX); + _this.$refs.widgets[i].value.position.top = _this.moveWidgets[_this.$refs.widgets[i].value.widgetId].top + (_this.downY2 - _this.downY); + } + } + }); + },50); + } + }, + upEvent(event){ + console.log("upEvent") + if(this.selectFlag && this.selectedWidgets.length === 0 && this.rect !== null){ + this.calculateMousePosition(event, false); + + //计算选择框内的组件 + const draggableArr = this.$refs.widgets; + for (let i = 0; i < draggableArr.length; i++) { + //判断组件是否在选择框内 + let widget = this.$refs.widgets[i]; + if(this.intersection(widget)){ + this.selectedWidgets.push(widget); + widget.$refs.draggable.setActive(true); + } + } + this.selectFlag = false; + this.kuangSelectFlag = true; //框选结束的时候 + } + if(this.rect){ + document.getElementById("workbench").removeChild(this.rect); + this.rect = null; + } + if(this.openMulDrag){ + this.mulWidgetMove(event); + this.openMulDrag = false; + } + }, + //判断矩形框与组件是否相交 + intersection(widget){ + return ( + (widget.value.position.left - this.downX) * (widget.value.position.left - this.downX2) < 0 || + (widget.value.position.left + widget.value.position.width - this.downX) * (widget.value.position.left + widget.value.position.width- this.downX2) < 0 + ) + && + ( + (widget.value.position.top - this.downY) * (widget.value.position.top - this.downY2) < 0 || + (widget.value.position.top + widget.value.position.height - this.downY) * (widget.value.position.top + widget.value.position.height- this.downY2) < 0 + ); + } }, };