前言:
公司的业务要求,做一个动态添加子属性或统计属性的一个业务,网上没有找到特别合适的,就自己参考了一些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>