CodeMirror 创建标签计算编辑器

news2024/11/18 18:39:47

         在日常开发中对于一些数据计算场景可能会遇到标签计算的需求,下面关于如何使用CodeMirror实现标签计算编辑功能。

1,结果图

2,主体代码逻辑

大家只需要复制粘贴主要codeMirror使用逻辑即可


<template>
  <el-dialog
    ref="dialogRef"
    :model-value="visible"
    width="800px"
    :close-on-press-escape="false"
    destroy-on-close
    append-to-body
    @close="
      () => {
        $emit('update:visible', false);
      }
    "
  >
    <template #title>
      <span> 编辑表达式 </span>
    </template>
      <!-- 左侧无用dom元素已删除 -->
      <div class="content__right">
        <div class="symbol-group">
          <div v-for="item in symbolList" :key="item.code" class="symbol-item" @click="handleSymbolClick(item)">
            {{ item.name }}
          </div>
        </div>
        <!-- 代码编辑器 -->
        <textarea
          ref="codeEditorContainerRef"
          v-model="currentCodeInEditor"
          class="editor"
        />
      </div>
    </div>

    <template #footer>
      <div class="dialog-button-box">
        <el-button
          size="small"
          @click="
            () => {
              $emit('update:visible', false);
            }
          "
        >
          取消
        </el-button>
        <el-button size="small" type="primary" @click="handleOk">
          确定
        </el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script lang="ts">
import { defineComponent, reactive, toRefs, watch, ref, onMounted, nextTick } from 'vue';
import { InfoFilled, Search } from '@element-plus/icons';
import { TreeNodeData } from 'element-plus/es/el-tree/src/tree.type.d';
// 引入代码编辑器核心配置包
import * as CodeMirror from 'codemirror';
import 'codemirror/lib/codemirror.css';

// 引入代码编辑器主题样式
import 'codemirror/theme/base16-light.css';
import 'codemirror/theme/ambiance.css';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/theme/monokai.css';
import 'codemirror/theme/material.css';
import 'codemirror/theme/dracula.css';
import 'codemirror/addon/fold/foldgutter.css';
import 'codemirror/mode/javascript/javascript';

// 引入代码编辑器常用语言包
require('codemirror/addon/edit/matchbrackets');
require('codemirror/addon/selection/active-line');
require('codemirror/mode/sql/sql');
require('codemirror/addon/hint/show-hint');
require('codemirror/addon/hint/sql-hint');
require('codemirror/keymap/sublime');

// 引入代码折叠文件
require('codemirror/addon/fold/foldcode');
require('codemirror/addon/fold/foldgutter');
require('codemirror/addon/fold/brace-fold');
require('codemirror/addon/fold/xml-fold');
require('codemirror/addon/fold/indent-fold');
require('codemirror/addon/fold/markdown-fold');
require('codemirror/addon/fold/comment-fold');


