vue tree设置搜索滚动

112 阅读1分钟
<template>
  <div class="equipmentList-wrap">
    <p-input-search class="leftTree-header mb-2" allowClear placeholder="请输入关键字搜索" @search="onSearch" />
    <div class="left_tree_wrapper" id="recipeTree" :style="{ height: TreeDomHeight }">
      <p-spin v-if="treeData.length <= 0" class="loading-spin" size="large" />
      <p-tree
        v-else
        show-icon
        @select="onSelect"
        :auto-expand-parent="autoExpandParent"
        :tree-data="treeData"
        :replaceFields="replaceFields"
        :expanded-keys="expandedKeys"
        :loadedKeys="loadedKeys"
        @expand="onExpand"
        @load="loadData"
      >
        <template slot="title" slot-scope="dataRef">
          <span v-if="dataRef.hasRecipe && dataRef.type == 'equipment'" class="hasRecipeSuccess"></span>
          <span v-if="!dataRef.hasRecipe && dataRef.type == 'equipment'" class="hasRecipeWarning"></span>

          <span v-if="dataRef.title.indexOf(searchValue) !== -1" :id="dataRef.title">
            {{ dataRef.title.substr(0, dataRef.title.indexOf(searchValue)) }}
            <span style="color: #f50">{{ searchValue }}</span>
            {{ dataRef.title.substr(dataRef.title.indexOf(searchValue) + searchValue.length) }}
          </span>
          <span v-else>{{ dataRef.title }}</span>
        </template>
        <p-icon type="database" slot="bank" theme="twoTone" />
      </p-tree>
    </div>
  </div>
</template>
<script>
import { getAllTree } from '@/api/tree.js'
const getParentKey = (key, tree) => {
  let parentKey
  for (let i = 0; i < tree.length; i++) {
    const node = tree[i]
    if (node.children) {
      if (node.children.some((item) => item.key === key)) {
        parentKey = node.key
      } else if (getParentKey(key, node.children)) {
        parentKey = getParentKey(key, node.children)
      }
    }
  }
  return parentKey
}

const traverse  = (tree)=> {
  for (let i = 0; i < tree.length; i++) {
    const item = tree[i]
    item.key = item.id
    item.title = item.eqpId
    item.scopedSlots = { title: 'title' }
    item.slots = item.type == 'equipment' ? {} : { icon: 'bank' }
    if (item.children) {
      traverse(item.children); //递归遍历
    }
  }
}
const setStyleDisplay = (title, nodes) => {
    let nums = 0
    for (let data of nodes) {
        let bl = false
        if (data.title.indexOf(title) > -1) {
            bl = true
            nums += 1
        }
        if (!!data.children) {
            if (data.children.length > 0) {
                let count = setStyleDisplay(title, data.children)
                // 如果想 子节点有key 即使父节点没有key也显示的话 添加下面的语句
                nums += count
                if (!bl && count == 0) {
                    bl = false
                } else {
                    bl = true
                }
            }
        }
        if (bl) {
            delete data['style']
        } else {
            data['style'] = 'display: none'
        }
    }
    return nums
}

