vue配置sql规则

vue配置sql规则

  • 实现效果
  • 组件完整代码
  • 父组件

前端页面实现动态配置sql条件,将JSON结构给到后端,后端进行sql组装。
这里涉及的分组后端在组装时用括号将这块规则括起来就行,分组的sql连接符(并且/或者)取组里的第一个。

实现效果

在这里插入图片描述

组件完整代码

<!-- conditionGroup.vue -->
<template>
  <div :class="{ marginClass: onlyOne }" v-if="reDraw">
    <div class="condition-header" v-if="onlyOne">
      <div class="group-button">
        <el-tooltip content="分组">
          <i
            class="el-icon-folder-opened"
            icon-class="group"
            :style="{
              width: groupBtnSize + 'px',
              height: groupBtnSize + 'px',
              color: '#1890ff',
              cursor: 'pointer',
            }"
            @click.stop="_addGroup"
          ></i>
        </el-tooltip>
      </div>
    </div>
    <div
      v-for="(item, index) in conditionList"
      :style="{ 'flex-direction': 'column' }"
    >
      <div
        :style="{
          display: 'flex',
          'flex-direction': 'row',
          'align-items': 'center',
        }"
        v-if="!item.groups"
      >
        <div
          :style="{
            display: 'flex',
            'flex-direction': 'row',
            'align-items': 'center',
          }"
        >
          <i
            class="el-icon-circle-plus-outline color-success font-title-large"
            style="cursor: pointer"
            @click="_addItem(item)"
          ></i>
          <i
            class="el-icon-circle-close color-danger font-title-large"
            style="cursor: pointer; margin-left: 5px"
            @click="_delItem(item)"
          ></i>

          <el-checkbox
            style="padding: 0 10px 0 10px"
            v-model="item.checked"
          ></el-checkbox>
          <template v-if="floor > 1 && (!item.line || item.line.length == 0)">
            <div
              :style="{
                width: gradWidth + leftWidth * (floor - item.floor - 1) + 'px',
                height: '42px',
              }"
            ></div>
          </template>
          <template v-else v-for="(n, li) in item.line">
            <div
              :style="{
                width:
                  li == item.line.length - 1
                    ? gradWidth + leftWidth * (floor - item.floor) + 'px'
                    : leftWidth + 'px',
                height: '42px',
                background: getFloorColor(li + 1),
              }"
              :class="{
                'group-left': n.l == 2,
                'group-top-left': n.l == 4,
                'group-bottom-left': n.l == 5,
              }"
            >
              <el-tooltip :content="'点击取消所在分组'" v-if="n.l == 4">
                <i
                  class="el-icon-folder-opened"
                  icon-class="group"
                  :style="{
                    width: groupBtnSize + 'px',
                    height: groupBtnSize + 'px',
                    color: '#1890ff',
                    cursor: 'pointer',
                  }"
                  @click="_delGroup(item, n.p)"
                ></i>
              </el-tooltip>
            </div>
          </template>
        </div>
        <div style="position: relative">
          <div
            v-if="item.header"
            :style="{
              position: 'absolute',
              top: '-23px',
              left: '0px',
              display: 'flex',
              'flex-direction': 'row',
              width: '100%',
            }"
          >
            <div class="condition-header" style="margin-left: 20px">
              并且/或者
            </div>
          </div>
          <el-select
            v-model="item.operate"
            style="width: 120px; padding: 5px 0 5px 1px"
            size="small"
            :disabled="item.header"
          >
            <el-option
              v-for="ot in [
                { key: '并且', val: 'and' },
                { key: '或者', val: 'or' },
              ]"
              :key="ot.val"
              :label="ot.key"
              :value="ot.val"
            >
            </el-option>
          </el-select>
        </div>

        <div style="position: relative">
          <div
            v-if="item.header"
            :style="{
              position: 'absolute',
              top: '-23px',
              left: '0px',
              display: 'flex',
              'flex-direction': 'row',
              width: '100%',
            }"
          >
            <div class="condition-header" style="margin-left: 20px">字段</div>
          </div>
          <el-select
            v-model="item.field"
            style="width: 200px; margin-left: 10px; padding: 5px 0 5px 0px"
            size="small"
            @change="(item.value = ''), (item.condition = '')"
          >
            <el-option
              v-for="ot in keyList"
              :key="ot.val"
              :label="ot.key"
              :value="ot.val"
            >
            </el-option>
          </el-select>
        </div>

        <div style="position: relative">
          <div
            v-if="item.header"
            :style="{
              position: 'absolute',
              top: '-23px',
              left: '0px',
              display: 'flex',
              'flex-direction': 'row',
              width: '100%',
            }"
          >
            <div class="condition-header" style="margin-left: 20px">运算符</div>
          </div>
          <el-select
            v-model="item.condition"
            v-if="conditionMap && conditionMap[item.field]"
            style="width: 120px; margin-left: 10px; padding: 5px 0 5px 0px"
            size="small"
          >
            <el-option
              v-for="ot in conditionMap[item.field]"
              :key="ot.val"
              :label="ot.key"
              :value="ot.val"
            >
            </el-option>
          </el-select>
          <el-select
            v-model="item.condition"
            v-else
            style="width: 120px; margin-left: 10px; padding: 5px 0 5px 0px"
            size="small"
          >
            <el-option
              v-for="ot in conditionSelect"
              :key="ot.val"
              :label="ot.key"
              :value="ot.val"
            >
            </el-option>
          </el-select>
        </div>

        <div style="position: relative">
          <div
            v-if="item.header"
            :style="{
              position: 'absolute',
              top: '-23px',
              left: '0px',
              display: 'flex',
              'flex-direction': 'row',
              width: '100%',
            }"
          >
            <div class="condition-header" style="margin-left: 20px"></div>
          </div>
          <!-- 值类型:下拉选项、日期、时间、小数、整数、文本框 -->
          <el-select
            v-model="item.value"
            v-if="
              valList &&
              valList[item.field] &&
              valList[item.field].dom == 'select'
            "
            style="width: 400px; margin-left: 10px; padding: 5px 0 5px 0px"
            size="small"
            placeholder="请选择"
          >
            <el-option
              v-for="ot in valList[item.field].data"
              :key="ot.dictValue"
              :label="ot.dictLabel"
              :value="ot.dictValue"
            >
            </el-option>
          </el-select>
          <el-date-picker
            v-else-if="
              valList &&
              valList[item.field] &&
              valList[item.field].dom == 'date'
            "
            size="small"
            v-model="item.value"
            type="date"
            placeholder="请选择日期"
            style="
              width: 400px;
              margin-left: 10px;
              cursor: pointer;
              padding: 5px 0 5px 0px;
            "
            :editable="false"
            format="yyyy-MM-dd"
            value-format="yyyy-MM-dd"
          >
          </el-date-picker>
          <el-date-picker
            v-else-if="
              valList &&
              valList[item.field] &&
              valList[item.field].dom == 'dateTime'
            "
            size="small"
            v-model="item.value"
            type="datetime"
            placeholder="请选择时间"
            style="
              width: 400px;
              margin-left: 10px;
              cursor: pointer;
              padding: 5px 0 5px 0px;
            "
            :editable="false"
            format="yyyy-MM-dd HH:mm:ss"
            value-format="yyyy-MM-dd HH:mm:ss"
          >
          </el-date-picker>
          <el-input
            v-else-if="
              valList &&
              valList[item.field] &&
              valList[item.field].dom == 'decimals'
            "
            v-model="item.value"
            style="width: 400px; margin-left: 10px; padding: 5px 0 5px 0px"
            placeholder="请输入(支持小数)"
            clearable
            type="number"
            size="small"
            oninput="if(value < 0 || value == '' || value == null) value='';"
          />
          <!-- if(!/^\d+(\.\d{1,2})?$/.test(value)) value=value.match(/[\d]+(\.\d{0,2})?/)?.[0];  两位 -->
          <el-input
            v-else-if="
              valList &&
              valList[item.field] &&
              valList[item.field].dom == 'integer'
            "
            v-model="item.value"
            style="width: 400px; margin-left: 10px; padding: 5px 0 5px 0px"
            placeholder="请输入(整数)"
            clearable
            type="number"
            :min="1"
            size="small"
            oninput="if(value.includes('.')) value=value.replace(/\./g, ''); if(value < 0 || value == '' || value == null) value=''; if(!/^\d+$/.test(value)) value=value.replace(/\D/g,'');"
          />
          <el-input
            v-else
            v-model="item.value"
            style="width: 400px; margin-left: 10px; padding: 5px 0 5px 0px"
            placeholder="请输入"
            clearable
            size="small"
          />
        </div>
      </div>
      <conditionGroup
        :conditionList="item.groups"
        v-if="item.groups && item.groups.length > 0"
        :only-one="false"
        :parentData="parentData"
        :floor="floor"
        :borderColor="borderColor"
        :key-list="keyList"
        :val-list="valList"
        :condition-map="conditionMap"
      ></conditionGroup>
    </div>
    <!-- <el-button
      v-if="onlyOne"
      size="small"
      style="margin-top: 10px; cursor: pointer"
      @click="_addChild"
      >添加规则</el-button
    > -->
  </div>
