前言:
公司的业务要求,做一个动态添加子属性或统计属性的一个业务,网上没有找到特别合适的,就自己参考了一些demo做了一个小组件,希望大佬们可以多多留言指点!
1、做法
要实现动态的渲染我们拿到的不知道有几层的菜单数据,有两种解决方案:
- 操作 dom 去一层一层 添加子菜单(vue 不推荐操作 dom,所以不推荐此方案)
- 将我们的菜单封装到组件中,通过递归组件实现菜单渲染
具体效果如下图:
2、数据
options: [
{
id: "473d342d-20d9-459c-ab56-bac4abceb7ad",
tagId: 1,
joinMethod: "and",
byProperty: "CompanyName",
operate: "=",
targetValue: "尖沙咀新港中心",
parentTagRuleId: 23,
child: [
{
id: "fd749e7b-7708-41e1-be97-0f5dce627e94",
tagId: 1,
joinMethod: "and",
byProperty: "CompanyName",
operate: "=",
targetValue: "尖沙咀新港中心",
parentTagRuleId: "473d342d-20d9-459c-ab56-bac4abceb7ad",
child: [
{
id: "fd749e7b-7708-41e1-be97-0f5dce627e94",
tagId: 1,
joinMethod: "and",
byProperty: "CompanyName",
operate: "=",
targetValue: "尖沙咀新港中心",
parentTagRuleId: "473d342d-20d9-459c-ab56-bac4abceb7ad",
child: null,
},
],
},
],
},
{
id: "473d342d-20d9-459c-ab56-bac4abceb7ad",
tagId: 1,
joinMethod: "and",
byProperty: "CompanyName",
operate: "=",
targetValue: "尖沙咀新港中心",
parentTagRuleId: 23,
child: [
{
id: "fd749e7b-7708-41e1-be97-0f5dce627e94",
tagId: 1,
joinMethod: "and",
byProperty: "CompanyName",
operate: "=",
targetValue: "尖沙咀新港中心",
parentTagRuleId: "473d342d-20d9-459c-ab56-bac4abceb7ad",
child: null,
},
],
},
],
3、组件
父组件:
<template>
<div style="margin: 0 100px">
<tag-rule :options="options" :isFirst="isFirst" ref="andOr"></tag-rule>
</div>
</template>
<script>
import tagRule from "./components/tagRule.vue";
export default {
components: { tagRule },
data() {
return {
isFirst: true,
options: [
{
id: "473d342d-20d9-459c-ab56-bac4abceb7ad",
tagId: 1,
joinMethod: "and",
byProperty: "CompanyName",
operate: "=",
targetValue: "尖沙咀新港中心",
parentTagRuleId: 23,
child: [
{
id: "fd749e7b-7708-41e1-be97-0f5dce627e94",
tagId: 1,
joinMethod: "and",
byProperty: "CompanyName",
operate: "=",
targetValue: "尖沙咀新港中心",
parentTagRuleId: "473d342d-20d9-459c-ab56-bac4abceb7ad",
child: [
{
id: "fd749e7b-7708-41e1-be97-0f5dce627e94",
tagId: 1,
joinMethod: "and",
byProperty: "CompanyName",
operate: "=",
targetValue: "尖沙咀新港中心",
parentTagRuleId: "473d342d-20d9-459c-ab56-bac4abceb7ad",
child: null,
},
],
},
],
},
{
id: "473d342d-20d9-459c-ab56-bac4abceb7ad",
tagId: 1,
joinMethod: "and",
byProperty: "CompanyName",
operate: "=",
targetValue: "尖沙咀新港中心",
parentTagRuleId: 23,
child: [
{
id: "fd749e7b-7708-41e1-be97-0f5dce627e94",
tagId: 1,
joinMethod: "and",
byProperty: "CompanyName",
operate: "=",
targetValue: "尖沙咀新港中心",
parentTagRuleId: "473d342d-20d9-459c-ab56-bac4abceb7ad",
child: null,
},
],
},
],
};
},
};
</script>
<style>
</style>
子组件:
子组件这里的重点就是组件的name,用来递归调用
<template>
<div>
<div
v-if="options.length == 0 && parentTagRuleId == 23"
class="and-or-template col-xs-12"
:class="isFirst ? 'and-or-first' : ''"
>
<div class="btn-group col-xs-7 btn-and-or">
<button @click.prevent="addLine" class="btn btn-xs btn-purple">
{{ "+madd" }}
</button>
<button @click.prevent="addTopGroup()" class="btn btn-xs btn-purple">
{{ "+(mGroup)" }}
</button>
</div>
</div>
<div
v-for="(item, index) in options"
:key="index"
class="and-or-template col-xs-12"
:class="isFirst ? 'and-or-first' : ''"
>
<!-- left:and-or center:data right:add -->
<div class="form-group col-sx-12">
<div class="btn-group col-xs-7 btn-and-or">
<button @click.prevent="addLine" class="btn btn-xs btn-purple">
{{ "+add" }}
</button>
<button
@click.prevent="addGroup(index)"
class="btn btn-xs btn-purple"
>
{{ "+(Group)" }}
</button>
<button
style="margin-right: 10px"
@click.prevent="deleteGroup(item, index)"
class="btn btn-xs btn-purple"
>
×
<i class="fa fa-fw fa-close"></i>
</button>
</div>
<el-form
:model="item"
:rules="rules"
ref="ruleForm"
size="small"
label-position="top"
class="full"
>
<el-row style="margin-left: 5px" :gutter="20">
<el-col :span="3">
<el-select v-model="item.joinMethod">
<el-option value="and" :label="'and'"></el-option>
<el-option value="or" :label="'or'"></el-option>
</el-select>
</el-col>
<el-col :span="6">
<el-form-item prop="byProperty">
<el-select v-model="item.byProperty">
<el-option
v-for="(item, index) in byProperty"
:key="index"
:label="item"
:value="item"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item prop="operate">
<el-select v-model="item.operate">
<el-option
v-for="(item, index) in operate"
:key="index"
:label="item"
:value="item"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item prop="targetValue">
<el-input v-model="item.targetValue"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<!-- 递归嵌套 -->
<div v-if="item.child">
<tag-rule
class="and-or-offset col-xs-11"
:options="item.child"
></tag-rule>
</div>
</div>
</div>
</template>
<script>
// import { getTagViewProperties } from "@/api/uxretail/vip/vipTag.js";
export default {
name: "tagRule",
props: {
options: {
type: Array,
require: true,
},
isFirst: {
type: Boolean,
default: false,
},
tagId: {},
},
data() {
return {
parentTagRuleId: "",
byProperty: [],
operate: [">", "=", "<"],
rules: {
byProperty: [{ required: true, trigger: ["blur", "change"] }],
operate: [{ required: true, trigger: ["blur", "change"] }],
targetValue: [{ required: true, trigger: ["blur", "change"] }],
},
};
},
methods: {
addLine() {
this.options.push({
joinMethod: "",
byProperty: "",
operate: "",
targetValue: "",
tagId: this.tagId,
child: null,
});
},
addGroupItem(data) {
if (data.child === null || data.child.length === 0) {
data.child = [
{
joinMethod: "",
byProperty: "",
operate: "",
targetValue: "",
tagId: this.tagId,
child: null,
},
];
} else if (data.child.length > 0) {
data.child.push({
joinMethod: "",
byProperty: "",
operate: "",
targetValue: "",
tagId: this.tagId,
child: null,
});
}
// else {
// this.addGroupItem(data);
// }
},
addGroup(index) {
this.addGroupItem(this.options[index]);
// console.log("this.$refs.ruleForm", this.$refs.ruleForm.validate());
},
addTopGroup() {
this.options.push({
joinMethod: "",
byProperty: "",
operate: "",
targetValue: "",
child: null,
});
},
deleteGroup(item, index) {
if (item.parentTagRuleId == 23) {
this.parentTagRuleId = item.parentTagRuleId;
}
this.options.splice(index, 1);
console.log("this.$refs.options", this.options);
},
// 校验数据
// validateForm() {
// let flag = null;
// this.$refs["ruleForm"].validate((valid) => {
// if (valid) {
// flag = true;
// } else {
// flag = false;
// }
// });
// return flag;
// },
},
created() {
// getTagViewProperties().then((result) => {
// this.byProperty = result;
// });
},
};
</script>
<style lang="scss">
$more-color: rgb(24, 144, 255);
.and-or-template {
padding: 8px;
position: relative;
border-radius: 3px;
border: 1px solid $more-color;
border-top: 3px solid #d2d6de;
margin-bottom: 15px;
/* width: 100%; */
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
border-top-color: $more-color;
background-color: rgba(255, 255, 255, 0.9);
}
.and-or-template:before,
.and-or-template:after {
content: "";
position: absolute;
left: -23px;
width: 22px;
height: calc(50% + 20px);
border-color: #c0c5e2;
border-style: solid;
}
.and-or-template:before {
top: -18px;
border-width: 0 0 2px 2px;
}
.and-or-template:after {
top: 50%;
border-width: 0 0 0 2px;
}
.and-or-first:before,
.and-or-first:after,
.and-or-template:last-child:after {
border: none;
}
.col-xs-12 {
width: 100%;
}
.form-group {
margin-bottom: 15px;
padding: 0;
}
.btn {
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
cursor: pointer;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-image: none;
border: 1px solid transparent;
border-radius: 4px;
padding: 1px 5px;
font-size: 12px;
line-height: 1.5;
border-radius: 3px;
}
.and-or-offset {
margin-left: 30px;
}
.col-xs-11 {
position: relative;
width: 91.66666667%;
min-height: 1px;
padding-left: 15px;
}
.btn-purple-outline {
color: #6d77b8;
background-color: transparent;
background-image: none;
border-color: #6d77b8;
}
.btn-purple-outline:hover {
color: #fff;
background-color: $more-color;
background-image: none;
border-color: $more-color;
}
.btn-radius {
padding: 0;
width: 22px;
height: 22px;
border-radius: 11px;
}
.btn-xs {
padding: 1px 5px;
font-size: 12px;
line-height: 1.5;
border-radius: 3px;
}
.btn-purple,
.btn-purple-outline:hover {
color: #fff;
background-color: $more-color;
border-color: $more-color;
}
.btn-group {
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.col-xs-7,
.col-xs-11 {
position: relative;
min-height: 1px;
padding-right: 5px;
padding-left: 15px;
padding-bottom: 10px;
}
.col-xs-7 {
width: 100%;
}
.btn-and-or button {
margin-left: 4px;
margin: 0px 0px 5px 7px;
}
</style>