This is an automated email from the ASF dual-hosted git repository.
wuzhiguo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bigtop-manager.git
The following commit(s) were added to refs/heads/main by this push:
new 5ecd17f BIGTOP-4168: Add log progress messages on the task log page
(#25)
5ecd17f is described below
commit 5ecd17faebf2512687ba9b853691b5662e87f68a
Author: Fdefined <[email protected]>
AuthorDate: Thu Jul 25 17:32:35 2024 +0800
BIGTOP-4168: Add log progress messages on the task log page (#25)
---
bigtop-manager-ui/src/api/sse/index.ts | 3 +-
.../{locales/en_US/index.ts => api/sse/types.ts} | 17 +--
bigtop-manager-ui/src/components/job-info/job.vue | 163 +++++++++++++--------
.../src/components/job-info/stage.vue | 46 ++----
.../src/components/job-info/task-log.vue | 34 +++--
bigtop-manager-ui/src/components/job-info/task.vue | 39 ++---
.../src/composables/use-base-table.ts | 76 ++++++++++
bigtop-manager-ui/src/locales/en_US/index.ts | 4 +-
.../src/locales/en_US/{index.ts => job.ts} | 15 +-
bigtop-manager-ui/src/locales/zh_CN/index.ts | 4 +-
.../src/locales/{en_US/index.ts => zh_CN/job.ts} | 15 +-
bigtop-manager-ui/src/utils/tools.ts | 5 +-
12 files changed, 249 insertions(+), 172 deletions(-)
diff --git a/bigtop-manager-ui/src/api/sse/index.ts
b/bigtop-manager-ui/src/api/sse/index.ts
index 52ee663..22975e5 100644
--- a/bigtop-manager-ui/src/api/sse/index.ts
+++ b/bigtop-manager-ui/src/api/sse/index.ts
@@ -19,12 +19,13 @@
import axios, { type AxiosProgressEvent, type CancelTokenSource } from 'axios'
import request from '@/api/request.ts'
+import type { LogsRes } from './types'
export const getLogs = (
clusterId: number,
id: number,
func: Function
-): { promise: Promise<any>; cancel: () => void } => {
+): LogsRes => {
const source: CancelTokenSource = axios.CancelToken.source()
const promise = request({
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts
b/bigtop-manager-ui/src/api/sse/types.ts
similarity index 69%
copy from bigtop-manager-ui/src/locales/en_US/index.ts
copy to bigtop-manager-ui/src/api/sse/types.ts
index 1f8ed5f..6f83d3e 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/api/sse/types.ts
@@ -17,18 +17,7 @@
* under the License.
*/
-import common from '@/locales/en_US/common.ts'
-import login from '@/locales/en_US/login'
-import user from '@/locales/en_US/user.ts'
-import cluster from '@/locales/en_US/cluster.ts'
-import hosts from '@/locales/en_US/hosts.ts'
-import service from '@/locales/en_US/service.ts'
-
-export default {
- common,
- login,
- user,
- cluster,
- hosts,
- service
+export interface LogsRes {
+ promise: Promise<any>
+ cancel: () => void
}
diff --git a/bigtop-manager-ui/src/components/job-info/job.vue
b/bigtop-manager-ui/src/components/job-info/job.vue
index d2a2b34..e645c60 100644
--- a/bigtop-manager-ui/src/components/job-info/job.vue
+++ b/bigtop-manager-ui/src/components/job-info/job.vue
@@ -18,9 +18,8 @@
-->
<script setup lang="ts">
- import { ref, watch, computed, reactive, toRaw, toRefs, nextTick } from 'vue'
+ import { ref, watch, computed, toRefs, nextTick } from 'vue'
import { useClusterStore } from '@/store/cluster'
- import { PaginationConfig } from 'ant-design-vue/es/pagination/Pagination'
import { storeToRefs } from 'pinia'
import {
JobVO,
@@ -36,8 +35,10 @@
import Stage from './stage.vue'
import Task from './task.vue'
import TaskLog from './task-log.vue'
+ import useBaseTable from '@/composables/use-base-table'
+ import type { TableColumnType, TablePaginationConfig } from 'ant-design-vue'
- const columns = [
+ const columns: TableColumnType[] = [
{
title: 'common.name',
dataIndex: 'name',
@@ -77,14 +78,14 @@
const emits = defineEmits(['update:visible', 'closed'])
- const loading = ref(false)
+ const showLogAwaitMsg = ref(false)
+ const isComplete = ref(false)
const stages = ref<StageVO[]>([])
const tasks = ref<TaskVO[]>([])
const breadcrumbs = ref<any[]>([{ name: 'Job Info' }])
const currTaskInfo = ref<TaskVO>()
- const jobs = ref<JobVO[]>([])
const intervalId = ref<Pausable | undefined>()
- const logRef = ref<InstanceType<typeof TaskLog> | null>()
+ const logRef = ref<InstanceType<typeof TaskLog> | null>(null)
const currPage = ref<string[]>([
'isJobTable',
'isStageTable',
@@ -92,12 +93,14 @@
'isTaskLogs'
])
- const paginationProps = reactive<PaginationConfig>({})
- const jobsPageState = reactive<Pagination>({
- pageNum: 1,
- pageSize: 10,
- sort: 'desc'
- })
+ const {
+ loading,
+ columnsProp,
+ dataSource: jobs,
+ paginationProps,
+ onChange,
+ resetState
+ } = useBaseTable<JobVO>(columns, [])
const getCurrPage = computed(() => {
return currPage.value[breadcrumbs.value.length - 1]
@@ -105,10 +108,9 @@
watch(visible, (val) => {
if (val) {
+ resetState()
loading.value = true
- Object.assign(paginationProps, initPagedProps())
- checkMetaOrigin(outerData.value ? true : false)
- loading.value = false
+ checkDataOrigin(outerData.value ? true : false)
}
})
@@ -127,12 +129,13 @@
}
})
- const checkMetaOrigin = (isOuter = false) => {
+ const checkDataOrigin = (isOuter = false) => {
if (isOuter) {
const { meta, currItem } = outerData.value as OuterData
jobs.value = meta
clickJob(meta[0])
clickStage(currItem as StageVO)
+ loading.value = false
return
}
getJobsList()
@@ -155,16 +158,21 @@
const getJobsList = async () => {
try {
- const params = { ...toRaw(jobsPageState) }
- const { content, total } = await getJobs(clusterId.value, params)
+ const params = {
+ pageNum: paginationProps.value.current,
+ pageSize: paginationProps.value.pageSize,
+ sort: 'desc'
+ } as Pagination
+ const { content } = await getJobs(clusterId.value, params)
jobs.value = content.map((v) => {
return {
...v
}
})
- paginationProps.total = total
loading.value = false
} catch (error) {
+ console.log('error :>> ', error)
+ } finally {
loading.value = false
}
}
@@ -173,7 +181,7 @@
breadcrumbs.value.push(record)
currTaskInfo.value = record
await nextTick()
- logRef.value?.getLogsInfo(record.id)
+ logRef.value?.getLogInfo(record.id)
}
const clickJob = (record: JobVO) => {
@@ -191,52 +199,47 @@
breadcrumbs.value.splice(idx + 1, len)
}
- const handlePageChange = (page: number) => {
- paginationProps.current = page
- jobsPageState.pageNum = page
- loading.value = true
- getJobsList()
+ const handleClose = () => {
+ intervalId.value?.pause()
+ breadcrumbs.value = [{ name: 'Job Info' }]
+ resetState()
+ emits('update:visible', false)
}
- const handlePageSizeChange = (_current: number, size: number) => {
- paginationProps.pageSize = size
- jobsPageState.pageSize = size
- loading.value = true
- getJobsList()
+ const setShowLogAwaitMsg = (status: boolean) => {
+ showLogAwaitMsg.value = status
}
- const initPagedProps = () => {
- return {
- current: 1,
- pageSize: 10,
- size: 'small',
- showSizeChanger: true,
- pageSizeOptions: ['10', '20', '30', '40', '50'],
- total: 0,
- onChange: handlePageChange,
- onShowSizeChange: handlePageSizeChange
- }
+ const onLogComplete = (status: boolean) => {
+ isComplete.value = status
}
- const handleClose = () => {
- intervalId.value?.pause()
- breadcrumbs.value = [{ name: 'Job Info' }]
- jobs.value = []
- Object.assign(jobsPageState, {
- pageNum: 1,
- pageSize: 10,
- sort: 'desc'
- })
- emits('update:visible', false)
+ const onTableChange = (pagination: TablePaginationConfig) => {
+ onChange(pagination)
+ loading.value = true
+ getJobsList()
}
</script>
<template>
<a-modal :open="props.visible" width="95%" @cancel="handleClose">
<template #footer>
- <a-button key="back" type="primary" @click="handleClose">
- {{ $t('common.confirm') }}
- </a-button>
+ <div :class="{ 'footer-btns': showLogAwaitMsg }">
+ <div
+ v-if="showLogAwaitMsg"
+ class="logs-wait-msg"
+ :class="{ 'loading-dot': !isComplete }"
+ >
+ {{
+ isComplete
+ ? $t('job.log_complete_message')
+ : $t('job.log_await_message')
+ }}
+ </div>
+ <a-button key="back" type="primary" @click="handleClose">
+ {{ $t('common.confirm') }}
+ </a-button>
+ </div>
</template>
<div class="breadcrumb">
@@ -250,15 +253,15 @@
</a-breadcrumb-item>
</a-breadcrumb>
</div>
-
<a-table
v-show="getCurrPage == 'isJobTable'"
:scroll="{ y: 500 }"
- :pagination="paginationProps"
:loading="loading"
:data-source="jobs"
- :columns="columns"
+ :columns="columnsProp"
+ :pagination="paginationProps"
destroy-on-close
+ @change="onTableChange"
>
<template #headerCell="{ column }">
<span>{{ $t(column.title) }}</span>
@@ -285,12 +288,56 @@
<task :columns="columns" :tasks="tasks" @click-task="clickTask" />
</template>
<template v-if="getCurrPage == 'isTaskLogs'">
- <task-log ref="logRef" />
+ <task-log
+ ref="logRef"
+ @vue:before-unmount="setShowLogAwaitMsg"
+ @on-log-receive="setShowLogAwaitMsg"
+ @on-log-complete="onLogComplete"
+ />
</template>
</a-modal>
</template>
<style lang="scss" scoped>
+ .footer-btns {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ }
+
+ .logs-wait-msg {
+ margin: 0;
+ padding: 0;
+ }
+
+ .loading-dot {
+ &::after {
+ content: '';
+ animation: wait 5s 0s infinite;
+ }
+ }
+
+ @keyframes wait {
+ 16% {
+ content: '.';
+ }
+ 32% {
+ content: '. .';
+ }
+ 48% {
+ content: '. . .';
+ }
+ 64% {
+ content: '. . . .';
+ }
+ 80% {
+ content: '. . . . .';
+ }
+ 96% {
+ content: '. . . . . .';
+ }
+ }
.breadcrumb {
:deep(.ant-breadcrumb) {
margin-bottom: 16px !important;
diff --git a/bigtop-manager-ui/src/components/job-info/stage.vue
b/bigtop-manager-ui/src/components/job-info/stage.vue
index 80942e6..183bfac 100644
--- a/bigtop-manager-ui/src/components/job-info/stage.vue
+++ b/bigtop-manager-ui/src/components/job-info/stage.vue
@@ -19,21 +19,20 @@
<script lang="ts" setup>
import { StageVO } from '@/api/job/types.ts'
- import { ref, reactive, watch, computed } from 'vue'
- import { PaginationConfig } from 'ant-design-vue/es/pagination/Pagination'
+ import { watch } from 'vue'
import CustomProgress from './custom-progress.vue'
+ import useBaseTable from '@/composables/use-base-table'
+ import type { TableColumnType } from 'ant-design-vue'
interface StageProps {
stages: StageVO[]
- columns: any
+ columns: TableColumnType[]
}
- const props = withDefaults(defineProps<StageProps>(), {})
- const loading = ref(false)
- const pagedList = computed(() => {
- return props.stages
- })
-
+ const props = defineProps<StageProps>()
+ const baseTable = useBaseTable<StageVO>(props.columns, props.stages)
+ const { dataSource, columnsProp, loading, paginationProps, onChange } =
+ baseTable
watch(
() => props.stages,
(val) => {
@@ -48,26 +47,6 @@
deep: true
}
)
-
- const handlePageChange = (page: number) => {
- paginationProps.current = page
- }
-
- const handlePageSizeChange = (_current: number, size: number) => {
- paginationProps.pageSize = size
- }
-
- const paginationProps = reactive<PaginationConfig>({
- current: 1,
- pageSize: 10,
- size: 'small',
- showSizeChanger: true,
- pageSizeOptions: ['10', '20', '30', '40', '50'],
- total: pagedList.value.length,
- onChange: handlePageChange,
- onShowSizeChange: handlePageSizeChange
- })
-
const emits = defineEmits(['clickStage'])
const clickStage = (record: StageVO) => {
emits('clickStage', record)
@@ -77,11 +56,12 @@
<template>
<div class="stage-info">
<a-table
- :pagination="paginationProps"
- :data-source="pagedList"
- :columns="props.columns"
- :loading="loading"
:scroll="{ y: 500 }"
+ :loading="loading"
+ :columns="columnsProp"
+ :data-source="dataSource"
+ :pagination="paginationProps"
+ @change="onChange"
>
<template #headerCell="{ column }">
<span>{{ $t(column.title) }}</span>
diff --git a/bigtop-manager-ui/src/components/job-info/task-log.vue
b/bigtop-manager-ui/src/components/job-info/task-log.vue
index 7665639..36f1bfd 100644
--- a/bigtop-manager-ui/src/components/job-info/task-log.vue
+++ b/bigtop-manager-ui/src/components/job-info/task-log.vue
@@ -36,6 +36,8 @@
const logMeta = ref<string>('')
const canceler = ref<Canceler>()
+ const emit = defineEmits(['onLogReceive', 'onLogComplete'])
+
watch(logMeta, (val) => {
logText.value = val
.split('\n\n')
@@ -45,16 +47,23 @@
.join('\n')
})
- const getLogsInfo = async (id: number) => {
- const { cancel } = getLogs(clusterId.value, id, getLogProgress)
+ const getLogInfo = async (id: number) => {
+ const { cancel, promise } = getLogs(clusterId.value, id, onLogReceive)
canceler.value = cancel
+ promise.then(onLogComplete)
+ emit('onLogReceive', true)
}
- const getLogProgress = ({ event }: AxiosProgressEvent) => {
+ const onLogReceive = ({ event }: AxiosProgressEvent) => {
logMeta.value = event.target.responseText
scrollToBottom(logsInfoRef.value)
}
+ const onLogComplete = (res: string | undefined) => {
+ cancelSseConnect()
+ emit('onLogComplete', res != undefined)
+ }
+
const cancelSseConnect = () => {
if (!canceler.value) {
return
@@ -78,10 +87,12 @@
onBeforeUnmount(() => {
cancelSseConnect()
+ emit('onLogReceive', false)
+ emit('onLogComplete', false)
})
defineExpose({
- getLogsInfo
+ getLogInfo
})
</script>
@@ -98,8 +109,10 @@
</a-button>
</div>
</div>
- <div class="logs_info">
- <pre id="logs" ref="logsInfoRef">{{ logText }}</pre>
+ <div ref="logsInfoRef" class="logs_info">
+ <div id="logs">
+ <pre>{{ logText }}</pre>
+ </div>
</div>
</div>
</template>
@@ -127,14 +140,17 @@
background-color: #f5f5f5;
border-radius: 4px;
position: relative;
- pre {
+ & > div {
height: 100%;
margin: 0;
padding: 16px 14px;
box-sizing: border-box;
- color: #444;
border-color: #eee;
- line-height: 16px;
+ box-sizing: border-box;
+ pre {
+ line-height: 20px;
+ color: #444;
+ }
}
}
}
diff --git a/bigtop-manager-ui/src/components/job-info/task.vue
b/bigtop-manager-ui/src/components/job-info/task.vue
index d5e5b88..79d01a3 100644
--- a/bigtop-manager-ui/src/components/job-info/task.vue
+++ b/bigtop-manager-ui/src/components/job-info/task.vue
@@ -18,9 +18,9 @@
-->
<script setup lang="ts">
- import { computed, reactive, ref } from 'vue'
- import { PaginationConfig } from 'ant-design-vue/es/pagination/Pagination'
import { TaskVO, State } from '@/api/job/types.ts'
+ import useBaseTable from '@/composables/use-base-table'
+ import type { TableColumnType } from 'ant-design-vue'
import {
CheckCircleTwoTone,
@@ -31,30 +31,13 @@
interface TaskProps {
tasks: TaskVO[]
- columns: any
+ columns: TableColumnType[]
}
- const props = withDefaults(defineProps<TaskProps>(), {})
- const pagedList = ref(computed(() => props.tasks))
-
- const handlePageChange = (page: number) => {
- paginationProps.current = page
- }
-
- const handlePageSizeChange = (_current: number, size: number) => {
- paginationProps.pageSize = size
- }
-
- const paginationProps = reactive<PaginationConfig>({
- current: 1,
- pageSize: 10,
- size: 'small',
- showSizeChanger: true,
- pageSizeOptions: ['10', '20', '30', '40', '50'],
- total: pagedList.value.length,
- onChange: handlePageChange,
- onShowSizeChange: handlePageSizeChange
- })
+ const props = defineProps<TaskProps>()
+ const baseTable = useBaseTable<TaskVO>(props.columns, props.tasks)
+ const { dataSource, columnsProp, loading, paginationProps, onChange } =
+ baseTable
const emits = defineEmits(['clickTask'])
const clickTask = (record: TaskVO) => {
@@ -65,10 +48,12 @@
<template>
<div class="task-info">
<a-table
- :pagination="paginationProps"
- :data-source="pagedList"
- :columns="props.columns"
:scroll="{ y: 500 }"
+ :loading="loading"
+ :columns="columnsProp"
+ :data-source="dataSource"
+ :pagination="paginationProps"
+ @change="onChange"
>
<template #headerCell="{ column }">
<span>{{ $t(column.title) }}</span>
diff --git a/bigtop-manager-ui/src/composables/use-base-table.ts
b/bigtop-manager-ui/src/composables/use-base-table.ts
new file mode 100644
index 0000000..1efb742
--- /dev/null
+++ b/bigtop-manager-ui/src/composables/use-base-table.ts
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { ref, onUnmounted } from 'vue'
+import type { TablePaginationConfig, TableColumnType } from 'ant-design-vue'
+
+const useBaseTable = <T>(
+ columns: TableColumnType[],
+ rows?: T[],
+ pagination?: TablePaginationConfig | false | undefined
+) => {
+ const loading = ref(false)
+ const dataSource = ref<T[]>(rows || [])
+ const columnsProp = ref<TableColumnType[]>(columns)
+ const paginationProps = ref<TablePaginationConfig>({
+ current: 1,
+ pageSize: 10,
+ total: dataSource.value.length,
+ size: 'small',
+ showSizeChanger: true,
+ pageSizeOptions: ['10', '20', '30', '40', '50']
+ })
+
+ // merge pagination config
+ if (pagination) {
+ paginationProps.value = Object.assign(paginationProps.value, pagination)
+ }
+
+ const onChange = (pagination: TablePaginationConfig) => {
+ paginationProps.value = Object.assign(paginationProps.value, pagination)
+ }
+
+ const resetState = () => {
+ loading.value = false
+ dataSource.value = []
+ paginationProps.value = {
+ current: 1,
+ pageSize: 10,
+ total: dataSource.value.length || 0,
+ size: 'small',
+ showSizeChanger: true,
+ pageSizeOptions: ['10', '20', '30', '40', '50']
+ }
+ }
+
+ onUnmounted(() => {
+ resetState()
+ })
+
+ return {
+ columnsProp,
+ dataSource,
+ loading,
+ paginationProps,
+ onChange,
+ resetState
+ }
+}
+
+export default useBaseTable
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts
b/bigtop-manager-ui/src/locales/en_US/index.ts
index 1f8ed5f..7fc6f12 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/locales/en_US/index.ts
@@ -23,6 +23,7 @@ import user from '@/locales/en_US/user.ts'
import cluster from '@/locales/en_US/cluster.ts'
import hosts from '@/locales/en_US/hosts.ts'
import service from '@/locales/en_US/service.ts'
+import job from '@/locales/en_US/job.ts'
export default {
common,
@@ -30,5 +31,6 @@ export default {
user,
cluster,
hosts,
- service
+ service,
+ job
}
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts
b/bigtop-manager-ui/src/locales/en_US/job.ts
similarity index 71%
copy from bigtop-manager-ui/src/locales/en_US/index.ts
copy to bigtop-manager-ui/src/locales/en_US/job.ts
index 1f8ed5f..52b7e99 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/locales/en_US/job.ts
@@ -17,18 +17,7 @@
* under the License.
*/
-import common from '@/locales/en_US/common.ts'
-import login from '@/locales/en_US/login'
-import user from '@/locales/en_US/user.ts'
-import cluster from '@/locales/en_US/cluster.ts'
-import hosts from '@/locales/en_US/hosts.ts'
-import service from '@/locales/en_US/service.ts'
-
export default {
- common,
- login,
- user,
- cluster,
- hosts,
- service
+ log_await_message: 'Waiting for more logs',
+ log_complete_message: 'Done!'
}
diff --git a/bigtop-manager-ui/src/locales/zh_CN/index.ts
b/bigtop-manager-ui/src/locales/zh_CN/index.ts
index 7521822..7ebd07b 100644
--- a/bigtop-manager-ui/src/locales/zh_CN/index.ts
+++ b/bigtop-manager-ui/src/locales/zh_CN/index.ts
@@ -23,6 +23,7 @@ import user from '@/locales/zh_CN/user.ts'
import cluster from '@/locales/zh_CN/cluster.ts'
import hosts from '@/locales/zh_CN/hosts.ts'
import service from '@/locales/zh_CN/service.ts'
+import job from '@/locales/zh_CN/job.ts'
export default {
common,
@@ -30,5 +31,6 @@ export default {
user,
cluster,
hosts,
- service
+ service,
+ job
}
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts
b/bigtop-manager-ui/src/locales/zh_CN/job.ts
similarity index 71%
copy from bigtop-manager-ui/src/locales/en_US/index.ts
copy to bigtop-manager-ui/src/locales/zh_CN/job.ts
index 1f8ed5f..9dff298 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/locales/zh_CN/job.ts
@@ -17,18 +17,7 @@
* under the License.
*/
-import common from '@/locales/en_US/common.ts'
-import login from '@/locales/en_US/login'
-import user from '@/locales/en_US/user.ts'
-import cluster from '@/locales/en_US/cluster.ts'
-import hosts from '@/locales/en_US/hosts.ts'
-import service from '@/locales/en_US/service.ts'
-
export default {
- common,
- login,
- user,
- cluster,
- hosts,
- service
+ log_await_message: '获取日志中 ',
+ log_complete_message: '完成 !'
}
diff --git a/bigtop-manager-ui/src/utils/tools.ts
b/bigtop-manager-ui/src/utils/tools.ts
index 033fc37..6dc36df 100644
--- a/bigtop-manager-ui/src/utils/tools.ts
+++ b/bigtop-manager-ui/src/utils/tools.ts
@@ -52,6 +52,7 @@ export const scrollToBottom = (container: HTMLElement | null)
=> {
if (!container) {
return
}
- const { clientHeight, scrollHeight } = container
- container.scrollTop = scrollHeight - clientHeight
+ requestAnimationFrame(() => {
+ container.scrollTop = container.scrollHeight
+ })
}