</template>

<script>
const condition = {
  id: 1,
  index: 1,
  condition: "",
  operate: "and",
  field: "",
  value: "",
  checked: false,
  header: true,
  pid: -1,
  floor: 1,
};

const gradWidth = 20;
const leftWidth = 20;
const groupBtnSize = 20;
const alpha = 0.2;

const initData = [Object.assign({}, condition)];

export default {
  name: "conditionGroup",
  components: {},
  props: {
    onlyOne: {
      type: Boolean,
      default: () => true,
    },
    floor: {
      type: Number,
      default: () => 1,
    },
    conditionList: {
      type: Array,
      default: () => initData,
    },
    keyList: {
      type: Array,
      default: () => [],
    },
    conditionMap: {
      type: Object,
      default: () => {},
    },
    valList: {
      type: Object,
      default: () => {},
    },
    parentData: {
      type: Object,
      default: () => {},
    },
    gradWidth: {
      type: Number,
      default: () => gradWidth,
    },
    leftWidth: {
      type: Number,
      default: () => leftWidth,
    },
    groupBtnSize: {
      type: Number,
      default: () => groupBtnSize,
    },
    borderColor: {
      type: Array,
      default: () => ["rgba(24, 124, 255, " + alpha + ")"],
    },
  },
  data() {
    return {
      plotList: [],
      loading: false,

      reDraw: true,
      addGroupIndex: 0,
      conditionSelect: [
        { key: "等于", val: "eq" },
        { key: "不等于", val: "notEq" },
        { key: "大于", val: "gt" },
        { key: "小于", val: "lt" },
        { key: "大于等于", val: "gtq" },
        { key: "小于等于", val: "ltq" },
        { key: "包含", val: "like" },
        { key: "不包含", val: "notLike" },
        // { key: "加", val: "add" },
        // { key: "减", val: "subtract" },
        // { key: "乘", val: "multiply" },
        // { key: "除", val: "divide" },
      ],
    };
  },
  computed: {
    sidebar() {
      return this.$store.state.app.sidebar.opened;
    },
  },
  watch: {
    conditionList(val, oldVal) {
      this.$emit("input", val);

      while (this.borderColor.length < this.floor) {
        var _color = this.randomHexColor();
        while (this.borderColor.indexOf(_color) != -1) {
          _color = this.randomHexColor();
        }
        this.borderColor.push(_color);
      }

      this.reDraw = false;
      this.$nextTick(() => {
        this.reDraw = true;
      });
    },
    sidebar(val) {},
  },
  methods: {
    findChecked(list, arrParam) {
      var arr = arrParam || new Array();
      for (var i = 0; i < list.length; i++) {
        var o = list[i];
        if (o.groups && o.groups.length > 0) {
          this.findChecked(o.groups, arr);
        } else {
          if (o.checked) {
            arr.push(o);
          }
        }
      }
      return arr;
    },
    removeNode(list, targetList) {
      for (var i = 0; i < list.length; i++) {
        var o = list[i];
        for (var tid of targetList) {
          if (o.id == tid) {
            list.splice(i--, 1);
          }
        }
      }
    },
    findParentGroups(list, pid, retParam) {
      var ret = null || retParam;
      for (var i = 0; i < list.length; i++) {
        var o = list[i];
        if (o.groups && o.groups.length > 0) {
          if (o.id == pid) {
            ret = o;
          } else {
            ret = this.findParentGroups(o.groups, pid, ret);
          }
        }
      }
      return ret;
    },
    _addGroup() {
      this.addGroup(this.parentData.conditionList, this.parentData);
    },
    _delGroup(item, groupId) {
      this.delGroup(groupId, this.parentData.conditionList, this.parentData);
    },
    _addChild() {
      this.addChild(this.parentData.conditionList);
    },
    _delItem(item) {
      this.delItem(
        this.conditionList,
        item,
        this.parentData.conditionList,
        this.parentData
      );
    },
    _addItem(item) {
      this.addItem(
        this.conditionList,
        item.index,
        this.parentData.conditionList,
        this.parentData
      );
    },
    addItem(groups, index, conditionList, parentThis) {
      var newItem = Object.assign({}, condition, {
        id: new Date().getTime(),
        index: index + 1,
        floor: groups[0].floor,
        pid: groups[0].pid,
      });
      groups.splice(index, 0, newItem);

      parentThis.floor = this.refreshData(conditionList);
    },
    addChild(conditionList) {
      var newItem = Object.assign({}, condition, {
        id: new Date().getTime(),
        index: conditionList.length + 1,
        floor: 1,
        pid: -1,
      });
      newItem.header = false;
      conditionList.splice(conditionList.length, 0, newItem);
    },
    delItem(groups, item, conditionList, parentThis) {
      var sum = this.countItem(conditionList);
      if (sum <= 1) {
        return;
      }
      groups.splice(item.index - 1, 1);

      var currentGroups = this.findParentGroups(conditionList, groups[0].pid);
      if (currentGroups) {
        var parentGroups = this.findParentGroups(
          conditionList,
          currentGroups.pid
        );
        if (currentGroups.groups.length == 1) {
          var ag = JSON.parse(JSON.stringify(currentGroups.groups[0]));
          ag.index = currentGroups.index;
          ag.id = currentGroups.id;
          ag.pid = parentGroups ? parentGroups.id : -1;
          ag.floor = currentGroups.floor;
          if (ag.groups) {
            ag.groups.forEach((o, index) => {
              o.pid = ag.id;
              o.floor = ag.floor + 1;
              o.index = index + 1;
            });
          }
          if (parentGroups) {
            var _groups = this.findParentGroups(conditionList, parentGroups.id);
            _groups.groups.splice(currentGroups.index - 1, 1, ag);
          } else {
            conditionList.splice(currentGroups.index - 1, 1, ag);
          }
        }
      }
      if (conditionList.length == 1 && conditionList[0].groups) {
        var newList = JSON.parse(JSON.stringify(conditionList[0].groups));
        conditionList.splice(0, 1);
        for (var nl of newList) {
          nl.pid = -1;
          nl.floor = 1;
          conditionList.push(nl);
        }
      }
      parentThis.floor = this.refreshData(conditionList);
    },
    addGroup(conditionList, parentThis) {
      var checkedList = this.findChecked(conditionList);
      if (!checkedList || checkedList.length <= 1) {
        this.$message({
          message: "至少选择2个查询条目",
          type: "warning",
          duration: 1000,
        });
        return;
      }

      var checkNodes = [];
      for (var item of checkedList) {
        if (item.pid == -1) {
          this.uniquePush(checkNodes, item);
        } else {
          var pNode = this.getRealParent(conditionList, item, checkedList);
          if (pNode) {
            this.uniquePush(checkNodes, pNode);
          }
        }
      }

      var _tmpRoot = [];
      for (var ck of checkNodes) {
        var _tmp = this.findParentGroups(conditionList, ck.pid);
        if (_tmp) {
          this.uniquePush(_tmpRoot, _tmp);
        }
      }

      var allSelectCount = 0;
      var floorCount = [];
      for (var cn of checkNodes) {
        if (cn.groups) {
          allSelectCount += this.countItem(cn.groups);
        } else {
          allSelectCount++;
        }
        if (floorCount.indexOf(cn.floor) == -1) {
          floorCount.push(cn.floor);
        }
      }
      var rootGroup = this.findParentGroups(conditionList, checkNodes[0].pid);
      if (_tmpRoot.length > 1) {
        rootGroup = this.findParentGroups(conditionList, rootGroup.pid);

        allSelectCount = 0;
        for (var cn of _tmpRoot) {
          if (cn.groups) {
            allSelectCount += this.countItem(cn.groups);
          } else {
            allSelectCount++;
          }
        }
      }
      var rootArray = conditionList;
      if (rootGroup) {
        rootArray = rootGroup.groups;
      }
      var allCount = this.countItem(rootArray);

      var currentSelectCount = checkedList.length;

      if (allSelectCount != currentSelectCount || floorCount.length > 1) {
        this.$message({
          message: "不能交叉分组",
          type: "warning",
          duration: 1000,
        });
        return;
      }

      if (checkNodes.length == 1 || allCount == currentSelectCount) {
        this.$message({
          message: "无效分组",
          type: "warning",
          duration: 1000,
        });
        return;
      }

      var newCheckNode = JSON.parse(JSON.stringify(checkNodes));
      newCheckNode.sort(function (a, b) {
        return a.index - b.index;
      });
      var groupId = new Date().getTime();
      var newGroup = {
        groups: newCheckNode,
        id: groupId,
        index: newCheckNode[0].index,
        pid: newCheckNode[0].pid,
        floor: newCheckNode[0].floor,
      };

      var waitRemoveNode = [];
      for (var o of newCheckNode) {
        o.floor += 1;
        o.pid = groupId;
        if (!o.groups) {
          o.checked = false;
        }
        waitRemoveNode.push(o.id);
      }

      if (!rootGroup) {
        this.removeNode(conditionList, waitRemoveNode);
        conditionList.splice(newCheckNode[0].index - 1, 0, newGroup);
      } else {
        var _groups = this.findParentGroups(conditionList, rootGroup.id);
        this.removeNode(_groups.groups, waitRemoveNode);
        _groups.groups.splice(newCheckNode[0].index - 1, 0, newGroup);
      }
      parentThis.floor = this.refreshData(conditionList);
    },
    delGroup(groupId, conditionList, parentThis) {
      var parentGroups = this.findParentGroups(conditionList, groupId);
      var rootGroups = this.findParentGroups(conditionList, parentGroups.pid);

      var waitRemoveNode = [parentGroups.id];
      var newList = JSON.parse(JSON.stringify(parentGroups.groups));
      newList.forEach((o, index) => {
        o.pid = parentGroups.pid;
        o.floor = parentGroups.floor;
        o.checked = false;
      });

      if (!rootGroups) {
        this.removeNode(conditionList, waitRemoveNode);
        newList.forEach((o, index) => {
          conditionList.splice(parentGroups.index - 1 + index, 0, o);
        });
      } else {
        var _groups = this.findParentGroups(conditionList, rootGroups.id);
        this.removeNode(_groups.groups, waitRemoveNode);
        newList.forEach((o, index) => {
          _groups.groups.splice(parentGroups.index - 1 + index, 0, o);
        });
      }
      parentThis.floor = this.refreshData(conditionList);
    },
    getRealParent(allItems, item, checkedList) {
      var parentGroups = this.findParentGroups(allItems, item.pid);
      var ret = parentGroups;
      if (parentGroups) {
        var childCount = this.countItem(parentGroups.groups);
        var realChildCount = 0;
        for (var cl of checkedList) {
          if (cl.pid == parentGroups.id) {
            realChildCount++;
          } else {
            var pg = this.findParentGroups(allItems, cl.pid);
            if (pg) {
              if (pg.pid == parentGroups.id) {
                realChildCount++;
              } else {
                while (pg && pg.pid != parentGroups.id) {
                  pg = this.findParentGroups(allItems, pg.pid);
                  if (pg && pg.pid == parentGroups.id) {
                    realChildCount++;
                  }
                }
              }
            }
          }
        }
        if (childCount == realChildCount) {
          var _tmp = this.getRealParent(allItems, parentGroups, checkedList);
          if (_tmp) {
            ret = _tmp;
          }
        } else {
          ret = item;
        }
      }
      return ret;
    },
    reIndex(list, i, arr) {
      for (var index = 0; index < list.length; index++) {
        var o = list[index];
        if (arr.indexOf(i) == -1) {
          arr.push(i);
        }
        if (o.groups && o.groups.length > 0) {
          o.index = index + 1;
          o.floor = i;
          if (i == 1) {
            o.pid = -1;
          }
          this.reIndex(o.groups, i + 1, arr);
        } else {
          o.index = index + 1;
          o.floor = i;
          o.checked = false;
          if (i == 1) {
            o.pid = -1;
          }
        }
      }
    },
    drawLineGroup(list, currentFloor, retList) {
      for (var index = 0; index < list.length; index++) {
        var o = list[index];
        if (o.groups && o.groups.length > 0) {
          this.drawLineGroup(o.groups, currentFloor + 1, retList);
        } else {
          o.line = new Array(currentFloor - 1);
          if (retList.length == 0) {
            o.header = true;
          } else {
            o.header = false;
          }

          for (var _k = 0; _k < o.line.length; _k++) {
            o.line[_k] = { l: 2, p: -1 };
          }
          retList.push(o);
        }
      }
    },
    refreshData(list) {
      var floorCountArr = [];
      this.reIndex(list, 1, floorCountArr);
      var maxFloor = floorCountArr.length;

      var ret = new Array();
      this.drawLineGroup(list, 1, ret);

      for (var item of ret) {
        var parentGroup = this.findParentGroups(list, item.pid);
        if (item.pid != -1) {
          if (item.index == 1) {
            var node = { l: 4, p: parentGroup.id };
            item.line[item.line.length - 1] = node;
          } else if (item.index == parentGroup.groups.length) {
            var node = { l: 5, p: -1 };
            item.line[item.line.length - 1] = node;
          }
        }
        if (parentGroup) {
          var parentIndex = parentGroup.index;
          var parentLength = parentGroup.groups.length;
          var i = 2;
          var currentParentGroup = this.findParentGroups(list, parentGroup.pid);
          while (currentParentGroup) {
            if (i != 2) {
              parentGroup = JSON.parse(JSON.stringify(currentParentGroup));
              currentParentGroup = this.findParentGroups(list, parentGroup.pid);
            }
            if (currentParentGroup) {
              if (
                parentGroup.index == 1 &&
                item.index == 1 &&
                parentIndex == 1
              ) {
                var node = { l: 4, p: currentParentGroup.id };
                item.line[item.line.length - i] = node;
              } else if (
                parentGroup.index == currentParentGroup.groups.length &&
                item.index == parentLength
              ) {
                item.line[item.line.length - i] = { l: 5, p: -1 };
              } else {
                break;
              }
              i++;
            }
          }
        }
      }
      return maxFloor;
    },
    countItem(list, i) {
      var sum = i || 0;
      for (var index = 0; index < list.length; index++) {
        var o = list[index];
        if (o.groups && o.groups.length > 0) {
          sum += this.countItem(o.groups, i);
        } else {
          sum++;
        }
      }
      return sum;
    },
    uniquePush(arr, item) {
      var exist = false;
      for (var o of arr) {
        if (o.id == item.id) {
          exist = true;
        }
      }
      if (!exist) {
        arr.push(item);
      }
    },
    randomHexColor() {
      // return '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).substr(-6);
      return this.randomColor(alpha);
    },
    randomColor(alpha) {
      alpha =
        alpha == undefined ? ((Math.random() * 10) / 10).toFixed(1) : alpha;
      alpha = Number(alpha);
      if (isNaN(alpha)) alpha = 1;
      var col = "rgba(";
      for (var i = 0; i < 3; i++) {
        col += parseInt(Math.random() * 256) + ",";
      }
      col += alpha + ")";
      return col;
    },
    getFloorColor(floor) {
      return this.borderColor[floor - 1];
    },
  },
  created() {
    if (
      typeof this.conditionList[0].field == "string" &&
      typeof this.conditionList[0].header == "undefined"
    ) {
      this.conditionList[0].header = true;
    }
    this.$nextTick(() => {});
  },
};
</script>

