diff --git a/src/views/bigscreenDesigner/designer/components/drillDrownSetting.vue b/src/views/bigscreenDesigner/designer/components/drillDrownSetting.vue
index ad3e135..aa2a816 100644
--- a/src/views/bigscreenDesigner/designer/components/drillDrownSetting.vue
+++ b/src/views/bigscreenDesigner/designer/components/drillDrownSetting.vue
@@ -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()"
>
@@ -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,10 +1395,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);
+ }
}
}
}
diff --git a/src/views/bigscreenDesigner/designer/widget/form/widgetTabs.vue b/src/views/bigscreenDesigner/designer/widget/form/widgetTabs.vue
index 9082c96..0cb7935 100644
--- a/src/views/bigscreenDesigner/designer/widget/form/widgetTabs.vue
+++ b/src/views/bigscreenDesigner/designer/widget/form/widgetTabs.vue
@@ -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);