export default defineComponent({
  name: 'RemarksDialog',
  components: {
    
  },
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    data: {
      type: String,
      default: '',
    },
  },
  emits: ['update:visible', 'save'],
  setup(props, { emit }) {
    const dialogRef = ref();
    const state = reactive({
      // 符号列表
      symbolList: [
        {
          id: 'plus',
          code: 'plus',
          name: '+',
          type: 'operator',
        },
        {
          id: 'minus',
          code: 'minus',
          name: '-',
          type: 'operator',
        },
        {
          id: 'multiply',
          code: 'multiply',
          name: '*',
          type: 'operator',
        },
        {
          id: 'exception',
          code: 'exception',
          name: '/',
          type: 'operator',
        },
        {
          id: 'leftBrackets',
          code: 'leftBrackets',
          name: '(',
          type: 'operator',
        },
        {
          id: 'rightBrackets',
          code: 'rightBrackets',
          name: ')',
          type: 'operator',
        },
      ] as SymbolItem[],
    });

    // 代码编辑器容器实例
    const codeEditorContainerRef = ref<HTMLElement | null>();
    // 代码编辑器
    let editor : TreeNodeData | null;

    // 编辑器当前所用编程语言
    const currentLanguage = ref('javascript');
    // 编辑器当前主题
    const currentTheme = ref('base16-light');
    // 编辑器当前展示的代码
    const currentCodeInEditor = ref();

    // 获取表达式的元素集合
    const getCalcResult = (): SymbolItem[] => {
      const temp: any[] = editor?.getValue()
        .split('$');
      // 清除最后一个空格元素
      temp.pop();
      // 循环生成最后的集合
      return temp
        .map((item: string) => state.calculationIdMap[item])
        .filter((item) => !!item);
    };


    /**
     * @description: 创建标签
     * @return {*}
     */
    const makeLabel = (mark: any) => {
      const spanDom = document.createElement('span');
      const label = mark.variable;
      spanDom.title = label;
      spanDom.innerText = label;
      spanDom.classList.add('textarea-tag');
      spanDom.dataset.variable = mark.variable;
      editor?.markText(mark.start, mark.end, {
        replacedWith: spanDom, // 将特定位置的文本替换成给定的节点元素,必须是行元素,不能是块元素
        atomic: true, // 原子化,会把节点元素当成一个整体,光标不会进入其中
      });
    };


    /**
     * @description: 插入标签
     * @return {*}
     */
    const insertLabel = (content: any, isCalcDim: boolean) => {
      if (!content) return;
      const cursor = editor?.getCursor();
      editor?.replaceSelection(`${content.code}$`);
      makeLabel({
        start: cursor,
        end: editor?.getCursor(), // 获取自定义标签插入之后的光标对象
        variable: content.name,
      });
      editor?.setCursor(editor?.getCursor());
      editor?.focus();
    };

    /**
     * 初始化代码编辑器
     */
    const initEditor = () => {
      nextTick(() => {
        if (codeEditorContainerRef.value) {
          editor = CodeMirror.fromTextArea(codeEditorContainerRef.value, {
          // 编辑器语言的模式
            mode: currentLanguage.value,
            // 编辑器主题风格
            theme: currentTheme.value,
            // 缩进的时候,是否把前面的 N*tab 大小的空间,转化为 N个tab 字符
            indentWithTabs: true,
            // 是否使用 mode 提供的上下文的缩进
            smartIndent: true,
            // 编辑器左侧是否显示行号
            lineNumbers: true,
            // 括号匹配
            matchBrackets: true,
            // 初始化时是否自动获得焦点
            autofocus: true,
            // 代码自动换行
            lineWrapping: true,
            // 代码块折叠
            foldGutter: true,
            // 只读
            readOnly: false,
            // 代码块折叠样式
            gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
            // 自定义快捷键
            extraKeys: { Ctrl: 'autocomplete' },
            // 自定义提示选项
            hintOptions: {
              tables: {
                users: ['name', 'score', 'birthDate'],
                countries: ['name', 'population', 'size'],
              },
            },
          });
          // 公式回显
          if (props.data) {
            const dataArray = JSON.parse(props.data);
            dataArray.forEach((item: any) => {
              insertLabel(item, false);
              state.calculationIdMap[item.code] = item;
            });
            // 重新计算时间维度和可用维度
            setTimeout(() => {
              // 重新计算时间维度和其他可用维度
              resetCurrentDateDimension();
              resetCurrentOtherDimension();
            }, 500);
          }
        }
      });
    };


    /**
     * @description: 符号点击触发方法
     * @param {SymbolItem} data
     * @return {*}
     */
    const handleSymbolClick = (data: SymbolItem) => {
      insertLabel(data, false);
      state.calculationIdMap[data.code] = data;
    };


    watch(
      () => props.visible,
      async (newVal) => {
        if (newVal) {
          initEditor();
          await getIndicatorList();
        } else {
          state.currentOtherDimension = [];
          state.currentDateDimension = {} as any;
        }
      },
    );

    return {
      ...toRefs(state),
      dialogRef,
      Search,
      codeEditorContainerRef,
      currentCodeInEditor,
      handleSymbolClick,
    };
  },
});
</script>

<style lang="scss" scoped>

</style>