<style type="text/css">
/* :root 是CSS中的一个伪类选择器,它代表文档的根元素。在HTML中,根元素通常是<html>标签。使用:root选择器允许开发者为整个文档定义全局样式变量
--borderWidth:这是一个自定义的CSS变量
使用var(--variableName)语法进行引用 */

:root {
  --borderWidth: 1px;
  --borderColor: rgba(158, 158, 158, 1);
}

table {
  border-collapse: collapse;
}

.marginClass {
  margin-bottom: 10px;
}

.condition-header {
  font-weight: 600;
  display: flex;
  flex-direction: row;
}

.group-button {
  margin-left: 47px;
  display: flex;
  flex-direction: row;
  align-items: center;
}

.group-left {
  border-left: var(--borderWidth) solid var(--borderColor);
}
.group-top-left {
  border-top: var(--borderWidth) solid var(--borderColor);
  border-left: var(--borderWidth) solid var(--borderColor);
}
.group-bottom-left {
  border-bottom: var(--borderWidth) solid var(--borderColor);
  border-left: var(--borderWidth) solid var(--borderColor);
}
</style>

父组件

<template>
 <!-- :parentData="this"传递父组件实例,子组件使用属性或方法时this.parentData.属性/方法名 -->
<conditionGroup
        :floor="floor"
        :conditionList="conditionList"
        :parentData="this"
        :key-list="keyOptions"
        :condition-map="conditionOptions"
        :val-list="valueOptions"
      >
      </conditionGroup>
