vue3实现规则编辑器

组件用于创建和编辑复杂的条件规则,支持添加、删除条件和子条件,以及选择不同的条件类型。

可实现json数据和页面显示的转换。

代码实现 :

index.vue:

<template>
  <div class="allany-container">
    <div class="control-bar">
      <el-select v-model="pipe.condition" style="width: 200px; margin-right: 16px;" :disabled="disabled">
        <el-option label="满足以下所有条件" value="all"/>
        <el-option label="满足以下任一条件" value="any"/>
        <el-option label="不包含以下条件" value="not"/>
      </el-select>
      <el-button v-if="!disabled" type="primary" @click="addCondition('condition')">添加条件</el-button>
      <el-button v-if="!disabled" type="success" @click="addCondition('all_any')">添加子条件</el-button>
      <el-button v-if=" !disabled" type="danger" :icon="Delete" circle @click="delSelf"/>
    </div>
    <div class="conditions-wrapper">
      <!--   侧边显示条   -->
      <div :class="`conditions-wrapper-${pipe.condition}`"></div>
      <div class="conditions-wrapper--conditions">
        <div v-for="(child, idx) in pipe.children" :key="idx" class="conditions-wrapper--condition">
          <biz-rule-all-any v-if="child.type === 'all_any'" :pipe="child" :attrOptions="attrOptions"
                            :disabled="disabled" @delRule="handleDelRule(idx,child)"/>
          <biz-rule-condition v-else-if="child.type === 'condition'" :pipe="child" :attrOptions="attrOptions"
                              :disabled="disabled" @delRule="handleDelCondition(idx,child)"/>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import {ref, watch} from 'vue';
import BizRuleCondition from './BizRuleCondition.vue';
import {Delete} from "@element-plus/icons-vue";
import {deepClone} from "@/utils.js";

defineOptions({
  name: 'BizRuleAllAny'
})

const emit = defineEmits(['delRule']);

const props = defineProps({
  pipe: {
    type: Object,
    default: () => ({
      type: 'all_any',
      condition: 'all',
      children: [],
    })
  },
  attrOptions: {
    type: Array,
    default: () => {
      return []
    }
  },
  disabled: {
    type: Boolean,
    default: false
  }
});
const pipe = ref(props.pipe);


function addCondition(type) {
  if (type === 'all_any') {
    pipe.value.children.push({
      type: 'all_any',
      condition: 'all',
      children: [],
    });
  } else if (type === 'condition') {
    pipe.value.children.push({
      type: 'condition',
      name: '',
      operator: 'eq',
      value: '',
      val_type: 'string',
    });
  }
}

function delSelf() {
 emit('delRule')
}

// 删除条件组
const handleDelRule = (idx,child) => {
  pipe.value.children.splice(idx, 1);
}

// 删除条件组中的子数据
const handleDelCondition = (idx,child) => {
  pipe.value.children.splice(idx, 1);
}

</script>

<style scoped lang="scss">

.allany-container {

  .control-bar {
    display: flex;
    flex-direction: row;
  }

  .conditions-wrapper {
    display: flex;
    flex-direction: row;
  }


  .conditions-wrapper-all {
    width: 4px;
    margin: 5px 20px 0;
    border-radius: 5px;
    transition: background-color 400ms;
    background-color: #67C23A;

    &:hover {
      background-color: #529b2e;
    }
  }


  .conditions-wrapper-any {
    width: 4px;
    margin: 5px 20px 0;
    border-radius: 5px;
    transition: background-color 400ms;
    background-color: #E6A23C;

    &:hover {
      background-color: #b88230;
    }
  }


  .conditions-wrapper-not {
    width: 4px;
    margin: 5px 20px 0;
    border-radius: 5px;
    transition: background-color 400ms;
    background-color: rgb(245, 245, 245);

    &:hover {
      background-color: rgb(144, 147, 153);
    }
  }


  .conditions-wrapper--conditions {
    display: flex;
    flex-direction: column;
  }

  .conditions-wrapper--condition {
    padding-top: 15px;
  }
}

</style>

BizRuleCondition.vue:

