vue树形组件封装(移动端)

最近在做移动端的项目,由于没有找见移动端树形组件,所以封装了一个。包含加载所有数据的功能以及懒加载功能。

以下是目录结构

以下是完成后的ui  点击左侧切换“展开”、“收起”   点击右侧其他操作

然后直接上代码 

以下是懒加载的例子,一次性全部加载的就不放了

组件说明:

* 组件说明:树形组件
  itemActive:是否需要高亮显示  Boolean
  data:树形组件的数据 Array类型
  props:组件属性  Object {id:'',children:'',name:''}
  lazy:是否开启懒加载 Boolean 
  @load:懒加载回调方法 Funciton  返回当前点击项的所有数据
  @click:点击事件方法 Funciton 返回当前点击项的所有数据
  clickableConditions: 可以点击的条件 Object
    {
        //所有的列表是否可以点击
        all: {
          enable: true
        },

      //部分的列表可以点击 
      //以下条件说明【 orgType = 1或orgType = 2时可以点击】
      part: {
        prop: "orgType", 
        value: [1,2]
      }
    },

WarningMessage.vue


/*
 * @Autor: Mr Lu
 * @Version: 1.0
 * @Date: 2019-12-03
 * @LastEditors: OBKoro1
 * @LastEditTime: 2019-12-03
 * @Description:  预警信息
 * 组件说明:树形组件
  itemActive:是否需要高亮显示  Boolean
  data:树形组件的数据 Array类型
  props:组件属性  Object {id:'',children:'',name:''}
  lazy:是否开启懒加载 Boolean 
  @load:懒加载回调方法 Funciton  返回当前点击项的所有数据
  @click:点击事件方法 Funciton 返回当前点击项的所有数据
  clickableConditions: 可以点击的条件 Object
    {
        //所有的列表是否可以点击
        all: {
          enable: true
        },

      //部分的列表可以点击 
      //以下条件说明【 orgType = 1或orgType = 2时可以点击】
      part: {
        prop: "orgType", 
        value: [1,2]
      }
    },
 */
<template>
  <div id="WarningMessage" class="p-box">
    <div class="p-return">
      <mu-appbar color="primary" class="lan-header">
        <mu-button icon slot="left" v-close>
          <i class="iconfont angle-left iconangle-left"></i>
        </mu-button>预警信息
      </mu-appbar>
    </div>
    <div class="p-content">
      <wallModule name="预警总计(个)" :total="totalCount" :bgImage="wallBgImage" />
      <div class="t-box">
        <div class="t-title">质量检查</div>
        <div class="t-content">
          <div class="t-header">
            <span class="name">超期未整改</span>
            <span class="total">{{totalCount}}</span>
          </div>
          <div class="t-body">
            <mTree
              v-show="tree.data.length"
              :data="tree.data"
              :props="tree.defaultProps"
              :lazy="true"
              @load="loadData"
              :clickableConditions="tree.clickableConditions"
              @click="skipPage"
              :showTotal="true"
            ></mTree>
            <NoData v-if="!tree.data.length&&!tree.loading" />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import WeatherModule from "@/weather/WeatherModule";
import wallModule from "@/wall/wallModule";
import mTree from "@/tree/mTree";
export default {
  name: "WarningMessage",
  components: {
    WeatherModule,
    mTree,
    wallModule
  },
  data() {
    return {
      totalCount: 0,
      overflowDateCount: 0,
      wallBgImage: require("#/assets/img/wall/wall.png"),
      tree: {
        loading: false,
        defaultProps: {
          id: "orgId",
          name: "orgName",
          children: "children"
        },
        clickableConditions: {
          all: {
            //所有的是否可以点击
            enable: false
          },
          part: {
            //部分的可以点击的条件
            prop: "orgType",
            value: [1]
          }
        },
        data: []
      }
    };
  },
  computed: {},
  created() {
    this.initPage();
  },
  mounted() {},
  watch: {},
  methods: {
    skipPage(treeRowData) {},
    initPage() {
      this.getTotal();
      let params = {
        orgId: this.$A.GS("orgInfo")["orgId"],
        type: this.$A.GS("orgInfo")["type"],
        alarmStatus: 2
      };

      this.getData(params).then(response => {
        if (response.code == 200) {
          this.tree.data =
            response.body && response.body.length ? response.body : [];
        } else {
          this.tree.data = [];
        }
      });
    },
    getTotal() {
      let params = {
        orgId: this.$A.GS("orgInfo")["orgId"],
        type: this.$A.GS("orgInfo")["type"]
      };
      this.$A.Go("get", "/safety/qualityTotalAlarm/statistic", params).then(
        response => {
          if (response.code == 200) {
            this.totalCount = response.body.totalAlarmCount;
          }
        },
        err => {
          console.log(err);
        }
      );
    },
    loadData(treeRowData) {
      let params = {
        orgId: treeRowData.orgId,
        type: treeRowData.orgType,
        alarmStatus: 2
      };
      this.getData(params).then(response => {
        if (response.code == 200) {
          if (response.body && response.body.length) {
            var newArray = response.body;
            newArray.forEach(element => {
              element.level = treeRowData.level + 1;
            });
            this.$set(treeRowData, "children", newArray);
            this.$set(treeRowData, "expanded", true);
          } else {
            this.$set(treeRowData, "isLeaf", true);
          }
        } else {
        }
      });
    },
    getData(params) {
      this.$openLoading();
      this.tree.loading = true;
      let p = new Promise((resolve, reject) => {
        this.$A.Go("post", "/safety/importantRectLayer/statistic", params).then(
          response => {
            this.$closeLoading();
            this.tree.loading = false;
            resolve(response);
          },
          err => {
            this.$closeLoading();
            this.tree.loading = false;
            console.log(err);
          }
        );
      });
      return p;
    }
  },
  destroyed() {}
};
</script>

