示例

一个基于vue实现数据统计公式的基本功能。
新建一个 formula.vue 组件,
应用 <formula ref="formulaPage" :data-list="dataList" ></formula>

<template>    <div id="formulaPage">        <!-- 公式编辑区域 -->        <div                class="formulaView"                id="formulaView"                ref="formulaView"                @click.stop="recordPosition()"        >            <div class="content-item" v-for="(item,index) in formulaList" :key="index" @click.stop="recordPosition(index)" >                <div class="num" v-if="item.type == 'num'" >                    &zwj;{{item.value}}                </div>                <div class="plain" v-else-if="item.type == 'plain'" >                    &zwj;{{item.value}}                </div>                <div class="obj" v-else-if="item.type == 'obj'" >                    &zwj;{{item.value}}                </div>                <!--光标-->                <div class="cursor" v-if="item.cursor" ></div>            </div>        </div>        <div class="tab mt_10 flex-lr">            <div class="">                <el-select @change="e=>{addItem(e,'obj',)}"                           style="width: 120px"                           v-model="dataId" placeholder="抉择指标" >                    <el-option v-for="item in dataList"                               :label="item.name" :value="item.id"                               :key="item.id" ></el-option>                </el-select>                <el-select @change="e=>{addItem(e,'plain',)}" v-model="operatorId"                           placeholder="抉择数学运算符"                           style="width: 120px"                           class="ml_20"                >                    <el-option v-for="item in operatorList" :label="item.name" :value="item.id" :key="item.id" >                    </el-option>                </el-select>            </div>            <div class="">                <span class="mr_10 pointer theme-col" @click="clearAll()" > 革除全副</span>            </div>        </div>    </div></template><script>    /**     * dataList 须要抉择数据的汇合     * defaultList 初始值的汇合     * @change 比变更后回传的数据     * **/    export default {        name: '',        props:{            dataList:{                type:Array,                default() {                    return [];                },            },            defaultList:{                type:Array,                default() {                    return [];                },            },        },        data: function () {            return {                // 公式字符串                formulaStr:'',                dataId:'',                operatorId:'',                formulaList:[],                //运算符                operatorList:[                    {                        name:'+',                        id:'+'                    },                    {                        name:'-',                        id:'-'                    },                    {                        name:'*',                        id:'*'                    },                    {                        name:'/',                        id:'/'                    },                    {                        name:'()',                        id:'()'                    },                ]            }        },        watch:{            formulaList(val){                this.$emit('change',val)            }        },        created() {            //监听鼠标事件            this.$nextTick(function () {                document.addEventListener("keydown", this.keydown, false);                document.addEventListener('click',this.formulaBlur);            });        },        destroyed () {            //移除监听事件            document.removeEventListener("keydown", this.keydown, false);            document.removeEventListener('click',this.formulaBlur);        },        methods: {            // 获取            getFormula: function(){            },            // 点选时记录光标地位            recordPosition(index) {                if(this.formulaList && this.formulaList.length >0){                    this.formulaList = this.formulaList.map((item,itemIndex)=>{                        item.cursor = false;                        if(index > -1 && index == itemIndex){                            item.cursor = true;                        }else if((index!==0 && !index) && itemIndex == (this.formulaList.length -1)){                            item.cursor = true;                        }                        return item                    });                }else {                    this.formulaList = [                        {                            cursor:true,                            type:'placeholder',                            value:'',                        }                    ]                }                // this.$forceUpdate();            },            //失去焦点            formulaBlur(e){                this.formulaList = this.formulaList?.map(item=>{                    item.cursor = false;                    return item                })            },            /**             * @returns {addItem<*, void, *>}             * 增加字段             * type obj 字段  num 数字 plain符号             * place 是否批改光标地位             */            addItem: function (val, type,place = true) {                if(!val) return false;                let that = this;                //插入括号                if(type == 'plain' && val == '()'){                    val = '(';                    setTimeout(function () {                        that.addItem(')',type,false)                    },50)                }                let obj={},data = {                    value:'',                    key:val,                    type:type,                };                if(type == 'obj'){                    //获取数据 为 value 赋值                    obj = this.dataList?.find(item=>item.id == val);                    data.value = obj.name;                }else {                    data.value = val;                }                if(this.formulaList && this.formulaList.length>0){                    const length = this.formulaList.length;                    for (let i = 0; i < length; i++) {                        //查找光标地位 如果光标地位为空 则在最初增加                        if(this.formulaList[i].cursor){                            this.formulaList.splice(i+1,0,data);                            place && this.recordPosition(i+1);                            break;                        }else if(i === (this.formulaList.length - 1)){                            this.formulaList.push(data)                            this.recordPosition(this.formulaList.length - 1);                        }                    }                }else {                    if(!this.formulaList){                        this.formulaList = [];                    }                    this.formulaList.push(data);                    this.recordPosition(this.formulaList.length - 1);                }            },            //革除全副            clearAll(){                this.formulaList = [];                let that = this;                setTimeout(function () {                    that.recordPosition();                },100);            },            //删除            deleteItem(type){                let arr = JSON.parse(JSON.stringify(this.formulaList)),index = null;                const length = arr?.length;                for (let i = 0; i < length; i++) {                    if(arr[i].cursor && arr[i].key){                        index = i;                        if(type == 'del'){                            index = i + 1;                        }                        if(index > -1){                            this.formulaList.splice(index,1);                            if(type == 'del') {                            }else {                                this.recordPosition(index - 1);                            }                        }                        break;                    }                }            },            // 键盘输入            keydown(e){                //禁止输出                // 检测光标是否存在                let index,cursorData = this.formulaList?.find((item,itemIndex)=>{                    if(item.cursor){                        index = itemIndex                    }                    return item.cursor                });                if(!cursorData){                    return false;                }                //左右挪动键管制光标地位                if (e && [37,39].includes(e.keyCode)){                    if(e.keyCode == 37){index = index - 1;}else {index = index + 1;}                    if(index > -1 && index < this.formulaList.length){                        this.recordPosition(index);                    }                }else if (e && e.keyCode == 8){                    //Backspace 键 删除后面的值                    this.deleteItem();                }else if (e && [107,109,106,111].includes(e.keyCode)){                    //运算符列表                    this.addItem(e.key,'plain')                }else if (e && e.shiftKey && [48,57].includes(e.keyCode) ){                    //括号                   if( e.keyCode == 48) e.key = ')';                   if( e.keyCode == 57) e.key = '(';                    this.addItem(e.key,'plain')                }else if (e && e.keyCode == 46){                    //delete键删除光标前面的值                    this.deleteItem('del');                }else {                    document.returnValue = false;                    var tt=/^([1-9]{1}[0-9]{0,7})$/;//能输出负数                    if(tt.test(e.key)){                        //输出为数字 插入数字                        this.addItem(e.key,'num')                    }                }            },            /**             * 公式转为字符串             * 格局 [id]符号数字             * **/            parsingFormula: function(formulaStr){                let str = '',arr = [];                arr = this.formulaList?.map(item=>{                    let val = item.key;                    if(val){                        if(item.type == 'obj'){                            val = '['+val+']'                        }                        str = str+val;                    }                    return val                });                return str            },            /**             * 格局效验             * */            formatValidation(){                let objData = null;                let arr = this.formulaList?.filter(item=>{                    if(item.type == 'obj'){                        objData = item;                    }                    return item.key;                }),data = {type:true,mag:''};               if(!objData){                   data.mag = '至多增加一个指标';               }else {                   for (let i = 0; i < arr.length; i++) {                       if(i < arr.length-1){                           //判断以后类型                           if(arr[i].type == 'obj' && arr[i+1].type =='plain' ){                               //类型为obj时 后一个 需以 符号结尾                               data.mag = '指标后缀';                           }                       }                   }               }               if(data.mag){                   data.type = false;               }               return data;            },        }    }</script><style lang="scss">    #formulaPage {        .formulaView{            padding: 3px 4px;            width: 100%;            height: 120px;            border: 1px solid #eee;            line-height: 1.3;            font-size: 12px;            overflow-y: scroll;            .content-item{                position: relative;                height: 16px;                cursor: text;                user-select: none;                display: flex;                align-items: center;                float: left;                .cursor{                    height: 13px;                    width: 1px;                    background: #333;                    animation:defaultCursor 1s steps(2) infinite;                    position: absolute;                    right: 0;                }                .obj {                    padding: 0 5px;                    margin: 0 1px;                    background: #f1f1f1;                    border-radius: 3px;                }                .num{                    color: #000;                    background: #fff;                    padding: 0 1px 0 0;                }            }        }    }    @keyframes defaultCursor {        0% {            opacity: 1;        }        100% {            opacity: 0;        }    }</style>