<template>
  <div class="bizrulecondition-container">
    <el-select v-model="pipe.name" placeholder="字段名称" style="width: 150px; margin-right: 16px;" clearable :disabled="disabled">
      <el-option v-for="item in attrOptions" :key="item.value" :label="item.label" :value="item.value"/>
    </el-select>
    <el-select v-model="pipe.operator" style="width: 90px; margin-right: 16px;" :disabled="disabled">
      <el-option label="==" value="eq"/>
      <el-option label="!=" value="neq"/>
      <el-option label="<" value="lt"/>
      <el-option label=">" value="gt"/>
      <el-option label="<=" value="lte"/>
      <el-option label=">=" value="gte"/>
      <el-option label="in" value="in"/>
      <el-option label="not in" value="not_in"/>
    </el-select>
    <el-badge
        :value="pipe.val_type === 'string' ? '字符串' : '数字'"
        :type="pipe.val_type === 'string' ? 'info' : 'success'"
        @click.native="switchVarType($event, pipe)"
        style="margin-right: 50px;"
        :disabled="disabled"
    >
      <el-input
          v-model="pipe.value"
          placeholder="字段值"
          style="width: 150px;"
          clearable
          :disabled="disabled"
      />
    </el-badge>
    <el-button type="danger" :icon="Delete" circle @click="delSelf" :disabled="disabled"/>

  </div>
</template>

<script setup>
import {ref, defineProps, watch} from 'vue';
import {Delete} from "@element-plus/icons-vue";
import {deepClone} from "@/utils.js";

const emit = defineEmits(['delRule']);


const props = defineProps({
  pipe: Object,
  attrOptions:Array,
  disabled:Boolean
});
const pipe = ref({});

watch(() => props.pipe, (newData) => {
  if (!newData) return
  pipe.value = deepClone(newData)
}, {
  deep: true,
  immediate: true
})


function switchVarType(e, kv) {
  if (String(e.target.tagName).toUpperCase() === 'SUP') {
    kv.val_type = kv.val_type === 'number' ? 'string' : 'number';
  }
}

function delSelf() {
  emit('delRule')
}
</script>

<style lang="scss" scoped>
.bizrulecondition-container {
  display: flex;
  flex-direction: row;

  .el-badge__content {
    transition: 400ms;
    user-select: none;
  }

  .el-badge__content:hover {
    cursor: pointer;
  }
}
</style>

BizRuleAdapter.js:

<template>
  <div class="bizrulecondition-container">
    <el-select v-model="pipe.name" placeholder="字段名称" style="width: 150px; margin-right: 16px;" clearable :disabled="disabled">
      <el-option v-for="item in attrOptions" :key="item.value" :label="item.label" :value="item.value"/>
    </el-select>
    <el-select v-model="pipe.operator" style="width: 90px; margin-right: 16px;" :disabled="disabled">
      <el-option label="==" value="eq"/>
      <el-option label="!=" value="neq"/>
      <el-option label="<" value="lt"/>
      <el-option label=">" value="gt"/>
      <el-option label="<=" value="lte"/>
      <el-option label=">=" value="gte"/>
      <el-option label="in" value="in"/>
      <el-option label="not in" value="not_in"/>
    </el-select>
    <el-badge
        :value="pipe.val_type === 'string' ? '字符串' : '数字'"
        :type="pipe.val_type === 'string' ? 'info' : 'success'"
        @click.native="switchVarType($event, pipe)"
        style="margin-right: 50px;"
        :disabled="disabled"
    >
      <el-input
          v-model="pipe.value"
          placeholder="字段值"
          style="width: 150px;"
          clearable
          :disabled="disabled"
      />
    </el-badge>
    <el-button type="danger" :icon="Delete" circle @click="delSelf" :disabled="disabled"/>

  </div>
</template>

<script setup>
import {ref, defineProps, watch} from 'vue';
import {Delete} from "@element-plus/icons-vue";
import {deepClone} from "@/utils.js";

const emit = defineEmits(['delRule']);


const props = defineProps({
  pipe: Object,
  attrOptions:Array,
  disabled:Boolean
});
const pipe = ref({});

watch(() => props.pipe, (newData) => {
  if (!newData) return
  pipe.value = deepClone(newData)
}, {
  deep: true,
  immediate: true
})


function switchVarType(e, kv) {
  if (String(e.target.tagName).toUpperCase() === 'SUP') {
    kv.val_type = kv.val_type === 'number' ? 'string' : 'number';
  }
}

function delSelf() {
  emit('delRule')
}
</script>

<style lang="scss" scoped>
.bizrulecondition-container {
  display: flex;
  flex-direction: row;

  .el-badge__content {
    transition: 400ms;
    user-select: none;
  }

  .el-badge__content:hover {
    cursor: pointer;
  }
}
</style>
相关推荐
小曲曲28 分钟前
接口上传视频和oss直传视频到阿里云组件
javascript·阿里云·音视频
学不会•2 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS3 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
活宝小娜4 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点4 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow4 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o4 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic5 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā5 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年6 小时前
react中useMemo的使用场景
前端·react.js·前端框架