<style lang="scss">
#WarningMessage {
    //树形组件样式
.treeList {
  border-top: 0.01rem solid $borderColor;



  [role="tree-content"] {
    background-color: #fff;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    color: inherit;
    height: 0.55rem;
    display: block;
    overflow: hidden;
    position: relative;
    text-decoration: none;
    display: flex;
    align-items: center;
    border-bottom: 0.01rem solid #e0e0e0;


    [role="title"].active {
      >.tree-content-icon {
        background: $themeColoro;
      }
    }

    [role="title"].active {
      >.tree-content-label {
        background: $themeColoro;
      }
    }

    [role="title"] {
      height: 100%;
      width: 100%;
      display: flex;
      align-items: center;

      .tree-content-icon {
        display: flex;
        align-items: center;
        justify-content: center;
        color: $themeColor;
        height: 100%;
        padding: 0 0.14rem;
        border-right: 0.01rem solid rgba(224, 224, 224, 1);

        .iconfont {
          margin-left: 0.03rem;
          font-size: 0.08rem;
        }
      }

      .tree-content-icon.disabled {
        color: $themeColoro;
        cursor: not-allowed;
      }

      .tree-content-label {
        flex: 1;
        height: 100%;
        display: flex;
        min-width: 0;
        align-items: center;
        justify-content: space-between;
        padding: 0 0.14rem;

        .name {
          flex: 1;
          white-space: nowrap;
          text-overflow: hidden;
          text-overflow: ellipsis;
          overflow: hidden;
        }

        .icon {
          display: flex;
          height: 100%;
          align-items: center;
          margin-left: 0.05rem;

          .iconfont {
            margin-left: 0.15rem;
            color: #dbdbdb;
          }
        }
      }
    }
  }

  [role="tree-children"] {
    background: #fff;
  }

  [role="tree-children"]::before {
    content: "";
  }

  .iconfont.iconhidden {
    visibility: hidden;
  }

  .iconfont.iconnone {
    display: none;
  }
}

}
</style>

mTree.vue

<template>
  <div role="tree" class="treeList">
    <treeItem :node="node" v-bind="$attrs" v-on="$listeners" ref="tree" />
  </div>
</template>
<script>
import treeItem from "./tree-item";
export default {
  name: "mTree",
  components: {
    treeItem
  },
  props: {
    data: {
      type: Array,
      default: []
    }
  },
  data() {
    return {
      node: []
    };
  },
  computed: {},
  created() {},
  mounted() {},
  watch: {
    data: {
      handler(val, oldVal) {
        this.node = this.dealTreeData(val, 1);
      },
      deep: true
    }
  },
  methods: {
    dealTreeData(tree, level) {
      let arr = [];
      tree.length &&
        tree.forEach((item, index) => {
          let obj = {};
          obj = this.deepCopy(item);
          obj.level = level;
          // obj.expanded = true;
          if (item.children && item.children.length) {
            obj.children = this.dealTreeData(item.children, level + 1);
          } else {
            if (!this.$attrs.lazy) {
              obj.isLeaf = true;
            } else {
              obj.isLeaf = false;
            }
          }
          arr.push(obj);
        });
      return arr;
    },
    deepCopy(obj) {
      var result = Array.isArray(obj) ? [] : {};
      for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
          if (typeof obj[key] === "object" && obj[key] !== null) {
            result[key] = this.deepCopy(obj[key]); //递归复制
          } else {
            result[key] = obj[key];
          }
        }
      }
      return result;
    },
    postData(data) {
      // this.$emit("load", data);
    }
  },
  destroyed() {}
};
</script>

