赞
踩
数据编辑时需要支持sql语法高亮, 并且支持自定义代码提示补全。
monaco详细说明和使用可参考另一篇发文Monaco Editor (vite/webpack + ts + vue项目使用)
npm i monaco-editor
- <template>
- <div ref="cusEditor"></div>
- </template>
- <script setup lang="ts">
- import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
- import { withDefaults, defineProps, ref, defineEmits, onMounted, onUnmounted, watch } from 'vue'
- import { OPTIONS_BASE } from './registerCompletion'
- import './worker'
- interface IProps {
- modelValue: string
- disabled?: boolean
- editorConfig?: { language: string; theme: 'vs' | 'vs-dark' | 'hc-black' }
- }
- const props = withDefaults(defineProps<IProps>(), {
- modelValue: '',
- disabled: false,
- editorConfig: () => ({ language: 'sql', theme: 'vs-dark' }),
- })
-
- const cusEditor = ref<HTMLElement | null>(null)
- let editor: Partial<monaco.editor.IStandaloneCodeEditor> = {}
- const emit = defineEmits(['update:modelValue'])
- /**初始化编辑器 */
- onMounted(() => {
- onDispose()
- if (cusEditor.value) {
- editor = monaco.editor.create(cusEditor.value, { ...OPTIONS_BASE, ...props.editorConfig, readOnly: props.disabled })
- editor.onDidChangeModelContent &&
- editor.onDidChangeModelContent(() => {
- const value = editor.getValue && editor.getValue() // 给父组件实时返回最新文本
- emit('update:modelValue', value)
- })
- }
- })
- /**销毁实例 */
- const onDispose = () => {
- editor && editor.dispose && editor.dispose()
- }
- onUnmounted(() => {
- onDispose()
- })
- /**修改只读状态 */
- watch(
- () => props.disabled,
- (val) => {
- editor.updateOptions && editor.updateOptions({ readOnly: val })
- }
- )
- /**修改配置 */
- watch(
- () => props.editorConfig,
- (val) => {
- const model = editor.getModel && editor.getModel()
- if (model) {
- monaco.editor.setModelLanguage(model, val.language)
- monaco.editor.setTheme(val.theme)
- }
- },
- { deep: true }
- )
- /**回显数据 */
- watch(
- () => props.modelValue,
- (val) => {
- if (editor) {
- const value = editor.getValue && editor.getValue()
- if (val !== value) {
- editor.setValue && editor.setValue(val || '')
- }
- }
- }
- )
- </script>
OPTIONS_BASE:
- export const OPTIONS_BASE: monaco.editor.IStandaloneEditorConstructionOptions = {
- value: '', // 初始显示文字
- lineNumbers: 'on', // 是否展示行号 'off' | 'on
- automaticLayout: false, // 自适应布局 默认true
- minimap: {
- enabled: false,
- },
- tabSize: 2,
- fontSize: 16
- }
worker.ts
- import * as monaco from 'monaco-editor';
- import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
- import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
- import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
- import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
- import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
-
- self.MonacoEnvironment = {
- getWorker(_, label) {
- if (label === 'json') {
- return new jsonWorker()
- }
- if (label === 'css' || label === 'scss' || label === 'less') {
- return new cssWorker()
- }
- if (label === 'html' || label === 'handlebars' || label === 'razor') {
- return new htmlWorker()
- }
- if (label === 'typescript' || label === 'javascript') {
- return new tsWorker()
- }
- return new editorWorker()
- }
- }
- monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
- <template>
- <el-form-item label="表名:" prop="table">
- <MonacoEditor class="w-full h-40" v-model="formData.table" :disabled="disabled" :constModelData="constModelData"></MonacoEditor>
- </el-form-item>
- </template>
- <script lang="ts" setup>
- import MonacoEditor from '@/components/monaco/index.vue'
- </script>
到此,就可以实现基础的编辑功能
由于我项目中使用的时sql语法,并且还需要支持自定义代码补全, 接下来就以sql语言为例:
- import * as monaco from 'monaco-editor';
- /**
- * 注册:自定义语法补全
- * @param language 语言类型
- * @param constValues 常量提示
- */
- export const registerProvider = (language: string, constValues: string[]) => {
- const monacoProvider = monaco.languages.registerCompletionItemProvider(language, {
- provideCompletionItems: function (model, position) {
- // 获取当前行数
- const line = position.lineNumber
- // 获取当前列数
- const column = position.column
- // 获取当前输入行的所有内容
- const content = model.getLineContent(line)
- // 通过下标来获取当前光标后一个内容,即为刚输入的内容
- const sym = content[column - 2]
- const word = model.getWordUntilPosition(position)
- const range = {
- startLineNumber: position.lineNumber,
- endLineNumber: position.lineNumber,
- startColumn: word.startColumn,
- endColumn: word.endColumn,
- }
- let suggestions: any[] = []
- if (sym === '$') {
- suggestions = constValues.map((e) => ({
- label: e,
- kind: monaco.languages.CompletionItemKind.Keyword,
- insertText: '{' + e + '}',
- detail: '常量配置',
- }))
- //拦截到用户输入$,开始设置提示内容,同else中代码一致,自行拓展
- } else if(language === 'sql'){
- // 直接提示,以下为sql语句关键词提示
- var sqlStr = ['SELECT', 'FROM', 'WHERE', 'AND', 'OR', 'LIMIT', 'ORDER BY', 'GROUP BY', 'LEFT', 'ON', 'if(){}', 'for(){}', 'size', 'get()', 'substring', 'return']
- suggestions = sqlStr.map((e) => ({
- label: e, // 显示的提示内容
- kind: monaco.languages.CompletionItemKind['Function'], // 用来显示提示内容后的不同的图标
- insertText: e, // 选择后粘贴到编辑器中的文字
- detail: '', // 提示内容后的说明
- range: range,
- }))
- }
- return {
- suggestions: suggestions,
- }
- },
- triggerCharacters: ['$', ''],
- })
- return monacoProvider
- }
- import { registerProvider } from '@/components/monaco/registerCompletion'
- const registerPro = registerProvider('sql', props.constModelData)
效果展示:
1.sql关键字提示
2.常量提示:
问题:每打开一次编辑弹框, 常量的提示就多一条重复数据。如图:
原因: registerCompletionItemProvider多次注册。由于自定义补全注册的代码写在了编辑弹框中,所以每打开一次弹框就执行一次注册自定义补全的方法,。
但是 由于功能需求,常量的提示内容要根据每次打开的弹框数据改变, 所以注册方法必须写在内部,(编辑弹框打开后,需根据弹框绑定数据的id去请求常量接口, 然后注册)
解决方案:参考官方:https://github.com/microsoft/monaco-editor/issues/2084
解决方案二:dispose(), 页面卸载时,销毁之前注册的实例
- const registerPro = registerProvider('sql', props.constModelData)
- onUnmounted(() => {
- registerPro && registerPro.dispose()
- })
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。