el-tree 添加且或条件并确定数据格式

首先给大家看看效果

 看到这个原型之后,再去看数据结构就容易懂了。

[{
	"key": "AND", (且、或)
	"type": "group",(条件组)
	"children": [{
		"type": "condition",
		"children": [],
		"indexCode": "2",
		"indexName": "",
		"value": "123",
        "tagsAll": [],
		"operator": "5",
		"del": false
	}, {
		"type": "condition",
		"children": [],
		"indexCode": "",
		"indexName": "",
		"value": "",
        "tagsAll": [],
		"operator": "",
		"del": false
	}, {
		"key": "AND",
		"type": "group",
		"children": [{
			"type": "condition",
			"children": [],
			"indexCode": "3",
			"indexName": "",
			"value": "111",
            "tagsAll": [],
			"operator": "4",
			"del": false
		}],
		"del": false
	}, {
		"type": "condition",
		"children": [],
		"indexCode": "4",
		"indexName": "",
		"value": "0",
        "tagsAll": [],
		"operator": "1",
		"del": false
	}],
	"del": false
}]

之前想的过于复杂,导致浪费了很多时间。话不多说上代码:

<template>
  <div class="filter-condition">
    <el-tree
      :data="ruleSetInfos"
      default-expand-all
      :expand-on-click-node="false"
    >
      <span
        class="custom-tree-node display"
        style="width: 100%"
        slot-scope="{ node, data }"
        @mouseenter="mouseenter(data)"
        @mouseleave="mouseleave(data)"
      >
        <!--   条件组      -->
        <div class="display" v-if="data.type === commandTypeEnum.GROUP">
          <el-button
            class="round"
            type="success"
            round
            size="mini"
            @click="updateRuleKey(data)"
          >
            {{ ruleNameItem[data.key] }}
          </el-button>
          <el-dropdown
            trigger="click"
            size="mini"
            @command="addRuleItem($event, data, node)"
          >
            <span class="el-dropdown-link">
              <i class="el-icon-circle-plus-outline add-rules-icon"></i>
            </span>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item :command="commandTypeEnum.CONDITION"
                >条件</el-dropdown-item
              >
              <el-dropdown-item :command="commandTypeEnum.GROUP"
                >条件组</el-dropdown-item
              >
            </el-dropdown-menu>
          </el-dropdown>
        </div>

        <!--   条件     -->
        <div
          class="displaydisplay"
          v-if="data.type === commandTypeEnum.CONDITION"
        >
          {{ node.data.indexName }}
          <el-select v-model="node.data.indexCode" size="mini" placeholder="选择字段" class="simple-type-select">
            <el-option v-for="item in timeFil" :key="item.id" :label="item.modelName" :value="item.id">
            </el-option>
          </el-select>
        </div>

        <!-- 运算符 -->
        <div
          class="displaydisplay"
          v-if="data.type === commandTypeEnum.CONDITION"
        >
          <el-select v-model="node.data.operator" size="mini" placeholder="选择比较符" class="simple-type-select">
            <el-option v-for="item in OperationList" :key="item.value" :label="item.name" :value="item.value">
            </el-option>
          </el-select>
        </div>

        <!-- 输入框(属于、不属于) -->
        <div
          class="displaydisplay"
          v-if="data.type === commandTypeEnum.CONDITION  && (node.data.operator == 'IN' || node.data.operator == 'NOT IN' )"
        >
          <!-- 父盒子 -->
          <div class="father_box" @click="onclick">
            <!-- 生成的标签 -->
            <div v-for="(item, index) in node.data.TagsAll" :key="index" class="spanbox">
              <span class="tagspan">{{ item }}</span>
              <i class="span_close" @click="removeTag(index, item, node.data)"></i>
            </div>
            <!-- 输入框 
              @keyup.delete="deleteTags"-->
            <input
              placeholder="输入后按<回车>创建"
              v-model="node.data.value"
              @keyup.enter="addTags(node)"
              :style="inputStyle"
              class="inputTag"
              ref="inputTag"
              type="text"
            />
          </div>
        </div>
        <!-- 输入框 -->
        <div
          class="displaydisplay"
          v-if="data.type === commandTypeEnum.CONDITION  && (node.data.operator !== 'IN' && node.data.operator !== 'NOT IN' )"
        >
          <el-input v-model="node.data.value" size="small"></el-input>
        </div>
        <!-- 删除图标 -->
          <i
            v-show="data.del && node.parent.level !== 0"
            class="el-icon-delete-solid delete-icon"
            @click="remove(node, data)"
          >
          </i>
      </span>
    </el-tree>
  </div>
</template>

<script>
import {
  commandTypeEnum,
  ruleNameItem,
  ruleNameTypeEnum,
  OperationList,
} from "../processjos/model";