</template>

<sctipt>
export default {
	data() {
		conditionList: [Object.assign({}, condition)], // 默认页面显示一条空规则
		floor: 1, // 树深度
        keyOptions: [], // 字段选项
      	conditionOptions: {}, // 特殊运算符(像下拉框的只有 等于 运算符等)
     	 valueOptions: {}, //下拉框类型的数据
	},
	// 一些关键方法
	methods: {
	// 递归找结构深度
    getFloorth(groups) {
      let deepestFloor = 0;
      let deepestGroup = null;

      function searchGroup(group) {
        // 找到比当前deepestFloor值大,则重新赋值deepestFloor,并将当前对象记录
        if (group.floor > deepestFloor) {
          deepestFloor = group.floor;
          deepestGroup = group;
        }
        if (group.groups && group.groups.length > 0) {
          group.groups.forEach(searchGroup);
        }
      }

      groups.forEach(searchGroup);
      return deepestGroup ? deepestGroup.floor : 1;
    },
    // 提交时校验规则是否有未填/选项
    traverse(data) {
      for (let item of data) {
        // 这里需要判空的参数field、condition、value,如果有为空则提示,不可保存
        if (
          !item.groups &&
          (!item?.field || !item?.condition || !item?.value)
        ) {
          this.$message.warning("请完善规则条件配置");
          // 当校验失败时抛出错误,`中断当前执行的代码块`,并被上层catch语句捕获
          throw new Error("字段验证失败"); 
        } else if (item.groups) {
          this.traverse(item.groups);
        }
      }
      return true;
    },
    // 根据其他下拉框,查询动态字段项
    selectChange(obj) {
      let { name, val } = obj;
      if (name === "equipType") {
        this.keyOptions = [];
        this.valueOptions = {};
        this.conditionOptions = {};
        let keyOptionsDict = val + "_column"; // keyOptionsDict 为val和_column拼接的字典名称去过滤对应字典项
        let options = this.$options.filters["dictOption"]({
          dictName: keyOptionsDict,
        });

        // 有过滤到字段项,给字段项字段push数据(字段key和val)
        if (options.length) {
          options.forEach((item) => {
             // item.dictValue用#拼接的三个参数,如heat_status_code#select#heat_status(第一个为规则字段键、第二个为值类型、第三个为字典名)
            let parts = item.dictValue.split("#");
            this.keyOptions.push({
              key: item.dictLabel,
              val: parts[0],
            });

            if (parts[1] === "select") {
              // 如果是select下拉框,则拼接下拉框值数据项(我们这块都是匹配的字典查询,可拓展为接口查询)
              this.valueOptions[parts[0]] = {
                dom: parts[1],
                data: this.$options.filters["dictOption"]({
                  dictName: parts[2],
                }),
              };

              // 若是下拉框,处理运算符
              this.conditionOptions[parts[0]] = [{ key: "等于", val: "eq" }];
            } else {
              // 其他字段直接给dom属性
              this.valueOptions[parts[0]] = { dom: parts[1] };
            }
          });
        } else {
          this.conditionList = [Object.assign({}, condition)];
          this.keyOptions = [];
          this.conditionOptions = {};
        }
      }
    },
    // 若需要前端组装给后端可使用下面方法(我这里是直接给的JSON,前端不进行组装)
    generateConditionString(conditionList) {
      let result = "";
      if (conditionList.length === 0) return result;
      const generateSubConditions = (conditions) => {
        return conditions
          .map((condition, index) => {
            if (condition.groups && condition.groups.length > 0) {
              let connectSym = condition.groups[0].operate;
              return index === 0
                ? `(${generateSubConditions(condition.groups)})`
                : ` ${connectSym} (${generateSubConditions(condition.groups)})`;
            }

            let value = condition.value;
            if (typeof value === "string") {
              if (
                condition.condition === "like" ||
                condition.condition === "notLike"
              ) {
                value = `'%${value.replace(/'/g, "")}%'`;
              } else {
                value = `'${value.replace(/'/g, "''")}'`;
              }
            }

            if (
              !condition?.field ||
              !condition?.condition ||
              !condition?.value
            ) {
              this.$message.warning("请完善规则条件配置");
              return result;
            }

            const conditionString = `${condition.field} ${
              this.operators[condition.condition]
            } ${value}`;

            const logicOperator = ` ${condition.operate} `;
            return `${index === 0 ? "" : logicOperator}${conditionString}`;
          })
          .join("");
      };

      result = generateSubConditions(conditionList);
      this.querystring = result;
      return result;
    },
	}
}
</script>

