fix: Fix missing line breaks in copied logs on the log page (#9283)

This commit is contained in:
2025-06-25 22:05:12 +08:00 committed by GitHub
parent 763df5606b
commit 4f377c04b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 23 additions and 121 deletions

View file

@ -25,15 +25,7 @@
</el-button>
</div>
<div class="log-container" :style="styleVars" ref="logContainer">
<div class="log-spacer" :style="{ height: `${totalHeight}px` }"></div>
<div
v-for="(log, index) in visibleLogs"
:key="startIndex + index"
class="log-item"
:style="{ top: `${(startIndex + index) * logHeight}px` }"
>
<hightlight :log="log" type="container"></hightlight>
</div>
<CodemirrorPro v-model="logInfo" :lineWrapping="true" :heightDiff="230" :disabled="true"></CodemirrorPro>
</div>
</template>
@ -44,7 +36,6 @@ import { dateFormatForName } from '@/utils/util';
import { onUnmounted, reactive, ref } from 'vue';
import { ElMessageBox } from 'element-plus';
import { MsgError, MsgSuccess } from '@/utils/message';
import hightlight from '@/components/log/custom-hightlight/index.vue';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
@ -72,8 +63,6 @@ const styleVars = computed(() => ({
}));
const logVisible = ref(false);
const logContainer = ref<HTMLElement | null>(null);
const logs = ref<string[]>([]);
let eventSource: EventSource | null = null;
const logSearch = reactive({
isWatch: true,
@ -82,17 +71,7 @@ const logSearch = reactive({
tail: 100,
compose: '',
});
const logHeight = 20;
const logCount = computed(() => logs.value.length);
const totalHeight = computed(() => logHeight * logCount.value);
const startIndex = ref(0);
const containerHeight = ref(500);
const visibleCount = computed(() => Math.ceil(containerHeight.value / logHeight) + 2);
const visibleLogs = computed(() => {
const start = Math.max(0, startIndex.value - 1);
const end = startIndex.value + visibleCount.value + 1;
return logs.value.slice(start, end);
});
const logInfo = ref<string>('');
const timeOptions = ref([
{ label: i18n.global.t('commons.table.all'), value: 'all' },
@ -135,7 +114,7 @@ const searchLogs = async () => {
if (!logSearch.isWatch) {
return;
}
logs.value = [];
logInfo.value = '';
let currentNode = globalStore.currentNode;
let url = `/api/v2/containers/search/log?container=${logSearch.container}&since=${logSearch.mode}&tail=${logSearch.tail}&follow=${logSearch.isWatch}&operateNode=${currentNode}`;
if (logSearch.compose !== '') {
@ -143,13 +122,7 @@ const searchLogs = async () => {
}
eventSource = new EventSource(url);
eventSource.onmessage = (event: MessageEvent) => {
const data = event.data;
logs.value.push(data);
nextTick(() => {
if (logContainer.value) {
logContainer.value.scrollTop = logContainer.value.scrollHeight;
}
});
logInfo.value += event.data.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '') + '\n';
};
eventSource.onerror = (event: MessageEvent) => {
stopListening();
@ -199,49 +172,23 @@ const onClean = async () => {
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(async () => {
console.log(logSearch);
await cleanContainerLog(logSearch.container);
searchLogs();
await searchLogs();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
});
};
const handleScroll = () => {
if (logContainer.value) {
const scrollTop = logContainer.value.scrollTop;
startIndex.value = Math.max(0, Math.floor(scrollTop / logHeight) - 1);
}
};
onUnmounted(() => {
handleClose();
if (logContainer.value) {
logContainer.value.removeEventListener('scroll', handleScroll);
}
});
const resizeObserver = ref<ResizeObserver | null>(null);
onMounted(() => {
logSearch.container = props.container;
logSearch.compose = props.compose;
logVisible.value = true;
logSearch.tail = 100;
logSearch.mode = 'all';
logSearch.isWatch = true;
nextTick(() => {
if (logContainer.value) {
containerHeight.value = logContainer.value.clientHeight;
logContainer.value.addEventListener('scroll', handleScroll);
resizeObserver.value = new ResizeObserver((entries) => {
containerHeight.value = entries[0].contentRect.height;
});
resizeObserver.value.observe(logContainer.value);
}
});
searchLogs();
});
</script>
@ -264,8 +211,7 @@ onMounted(() => {
}
.log-container {
height: calc(100vh - var(--custom-height, 320px));
overflow-y: auto;
overflow-y: hidden;
overflow-x: auto;
position: relative;
background-color: #1e1e1e;

View file

@ -1,7 +1,7 @@
<template>
<div v-loading="firstLoading">
<div v-if="defaultButton">
<el-checkbox border v-model="tailLog" class="float-left" @change="changeTail(false)" v-if="showTail">
<el-checkbox border v-model="tailLog" class="float-left" @change="changeTail()" v-if="showTail">
{{ $t('commons.button.watch') }}
</el-checkbox>
<el-button
@ -17,26 +17,23 @@
<slot name="button"></slot>
</span>
</div>
<div class="log-container" ref="logContainer" @scroll="onScroll" :style="containerStyle">
<div class="log-spacer" :style="{ height: `${totalHeight}px` }"></div>
<div
v-for="(log, index) in visibleLogs"
:key="startIndex + index"
class="log-item"
:style="{ top: `${(startIndex + index) * logHeight}px` }"
>
<hightlight :log="log" :type="config.colorMode ?? 'nginx'"></hightlight>
</div>
<div class="log-container" ref="logContainer" :style="containerStyle">
<CodemirrorPro
v-if="true"
v-model="logInfo"
:lineWrapping="true"
:heightDiff="330"
:disabled="true"
></CodemirrorPro>
</div>
</div>
</template>
<script lang="ts" setup>
import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
import { onMounted, onUnmounted, reactive, ref } from 'vue';
import { downloadFile } from '@/utils/util';
import { readByLine } from '@/api/modules/files';
import { GlobalStore } from '@/store';
import bus from '@/global/bus';
import hightlight from '@/components/log/custom-hightlight/index.vue';
const globalStore = GlobalStore();
interface LogProps {
@ -129,34 +126,10 @@ const minPage = ref(0);
let timer: NodeJS.Timer | null = null;
const logPath = ref('');
const logInfo = ref<string>('');
const firstLoading = ref(false);
const logs = ref<string[]>([]);
const logContainer = ref<HTMLElement | null>(null);
const logHeight = 20;
const logCount = ref(0);
const totalHeight = computed(() => logHeight * logCount.value);
const containerHeight = ref(500);
const visibleCount = computed(() => Math.ceil(containerHeight.value / logHeight));
const startIndex = ref(0);
const visibleLogs = computed(() => {
return logs.value.slice(startIndex.value, startIndex.value + visibleCount.value);
});
const onScroll = () => {
if (logContainer.value) {
const scrollTop = logContainer.value.scrollTop;
if (scrollTop == 0) {
readReq.page = minPage.value - 1;
if (readReq.page < 1) {
return;
}
minPage.value = readReq.page;
getContent(true);
}
startIndex.value = Math.floor(scrollTop / logHeight);
}
};
const changeLoading = () => {
loading.value = !loading.value;
@ -169,10 +142,8 @@ const onDownload = async () => {
changeLoading();
};
const changeTail = (fromOutSide: boolean) => {
if (fromOutSide) {
const changeTail = () => {
tailLog.value = !tailLog.value;
}
if (tailLog.value) {
timer = setInterval(() => {
getContent(false);
@ -212,7 +183,7 @@ const getContent = async (pre: boolean) => {
isLoading.value = false;
firstLoading.value = false;
}
logInfo.value = res.data.content;
logPath.value = res.data.path;
firstLoading.value = false;
@ -255,15 +226,6 @@ const getContent = async (pre: boolean) => {
logs.value = pre ? [...newLogs, ...logs.value] : [...logs.value, ...newLogs];
}
}
nextTick(() => {
if (pre) {
logContainer.value.scrollTop = 2000;
} else {
logContainer.value.scrollTop = totalHeight.value;
containerHeight.value = logContainer.value.getBoundingClientRect().height;
}
});
}
logCount.value = logs.value.length;
@ -317,7 +279,7 @@ const init = async () => {
tailLog.value = false;
}
if (tailLog.value) {
changeTail(false);
changeTail();
}
readReq.latest = true;
await getContent(false);
@ -331,12 +293,6 @@ onMounted(async () => {
logs.value = [];
firstLoading.value = true;
await init();
nextTick(() => {
if (logContainer.value) {
logContainer.value.scrollTop = totalHeight.value;
containerHeight.value = logContainer.value.getBoundingClientRect().height;
}
});
});
onUnmounted(() => {
@ -347,7 +303,7 @@ defineExpose({ changeTail, onDownload, clearLog });
</script>
<style lang="scss" scoped>
.log-container {
overflow-y: auto;
overflow-y: hidden;
overflow-x: auto;
position: relative;
background-color: var(--panel-logs-bg-color);

View file

@ -351,7 +351,7 @@
<PruneDialog @search="search" ref="dialogPruneRef" />
<RenameDialog @search="search" ref="dialogRenameRef" />
<ContainerLogDialog ref="dialogContainerLogRef" :highlightDiff="235" />
<ContainerLogDialog ref="dialogContainerLogRef" :highlightDiff="210" />
<UpgradeDialog @search="search" ref="dialogUpgradeRef" />
<CommitDialog @search="search" ref="dialogCommitRef" />
<MonitorDialog ref="dialogMonitorRef" />