最近在做移动端的项目,由于没有找见移动端树形组件,所以封装了一个。包含加载所有数据的功能以及懒加载功能。
以下是目录结构
以下是完成后的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>