vue2实现公式规则编辑校验弹窗功能

4 篇文章 0 订阅
2 篇文章 0 订阅

需求描述

    需要一个弹窗,弹窗内部需要能够进行公式规则的配置并进行公式规则合法性校验。

技术栈

  • vue2
  • element-ui

最终效果演示

公式规则功能效果演示

功能实现

逻辑拆分

    我将弹窗大致拆分成了三部分,具体如下:

代码目录结构

实现思路
光标实现

使用的是div块级元素+css3动画实现,核心代码如下:

// template
<div v-if="item.show" class="expression-blink-cursor" :key="item.id"></div>

// style
.expression-blink-cursor {
  width: 1px;
  height: 18px;
  border-left: 1px solid;
  margin: 2px;
  animation:cursorImg 1s infinite steps(1, start);
  @keyframes cursorImg {
    0%, 100% {
      opacity: 0;
    }
    50% {
      opacity: 1;
    }
  }
}
底部单个符号或字段结构设计

实现公式规则的编辑功能主要是通过操作数组实现的,所以对于弹窗底部的符号或字段设计为对象数组,便于后续数组操作。部分代码如下:

// 子组件template
<template>
  <div id="singleSymbol">
    <template v-for="item in symbolBtnList">
      <template v-if="item.code === 'formFields'">
        <el-cascader class="custom-cascader-class" v-model="selectedFormFieldsValue" :options="formFieldsOptions" :key="item.id" :placeholder="item.label" @change="selectFormFieldsChange">
          <template slot-scope="{ node, data }">
            <span>{{ data.label }}</span>
            <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
          </template>
        </el-cascader>
      </template>
      <template v-else>
        <el-button class="symbol-btn" :key="item.code" @click="singleSymbolClick(item)">
          {{item.label}}
        </el-button>
      </template>
    </template>
  </div>
</template>

// js
import {nanoid} from 'nanoid'
export const singleSymbolBtnList = [
  {
    label: '表单字段',
    code: 'formFields',
    templateName: 'formFields',
    type: 'customTemplate',
    id: nanoid()
  },
  {
    label: '+',
    code: 'plusSign',
    type: 'bottomSingleSign',
    id: nanoid()
  },
  ...
]
export const formFieldsOptions = [
  {
    value: 'testForm1',
    label: '测试表单1',
    children: [
      {
        value: 'formFields1',
        uuid: '',
        label: '表单字段1'
      },
      {
        value: 'formFields2',
        uuid: '',
        label: '表单字段2'
      },
      {
        value: 'formFields3',
        uuid: '',
        label: '表单字段3'
      },
      {
        value: 'formFields4',
        uuid: '',
        label: '表单字段4'
      }
    ]
  },
  {
    value: 'testForm2',
    label: '测试表单2',
    children: [
      {
        value: 'formFields1',
        uuid: '',
        label: '表单字段1'
      },
      {
        value: 'formFields2',
        uuid: '',
        label: '表单字段2'
      },
      {
        value: 'formFields3',
        uuid: '',
        label: '表单字段3'
      },
      {
        value: 'formFields4',
        uuid: '',
        label: '表单字段4'
      }
    ]
  }
]

// 父组件template
<template>
  <div id="formulaRulesMain" class="formula-rules-main">
    <!-- 公式规则配置区域 -->
    <template v-for="(item, index) in formulaRulesArray">
      <template v-if="item.type === 'customTemplate'">
        <template v-if="item.templateName === 'blinkCursor'">
          <div v-if="item.show" class="expression-blink-cursor" :key="item.id"></div>
        </template>
        <template v-if="item.templateName === 'staticValue'">
          <el-input type="text" v-focus class="custom-input" :key="item.id" v-model="item.bindValue" @focus="inputFocusOrBlur('focus')" @blur="inputFocusOrBlur('blur')"></el-input>
        </template>
        <template v-if="item.templateName === 'booleanValue'">
          <div class="custom-boolean-value" :key="item.id">{{item.label}}</div>
        </template>
        <template v-if="item.templateName === 'formFields'">
          <div class="custom-form-fields" :key="item.id">{{item.label}}</div>
        </template>
        <template v-if="item.templateName === 'emptyDiv'">
          <div class="custom-form-empty-div" :key="item.id" @click="emptyDivClick(index)"></div>
        </template>
      </template>
      <template v-else>
        <template v-if="item.type === 'bottomSingleSign'">
          <div :key="item.id">
            <span class="expression-item">{{item.label}}</span>
          </div>
        </template>
      </template>
    </template>
  </div>
</template>
监听键盘事件&处理光标

    在mounted挂载键盘监听事件,并对不同按键操作做出不同逻辑处理(对数组进行操作),核心代码如下:

  mounted () {
    this.keyDown()
  },
  methods: {
    // 监听键盘
    keyDown () {
      window.onkeyup = (e) => {
        // 事件对象兼容
        // console.log('查看对象兼容', e, e.keyCode)
        let e1 = e || event || window.event
        // 键盘按键判断:左箭头-37;上箭头-38;右箭头-39;下箭头-40;删除键-8
        // 左
        if (e1 && e1.keyCode === 37) {
          // 按下左箭头
          this.handleKeydownEvent('left')
        } else if (e1 && e1.keyCode === 39) {
          // 按下右箭头
          this.handleKeydownEvent('right')
        } else if (e1 && e1.keyCode === 8) {
          // 按下删除键
          this.handleKeydownEvent('delete')
        }
      }
    },
    // 处理键盘事件
    handleKeydownEvent (type) {
      let arr = this.formulaRulesArray
      if (type === 'left') {
        if (this.cursorIndex !== 0) arr[this.cursorIndex] = arr.splice(this.cursorIndex - 2, 1, arr[this.cursorIndex])[0];
      } else if (type === 'right') {
        if (this.cursorIndex !== arr.length - 1) arr[this.cursorIndex] = arr.splice(this.cursorIndex + 2, 1, arr[this.cursorIndex])[0];
      } else if (type === 'delete') {
        if (this.cursorIndex !== 0) arr.splice(this.cursorIndex - 2, 2)
      }
    },
  }
