vue配置sql规则

news2024/11/19 14:45:23

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.coloradmin.cn/o/1896376.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

细说MCU的ADC模块单通道连续采样的实现方法

目录 一、工程依赖的硬件及背景 二、设计目的 三、建立工程 1、配置GPIO 2、选择时钟源和Debug 3、配置ADC 4、配置系统时钟和ADC时钟 5、配置TIM3 6、配置串口 四、代码修改 1、重定义TIM3中断回调函数 2、启动ADC及重写其回调函数 3、定义用于存储转换结果的数…

30斤用什么快递便宜?大件物品怎么寄划算省钱?

大学生小李最近因为毕业要搬家&#xff0c;不得不把一堆书籍、衣服和一些生活用品寄回家。作为一个精打细算的“穷学生”&#xff0c;小李可是不愿意在快递费上花冤枉钱的。于是&#xff0c;他开始研究各种寄快递省钱的方法&#xff0c;今天我们就来看看小李是怎么操作的。一、…

【Python画图-seaborn驯化】一文学会seaborn画散点图scatterplot、swarmplot技巧

【Python画图-seaborn驯化】一文学会seaborn画散点图scatterplot、swarmplot 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取相关内…

用免费的可视化工具制作3D智慧城市大屏,融合数字孪生,引领数据升级

在如今数据驱动的时代&#xff0c;越来越多的场景中都有可视化大屏的身影&#xff0c;许多企业和政府部门也从常规的二维看板渐渐地转向更加炫酷&#xff0c;立体的3D可视化大屏。3D可视化大屏成为了展示复杂数据、实时监控业务动态的重要工具。本文将详细介绍如何使用免费的数…

基于Springboot的智慧养老中心管理系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于Springboot的智慧养老中心管理系统,…

Appium+python自动化(四十一)-Appium自动化测试框架综合实践 - 即将落下帷幕(超详解)

1.简介 今天我们紧接着上一篇继续分享Appium自动化测试框架综合实践 - 代码实现。到今天为止&#xff0c;大功即将告成&#xff1b;框架所需要的代码实现都基本完成。 2.data数据封装 2.1使用背景 在实际项目过程中&#xff0c;我们的数据可能是存储在一个数据文件中&#x…

RRStudio 下载及安装(详尽版)

R语言来自S语言&#xff0c;是S语言的一个变种。S语言、C语言、Unix系统都是贝尔实验室的研究成果。R 语言是一种解释型的面向数学理论研究工作者的语言&#xff0c;主要用于统计分析、绘图、数据挖掘。 R 语言自由软件&#xff0c;免费、开放源代码&#xff0c;支持各个主要计…

NLP入门——前馈词袋分类模型的搭建、训练与预测

