封装一个树形条件配置组件

82 阅读3分钟

子组件

<template>
  <div class="relation">
    <div class="compose-trunk">
      <div v-if="!isEmptyObj(currentNode) && currentNode.conditions.length > 0">
        <div
          class="compose-leaf rule-leaf"
          v-for="(subItem, subIndex) in currentNode.conditions"
          :key="subIndex"
        >
          <div class="item leaf-item leaf-edit">
            <el-select
              v-model="subItem.dimension"
              placeholder="请选择维度"
              size="mini"
            >
              <el-option
                v-for="(dItem, DIndex) in dimensionList"
                :key="DIndex"
                :value="dItem.columnName"
                :label="dItem.columnName"
              ></el-option>
            </el-select>
            <i
              class="el-icon-circle-close close"
              @click="handleDeleteDimension(subIndex)"
            ></i>
          </div>
          <div class="rule-text">
            <span
              v-if="
                subItem.dimensionSubGroup.conditionDetails[0].value == '' &&
                subItem.dimensionSubGroup.conditionDetails[0].subOperator == ''
              "
              >该维度暂未设置条件</span
            >
            <i
              class="cond"
              v-if="
                subItem.dimensionSubGroup.conditionDetails[0].value == '' &&
                subItem.dimensionSubGroup.conditionDetails[0].subOperator == ''
              "
              @click="handleSetConfig(subItem, subIndex)"
              >设置条件</i
            >
            <i class="cond" v-else @click="handleSetConfig(subItem, subIndex)"
              >修改条件</i
            >
          </div>
        </div>
      </div>
      <div
        v-if="
          currentNode.subGroups &&
          currentNode.subGroups.length > 0 &&
          maxDepth > 0
        "
      >
        <!--递归-->
        <tree-node
          v-for="(nodeItem, nodeIndex) in currentNode.subGroups"
          :key="nodeIndex"
          class="tree-node"
          :nodeData="nodeItem"
          :maxDepth="maxDepth - 1"
          :dimensionList="dimensionList"
        ></tree-node>
      </div>

      <div class="compose-add" v-if="!isEmptyObj(currentNode)">
        <div class="item add-item">
          <el-dropdown>
            <div class="add-btn"><i class="el-icon-plus"></i>添加</div>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item @click.native="handleAddFather(currentNode)"
                >添加父层关系</el-dropdown-item
              >
              <el-dropdown-item @click.native="handleAddChild(currentNode)"
                >添加子层关系</el-dropdown-item
              >
              <el-dropdown-item @click.native="handleAddBrother(currentNode)"
                >添加维度条件</el-dropdown-item
              >
            </el-dropdown-menu>
          </el-dropdown>
        </div>
      </div>
      <div class="compose-node" v-if="!isEmptyObj(currentNode)">
        <div class="item node-item">
          <el-select
            class="node"
            v-model="currentNode.logicalOperator"
            placeholder="请选择"
            size="mini"
          >
            <el-option label="且" value="AND"></el-option>
            <el-option label="或" value="OR"></el-option>
          </el-select>
          <i
            class="el-icon-circle-close close"
            @click="handleDeleteNode(currentNode)"
          ></i>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import Bus from '@/assets/js/eventBus'
export default {
  name: 'TreeNode', // 与组件名称保持一致
  props: {
    nodeData: {
      type: Object,
      default: () => {
        return {}
      },
    },
    dimensionList: {
      type: Array,
    },
    maxDepth: {
      type: Number,
      default: 4, // 默认递归次数为4次
    },
  },
  data() {
    return {
      value: 'and',
      currentNode: this.nodeData,
    }
  },
  methods: {
    // 对象是否为空
    isEmptyObj(obj) {
      return Object.keys(obj).length === 0
    },
    // 添加父级节点
    handleAddFather(data) {
      let newData = {
        logicalOperator: 'AND',
        conditions: [],
        subGroups: [
          {
            logicalOperator: data.logicalOperator,
            id: Date.now() + 1,
            conditions: [].concat(data.conditions),
          },
        ],
      }
      this.currentNode = newData
    },
    // 添加子层关系
    handleAddChild(data) {
      console.log('zi', data)
      if (data.subGroups == undefined) {
        let obj = [
          {
            logicalOperator: 'AND',
            conditions: [
              {
                dimension: '',
                dimensionSubGroup: {
                  logicalOperator: '',
                  conditionDetails: [
                    {
                      dimension: '',
                      operator: '',
                      value: '',
                      subOperator: null,
                    },
                  ],
                },
              },
            ],
          },
        ]
        this.$set(data, 'subGroups', obj)
      } else {
        data.subGroups.push({
          logicalOperator: 'AND',
          conditions: [
            {
              dimension: '',
              dimensionSubGroup: {
                logicalOperator: '',
                conditionDetails: [
                  {
                    dimension: '',
                    operator: '',
                    value: '',
                    subOperator: null,
                  },
                ],
              },
            },
          ],
        })
      }
    },
    // 添加维度
    handleAddBrother(data) {
      console.log('data---', data)
      let obj = {
        dimension: '',
        dimensionSubGroup: {
          logicalOperator: '',
          conditionDetails: [
            {
              dimension: '',
              operator: '',
              value: '',
              subOperator: null,
            },
          ],
        },
      }
      data.conditions.push(obj)
    },
    // 删除维度
    handleDeleteDimension(index) {
      console.log('this.nodeData----', this.nodeData)
      this.currentNode.conditions.splice(index, 1)
      Bus.$emit('getNodeData', this.nodeData)
    },
    // 删除节点
    handleDeleteNode(data) {
      console.log('this.nodeData----', this.nodeData)
      this.currentNode = {}
      Bus.$emit('getNodeData', this.nodeData)
    },
  },
}
</script>
<style lang="scss" scoped>
.compose-trunk {
  margin-left: 57px;
  overflow: hidden;
  position: relative;
  vertical-align: top;
}
.compose-leaf,
.compose-leaf .leaf-item {
  position: relative;
}
.compose-leaf {
  margin-left: 94px;
  padding-right: 80px;
  padding-top: 12px;
}
.compose-leaf .leaf-edit {
  height: 28px;
  line-height: 27px;
  width: 140px;
}
.compose-leaf:first-child .leaf-item:before {
  @include theme_background_color('mainBackColor');
  content: '';
  height: 30px;
  left: -37px;
  position: absolute;
  top: -17px;
  width: 1px;
  z-index: 1;
}
.compose-leaf:first-child .leaf-item:after {
  border-top: 1px solid #ccc;
  content: '';
  left: -37px;
  position: absolute;
  top: 13px;
  width: 37px;
}

