mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-12-25 09:06:34 +08:00
fix: machine file & i18n & icon
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="monaco-editor" style="border: 1px solid var(--el-border-color-light, #ebeef5); height: 100%">
|
||||
<div class="monaco-editor-content" ref="monacoTextarea" :style="{ height: height }"></div>
|
||||
<div class="monaco-editor-content" ref="monacoTextareaRef" :style="{ height: height }"></div>
|
||||
<el-select v-if="canChangeMode" class="code-mode-select" v-model="languageMode" @change="changeLanguage" filterable>
|
||||
<el-option v-for="mode in languageArr" :key="mode.value" :label="mode.label" :value="mode.value"> </el-option>
|
||||
</el-select>
|
||||
@@ -8,7 +8,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, toRefs, reactive, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { watch, toRefs, reactive, onMounted, onBeforeUnmount, useTemplateRef, Ref } from 'vue';
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||
// 相关语言
|
||||
import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution.js';
|
||||
@@ -45,6 +45,7 @@ import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
|
||||
// import Dracula from 'monaco-themes/themes/Dracula.json'
|
||||
import SolarizedLight from 'monaco-themes/themes/Solarized-light.json';
|
||||
import { language as shellLan } from 'monaco-editor/esm/vs/basic-languages/shell/shell.js';
|
||||
|
||||
import { ElOption, ElSelect } from 'element-plus';
|
||||
|
||||
import { storeToRefs } from 'pinia';
|
||||
@@ -53,9 +54,6 @@ import { useThemeConfig } from '@/store/themeConfig';
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
default: null,
|
||||
@@ -74,12 +72,11 @@ const props = defineProps({
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: null,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const modelValue = defineModel<any>('modelValue', { required: true });
|
||||
|
||||
const languageArr = [
|
||||
{
|
||||
@@ -154,7 +151,7 @@ const defaultOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
const monacoTextarea: any = ref();
|
||||
const monacoTextareaRef: Ref<any> = useTemplateRef('monacoTextareaRef');
|
||||
|
||||
let monacoEditorIns: editor.IStandaloneCodeEditor = null as any;
|
||||
let completionItemProvider: any = null;
|
||||
@@ -177,7 +174,7 @@ const { languageMode } = toRefs(state);
|
||||
onMounted(() => {
|
||||
state.languageMode = props.language;
|
||||
initMonacoEditorIns();
|
||||
setEditorValue(props.modelValue);
|
||||
setEditorValue(modelValue.value);
|
||||
registerCompletionItemProvider();
|
||||
});
|
||||
|
||||
@@ -191,18 +188,15 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue: any) => {
|
||||
if (!monacoEditorIns.hasTextFocus()) {
|
||||
state.languageMode = props.language;
|
||||
if (newValue == null) {
|
||||
newValue = '';
|
||||
}
|
||||
monacoEditorIns?.setValue(newValue);
|
||||
watch(modelValue, (newValue: any) => {
|
||||
if (!monacoEditorIns.hasTextFocus()) {
|
||||
state.languageMode = props.language;
|
||||
if (newValue == null) {
|
||||
newValue = '';
|
||||
}
|
||||
monacoEditorIns?.setValue(newValue);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.language,
|
||||
@@ -227,11 +221,11 @@ const initMonacoEditorIns = () => {
|
||||
monaco.editor.defineTheme('SolarizedLight', SolarizedLight);
|
||||
defaultOptions.language = state.languageMode;
|
||||
defaultOptions.theme = themeConfig.value.editorTheme;
|
||||
monacoEditorIns = monaco.editor.create(monacoTextarea.value, Object.assign(defaultOptions, props.options as any));
|
||||
monacoEditorIns = monaco.editor.create(monacoTextareaRef.value, Object.assign(defaultOptions, props.options as any));
|
||||
|
||||
// 监听内容改变,双向绑定
|
||||
monacoEditorIns.onDidChangeModelContent(() => {
|
||||
emit('update:modelValue', monacoEditorIns.getModel()?.getValue());
|
||||
modelValue.value = monacoEditorIns.getModel()?.getValue();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
66
frontend/src/components/monaco/MonacoEditorBox.ts
Normal file
66
frontend/src/components/monaco/MonacoEditorBox.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { VNode, h, render } from 'vue';
|
||||
import MonacoEditorDialog from './MonacoEditorDialog.vue';
|
||||
import * as monaco from 'monaco-editor';
|
||||
|
||||
export type MonacoEditorDialogProps = {
|
||||
content: string;
|
||||
title: string;
|
||||
language: string;
|
||||
height?: string;
|
||||
width?: string;
|
||||
options?: any; // 可选项,如字体大小等
|
||||
canChangeLang?: boolean; // 是否可以切换语言
|
||||
showConfirmButton?: boolean;
|
||||
confirmFn?: Function; // 点击确认的回调函数,入参editor value
|
||||
closeFn?: Function; // 点击取消 或 关闭弹窗的回调函数
|
||||
completionItemProvider?: monaco.languages.CompletionItemProvider; // 自定义补全项
|
||||
};
|
||||
|
||||
const MonacoEditorBox = (props: MonacoEditorDialogProps): void => {
|
||||
const boxId = 'monaco-editor-dialog-id';
|
||||
let boxInstance: VNode;
|
||||
|
||||
const container = document.getElementById(boxId);
|
||||
if (!container) {
|
||||
const container = document.createElement('div');
|
||||
container.id = boxId;
|
||||
|
||||
if (props.showConfirmButton === undefined) {
|
||||
props.showConfirmButton = true;
|
||||
}
|
||||
if (props.canChangeLang === undefined) {
|
||||
props.canChangeLang = true;
|
||||
}
|
||||
|
||||
// 创建 虚拟dom
|
||||
boxInstance = h(MonacoEditorDialog, {
|
||||
...props,
|
||||
modelValue: props.content,
|
||||
'onUpdate:modelValue': (value: string) => {
|
||||
props.content = value;
|
||||
},
|
||||
// 'onUpdate:visible': (value: boolean) => {},
|
||||
visible: true,
|
||||
onClose: () => {
|
||||
// 卸载组件
|
||||
if (boxInstance) {
|
||||
render(null, container);
|
||||
boxInstance = null as any;
|
||||
}
|
||||
// 移除 container DOM 元素
|
||||
document.body.removeChild(container);
|
||||
props.closeFn && props.closeFn();
|
||||
console.log('close editor');
|
||||
},
|
||||
onConfirm: () => {
|
||||
props.confirmFn && props.confirmFn(props.content);
|
||||
},
|
||||
});
|
||||
// 将虚拟dom渲染到 container dom 上
|
||||
render(boxInstance, container);
|
||||
// 最后将 container 追加到 body 上
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
};
|
||||
|
||||
export default MonacoEditorBox;
|
||||
@@ -1,39 +0,0 @@
|
||||
import { VNode, h, render } from 'vue';
|
||||
import MonacoEditorDialogComp from './MonacoEditorDialogComp.vue';
|
||||
|
||||
export type MonacoEditorDialogProps = {
|
||||
content: string;
|
||||
title: string;
|
||||
language: string;
|
||||
height?: string;
|
||||
width?: string;
|
||||
showConfirmButton?: boolean;
|
||||
confirmFn?: Function; // 点击确认的回调函数,入参editor value
|
||||
cancelFn?: Function; // 点击取消 或 关闭弹窗的回调函数
|
||||
};
|
||||
|
||||
const boxId = 'monaco-editor-dialog-id';
|
||||
|
||||
let boxInstance: VNode;
|
||||
|
||||
const MonacoEditorDialog = (props: MonacoEditorDialogProps): void => {
|
||||
const container = document.getElementById(boxId);
|
||||
if (!container) {
|
||||
const container = document.createElement('div');
|
||||
container.id = boxId;
|
||||
// 创建 虚拟dom
|
||||
boxInstance = h(MonacoEditorDialogComp);
|
||||
// 将虚拟dom渲染到 container dom 上
|
||||
render(boxInstance, container);
|
||||
// 最后将 container 追加到 body 上
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
const boxVue = boxInstance.component;
|
||||
if (boxVue) {
|
||||
// 调用open方法显示弹框,注意不能使用boxVue.ctx来调用组件函数(build打包后ctx会获取不到)
|
||||
boxVue.exposed?.open(props);
|
||||
}
|
||||
};
|
||||
|
||||
export default MonacoEditorDialog;
|
||||
133
frontend/src/components/monaco/MonacoEditorDialog.vue
Normal file
133
frontend/src/components/monaco/MonacoEditorDialog.vue
Normal file
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog :title="props.title" v-model="dialogVisible" :width="props.width" @close="close">
|
||||
<monaco-editor
|
||||
ref="editorRef"
|
||||
:height="props.height"
|
||||
class="editor"
|
||||
:language="props.language"
|
||||
v-model="modelValue"
|
||||
:options="props.options"
|
||||
:can-change-mode="props.canChangeLang"
|
||||
/>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">{{ i18n.global.t('common.cancel') }}</el-button>
|
||||
<el-button v-if="props.showConfirmButton" @click="confirm" type="primary">{{ i18n.global.t('common.confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { ElDialog, ElButton, ElMessage } from 'element-plus';
|
||||
// import base style
|
||||
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
|
||||
import { MonacoEditorDialogProps } from './MonacoEditorBox';
|
||||
import { i18n } from '@/i18n';
|
||||
import { registerCompletionItemProvider } from './completionItemProvider';
|
||||
|
||||
const editorRef: any = ref(null);
|
||||
|
||||
const props = defineProps<MonacoEditorDialogProps>();
|
||||
|
||||
const modelValue = defineModel<string>('modelValue', {
|
||||
type: String,
|
||||
default: '',
|
||||
});
|
||||
|
||||
const dialogVisible = defineModel<boolean>('visible', {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['close', 'confirm']);
|
||||
|
||||
watch(
|
||||
() => props.language,
|
||||
() => {
|
||||
// 格式化输出html;
|
||||
const language = props.language;
|
||||
if (language === 'html' || language == 'xml') {
|
||||
modelValue.value = formatXML(modelValue.value);
|
||||
}
|
||||
|
||||
if (props.completionItemProvider) {
|
||||
registerCompletionItemProvider(language, props.completionItemProvider);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
editorRef.value?.focus();
|
||||
editorRef.value?.format();
|
||||
}, 300);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**
|
||||
* 确认按钮
|
||||
*/
|
||||
const confirm = async () => {
|
||||
let value = modelValue.value;
|
||||
if (props.language === 'json') {
|
||||
let val;
|
||||
try {
|
||||
val = JSON.parse(value);
|
||||
if (typeof val !== 'object') {
|
||||
ElMessage.error('请输入正确的json');
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('请输入正确的json');
|
||||
return;
|
||||
}
|
||||
|
||||
// 压缩json字符串
|
||||
value = JSON.stringify(val);
|
||||
} else if (props.language === 'html') {
|
||||
// 压缩html字符串
|
||||
value = compressHTML(value);
|
||||
}
|
||||
|
||||
emit('confirm', value);
|
||||
close();
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
dialogVisible.value = false;
|
||||
emit('close');
|
||||
setTimeout(() => {
|
||||
modelValue.value = '';
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const formatXML = function (xml: string, tab?: string) {
|
||||
let formatted = '',
|
||||
indent = '';
|
||||
tab = tab || ' ';
|
||||
xml.split(/>\s*</).forEach(function (node) {
|
||||
if (node.match(/^\/\w/)) indent = indent.substring(tab!.length);
|
||||
formatted += indent + '<' + node + '>\r\n';
|
||||
if (node.match(/^<?\w[^>]*[^\/]$/)) indent += tab;
|
||||
});
|
||||
return formatted.substring(1, formatted.length - 3);
|
||||
};
|
||||
|
||||
function compressHTML(html: string) {
|
||||
return (
|
||||
html
|
||||
.replace(/[\r\n\t]+/g, ' ') // 移除换行符和制表符
|
||||
// .replace(/<!--[\s\S]*?-->/g, '') // 移除注释
|
||||
.replace(/\s{2,}/g, ' ') // 合并多个空格为一个空格
|
||||
.replace(/>\s+</g, '><')
|
||||
); // 移除标签之间的空格
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.editor {
|
||||
font-size: 9pt;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -1,144 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog :title="state.title" v-model="state.dialogVisible" :width="state.width" @close="cancel">
|
||||
<monaco-editor ref="editorRef" :height="state.height" class="editor" :language="state.language" v-model="contentValue" can-change-mode />
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="cancel">{{ i18n.global.t('common.cancel') }}</el-button>
|
||||
<el-button v-if="state.showConfirmButton" @click="confirm" type="primary">{{ i18n.global.t('common.confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, ref, reactive } from 'vue';
|
||||
import { ElDialog, ElButton, ElMessage } from 'element-plus';
|
||||
// import base style
|
||||
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
|
||||
import { MonacoEditorDialogProps } from './MonacoEditorDialog';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
const editorRef: any = ref(null);
|
||||
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
height: '450px',
|
||||
width: '800px',
|
||||
contentValue: '',
|
||||
title: '',
|
||||
language: '',
|
||||
showConfirmButton: true,
|
||||
});
|
||||
|
||||
let confirmFn: any;
|
||||
let cancelFn: any;
|
||||
|
||||
const { contentValue } = toRefs(state);
|
||||
|
||||
function compressHTML(html: string) {
|
||||
return (
|
||||
html
|
||||
.replace(/[\r\n\t]+/g, ' ') // 移除换行符和制表符
|
||||
// .replace(/<!--[\s\S]*?-->/g, '') // 移除注释
|
||||
.replace(/\s{2,}/g, ' ') // 合并多个空格为一个空格
|
||||
.replace(/>\s+</g, '><')
|
||||
); // 移除标签之间的空格
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认按钮
|
||||
*/
|
||||
const confirm = async () => {
|
||||
if (confirmFn) {
|
||||
if (state.language === 'json') {
|
||||
let val;
|
||||
try {
|
||||
val = JSON.parse(contentValue.value);
|
||||
if (typeof val !== 'object') {
|
||||
ElMessage.error('请输入正确的json');
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('请输入正确的json');
|
||||
return;
|
||||
}
|
||||
|
||||
// 压缩json字符串
|
||||
confirmFn(JSON.stringify(val));
|
||||
} else if (state.language === 'html') {
|
||||
// 压缩html字符串
|
||||
confirmFn(compressHTML(contentValue.value));
|
||||
} else {
|
||||
confirmFn(contentValue.value);
|
||||
}
|
||||
}
|
||||
state.dialogVisible = false;
|
||||
setTimeout(() => {
|
||||
state.contentValue = '';
|
||||
state.title = '';
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
state.dialogVisible = false;
|
||||
// 没有执行成功,并且取消回调函数存在,则执行
|
||||
cancelFn && cancelFn();
|
||||
setTimeout(() => {
|
||||
state.contentValue = '';
|
||||
state.title = '';
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const formatXML = function (xml: string, tab?: string) {
|
||||
let formatted = '',
|
||||
indent = '';
|
||||
tab = tab || ' ';
|
||||
xml.split(/>\s*</).forEach(function (node) {
|
||||
if (node.match(/^\/\w/)) indent = indent.substring(tab!.length);
|
||||
formatted += indent + '<' + node + '>\r\n';
|
||||
if (node.match(/^<?\w[^>]*[^\/]$/)) indent += tab;
|
||||
});
|
||||
return formatted.substring(1, formatted.length - 3);
|
||||
};
|
||||
|
||||
const open = (optionProps: MonacoEditorDialogProps) => {
|
||||
confirmFn = optionProps.confirmFn;
|
||||
cancelFn = optionProps.cancelFn;
|
||||
|
||||
if (optionProps.showConfirmButton === undefined) {
|
||||
state.showConfirmButton = true;
|
||||
} else {
|
||||
state.showConfirmButton = optionProps.showConfirmButton;
|
||||
}
|
||||
|
||||
const language = optionProps.language;
|
||||
state.language = language;
|
||||
state.title = optionProps.title;
|
||||
if (optionProps.height) {
|
||||
state.height = optionProps.height;
|
||||
}
|
||||
|
||||
state.contentValue = optionProps.content;
|
||||
// 格式化输出html;
|
||||
if (language === 'html' || language == 'xml') {
|
||||
state.contentValue = formatXML(optionProps.content);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
editorRef.value?.focus();
|
||||
editorRef.value?.format();
|
||||
}, 300);
|
||||
|
||||
state.dialogVisible = true;
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.editor {
|
||||
font-size: 9pt;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user