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

大家只需要复制粘贴主要codeMirror使用逻辑即可
-
- <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>
-
- <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>
-
-
- <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 的使用即可。
未解决的问题:在点击生成标签只有,不能通过鼠标点击直接移动光标位置,只能通过方向键移动光标位置。