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>
相关推荐
想买Rolex和Supra的凯美瑞车主1 分钟前
Taro + Vite 开发中 fs.allow 配置问题分析与解决
前端
ruanCat3 分钟前
使用 vite 的 base 命令行参数来解决项目部署在 github page 的路径问题
前端·github
Codebee8 分钟前
使用Qoder 改造前端UI/UE升级改造实践:从传统界面到现代化体验的华丽蜕变
前端·人工智能
叫我詹躲躲12 分钟前
开发提速?Vue3模板隐藏技巧来了
前端·vue.js·ai编程
华仔啊12 分钟前
面试都被问懵了?CSS 的 flex:1 和 flex:auto 真不是一回事!90%的人都搞错了
前端·javascript
前端康师傅14 分钟前
JavaScript 函数详解
前端·javascript
金金金__17 分钟前
antd v5 support React is 16 ~ 18. see https://u.ant.design/v5-for-19 for...
前端
葡萄城技术团队18 分钟前
从基础到实战:一文吃透 JS Tuples 与 Records 的所有核心用法
javascript
会豪18 分钟前
工业仿真(simulation)--前端(二)-资源管理器
前端
数字冰雹30 分钟前
“图观”端渲染场景编辑器
人工智能·编辑器