export default {
  name: "filterCondition",
  
  props: {
    timeFil: {
      type: Array,
      default: null,
    },
  },

  data() {
    return {
      flag: [],
      //输入框宽度
      inputLength: "",

      // 树节点数据
      ruleSetInfos: [{
          key: "AND", // 且/或 AND/OR
          type: "group", // 条件/条件组  group/condition
          children: [],
        }],

      // 筛选条件
      ruleNameItem: ruleNameItem,

      // 筛选条件常量
      ruleNameTypeEnum: ruleNameTypeEnum,

      // 条件/条件组常量
      commandTypeEnum: commandTypeEnum,

      OperationList: OperationList,
    };
  },
  watch: {
    //监听输入的值越多,输入框越长
    currentval(val) {
      // 实时改变input输入框宽度,防止输入内容超出input默认宽度显示不全
      this.inputLength = this.$refs.inputTag.value.length * 12 + 50;
    },
  },
  computed: {
    //计算属性:计算出动态输入框宽度
    inputStyle() {
      let style = {};
      style.width = `${this.inputLength}px`;
      return style;
    },
  },

  methods: {
    //删除tag
    removeTag(index, item, node) {
      node.TagsAll.splice(index, 1);
    },

    //回车增加tag
    addTags(node) {

      if (node.data.value) {

          //添加tag
          node.data.TagsAll.push(node.data.value);

          //清空输入框
          node.data.value = "";
      }
    },

    //点击父盒子输入框获取焦点
    onclick() {
      //   console.log(2345);
    },
    /**
     * 父组件调用
     */
    clickQ() {
      this.ruleFlag(this.ruleSetInfos)
      return this.ruleSetInfos
    },
    /**
     * 校验过滤条件非空
     */
    ruleFlag(val) {
      if (val) {
        // 每一层添加 flag
        for (let index = 0; index < val.length; index++) {
          if (val[index].type == 'group') {
            val[index].children && val[index].children.forEach(item => {
              if (item.type == 'condition') {
                if (!item.indexCode || !item.operator || ((!item.value && item.TagsAll.length < 1))) {
                  this.$message.warning('将条件输入完整')
                  this.flag.push(false)
                  return false
                } else {
                  this.flag.push(true)
                }
              } else {
                if (item.type == 'group') {
                  item.children && item.children.forEach(el => {
                     if (el.type == 'condition') {
                      if (!el.indexCode || !el.operator || (!el.value && el.TagsAll.length < 1)) {
                        this.$message.warning('将条件输入完整')
                        this.flag.push(false)
                        return false
                      } else {
                        this.flag.push(true)
                      }
                     }
                  })
                }

              }
            });
          }
        }
      }
    },
    /**
     * 修改规则key
     * @param index 数组的某一项
     */
    updateRuleKey(item) {
      if (item.key === this.ruleNameTypeEnum.AND) {
        item.key = this.ruleNameTypeEnum.OR;
        return;
      }
      item.key = this.ruleNameTypeEnum.AND;
    },

    /**
     * 添加规则
     * @param type 添加规则的类型
     * @param data 点击的当前数据
     */
    addRuleItem(type, data, node) {
      // 限制添加层级
      if (node.level == 3) {
        this.$message.warning('只可添加三层')
        return
      }
      // 添加条件组
      let ruleItem = { key: 'AND', type: 'group', children: [] };
      if (type === this.commandTypeEnum.CONDITION) {
        ruleItem = { type: 'condition', children: [], indexCode: '', indexName: '', value: '', operator: '=', TagsAll: [] };
      }
      this.append(data, ruleItem);
    },

    /**
     * 在树中添加数据
     * @param data 当前数据
     * @param ruleItem 添加的数据
     */
    append(data, ruleItem) {
      if (!data.children) {
        this.$set(data, "children", []);
      }
      data.children.push(ruleItem);
    },


    /**
     * 删除节点
     */
    remove(node, data) {
      const parent = node.parent;
      const children = parent.data.children ? parent.data.children : parent.data;
      const index = children.findIndex((d) => d.$treeNodeId === data.$treeNodeId);
      children.splice(index, 1);
    },
    /**
     * 选择字段值
     */
    handleCommand(value, data, node) {
      node.data.indexCode = value;
      node.data.indexName = timeFil.find(item => item.value === value).name;
    },

    /**
     * 运算符
     */
    commandOperator(command) {
      node.data.OperationVal = OperationList.find(item => item.value === command).name;
    },

    /**
     * 清除数据
     */
    clearValue() {
      this.ruleSetInfos = [{ key: 'AND', type: 'group', children: [] }]
    },
  },
};
</script>

<style  lang="scss">

