vue带连线的树形组件(升级版)

之前开源过一个简单的https://blog.csdn.net/weixin_39168678/article/details/102457894#comments树形连线组件当时没做业务剥离而且也不能无限展开,现在优化了一下。

效果
vue带连线的树形组件(升级版)_第1张图片

点击查询效果,以及相关配置说明

图标下载地址
css下载地址

源码
主要是递归lineItem,稍微有点基础的应该一看就明白,不明白的按照项目路径copy过去就可以用了
lineItem

<template>
  <div class="tree_item_box">
    <div
      class="column-start-start linkLine_default"
      v-for="(item,s_index) in list"
      :key="s_index"
      :class="{
     
                'linkLine_first': s_index===0&list.length>1,
                'linkLine_half_top': s_index===0&&list.length===1,
                'linkLine_last': (s_index===(list.length-1)&&s_index!==0),
                'third_layer':!item.children,
                'second_layer':item.children
        }"
    >
      <div
        class="row-flex-start basic_banner"
        @click="itemClick(item)"
        :class="{
     
          'active_color': item.expand
        }"
      >
        <div
          v-if="item.children"
          class="reTree_icon"
          :style="{height: (size||14*1.2) +'px',width: (size||14*1.2) +'px'}"
          :class="{
     
          'reTree_default_icon': item.children.length===0,
          'reTree_collapse_icon': item.expand && item.children.length>0,
          'reTree_expand_icon': !item.expand && item.children.length>0,
        }"
        ></div>
        <div
          class="layer_text nowrap"
          :class="{
     
          'active_color': item.expand
        }"
        >{
     {
     item.label}}</div>
      </div>
      <line-item
        :list="item.children"
        v-on="$listeners"
        :size="size"
        v-if="item.expand&&item.children&&item.children.length>0"
      ></line-item>
    </div>
  </div>
</template>
<script>
export default {
     
  name: "line-item",
  props: {
     
    list: {
     
      type: Array,
      default: () => {
     
        return [];
      }
    },
    size: {
     
      type: Number,
      default: 14
    }
  },
  methods: {
     
    itemClick(item) {
     
      item.expand = !item.expand;
      this.$emit("itemClick", item);
      if (!item.children) {
     
        this.$emit("valClick", item);
      }
    }
  }
};
</script>
<style lang='scss'>
.active_color {
     
  color: #409EFF;
}
.reTree_box {
     
  overflow-y: auto;
}
.reTree_icon {
     
  width: 17px;
  height: 17px;
  margin-right: 10px;
}
.no_icon {
     
  width: 17px;
  height: 17px;
}
.tree_item_box {
     
  position: relative;
  width: 100%;
  cursor: pointer;
}
.basic_layer {
     
  width: 100%;
  position: relative;
  color: #606764;
  cursor: pointer;
  .layer_text {
     
    flex: 1;
  }
}
.first_vertical_line {
     
  content: "";
  position: absolute;
  width: 1px;
  left: 6px;
  top: 17px;
  background: #c3c5c8;
}
.basic_banner {
     
  position: relative;
  width: 100%;
  padding-bottom: 13px;
}
.second_layer {
     
  position: relative;
  width: 100%;
  cursor: pointer;
  padding-left: 25px;
}
.third_layer {
     
  position: relative;
  // padding-bottom: 15px;
  width: 100%;
  padding-left: 40px;
  color: #999999;
}

.second_layer::before {
     
  content: "";
  position: absolute;
  height: 1px;
  width: 16px;
  left: 9px;
  top: 9px;
  background: #c3c5c8;
}
.third_layer::before {
     
  content: "";
  position: absolute;
  height: 1px;
  width: 22px;
  left: 9px;
  top: 9px;
  background: #c3c5c8;
}

.linkLine_default::after {
     
  content: "";
  position: absolute;
  height: 100%;
  width: 1px;
  left: 9px;
  top: 0px;
  background: #c3c5c8;
}
.linkLine_first::after {
     
  content: "";
  position: absolute;
  /* 为了触顶 */
  top: -14px;
  height: calc(100% + 14px);
  width: 1px;
  left: 9px;
  background: #c3c5c8;
}
// 上半截
.linkLine_half_top::after {
     
  content: "";
  position: absolute;
  height: 24px;
  top: -14px;
  width: 1px;
  left: 9px;
  background: #c3c5c8;
}
.linkLine_last::after {
     
  content: "";
  position: absolute;
  height: 9px;
  width: 1px;
  left: 9px;
  top: 0px;
  background: #c3c5c8;
}
.reTree_collapse_icon {
     
  background: url("/static/image/reTree_collapse_.svg") no-repeat center center;
  background-size: contain;
}

.reTree_default_icon {
     
  background: url("/static/image/reTree_default_.svg") no-repeat center center;
  background-size: contain;
}

.reTree_expand_icon {
     
  background: url("/static/image/reTree_expand_.svg") no-repeat center center;
  background-size: contain;
}

.reTree_focus_icon {
     
  background: url("/static/image/reTree_focus_.svg") no-repeat center center;
  background-size: contain;
}
</style>

lineTree

