mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-12 08:26:50 +08:00
fix: Fix missing line breaks in copied logs on the log page (#9283)
This commit is contained in:
parent
763df5606b
commit
4f377c04b8
3 changed files with 23 additions and 121 deletions
|
@ -25,15 +25,7 @@
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="log-container" :style="styleVars" ref="logContainer">
|
<div class="log-container" :style="styleVars" ref="logContainer">
|
||||||
<div class="log-spacer" :style="{ height: `${totalHeight}px` }"></div>
|
<CodemirrorPro v-model="logInfo" :lineWrapping="true" :heightDiff="230" :disabled="true"></CodemirrorPro>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -44,7 +36,6 @@ import { dateFormatForName } from '@/utils/util';
|
||||||
import { onUnmounted, reactive, ref } from 'vue';
|
import { onUnmounted, reactive, ref } from 'vue';
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||||
import hightlight from '@/components/log/custom-hightlight/index.vue';
|
|
||||||
import { GlobalStore } from '@/store';
|
import { GlobalStore } from '@/store';
|
||||||
const globalStore = GlobalStore();
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
|
@ -72,8 +63,6 @@ const styleVars = computed(() => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const logVisible = ref(false);
|
const logVisible = ref(false);
|
||||||
const logContainer = ref<HTMLElement | null>(null);
|
|
||||||
const logs = ref<string[]>([]);
|
|
||||||
let eventSource: EventSource | null = null;
|
let eventSource: EventSource | null = null;
|
||||||
const logSearch = reactive({
|
const logSearch = reactive({
|
||||||
isWatch: true,
|
isWatch: true,
|
||||||
|
@ -82,17 +71,7 @@ const logSearch = reactive({
|
||||||
tail: 100,
|
tail: 100,
|
||||||
compose: '',
|
compose: '',
|
||||||
});
|
});
|
||||||
const logHeight = 20;
|
const logInfo = ref<string>('');
|
||||||
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 timeOptions = ref([
|
const timeOptions = ref([
|
||||||
{ label: i18n.global.t('commons.table.all'), value: 'all' },
|
{ label: i18n.global.t('commons.table.all'), value: 'all' },
|
||||||
|
@ -135,7 +114,7 @@ const searchLogs = async () => {
|
||||||
if (!logSearch.isWatch) {
|
if (!logSearch.isWatch) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logs.value = [];
|
logInfo.value = '';
|
||||||
let currentNode = globalStore.currentNode;
|
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}`;
|
let url = `/api/v2/containers/search/log?container=${logSearch.container}&since=${logSearch.mode}&tail=${logSearch.tail}&follow=${logSearch.isWatch}&operateNode=${currentNode}`;
|
||||||
if (logSearch.compose !== '') {
|
if (logSearch.compose !== '') {
|
||||||
|
@ -143,13 +122,7 @@ const searchLogs = async () => {
|
||||||
}
|
}
|
||||||
eventSource = new EventSource(url);
|
eventSource = new EventSource(url);
|
||||||
eventSource.onmessage = (event: MessageEvent) => {
|
eventSource.onmessage = (event: MessageEvent) => {
|
||||||
const data = event.data;
|
logInfo.value += event.data.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '') + '\n';
|
||||||
logs.value.push(data);
|
|
||||||
nextTick(() => {
|
|
||||||
if (logContainer.value) {
|
|
||||||
logContainer.value.scrollTop = logContainer.value.scrollHeight;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
eventSource.onerror = (event: MessageEvent) => {
|
eventSource.onerror = (event: MessageEvent) => {
|
||||||
stopListening();
|
stopListening();
|
||||||
|
@ -199,49 +172,23 @@ const onClean = async () => {
|
||||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||||
type: 'info',
|
type: 'info',
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
console.log(logSearch);
|
|
||||||
await cleanContainerLog(logSearch.container);
|
await cleanContainerLog(logSearch.container);
|
||||||
searchLogs();
|
await searchLogs();
|
||||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
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(() => {
|
onUnmounted(() => {
|
||||||
handleClose();
|
handleClose();
|
||||||
if (logContainer.value) {
|
|
||||||
logContainer.value.removeEventListener('scroll', handleScroll);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const resizeObserver = ref<ResizeObserver | null>(null);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
logSearch.container = props.container;
|
logSearch.container = props.container;
|
||||||
logSearch.compose = props.compose;
|
logSearch.compose = props.compose;
|
||||||
|
|
||||||
logVisible.value = true;
|
logVisible.value = true;
|
||||||
logSearch.tail = 100;
|
logSearch.tail = 100;
|
||||||
logSearch.mode = 'all';
|
logSearch.mode = 'all';
|
||||||
logSearch.isWatch = true;
|
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();
|
searchLogs();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -264,8 +211,7 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-container {
|
.log-container {
|
||||||
height: calc(100vh - var(--custom-height, 320px));
|
overflow-y: hidden;
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #1e1e1e;
|
background-color: #1e1e1e;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="firstLoading">
|
<div v-loading="firstLoading">
|
||||||
<div v-if="defaultButton">
|
<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') }}
|
{{ $t('commons.button.watch') }}
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
<el-button
|
<el-button
|
||||||
|
@ -17,26 +17,23 @@
|
||||||
<slot name="button"></slot>
|
<slot name="button"></slot>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="log-container" ref="logContainer" @scroll="onScroll" :style="containerStyle">
|
<div class="log-container" ref="logContainer" :style="containerStyle">
|
||||||
<div class="log-spacer" :style="{ height: `${totalHeight}px` }"></div>
|
<CodemirrorPro
|
||||||
<div
|
v-if="true"
|
||||||
v-for="(log, index) in visibleLogs"
|
v-model="logInfo"
|
||||||
:key="startIndex + index"
|
:lineWrapping="true"
|
||||||
class="log-item"
|
:heightDiff="330"
|
||||||
:style="{ top: `${(startIndex + index) * logHeight}px` }"
|
:disabled="true"
|
||||||
>
|
></CodemirrorPro>
|
||||||
<hightlight :log="log" :type="config.colorMode ?? 'nginx'"></hightlight>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<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 { downloadFile } from '@/utils/util';
|
||||||
import { readByLine } from '@/api/modules/files';
|
import { readByLine } from '@/api/modules/files';
|
||||||
import { GlobalStore } from '@/store';
|
import { GlobalStore } from '@/store';
|
||||||
import bus from '@/global/bus';
|
import bus from '@/global/bus';
|
||||||
import hightlight from '@/components/log/custom-hightlight/index.vue';
|
|
||||||
const globalStore = GlobalStore();
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
interface LogProps {
|
interface LogProps {
|
||||||
|
@ -129,34 +126,10 @@ const minPage = ref(0);
|
||||||
let timer: NodeJS.Timer | null = null;
|
let timer: NodeJS.Timer | null = null;
|
||||||
const logPath = ref('');
|
const logPath = ref('');
|
||||||
|
|
||||||
|
const logInfo = ref<string>('');
|
||||||
const firstLoading = ref(false);
|
const firstLoading = ref(false);
|
||||||
const logs = ref<string[]>([]);
|
const logs = ref<string[]>([]);
|
||||||
const logContainer = ref<HTMLElement | null>(null);
|
|
||||||
const logHeight = 20;
|
|
||||||
const logCount = ref(0);
|
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 = () => {
|
const changeLoading = () => {
|
||||||
loading.value = !loading.value;
|
loading.value = !loading.value;
|
||||||
|
@ -169,10 +142,8 @@ const onDownload = async () => {
|
||||||
changeLoading();
|
changeLoading();
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeTail = (fromOutSide: boolean) => {
|
const changeTail = () => {
|
||||||
if (fromOutSide) {
|
|
||||||
tailLog.value = !tailLog.value;
|
tailLog.value = !tailLog.value;
|
||||||
}
|
|
||||||
if (tailLog.value) {
|
if (tailLog.value) {
|
||||||
timer = setInterval(() => {
|
timer = setInterval(() => {
|
||||||
getContent(false);
|
getContent(false);
|
||||||
|
@ -212,7 +183,7 @@ const getContent = async (pre: boolean) => {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
firstLoading.value = false;
|
firstLoading.value = false;
|
||||||
}
|
}
|
||||||
|
logInfo.value = res.data.content;
|
||||||
logPath.value = res.data.path;
|
logPath.value = res.data.path;
|
||||||
firstLoading.value = false;
|
firstLoading.value = false;
|
||||||
|
|
||||||
|
@ -255,15 +226,6 @@ const getContent = async (pre: boolean) => {
|
||||||
logs.value = pre ? [...newLogs, ...logs.value] : [...logs.value, ...newLogs];
|
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;
|
logCount.value = logs.value.length;
|
||||||
|
@ -317,7 +279,7 @@ const init = async () => {
|
||||||
tailLog.value = false;
|
tailLog.value = false;
|
||||||
}
|
}
|
||||||
if (tailLog.value) {
|
if (tailLog.value) {
|
||||||
changeTail(false);
|
changeTail();
|
||||||
}
|
}
|
||||||
readReq.latest = true;
|
readReq.latest = true;
|
||||||
await getContent(false);
|
await getContent(false);
|
||||||
|
@ -331,12 +293,6 @@ onMounted(async () => {
|
||||||
logs.value = [];
|
logs.value = [];
|
||||||
firstLoading.value = true;
|
firstLoading.value = true;
|
||||||
await init();
|
await init();
|
||||||
nextTick(() => {
|
|
||||||
if (logContainer.value) {
|
|
||||||
logContainer.value.scrollTop = totalHeight.value;
|
|
||||||
containerHeight.value = logContainer.value.getBoundingClientRect().height;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -347,7 +303,7 @@ defineExpose({ changeTail, onDownload, clearLog });
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.log-container {
|
.log-container {
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: var(--panel-logs-bg-color);
|
background-color: var(--panel-logs-bg-color);
|
||||||
|
|
|
@ -351,7 +351,7 @@
|
||||||
<PruneDialog @search="search" ref="dialogPruneRef" />
|
<PruneDialog @search="search" ref="dialogPruneRef" />
|
||||||
|
|
||||||
<RenameDialog @search="search" ref="dialogRenameRef" />
|
<RenameDialog @search="search" ref="dialogRenameRef" />
|
||||||
<ContainerLogDialog ref="dialogContainerLogRef" :highlightDiff="235" />
|
<ContainerLogDialog ref="dialogContainerLogRef" :highlightDiff="210" />
|
||||||
<UpgradeDialog @search="search" ref="dialogUpgradeRef" />
|
<UpgradeDialog @search="search" ref="dialogUpgradeRef" />
|
||||||
<CommitDialog @search="search" ref="dialogCommitRef" />
|
<CommitDialog @search="search" ref="dialogCommitRef" />
|
||||||
<MonitorDialog ref="dialogMonitorRef" />
|
<MonitorDialog ref="dialogMonitorRef" />
|
||||||
|
|
Loading…
Add table
Reference in a new issue