From 00989b7ac9b92685be495c15c1f11dd2546eb6be Mon Sep 17 00:00:00 2001
From: lt5227 <995362096@qq.com>
Date: Wed, 15 May 2024 10:16:18 +0800
Subject: [PATCH] feat: Add a new component CodeEditor (#466)
---
mock/role/index.mock.ts | 9 ++
package.json | 3 +-
src/components/CodeEditor/index.ts | 3 +
src/components/CodeEditor/src/CodeEditor.vue | 119 ++++++++++++++++
.../CodeEditor/src/config/config.ts | 129 ++++++++++++++++++
src/hooks/web/useMonacoEditor.ts | 129 ++++++++++++++++++
src/locales/en.ts | 5 +-
src/locales/zh-CN.ts | 5 +-
src/views/Components/Editor/CodeEditor.vue | 23 ++++
9 files changed, 422 insertions(+), 3 deletions(-)
create mode 100644 src/components/CodeEditor/index.ts
create mode 100644 src/components/CodeEditor/src/CodeEditor.vue
create mode 100644 src/components/CodeEditor/src/config/config.ts
create mode 100644 src/hooks/web/useMonacoEditor.ts
create mode 100644 src/views/Components/Editor/CodeEditor.vue
diff --git a/mock/role/index.mock.ts b/mock/role/index.mock.ts
index 1e33d93..7aae345 100644
--- a/mock/role/index.mock.ts
+++ b/mock/role/index.mock.ts
@@ -202,6 +202,14 @@ const adminList = [
meta: {
title: 'router.jsonEditor'
}
+ },
+ {
+ path: 'code-editor',
+ component: 'views/Components/Editor/CodeEditor',
+ name: 'CodeEditor',
+ meta: {
+ title: 'router.codeEditor'
+ }
}
]
},
@@ -687,6 +695,7 @@ const testList: string[] = [
'/components/editor-demo',
'/components/editor-demo/editor',
'/components/editor-demo/json-editor',
+ '/components/editor-demo/code-editor',
'/components/search',
'/components/descriptions',
'/components/image-viewer',
diff --git a/package.json b/package.json
index abe63e6..689ee9f 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,8 @@
"vue-json-pretty": "^2.4.0",
"vue-router": "^4.3.0",
"vue-types": "^5.1.1",
- "xgplayer": "^3.0.14"
+ "xgplayer": "^3.0.14",
+ "monaco-editor": "^0.48.0"
},
"devDependencies": {
"@commitlint/cli": "^19.2.1",
diff --git a/src/components/CodeEditor/index.ts b/src/components/CodeEditor/index.ts
new file mode 100644
index 0000000..5bf3e9e
--- /dev/null
+++ b/src/components/CodeEditor/index.ts
@@ -0,0 +1,3 @@
+import CodeEditor from './src/CodeEditor.vue'
+
+export { CodeEditor }
diff --git a/src/components/CodeEditor/src/CodeEditor.vue b/src/components/CodeEditor/src/CodeEditor.vue
new file mode 100644
index 0000000..306db13
--- /dev/null
+++ b/src/components/CodeEditor/src/CodeEditor.vue
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/CodeEditor/src/config/config.ts b/src/components/CodeEditor/src/config/config.ts
new file mode 100644
index 0000000..4fa7b30
--- /dev/null
+++ b/src/components/CodeEditor/src/config/config.ts
@@ -0,0 +1,129 @@
+export const languageOptions = [
+ { label: 'plaintext', value: 'plaintext' },
+ { label: 'abap', value: 'abap' },
+ { label: 'apex', value: 'apex' },
+ { label: 'azcli', value: 'azcli' },
+ { label: 'bat', value: 'bat' },
+ { label: 'bicep', value: 'bicep' },
+ { label: 'cameligo', value: 'cameligo' },
+ { label: 'clojure', value: 'clojure' },
+ { label: 'coffeescript', value: 'coffeescript' },
+ { label: 'c', value: 'c' },
+ { label: 'cpp', value: 'cpp' },
+ { label: 'csharp', value: 'csharp' },
+ { label: 'csp', value: 'csp' },
+ { label: 'css', value: 'css' },
+ { label: 'cypher', value: 'cypher' },
+ { label: 'dart', value: 'dart' },
+ { label: 'dockerfile', value: 'dockerfile' },
+ { label: 'ecl', value: 'ecl' },
+ { label: 'elixir', value: 'elixir' },
+ { label: 'flow9', value: 'flow9' },
+ { label: 'fsharp', value: 'fsharp' },
+ { label: 'freemarker2', value: 'freemarker2' },
+ {
+ label: 'freemarker2.tag-angle.interpolation-dollar',
+ value: 'freemarker2.tag-angle.interpolation-dollar'
+ },
+ {
+ label: 'freemarker2.tag-bracket.interpolation-dollar',
+ value: 'freemarker2.tag-bracket.interpolation-dollar'
+ },
+ {
+ label: 'freemarker2.tag-angle.interpolation-bracket',
+ value: 'freemarker2.tag-angle.interpolation-bracket'
+ },
+ {
+ label: 'freemarker2.tag-bracket.interpolation-bracket',
+ value: 'freemarker2.tag-bracket.interpolation-bracket'
+ },
+ {
+ label: 'freemarker2.tag-auto.interpolation-dollar',
+ value: 'freemarker2.tag-auto.interpolation-dollar'
+ },
+ {
+ label: 'freemarker2.tag-auto.interpolation-bracket',
+ value: 'freemarker2.tag-auto.interpolation-bracket'
+ },
+ { label: 'go', value: 'go' },
+ { label: 'graphql', value: 'graphql' },
+ { label: 'handlebars', value: 'handlebars' },
+ { label: 'hcl', value: 'hcl' },
+ { label: 'html', value: 'html' },
+ { label: 'ini', value: 'ini' },
+ { label: 'java', value: 'java' },
+ { label: 'javascript', value: 'javascript' },
+ { label: 'julia', value: 'julia' },
+ { label: 'kotlin', value: 'kotlin' },
+ { label: 'less', value: 'less' },
+ { label: 'lexon', value: 'lexon' },
+ { label: 'lua', value: 'lua' },
+ { label: 'liquid', value: 'liquid' },
+ { label: 'm3', value: 'm3' },
+ { label: 'markdown', value: 'markdown' },
+ { label: 'mdx', value: 'mdx' },
+ { label: 'mips', value: 'mips' },
+ { label: 'msdax', value: 'msdax' },
+ { label: 'mysql', value: 'mysql' },
+ { label: 'objective-c', value: 'objective-c' },
+ { label: 'pascal', value: 'pascal' },
+ { label: 'pascaligo', value: 'pascaligo' },
+ { label: 'perl', value: 'perl' },
+ { label: 'pgsql', value: 'pgsql' },
+ { label: 'php', value: 'php' },
+ { label: 'pla', value: 'pla' },
+ { label: 'postiats', value: 'postiats' },
+ { label: 'powerquery', value: 'powerquery' },
+ { label: 'powershell', value: 'powershell' },
+ { label: 'proto', value: 'proto' },
+ { label: 'pug', value: 'pug' },
+ { label: 'python', value: 'python' },
+ { label: 'qsharp', value: 'qsharp' },
+ { label: 'r', value: 'r' },
+ { label: 'razor', value: 'razor' },
+ { label: 'redis', value: 'redis' },
+ { label: 'redshift', value: 'redshift' },
+ { label: 'restructuredtext', value: 'restructuredtext' },
+ { label: 'ruby', value: 'ruby' },
+ { label: 'rust', value: 'rust' },
+ { label: 'sb', value: 'sb' },
+ { label: 'scala', value: 'scala' },
+ { label: 'scheme', value: 'scheme' },
+ { label: 'scss', value: 'scss' },
+ { label: 'shell', value: 'shell' },
+ { label: 'sol', value: 'sol' },
+ { label: 'aes', value: 'aes' },
+ { label: 'sparql', value: 'sparql' },
+ { label: 'sql', value: 'sql' },
+ { label: 'st', value: 'st' },
+ { label: 'swift', value: 'swift' },
+ { label: 'systemverilog', value: 'systemverilog' },
+ { label: 'verilog', value: 'verilog' },
+ { label: 'tcl', value: 'tcl' },
+ { label: 'twig', value: 'twig' },
+ { label: 'typescript', value: 'typescript' },
+ { label: 'vb', value: 'vb' },
+ { label: 'wgsl', value: 'wgsl' },
+ { label: 'xml', value: 'xml' },
+ { label: 'yaml', value: 'yaml' },
+ { label: 'json', value: 'json' }
+]
+
+export const themeOptions = [
+ {
+ label: 'vs',
+ value: 'vs'
+ },
+ {
+ label: 'vs-dark',
+ value: 'vs-dark'
+ },
+ {
+ label: 'hc-black',
+ value: 'hc-black'
+ },
+ {
+ label: 'hc-light',
+ value: 'hc-light'
+ }
+]
diff --git a/src/hooks/web/useMonacoEditor.ts b/src/hooks/web/useMonacoEditor.ts
new file mode 100644
index 0000000..ab464ea
--- /dev/null
+++ b/src/hooks/web/useMonacoEditor.ts
@@ -0,0 +1,129 @@
+import * as monaco from 'monaco-editor'
+import { ref, nextTick, onBeforeUnmount } from 'vue'
+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()
+ }
+}
+
+export function useMonacoEditor(language: string = 'javascript') {
+ // 编辑器示例
+ let monacoEditor: monaco.editor.IStandaloneCodeEditor | null = null
+ // 目标元素
+ const monacoEditorRef = ref()
+
+ // 创建实例
+ function createEditor(editorOption: monaco.editor.IStandaloneEditorConstructionOptions = {}) {
+ if (!monacoEditorRef.value) return
+ monacoEditor = monaco.editor.create(monacoEditorRef.value, {
+ // 初始模型
+ model: monaco.editor.createModel('', language),
+ // 是否启用预览图
+ minimap: { enabled: true },
+ // 圆角
+ roundedSelection: true,
+ // 主题
+ theme: 'vs-dark',
+ // 主键
+ multiCursorModifier: 'ctrlCmd',
+ // 滚动条
+ scrollbar: {
+ verticalScrollbarSize: 8,
+ horizontalScrollbarSize: 8
+ },
+ // 行号
+ lineNumbers: 'on',
+ // tab大小
+ tabSize: 2,
+ //字体大小
+ fontSize: 14,
+ // 控制编辑器在用户键入、粘贴、移动或缩进行时是否应自动调整缩进
+ autoIndent: 'advanced',
+ // 自动布局
+ automaticLayout: true,
+ ...editorOption
+ })
+ return monacoEditor
+ }
+
+ // 格式化
+ async function formatDoc() {
+ await monacoEditor?.getAction('editor.action.formatDocument')?.run()
+ }
+
+ // 数据更新
+ function updateVal(val: string) {
+ nextTick(() => {
+ if (getOption(monaco.editor.EditorOption.readOnly)) {
+ updateOptions({ readOnly: false })
+ }
+ monacoEditor?.setValue(val)
+ setTimeout(async () => {
+ await formatDoc()
+ }, 10)
+ })
+ }
+
+ // 配置更新
+ function updateOptions(opt: monaco.editor.IStandaloneEditorConstructionOptions) {
+ monacoEditor?.updateOptions(opt)
+ }
+
+ // 获取配置
+ function getOption(name: monaco.editor.EditorOption) {
+ return monacoEditor?.getOption(name)
+ }
+
+ // 获取实例
+ function getEditor() {
+ return monacoEditor
+ }
+
+ function changeLanguage(newLanguage: string) {
+ const model = monacoEditor?.getModel()
+ if (model) {
+ monaco.editor.setModelLanguage(model, newLanguage)
+ }
+ }
+
+ function changeTheme(newTheme: string) {
+ monaco.editor.setTheme(newTheme)
+ }
+
+ // 页面离开 销毁
+ onBeforeUnmount(() => {
+ if (monacoEditor) {
+ monacoEditor.dispose()
+ }
+ })
+
+ return {
+ monacoEditorRef,
+ createEditor,
+ getEditor,
+ updateVal,
+ updateOptions,
+ getOption,
+ formatDoc,
+ changeLanguage,
+ changeTheme
+ }
+}
diff --git a/src/locales/en.ts b/src/locales/en.ts
index c68a9d8..f038abf 100644
--- a/src/locales/en.ts
+++ b/src/locales/en.ts
@@ -153,6 +153,7 @@ export default {
editor: 'Editor',
richText: 'Rich text',
jsonEditor: 'JSON Editor',
+ codeEditor: 'Code Editor',
dialog: 'Dialog',
imageViewer: 'Image viewer',
descriptions: 'Descriptions',
@@ -473,7 +474,9 @@ export default {
richText: 'Rich text',
richTextDes: 'Secondary packaging based on wangeditor',
jsonEditor: 'JSON Editor',
- jsonEditorDes: 'Secondary packaging based on vue-json-pretty'
+ jsonEditorDes: 'Secondary packaging based on vue-json-pretty',
+ codeEditor: 'Code Editor',
+ codeEditorDes: 'Secondary packaging based on monaco-editor'
},
dialogDemo: {
dialog: 'Dialog',
diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts
index 7d4cf72..a4df161 100644
--- a/src/locales/zh-CN.ts
+++ b/src/locales/zh-CN.ts
@@ -151,6 +151,7 @@ export default {
editor: '编辑器',
richText: '富文本',
jsonEditor: 'JSON编辑器',
+ codeEditor: '代码编辑器',
dialog: '弹窗',
imageViewer: '图片预览',
descriptions: '描述',
@@ -464,7 +465,9 @@ export default {
richText: '富文本',
richTextDes: '基于 wangeditor 二次封装',
jsonEditor: 'JSON编辑器',
- jsonEditorDes: '基于 vue-json-pretty 二次封装'
+ jsonEditorDes: '基于 vue-json-pretty 二次封装',
+ codeEditor: '代码编辑器',
+ codeEditorDes: '基于 monaco-editor 二次封装'
},
dialogDemo: {
dialog: '弹窗',
diff --git a/src/views/Components/Editor/CodeEditor.vue b/src/views/Components/Editor/CodeEditor.vue
new file mode 100644
index 0000000..db80bb0
--- /dev/null
+++ b/src/views/Components/Editor/CodeEditor.vue
@@ -0,0 +1,23 @@
+
+
+
+ 控制台打印内容
+
+
+
+
+
+