yusheng 2 месяцев назад
Родитель
Сommit
b22c0b998b

+ 431 - 0
src/components/CommomSelect/dept-selectNew.vue

@@ -0,0 +1,431 @@
+<template>
+  <div class="tree-select-wrapper">
+    <el-select
+      ref="selectRef"
+      v-model="selectedLabel"
+      :popper-append-to-body="popperAppendToBody"
+      :teleported="teleported"
+      :placeholder="placeholder"
+      :disabled="disabled"
+      :clearable="clearable"
+      :filterable="filterable"
+      :filter-method="filterMethod"
+      popper-class="tree-select-popper"
+      @clear="handleClear"
+      @visible-change="handleVisibleChange"
+    >
+      <!-- 关键修复:el-option 的 value 不能绑定 selectedLabel,需要绑定一个唯一值 -->
+      <el-option
+        :value="selectedValue"
+        :label="selectedLabel"
+        style="height: auto; padding: 0"
+        class="tree-select-option"
+      >
+        <!-- 搜索框 -->
+        <div v-if="filterable" class="tree-search">
+          <el-input
+            v-model="searchText"
+            size="small"
+            placeholder="输入关键词搜索"
+            clearable
+            @input="handleSearch"
+          />
+        </div>
+
+        <!-- 树形结构 -->
+        <el-tree
+          ref="treeRef"
+          :data="treeData"
+          :props="treeProps"
+          :show-checkbox="multiple"
+          :node-key="nodeKey"
+          :default-expand-all="defaultExpandAll"
+          :default-expanded-keys="defaultExpandedKeys"
+          :filter-node-method="filterNode"
+          :check-strictly="checkStrictly"
+          :expand-on-click-node="false"
+          :highlight-current="!multiple"
+          :current-node-key="currentNodeKey"
+          @node-click="handleNodeClick"
+          @check="handleCheck"
+        >
+          <span slot-scope="{ node, data }" class="custom-tree-node">
+            <span>{{ node.label }}</span>
+            <span v-if="data.count" class="node-count">({{ data.count }})</span>
+          </span>
+        </el-tree>
+      </el-option>
+    </el-select>
+  </div>
+</template>
+
+<script>
+  import { listOrganizations } from '@/api/system/organization';
+
+  export default {
+    name: 'TreeSelect',
+    props: {
+      value: {
+        type: [String, Number, Array],
+        default: null
+      },
+      data: {
+        type: Array,
+        required: true,
+        default: () => []
+      },
+      multiple: {
+        type: Boolean,
+        default: false
+      },
+      placeholder: {
+        type: String,
+        default: '请选择'
+      },
+      disabled: {
+        type: Boolean,
+        default: false
+      },
+      clearable: {
+        type: Boolean,
+        default: true
+      },
+      filterable: {
+        type: Boolean,
+        default: false
+      },
+      nodeKey: {
+        type: String,
+        default: 'id'
+      },
+      treeProps: {
+        type: Object,
+        default: () => ({
+          children: 'children',
+          label: 'name'
+        })
+      },
+      defaultExpandAll: {
+        type: Boolean,
+        default: true
+      },
+      defaultExpandedKeys: {
+        type: Array,
+        default: () => []
+      },
+      checkStrictly: {
+        type: Boolean,
+        default: false
+      },
+      popperAppendToBody: {
+        type: Boolean,
+        default: false
+      },
+      teleported: {
+        type: Boolean,
+        default: false
+      }
+    },
+    data() {
+      return {
+        selectedLabel: '', // 显示的文本
+        selectedValue: null, // 选中的值(用于单选)
+        selectedValues: [], // 多选时选中的值数组
+        currentNodeKey: null, // 当前高亮的节点key
+        searchText: '', // 搜索关键词
+        treeData: [], // 过滤后的树数据
+        dataLoaded: false // 新增:标记数据是否加载完成
+      };
+    },
+    watch: {
+      value: {
+        handler() {
+          // 等待数据加载完成后再初始化
+          if (this.dataLoaded) {
+            this.initSelectedValue();
+          }
+        },
+        immediate: true
+      },
+      // 新增:监听 treeData 变化,数据加载完成后初始化选中值
+      treeData: {
+        handler(val) {
+          if (val && val.length && !this.dataLoaded) {
+            this.dataLoaded = true;
+            this.initSelectedValue();
+          }
+        },
+        immediate: true
+      }
+    },
+    async created() {
+      await this.getData(); // 改为 await 等待数据加载完成
+    },
+    mounted() {
+      this.$nextTick(() => {
+        if (this.$refs.treeRef) {
+          this.$refs.treeRef.$on('node-expand', this.updatePopperPosition);
+          this.$refs.treeRef.$on('node-collapse', this.updatePopperPosition);
+        }
+      });
+    },
+    beforeDestroy() {
+      if (this.$refs.treeRef) {
+        this.$refs.treeRef.$off('node-expand', this.updatePopperPosition);
+        this.$refs.treeRef.$off('node-collapse', this.updatePopperPosition);
+      }
+    },
+    methods: {
+      async getData(params = {}) {
+        const data = await listOrganizations(params);
+        this.treeData = this.$util.toTreeData({
+          data: data || [],
+          idField: 'id',
+          parentIdField: 'parentId'
+        });
+        // 数据加载完成后标记
+        this.dataLoaded = true;
+      },
+
+      // 初始化选中的值(优化:从 this.treeData 查找,而不是 this.data)
+      initSelectedValue() {
+        // 如果数据还没加载完成,先不处理
+        if (!this.dataLoaded && !this.treeData.length) {
+          return;
+        }
+
+        if (this.multiple) {
+          const val = this.value || [];
+          this.selectedValues = [...val];
+          this.selectedLabel = this.getSelectedLabels(val).join('、');
+          this.selectedValue = val.length ? val.join(',') : '';
+
+          this.$nextTick(() => {
+            if (this.$refs.treeRef) {
+              this.$refs.treeRef.setCheckedKeys(this.selectedValues);
+            }
+          });
+        } else {
+          this.selectedValue = this.value;
+          if (this.value !== null && this.value !== undefined) {
+            // 关键修复:从 this.treeData 查找,而不是 this.data
+            const node = this.findNodeByValue(this.value, this.treeData);
+            this.selectedLabel = node ? node[this.treeProps.label] : '';
+            this.currentNodeKey = this.value;
+          } else {
+            this.selectedLabel = '';
+            this.currentNodeKey = null;
+          }
+
+          this.$nextTick(() => {
+            if (this.$refs.treeRef && this.value) {
+              this.$refs.treeRef.setCurrentKey(this.value);
+            }
+          });
+        }
+      },
+
+      // 根据值查找节点(从指定数据源查找)
+      findNodeByValue(value, dataSource = this.treeData) {
+        if (!dataSource || !dataSource.length) return null;
+
+        for (const item of dataSource) {
+          if (item[this.nodeKey] === value) {
+            return item;
+          }
+          const children = item[this.treeProps.children];
+          if (children && children.length) {
+            const found = this.findNodeByValue(value, children);
+            if (found) return found;
+          }
+        }
+        return null;
+      },
+
+      // 获取节点标签
+      getNodeLabel(value, dataSource = this.treeData) {
+        const node = this.findNodeByValue(value, dataSource);
+        return node ? node[this.treeProps.label] : '';
+      },
+
+      // 批量获取标签文本(多选)
+      getSelectedLabels(values, dataSource = this.treeData, result = []) {
+        if (!dataSource || !dataSource.length) return result;
+
+        for (const item of dataSource) {
+          if (values.includes(item[this.nodeKey])) {
+            result.push(item[this.treeProps.label]);
+          }
+          const children = item[this.treeProps.children];
+          if (children && children.length) {
+            this.getSelectedLabels(values, children, result);
+          }
+        }
+        return result;
+      },
+
+      // 树节点点击(单选模式)
+      handleNodeClick(data) {
+        if (!this.multiple) {
+          const value = data[this.nodeKey];
+          const label = data[this.treeProps.label];
+
+          this.selectedValue = value;
+          this.selectedLabel = label;
+          this.currentNodeKey = value;
+
+          this.$emit('input', value);
+          this.$emit('change', value, data);
+
+          this.$nextTick(() => {
+            if (this.$refs.selectRef) {
+              this.$refs.selectRef.visible = false;
+            }
+          });
+        }
+      },
+
+      // 树节点勾选(多选模式)
+      handleCheck(data, checkedState) {
+        const checkedKeys = checkedState.checkedKeys;
+        this.selectedValues = [...checkedKeys];
+        this.selectedLabel = this.getSelectedLabels(checkedKeys).join('、');
+        this.selectedValue = checkedKeys.length ? checkedKeys.join(',') : '';
+
+        this.$emit('input', checkedKeys);
+        this.$emit('change', checkedKeys, data);
+      },
+
+      // 清空
+      handleClear() {
+        if (this.multiple) {
+          this.selectedValues = [];
+          this.selectedLabel = '';
+          this.selectedValue = '';
+          this.$emit('input', []);
+          if (this.$refs.treeRef) {
+            this.$refs.treeRef.setCheckedKeys([]);
+          }
+        } else {
+          this.selectedValue = null;
+          this.selectedLabel = '';
+          this.currentNodeKey = null;
+          this.$emit('input', null);
+          if (this.$refs.treeRef) {
+            this.$refs.treeRef.setCurrentKey(null);
+          }
+        }
+      },
+
+      // 下拉框显示/隐藏
+      handleVisibleChange(visible) {
+        if (visible) {
+          this.$nextTick(() => {
+            if (this.$refs.treeRef) {
+              if (this.multiple) {
+                this.$refs.treeRef.setCheckedKeys(this.selectedValues);
+              } else {
+                this.$refs.treeRef.setCurrentKey(this.selectedValue);
+              }
+            }
+            this.updatePopperPosition();
+          });
+        }
+      },
+
+      // 更新下拉框位置
+      updatePopperPosition() {
+        this.$nextTick(() => {
+          if (this.$refs.selectRef && this.$refs.selectRef.visible) {
+            const select = this.$refs.selectRef;
+            if (select.popper) {
+              select.popper.update();
+            }
+          }
+        });
+      },
+
+      // 树节点过滤
+      filterNode(value, data) {
+        if (!value) return true;
+        const label = data[this.treeProps.label];
+        return label && label.toLowerCase().includes(value.toLowerCase());
+      },
+
+      // 搜索方法
+      filterMethod(query) {
+        this.searchText = query;
+      },
+
+      // 处理搜索
+      handleSearch(value) {
+        if (this.$refs.treeRef) {
+          this.$refs.treeRef.filter(value);
+        }
+      }
+    }
+  };
+</script>
+
+<style scoped>
+  .tree-select-wrapper {
+    width: 100%;
+  }
+
+  .tree-search {
+    padding: 8px 12px;
+    border-bottom: 1px solid #e4e7ed;
+  }
+
+  .tree-search >>> .el-input__inner {
+    height: 28px;
+    line-height: 28px;
+  }
+
+  .custom-tree-node {
+    display: inline-block;
+    width: 100%;
+    font-size: 14px;
+  }
+
+  .node-count {
+    margin-left: 8px;
+    color: #909399;
+    font-size: 12px;
+  }
+</style>
+
+<style>
+  /* 下拉框样式 - 全局样式 */
+  .tree-select-popper {
+    max-height: 400px;
+    overflow: auto;
+    padding: 0 !important;
+  }
+
+  .tree-select-popper .el-tree {
+    min-width: 200px;
+    max-height: 350px;
+    overflow: auto;
+  }
+
+  .tree-select-popper .el-tree-node__content {
+    height: 32px;
+  }
+
+  .tree-select-popper .el-checkbox {
+    margin-right: 8px;
+  }
+
+  /* 关键修复:让 el-option 内容完全展开 */
+  .tree-select-option {
+    height: auto !important;
+    line-height: normal !important;
+    padding: 0 !important;
+  }
+
+  .tree-select-option .el-select-dropdown__item {
+    height: auto !important;
+    padding: 0 !important;
+  }
+</style>