参考文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/773438.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

论文配色:跟着顶刊学配色(Nature篇)

写在前面&#xff1a; 截至目前&#xff0c;nature共发表Article 572篇&#xff0c;本文挑选了部分最新的文献&#xff0c;进行配色总结&#xff0c;每种颜色分别提供十六进制、RGB、HSB、CMYK和LAB5种描述模型&#xff0c;方便后期配色使用。 三色&#xff1a; 四色&#xff…

Java增加线程后kafka仍然消费很慢

文章目录 一、问题分析二、控制kafka消费速度属性三、案例描述 一、问题分析 Java增加线程通常是为了提高程序的并发处理能力&#xff0c;但如果Kafka仍然消费很慢&#xff0c;可能的原因有&#xff1a; 网络延迟较大&#xff1a;如果网络延迟较大&#xff0c;即使开启了多线…

【MindSpore学习打卡】应用实践-计算机视觉-SSD目标检测:从理论到实现

在计算机视觉领域&#xff0c;目标检测是一个至关重要的任务。它不仅要求识别图像中的目标物体&#xff0c;还需要精确定位这些物体的位置。近年来&#xff0c;随着深度学习技术的飞速发展&#xff0c;各种高效的目标检测算法层出不穷。SSD&#xff08;Single Shot MultiBox De…

