vue 一个公式 编辑器 组件

时间:2025-02-16 19:00:24

修改大佬的代码,原来是可以修改参数的,我这边要求不能手动修改参数 

增加删除功能,自定义添加参数,

反向解析公式没整好,有大佬会的,可以修改一下,联系一下我,不胜感激

原文  用vue手写一个公式组件 - SegmentFault 思否

后来进行了改进 功能进行了简化 ,可详见vue公式编辑器 加强版(简化版)_你好,我叫A某人的博客-****博客_vue公式编辑器

<template>
    <div >
        <h1>formulaPage</h1>
        <p>{{ formulaStr }}</p>
        <div class="btnGroup">
            <!-- <button @click="mouseRange($event)">获取光标</button> -->
            <button @click="getFormula">获取公式</button>
            <button
                @click="
                    parsingFormula(
                        '#字段1#+plus(#字段1#+#字段3#)*abs(#字段4#/#字段2#)'
                    )
                "
            >
                反向解析公式
            </button>
        </div>
        <!-- <div class="tab">
            <div class="tit">添加公式</div>
            <ul>
                <li @click="addItem($event, 2)">plus()</li>
                <li @click="addItem($event, 2)">abs()</li>
            </ul>
        </div> -->
        <div class="tab">
            <div class="tit">添加字段</div>
            <ul>
                <li @click="addItem($event, 1)">字段1</li>
                <li @click="addItem($event, 1)">字段2</li>
                <li @click="addItem($event, 1)">字段3</li>
                <li @click="addItem($event, 1)">字段4</li>
            </ul>
        </div>
        <div class="tab">
            <div class="tit">数字</div>
            <ul>
                <li @click="addItem($event, 3)">1</li>
                <li @click="addItem($event, 3)">2</li>
                <li @click="addItem($event, 3)">3</li>
                <li @click="addItem($event, 3)">4</li>
                <li @click="addItem($event, 3)">5</li>
                <li @click="addItem($event, 3)">6</li>
                <li @click="addItem($event, 3)">7</li>
                <li @click="addItem($event, 3)">8</li>
                <li @click="addItem($event, 3)">9</li>
                <li @click="addItem($event, 3)">0</li>
            </ul>
        </div>
        <div class="tab">
            <div class="tit">数学运算符</div>
            <ul>
                <li @click="addItem($event, 3)">+</li>
                <li @click="addItem($event, 3)">-</li>
                <li @click="addItem($event, 3)">*</li>
                <li @click="addItem($event, 3)">/</li>
                <li @click="addItem($event, 3)">(</li>
                <li @click="addItem($event, 3)">)</li>

                <li @click="remove()">del</li>
            </ul>
        </div>
        <!-- 公式编辑区域 -->
        <div
            class="formulaView"
            
            ref="formulaView"
            @click="recordPosition"
            @keyup="editEnter($event)"
            @copy="copy($event)"
            @paste="paste($event)"
            contentEditable="false "
        ></div>
    </div>
