主要涉及组件自调用实现渲染,js实现递归数组的增删
老规矩,还是先看效果
- 这块主要分了几个组件来实现:连接线、卡片、连接线+卡片(含自身嵌套,这个组件就是调用了前面连接线和卡片的组件,大家可以合在一起)、父组件调用,以及通过eventBus实现增加、删除操作
连接线这块比较简单,不需要的跳过
<!-- ConnectLine.vue 连接关系线 !-->
<template>
<div class="line-wrap">
<div class="line line-top"></div>
<!-- 需要的可以在这里做一些关系选择,涉及业务,隐去了 -->
<div class="line line-bottom"></div>
</div>
</template>
<script>
export default {
name: "ConnectLine",
data() {
return {};
},
};
</script>
<style lang='scss' scoped>
.line-wrap {
display: flex;
flex-direction: column;
align-items: center;
.line {
flex: 1;
width: 30px;
border-left: 1px solid #c0c4cc;
&.line-bottom {
margin-bottom: 20px;
}
}
}
</style>
下面看一下卡片这部分,涉及业务内容,我用id代替了卡片内容(此处id也作为唯一key值在组件间传递)
<!-- RuleCard.vue 卡片组件 !-->
<template>
<div class="card-wrap">
<el-card>{{item.id}}</el-card>
<div class="operate-wrap">
<i class="icon el-icon-delete" @click="editCard('delete')"></i>
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link"><i class="icon el-icon-plus"></i></span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="addEqual">同级</el-dropdown-item>
<el-dropdown-item command="addChild">子级</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
<script>
export default {
name: "RuleCard",
props: {
item: Object,
},
data() {
return {};
},
methods: {
//删除卡片
editCard() {
this.$emit("editCard", "delete", this.item.id);
},
//增加卡片
handleCommand(command) {
this.$emit("editCard", command, this.item.id);
},
},
};
</script>
<style lang='scss' scoped>
.card-wrap {
width: 370px;
display: flex;
align-items: center;
.el-card {
flex: 1;
margin-bottom: 10px;
/deep/.el-card__body {
padding: 10px;
}
}
.operate-wrap {
width: 70px;
.icon {
font-size: 16px;
padding: 5px;
margin-left: 5px;
cursor: pointer;
}
}
}
</style>
下面就是连接线+卡片组件,前面准备工作都做好了,这个组件就比较简单(嵌套自身通过本身组件的name实现)
<!-- RulePanel.vue 面板组件 !-->
<template>
<div class="rule-wrap">
<!-- 设置连接线:数组长度大于1,或含有子级 -->
<connect-line v-if="list.length>1||(list[0]&&list[0].children&&list[0].children.length>0)"></connect-line>
<div>
<div v-for="(li,i) in list" :key="i">
<!-- 卡片 -->
<rule-card :item="li" @editCard="editCard"></rule-card>
<!-- 嵌套自身 -->
<RulePanel :list="li.children"></RulePanel>
</div>
</div>
</div>
</template>
<script>
import ConnectLine from "./ConnectLine";
import RuleCard from "./RuleCard";
import Bus from "@/plugins/eventBus";
export default {
name: "RulePanel",
components: { ConnectLine, RuleCard },
props: {
list: {
type: Array,
default: () => [],
},
},
data() {
return {};
},
methods: {
//触发卡片删除、添加
editCard(type, id) {
Bus.$emit("editCard", type, id);
},
},
};
</script>
<style lang='scss' scoped>
.rule-wrap {
display: flex;
overflow-x: auto;
}
</style>
最后就是父组件调用啦
<!-- ElementRule.vue 父组件 !-->
<template>
<div class="custom-panel-tab__content">
<rule-panel :list="list"></rule-panel>
</div>
</template>
<script>
import RulePanel from "./components/RulePanel";
import Bus from "@/plugins/eventBus";
export default {
name: "ElementRule",
components: { RulePanel },
data() {
return {
radio: "1",
list: [
{ id: "1", children: [] },
{
id: "2",
children: [
{
id: "2-1",
children: [{id: "2-1-1", children: [{ id: "2-1-1-1" }, { id: "2-1-1-2" }]}, { id: "2-1-2" }],
},
],
},
],
};
},
created() {
//监听卡片删除、添加
Bus.$on("editCard", (type, id) => {
if (type === "delete") {
this.deleteCard(this.list, id);
} else {
this.addCard(this.list, type, id);
}
});
},
methods: {
//删除卡片
deleteCard(arr, id) {
if (this.list.length === 1 && this.list[0].id === id) {
this.$message("至少保留一个卡片");
return;
}
arr.forEach((item, index) => {
if (item.id === id) {
arr.splice(index, 1);
return;
}
if (item.children && item.children.length > 0) {
return this.deleteCard(item.children, id);
}
});
},
//增加卡片
addCard(arr, type, id) {
arr.forEach((item) => {
if (item.id === id) {
//同级
if (type === "addEqual") {
let indArr = id.split("-");
let num = arr.length + 1;
indArr[indArr.length - 1] = num;
arr.push({ id: indArr.join("-") });
}
//子级
if (type === "addChild") {
let newId = `${id}-${
item.children ? item.children.length + 1 : "1"
}`;
item.children
? item.children.push({ id: newId })
: (item.children = [{ id: newId }]);
}
this.list = JSON.parse(JSON.stringify(this.list)); //强制页面刷新
return;
}
if (item.children && item.children.length > 0) {
return this.addCard(item.children, type, id);
}
});
},
},
};
</script>
<style scoped lang="scss">
.node-type {
margin-bottom: 20px;
}
</style>
这样就好啦~~~
- PS: 附一下eventBus的代码吧
import Vue from 'vue'
export default new Vue()