mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 15:30:25 +08:00
fix: machine file & i18n & icon
This commit is contained in:
@@ -11,15 +11,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@vueuse/core": "^12.0.0",
|
||||
"@vueuse/core": "^12.3.0",
|
||||
"asciinema-player": "^3.8.1",
|
||||
"axios": "^1.6.2",
|
||||
"clipboard": "^2.0.11",
|
||||
"cropperjs": "^1.6.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"echarts": "^5.5.1",
|
||||
"element-plus": "^2.9.1",
|
||||
"echarts": "^5.6.0",
|
||||
"element-plus": "^2.9.2",
|
||||
"js-base64": "^3.7.7",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -37,7 +37,7 @@
|
||||
"trzsz": "^1.1.5",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^10.0.5",
|
||||
"vue-i18n": "^11.0.1",
|
||||
"vue-router": "^4.5.0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"xterm": "^5.3.0",
|
||||
@@ -60,9 +60,9 @@
|
||||
"eslint": "^8.35.0",
|
||||
"eslint-plugin-vue": "^9.31.0",
|
||||
"prettier": "^3.2.5",
|
||||
"sass": "^1.82.0",
|
||||
"sass": "^1.83.1",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.0.6",
|
||||
"vite": "^6.0.7",
|
||||
"vue-eslint-parser": "^9.4.3"
|
||||
},
|
||||
"browserslist": [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const allSvgIcons = import.meta.glob('./**/*.svg', { eager: true, as: 'raw' });
|
||||
const allSvgIcons = import.meta.glob('./**/*.svg', { eager: true, query: 'raw' });
|
||||
|
||||
const iconNames = [];
|
||||
|
||||
@@ -45,7 +45,7 @@ function convertSvgToSymbol(svgString, symbolId) {
|
||||
// 转为 df/input
|
||||
const name = path.replace('.svg', '').replace(/^\.\//, '');
|
||||
iconNames.push(`icon ${name}`);
|
||||
svgsymbols += convertSvgToSymbol(allSvgIcons[path], name);
|
||||
svgsymbols += convertSvgToSymbol(allSvgIcons[path].default, name);
|
||||
}
|
||||
svgsymbols += '</svg>';
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ export const I18nEnum = {
|
||||
export const ResourceTypeEnum = {
|
||||
Machine: EnumValue.of(1, '机器').setExtra({ icon: 'Monitor', iconColor: 'var(--el-color-primary)' }).tagTypeSuccess(),
|
||||
Db: EnumValue.of(2, '数据库实例').setExtra({ icon: 'Coin', iconColor: 'var(--el-color-warning)' }).tagTypeWarning(),
|
||||
Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'iconfont icon-redis', iconColor: 'var(--el-color-danger)' }).tagTypeInfo(),
|
||||
Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'iconfont icon-mongo', iconColor: 'var(--el-color-success)' }).tagTypeDanger(),
|
||||
Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'icon redis/redis', iconColor: 'var(--el-color-danger)' }).tagTypeInfo(),
|
||||
Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'icon mongo/mongo', iconColor: 'var(--el-color-success)' }).tagTypeDanger(),
|
||||
};
|
||||
|
||||
// 标签关联的资源类型
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
virtual-triggering
|
||||
>
|
||||
<template #default>
|
||||
<div class="ml5 mt5">{{ $t(title) }}</div>
|
||||
<div class="icon-selector-warp">
|
||||
<div class="ml10 mt10">{{ title }}</div>
|
||||
<el-tabs v-model="state.fontIconTabActive" @tab-click="onIconClick">
|
||||
<el-tab-pane lazy label="ele" name="ele">
|
||||
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
|
||||
@@ -57,7 +57,7 @@ const props = defineProps({
|
||||
// 输入框占位文本
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: () => '请输入内容搜索图标或者选择图标',
|
||||
default: () => 'components.iconSelector.placeholder',
|
||||
},
|
||||
// 输入框占位文本
|
||||
size: {
|
||||
@@ -67,7 +67,7 @@ const props = defineProps({
|
||||
// 弹窗标题
|
||||
title: {
|
||||
type: String,
|
||||
default: () => '请选择图标',
|
||||
default: () => 'components.iconSelector.title',
|
||||
},
|
||||
// 禁用
|
||||
disabled: {
|
||||
|
||||
@@ -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>
|
||||
@@ -444,5 +444,9 @@ export default {
|
||||
last5runTimes: 'Last 5 running times',
|
||||
calculationing: 'In the calculation result',
|
||||
},
|
||||
iconSelector: {
|
||||
title: 'please select the icon',
|
||||
placeholder: 'please enter content search icon or select icon',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -452,5 +452,9 @@ export default {
|
||||
last5runTimes: '最近5次运行时间',
|
||||
calculationing: '计算结果中',
|
||||
},
|
||||
iconSelector: {
|
||||
title: '请选择图标',
|
||||
placeholder: '请输入内容搜索图标或者选择图标',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -88,8 +88,8 @@
|
||||
}
|
||||
|
||||
// 第三方图标字体间距/大小设置
|
||||
.el-menu-item .iconfont,
|
||||
.el-sub-menu .iconfont,
|
||||
.el-menu-item .icon,
|
||||
.el-sub-menu .icon,
|
||||
.el-menu-item .fa,
|
||||
.el-sub-menu .fa {
|
||||
@include mixins.generalIcon;
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="dataSql" :label="$t('db.srcDataSql')" required>
|
||||
<monaco-editor height="150px" class="task-sql" language="sql" v-model="form.dataSql" />
|
||||
<monaco-editor height="200px" class="task-sql" language="sql" v-model="form.dataSql" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
@@ -90,8 +90,8 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-row class="w100">
|
||||
<el-col :span="12">
|
||||
<el-form-item class="w100" prop="updField">
|
||||
<template #label>
|
||||
{{ $t('db.updateField') }}
|
||||
@@ -103,7 +103,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<el-col :span="12">
|
||||
<el-form-item class="w100" prop="updFieldVal">
|
||||
<template #label>
|
||||
{{ $t('db.updateFieldValue') }}
|
||||
@@ -116,8 +116,12 @@
|
||||
<el-input v-model.trim="form.updFieldVal" :placeholder="$t('db.updateFieldValuePlaceholder')" auto-complete="off" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
|
||||
<el-col :span="8">
|
||||
<el-form-item>
|
||||
<el-row class="w100">
|
||||
<el-col :span="12">
|
||||
<el-form-item class="w100" prop="updFieldSrc">
|
||||
<template #label>
|
||||
{{ $t('db.fieldValueSrc') }}
|
||||
@@ -136,7 +140,7 @@
|
||||
|
||||
<el-tab-pane :label="$t('db.fieldMap')" :name="fieldTab" :disabled="!baseFieldCompleted">
|
||||
<el-form-item prop="fieldMap" :label="$t('db.fieldMap')" required>
|
||||
<el-table :data="form.fieldMap" :max-height="650" size="small">
|
||||
<el-table :data="form.fieldMap" :max-height="fieldMapTableHeight" size="small">
|
||||
<el-table-column prop="src" :label="$t('db.srcField')" :width="200" />
|
||||
<el-table-column prop="target" :label="$t('db.targetField')">
|
||||
<template #default="scope">
|
||||
@@ -159,10 +163,10 @@
|
||||
<EnumSelect :enums="DbDataSyncDuplicateStrategyEnum" v-model="form.duplicateStrategy" @change="handleDuplicateStrategy" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="fieldMap" :label="$t('db.selectSql')">
|
||||
<el-input type="textarea" v-model="state.previewDataSql" readonly :input-style="{ height: '300px' }" />
|
||||
<el-input type="textarea" v-model="state.previewDataSql" readonly :rows="10" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="fieldMap" :label="$t('db.insertSql')">
|
||||
<el-input type="textarea" v-model="state.previewInsertSql" readonly :input-style="{ height: '300px' }" />
|
||||
<el-input type="textarea" v-model="state.previewInsertSql" readonly :rows="10" />
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
@@ -327,9 +331,10 @@ const state = reactive({
|
||||
previewDataSql: '',
|
||||
previewInsertSql: '',
|
||||
previewFieldArr: [] as string[],
|
||||
fieldMapTableHeight: window.innerHeight - 50,
|
||||
});
|
||||
|
||||
const { tabActiveName, form, submitForm } = toRefs(state);
|
||||
const { tabActiveName, form, submitForm, fieldMapTableHeight } = toRefs(state);
|
||||
|
||||
const { isFetching: saveBtnLoading, execute: saveExec } = dbApi.saveDatasyncTask.useApi(submitForm);
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ import { computed, nextTick, ref, Ref } from 'vue';
|
||||
import { ElInput, ElMessage } from 'element-plus';
|
||||
import { DataType } from '../../dialect/index';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import MonacoEditorDialog from '@/components/monaco/MonacoEditorDialog';
|
||||
import MonacoEditorBox from '@/components/monaco/MonacoEditorBox';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -123,7 +123,7 @@ const openEditor = () => {
|
||||
editorOpening.value = true;
|
||||
// 编辑器语言,如:json、html、text
|
||||
let editorLang = getEditorLangByValue(itemValue.value);
|
||||
MonacoEditorDialog({
|
||||
MonacoEditorBox({
|
||||
content: itemValue.value,
|
||||
title: `${t('db.editField')} [${props.columnName}]`,
|
||||
language: editorLang,
|
||||
@@ -131,7 +131,7 @@ const openEditor = () => {
|
||||
itemValue.value = newVal;
|
||||
closeEditorDialog();
|
||||
},
|
||||
cancelFn: closeEditorDialog,
|
||||
closeFn: closeEditorDialog,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@ const columns = [
|
||||
TableColumn.new('name', 'common.name'),
|
||||
TableColumn.new('ipPort', 'Ip:Port').isSlot().setAddWidth(50),
|
||||
TableColumn.new('authCerts[0].username', 'machine.acName').isSlot('authCert').setAddWidth(10),
|
||||
TableColumn.new('status', 'common.status').isSlot(),
|
||||
TableColumn.new('status', 'common.status').isSlot().setAddWidth(5),
|
||||
TableColumn.new('stat', 'machine.runningStat').isSlot().setAddWidth(55),
|
||||
TableColumn.new('fs', 'machine.fs').isSlot().setAddWidth(25),
|
||||
TableColumn.new('remark', 'common.remark'),
|
||||
|
||||
@@ -32,8 +32,8 @@ require (
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/veops/go-ansiterm v0.0.5
|
||||
go.mongodb.org/mongo-driver v1.16.0 // mongo
|
||||
golang.org/x/crypto v0.31.0 // ssh
|
||||
golang.org/x/oauth2 v0.24.0
|
||||
golang.org/x/crypto v0.32.0 // ssh
|
||||
golang.org/x/oauth2 v0.25.0
|
||||
golang.org/x/sync v0.10.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -93,7 +93,7 @@ require (
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
|
||||
golang.org/x/image v0.13.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type DataSyncTask interface {
|
||||
@@ -348,7 +349,7 @@ func (app *dataSyncAppImpl) srcData2TargetDb(srcRes []map[string]any, fieldMap [
|
||||
updFieldVal = srcRes[len(srcRes)-1][strings.ToLower(field)]
|
||||
}
|
||||
|
||||
task.UpdFieldVal = updFieldType.DataType.SQLValue(updFieldVal)
|
||||
task.UpdFieldVal = cast.ToString(updFieldVal)
|
||||
}
|
||||
|
||||
// 如果指定了更新字段,则以更新字段取值
|
||||
|
||||
@@ -479,7 +479,7 @@ func (d *dbSqlExecAppImpl) doUpdate(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
nowRec++
|
||||
res = append(res, row)
|
||||
if nowRec == maxRec {
|
||||
return errorx.NewBiz(fmt.Sprintf("update SQL - the maximum number of updated queries is exceeded: %d", maxRec))
|
||||
return errorx.NewBiz("update SQL - the maximum number of updated queries is exceeded: %d", maxRec)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -535,12 +535,13 @@ func (d *dbSqlExecAppImpl) doDelete(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
}
|
||||
execRecord.Table = tableName
|
||||
|
||||
whereStr := deletestmt.Where.GetText()
|
||||
if whereStr == "" {
|
||||
deleteWhere := deletestmt.Where
|
||||
if deleteWhere == nil {
|
||||
logx.ErrorContext(ctx, "delete SQL - there is no where condition")
|
||||
return d.doExec(ctx, dbConn, sqlExecParam.Sql)
|
||||
}
|
||||
|
||||
whereStr := deleteWhere.GetText()
|
||||
// 查询删除数据
|
||||
selectSql := fmt.Sprintf("SELECT * FROM %s where %s LIMIT 200", tableName+" "+tableAlias, whereStr)
|
||||
_, res, _ := dbConn.QueryContext(ctx, selectSql)
|
||||
|
||||
@@ -67,13 +67,13 @@ func (dbInfo *DbInfo) Conn(meta Meta) (*DbConn, error) {
|
||||
conn, err := meta.GetSqlDb(dbInfo)
|
||||
if err != nil {
|
||||
logx.Errorf("db connection failed: %s:%d/%s, err:%s", dbInfo.Host, dbInfo.Port, database, err.Error())
|
||||
return nil, errorx.NewBiz(fmt.Sprintf("db connection failed: %s", err.Error()))
|
||||
return nil, errorx.NewBiz("db connection failed: %s", err.Error())
|
||||
}
|
||||
|
||||
err = conn.Ping()
|
||||
if err != nil {
|
||||
logx.Errorf("db ping failed: %s:%d/%s, err:%s", dbInfo.Host, dbInfo.Port, database, err.Error())
|
||||
return nil, errorx.NewBiz(fmt.Sprintf("db connection failed: %s", err.Error()))
|
||||
return nil, errorx.NewBiz("db connection failed: %s", err.Error())
|
||||
}
|
||||
|
||||
dbc := &DbConn{Id: GetDbConnId(dbInfo.Id, database), Info: dbInfo}
|
||||
|
||||
@@ -279,7 +279,7 @@ func (m *machineFileAppImpl) WriteFileContent(ctx context.Context, opParam *dto.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f, err := sftpCli.OpenFile(path, os.O_RDWR)
|
||||
f, err := sftpCli.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_TRUNC)
|
||||
if err != nil {
|
||||
return mi, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user