<template>
  <div
    class="content column-start-center reTree_box"
    :style="{fontSize: (size||14) +'px',lineHeight:(size||14) +'px'}"
    style="width:347px"
  >
    <div class="column-start-center basic_layer" v-for="(item,index) in formatData" :key="index">
      <div
        class="row-flex-start basic_banner"
        :class="{
     
          'active_color': item.expand && item.children.length>0,
          
        }"
        @click="itemClick(item)"
      >
        <div
          class="reTree_icon"
          :style="{height: (size||14*1.2 )+'px',width: (size||14*1.2) +'px'}"
          :class="{
     
          'reTree_default_icon': item.children.length===0,
          'reTree_collapse_icon': item.expand && item.children.length>0,
          'reTree_expand_icon': !item.expand && item.children.length>0,
        }"
        ></div>
        <div class="layer_text nowrap">{
     {
     item.label}}</div>
      </div>

      <lineItem v-if="item.expand&&item.children.length>0" v-on="$listeners" :list="item.children" :size="size"></lineItem>
    </div>
  </div>
</template>
<script>
import lineItem from "./lineItem.vue";
export default {
     
  inheritAttrs: false,
  props: {
     
    pd: {
     },
    size: {
     
      type: Number,
      default: 14
    }
  },
  components: {
      lineItem },
  data() {
     
    return {
     
      selectedDetail: {
     },
      timer: {
     },
      formatData: []
    };
  },
  watch: {
     
    pd(n, o) {
     
      this.formatData = this.preDealData(n);
      console.log(this.formatData);
    }
  },
  created() {
     
    console.log("lineTree create");
    this.preDealData(this.pd);
  },

  methods: {
     
    preDealData(list) {
     
      list.forEach(x => {
     
        if (!x.expand) this.$set(x, "expand", false);
        if (x.children && x.children.length > 0) this.preDealData(x.children);
      });
      return list;
    },
    // 根据id展开树的具体项
    expandTreeItemById(idList) {
     
      let _this = this;
      function loopTree(list) {
     
        list.forEach(x => {
     
          if (idList.includes(x.id)) {
     
            _this.$set(x, "expand", true);
          } else {
     
            _this.$set(x, "expand", false);
          }
          if (x.children && x.children.length > 0) loopTree(x.children);
        });
        return list;
      }
      this.formatData = loopTree(this.pd);
    },
    itemClick(item){
     
      item.expand=!item.expand
      this.$emit('itemClick',item)
    },
    // 详情点击
    detailClick(data) {
     
      clearTimeout(this.timer);
      this.timer = setTimeout(() => {
     
        this.selectedDetail = data;
        this.$emit("detailClick", data);
      }, 300);
    },
    detailDoubleClick(data) {
     
      clearTimeout(this.timer);
      this.selectedDetail = data;
      this.$emit("detailDoubleClick", data);
    }
  }
};
</script>
<style lang="scss" scoped>
.active_color{
     
    color: #409EFF ;
}
.reTree_box {
     
  overflow-y: auto;
}
.reTree_icon {
     
  width: 17px;
  height: 17px;
  margin-right: 10px;
}

.basic_layer {
     
  width: 100%;
  position: relative;
  color: #606764;
  cursor: pointer;
  .layer_text {
     
    flex: 1;
  }
}
.first_vertical_line {
     
  content: "";
  position: absolute;
  width: 1px;
  left: 6px;
  top: 17px;
  background: #c3c5c8;
}
.basic_banner {
     
  position: relative;
  width: 100%;
  padding-bottom: 13px;
}
.second_layer {
     
  position: relative;
  width: 100%;
  cursor: pointer;
  padding-left: 25px;
}
.third_layer {
     
  position: relative;
  padding-bottom: 15px;
  width: 100%;
  padding-left: 40px;
  color: #999999;
}

.second_layer::before {
     
  content: "";
  position: absolute;
  height: 1px;
  width: 16px;
  left: 9px;
  top: 9px;
  background: #c3c5c8;
}
.third_layer::before {
     
  content: "";
  position: absolute;
  height: 1px;
  width: 20px;
  left: 9px;
  top: 9px;
  background: #c3c5c8;
}

.linkLine_default::after {
     
  content: "";
  position: absolute;
  height: 100%;
  width: 1px;
  left: 9px;
  top: 0px;
  background: #c3c5c8;
}
.linkLine_first::after {
     
  content: "";
  position: absolute;
  /* 为了触顶 */
  top: -14px;
  height: calc(100% + 14px);
  width: 1px;
  left: 9px;
  background: #c3c5c8;
}
// 上半截
.linkLine_half_top::after {
     
  content: "";
  position: absolute;
  height: 24px;
  top: -14px;
  width: 1px;
  left: 9px;
  background: #c3c5c8;
}
.linkLine_last::after {
     
  content: "";
  position: absolute;
  height: 9px;
  width: 1px;
  left: 9px;
  top: 0px;
  background: #c3c5c8;
}
.reTree_collapse_icon {
     
  background: url("/static/image/reTree_collapse_.svg") no-repeat center center;
  background-size: contain;
}

.reTree_default_icon {
     
  background: url("/static/image/reTree_default_.svg") no-repeat center center;
  background-size: contain;
}

.reTree_expand_icon {
     
  background: url("/static/image/reTree_expand_.svg") no-repeat center center;
  background-size: contain;
}

.reTree_focus_icon {
     
  background: url("/static/image/reTree_focus_.svg") no-repeat center center;
  background-size: contain;
}
</style>

你可能感兴趣的:(vue自定义组件)