export default {
  data() {
    return {
      dataList: [],
      hasRecipe: null,
      searchValue: '',
      timer: null,
      loadedKeys: [],
      expandedKeys: [],
      autoExpandParent: true,
      treeData: [],
      TreeDomHeight: '67vh',
      replaceFields: {
        title: 'title',
        key: 'key',
        children: 'children'
      }
    }
  },
  methods: {
    // 新加: 获取组织的第一级
    getCompanyList() {
      const param = {
        company: '',
        factory: '',
        lineId: ''
      }
      this.loadedKeys = []
      this.expandedKeys = []
      getAllTree(param).then((res) => {
        let resData = res.data
        if (resData.success && resData.data) {
          // this.treeData = resData.data.map((item) => {
          //   item.key = item.id
          //   item.title = item.eqpId
          //   item.scopedSlots = { title: 'title' }
          //   item.slots = { icon: 'bank' }
          //   return item
          // })
          traverse(resData.data)
          this.treeData = resData.data
          this.generateList(this.treeData)
        } else {
          this.$message.error('请稍后再试!')
        }
      })
    },
    onLoadData(treeNode) {
      return new Promise((resolve) => {
        if (treeNode.dataRef.children) {
          resolve()
          return
        }
        console.log(treeNode.dataRef)
        const param = {
          company: treeNode.dataRef.company,
          factory: treeNode.dataRef.factory,
          lineId: treeNode.dataRef.lineId
        }
        setTimeout(async () => {
          const res = await getAllTree(param)
          let resData = res.data
          if (resData && resData.code == 0) {
            let data = resData.data
            treeNode.dataRef.children = data.map((item) => {
              item.key = item.treeId
              item.title = item.eqpId
              item.scopedSlots = { title: 'title' }
              item.slots = item.type == 'equipment' ? {} : { icon: 'bank' }
              item.isLeaf = item.type == 'equipment'
              return item
            })
          }
          this.treeData = [...this.treeData]
          this.generateList(this.treeData)
          resolve()
        }, 500)
      })
    },
    loadData(loadedKeys) {
      this.loadedKeys = loadedKeys
    },
    onExpand(expandedKeys) {
      this.expandedKeys = expandedKeys
      this.autoExpandParent = false
    },
    // 模糊查询
    onSearch(val) {
      const value = val.trim(),
        { dataList } = this
      let machArr = []
      let expandedKeys = []
      if(value && value.length<5) {
        this.$message.info('输入关键字较少,会导致页面出现卡机状态,请至少输入5位以上的关键字')
        return
      }
      if(value) {
      expandedKeys = dataList
        .map((item) => {
          if (item.title.indexOf(value) > -1) {
            machArr.push(item.title)
            return getParentKey(item.key, this.treeData)
          }
          return ''
        })
        .filter((item, i, self) => item && self.indexOf(item) === i)
      }
      // 隐藏 不需要的选项
      setStyleDisplay(value,this.treeData)
      this.searchValue = value
      this.$forceUpdate()
      if(expandedKeys.length===0) return
      Object.assign(this, {
        expandedKeys,
        autoExpandParent: true
      })
      this.$nextTick(()=>{
        setTimeout(()=>{
          if(machArr.length>0) {
            document.getElementById(machArr[0]).scrollIntoView()
          }
        },300)
      })
    },

    // 格式化树形结构
    generateList(data) {
      for (let i = 0; i < data.length; i++) {
        const node = data[i]
        const key = node.key
        const title = node.title
        this.dataList.push({ key, title })
        if (node.children) {
          this.generateList(node.children)
        }
      }
    },

    onSelect(keys, node) {
      node.selected ? this.$emit('checkNode', node.node.dataRef) : this.$emit('checkNode', {})
    },

    // 计算架构树的容器的高度
    computedTreeHeight() {
      let recipeTreeDom = document.getElementById('recipeTree')
      let screenHeight = document.body.clientHeight
      let recipeTreeDomTop = recipeTreeDom&& recipeTreeDom.getBoundingClientRect().top + 30
      this.TreeDomHeight = screenHeight - recipeTreeDomTop + 'px'
    }
  },
  created() {
    this.getCompanyList()
  },
  beforeDestroy() {
    window.removeEventListener('resize', this.computedTreeHeight)
  },
  mounted() {
    this.computedTreeHeight()
    // 监听屏幕高度
    window.addEventListener('resize', this.computedTreeHeight)
  }
}
</script>
<style scoped lang="less">
.statusButton {
  cursor: pointer;
}
.hasRecipeSuccess {
  position: relative;
  border-radius: 100%;
  display: inline-block;
  width: 12px;
  top: 1px;
  height: 12px;
  margin-right: 4px;
  background-color: rgb(15, 187, 78);
}
.hasRecipeWarning {
  position: relative;
  border-radius: 100%;
  display: inline-block;
  width: 12px;
  top: 1px;
  height: 12px;
  margin-right: 4px;
  background-color: #ffb048;
}
.equipmentList-wrap {
  height: 100%;
  font-size: 14px;
  font-weight: 400;
  overflow: hidden;

  .left_tree_wrapper {
    position: relative;
    // flex: 1 0 22%;
    overflow: auto;
    .loading-spin {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
  }

  /deep/ .poros-tree {
    font-size: 14px;
    color: #333;
    font-weight: 400;
    > li {
      > .poros-tree-node-content-wrapper {
        font-weight: bold;
      }
    }

    li {
      padding: 4px 0;

      .poros-tree-node-content-wrapper {
        padding-left: 0;
      }
      .poros-tree-node-content-wrapper.poros-tree-node-selected {
        background-color: @primary-color;
        color: #fff;
      }
      span.poros-tree-switcher {
        width: 20px;
        height: 24px;
        line-height: 24px;
        color: #848484;
      }
    }
  }
}
.equipment-item {
  color: #000;
  font-size: 14px;
  list-style: none;
  cursor: pointer;
  user-select: none;
  padding: 4px 0px;
}
.equipment-item:hover {
  color: #000;
  background-color: #e8e8e8;
  font-size: 14px;
  list-style: none;
  cursor: pointer;
  user-select: none;
}
.equipment-item.active {
  color: #fff;
  background-color: blue;
  font-size: 14px;
  list-style: none;
  cursor: pointer;
  user-select: none;
}
</style>