.filter-condition {
  margin-left: 50px;
  .displaydisplay {
    // margin-top: 10px;
    // margin-bottom: 10px;
    margin-left: 15px;
  }
  .el-tree-node__content {
    margin-bottom: 10px;
    height: 34px !important;
    padding-left: 3px !important;
  }

  .add-rules-icon {
    color: #ff8e18;
    margin-left: 6px;
    margin-top: 5px;
    font-size: 14px;
  }
  .display {
    display: flex;
    height: 100%;
    align-items: center;
  }
  .round {
    padding: 5px !important;
  }
  .el-dropdown-link {
    color: #409eff;
    font-size: 13px;
  }
  ::v-deep.el-tree > .el-tree-node:after {
    border-top: none;
  }

  .el-tree-node {
    position: relative;
    padding-left: 16px;
  }

  .el-tree-node__expand-icon.is-leaf {
    display: none;
  }

  .el-tree-node__children {
    padding-left: 16px;
  }

  .el-tree-node :last-child:before {
    height: 38px;
  }

  .el-tree > .el-tree-node:before {
    border-left: none;
  }

  .el-tree > .el-tree-node:after {
    border-top: none;
  }

  .el-tree-node:before {
    content: "";
    left: -4px;
    position: absolute;
    right: auto;
    border-width: 1px;
  }

  .el-tree-node:after {
    content: "";
    left: -4px;
    position: absolute;
    right: auto;
    border-width: 1px;
  }

  .el-tree-node:before {
    border-left: 2px dashed #ddd;
    bottom: 0px;
    height: 100%;
    top: -26px;
    width: 1px;
  }

  .el-tree-node:after {
    border-top: 2px dashed #ddd;
    height: 20px;
    top: 16px;
    width: 20px;
  }
  .delete-icon {
    color: #f56c6c;
    margin-top: 3px;
    margin-left: 12px;
  }

  .simple-type-select .el-input__inner {
    width: 105px;
    font-size: 12px;
    color: rgb(93, 160, 210);
    position: relative;
    bottom: 1px;
    border: none;
    background: unset;
  }
  .el-tag {
    margin-right: 5px;
  }
}

</style>

<style scoped>
/* 外层div */
.father_box {
  /* width: 600px; */
  box-sizing: border-box;
  background-color: white;
  border: 1px solid #409EFF;
  border-radius: 4px;
  font-size: 12px;
  text-align: left;
  padding-left: 5px;
  word-wrap: break-word;
  overflow: hidden;
}
/* 标签 */
.spanbox {
  display: inline-block;
  font-size: 14px;
  margin: 3px 4px 3px 0;
  background-color: #ecf5ff;
  border: 1px solid #e8eaec;
  border-radius: 3px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)
}
/* 标签文字 */
.tagspan {
  height: 24px;
  line-height: 22px;
  max-width: 99%;
  position: relative;
  display: inline-block;
  padding-left: 8px;
  color: #409EFF;
  font-size: 14px;
  opacity: 1;
  vertical-align: middle;
  overflow: hidden;
  transition: 0.25s linear;
}
/* tag的叉叉 */
.span_close {
  padding: 0 6px 0 4px;
  opacity: 1;
  -webkit-filter: none;
  filter: none;
  color: #409EFF;
  /* font-weight: 600; */
  cursor:pointer;
}
/* 鼠标经过叉叉 */
.span_close:hover{
  background-color: #409EFF;
  border-radius: 50%;
  color: #fff;
}
.span_close:after {
  content: "\00D7";
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* line-height: 27px; */
  transition: 0.3s, color 0s;
}
/* input */
.inputTag {
  margin-right: 50px;
  font-size: 16px;
  border: none;
  box-shadow: none;
  outline: none;
  background-color: transparent;
  padding: 0;
  width: auto;
  min-width: 150px;
  vertical-align: top;
  height: 32px;
  color: #495060;
  line-height: 32px;
}
/* 输入框提示文字大小 */
input:placeholder-shown {
font-size: 0.6rem;
}
</style>

非空校验可以写成轮询,我这里只有三层就没有修改,有兴趣的朋友可以修改下。

另外如果你不止一个地方使用了el-tree,要修改样式且只在当前页面有效,直接scoped是不行的,最好是在外层加一层div,使用scss。

js代码

// 筛选条件
export const ruleNameItem = {
    'AND': '且',
    'OR': '或'
  }
  
  // 筛选条件常量
  export const ruleNameTypeEnum = {
    AND: 'AND',
    OR: 'OR'
  }
  // 条件/条件组常量
  export const commandTypeEnum ={
    GROUP: 'group',
    CONDITION: 'condition'
  }

  /**
   * 运算符
   */
  export const OperationList = [
    {name: '等于', value: '='},
    {name: '不等于', value: '<>'},
    {name: '包含', value: 'LIKE'},
    {name: '不包含', value: 'NOT LIKE'},
    {name: '属于', value: 'IN'},
    {name: '不属于', value: 'NOT IN'},
  ]

写的不好,有什么问题,欢迎指出~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值