+ 32 - 26
src/views/equipmentOperationMonitoring/index.vue

@@ -1,5 +1,9 @@
 <template>
-  <vue-fullscreen v-model="isFullscreen" :exit-on-click-wrapper="false">
+  <vue-fullscreen
+    v-model="isFullscreen"
+    :exit-on-click-wrapper="false"
+    style="z-index: 999"
+  >
     <div
       class="energy-monitoring card-mode"
       :style="{
@@ -80,6 +84,9 @@
                     style="width: 280px"
                   >
                   </el-input>
+
+        
+
                   <DeptSelect
                     v-model="postId"
                     placeholder="使用单位"
@@ -213,7 +220,7 @@
   import { getTreeByPid } from '@/api/classifyManage';
   import { basicAreaPageAPI } from '@/api/factoryModel';
   import { businessStatus } from '@/utils/dict/warehouse';
-  import DeptSelect from '@/components/CommomSelect/dept-select.vue';
+  import DeptSelect from '@/components/CommomSelect/dept-selectNew.vue';
   import { component } from 'vue-fullscreen';
 
   export default {
@@ -463,7 +470,6 @@
     // padding: 15px;
 
     .monitoring-card {
-
       ::v-deep .el-card__body {
         height: 100%;
         padding: 15px;
@@ -963,28 +969,28 @@
       transform: rotate(10deg);
     }
   }
-        .fullscreen-btn {
-        position: absolute;
-        right: 15px;
-        width: 36px;
-        height: 36px;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        background: rgba(56, 255, 249, 0.15);
-        border: 1px solid #38fff9;
-        border-radius: 4px;
-        cursor: pointer;
-        transition: all 0.3s ease;
-
-        i {
-          font-size: 18px;
-          color: #38fff9;
-        }
+  .fullscreen-btn {
+    position: absolute;
+    right: 15px;
+    width: 36px;
+    height: 36px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: rgba(56, 255, 249, 0.15);
+    border: 1px solid #38fff9;
+    border-radius: 4px;
+    cursor: pointer;
+    transition: all 0.3s ease;
+
+    i {
+      font-size: 18px;
+      color: #38fff9;
+    }
 
-        &:hover {
-          background: rgba(56, 255, 249, 0.3);
-          box-shadow: 0 0 10px rgba(56, 255, 249, 0.5);
-        }
-      }
+    &:hover {
+      background: rgba(56, 255, 249, 0.3);
+      box-shadow: 0 0 10px rgba(56, 255, 249, 0.5);
+    }
+  }
 </style>