.compose-leaf + .compose-leaf .leaf-item:after,
.compose-trunk + .compose-leaf .leaf-item:after {
  border: 1px solid #ccc;
  border-width: 0 0 1px 1px;
  content: '';
  height: 10px;
  left: -37px;
  position: absolute;
  top: 4px;
  width: 37px;
}
/*添加节点*/
.compose-add,
.compose-add .add-item {
  position: relative;
}
.compose-add {
  margin-left: 94px;
  padding-right: 80px;
  padding-top: 12px;
}
.compose-leaf .compose-add .add-item:before,
.compose-trunk .compose-add .add-item:before {
  border-left: 1px solid #ccc;
  bottom: 19px;
  content: '';
  left: -37px;
  position: absolute;
  top: -10000px;
}
.compose-leaf .compose-add .add-item:after,
.compose-trunk .compose-add .add-item:after {
  border: 1px solid #ccc;
  border-width: 0 0 1px 1px;
  content: '';
  height: 10px;
  left: -37px;
  position: absolute;
  top: 4px;
  width: 37px;
}
.compose-add .add-item .add-btn {
  color: var(--main-color);
  cursor: pointer;
  height: 28px;
  line-height: 28px;
  padding: 0 6px;
  width: 100px;
}

/*条件节点*/
.compose-node {
  left: 0;
  margin-left: 34px;
  margin-top: -18px;
  padding-top: 12px;
  position: absolute;
  top: 50%;
}
.compose-node .item {
  position: relative;
}
.compose-node .close {
  @include theme_background_color('moduleBackColor');
  color: var(--main-color);
  display: none;
  position: absolute;
  right: -3px;
  top: 3px;
}
.compose-node .item:hover {
  .close {
    display: block;
  }
  ::v-deep {
    .el-input .el-input__inner:hover {
      border: 1px solid var(--main-color);
    }
  }
}
.compose-trunk .compose-trunk:first-child > .compose-node .node-item:before {
  @include theme_background_color('mainBackColor');
  content: '';
  height: 99999px;
  left: -35px;
  position: absolute;
  top: -99988px;
  width: 1px;
  z-index: 2;
}
.compose-trunk .compose-trunk:first-child > .compose-node .node-item:after {
  border-top: 1px solid #ccc;
  content: '';
  left: -37px;
  position: absolute;
  top: 11px;
  width: 37px;
}
.compose-leaf + .compose-trunk > .compose-node .node-item:after,
.compose-trunk + .compose-trunk > .compose-node .node-item:after {
  border: 1px solid #ccc;
  border-width: 0 0 1px 1px;
  content: '';
  height: 10px;
  left: -37px;
  position: absolute;
  top: 0;
  width: 37px;
}
.compose-leaf .rule-text {
  left: 150px;
  position: absolute;
  top: 5px;
}
.compose-leaf .rule-text span {
  display: inline-block;
  max-width: 300px;
  overflow: hidden;
  word-wrap: normal;
  text-overflow: ellipsis;
  vertical-align: middle;
  white-space: nowrap;
}
.compose-leaf .rule-text .cond {
  padding-left: 5px;
  position: static;
  vertical-align: middle;
  color: var(--main-color);
  cursor: pointer;
}
.compose-leaf .close {
  color: var(--main-color);
  display: none;
  position: absolute;
  right: -5px;
  top: -5px;
  @include theme_background_color('moduleBackColor');
}
.compose-leaf .item:hover {
  .close {
    display: block;
  }
  ::v-deep {
    .el-input .el-input__inner:hover {
      border: 1px solid var(--main-color);
    }
  }
}
.node {
  width: 47px;
  ::v-deep {
    .el-input .el-input__inner {
      border-radius: 60px;
      height: 24px;
      line-height: 24px;
      padding: 0 14px 0;
    }
    .el-input .el-input__inner:hover {
      border: 1px solid var(--main-color);
    }
    .el-input__suffix {
      width: 5px;
      top: -3px;
    }
    .el-input__icon {
      width: 5px;
    }
  }
}
</style>

父组件调用

<tree-node-all
    :nodeData="treeData"
    :dimensionList="dimensionList"
    :maxDepth="maxDepth"
></tree-node-all>

数据格式参考

treeData: {
    logicalOperator: 'AND',
    conditions: [
      {
        dimension: '',
        dimensionSubGroup: {
          logicalOperator: '',
          conditionDetails: [
            {
              dimension: '',
              operator: '',
              value: '',
              subOperator: null,
            },
          ],
        },
      },
      {
        dimension: '',
        dimensionSubGroup: {
          logicalOperator: '',
          conditionDetails: [
            {
              dimension: '',
              operator: '',
              value: '',
              subOperator: null,
            },
          ],
        },
      },
    ],
    },

效果

树形.jpg