推动高效能:东芝TB67H301FTG全桥直流电机驱动IC

在如今高度自动化的时代&#xff0c;电子产品的性能和效率成为了工程师们关注的焦点。东芝的TB67H301FTG全桥直流电机驱动IC应运而生&#xff0c;以其卓越的技术和可靠性&#xff0c;成为众多应用的理想选择。无论是在机器人、家用电器、工业自动化&#xff0c;还是在其他需要精…

企业怎么选购USB Server?先看这条!

一、首先&#xff0c;USB Server是什么&#xff1f; USB Server&#xff1f;听起来像是个高科技玩意儿&#xff01; 其实&#xff0c;它就是个很多企业都在用的远程“传送门”&#xff0c;把USB设备都固定插在USB Server上&#xff0c;然后将USB Server与计算机网络连接&…

LaTeX表格灵活设置列宽

一些基本的插入表格的操作见&#xff1a;https://blog.csdn.net/gsgbgxp/article/details/129457872 遇到问题先查阅《IShort》和刘海洋老师的《LaTeX入门》。 设置表格列宽基础操作&#xff08;不借助tabularx&#xff09; 先从一个简单表格开始 \begin{table}[!h]\centeri…

Python基础小知识问答系列-过滤列表元素

1. 问题&#xff1a; 如何根据单一条件过滤列表的元素&#xff1f; 如何根据复杂条件过滤列表的元素&#xff1f; 2. 解决方式&#xff1a; 可以使用推导式生成器&#xff0c;进行单一条件的列表元素过滤&#xff0c;尤其是列表内容较多时; 也可以使用filter函数进行列…

