From ad4a64a35cd717fdf427a6c7e589737a2679bd1f Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Sun, 15 Feb 2026 09:59:15 +0800 Subject: [PATCH 1/2] feat: Enhance log container UI and functionality Updated the log container component to improve the user interface with a new toolbar layout, added copy mode functionality, and included additional log display options. The sidebar component was also modified to remove the unique-opened property. In the compose view, introduced an environment editing option and improved the save confirmation dialog with a force pull checkbox. --- .../src/components/log/container/index.vue | 181 +++++++++++++----- .../src/layout/components/Sidebar/index.vue | 1 - .../src/views/container/compose/index.vue | 86 ++++++--- 3 files changed, 195 insertions(+), 73 deletions(-) diff --git a/frontend/src/components/log/container/index.vue b/frontend/src/components/log/container/index.vue index cb7fb659c0f0..8a466c453be5 100644 --- a/frontend/src/components/log/container/index.vue +++ b/frontend/src/components/log/container/index.vue @@ -1,44 +1,63 @@ @@ -97,6 +116,8 @@ const styleVars = computed(() => ({ const logVisible = ref(false); const logContainer = ref(null); const logs = ref([]); +const isCopyMode = ref(false); +const copyPendingLogs = ref([]); let eventSource: EventSource | null = null; const logSearch = reactive({ isWatch: props.defaultFollow ? true : true, @@ -148,16 +169,42 @@ const stopListening = () => { const handleClose = async () => { stopListening(); + isCopyMode.value = false; + copyPendingLogs.value = []; logVisible.value = false; }; +const flushCopyPendingLogs = () => { + if (copyPendingLogs.value.length === 0) { + return; + } + logs.value.push(...copyPendingLogs.value); + copyPendingLogs.value = []; +}; + +const toggleCopyMode = () => { + if (isCopyMode.value) { + isCopyMode.value = false; + flushCopyPendingLogs(); + nextTick(() => { + if (logContainer.value) { + logContainer.value.scrollTop = logContainer.value.scrollHeight; + } + }); + return; + } + isCopyMode.value = true; +}; + const searchLogs = async () => { if (Number(logSearch.tail) < 0) { MsgError(i18n.global.t('container.linesHelper')); return; } stopListening(); + isCopyMode.value = false; logs.value = []; + copyPendingLogs.value = []; let currentNode = globalStore.currentNode; if (props.node && props.node !== '') { currentNode = props.node; @@ -169,6 +216,10 @@ const searchLogs = async () => { eventSource = new EventSource(url); eventSource.onmessage = (event: MessageEvent) => { const data = event.data; + if (isCopyMode.value) { + copyPendingLogs.value.push(data); + return; + } logs.value.push(data); nextTick(() => { if (logContainer.value) { @@ -290,20 +341,50 @@ onMounted(() => { diff --git a/frontend/src/layout/components/Sidebar/index.vue b/frontend/src/layout/components/Sidebar/index.vue index 71e30f6fc2ba..96e69045ed4c 100644 --- a/frontend/src/layout/components/Sidebar/index.vue +++ b/frontend/src/layout/components/Sidebar/index.vue @@ -16,7 +16,6 @@ :router="true" :collapse="isCollapse" :collapse-transition="false" - :unique-opened="true" @select="handleMenuClick" class="custom-menu" > diff --git a/frontend/src/views/container/compose/index.vue b/frontend/src/views/container/compose/index.vue index 1e40fc878a89..97dc85b5d7ea 100644 --- a/frontend/src/views/container/compose/index.vue +++ b/frontend/src/views/container/compose/index.vue @@ -221,10 +221,18 @@ - - {{ $t('container.compose') }} - {{ $t('commons.button.log') }} - +
+ + {{ $t('container.compose') }} + {{ $t('commons.button.log') }} + + {{ $t('container.env') }} + + + + {{ $t('commons.button.save') }} + +
- {{ $t('container.env') }} - - - {{ $t('container.composeEnvHelper2') }} - -
- - {{ $t('container.forcePullHelper') }} -
- - - {{ $t('commons.button.save') }} -
@@ -277,6 +266,20 @@ :defaultFollow="true" />
+ +
+ {{ $t('container.env') }} + + + {{ $t('container.composeEnvHelper2') }} + +