大家只需要关注codeMirror插件的引入以及相关api 的使用即可。

未解决的问题:在点击生成标签只有,不能通过鼠标点击直接移动光标位置,只能通过方向键移动光标位置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1799419.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【电路笔记】-分贝

分贝 分贝是以 10 为底的对数比,用于表示电路中功率、电压或电流的增加或减少。 1、概述 一般来说,分贝是响度的度量。 在设计或使用放大器和滤波器电路时,计算中使用的一些数字可能非常大或非常小。 例如,如果我们将两个放大器级级联在一起,功率或电压增益分别为 20 和…

qmt量化交易策略小白学习笔记第18期【qmt编程之获取对应周期的北向南向数据--方式2:原生python】

qmt编程之获取对应周期的北向南向数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 获取对应周期的北向南向数据 提示 该数据通过get_market_data_ex接口获取获取历史数据前需要先用downl…

力扣303. 区域和检索 - 数组不可变

Problem: 303. 区域和检索 - 数组不可变 文章目录 题目描述思路复杂度Code 题目描述 思路 创建前缀和数组preSum&#xff0c;其中preSum[i]处元素值为nums[0] - nums[i - 1]处元素值得和&#xff0c;当调用sumRange函数时直接返回preSum[right 1] - preSum[left] 复杂度 函数…

通过U盘将第三方软件安装到各大品牌电视的方法

在本教程中&#xff0c;小武给大家整理了通过U盘的方式安装第三方软件到电视盒子上&#xff0c;可直接使用通用U盘的方式来进行安装。 如果您相应电视品牌按通用方式无法完成需求&#xff0c;下面为您也贴心整理了20款主流智能电视和电视盒子的U盘安装指南。这些步骤适用于小米…

MSP430单片机控制流水灯,Proteus仿真

作品功能 本项目利用MSP430单片机控制一个简单的流水灯&#xff0c;通过按键切换流水灯的模式。用户可以通过按键控制LED灯的方向&#xff0c;从左向右或从右向左依次点亮。 作品的硬件材料 MSP430单片机 具体型号&#xff1a;MSP430G2553 LED灯 数量&#xff1a;8个类型&…

一篇文章讲透排序算法之归并排序

0.前言 本篇文章将详细解释归并排序的原理&#xff0c;以及递归和非递归的代码原理。 一.概念 归并排序是建立在归并操作上的一种有效的排序算法&#xff0c;该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使…

[消息队列 Kafka] Kafka 架构组件及其特性(二)Producer原理

这边整理下Kafka三大主要组件Producer原理。 目录 一、Producer发送消息源码流程 二、ACK应答机制和ISR机制 1&#xff09;ACK应答机制 2&#xff09;ISR机制 三、消息的幂等性 四、Kafka生产者事务 一、Producer发送消息源码流程 Producer发送消息流程如上图。主要是用…

【Python】使用Gradio作为机器学习web服务器

在机器学习领域&#xff0c;模型的展示和验证是一个重要的环节。传统的模型展示方式往往需要复杂的Web开发知识&#xff0c;这对于许多机器学习研究者或数据科学家来说可能是一个挑战。然而&#xff0c;Gradio的出现为我们提供了一个简单而强大的解决方案&#xff0c;让我们能够…

ffmpeg视频编码原理和实战-(2)视频帧的创建和编码packet压缩

源文件&#xff1a; #include <iostream> using namespace std; extern "C" { //指定函数是c语言函数&#xff0c;函数名不包含重载标注 //引用ffmpeg头文件 #include <libavcodec/avcodec.h> } //预处理指令导入库 #pragma comment(lib,"avcodec.…

【Week-R2】使用LSTM实现火灾预测(tf版本)

【Week-R2】使用LSTM实现火灾预测&#xff08;tf版本&#xff09; 一、 前期准备1.1 设置GPU1.2 导入数据1.3 数据可视化 二、数据预处理(构建数据集)2.1 设置x、y2.2 归一化2.3 划分数据集 三、模型创建、编译、训练、得到训练结果3.1 构建模型3.2 编译模型3.3 训练模型3.4 模…