怎么看一家TPM管理咨询公司专不专业

在评估一家TPM管理咨询公司是否专业时&#xff0c;我们需要从多个维度进行深入的考量。TPM作为一种以提升设备综合效率为目标&#xff0c;以全系统的预防维修为过程&#xff0c;以全体人员参与为基础的设备保养和维修管理体系&#xff0c;其实施的成功与否直接关系到企业的生产…

二二复制模式,发展下属并形成一个销售网络体系来实现收入增长!

二二复制模式&#xff0c;又称为双轨制&#xff0c;是一种直销理念的营销模式&#xff0c;其核心在于通过发展下属并形成一个销售网络体系来实现收入增长。以下是对二二复制模式的详细讲解&#xff0c;包括其优势和玩法介绍&#xff0c;以及适合的行业。 一、二二复制模式的定…

刚办理的手机号被停用,你可能遇到这些问题了!

很多朋友都会遇到手机号被停用的情况&#xff0c;那么你知道你的手机号为什么会被停用吗&#xff1f;接下来&#xff0c;关于手机号被停用的问题&#xff0c;跟着小编一块来了解一下吧。 ​停机的两种形态&#xff1a; 1、第一个是局方停机&#xff0c;即语音、短信和流量都不…

opencv实现人脸检测功能----20240704