<style lang="scss">
</style>

treeItem.vue

<template>
  <div class>
    <div role="tree-item" class="treeItem" v-for="(item) in node" :key="item[$attrs.props.id]">
      <div role="tree-content">
        <div
          role="title"
          :class="item[$attrs.props.id]==currentId?'active':''"
          :style="{'background':`rgba(${255-colorLevel*(item.level-1)},${255-colorLevel*(item.level-1)},${255-colorLevel*(item.level-1)})`}"
        >
          <!-- 展开或收起 -->
          <!-- :style="{'visibility':item.isLeaf&&!item[$attrs.props.children]?'hidden':'visible'}" -->
          <span
            class="tree-content-icon"
            @click.stop="item.isLeaf&&!item[$attrs.props.children]?'':treeToggleClick(item)"
            :class="item.isLeaf&&!item[$attrs.props.children]?'disabled':''"
          >
            {{item.isLeaf&&!item[$attrs.props.children]?"展开":item['expanded']?'收起':"展开"}}
            <!-- <span
              class="iconfont"
              :class="item['expanded']?'iconangle-down':'iconangle-right'"
            ></span>-->
          </span>
          <!-- 标题 -->
          <!-- !enabledConfigClick?'':clickType == 'all'?treeConfigClick(item):(item[$attrs.clickableConditions.part.prop]==$attrs.clickableConditions.part.value?treeConfigClick(item):false) -->
          <label
            class="tree-content-label"
            @click.stop="!enabledConfigClick?'':clickType == 'all'?treeConfigClick(item):($attrs.clickableConditions.part.value.includes(item[$attrs.clickableConditions.part.prop])?treeConfigClick(item):false)"
          >
            <span class="name">{{ item[$attrs.props.name]}}</span>
            <span class="icon">
              <span v-if="$attrs.showTotal">{{item.projectCount}}</span>
              <i
                class="iconfont iconangle-right"
                v-if="!enabledConfigClick?false:clickType == 'all'?true:($attrs.clickableConditions.part.value.includes(item[$attrs.clickableConditions.part.prop])?true:false)"
              ></i>
              <i
                class="iconfont iconangle-right"
                v-else
                :class="$attrs.showTotal?'iconhidden':'iconnone'"
              ></i>
            </span>
          </label>
        </div>
      </div>
      <VerticalToggle>
        <!-- :style="{ 'padding-left':  .14 + 'rem' }" -->
        <div role="tree-children" v-if="item['expanded']">
          <treeItem :node="item[$attrs.props.children]" v-bind="$attrs" v-on="$listeners" />
        </div>
      </VerticalToggle>
    </div>
  </div>
</template>
<script>
import VerticalToggle from "./js/VerticalToggle";
export default {
  name: "treeItem",
  components: {
    VerticalToggle
  },
  props: {
    node: {}
  },
  data() {
    return {
      tree: null,
      data: [],
      currentId: "",
      enabledConfigClick: false,
      clickType: "all",
      colorLevel: 5
    };
  },
  computed: {},
  created() {
    if (this.$attrs.clickableConditions) {
      if (
        this.$attrs.clickableConditions.all &&
        this.$attrs.clickableConditions.all.enable
      ) {
        this.enabledConfigClick = true;
        this.clickType = "all";
      } else if (this.$attrs.clickableConditions.part) {
        this.enabledConfigClick = true;
        this.clickType = "part";
      } else {
        this.enabledConfigClick = false;
      }
    } else {
      this.enabledConfigClick = false;
    }
  },
  mounted() {},
  watch: {},
  methods: {
    /**点击展开收起的事件 */
    treeToggleClick(item) {
      // this.currentId = item[this.$attrs.props.id];
      if (this.$attrs.lazy) {
        if (item.isLeaf || item[this.$attrs.props.children]) {
          this.$set(item, "expanded", !item.expanded);
        } else {
          if (item.orgType && item.orgType == 1) {
            this.$set(item, "expanded", !item.expanded);
            this.$set(item, "isLeaf", true);
          } else {
            this.$parent.$emit("load", item);
          }
          //this.$parent.$emit("load", item);
        }
      } else {
        this.$set(item, "expanded", !item.expanded);
      }
    },
    /**点击右侧文字的事件 */
    treeConfigClick(item) {
      this.$attrs.itemActive
        ? (this.currentId = item[this.$attrs.props.id])
        : "";
      this.$parent.$emit("click", item);
    },
    postData(item) {
      debugger;
    }
  },
  destroyed() {}
};
</script>

<style lang="scss">
/*树形组件*/
</style>

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值