• CodeMirror 创建标签计算编辑器


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

    1,结果图

    2,主体代码逻辑

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

    1. <script lang="ts">
    2. import { defineComponent, reactive, toRefs, watch, ref, onMounted, nextTick } from 'vue';
    3. import { InfoFilled, Search } from '@element-plus/icons';
    4. import { TreeNodeData } from 'element-plus/es/el-tree/src/tree.type.d';
    5. // 引入代码编辑器核心配置包
    6. import * as CodeMirror from 'codemirror';
    7. import 'codemirror/lib/codemirror.css';
    8. // 引入代码编辑器主题样式
    9. import 'codemirror/theme/base16-light.css';
    10. import 'codemirror/theme/ambiance.css';
    11. import 'codemirror/addon/hint/show-hint.css';
    12. import 'codemirror/theme/monokai.css';
    13. import 'codemirror/theme/material.css';
    14. import 'codemirror/theme/dracula.css';
    15. import 'codemirror/addon/fold/foldgutter.css';
    16. import 'codemirror/mode/javascript/javascript';
    17. // 引入代码编辑器常用语言包
    18. require('codemirror/addon/edit/matchbrackets');
    19. require('codemirror/addon/selection/active-line');
    20. require('codemirror/mode/sql/sql');
    21. require('codemirror/addon/hint/show-hint');
    22. require('codemirror/addon/hint/sql-hint');
    23. require('codemirror/keymap/sublime');
    24. // 引入代码折叠文件
    25. require('codemirror/addon/fold/foldcode');
    26. require('codemirror/addon/fold/foldgutter');
    27. require('codemirror/addon/fold/brace-fold');
    28. require('codemirror/addon/fold/xml-fold');
    29. require('codemirror/addon/fold/indent-fold');
    30. require('codemirror/addon/fold/markdown-fold');
    31. require('codemirror/addon/fold/comment-fold');
    32. export default defineComponent({
    33. name: 'RemarksDialog',
    34. components: {
    35. },
    36. props: {
    37. visible: {
    38. type: Boolean,
    39. default: false,
    40. },
    41. data: {
    42. type: String,
    43. default: '',
    44. },
    45. },
    46. emits: ['update:visible', 'save'],
    47. setup(props, { emit }) {
    48. const dialogRef = ref();
    49. const state = reactive({
    50. // 符号列表
    51. symbolList: [
    52. {
    53. id: 'plus',
    54. code: 'plus',
    55. name: '+',
    56. type: 'operator',
    57. },
    58. {
    59. id: 'minus',
    60. code: 'minus',
    61. name: '-',
    62. type: 'operator',
    63. },
    64. {
    65. id: 'multiply',
    66. code: 'multiply',
    67. name: '*',
    68. type: 'operator',
    69. },
    70. {
    71. id: 'exception',
    72. code: 'exception',
    73. name: '/',
    74. type: 'operator',
    75. },
    76. {
    77. id: 'leftBrackets',
    78. code: 'leftBrackets',
    79. name: '(',
    80. type: 'operator',
    81. },
    82. {
    83. id: 'rightBrackets',
    84. code: 'rightBrackets',
    85. name: ')',
    86. type: 'operator',
    87. },
    88. ] as SymbolItem[],
    89. });
    90. // 代码编辑器容器实例
    91. const codeEditorContainerRef = ref<HTMLElement | null>();
    92. // 代码编辑器
    93. let editor : TreeNodeData | null;
    94. // 编辑器当前所用编程语言
    95. const currentLanguage = ref('javascript');
    96. // 编辑器当前主题
    97. const currentTheme = ref('base16-light');
    98. // 编辑器当前展示的代码
    99. const currentCodeInEditor = ref();
    100. // 获取表达式的元素集合
    101. const getCalcResult = (): SymbolItem[] => {
    102. const temp: any[] = editor?.getValue()
    103. .split('$');
    104. // 清除最后一个空格元素
    105. temp.pop();
    106. // 循环生成最后的集合
    107. return temp
    108. .map((item: string) => state.calculationIdMap[item])
    109. .filter((item) => !!item);
    110. };
    111. /**
    112. * @description: 创建标签
    113. * @return {*}
    114. */
    115. const makeLabel = (mark: any) => {
    116. const spanDom = document.createElement('span');
    117. const label = mark.variable;
    118. spanDom.title = label;
    119. spanDom.innerText = label;
    120. spanDom.classList.add('textarea-tag');
    121. spanDom.dataset.variable = mark.variable;
    122. editor?.markText(mark.start, mark.end, {
    123. replacedWith: spanDom, // 将特定位置的文本替换成给定的节点元素,必须是行元素,不能是块元素
    124. atomic: true, // 原子化,会把节点元素当成一个整体,光标不会进入其中
    125. });
    126. };
    127. /**
    128. * @description: 插入标签
    129. * @return {*}
    130. */
    131. const insertLabel = (content: any, isCalcDim: boolean) => {
    132. if (!content) return;
    133. const cursor = editor?.getCursor();
    134. editor?.replaceSelection(`${content.code}$`);
    135. makeLabel({
    136. start: cursor,
    137. end: editor?.getCursor(), // 获取自定义标签插入之后的光标对象
    138. variable: content.name,
    139. });
    140. editor?.setCursor(editor?.getCursor());
    141. editor?.focus();
    142. };
    143. /**
    144. * 初始化代码编辑器
    145. */
    146. const initEditor = () => {
    147. nextTick(() => {
    148. if (codeEditorContainerRef.value) {
    149. editor = CodeMirror.fromTextArea(codeEditorContainerRef.value, {
    150. // 编辑器语言的模式
    151. mode: currentLanguage.value,
    152. // 编辑器主题风格
    153. theme: currentTheme.value,
    154. // 缩进的时候,是否把前面的 N*tab 大小的空间,转化为 N个tab 字符
    155. indentWithTabs: true,
    156. // 是否使用 mode 提供的上下文的缩进
    157. smartIndent: true,
    158. // 编辑器左侧是否显示行号
    159. lineNumbers: true,
    160. // 括号匹配
    161. matchBrackets: true,
    162. // 初始化时是否自动获得焦点
    163. autofocus: true,
    164. // 代码自动换行
    165. lineWrapping: true,
    166. // 代码块折叠
    167. foldGutter: true,
    168. // 只读
    169. readOnly: false,
    170. // 代码块折叠样式
    171. gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
    172. // 自定义快捷键
    173. extraKeys: { Ctrl: 'autocomplete' },
    174. // 自定义提示选项
    175. hintOptions: {
    176. tables: {
    177. users: ['name', 'score', 'birthDate'],
    178. countries: ['name', 'population', 'size'],
    179. },
    180. },
    181. });
    182. // 公式回显
    183. if (props.data) {
    184. const dataArray = JSON.parse(props.data);
    185. dataArray.forEach((item: any) => {
    186. insertLabel(item, false);
    187. state.calculationIdMap[item.code] = item;
    188. });
    189. // 重新计算时间维度和可用维度
    190. setTimeout(() => {
    191. // 重新计算时间维度和其他可用维度
    192. resetCurrentDateDimension();
    193. resetCurrentOtherDimension();
    194. }, 500);
    195. }
    196. }
    197. });
    198. };
    199. /**
    200. * @description: 符号点击触发方法
    201. * @param {SymbolItem} data
    202. * @return {*}
    203. */
    204. const handleSymbolClick = (data: SymbolItem) => {
    205. insertLabel(data, false);
    206. state.calculationIdMap[data.code] = data;
    207. };
    208. watch(
    209. () => props.visible,
    210. async (newVal) => {
    211. if (newVal) {
    212. initEditor();
    213. await getIndicatorList();
    214. } else {
    215. state.currentOtherDimension = [];
    216. state.currentDateDimension = {} as any;
    217. }
    218. },
    219. );
    220. return {
    221. ...toRefs(state),
    222. dialogRef,
    223. Search,
    224. codeEditorContainerRef,
    225. currentCodeInEditor,
    226. handleSymbolClick,
    227. };
    228. },
    229. });
    230. script>
    231. <style lang="scss" scoped>
    232. style>

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

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

  • 相关阅读:
    OpenGL 桶形畸变算法-常用VR
    2024.05.14 Diffusion 代码学习笔记
    Rocky Linux 8.9 一键安装 Oracle 11GR2(231017)单机 ASM
    Win11 22000.918(KB5016691)正式版发布,解决一系列问题!
    [杂谈]-电动汽车有哪些不同类型
    回归预测 | MATLAB实现BP神经网络多输入多输出回归预测
    我赢助手之爆款内容创作:爆款内容的四大特性,梳理下自己的账号吧?
    R语言数据分析(四)
    SpringCloud复习:(1)netflix包里的DiscoveryClient类
    HDFS中DataNode的工作机制
  • 原文地址:https://blog.csdn.net/qq_43225508/article/details/139375533