feat: 统一日志组件 (#5727)

This commit is contained in:
zhengkunwang 2024-07-09 16:29:23 +08:00 committed by zhengkunwang223
parent d91ce3da65
commit f3fac23a84
9 changed files with 146 additions and 257 deletions

View file

@ -1,6 +1,6 @@
<template>
<div>
<div style="display: flex; flex-wrap: wrap">
<div class="flex flex-wrap">
<el-select @change="searchLogs" v-model="logSearch.mode" class="selectWidth">
<template #prefix>{{ $t('container.fetch') }}</template>
<el-option v-for="item in timeOptions" :key="item.label" :value="item.value" :label="item.label" />
@ -25,17 +25,7 @@
{{ $t('commons.button.clean') }}
</el-button>
</div>
<div :style="{ height: `calc(100vh - ${loadHeight()})`, 'margin-top': '10px', 'min-height': '400px' }">
<highlightjs
v-if="showLog"
ref="editorRef"
class="editor-main"
language="JavaScript"
:autodetect="false"
:code="logInfo"
></highlightjs>
</div>
<LogPro v-model="logInfo" :heightDiff="400"></LogPro>
</div>
</template>
@ -45,18 +35,10 @@ import i18n from '@/lang';
import { dateFormatForName, downloadWithContent } from '@/utils/util';
import { onBeforeUnmount, reactive, ref } from 'vue';
import { MsgError, MsgSuccess } from '@/utils/message';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
import LogPro from '@/components/log-pro/index.vue';
const logInfo = ref();
const terminalSocket = ref<WebSocket>();
const editorRef = ref();
const scrollerElement = ref<HTMLElement | null>(null);
const showLog = ref(false);
const loadHeight = () => {
return globalStore.openMenuTabs ? '405px' : '375px';
};
const logSearch = reactive({
isWatch: false,
@ -102,9 +84,6 @@ const searchLogs = async () => {
);
terminalSocket.value.onmessage = (event) => {
logInfo.value += event.data;
nextTick(() => {
scrollerElement.value.scrollTop = scrollerElement.value.scrollHeight;
});
};
};
@ -152,17 +131,6 @@ onBeforeUnmount(() => {
terminalSocket.value?.send('close conn');
});
onMounted(async () => {
await nextTick(() => {
showLog.value = true;
});
if (editorRef.value) {
scrollerElement.value = editorRef.value.$el as HTMLElement;
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
hljsDom.style['min-height'] = '500px';
}
});
defineExpose({
acceptParams,
});

View file

@ -0,0 +1,61 @@
<template>
<highlightjs
ref="editorRef"
language="JavaScript"
:style="customStyle"
:autodetect="false"
:code="content"
></highlightjs>
</template>
<script lang="ts" setup>
import { CSSProperties } from 'vue';
const editorRef = ref();
const scrollerElement = ref<HTMLElement | null>(null);
const props = defineProps({
modelValue: {
type: String,
default: '',
},
heightDiff: {
type: Number,
default: 280,
},
});
const content = ref('');
const customStyle = computed<CSSProperties>(() => ({
width: '100%',
overflow: 'auto',
}));
const initLog = async () => {
await nextTick();
if (editorRef.value && scrollerElement.value == undefined) {
const parentElement = editorRef.value.$el as HTMLElement;
scrollerElement.value = parentElement.querySelector('.hljs') as HTMLElement;
scrollerElement.value.style['min-height'] = '500px';
scrollerElement.value.style['max-height'] = 'calc(100vh - ' + props.heightDiff + 'px)';
}
};
watch(
() => props.modelValue,
async (newValue) => {
if (editorRef.value && scrollerElement.value != undefined && newValue != content.value) {
content.value = newValue;
scrollerElement.value.scrollTop = scrollerElement.value.scrollHeight;
} else {
initLog();
}
},
{ immediate: true },
);
onMounted(() => {
initLog();
});
</script>

View file

@ -31,7 +31,7 @@
<el-option :value="500" :label="500" />
<el-option :value="1000" :label="1000" />
</el-select>
<div class="margin-button" style="float: left">
<div class="margin-button float-left">
<el-checkbox border @change="searchLogs" v-model="logSearch.isWatch">
{{ $t('commons.button.watch') }}
</el-checkbox>
@ -43,14 +43,7 @@
{{ $t('commons.button.clean') }}
</el-button>
</div>
<highlightjs
class="mt-10 editor-main"
ref="editorRef"
language="JavaScript"
:autodetect="false"
:code="logInfo"
></highlightjs>
<LogPro v-model="logInfo"></LogPro>
</template>
<template #footer>
<span class="dialog-footer">
@ -70,6 +63,7 @@ import { ElMessageBox } from 'element-plus';
import { MsgError, MsgSuccess } from '@/utils/message';
import screenfull from 'screenfull';
import { GlobalStore } from '@/store';
import LogPro from '@/components/log-pro/index.vue';
const logVisible = ref(false);
const mobile = computed(() => {
@ -79,8 +73,6 @@ const mobile = computed(() => {
const logInfo = ref<string>('');
const globalStore = GlobalStore();
const terminalSocket = ref<WebSocket>();
const editorRef = ref();
const scrollerElement = ref<HTMLElement | null>(null);
const logSearch = reactive({
isWatch: true,
@ -142,10 +134,6 @@ const searchLogs = async () => {
);
terminalSocket.value.onmessage = (event) => {
logInfo.value += event.data.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '');
nextTick(() => {
initLog();
scrollerElement.value.scrollTop = scrollerElement.value.scrollHeight;
});
};
};
@ -215,13 +203,6 @@ const acceptParams = (props: DialogProps): void => {
onBeforeUnmount(() => {
handleClose();
});
const initLog = () => {
if (editorRef.value && scrollerElement.value == undefined) {
scrollerElement.value = editorRef.value.$el as HTMLElement;
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
hljsDom.style['min-height'] = '500px';
}
};
defineExpose({
acceptParams,

View file

@ -9,7 +9,7 @@
type="primary"
:disabled="mysqlStatus !== 'Running'"
:plain="activeName !== 'status'"
@click="activeName = 'status'"
@click="changeTab('status')"
>
{{ $t('database.currentStatus') }}
</el-button>
@ -17,7 +17,7 @@
type="primary"
:disabled="mysqlStatus !== 'Running'"
:plain="activeName !== 'tuning'"
@click="activeName = 'tuning'"
@click="changeTab('tuning')"
>
{{ $t('database.performanceTuning') }}
</el-button>
@ -28,14 +28,14 @@
type="primary"
:disabled="mysqlStatus !== 'Running'"
:plain="activeName !== 'log'"
@click="activeName = 'log'"
@click="changeTab('log')"
>
{{ $t('database.log') }}
</el-button>
<el-button
type="primary"
:disabled="mysqlStatus !== 'Running'"
@click="jumpToSlowlog"
@click="changeTab('slowLog')"
:plain="activeName !== 'slowLog'"
>
{{ $t('database.slowLog') }}
@ -48,19 +48,7 @@
<template #main>
<div v-if="activeName === 'conf'">
<codemirror
:autofocus="true"
:placeholder="$t('commons.msg.noneData')"
:indent-with-tab="true"
:tabSize="4"
:style="{ height: `calc(100vh - ${loadHeight()})`, 'margin-top': '10px' }"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
v-model="mysqlConf"
/>
<CodemirrorPro v-model="mysqlConf" :heightDiff="400"></CodemirrorPro>
<el-button class="mt-2.5" @click="getDefaultConfig()">
{{ $t('app.defaultConfig') }}
</el-button>
@ -79,9 +67,9 @@
</el-col>
</el-row>
</div>
<Status v-show="activeName === 'status'" ref="statusRef" />
<Variables @loading="changeLoading" v-show="activeName === 'tuning'" ref="variablesRef" />
<div v-show="activeName === 'port'">
<Status v-if="activeName === 'status'" ref="statusRef" />
<Variables @loading="changeLoading" v-if="activeName === 'tuning'" ref="variablesRef" />
<div v-if="activeName === 'port'">
<el-form :model="baseInfo" ref="panelFormRef" label-position="top">
<el-row>
<el-col :span="1"><br /></el-col>
@ -98,11 +86,11 @@
</el-row>
</el-form>
</div>
<ContainerLog v-show="activeName === 'log'" ref="dialogContainerLogRef" />
<ContainerLog v-if="activeName === 'log'" ref="dialogContainerLogRef" />
<SlowLog
@loading="changeLoading"
@refresh="loadBaseInfo"
v-show="activeName === 'slowLog'"
v-if="activeName === 'slowLog'"
ref="slowLogRef"
/>
</template>
@ -140,21 +128,16 @@ import Variables from '@/views/database/mysql/setting/variables/index.vue';
import SlowLog from '@/views/database/mysql/setting/slow-log/index.vue';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { onMounted, reactive, ref } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { loadDBFile, loadDBBaseInfo, loadMysqlVariables, updateDBFile } from '@/api/modules/database';
import { ChangePort, CheckAppInstalled, GetAppDefaultConfig } from '@/api/modules/app';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import router from '@/routers';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
const loading = ref(false);
const extensions = [javascript(), oneDark];
const activeName = ref('conf');
const baseInfo = reactive({
@ -194,13 +177,29 @@ const jumpToConf = async () => {
loadMysqlConf();
};
const loadHeight = () => {
return globalStore.openMenuTabs ? '405px' : '375px';
};
const changeTab = (tab: string) => {
activeName.value = tab;
const jumpToSlowlog = async () => {
activeName.value = 'slowLog';
loadSlowLogs();
switch (tab) {
case 'log':
nextTick(() => {
loadContainerLog(baseInfo.containerID);
});
break;
case 'slowLog':
nextTick(() => {
loadSlowLogs();
});
break;
case 'status':
nextTick(() => {
statusRef.value!.acceptParams({ type: props.type, database: props.database });
});
break;
case 'tuning':
loadVariables();
break;
}
};
const onSubmitChangePort = async () => {
@ -292,7 +291,6 @@ const loadBaseInfo = async () => {
baseInfo.port = res.data?.port;
baseInfo.containerID = res.data?.containerName;
loadMysqlConf();
loadContainerLog(baseInfo.containerID);
};
const changeLoading = (status: boolean) => {
@ -345,11 +343,6 @@ const onLoadInfo = async () => {
mysqlStatus.value = res.data.status;
mysqlVersion.value = res.data.version;
loadBaseInfo();
if (mysqlStatus.value === 'Running') {
loadVariables();
loadSlowLogs();
statusRef.value!.acceptParams({ type: props.type, database: props.database });
}
});
};

View file

@ -10,61 +10,37 @@
/>
</el-form-item>
<el-form-item :label="$t('database.longQueryTime')" v-if="detailShow">
<div style="float: left">
<div class="float-left">
<el-input type="number" v-model.number="variables.long_query_time" />
</div>
<el-button style="float: left; margin-left: 10px" @click="changeSlowLogs">
<el-button class="float-left ml-5" @click="changeSlowLogs">
{{ $t('commons.button.save') }}
</el-button>
<div style="float: left; margin-left: 20px">
<el-checkbox style="margin-top: 2px" :disabled="!currentStatus" border v-model="isWatch">
<div class="float-left ml-10">
<el-checkbox :disabled="!currentStatus" border v-model="isWatch">
{{ $t('commons.button.watch') }}
</el-checkbox>
</div>
<el-button :disabled="!currentStatus" style="margin-left: 20px" @click="onDownload" icon="Download">
<el-button :disabled="!currentStatus" class="ml-20" @click="onDownload" icon="Download">
{{ $t('file.download') }}
</el-button>
</el-form-item>
</el-form>
<codemirror
:autofocus="true"
:placeholder="$t('database.noData')"
:indent-with-tab="true"
:tabSize="4"
:style="{ height: getDynamicHeight(), width: '100%' }"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
@ready="handleReady"
v-model="slowLogs"
:disabled="true"
/>
<LogPro v-model="slowLogs"></LogPro>
<ConfirmDialog @cancel="onCancel" ref="confirmDialogRef" @confirm="onSave"></ConfirmDialog>
</div>
</template>
<script lang="ts" setup>
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
import { onBeforeUnmount, reactive, ref } from 'vue';
import { Database } from '@/api/interface/database';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { loadDBFile, updateMysqlVariables } from '@/api/modules/database';
import { dateFormatForName, downloadWithContent } from '@/utils/util';
import i18n from '@/lang';
import { MsgError, MsgInfo, MsgSuccess } from '@/utils/message';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
import LogPro from '@/components/log-pro/index.vue';
const extensions = [javascript(), oneDark];
const slowLogs = ref();
const view = shallowRef();
const handleReady = (payload) => {
view.value = payload.view;
};
const detailShow = ref();
const currentStatus = ref();
@ -121,20 +97,20 @@ const handleSlowLogs = async () => {
confirmDialogRef.value!.acceptParams(params);
};
const getDynamicHeight = () => {
if (variables.slow_query_log === 'ON') {
if (globalStore.openMenuTabs) {
return `calc(100vh - 467px)`;
} else {
return `calc(100vh - 437px)`;
}
}
if (globalStore.openMenuTabs) {
return `calc(100vh - 413px)`;
} else {
return `calc(100vh - 383px)`;
}
};
// const getDynamicHeight = () => {
// if (variables.slow_query_log === 'ON') {
// if (globalStore.openMenuTabs) {
// return `calc(100vh - 467px)`;
// } else {
// return `calc(100vh - 437px)`;
// }
// }
// if (globalStore.openMenuTabs) {
// return `calc(100vh - 413px)`;
// } else {
// return `calc(100vh - 383px)`;
// }
// };
const changeSlowLogs = () => {
if (!(variables.long_query_time > 0 && variables.long_query_time <= 600)) {
@ -190,13 +166,13 @@ const onDownload = async () => {
const loadMysqlSlowlogs = async () => {
const res = await loadDBFile(currentDB.type + '-slow-logs', currentDB.database);
slowLogs.value = res.data || '';
nextTick(() => {
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
});
});
// nextTick(() => {
// const state = view.value.state;
// view.value.dispatch({
// selection: { anchor: state.doc.length, head: state.doc.length },
// scrollIntoView: true,
// });
// });
};
onBeforeUnmount(() => {

View file

@ -52,7 +52,7 @@
<LayoutContent>
<template #main>
<MainDiv :heightDiff="300">
<MainDiv :heightDiff="3200">
<el-radio-group v-model="confShowType" @change="changeMode">
<el-radio-button value="base">{{ $t('database.baseConf') }}</el-radio-button>
<el-radio-button value="all">{{ $t('database.allConf') }}</el-radio-button>
@ -125,19 +125,12 @@
</el-row>
<div v-if="confShowType === 'all'">
<codemirror
:autofocus="true"
placeholder="# The SSH configuration file does not exist or is empty (/etc/ssh/sshd_config)"
:indent-with-tab="true"
:tabSize="4"
style="margin-top: 10px; height: calc(100vh - 405px)"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
<CodemirrorPro
:heightDiff="400"
class="mt-5"
v-model="sshConf"
/>
placeholder="# The SSH configuration file does not exist or is empty (/etc/ssh/sshd_config)"
></CodemirrorPro>
<el-button :disabled="loading" type="primary" @click="onSaveFile" style="margin-top: 5px">
{{ $t('commons.button.save') }}
</el-button>
@ -155,10 +148,7 @@
<script lang="ts" setup>
import { onMounted, reactive, ref } from 'vue';
import { Codemirror } from 'vue-codemirror';
import FireRouter from '@/views/host/ssh/index.vue';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import PubKey from '@/views/host/ssh/ssh/pubkey/index.vue';
import Root from '@/views/host/ssh/ssh/root/index.vue';
import Port from '@/views/host/ssh/ssh/port/index.vue';
@ -168,10 +158,10 @@ import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { getSSHConf, getSSHInfo, operateSSH, updateSSH, updateSSHByfile } from '@/api/modules/host';
import { ElMessageBox, FormInstance } from 'element-plus';
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
const loading = ref(false);
const formRef = ref();
const extensions = [javascript(), oneDark];
const confShowType = ref('base');
const pubKeyRef = ref();
const portRef = ref();

View file

@ -143,22 +143,7 @@
<el-option :value="500" :label="500" />
<el-option :value="1000" :label="1000" />
</el-select>
<codemirror
ref="mymirror"
:autofocus="true"
:placeholder="$t('cronjob.noLogs')"
:indent-with-tab="true"
:tabSize="4"
style="height: calc(100vh - 498px); width: 100%; margin-top: 5px"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
@ready="handleReady"
v-model="logContent"
:disabled="true"
/>
<LogPro v-model="logContent"></LogPro>
</el-row>
</el-form>
</el-col>
@ -181,9 +166,6 @@
import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
import i18n from '@/lang';
import { ElMessageBox } from 'element-plus';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { MsgSuccess } from '@/utils/message';
import { shortcuts } from '@/utils/shortcuts';
import { Toolbox } from '@/api/interface/toolbox';
@ -197,13 +179,6 @@ const hasRecords = ref();
let timer: NodeJS.Timer | null = null;
const mymirror = ref();
const extensions = [javascript(), oneDark];
const view = shallowRef();
const handleReady = (payload) => {
view.value = payload.view;
};
const recordShow = ref(false);
interface DialogProps {
rowData: Toolbox.ClamInfo;
@ -318,13 +293,6 @@ const loadRecordLog = async () => {
return;
}
logContent.value = res.data;
nextTick(() => {
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
});
});
};
const onClean = async () => {

View file

@ -38,22 +38,14 @@
<el-option :value="500" :label="500" />
<el-option :value="1000" :label="1000" />
</el-select>
<codemirror
:autofocus="true"
:placeholder="$t('commons.msg.noneData')"
:indent-with-tab="true"
:tabSize="4"
:style="{ height: `calc(100vh - ${loadHeight()})`, 'margin-top': '10px' }"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
@ready="handleReady"
:extensions="extensions"
<CodemirrorPro
:heightDiff="400"
class="mt-5"
v-model="content"
:disabled="!canUpdate()"
/>
<el-button type="primary" style="margin-top: 10px" v-if="canUpdate()" @click="onSave">
:placeholder="$t('commons.msg.noneData')"
></CodemirrorPro>
<el-button type="primary" class="mt-5" v-if="canUpdate()" @click="onSave">
{{ $t('commons.button.save') }}
</el-button>
</div>
@ -66,36 +58,19 @@
<script lang="ts" setup>
import { nextTick, onMounted, ref, shallowRef } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import ClamStatus from '@/views/toolbox/clam/status/index.vue';
import { searchClamFile, updateClamFile } from '@/api/modules/toolbox';
import { oneDark } from '@codemirror/theme-one-dark';
import { GlobalStore } from '@/store';
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
const globalStore = GlobalStore();
const loading = ref(false);
const extensions = [javascript(), oneDark];
const view = shallowRef();
const handleReady = (payload) => {
view.value = payload.view;
};
const activeName = ref('clamd');
const tail = ref(200);
const content = ref();
const confirmRef = ref();
const loadHeight = () => {
let height = globalStore.openMenuTabs ? '405px' : '375px';
if (!canUpdate()) {
height = globalStore.openMenuTabs ? '383px' : '353px';
}
return height;
};
const canUpdate = () => {
return activeName.value.indexOf('-log') === -1;
};
@ -110,13 +85,6 @@ const search = async (itemName?: string) => {
.then((res) => {
loading.value = false;
content.value = res.data;
nextTick(() => {
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
});
});
})
.catch(() => {
loading.value = false;

View file

@ -1,28 +1,12 @@
<template>
<div v-loading="loading">
<codemirror
:autofocus="true"
:placeholder="$t('commons.msg.noneData')"
:indent-with-tab="true"
:tabSize="4"
style="height: calc(100vh - 375px)"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
v-model="content"
/>
<LogPro v-model="content" :heightDiff="320"></LogPro>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { GetSupervisorLog } from '@/api/modules/host-tool';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
const extensions = [javascript(), oneDark];
import LogPro from '@/components/log-pro/index.vue';
let content = ref('');
let loading = ref(false);