? ? ? ? ?在日常開發中對于一些數據計算場景可能會遇到標簽計算的需求,下面關于如何使用CodeMirror實現標簽計算編輯功能。
1,結果圖
2,主體代碼邏輯
大家只需要復制粘貼主要codeMirror使用邏輯即可
<template><el-dialogref="dialogRef":model-value="visible"width="800px":close-on-press-escape="false"destroy-on-closeappend-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><!-- 代碼編輯器 --><textarearef="codeEditorContainerRef"v-model="currentCodeInEditor"class="editor"/></div></div><template #footer><div class="dialog-button-box"><el-buttonsize="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 的使用即可。
未解決的問題:在點擊生成標簽只有,不能通過鼠標點擊直接移動光標位置,只能通過方向鍵移動光標位置。