Merge pull request 'tab20260208' (#3) from tab20260208 into main
Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
v-for="(it, idx) in item.list"
|
||||
:key="idx"
|
||||
draggable="true"
|
||||
@dragstart="dragStart(it.code)"
|
||||
@dragstart="dragStart(it.code, $event)"
|
||||
@dragend="dragEnd()"
|
||||
>
|
||||
<div class="tools-item">
|
||||
@@ -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"
|
||||
@@ -449,6 +451,8 @@ export default {
|
||||
rect : null, //框选矩形对象
|
||||
openMulDrag: false, //批量拖拽开关
|
||||
moveWidgets:{}, //记录移动的组件的起始left和top属性
|
||||
// 记录当前是否选中了 Tabs 内部子组件,如果为 null 则表示选中的是顶层组件或大屏
|
||||
innerWidgetSelected: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -988,9 +992,14 @@ export default {
|
||||
getPXUnderScale(px) {
|
||||
return this.bigscreenScaleInWorkbench * px;
|
||||
},
|
||||
dragStart(widgetCode) {
|
||||
dragStart(widgetCode, evt) {
|
||||
this.dragWidgetCode = widgetCode;
|
||||
this.currentWidgetTotal = this.widgets.length; // 当前操作面板上有多少各组件
|
||||
// 通过 dataTransfer 传递组件 code,便于 Tabs 等嵌套容器在 drop 时读取(不依赖父组件链)
|
||||
if (evt && evt.dataTransfer) {
|
||||
evt.dataTransfer.setData("application/x-widget-code", widgetCode);
|
||||
evt.dataTransfer.effectAllowed = "copy";
|
||||
}
|
||||
},
|
||||
dragEnd() {
|
||||
/**
|
||||
@@ -1017,12 +1026,48 @@ export default {
|
||||
});
|
||||
},
|
||||
dragOver(evt) {
|
||||
// 鼠标在 Tabs 标签内容区内时不处理,让 tab 内容区成为 drop 目标,避免工作台抢走
|
||||
if (evt.target && evt.target.closest && (evt.target.closest(".tab-content") || evt.target.closest("[data-tab-content]"))) {
|
||||
return;
|
||||
}
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
evt.dataTransfer.dropEffect = "copy";
|
||||
},
|
||||
// 拖动一个组件放到工作区中去,在拖动结束时,放到工作区对应的坐标点上去
|
||||
widgetOnDragged(evt) {
|
||||
// 若落在 Tabs 标签内容区内,由 widgetTabs 自己处理,不在此处添加到主画布
|
||||
if (evt.target && evt.target.closest && (evt.target.closest(".tab-content") || evt.target.closest("[data-tab-content]"))) {
|
||||
return;
|
||||
}
|
||||
// 若落在 Tabs 组件的外层容器上(如 avue-draggable),委托给对应 Tabs 加入当前激活的 tab
|
||||
const widgetsRef = this.$refs.widgets;
|
||||
const widgetList = Array.isArray(widgetsRef) ? widgetsRef : (widgetsRef ? [widgetsRef] : []);
|
||||
if (evt.target && widgetList.length) {
|
||||
for (let i = 0; i < widgetList.length; i++) {
|
||||
const w = widgetList[i];
|
||||
if (!w || !w.$el || !w.$el.contains(evt.target)) continue;
|
||||
if (this.widgets[i] && this.widgets[i].type === "widget-tabs") {
|
||||
const findTabs = (comp) => {
|
||||
if (!comp) return null;
|
||||
if (typeof comp.addWidgetFromDrop === "function") return comp;
|
||||
if (comp.$children && comp.$children.length) {
|
||||
for (let j = 0; j < comp.$children.length; j++) {
|
||||
const found = findTabs(comp.$children[j]);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const tabsComp = findTabs(w);
|
||||
if (tabsComp) {
|
||||
tabsComp.addWidgetFromDrop(evt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
let widgetType = this.dragWidgetCode;
|
||||
|
||||
// 获取结束坐标和列名
|
||||
@@ -1058,6 +1103,7 @@ export default {
|
||||
options: tool.options,
|
||||
};
|
||||
// 处理默认值
|
||||
console.log(widgetType)
|
||||
const widgetJsonValue = this.getWidgetConfigValue(widgetJson);
|
||||
|
||||
widgetJsonValue.value.position.left =
|
||||
@@ -1138,6 +1184,118 @@ export default {
|
||||
});
|
||||
this.widgetOptions = this.deepClone(this.widgets[obj.index]["options"]);
|
||||
},
|
||||
/**
|
||||
* 选中 Tabs 组件内部的子组件,展示该子组件的右侧配置
|
||||
*/
|
||||
setOptionsOnClickInnerWidget(payload) {
|
||||
if (!payload) return;
|
||||
console.log('setOptionsOnClickInnerWidget payload:', payload);
|
||||
|
||||
const { rootWidgetIndex, tabIndex, childIndex } = payload;
|
||||
const rootIndex = typeof rootWidgetIndex === "number" ? rootWidgetIndex : 0;
|
||||
const rootWidget = this.widgets[rootIndex];
|
||||
if (!rootWidget || !rootWidget.value) return;
|
||||
|
||||
// 1. 兼容两种存储位置:value.setup.tabsList / options.setup 里的 tabsList
|
||||
const setupObj = rootWidget.value.setup || {};
|
||||
let tabsList = setupObj.tabsList;
|
||||
if (!tabsList || !tabsList.length) {
|
||||
const setupArr = rootWidget.options && rootWidget.options.setup ? rootWidget.options.setup : [];
|
||||
const tabsItem = setupArr.find(it => it.name === "tabsList");
|
||||
if (tabsItem && Array.isArray(tabsItem.value)) {
|
||||
tabsList = tabsItem.value;
|
||||
}
|
||||
}
|
||||
tabsList = tabsList || [];
|
||||
|
||||
const targetTab = tabsList[tabIndex];
|
||||
if (!targetTab || !targetTab.children || !targetTab.children[childIndex]) {
|
||||
console.warn("Tabs 子组件未找到:", { rootIndex, tabIndex, childIndex, tabsList });
|
||||
return;
|
||||
}
|
||||
|
||||
let childWidget = targetTab.children[childIndex];
|
||||
|
||||
// 2. 确保子组件有 options(老数据里可能只有 type + value)
|
||||
if (!childWidget.options) {
|
||||
try {
|
||||
const tool = getToolByCode(childWidget.type);
|
||||
if (tool && tool.options) {
|
||||
childWidget.options = this.deepClone(tool.options);
|
||||
} else {
|
||||
console.warn("未找到子组件 options 配置:", childWidget.type);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("为子组件补全 options 失败:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 用实际 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];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 4. 记录当前选中的内部子组件路径 & 切换右侧到子组件配置
|
||||
this.innerWidgetSelected = {
|
||||
rootWidgetIndex: rootIndex,
|
||||
tabIndex,
|
||||
childIndex,
|
||||
};
|
||||
this.screenCode = "";
|
||||
this.widgetIndex = rootIndex;
|
||||
this.activeName = "first";
|
||||
|
||||
this.widgetOptions = this.deepClone(childWidget.options || {});
|
||||
},
|
||||
/**
|
||||
* 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;
|
||||
// 选中 Tabs 组件本身,触发蓝色点框和右侧配置
|
||||
this.innerWidgetSelected = null;
|
||||
this.widgetsClickFocus(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);
|
||||
},
|
||||
widgetsClick(event,index) {
|
||||
console.log("widgetsClick");
|
||||
//判断是否按住了Ctrl按钮,表示Ctrl多选
|
||||
@@ -1237,6 +1395,35 @@ export default {
|
||||
console.log(newSetup);
|
||||
this.widgetOptions.setup = newSetup;
|
||||
} else {
|
||||
// 如果当前选中的是 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);
|
||||
@@ -1244,6 +1431,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
setDefaultValue(options, val) {
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
|
||||
@@ -355,9 +355,16 @@ export default {
|
||||
},
|
||||
setActiveTab() {
|
||||
const tabs = this.tabsList;
|
||||
if (tabs && tabs.length > 0) {
|
||||
this.activeTab = tabs[0].name || 'tab0';
|
||||
if (!tabs || tabs.length === 0) return;
|
||||
|
||||
// 如果当前 activeTab 还在 tabs 中,则保持不变,避免每次数据变化都跳回第一个标签
|
||||
if (this.activeTab) {
|
||||
const exist = tabs.some((t, i) => (t.name || `tab${i}`) === this.activeTab);
|
||||
if (exist) return;
|
||||
}
|
||||
|
||||
// 当前没有选中标签,或原选中标签已被删除时,才默认选中第一个
|
||||
this.activeTab = tabs[0].name || 'tab0';
|
||||
},
|
||||
handleTabClick(tab) {
|
||||
const currentTab = this.tabsList.find(t => (t.name || `tab${this.tabsList.indexOf(t)}`) === tab.name);
|
||||
@@ -824,6 +831,7 @@ export default {
|
||||
.tab-content-empty {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
Reference in New Issue
Block a user