虚拟机Ubuntu 22.04上搭建GitLab操作步骤

GitLab是仓库管理系统&#xff0c;使用Git作为代码管理工具。GitLab提供了多个版本&#xff0c;包括社区版(Community Edition)和企业版(Enterprise Edition)。实际应用场景中要求CPU最小4核、内存最小8GB&#xff0c;非虚拟环境。 以下是在虚拟机中安装社区版步骤&#xff1a;…

C++青少年简明教程:C++函数

C青少年简明教程&#xff1a;C函数 C函数是一段可重复使用的代码&#xff0c;用于执行特定的任务&#xff0c;可以提高代码的可读性和可维护性。函数可以接受参数&#xff08;输入&#xff09;并返回一个值&#xff08;输出&#xff09;&#xff0c;也可以没有参数和返回值。 …

应用层——HTTP协议(自己实现一个http协议)——客户端(浏览器)的请求做反序列化和请求分析,然后创建http向响应结构

应用层&#xff1a;之前我们写的创建套接字&#xff0c;发送数据&#xff0c;序列化反序列化这些都是在写应用层 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层 之前的网络计算机是我们自定义的协议&#xff1a;传输的数据最终是什么样的结…

Redis缓存(笔记二:Redis常用五大数据类型)

目录 1、Redis中String字符串 1.1 常用命令解释&#xff1a; 1.2 原子性 1.3 具有原子性的常用命令 1.4 String数据结构 1、Redis中String字符串 概念 String 是 Redis 最基本的类型&#xff0c;可以理解成与 Memcached 一模一样的类型&#xff0c;一个 key对应一个 value…

Go微服务: 基于使用场景理解分布式之二阶段提交

概述 二阶段提交&#xff08;Two-Phase Commit&#xff0c;2PC&#xff09;是一种分布式事务协议&#xff0c;用于在分布式系统中确保多个参与者的操作具有原子性即所有参与者要么全部提交事务&#xff0c;要么全部回滚事务&#xff0c;以维持数据的一致性它分为两个阶段进行&…

php反序列化中的pop链

目录 一、什么是POP 二、成员属性赋值对象 例题&#xff1a; 方法一 方法二 三、魔术方法的触发规则 例题&#xff1a; 四、POC的编写 例题1&#xff1a; 例题2 [NISACTF 2022]babyserialize 今日总结&#xff1a; 一、什么是POP 在反序列化中&#xff0c;我们…

DexCap——斯坦福李飞飞团队泡茶机器人:更好数据收集系统的原理解析、源码剖析

前言 2023年7月&#xff0c;我司组建大模型项目开发团队&#xff0c;从最开始的论文审稿&#xff0c;演变成目前的两大赋能方向 大模型应用方面&#xff0c;以微调和RAG为代表 除了论文审稿微调之外&#xff0c;目前我司内部正在逐一开发论文翻译、论文对话、论文idea提炼、论…

RDMA (1)

RDMA是什么 Remote Direct Memory Access(RDMA)是用来给有高速需求的应用释放网络消耗的。 RDMA在网络的两个应用之间进行低延迟,高吞吐的内存对内存的直接数据通信。 InfiniBand需要部署独立的协议。 RoCE(RDMA over Converged Ethernet),也是由InfiniBand Trade Associat…

不要硬来!班组管理有“巧思”

班组管理&#xff0c;听起来似乎是一个充满“硬气”的词汇&#xff0c;让人联想到严肃、刻板的制度和规矩。然而&#xff0c;在实际操作中&#xff0c;我们却需要运用一些“巧思”&#xff0c;以柔克刚&#xff0c;让班组管理既有力度又不失温度。 在班组管理中&#xff0c;我们…

Istio_1.17.8安装

项目背景 按照istio官网的命令一路安装下来&#xff0c;安装好的istio版本为目前的最新版本&#xff0c;1.22.0。而我的k8s集群的版本并不支持istio_1.22的版本&#xff0c;导致ingress-gate网关安装不上&#xff0c;再仔细查看istio的发布文档&#xff0c;如果用istio_1.22版本…