公式规则校验

    通过上述步骤 基本的编辑功能已经实现,只差一步校验就完成所需功能了。
    最开始考虑的是通过正则去判断公式规则的合法性,但是整不出来😊 所以换了另外一种思路。采用关键字替换然后使用eval函数执行调用自定义函数看是否能够成功执行。

  • 首先可以校验一下左右括号个数是否匹配 比较简单
  • 检验完括号后 如果匹配 调用eval函数执行公式规则字符串。
  • js函数能够支持多个传参 省参执行,js函数会通过实参(arguments)对象接收,无论调用函数时传递了多少个参数,都会被arguments接收,利用这一点就可以替换完关键字后直接调用自定义函数通过js引擎去帮我们“校验”公式规则的合法性😄。
    核心代码如下:
methods: {
    // 处理公式规则结构类型
    handleFormulaArray (formulaArr) {
      let tempArr = Array.from(formulaArr, ({label}) => label)
      let finalArr = []
      formulaArr.forEach(item => {
        let each = item.label
        switch (item.label) {
          case 'SUM':
            each = 'this.sum'
            break;
          case 'SUB':
            each = 'this.sub'
            break
          case '静态值':
            each = item.bindValue
            break
          case 'TRUE':
            each = true
            break
          case 'FALSE':
            each = false
            break
          case 'IF':
            each = 'this.judgeIf'
            break
          case 'INT1':
            each = 'this.int1'
            break
          case 'INT2':
            each = 'this.int2'
            break
          case 'INT3':
            each = 'this.int3'
            break
        }
        if (item.code === 'formFields') {
          // TODO 通过uuid获取到表单对应字段的值,item有完整数据(uuid...)
          each = 5
        }
        finalArr.push(each)
      })
      console.log('查看处理后的数组: ', finalArr, finalArr.join(''))
      try {
        if (finalArr.length === 0) {
          this.$message({
            message: '校验失败,公式规则不可为空!',
            type: 'warning'
          });
          return
        }

        this.showResultVisible = true
        this.resltFormulaStr = finalArr.join('')
        const str = finalArr.join('')
        if (!eval(str)) throw new Error()
        this.showResultVisible = true
        this.resltFormulaStr = finalArr.join('')
        this.resultFormulaValue = eval(str)
        // TODO 预留存储公式规则逻辑 包括公式规则完整信息formulaArr及转化后的字符串str、校验成功后进行字段赋值操作及关闭弹窗
        // this.dialogVisible = false
      } catch (error) {
        this.$message({
          message: '校验失败,请检查公式规则!',
          type: 'warning'
        });
        this.resultFormulaValue = null
      }
    }
}

// mixin
export default {
  methods: {
    // -------------------------------------------  数学函数  -----------------------------------------------
    // 求和
    sum () {
      try {
        if (!arguments[0] || arguments.length < 2) throw new Error()
        let result = 0
        for (let i = 0; i < arguments.length; i++) {
          result = result + arguments[i]
        }
        return result
      } catch (error) {
        this.$message({
          message: '校验失败,请检查SUM公式规则!',
          type: 'warning'
        });
      }
    },
    // 相减
    sub () {
      try {
        if (!arguments[0] || arguments.length < 2) throw new Error()
        let result = arguments[0]
        for (let i = 1; i < arguments.length; i++) {
          result = result - arguments[i]
        }
        return result
      } catch (error) {
        this.$message({
          message: '校验失败,请检查SUB公式规则!',
          type: 'warning'
        });
      }
    },
    // if判断函数
    judgeIf (judgeCode, trueCode, falseCode) {
      try {
        if (arguments.length !== 3) throw new Error()
        return judgeCode ? trueCode : falseCode
      } catch (error) {
        this.$message({
          message: '校验失败,请检查IF公式规则!',
          type: 'warning'
        });
      }
    },
    // 向下取整
    int1 () {
      try {
        if (!arguments[0]) throw new Error()
        return parseInt(arguments[0])
      } catch (error) {
        this.$message({
          message: '校验失败,请检查向下取整函数INT1公式规则!',
          type: 'warning'
        });
      }
    },
    // 向上取整
    int2 () {
      try {
        if (!arguments[0]) throw new Error()
        return parseInt(arguments[0]) + 1
      } catch (error) {
        this.$message({
          message: '校验失败,请检查向上取整函数INT2公式规则!',
          type: 'warning'
        });
      }
    },
    // 四舍五入取整
    int3 () {
      try {
        if (!arguments[0]) throw new Error()
        return Math.round(arguments[0])
      } catch (error) {
        this.$message({
          message: '校验失败,请检查四舍五入取整函数INT3公式规则!',
          type: 'warning'
        });
      }
    }
  }
}

总结

    遇到较难的问题可以变通一下去考虑解决方案(正则太难了😭), 然后一步步去验证,此需求实现代码是我的一个小demo,不完善的地方还有很多,望大佬们能够指正,当然,基本功能是完全OK的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这都能重名?!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值