tabs组件优化

This commit is contained in:
Erin66
2026-02-10 07:32:43 +08:00
parent 1ed8a1e76d
commit 996d0a5ba5
3 changed files with 415 additions and 25 deletions

View File

@@ -290,6 +290,8 @@
:type="widget.type"
:bigscreen="{ bigscreenWidth, bigscreenHeight }"
@onActivated="setOptionsOnClickWidget"
@onChildActivated="setOptionsOnClickInnerWidget"
@onTabsHeaderMouseDown="onTabsHeaderMouseDown($event)"
@contextmenu.prevent.native="rightClick($event, index)"
@mousedown.prevent.native="widgetsClick($event, index)"
@mouseup.prevent.native="grade = false"
@@ -432,6 +434,8 @@ export default {
rect : null, //框选矩形对象
openMulDrag: false, //批量拖拽开关
moveWidgets:{}, //记录移动的组件的起始left和top属性
// 记录当前是否选中了 Tabs 内部子组件,如果为 null 则表示选中的是顶层组件或大屏
innerWidgetSelected: null,
};
},
computed: {
@@ -683,6 +687,50 @@ export default {
this.widgetIndex = index;
this.widgetsClick(event,index);
},
/**
* Tabs 标题栏按下:启动整块 Tabs 的拖动(因 Tabs 外层 avue-draggable 已禁用)
*/
onTabsHeaderMouseDown(payload) {
if (!payload || !payload.event || typeof payload.rootWidgetIndex !== 'number') return;
const rootIndex = payload.rootWidgetIndex;
const widget = this.widgets[rootIndex];
if (!widget || !widget.value || !widget.value.position) return;
this.innerWidgetSelected = null;
this.widgetIndex = rootIndex;
this.setOptionsOnClickWidget(rootIndex);
const evt = payload.event;
const workbenchRect = document.getElementById('workbench') && document.getElementById('workbench').getBoundingClientRect();
if (!workbenchRect) return;
const scale = this.currentSizeRangeIndex === this.defaultSize.index
? this.bigscreenScaleInWorkbench
: this.sizeRange[this.currentSizeRangeIndex] / 100;
const startX = evt.clientX;
const startY = evt.clientY;
const startLeft = widget.value.position.left;
const startTop = widget.value.position.top;
const onMove = (e) => {
const dx = (e.clientX - startX) / scale;
const dy = (e.clientY - startY) / scale;
let newLeft = startLeft + dx;
let newTop = startTop + dy;
if (newLeft < 0) newLeft = 0;
if (newTop < 0) newTop = 0;
if (newLeft + widget.value.position.width > this.bigscreenWidth) {
newLeft = this.bigscreenWidth - widget.value.position.width;
}
if (newTop + widget.value.position.height > this.bigscreenHeight) {
newTop = this.bigscreenHeight - widget.value.position.height;
}
this.$set(widget.value.position, 'left', newLeft);
this.$set(widget.value.position, 'top', newTop);
};
const onUp = () => {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
};
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
},
// 如果是点击大屏设计器中的底层,加载大屏底层属性
setOptionsOnClickScreen() {
console.log("setOptionsOnClickScreen");
@@ -701,6 +749,8 @@ export default {
// 如果是点击某个组件,获取该组件的配置项
setOptionsOnClickWidget(obj) {
this.screenCode = "";
// 选中顶层组件时清空内部子组件的选中状态
this.innerWidgetSelected = null;
if (typeof obj == "number") {
this.widgetOptions = this.deepClone(this.widgets[obj]["options"]);
return;
@@ -719,8 +769,72 @@ export default {
});
this.widgetOptions = this.deepClone(this.widgets[obj.index]["options"]);
},
/**
* 选中 Tabs 组件内部的子组件,展示该子组件的右侧配置
*/
setOptionsOnClickInnerWidget(payload) {
if (!payload) return;
const { rootWidgetIndex, tabIndex, childIndex, widget } = payload;
const rootIndex = typeof rootWidgetIndex === "number" ? rootWidgetIndex : 0;
const rootWidget = this.widgets[rootIndex];
if (!rootWidget || !rootWidget.value || !rootWidget.value.setup) return;
// 记录当前选中的内部子组件路径
this.innerWidgetSelected = {
rootWidgetIndex: rootIndex,
tabIndex,
childIndex,
};
this.screenCode = "";
this.widgetIndex = rootIndex;
this.activeName = "first";
// 找到真正的子组件对象
const tabsList = (rootWidget.value.setup && rootWidget.value.setup.tabsList) || [];
const targetTab = tabsList[tabIndex];
if (!targetTab || !targetTab.children || !targetTab.children[childIndex]) {
return;
}
const childWidget = widget || targetTab.children[childIndex];
// 用实际 position 值回填到 options.position 中,保证坐标表单显示正确
if (childWidget.options && Array.isArray(childWidget.options.position)) {
const pos = childWidget.value && childWidget.value.position ? childWidget.value.position : {};
childWidget.options.position.forEach((el) => {
if (pos.hasOwnProperty(el.name)) {
el.value = pos[el.name];
}
});
}
this.widgetOptions = this.deepClone(childWidget.options);
},
widgetsClick(event,index) {
console.log("widgetsClick");
// 如果点击发生在 Tabs 组件内部的子组件上,优先选中内部组件
if (event && event.target && event.target.closest) {
const tabChildWrapper = event.target.closest(".tab-child-wrapper");
const tabsRootEl = event.target.closest(".widget-tabs");
if (tabChildWrapper && tabsRootEl) {
const tabIndexAttr = tabChildWrapper.getAttribute("data-tab-index");
const childIndexAttr = tabChildWrapper.getAttribute("data-child-index");
const tabIndex = Number(tabIndexAttr);
const childIndex = Number(childIndexAttr);
if (!isNaN(tabIndex) && !isNaN(childIndex)) {
this.setOptionsOnClickInnerWidget({
rootWidgetIndex: index,
tabIndex,
childIndex,
});
return;
}
}
// 如果只是点击在 Tabs 内容区的空白处,则交给 Tabs 自己内部处理,避免整个 Tabs 组件被选中
const tabContentEl = event.target.closest(".tab-content");
if (tabContentEl && tabsRootEl) {
return;
}
}
//判断是否按住了Ctrl按钮表示Ctrl多选
let _this = this;
let eventWidget = null;
@@ -818,10 +932,40 @@ export default {
console.log(newSetup);
this.widgetOptions.setup = newSetup;
} else {
for (let i = 0; i < this.widgets.length; i++) {
if (this.widgetIndex == i) {
this.widgets[i].value[key] = this.deepClone(val);
this.setDefaultValue(this.widgets[i].options[key], val);
// 如果当前选中的是 Tabs 内部子组件,优先更新内部子组件的配置
if (this.innerWidgetSelected) {
const { rootWidgetIndex, tabIndex, childIndex } = this.innerWidgetSelected;
const rootWidget = this.widgets[rootWidgetIndex];
if (rootWidget && rootWidget.value && rootWidget.value.setup) {
const tabsList = rootWidget.value.setup.tabsList || [];
const targetTab = tabsList[tabIndex];
if (targetTab && targetTab.children && targetTab.children[childIndex]) {
const childWidget = targetTab.children[childIndex];
// 更新子组件的 value 和 options
if (!childWidget.value) {
this.$set(childWidget, "value", {});
}
childWidget.value[key] = this.deepClone(val);
if (childWidget.options && childWidget.options[key]) {
this.setDefaultValue(childWidget.options[key], val);
}
// 回写到 rootWidget 的 setup 中,保证 Tabs 组件保存时能带上最新配置
rootWidget.value.setup.tabsList = tabsList;
const setupArr = rootWidget.options && rootWidget.options.setup ? rootWidget.options.setup : [];
setupArr.forEach((item) => {
if (item.name === "tabsList") {
item.value = tabsList;
}
});
}
}
} else {
// 普通顶层组件的配置更新逻辑保持不变
for (let i = 0; i < this.widgets.length; i++) {
if (this.widgetIndex == i) {
this.widgets[i].value[key] = this.deepClone(val);
this.setDefaultValue(this.widgets[i].options[key], val);
}
}
}
}