opencv实现人脸检测 早在 2017 年 8 月,OpenCV 3.3 正式发布,带来了高度改进的“深度神经网络”(dnn)模块。 该模块支持多种深度学习框架,包括 Caffe、TensorFlow 和 Torch/PyTorch。OpenCV 的官方版本中包含了一个更准确、基于深度学习的人脸检测器, 链接:基于深度学习…

Day04-SonarQube

Day04-SonarQube 1.SonarQube基本概述1.1 什么是SonarQube1.2 使用SonarQube前提环境要求 2. SonarQube服务安装-8.9 lts (PostgreSQL)2.1 环境准备2.2 安装Sonarqube依赖工具 -PSQL 2.SonarQube服务安装-7.7 (MySQL)故障与排查 3.Sonarqube插件管理4. 创建项目及分析1) 分析ja…

简历–求职信–通用

每个毕业生的简历首页大概都会是一封求职信。如果说对求职者的简历正文我们只是浮光掠影看上几眼的话&#xff0c;那么对求职信&#xff0c;简直连浮光掠影都称不上。说实话&#xff0c;我在看求职者简历的时候一般会把这一页翻过去&#xff0c;很少去看。为什么呢&#xff1f;…

springboot宠物领养系统-计算机毕业设计源码08373

目 录 摘要 1 绪论 1.1选题依据 1.2国内外研究现状 1.3相关技术介绍 1.4论文结构与章节安排 2 基于springboot宠物领养系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2经济可行性分析 2.1.3操作可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功…

java 常见错误问题

1.java中datetime数据类型如何定义 在Java中&#xff0c;可以使用java.time包中的DateTime类来定义DateTime数据类型。 要定义DateTime数据类型&#xff0c;你可以使用以下代码&#xff1a; public static void test() {// 获取当前日期和时间LocalDateTime datetime Local…

如何利用AI撰写短文案获客?分享6大平台和3大步骤!

从去年开始&#xff0c;很多大厂都在裁员&#xff0c;原因就是因为AI的火爆&#xff0c;替代了很多机械式的劳动力。以前很多人可以通过机械式的工作来摸鱼&#xff0c;现在AI完成的效率比人工的要高很多倍。 国内好用的AI平台非常多&#xff0c;有时候也可以使用几个AI平台结合…

RAG 工业落地方案框架(Qanything、RAGFlow、FastGPT、智谱RAG)细节比对!CVPR自动驾驶最in挑战赛赛道,全球冠军被算力选手夺走了

RAG 工业落地方案框架&#xff08;Qanything、RAGFlow、FastGPT、智谱RAG&#xff09;细节比对&#xff01;CVPR自动驾驶最in挑战赛赛道&#xff0c;全球冠军被算力选手夺走了。 本文详细比较了四种 RAG 工业落地方案 ——Qanything、RAGFlow、FastGPT 和智谱 RAG&#xff0c;重…

后端之路——最规范、便捷的spring boot工程配置

一、参数配置化 上一篇我们学了阿里云OSS的使用&#xff0c;那么我们为了方便使用OSS来上传文件&#xff0c;就创建了一个【util】类&#xff0c;里面有一个【AliOSSUtils】类&#xff0c;虽然本人觉得没啥不方便的&#xff0c;但是黑马视频又说这样还是存在不便维护和管理问题…

Artificial Intelligence Self-study

Artificial Intelligence Self-study Traditional AI (Symbolic AI) 基于&#xff1a;符号表示 数理逻辑 搜索 - 有明确规则&#xff0c;依靠算力。Appliance &#xff1a; 数学难题(Heuristic Algorithm)&#xff0c;棋牌对抗(围棋)&#xff0c;专家系统(输入病症&#xf…

02-android studio实现下拉列表+单选框+年月日功能

一、下拉列表功能 1.效果图 2.实现过程 1&#xff09;添加组件 <LinearLayoutandroid:layout_width"match_parent"android:layout_height"wrap_content"android:layout_marginLeft"20dp"android:layout_marginRight"20dp"android…