</template>
<script>
export default {
    name: "formulaPage",
    data: function () {
        return {
            // 公式字符串
            formulaStr: "",
            // 公式编辑器最后光标位置
            formulaLastRange: null,
        };
    },
    methods: {
        // 获取公式
        getFormula: function () {
            var nodes = this.$;
            var str = "";
            for (let i = 0; i < ; i++) {
                var el = nodes[i];
                if ( == "SPAN") {
                    // (el);
                    str += "#" + () + "#";
                } else if ( == "B") {
                    // (el);
                    str += () ;
                }else {
                    // ();
                    str +=  ? () : "";
                }
            }
            // (str);
             = str;
        },
        // 点选时记录光标位置
        recordPosition: function () {
            // 保存最后光标点
             = ().getRangeAt(0);
        },
        // 添加字段 type 1 字段  2 公式 3 数字或者数字运算符
        addItem: function (e, type) {
            // 当前元素所有子节点
            var nodes = this.$;
            // 当前子元素偏移量
            var offset =
                 && ;
            // 当前光标后的元素
            var nextEl =
                 && ;

            // 创建节点片段
            var fd = ();

            // 创建字段节点  空白间隔节点  公式节点
            var spanEl = ("span");
            ("contentEditable", false);
            // 标识为新添加元素 用于定位光标
            ("new-el", true);
             = ;

            var spanEl2 = ("b");
            // 标识为新添加元素 用于定位光标
            ("new-el", true);
             = ;

            var empty = (" ");
            var formulaEl = (
                " " +  + " "
            );

            // 区分文本节点替换 还是父节点插入
            if (nextEl &&  != "formulaView") {
                // 获取文本节点内容
                var content = ;

                // 添加前段文本
                (
                    ((0, offset) + " ")
                );
                (type == 1 ? spanEl : formulaEl);
                // 添加后段文本
                (
                    (" " + (offset))
                );
                // 替换节点
                this.$(fd, nextEl);
                // 添加前段文本
                // (empty);
                // (type == 1 ? spanEl : spanEl2);
                // (empty);

                // // 如果有偏移元素且不是最后节点  中间插入节点  最后添加节点
                // if ( &&  > offset) {
                //     this.$(
                //         fd,
                //         nextEl &&  != "formulaView"
                //             ? nextEl
                //             : nodes[offset]
                //     );
                // } else {
                //     this.$(fd);
                // }
            } else {
                // 添加前段文本
                (empty);
                (type == 1 ? spanEl : spanEl2);
                (empty);

                // 如果有偏移元素且不是最后节点  中间插入节点  最后添加节点
                if ( &&  > offset) {
                    this.$(
                        fd,
                        nextEl &&  != "formulaView"
                            ? nextEl
                            : nodes[offset]
                    );
                } else {
                    this.$(fd);
                }
            }

            // 遍历光标偏移数据 删除标志
            var elOffSet = 0;
            for (let i = 0; i < ; i++) {
                let el = nodes[i];
                // (el, == 'SPAN'&&('new-el'));
                if ( == "SPAN" && ("new-el")) {
                    elOffSet = i;
                    ("new-el");
                }
                if ( == "B" && ("new-el")) {
                    elOffSet = i;
                    ("new-el");
                }
            }

            // 创建新的光标对象
            var range = ();
            // 光标对象的范围界定
            (
                type == 1 ? this.$ : formulaEl
            );
            // 光标位置定位
            (
                type == 1
                    ? this.$
                    : type == 2
                    ? formulaEl
                    : formulaEl,
                type == 1
                    ? elOffSet + 1
                    : type == 2
                    ?  - 2
                    :  - 1
            );

            // 使光标开始和光标结束重叠
            (true);
            // 清除选定对象的所有光标对象
            ().removeAllRanges();
            // 插入新的光标对象
            ().addRange(range);

            // 保存新光标
            // ();
        },
        remove() {
            var formulaView = ("formulaView");
            (formulaView);
            ();
        },
        // 复制
        copy: function (e) {
            // 选中复制内容
            ();
            //
            var selContent = ().toString().split("\n")[0];
            // 替换选中内容
            ("text/plain", selContent);
        },
        // 输入回车
        editEnter: function (e) {
            // (e);
            ();

            // return '<br/>';
            // return
            if ( == 13) {
                // 获取标签内容 并把多个换行替换成1个
                var content = this.$(
                    /(<div><br><\/div>){2,2}/g,
                    "<div><br></div>"
                );

                // debugger;

                // 记录是否第一行回车
                var divCount = this.$("div");
                // var tE = this.$('div');
                // (this.$);
                // (this.$("div"));
                // 获取当前元素内所有子节点
                var childNodes = this.$;
                // 记录当前光标子节点位置
                var rangeIndex = 0;
                for (let i = 0; i < ; i++) {
                    var one = childNodes[i];
                    if ( == "DIV") {
                        rangeIndex = i;
                    }
                }
                // (rangeIndex);
                // debugger;
                // (content);

                // 如果有替换则进行光标复位
                if ( >= 1) {
                    // 替换回车插入的div标签
                    content = (
                        /<div>|<\/div>/g,
                        function (word) {
                            // (word);
                            if (word == "<div>") {
                                // 如果是第一行不在替换br
                                return  > 1 ? " " : " <br>";
                            } else if (word == "</div>") {
                                return " ";
                            }
                        }
                    );
                    // 更新替换内容,光标复位
                    this.$ = content;
                    // 创建新的光标对象
                    var range = ();
                    // 光标对象的范围界定为新建的表情节点
                    (this.$);
                    // 光标位置定位在表情节点的最大长度
                    (
                        this.$,
                        rangeIndex + ( > 1 ? 0 : 1)
                    );
                    // 使光标开始和光标结束重叠
                    (true);
                    // 清除选定对象的所有光标对象
                    ().removeAllRanges();
                    // 插入新的光标对象
                    ().addRange(range);
                }
            }
            // 保存最后光标点
             = ().getRangeAt(0);
        },
        // 获取粘贴事件
        paste: function (e) {
            ();
            // var txt=();
            // (e, ());
            return "";
        },
        // 公式反向解析
        parsingFormula: function (formulaStr) {
            // 渲染视口
            var view = this.$;
            // 反向解析公式
            var str = (/#(.+?)#/g, function (word, $1) {
                // (word,$1);
                return "<span contentEditable='false'>" + $1 + "</span>";
            });

            // (str,);
             = str;
            // this.$(fd);

            // 创建新的光标对象
            var range = ();
            // 光标对象的范围界定为新建的表情节点
            (view);
            // 光标位置定位在表情节点的最大长度
            (view, );

            // 使光标开始和光标结束重叠
            (true);
            // 清除选定对象的所有光标对象
            ().removeAllRanges();
            // 插入新的光标对象
            ().addRange(range);

            // 保存新光标
            // ();
        },
    },
};
</script>
<style lang="less">
#formulaPage {
    > .tab {
        > ul {
            &:after {
                content: "";
                display: table;
                clear: both;
            }
            > li {
                margin-right: 20px;
                float: left;
                padding: 0 10px;
                height: 25px;
                line-height: 25px;
                border-radius: 5px;
                border: 1px solid #000;
            }
        }
    }
    > .formulaView {
        margin-top: 20px;
        min-height: 100px;
        width: 300px;
        padding: 5px;
        border: 2px solid #000;
        resize: both;
        overflow: auto;
        line-height: 25px;
        span {
            user-select: none;
            display: inline-block;
            margin: 0 3px;
            height: 20px;
            line-height: 20px;
            letter-spacing: 2px;
            background: #aaa;
            border-radius: 3px;
            white-space: nowrap;
            color: red;
            &:first-child {
                margin-left: 0;
            }
        }
    }
}
</style>