模型的搭建 线性层 >>> import torch >>> from torch import nn >>> class DBG(nn.Module): ... def forward(self,x): ... print(x.size()) ... return x ... >>> tmod nn.Sequential(nn.Linear(3,4),DB…

面试-微服务篇

springcloud组件有哪些&#xff1f; eureka、ribbon负载均衡、feign、hystrix、zuul/gateway网关 nacos、ribbon、feign、sentinel、gateway 服务注册和发现是什么意思&#xff1f;springcloud如何实现服务注册发现&#xff1f; 微服务中必须要使用的组件&#xff0c;考察我们使…

【React】Ant Design -- Table分页功能实现

实现步骤 为Table组件指定pagination属性来展示分页效果在分页切换事件中获取到筛选表单中选中的数据使用当前页数据修改params参数依赖引起接口重新调用获取最新数据 const pageChange (page) > {// 拿到当前页参数 修改params 引起接口更新setParams({...params,page})…

【数据清洗中分段线性插值法原理】

数据清洗中分段线性插值法原理 一、什么是分段线性插值法&#xff1f;二、分段线性插值法的数学原理三、分段线性插值法的应用步骤1. 引入库2. 创建示例数据3. 应用分段线性插值法4. 可视化插值结果 一、什么是分段线性插值法&#xff1f; 分段线性插值法通过在已知数据点之间…

己内酰胺纯化除杂的最佳工艺

己内酰胺纯化除杂的最佳工艺包括结晶法、离子交换树脂法、精馏法和萃取法等&#xff0c;每种方法都有其特定的应用场景和优缺点。以下是对这些方法的详细介绍&#xff1a; 最佳工艺介绍 ● 结晶法&#xff1a;通过调节pH值&#xff0c;使己内酰胺在特定条件下结晶&#xff0…

CEPH 系统盘挂了,如何使用数据盘恢复

硬盘损坏是早晚的时&#xff0c;CEHP数据盘坏了&#xff0c;使用CEPH的基本都轻车熟路了&#xff0c;如果系统盘坏了呢&#xff1f;不知道的可能会采取整个系统盘全做的方式 前提条件&#xff1a;使用cephadm搭建集群 如果换服务器&#xff0c;请确保CEPH数据盘放到其它服务器上…

油猴Safari浏览器插件:Tampermonkey for Mac 下载

Tampermonkey 是一个强大的浏览器扩展&#xff0c;用于运行用户脚本&#xff0c;这些脚本可以自定义和增强网页的功能。它允许用户在网页上执行各种自动化任务&#xff0c;比如自动填写表单、移除广告、改变页面布局等。适用浏览器&#xff1a; Tampermonkey 适用于多数主流浏览…

【数智化人物展】数势科技创始人兼CEO黎科峰:数智化时代To B软件行业面临颠覆与重塑...

黎科峰 本文由数势科技创始人兼CEO黎科峰投递并参与由数据猿联合上海大数据联盟共同推出的《2024中国数智化转型升级先锋人物》榜单/奖项评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 2020年&#xff0c;对我而言&#xff0c;是职业生涯中的一个重大转折点。在全球新…

手机如何充当电脑摄像头,新手使用教程分享(新)

手机如何充当电脑摄像头&#xff1f;随着科技的发展&#xff0c;智能手机已经成为我们日常生活中不可或缺的一部分。手机的摄像头除了拍摄记录美好瞬间之外&#xff0c;其实还有个妙用&#xff0c;那就是充当电脑的摄像头。手机摄像头充当电脑摄像头使用的话&#xff0c;我们就…

FPGA基本资源介绍

文章目录 FPGA资源介绍1.可编程输入输出单元(IOB)2.可配置逻辑块(CLB)3.数字时钟管理模块(DCM)4.嵌入式块RAM(BLOCK RAM / BRAM)4.1其他ram 5.丰富的布线资源6.底层内嵌功能单元7.内嵌专用硬核软核、硬核、以及固核的概念 FPGA资源介绍 1.可编程输入输出单元(IOB) 可编程输入…

LangChain终极内幕指南,学会langchain就看它了

1.概述 在人工智能迅速演进的时代&#xff0c;诸如Open AI的ChatGPT和Google的Bard等大型语言模型(LLMs)正彻底改变我们与技术互动的方式。这些技术巨头和SaaS公司正在竞相利用LLMs的威力&#xff0c;创造更为智能和实用的应用程序。 然而&#xff0c;真正的变革并非仅仅停留…

赋能电子行业:三品PLM软件系统如何加速电子产品创新

在当今竞争激烈的市场中&#xff0c;企业如何通过技术创新和管理优化来提升自身的竞争力&#xff0c;已成为一个重要课题。电子行业快速发展的同时也遇到了不少的困难&#xff0c;使得企业效率低下、产能没能跟上时代的发展。而PLM系统的出现&#xff0c;给了电子行业新的希望。…

springboot学生档案信息管理系统-计算机毕业设计源码96509

目 录 第 1 章 引 言 1.1 选题背景 1.2 研究现状 1.3 论文结构安排 第 2 章 系统的需求分析 2.1 系统可行性分析 2.1.1 技术方面可行性分析 2.1.2 经济方面可行性分析 2.1.3 法律方面可行性分析 2.1.4 操作方面可行性分析 2.2 系统功能需求分析 2.3 系统性需求分析…