文件目录组件

treeContainer/index
<template>
  <div style="border:1px solid #ccc;padding:0 20px;" v-if="show">
    树形
    <hr />
    <treeItem :arr="data" />
  </div>
</template>

<script>
import treeItem from "./tree-item";

export default {
  name: "",
  props: {
    show: {
      type: Boolean,
      default: false,
    },
  },
  components: { treeItem },
  watch: {
    show: {
      handler(val, oldVal) {
        console.log(oldVal);
        if (!val) {
          this.data = this.takeup(this.data);
        }
      },
      deep: true, //true 深度监听
    },
  },
  data() {
    return {
      data: [
        {
          label: "一级 1",
          flag: false,
          children: [
            {
              label: "二级 1-1",
              flag: false,
              children: [
                {
                  label: "三级 1-1-1",
                },
              ],
            },
          ],
        },
        {
          label: "一级 2",
          flag: false,
          children: [
            {
              label: "二级 2-1",
              flag: false,
              children: [
                {
                  label: "三级 2-1-1",
                },
              ],
            },
            {
              label: "二级 2-2",
              flag: false,
              children: [
                {
                  label: "三级 2-2-1",
                },
              ],
            },
          ],
        },
        {
          label: "一级 3",
          flag: false,
          children: [
            {
              label: "二级 3-1",
              flag: false,
              children: [
                {
                  label: "三级 3-1-1",
                },
              ],
            },
            {
              label: "二级 3-2",
              flag: false,
              children: [
                {
                  label: "三级 3-2-1",
                },
              ],
            },
          ],
        },
      ],
      defaultProps: {
        children: "children",
        label: "label",
      },
    };
  },
  computed: {},
  created() {},
  mounted() {},
  beforeDestroy() {},
  methods: {
    takeup(arr) {
      return arr.map((item) => {
        item.flag = false;
        if (item.children) this.takeup(item.children);
        return item;
      });
    },
  },
};
</script>
<style>
</style>

treeContainer/tree-item.vue
<template>
  <div style="width:400px;">
    <div v-for="(item,index) in arr" :key="index" class="tree-div">
      <i @click="tagClick(item)" class="pointer" :class="[item.children ? (item.flag ? 'el-icon-folder-opened': 'el-icon-folder') : 'el-icon-tickets']"></i><span  style="margin-left:12px">{{item.label}}</span>
       <template>
             <treeItem v-if="item.children && item.flag" :arr="item.children" class="subItem"/>
       </template>

    </div>
  </div>
</template>

<script>
export default {
  name: "treeItem",
  props:{
      arr:{
        type:Array,
        default:()=>{
            return []
        }
      }
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {},
  beforeDestroy() {},
  methods: {
    tagClick(item){
      this.$set(item,'flag',!item.flag)
    }
  },
};
</script>
<style>
.tree-div{
  line-height: 30px;
}
span{
  cursor: pointer;
}
.pointer{
  cursor: pointer;
}
span + .subItem{
  padding-left: 15px;
}
</style>

treeContainer/vueClickOutSize.js
const clickOutside = {
    // 初始化指令
    bind(el, binding, vnode) {
        console.log(vnode)
      function clickHandler(e) {
        // 这里判断点击的元素是否是本身,是本身,则返回
        if (el.contains(e.target)) {
          return false;
        }
        console.log(binding)
        // 判断指令中是否绑定了函数
        if (binding.expression) {
          // 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
          binding.value(e);
        }
      }
      // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
      el.__vueClickOutside__ = clickHandler;
      document.addEventListener("click", clickHandler);
    },
    unbind(el, binding) {
        console.log(binding)
      // 解除事件监听
      document.removeEventListener("click", el.__vueClickOutside__);
      delete el.__vueClickOutside__; // 删除属性
    }
  };
  export default clickOutside

views/b.vue

<template>
  <div style="width:500px" v-clickOutside="hideTree">
      <div style="height:30px;border:1px solid #ccc;" @click="toggerShow">
         {{select}}
      </div>
      <treeContainer :show="show"/>
  </div>
</template>

<script>
import treeContainer from './treeContainer'
import clickOutside from "./treeContainer/vueClickOutSize";
export default {
name:'',
components: {treeContainer},
data(){
return {
    select:null,
    show:false
}
},
  directives: { clickOutside },
computed: {},
watch:{},
created(){},
mounted(){},
beforeDestroy() {},
methods: {
    hideTree(){
       this.show=false
    },
    toggerShow(){
      //  this.show=!this.show;
      this.show=true;
    }
}
}
</script>
<style>
</style>