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 6e907c43 BIGTOP-4509: Fix query wrong components for a service (#281)
6e907c43 is described below
commit 6e907c43fd19f926755bc94ebd8cbdc91ea011c5
Author: Fdefined <[email protected]>
AuthorDate: Tue Oct 14 18:49:05 2025 +0800
BIGTOP-4509: Fix query wrong components for a service (#281)
---
.../create-cluster/components/check-workflow.vue | 13 +--
.../components/component-installer.vue | 14 +--
bigtop-manager-ui/src/features/job-modal/index.vue | 10 +-
bigtop-manager-ui/src/features/job/index.vue | 36 +++----
.../src/features/metric/category-chart.vue | 17 ++--
.../src/features/metric/gauge-chart.vue | 13 +--
.../src/features/service-management/components.vue | 75 +++++++++------
.../src/features/service-management/configs.vue | 29 +++---
.../src/features/service-management/index.vue | 78 +++++++--------
.../src/features/service-management/overview.vue | 107 ++++++++++++++-------
.../src/pages/cluster-manage/cluster/host.vue | 23 +++--
.../src/pages/cluster-manage/cluster/index.vue | 35 ++++---
.../src/pages/cluster-manage/cluster/overview.vue | 96 +++++++++---------
.../src/pages/cluster-manage/cluster/service.vue | 35 +++----
.../src/pages/cluster-manage/cluster/user.vue | 10 +-
.../src/pages/cluster-manage/hosts/detail.vue | 11 +--
.../src/pages/cluster-manage/hosts/index.vue | 12 +--
.../src/pages/cluster-manage/hosts/overview.vue | 42 +++++---
.../pages/cluster-manage/infrastructures/index.vue | 2 +-
.../cluster-manage/infrastructures/service.vue | 49 +++++-----
bigtop-manager-ui/src/store/tab-state/index.ts | 29 ++++--
bigtop-manager-ui/src/utils/constant.ts | 30 ++++++
22 files changed, 422 insertions(+), 344 deletions(-)
diff --git
a/bigtop-manager-ui/src/features/create-cluster/components/check-workflow.vue
b/bigtop-manager-ui/src/features/create-cluster/components/check-workflow.vue
index da18fbae..48ad2b7b 100644
---
a/bigtop-manager-ui/src/features/create-cluster/components/check-workflow.vue
+++
b/bigtop-manager-ui/src/features/create-cluster/components/check-workflow.vue
@@ -20,6 +20,7 @@
<script setup lang="ts">
import { Empty } from 'ant-design-vue'
import { getJobDetails, retryJob } from '@/api/job'
+ import { JOB_STATUS } from '@/utils/constant'
import LogsView, { type LogViewProps } from '@/features/log-view/index.vue'
@@ -39,14 +40,6 @@
open: false
})
- const status = shallowRef<Record<StateType, string>>({
- Pending: 'installing',
- Processing: 'processing',
- Failed: 'failed',
- Canceled: 'canceled',
- Successful: 'success'
- })
-
const stages = computed(() => {
if (jobDetail.value.stages) {
return [...jobDetail.value.stages].sort((a, b) => a.order! - b.order!)
@@ -144,14 +137,14 @@
<div class="stage-item">
<span>{{ stage.name }}</span>
<div style="min-width: 100px">
- <svg-icon :name="stage.state &&
status[stage.state]"></svg-icon>
+ <svg-icon :name="stage.state &&
JOB_STATUS[stage.state]"></svg-icon>
</div>
</div>
</template>
<div v-for="task in stage.tasks" :key="task.id" class="task-item">
<span>{{ task.name }}</span>
<a-space :size="16">
- <svg-icon :name="task.state && status[task.state]"></svg-icon>
+ <svg-icon :name="task.state &&
JOB_STATUS[task.state]"></svg-icon>
<div style="min-width: 62px">
<a-button
v-if="task.state && !['Canceled',
'Pending'].includes(task.state)"
diff --git
a/bigtop-manager-ui/src/features/create-service/components/component-installer.vue
b/bigtop-manager-ui/src/features/create-service/components/component-installer.vue
index be1f9de2..08826e43 100644
---
a/bigtop-manager-ui/src/features/create-service/components/component-installer.vue
+++
b/bigtop-manager-ui/src/features/create-service/components/component-installer.vue
@@ -21,6 +21,8 @@
import { Empty } from 'ant-design-vue'
import { getJobDetails, retryJob } from '@/api/job'
import { CommandVO } from '@/api/command/types'
+
+ import { JOB_STATUS } from '@/utils/constant'
import { useCreateServiceStore } from '@/store/create-service'
import LogsView, { type LogViewProps } from '@/features/log-view/index.vue'
@@ -31,6 +33,7 @@
const { t } = useI18n()
const createStore = useCreateServiceStore()
+
const activeKey = ref<number[]>([])
const jobDetail = ref<JobVO>({})
const spinning = ref(false)
@@ -38,13 +41,6 @@
const logsViewState = reactive<LogViewProps>({
open: false
})
- const status = shallowRef<Record<StateType, string>>({
- Pending: 'installing',
- Processing: 'processing',
- Failed: 'failed',
- Canceled: 'canceled',
- Successful: 'success'
- })
const stages = computed(() => {
if (jobDetail.value.stages) {
@@ -144,14 +140,14 @@
<div class="stage-item">
<span>{{ stage.name }}</span>
<div style="min-width: 100px">
- <svg-icon :name="stage.state &&
status[stage.state]"></svg-icon>
+ <svg-icon :name="stage.state &&
JOB_STATUS[stage.state]"></svg-icon>
</div>
</div>
</template>
<div v-for="task in stage.tasks" :key="task.id" class="task-item">
<span>{{ task.name }}</span>
<a-space :size="16">
- <svg-icon :name="task.state && status[task.state]"></svg-icon>
+ <svg-icon :name="task.state &&
JOB_STATUS[task.state]"></svg-icon>
<div style="min-width: 62px">
<a-button
v-if="task.state && !['Canceled',
'Pending'].includes(task.state)"
diff --git a/bigtop-manager-ui/src/features/job-modal/index.vue
b/bigtop-manager-ui/src/features/job-modal/index.vue
index 5f95b32f..14ef730c 100644
--- a/bigtop-manager-ui/src/features/job-modal/index.vue
+++ b/bigtop-manager-ui/src/features/job-modal/index.vue
@@ -20,6 +20,7 @@
<script setup lang="ts">
import { TableColumnType, TableProps } from 'ant-design-vue'
import LogsView, { type LogViewProps } from '@/features/log-view/index.vue'
+ import { JOB_STATUS } from '@/utils/constant'
import type { JobVO, StageVO, StateType, TaskListParams, TaskVO } from
'@/api/job/types'
import type { CommandRes, JobStageProgressItem } from '@/store/job-progress'
@@ -43,13 +44,6 @@
const { t } = useI18n()
const breadcrumbs = ref<BreadcrumbItem[]>([])
- const status = shallowRef<Record<StateType, string>>({
- Pending: 'installing',
- Processing: 'processing',
- Failed: 'failed',
- Canceled: 'canceled',
- Successful: 'success'
- })
const apiMap = shallowRef([{ key: 'jobId' }, { key: 'stageId' }])
const logsViewState = reactive<LogViewProps>({ open: false })
@@ -212,7 +206,7 @@
</template>
<template v-if="column.key === 'state'">
<job-progress v-if="breadcrumbLen === 1" :key="record.id"
:state="text" :progress-data="record.progress" />
- <svg-icon v-else :name="status[record.state as
StateType]"></svg-icon>
+ <svg-icon v-else :name="JOB_STATUS[record.state as
StateType]"></svg-icon>
</template>
</template>
</a-table>
diff --git a/bigtop-manager-ui/src/features/job/index.vue
b/bigtop-manager-ui/src/features/job/index.vue
index 9003f5ce..ff7f5c40 100644
--- a/bigtop-manager-ui/src/features/job/index.vue
+++ b/bigtop-manager-ui/src/features/job/index.vue
@@ -20,11 +20,11 @@
<script setup lang="ts">
import { TableColumnType, TableProps } from 'ant-design-vue'
import { getJobList, getStageList, getTaskList, retryJob } from '@/api/job'
+ import { POLLING_INTERVAL } from '@/utils/constant'
import LogsView, { type LogViewProps } from '@/features/log-view/index.vue'
import type { JobVO, StageVO, StateType, TaskListParams, TaskVO } from
'@/api/job/types'
- import type { ClusterVO } from '@/api/cluster/types'
import type { ListParams } from '@/api/types'
interface BreadcrumbItem {
@@ -33,16 +33,16 @@
pagination: ListParams
}
- const POLLING_INTERVAL = 3000
const { t } = useI18n()
+ const route = useRoute()
const { confirmModal } = useModal()
- const clusterInfo = useAttrs() as ClusterVO
+ const clusterId = ref(Number(route.params.id ?? 0))
const pollingIntervalId = ref<any>(null)
const breadcrumbs = ref<BreadcrumbItem[]>([
{
name: computed(() => t('job.job_list')),
- id: `clusterId-${clusterInfo.id}`,
+ id: `clusterId-${clusterId.value}`,
pagination: {
pageNum: 1,
pageSize: 10,
@@ -51,19 +51,15 @@
}
])
const apiMap = ref([
- {
- key: 'clusterId',
- api: getJobList
- },
- {
- key: 'jobId',
- api: getStageList
- },
- {
- key: 'stageId',
- api: getTaskList
- }
+ { key: 'clusterId', api: getJobList },
+ { key: 'jobId', api: getStageList },
+ { key: 'stageId', api: getTaskList }
])
+
+ const logsViewState = reactive<LogViewProps>({
+ open: false
+ })
+
const status = shallowRef<Record<StateType, string>>({
Pending: 'installing',
Processing: 'processing',
@@ -71,9 +67,7 @@
Canceled: 'canceled',
Successful: 'success'
})
- const logsViewState = reactive<LogViewProps>({
- open: false
- })
+
const breadcrumbLen = computed(() => breadcrumbs.value.length)
const currBreadcrumb = computed(() => breadcrumbs.value.at(-1))
const columns = computed((): TableColumnType[] => [
@@ -217,7 +211,7 @@
getListData(true)
pollingIntervalId.value = setInterval(() => {
getListData()
- }, POLLING_INTERVAL)
+ }, POLLING_INTERVAL / 10)
}
const stopPolling = () => {
@@ -232,7 +226,7 @@
tipText: t('job.retry'),
async onOk() {
try {
- const state = await retryJob({ jobId: row.id!, clusterId:
clusterInfo.id! })
+ const state = await retryJob({ jobId: row.id!, clusterId:
clusterId.value! })
row.state = state.state
} catch (error) {
console.log('error :>> ', error)
diff --git a/bigtop-manager-ui/src/features/metric/category-chart.vue
b/bigtop-manager-ui/src/features/metric/category-chart.vue
index 5185df2e..fa9dbfd7 100644
--- a/bigtop-manager-ui/src/features/metric/category-chart.vue
+++ b/bigtop-manager-ui/src/features/metric/category-chart.vue
@@ -155,25 +155,26 @@
}))
}
- onMounted(() => {
- const selector = document.getElementById(`${chartId.value}`)
- if (selector) {
- initChart(selector!, option.value)
- }
- })
+ const renderChart = () => {
+ const el = document.getElementById(chartId.value)
+ if (el) initChart(el, option.value)
+ }
+
+ onMounted(renderChart)
+ onActivated(renderChart)
watchEffect(() => {
let series = [] as any,
legend = { data: [] } as any
- const { series: temp_series = [] } = data.value
+ const { series: temp_series = [], valueType } = data.value
const xAxis = xAxisData.value?.map((v) => dayjs(Number(v) *
1000).format('HH:mm')) ?? []
if (legendMap.value) {
legend = new Map(legendMap.value).values()
series = generateChartSeries(data.value, legendMap.value)
} else {
- if (temp_series.length > 1) {
+ if (valueType) {
legend.data = temp_series.map((s) => s.name)
series = [...temp_series]
} else {
diff --git a/bigtop-manager-ui/src/features/metric/gauge-chart.vue
b/bigtop-manager-ui/src/features/metric/gauge-chart.vue
index 73ca8e63..0974c41a 100644
--- a/bigtop-manager-ui/src/features/metric/gauge-chart.vue
+++ b/bigtop-manager-ui/src/features/metric/gauge-chart.vue
@@ -90,12 +90,13 @@
]
})
- onMounted(() => {
- const selector = document.getElementById(`${chartId.value}`)
- if (selector) {
- initChart(document.getElementById(`${chartId.value}`)!, option.value)
- }
- })
+ const renderChart = () => {
+ const el = document.getElementById(chartId.value)
+ if (el) initChart(el, option.value)
+ }
+
+ onMounted(renderChart)
+ onActivated(renderChart)
watchEffect(() => {
setOptions({ series: [{ data: [{ value: percent.value }] }] })
diff --git a/bigtop-manager-ui/src/features/service-management/components.vue
b/bigtop-manager-ui/src/features/service-management/components.vue
index e0653d35..38c75023 100644
--- a/bigtop-manager-ui/src/features/service-management/components.vue
+++ b/bigtop-manager-ui/src/features/service-management/components.vue
@@ -22,6 +22,9 @@
import { deleteComponent, getComponents } from '@/api/component'
import { useStackStore } from '@/store/stack'
import { useJobProgress } from '@/store/job-progress'
+ import { useTabStore } from '@/store/tab-state'
+
+ import { COMPONENT_STATUS, POLLING_INTERVAL } from '@/utils/constant'
import type { GroupItem } from '@/components/common/button-group/types'
import type { ComponentVO } from '@/api/component/types'
@@ -30,6 +33,7 @@
import type { ServiceVO } from '@/api/service/types'
type Key = string | number
+
interface TableState {
selectedRowKeys: Key[]
searchText: string
@@ -37,21 +41,25 @@
selectedRows: ComponentVO[]
}
- const POLLING_INTERVAL = 3000
+ interface RouteParams {
+ id: number
+ serviceId: number
+ }
const { t } = useI18n()
- const { confirmModal } = useModal()
-
- const jobProgressStore = useJobProgress()
- const stackStore = useStackStore()
+ const attrs = useAttrs() as Partial<ServiceVO>
const route = useRoute()
const router = useRouter()
- const attrs = useAttrs() as unknown as Required<ServiceVO> & { clusterId:
number }
+ const tabStore = useTabStore()
+ const jobProgressStore = useJobProgress()
+ const stackStore = useStackStore()
+ const { confirmModal } = useModal()
const { stacks, stackRelationMap } = storeToRefs(stackStore)
+
const searchInputRef = ref()
const pollingIntervalId = ref<any>(null)
- const componentStatus = ref(['INSTALLING', 'SUCCESS', 'FAILED', 'UNKNOWN'])
+ const currTab = ref(tabStore.getActiveTab(route.path) ?? '1')
const commandRequest = shallowRef<CommandRequest>({
command: 'Add',
@@ -66,17 +74,24 @@
selectedRows: []
})
- const componentsFromStack = computed(
- () =>
- new Map(
- stacks.value.flatMap(
- (stack) =>
- stack.services?.flatMap((service) =>
- service.name && service.components ? [[service.name,
service.components]] : []
- ) ?? []
- )
- )
- )
+ const payload = computed(() => {
+ const { id, serviceId } = route.params as unknown as RouteParams
+ return [id, serviceId] as [number, number]
+ })
+
+ const componentsFromStack = computed(() => {
+ const entries: [string, ComponentVO[]][] = []
+
+ stacks.value.forEach(({ services = [] }) => {
+ services.forEach(({ name, components }) => {
+ if (name && components) {
+ entries.push([name, components])
+ }
+ })
+ })
+
+ return new Map(entries)
+ })
const columns = computed((): TableColumnType<ComponentVO>[] => [
{
@@ -85,7 +100,7 @@
key: 'name',
ellipsis: true,
filterMultiple: false,
- filters: [...(componentsFromStack.value.get(attrs.name)?.values() ||
[])]?.map((v) => ({
+ filters: [...(componentsFromStack.value.get(attrs.name!)?.values() ||
[])]?.map((v) => ({
text: v?.displayName || '',
value: v?.name || ''
}))
@@ -182,7 +197,7 @@
text: t('common.stop', [t('common.selected')])
}
],
- dropdownMenuClickEvent: (info) => dropdownMenuClick &&
dropdownMenuClick(info)
+ dropdownMenuClickEvent: (info) => dropdownMenuClick!(info)
}
])
@@ -234,9 +249,10 @@
}
const execOperation = (rows?: ComponentVO[]) => {
+ const [clusterId] = payload.value
const displayNameOfRows: string[] = rows ? rows.map((v) => v.displayName
?? '').filter((v) => v) : []
jobProgressStore.processCommand(
- { ...commandRequest.value, clusterId: attrs.clusterId },
+ { ...commandRequest.value, clusterId },
async () => {
getComponentList(true, true)
state.selectedRowKeys = []
@@ -251,7 +267,8 @@
tipText: t('common.delete_msg'),
async onOk() {
try {
- const data = await deleteComponent({ clusterId: attrs.clusterId, id:
row.id! })
+ const [clusterId] = payload.value
+ const data = await deleteComponent({ clusterId, id: row.id! })
if (data) {
message.success(t('common.delete_success'))
getComponentList(true, true)
@@ -264,7 +281,7 @@
}
const getComponentList = async (isReset = false, isFirstCall = false) => {
- const { clusterId, id: serviceId } = attrs
+ const [clusterId, serviceId] = payload.value
if (!paginationProps.value) {
loading.value = false
return
@@ -297,7 +314,7 @@
getComponentList(isReset, isFirstCall)
pollingIntervalId.value = setInterval(() => {
getComponentList()
- }, POLLING_INTERVAL)
+ }, POLLING_INTERVAL / 10)
}
const stopPolling = () => {
@@ -308,8 +325,9 @@
}
const addComponent = () => {
- const creationMode = Number(attrs.clusterId) === 0 ? 'public' : 'internal'
- const routerName = Number(attrs.clusterId) === 0 ? 'CreateInfraComponent'
: 'CreateComponent'
+ const [clusterId] = payload.value
+ const creationMode = clusterId === 0 ? 'public' : 'internal'
+ const routerName = clusterId === 0 ? 'CreateInfraComponent' :
'CreateComponent'
router.push({
name: routerName,
params: { ...route.params, creationMode, type: 'component' }
@@ -317,6 +335,7 @@
}
onActivated(() => {
+ if (currTab.value != '2') return
startPolling()
})
@@ -369,8 +388,8 @@
</template>
<template #bodyCell="{ record, column }">
<template v-if="['status'].includes(column.key as string)">
- <svg-icon style="margin-left: 0"
:name="componentStatus[record.status].toLowerCase()" />
- <span>{{ t(`common.${componentStatus[record.status].toLowerCase()}`)
}}</span>
+ <svg-icon style="margin-left: 0"
:name="COMPONENT_STATUS[record.status].toLowerCase()" />
+ <span>{{
t(`common.${COMPONENT_STATUS[record.status].toLowerCase()}`) }}</span>
</template>
<template v-if="column.key === 'quickLink'">
<span v-if="!record.quickLink">{{ t('common.no_link') }}</span>
diff --git a/bigtop-manager-ui/src/features/service-management/configs.vue
b/bigtop-manager-ui/src/features/service-management/configs.vue
index a1db8c52..0378abee 100644
--- a/bigtop-manager-ui/src/features/service-management/configs.vue
+++ b/bigtop-manager-ui/src/features/service-management/configs.vue
@@ -29,9 +29,15 @@
type FormStateType = { configs: ServiceConfig[] }
+ interface RouteParams {
+ id: number
+ serviceId: number
+ }
+
const { t } = useI18n()
const createStore = useCreateServiceStore()
- const attrs = useAttrs() as unknown as Required<ServiceVO> & { clusterId:
number }
+ const attrs = useAttrs() as Partial<ServiceVO>
+ const route = useRoute()
const getServiceDetail = inject('getServiceDetail') as () => any
@@ -63,6 +69,11 @@
}
})
+ const payload = computed(() => {
+ const { id: clusterId, serviceId: id } = route.params as unknown as
RouteParams
+ return { clusterId, id }
+ })
+
/**
* Filters service configurations based on a search keyword.
* Only includes configurations with properties matching the keyword.
@@ -130,13 +141,11 @@
const saveConfigs = async () => {
try {
const valid = await validate()
- if (!valid) {
- return
- }
- const { id, clusterId } = attrs
+ if (!valid) return
+
loading.value = true
const params = createStore.getDiffConfigs(configs.value,
snapshotConfigs.value)
- const data = await updateServiceConfigs({ id, clusterId }, params)
+ const data = await updateServiceConfigs({ ...payload.value }, params)
if (data) {
message.success(t('common.update_success'))
getServiceDetail()
@@ -162,18 +171,16 @@
}
const onCaptureSnapshot = () => {
- const { id, clusterId } = attrs
- captureRef.value?.handleOpen({ id, clusterId })
+ captureRef.value?.handleOpen({ ...payload.value })
}
const openSnapshotManagement = () => {
- const { id, clusterId } = attrs
- snapshotRef.value?.handleOpen({ id, clusterId })
+ snapshotRef.value?.handleOpen({ ...payload.value })
}
onActivated(async () => {
await getServiceDetail()
- configs.value = createStore.injectKeysToConfigs(attrs.configs)
+ configs.value = createStore.injectKeysToConfigs(attrs.configs ?? [])
snapshotConfigs.value = JSON.parse(JSON.stringify(attrs.configs))
})
</script>
diff --git a/bigtop-manager-ui/src/features/service-management/index.vue
b/bigtop-manager-ui/src/features/service-management/index.vue
index d0679b95..7c3e2769 100644
--- a/bigtop-manager-ui/src/features/service-management/index.vue
+++ b/bigtop-manager-ui/src/features/service-management/index.vue
@@ -20,7 +20,7 @@
<script setup lang="ts">
import { useServiceStore } from '@/store/service'
import { useJobProgress } from '@/store/job-progress'
- import { Command } from '@/api/command/types'
+ import { Command, type CommandRequest } from '@/api/command/types'
import Overview from './overview.vue'
import Components from './components.vue'
@@ -35,30 +35,30 @@
serviceId: number
}
+ type Key = keyof typeof Command | 'Remove'
+
const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const serviceStore = useServiceStore()
const jobProgressStore = useJobProgress()
+ const { activeTab } = useTabState(route.path, '1')
const { loading, serviceMap } = storeToRefs(serviceStore)
- const activeKey = ref('1')
const serviceDetail = shallowRef<ServiceVO>()
+ const stepPages = shallowRef([Overview, Components, Configs])
+
+ const getCompName = computed(() => stepPages.value[parseInt(activeTab.value)
- 1])
+
+ const componentPayload = computed(() => {
+ const { id, serviceId } = route.params as unknown as RouteParams
+ return [id, serviceId] as [number, number]
+ })
- const routeParams = computed(() => route.params as unknown as RouteParams)
const tabs = computed((): TabItem[] => [
- {
- key: '1',
- title: t('common.overview')
- },
- {
- key: '2',
- title: t('common.component')
- },
- {
- key: '3',
- title: t('common.configs')
- }
+ { key: '1', title: t('common.overview') },
+ { key: '2', title: t('common.component') },
+ { key: '3', title: t('common.configs') }
])
const actionGroup = computed<GroupItem[]>(() => [
@@ -72,44 +72,37 @@
{ action: 'Stop', text: t('common.stop', [t('common.service')]) },
{ action: 'Remove', text: t('common.remove', [t('common.service')]),
divider: true, danger: true }
],
- dropdownMenuClickEvent: (info) => dropdownMenuClick &&
dropdownMenuClick(info)
+ dropdownMenuClickEvent: (info) => dropdownMenuClick!(info)
}
])
- const getCompName = computed(() => {
- const components = [Overview, Components, Configs]
- return components[parseInt(activeKey.value) - 1]
- })
+ const onServiceDeleted = (clusterId: number) => {
+ router.replace({ path: `/cluster-manage/clusters/${clusterId}` })
+ }
const dropdownMenuClick: GroupItem['dropdownMenuClickEvent'] = async ({ key
}) => {
- const { id: clusterId, serviceId } = routeParams.value
- const service = serviceMap.value[clusterId].filter((service) =>
Number(serviceId) === service.id)[0]
+ const [clusterId, serviceId] = componentPayload.value
+ const service = serviceMap.value[clusterId].filter((s) =>
Number(serviceId) == s.id)[0]
+ const { name: serviceName, displayName } = service
+
+ const processParams = {
+ command: key as Key,
+ clusterId,
+ commandLevel: 'service',
+ serviceCommands: [{ serviceName, installed: true }]
+ } as CommandRequest
if (key === 'Remove') {
- serviceStore.removeService(service, clusterId, () => {
- router.replace({ path: `/cluster-manage/clusters/${clusterId}` })
- })
+ serviceStore.removeService(service, clusterId, () =>
onServiceDeleted(clusterId))
} else {
- jobProgressStore.processCommand(
- {
- command: key as keyof typeof Command,
- clusterId,
- commandLevel: 'service',
- serviceCommands: [{ serviceName: service.name!, installed: true }]
- },
- getServiceDetail,
- {
- displayName: service.displayName
- }
- )
+ jobProgressStore.processCommand(processParams, getServiceDetail, {
displayName })
}
}
const getServiceDetail = async () => {
try {
loading.value = true
- const { id: clusterId, serviceId } = routeParams.value
- serviceDetail.value = await serviceStore.getServiceDetail(clusterId,
serviceId)
+ serviceDetail.value = await
serviceStore.getServiceDetail(...componentPayload.value)
} catch (error) {
console.log('error :>> ', error)
} finally {
@@ -132,13 +125,10 @@
:desc="serviceDetail?.desc"
:action-groups="actionGroup"
/>
- <main-card v-model:active-key="activeKey" :tabs="tabs">
+ <main-card v-model:active-key="activeTab" :tabs="tabs">
<template #tab-item>
<keep-alive>
- <component
- :is="getCompName"
- v-bind="{ ...serviceDetail, clusterId: routeParams.id,
...routeParams }"
- ></component>
+ <component :is="getCompName" v-bind="{ ...serviceDetail
}"></component>
</keep-alive>
</template>
</main-card>
diff --git a/bigtop-manager-ui/src/features/service-management/overview.vue
b/bigtop-manager-ui/src/features/service-management/overview.vue
index 89e9001a..47459fc9 100644
--- a/bigtop-manager-ui/src/features/service-management/overview.vue
+++ b/bigtop-manager-ui/src/features/service-management/overview.vue
@@ -18,30 +18,28 @@
-->
<script setup lang="ts">
- import { CommonStatus, CommonStatusTexts } from '@/enums/state'
import { Empty } from 'ant-design-vue'
- import { formatFromByte } from '@/utils/storage.ts'
import { getServiceMetricsInfo } from '@/api/metrics'
+
+ import { CommonStatus } from '@/enums/state'
+ import { formatFromByte } from '@/utils/storage.ts'
import { isEmpty } from '@/utils/tools'
+ import { STATUS_COLOR, TIME_RANGES, POLLING_INTERVAL } from
'@/utils/constant'
+
+ import { useTabStore } from '@/store/tab-state'
import CategoryChart from '@/features/metric/category-chart.vue'
- import type { ServiceVO, ServiceStatusType } from '@/api/service/types'
+ import type { ServiceVO } from '@/api/service/types'
import type { ServiceMetricItem, ServiceMetrics, ServiceMetricType,
TimeRangeType } from '@/api/metrics/types'
- const { t } = useI18n()
- const attrs = useAttrs() as any
- const currTimeRange = ref<TimeRangeType>('5m')
- const chartData = ref<Partial<ServiceMetrics>>({})
+ type RouteParams = { id: number; serviceId: number }
- const timeRanges = shallowRef<TimeRangeType[]>(['1m', '5m', '15m', '30m',
'1h', '2h'])
- const statusColors = shallowRef<Record<ServiceStatusType, keyof typeof
CommonStatusTexts>>({
- 1: 'healthy',
- 2: 'unhealthy',
- 3: 'unknown'
- })
+ type BaseConfigType = Partial<Record<keyof ServiceVO, string>>
+
+ type UnitMapType = Record<Lowercase<ServiceMetricType>, string | ((value:
number) => string)>
- const unitMap: Record<Lowercase<ServiceMetricType>, string | ((value:
number) => string)> = {
+ const UNIT_MAP: UnitMapType = {
number: '',
percent: '%',
byte: (val) => formatFromByte(val, 0),
@@ -50,11 +48,25 @@
nps: 'N/s'
}
- const clusterId = computed(() => attrs.id)
+ const { t } = useI18n()
+ const route = useRoute()
+ const tabStore = useTabStore()
+ const attrs = useAttrs() as Partial<ServiceVO>
+
+ const isRunning = ref(false)
+ const interval = ref<TimeRangeType>('5m')
+ const chartData = ref<Partial<ServiceMetrics>>({})
+
const noChartData = computed(() => Object.values(chartData.value).length ===
0)
const serviceKeys = computed(() => Object.keys(baseConfig.value) as (keyof
ServiceVO)[])
+
+ const payload = computed(() => {
+ const { id: clusterId, serviceId } = route.params as unknown as RouteParams
+ return { clusterId, serviceId }
+ })
+
const baseConfig = computed(
- (): Partial<Record<keyof ServiceVO, string>> => ({
+ (): BaseConfigType => ({
status: t('overview.service_status'),
displayName: t('overview.service_name'),
version: t('overview.service_version'),
@@ -65,40 +77,61 @@
})
)
- const handleTimeRange = (time: TimeRangeType) => {
- if (currTimeRange.value === time) return
- currTimeRange.value = time
- getServiceMetrics()
+ const getChartFormatter = (chart: ServiceMetricItem) => {
+ const unit = UNIT_MAP[chart.valueType]
+ const valueWithUnit = (val: any) => (typeof unit === 'function' ? unit(val
as number) : `${val} ${unit}`)
+ return {
+ tooltip: (val: any) => `${isEmpty(val) ? '--' : valueWithUnit(val)}`,
+ yAxis: valueWithUnit
+ }
+ }
+
+ const shouldRunMetrics = () => {
+ const currTab = tabStore.getActiveTab(route.path) ?? '1'
+ const clusterId = payload.value.clusterId ?? 0
+ return clusterId != 0 && currTab === '1'
}
const getServiceMetrics = async () => {
+ if (isRunning.value) {
+ return
+ }
+
+ isRunning.value = true
+
try {
- const res = await getServiceMetricsInfo({ id: attrs.serviceId }, {
interval: currTimeRange.value })
- chartData.value = { ...res }
+ const { serviceId: id } = payload.value
+ const data = await getServiceMetricsInfo({ id }, { interval:
interval.value })
+ chartData.value = { ...data }
} catch (error) {
console.log('Failed to fetch service metrics:', error)
+ } finally {
+ isRunning.value = false
}
}
- const getChartFormatter = (chart: ServiceMetricItem) => {
- const unit = unitMap[chart.valueType]
- const valueWithUnit = (val: any) => (typeof unit === 'function' ? unit(val
as number) : `${val} ${unit}`)
- return {
- tooltip: (val: any) => `${isEmpty(val) ? '--' : valueWithUnit(val)}`,
- yAxis: valueWithUnit
- }
+ const { pause, resume } = useIntervalFn(getServiceMetrics, POLLING_INTERVAL,
{ immediate: true })
+
+ const handleTimeRange = (time: TimeRangeType) => {
+ if (interval.value === time) return
+ interval.value = time
+ if (!shouldRunMetrics()) return
+ getServiceMetrics()
+ restartMetrics()
}
- const { pause, resume } = useIntervalFn(getServiceMetrics, 30000, {
immediate: true })
+ const restartMetrics = () => {
+ pause()
+ resume()
+ }
onActivated(() => {
- if (clusterId.value == 0) return
+ if (!shouldRunMetrics()) return
getServiceMetrics()
resume()
})
onDeactivated(() => {
- if (clusterId.value == 0) return
pause()
})
</script>
@@ -131,10 +164,10 @@
<a-tag
v-if="key === 'status'"
class="reset-tag"
- :color="CommonStatus[statusColors[attrs[key]]]"
+ :color="CommonStatus[STATUS_COLOR[attrs[key]!]]"
>
- <status-dot
:color="CommonStatus[statusColors[attrs[key]]]" />
- {{ attrs[key] &&
t(`common.${statusColors[attrs[key]]}`) }}
+ <status-dot
:color="CommonStatus[STATUS_COLOR[attrs[key]!]]" />
+ {{ attrs[key] &&
t(`common.${STATUS_COLOR[attrs[key]]}`) }}
</a-tag>
<a-typography-text
v-else-if="key === 'stack'"
@@ -166,11 +199,11 @@
<a-typography-text strong :content="t('overview.chart')" />
<a-space :size="12">
<div
- v-for="time in timeRanges"
+ v-for="time in TIME_RANGES"
:key="time"
tabindex="0"
class="time-range"
- :class="{ 'time-range-activated': currTimeRange === time }"
+ :class="{ 'time-range-activated': interval === time }"
@click="handleTimeRange(time)"
>
{{ time }}
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/cluster/host.vue
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/host.vue
index d360f525..b5003294 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/cluster/host.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/cluster/host.vue
@@ -21,6 +21,7 @@
import { message, TableColumnType, TableProps } from 'ant-design-vue'
import { getHosts } from '@/api/host'
import * as hostApi from '@/api/host'
+ import { HOST_STATUS } from '@/utils/constant'
import HostCreate from '@/features/create-host/index.vue'
import InstallDependencies from
'@/features/create-host/install-dependencies.vue'
@@ -28,7 +29,6 @@
import type { FilterConfirmProps, FilterResetProps } from
'ant-design-vue/es/table/interface'
import type { GroupItem } from '@/components/common/button-group/types'
import type { HostVO } from '@/api/host/types'
- import type { ClusterVO } from '@/api/cluster/types'
import type { HostReq } from '@/api/command/types'
type Key = string | number
@@ -40,15 +40,14 @@
}
const { t } = useI18n()
- const { confirmModal } = useModal()
-
const router = useRouter()
- const attrs = useAttrs() as ClusterVO
+ const route = useRoute()
+ const { confirmModal } = useModal()
const searchInputRef = ref()
+ const clusterId = ref(Number(route.params.id))
const hostCreateRef = ref<InstanceType<typeof HostCreate> | null>(null)
const installRef = ref<InstanceType<typeof InstallDependencies> | null>(null)
- const hostStatus = ref(['INSTALLING', 'SUCCESS', 'FAILED', 'UNKNOWN'])
const state = reactive<TableState>({
searchText: '',
@@ -143,7 +142,7 @@
}
const handleEdit = (row: HostVO) => {
- const formatHost = { ...row, displayName: row.clusterDisplayName,
clusterId: attrs?.id }
+ const formatHost = { ...row, displayName: row.clusterDisplayName,
clusterId: clusterId.value }
hostCreateRef.value?.handleOpen('EDIT', formatHost)
}
@@ -174,11 +173,11 @@
const viewHostDetail = (row: HostVO) => {
const { id: hostId } = row
- router.push({ name: 'HostDetail', query: { hostId, clusterId: attrs.id } })
+ router.push({ name: 'HostDetail', query: { hostId, clusterId:
clusterId.value } })
}
const addHost = () => {
- hostCreateRef.value?.handleOpen('ADD', { clusterId: attrs.id })
+ hostCreateRef.value?.handleOpen('ADD', { clusterId: clusterId.value })
}
const afterSetupHostConfig = async (type: 'ADD' | 'EDIT', item: HostReq) => {
@@ -191,7 +190,7 @@
const getHostList = async (isReset = false) => {
loading.value = true
- if (attrs.id == undefined || !paginationProps.value) {
+ if (clusterId.value == undefined || !paginationProps.value) {
loading.value = false
return
}
@@ -199,7 +198,7 @@
paginationProps.value.current = 1
}
try {
- const res = await getHosts({ ...filtersParams.value, clusterId: attrs.id
})
+ const res = await getHosts({ ...filtersParams.value, clusterId:
clusterId.value })
dataSource.value = res.content
paginationProps.value.total = res.total
loading.value = false
@@ -265,8 +264,8 @@
<a-typography-link underline @click="viewHostDetail(record)"> {{
record.hostname }} </a-typography-link>
</template>
<template v-if="column.key === 'status'">
- <svg-icon style="margin-left: 0"
:name="hostStatus[record.status].toLowerCase()" />
- <span>{{ t(`common.${hostStatus[record.status].toLowerCase()}`)
}}</span>
+ <svg-icon style="margin-left: 0"
:name="HOST_STATUS[record.status].toLowerCase()" />
+ <span>{{ t(`common.${HOST_STATUS[record.status].toLowerCase()}`)
}}</span>
</template>
<template v-if="column.key === 'operation'">
<button-group
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/cluster/index.vue
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/index.vue
index 3df28895..94c83c6e 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/cluster/index.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/cluster/index.vue
@@ -19,7 +19,8 @@
<script setup lang="ts">
import { useClusterStore } from '@/store/cluster'
- import { CommonStatus, CommonStatusTexts } from '@/enums/state'
+ import { STATUS_COLOR } from '@/utils/constant'
+ import { CommonStatus } from '@/enums/state'
import { useJobProgress } from '@/store/job-progress'
import Overview from './overview.vue'
@@ -31,28 +32,22 @@
import type { Command } from '@/api/command/types'
import type { TabItem } from '@/components/base/main-card/types'
import type { GroupItem } from '@/components/common/button-group/types'
- import type { ClusterStatusType } from '@/api/cluster/types'
const { t } = useI18n()
const router = useRouter()
const route = useRoute()
const jobProgressStore = useJobProgress()
const clusterStore = useClusterStore()
+ const { activeTab } = useTabState(route.path, '1')
const { loading, currCluster } = storeToRefs(clusterStore)
- const { activeTab } = useTabState(route.path, '1')
+ const clusterId = ref(Number(route.params.id))
- const statusColors = shallowRef<Record<ClusterStatusType, keyof typeof
CommonStatusTexts>>({
- 1: 'healthy',
- 2: 'unhealthy',
- 3: 'unknown'
- })
+ const getCompName = computed(() => [Overview, Service, Host, User,
Job][Number(activeTab.value) - 1])
/**
* Determines the component to render based on the active tab.
*/
- const getCompName = computed(() => [Overview, Service, Host, User,
Job][parseInt(activeTab.value) - 1])
-
const tabs = computed((): TabItem[] => [
{ key: '1', title: t('common.overview') },
{ key: '2', title: t('common.service') },
@@ -89,12 +84,12 @@
jobProgressStore.processCommand(
{
command: key as keyof typeof Command,
- clusterId: currCluster.value.id,
+ clusterId: clusterId.value,
commandLevel: 'cluster'
},
async () => {
await clusterStore.loadClusters()
- await clusterStore.getClusterDetail(currCluster.value.id!)
+ await getClusterInfo()
},
{ displayName: currCluster.value.displayName }
)
@@ -104,8 +99,16 @@
}
const addService: GroupItem['clickEvent'] = () => {
- router.push({ name: 'CreateService', params: { id: currCluster.value.id,
creationMode: 'internal' } })
+ router.push({ name: 'CreateService', params: { id: clusterId.value,
creationMode: 'internal' } })
}
+
+ const getClusterInfo = async () => {
+ await clusterStore.getClusterDetail(clusterId.value)
+ }
+
+ onMounted(async () => {
+ await getClusterInfo()
+ })
</script>
<template>
@@ -113,14 +116,14 @@
<header-card
:title="currCluster.displayName"
avatar="cluster"
- :status="CommonStatus[statusColors[currCluster.status as
ClusterStatusType]]"
+ :status="CommonStatus[STATUS_COLOR[currCluster.status!]]"
:desc="currCluster.desc"
:action-groups="actionGroup"
/>
<main-card v-model:active-key="activeTab" :tabs="tabs">
<template #tab-item>
- <keep-alive :key="activeTab">
- <component :is="getCompName" v-bind="currCluster"
v-model:payload="currCluster"></component>
+ <keep-alive :key="clusterId">
+ <component :is="getCompName" :payload="currCluster" />
</keep-alive>
</template>
</main-card>
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/cluster/overview.vue
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/overview.vue
index e7c368ee..51f13c87 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/cluster/overview.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/cluster/overview.vue
@@ -20,13 +20,13 @@
<script setup lang="ts">
import { formatFromByte } from '@/utils/storage'
import { usePngImage } from '@/utils/tools'
-
- import { CommonStatus, CommonStatusTexts } from '@/enums/state'
+ import { CommonStatus } from '@/enums/state'
+ import { TIME_RANGES, STATUS_COLOR, POLLING_INTERVAL } from
'@/utils/constant'
import { useServiceStore } from '@/store/service'
import { useJobProgress } from '@/store/job-progress'
import { useStackStore } from '@/store/stack'
- import { useClusterStore } from '@/store/cluster'
+ import { useTabStore } from '@/store/tab-state'
import { Empty } from 'ant-design-vue'
import { getClusterMetricsInfo } from '@/api/metrics'
@@ -34,31 +34,26 @@
import type { ClusterStatusType, ClusterVO } from '@/api/cluster/types'
import type { ServiceVO } from '@/api/service/types'
import type { StackVO } from '@/api/stack/types'
- import type { Command } from '@/api/command/types'
import type { MetricsData, TimeRangeType } from '@/api/metrics/types'
+ import type { CommandRequest } from '@/api/command/types'
const props = defineProps<{ payload: ClusterVO }>()
- const emits = defineEmits<{ (event: 'update:payload', value: ClusterVO):
void }>()
const { t } = useI18n()
const route = useRoute()
+ const tabStore = useTabStore()
const jobProgressStore = useJobProgress()
const stackStore = useStackStore()
const serviceStore = useServiceStore()
- const clusterStore = useClusterStore()
+ const isRunning = ref(false)
const currTimeRange = ref<TimeRangeType>('5m')
+ const clusterId = ref(Number(route.params.id))
const chartData = ref<Partial<MetricsData>>({})
- const timeRanges = shallowRef<TimeRangeType[]>(['1m', '5m', '15m', '30m',
'1h', '2h'])
const locateStackWithService = shallowRef<StackVO[]>([])
- const statusColors = shallowRef<Record<ClusterStatusType, keyof typeof
CommonStatusTexts>>({
- 1: 'healthy',
- 2: 'unhealthy',
- 3: 'unknown'
- })
-
const { serviceNames } = storeToRefs(serviceStore)
+
const { payload } = toRefs(props)
const clusterDetail = computed(() => ({
@@ -95,59 +90,66 @@
Stop: t('common.stop', [t('common.service')])
}))
- const clusterId = computed(() => route.params.id as unknown as number)
const noChartData = computed(() => Object.values(chartData.value).length ===
0)
const detailKeys = computed(() => Object.keys(baseConfig.value) as (keyof
ClusterVO)[])
- const handleServiceOperate = (item: any, service: ServiceVO) => {
- jobProgressStore.processCommand(
- {
- command: item.key as keyof typeof Command,
- clusterId: clusterId.value,
- commandLevel: 'service',
- serviceCommands: [{ serviceName: service.name!, installed: true }]
- },
- undefined,
- {
- displayName: service.displayName
- }
+ watchEffect(() => {
+ locateStackWithService.value = stackStore.stacks.filter((item) =>
+ item.services.some((service) => service.name &&
serviceNames.value.includes(service.name))
)
+ })
+
+ const handleServiceOperate = (item: any, service: ServiceVO) => {
+ const { name, displayName } = service
+ const { key: command } = item
+ const params = {
+ command,
+ clusterId: clusterId.value,
+ commandLevel: 'service',
+ serviceCommands: [{ serviceName: name!, installed: true }]
+ } as CommandRequest
+
+ jobProgressStore.processCommand(params, undefined, { displayName })
}
const handleTimeRange = (time: TimeRangeType) => {
- if (currTimeRange.value !== time) {
- currTimeRange.value = time
- getClusterMetrics()
- }
+ if (currTimeRange.value === time) return
+ currTimeRange.value = time
+ getClusterMetrics()
+ pause()
+ resume()
}
- const servicesFromCurrentCluster = (stack: StackVO) => {
- return stack.services.filter((v) => serviceNames.value.includes(v.name))
- }
+ const servicesFromCurrentCluster = (stack: StackVO) =>
+ stack.services.filter((v) => serviceNames.value.includes(v.name))
const getClusterMetrics = async () => {
+ if (isRunning.value) {
+ return
+ }
+
+ isRunning.value = true
+
try {
chartData.value = await getClusterMetricsInfo({ id: clusterId.value }, {
interval: currTimeRange.value })
} catch (error) {
console.log('Failed to fetch cluster metrics:', error)
+ } finally {
+ isRunning.value = false
}
}
- const { pause, resume } = useIntervalFn(getClusterMetrics, 30000, {
immediate: true })
+ const { pause, resume } = useIntervalFn(getClusterMetrics, POLLING_INTERVAL,
{ immediate: true })
- onActivated(async () => {
- await clusterStore.getClusterDetail(clusterId.value)
- emits('update:payload', clusterStore.currCluster)
+ onActivated(() => {
+ const currTab = tabStore.getActiveTab(route.path) ?? '1'
+ if (currTab != '1') return
getClusterMetrics()
resume()
})
- onDeactivated(pause)
-
- watchEffect(() => {
- locateStackWithService.value = stackStore.stacks.filter((item) =>
- item.services.some((service) => service.name &&
serviceNames.value.includes(service.name))
- )
+ onDeactivated(() => {
+ pause()
})
</script>
@@ -179,11 +181,11 @@
<a-tag
v-if="base === 'status'"
class="reset-tag"
-
:color="CommonStatus[statusColors[clusterDetail[base] as ClusterStatusType]]"
+
:color="CommonStatus[STATUS_COLOR[clusterDetail[base] as ClusterStatusType]]"
>
- <status-dot
:color="CommonStatus[statusColors[clusterDetail[base] as ClusterStatusType]]" />
+ <status-dot
:color="CommonStatus[STATUS_COLOR[clusterDetail[base] as ClusterStatusType]]" />
{{
- clusterDetail[base] &&
t(`common.${statusColors[clusterDetail[base] as ClusterStatusType]}`)
+ clusterDetail[base] &&
t(`common.${STATUS_COLOR[clusterDetail[base] as ClusterStatusType]}`)
}}
</a-tag>
<a-typography-text
@@ -252,7 +254,7 @@
<a-typography-text strong :content="t('overview.chart')" />
<a-space :size="12">
<div
- v-for="time in timeRanges"
+ v-for="time in TIME_RANGES"
:key="time"
tabindex="0"
class="time-range"
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/cluster/service.vue
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/service.vue
index 38091106..4bcf6f97 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/cluster/service.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/cluster/service.vue
@@ -20,31 +20,30 @@
<script setup lang="ts">
import { Empty } from 'ant-design-vue'
import { useServiceStore } from '@/store/service'
- import { CommonStatus, CommonStatusTexts } from '@/enums/state'
+ import { CommonStatus } from '@/enums/state'
import { useJobProgress } from '@/store/job-progress'
+ import { useTabStore } from '@/store/tab-state'
import { usePngImage } from '@/utils/tools'
+ import { STATUS_COLOR } from '@/utils/constant'
import type { GroupItem } from '@/components/common/button-group/types'
import type { FilterFormItem } from '@/components/common/form-filter/types'
- import type { ServiceStatusType, ServiceVO } from '@/api/service/types'
- import type { ClusterVO } from '@/api/cluster/types'
+ import type { ServiceVO } from '@/api/service/types'
import type { Command, CommandRequest } from '@/api/command/types'
type GroupItemActionType = keyof typeof Command | 'More'
const { t } = useI18n()
const router = useRouter()
- const attrs = useAttrs() as ClusterVO
+ const route = useRoute()
+ const tabStore = useTabStore()
const jobProgressStore = useJobProgress()
const serviceStore = useServiceStore()
- const { services, loading } = toRefs(serviceStore)
+
+ const { services, loading } = storeToRefs(serviceStore)
const filterValue = ref({})
- const statusColors = shallowRef<Record<ServiceStatusType, keyof typeof
CommonStatusTexts>>({
- 1: 'healthy',
- 2: 'unhealthy',
- 3: 'unknown'
- })
+ const clusterId = ref(Number(route.params.id))
const actionGroups = computed((): GroupItem<GroupItemActionType,
ServiceVO>[] => [
{
@@ -118,19 +117,19 @@
if (!['More', 'Remove'].includes(command)) {
const execCommandParams = {
command: command,
- clusterId: attrs.id,
+ clusterId: clusterId.value,
commandLevel: 'service',
serviceCommands: [{ serviceName: service.name, installed: true }]
} as CommandRequest
jobProgressStore.processCommand(execCommandParams, getServices, {
displayName: service.displayName })
} else {
- serviceStore.removeService(service, attrs.id!, getServices)
+ serviceStore.removeService(service, clusterId.value!, getServices)
}
}
const getServices = () => {
- if (attrs.id != undefined) {
- serviceStore.getServices(attrs.id, filterValue.value)
+ if (clusterId.value != undefined) {
+ serviceStore.getServices(clusterId.value, filterValue.value)
}
}
@@ -142,6 +141,8 @@
}
onActivated(() => {
+ const currTab = tabStore.getActiveTab(route.path ?? '2')
+ if (currTab != '2') return
getServices()
})
</script>
@@ -166,10 +167,10 @@
<span class="small-gray">{{ item.version }}</span>
</div>
<div class="header-base-status">
- <a-tag :color="CommonStatus[statusColors[item.status]]">
+ <a-tag :color="CommonStatus[STATUS_COLOR[item.status]]">
<div class="header-base-status-inner">
- <status-dot :color="CommonStatus[statusColors[item.status]]"
/>
- <span class="small">{{
t(`common.${statusColors[item.status]}`) }}</span>
+ <status-dot :color="CommonStatus[STATUS_COLOR[item.status]]"
/>
+ <span class="small">{{
t(`common.${STATUS_COLOR[item.status]}`) }}</span>
</div>
</a-tag>
</div>
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/cluster/user.vue
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/user.vue
index 5fbfbd9e..830038dd 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/cluster/user.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/cluster/user.vue
@@ -21,11 +21,11 @@
import { getUserListOfService } from '@/api/cluster'
import type { TableColumnType } from 'ant-design-vue'
- import type { ClusterVO, ServiceUserVO } from '@/api/cluster/types'
+ import type { ServiceUserVO } from '@/api/cluster/types'
const { t } = useI18n()
- const attrs = useAttrs() as ClusterVO
-
+ const route = useRoute()
+ const clusterId = ref(Number(route.params.id))
const columns = computed((): TableColumnType[] => [
{
title: '#',
@@ -66,12 +66,12 @@
})
const loadUserListOfService = async () => {
- if (attrs.id == undefined || !paginationProps.value) {
+ if (clusterId.value == undefined || !paginationProps.value) {
loading.value = false
return
}
try {
- const data = await getUserListOfService(attrs.id, filtersParams.value)
+ const data = await getUserListOfService(clusterId.value,
filtersParams.value)
dataSource.value = data.content
paginationProps.value.total = data.total
} catch (error) {
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/hosts/detail.vue
b/bigtop-manager-ui/src/pages/cluster-manage/hosts/detail.vue
index a155ba45..2bf87749 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/hosts/detail.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/hosts/detail.vue
@@ -20,7 +20,8 @@
<script setup lang="ts">
import { getHost, restartAgent, startAgent, stopAgent } from '@/api/host'
import { message } from 'ant-design-vue'
- import { CommonStatus, CommonStatusTexts } from '@/enums/state'
+ import { CommonStatus } from '@/enums/state'
+ import { STATUS_COLOR } from '@/utils/constant'
import Overview from './overview.vue'
@@ -39,12 +40,6 @@
stop: stopAgent
})
- const statusColors = shallowRef<Record<HostStatusType, keyof typeof
CommonStatusTexts>>({
- 1: 'healthy',
- 2: 'unhealthy',
- 3: 'unknown'
- })
-
const actionGroup = computed<GroupItem[]>(() => [
{
shape: 'default',
@@ -112,7 +107,7 @@
<header-card
:title="hostInfo.hostname"
avatar="host"
- :status="CommonStatus[statusColors[hostInfo.status as HostStatusType]]"
+ :status="CommonStatus[STATUS_COLOR[hostInfo.status as HostStatusType]]"
:desc="hostInfo.desc"
:action-groups="actionGroup"
/>
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/hosts/index.vue
b/bigtop-manager-ui/src/pages/cluster-manage/hosts/index.vue
index 2ece464c..51508deb 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/hosts/index.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/hosts/index.vue
@@ -22,6 +22,7 @@
import { useClusterStore } from '@/store/cluster'
import * as hostApi from '@/api/host'
+ import { HOST_STATUS, POLLING_INTERVAL } from '@/utils/constant'
import useBaseTable from '@/composables/use-base-table'
import HostCreate from '@/features/create-host/index.vue'
@@ -32,7 +33,6 @@
import type { GroupItem } from '@/components/common/button-group/types'
import type { HostVO } from '@/api/host/types'
- const POLLING_INTERVAL = 30000
type Key = string | number
interface TableState {
@@ -42,20 +42,20 @@
}
const { t } = useI18n()
- const { confirmModal } = useModal()
-
const router = useRouter()
const clusterStore = useClusterStore()
+ const { confirmModal } = useModal()
+
const searchInputRef = ref()
const pollingIntervalId = ref<any>(null)
const hostCreateRef = ref<InstanceType<typeof HostCreate> | null>(null)
const installRef = ref<InstanceType<typeof InstallDependencies> | null>(null)
- const hostStatus = shallowRef(['INSTALLING', 'SUCCESS', 'FAILED', 'UNKNOWN'])
const state = reactive<TableState>({
searchText: '',
searchedColumn: '',
selectedRowKeys: []
})
+
const filtersOfClusterDisplayName = computed(() =>
Object.values(clusterStore.clusterMap).map((v) => ({
text: v.displayName || v.name,
@@ -329,8 +329,8 @@
</span>
</template>
<template v-if="column.key === 'status'">
- <svg-icon style="margin-left: 0"
:name="hostStatus[record.status].toLowerCase()" />
- <span>{{ t(`common.${hostStatus[record.status].toLowerCase()}`)
}}</span>
+ <svg-icon style="margin-left: 0"
:name="HOST_STATUS[record.status].toLowerCase()" />
+ <span>{{ t(`common.${HOST_STATUS[record.status].toLowerCase()}`)
}}</span>
</template>
<template v-if="column.key === 'operation'">
<button-group
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/hosts/overview.vue
b/bigtop-manager-ui/src/pages/cluster-manage/hosts/overview.vue
index 7e375cb0..78be5a3a 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/hosts/overview.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/hosts/overview.vue
@@ -21,10 +21,13 @@
import { Empty } from 'ant-design-vue'
import { formatFromByte } from '@/utils/storage.ts'
import { usePngImage, isEmpty } from '@/utils/tools'
- import { CommonStatus, CommonStatusTexts } from '@/enums/state.ts'
+ import { TIME_RANGES, STATUS_COLOR, POLLING_INTERVAL } from
'@/utils/constant'
+ import { CommonStatus } from '@/enums/state.ts'
+
import { useServiceStore } from '@/store/service'
import { useJobProgress } from '@/store/job-progress'
import { useStackStore } from '@/store/stack'
+
import { getComponentsByHost } from '@/api/host'
import { Command } from '@/api/command/types'
import { getHostMetricsInfo } from '@/api/metrics'
@@ -32,13 +35,11 @@
import GaugeChart from '@/features/metric/gauge-chart.vue'
import CategoryChart from '@/features/metric/category-chart.vue'
- import type { HostStatusType, HostVO } from '@/api/host/types'
+ import type { HostVO } from '@/api/host/types'
import type { ClusterStatusType } from '@/api/cluster/types.ts'
import type { ComponentVO } from '@/api/component/types.ts'
import type { MetricsData, TimeRangeType } from '@/api/metrics/types'
- type StatusColorType = Record<HostStatusType, keyof typeof CommonStatusTexts>
-
interface Props {
hostInfo: HostVO
}
@@ -50,13 +51,12 @@
const serviceStore = useServiceStore()
const jobProgressStore = useJobProgress()
+ const isRunning = ref(false)
const currTimeRange = ref<TimeRangeType>('5m')
const chartData = ref<Partial<MetricsData>>({})
const componentsFromCurrentHost = shallowRef<Map<string, ComponentVO[]>>(new
Map())
const needFormatFormByte = shallowRef(['totalMemorySize', 'totalDisk'])
- const timeRanges = shallowRef<TimeRangeType[]>(['1m', '5m', '15m', '30m',
'1h', '2h'])
- const statusColors = shallowRef<StatusColorType>({ 1: 'healthy', 2:
'unhealthy', 3: 'unknown' })
const { hostInfo } = toRefs(props)
@@ -113,18 +113,27 @@
}
const handleTimeRange = (time: TimeRangeType) => {
- if (currTimeRange.value == time) {
- return
+ if (currTimeRange.value !== time) {
+ currTimeRange.value = time
+ pause()
+ getHostMetrics()
+ resume()
}
- currTimeRange.value = time
- getHostMetrics()
}
const getHostMetrics = async () => {
+ if (isRunning.value) {
+ return
+ }
+
+ isRunning.value = true
+
try {
chartData.value = await getHostMetricsInfo({ id: hostInfo.value.id! }, {
interval: currTimeRange.value })
} catch (error) {
console.log('Failed to fetch host metrics:', error)
+ } finally {
+ isRunning.value = false
}
}
@@ -144,12 +153,13 @@
}
}
- const { pause, resume } = useIntervalFn(getHostMetrics, 30000, { immediate:
true })
+ const { pause, resume } = useIntervalFn(getHostMetrics, POLLING_INTERVAL, {
immediate: true })
watch(
() => hostInfo.value,
- (val) => {
+ async (val) => {
if (val.id) {
+ pause()
getComponentInfo()
getHostMetrics()
resume()
@@ -192,10 +202,10 @@
<a-tag
v-if="base === 'status'"
class="reset-tag"
- :color="CommonStatus[statusColors[hostInfo[base] as
ClusterStatusType]]"
+ :color="CommonStatus[STATUS_COLOR[hostInfo[base] as
ClusterStatusType]]"
>
- <status-dot
:color="CommonStatus[statusColors[hostInfo[base] as ClusterStatusType]]" />
- {{ hostInfo[base] &&
t(`common.${statusColors[hostInfo[base] as ClusterStatusType]}`) }}
+ <status-dot
:color="CommonStatus[STATUS_COLOR[hostInfo[base] as ClusterStatusType]]" />
+ {{ hostInfo[base] &&
t(`common.${STATUS_COLOR[hostInfo[base] as ClusterStatusType]}`) }}
</a-tag>
<a-typography-text v-else
class="desc-sub-item-desc-column">
<span
v-if="Object.keys(unitOfBaseConfig).includes(base as string)">
@@ -268,7 +278,7 @@
<a-typography-text strong :content="t('overview.chart')" />
<a-space :size="12">
<div
- v-for="time in timeRanges"
+ v-for="time in TIME_RANGES"
:key="time"
tabindex="0"
class="time-range"
diff --git
a/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/index.vue
b/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/index.vue
index 6150d92a..b5d11b7d 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/index.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/index.vue
@@ -59,7 +59,7 @@
<main-card v-model:active-key="activeTab" :tabs="tabs">
<template #tab-item>
<keep-alive>
- <component :is="getCompName" :key="activeTab"
v-bind="currCluster"></component>
+ <component :is="getCompName" :key="activeTab"
:payload="currCluster"></component>
</keep-alive>
</template>
</main-card>
diff --git
a/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/service.vue
b/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/service.vue
index 02e1bfef..04da3813 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/service.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/service.vue
@@ -18,32 +18,33 @@
-->
<script setup lang="ts">
- import { usePngImage } from '@/utils/tools'
- import { CommonStatus, CommonStatusTexts } from '@/enums/state'
+ import { Empty } from 'ant-design-vue'
import { useServiceStore } from '@/store/service'
import { useJobProgress } from '@/store/job-progress'
- import { Empty } from 'ant-design-vue'
- import type { ServiceStatusType, ServiceVO } from '@/api/service/types'
+ import { CommonStatus } from '@/enums/state'
+ import { usePngImage } from '@/utils/tools'
+ import { STATUS_COLOR } from '@/utils/constant'
+
+ import type { ServiceVO } from '@/api/service/types'
import type { GroupItem } from '@/components/common/button-group/types'
import type { FilterFormItem } from '@/components/common/form-filter/types'
import type { Command, CommandRequest } from '@/api/command/types'
+ import type { ClusterVO } from '@/api/cluster/types'
type GroupItemActionType = keyof typeof Command | 'More'
+ const props = defineProps<{ payload: ClusterVO }>()
+
const { t } = useI18n()
const router = useRouter()
const jobProgressStore = useJobProgress()
const serviceStore = useServiceStore()
const { services, loading } = toRefs(serviceStore)
- const clusterInfo = useAttrs() as { id: number; name: string }
const filterValue = ref({})
- const statusColors = shallowRef<Record<ServiceStatusType, keyof typeof
CommonStatusTexts>>({
- 1: 'healthy',
- 2: 'unhealthy',
- 3: 'unknown'
- })
+
+ const clusterId = computed(() => props.payload.id)
const filterFormItems = computed((): FilterFormItem[] => [
{ type: 'search', key: 'name', label: t('service.name') },
{
@@ -60,9 +61,9 @@
key: 'status',
label: t('common.status'),
options: [
- { label: t(`common.${statusColors.value[1]}`), value: 1 },
- { label: t(`common.${statusColors.value[2]}`), value: 2 },
- { label: t(`common.${statusColors.value[3]}`), value: 3 }
+ { label: t(`common.${STATUS_COLOR[1]}`), value: 1 },
+ { label: t(`common.${STATUS_COLOR[2]}`), value: 2 },
+ { label: t(`common.${STATUS_COLOR[3]}`), value: 3 }
]
}
])
@@ -108,29 +109,29 @@
])
const infraAction = async (command: GroupItemActionType | 'Remove', service:
ServiceVO) => {
- const { id: clusterId } = clusterInfo
if (!['More', 'Remove'].includes(command)) {
+ const { name: serviceName, displayName } = service
const execCommandParams = {
- command: command,
- clusterId,
+ command,
+ clusterId: clusterId.value,
commandLevel: 'service',
- serviceCommands: [{ serviceName: service.name, installed: true }]
+ serviceCommands: [{ serviceName, installed: true }]
} as CommandRequest
- jobProgressStore.processCommand(execCommandParams, getServices, {
displayName: service.displayName })
+ jobProgressStore.processCommand(execCommandParams, getServices, {
displayName })
} else {
- serviceStore.removeService(service, clusterId, getServices)
+ serviceStore.removeService(service, clusterId.value!, getServices)
}
}
const getServices = async () => {
- await serviceStore.getServices(clusterInfo.id, filterValue.value)
+ await serviceStore.getServices(clusterId.value!, filterValue.value)
}
const viewServiceDetail = (payload: ServiceVO) => {
router.push({
name: 'InfraServiceDetail',
params: {
- id: clusterInfo.id,
+ id: clusterId.value,
serviceId: payload.id
}
})
@@ -162,10 +163,10 @@
<span class="small-gray">{{ item.version }}</span>
</div>
<div class="header-base-status">
- <a-tag :color="CommonStatus[statusColors[item.status]]">
+ <a-tag :color="CommonStatus[STATUS_COLOR[item.status]]">
<div class="header-base-status-inner">
- <status-dot
:color="CommonStatus[statusColors[item.status]]" />
- <span class="small">{{
t(`common.${statusColors[item.status]}`) }}</span>
+ <status-dot
:color="CommonStatus[STATUS_COLOR[item.status]]" />
+ <span class="small">{{
t(`common.${STATUS_COLOR[item.status]}`) }}</span>
</div>
</a-tag>
</div>
diff --git a/bigtop-manager-ui/src/store/tab-state/index.ts
b/bigtop-manager-ui/src/store/tab-state/index.ts
index 6b940e6a..bec4b90c 100644
--- a/bigtop-manager-ui/src/store/tab-state/index.ts
+++ b/bigtop-manager-ui/src/store/tab-state/index.ts
@@ -17,16 +17,25 @@
* under the License.
*/
-export const useTabStore = defineStore('tab', () => {
- const activeTabs = ref<Record<string, string>>({})
+export const useTabStore = defineStore(
+ 'tab',
+ () => {
+ const activeTabs = ref<Record<string, string>>({})
- function setActiveTab(pageKey: string, tabIndex: string) {
- activeTabs.value[pageKey] = tabIndex
- }
+ function setActiveTab(pageKey: string, tabIndex: string) {
+ activeTabs.value[pageKey] = tabIndex
+ }
- function getActiveTab(pageKey: string) {
- return activeTabs.value[pageKey]
- }
+ function getActiveTab(pageKey: string) {
+ return activeTabs.value[pageKey]
+ }
- return { activeTabs, setActiveTab, getActiveTab }
-})
+ return { activeTabs, setActiveTab, getActiveTab }
+ },
+ {
+ persist: {
+ storage: sessionStorage,
+ paths: ['activeTabs']
+ }
+ }
+)
diff --git a/bigtop-manager-ui/src/utils/constant.ts
b/bigtop-manager-ui/src/utils/constant.ts
index 9d8b8f32..9fd050ba 100644
--- a/bigtop-manager-ui/src/utils/constant.ts
+++ b/bigtop-manager-ui/src/utils/constant.ts
@@ -17,8 +17,38 @@
* under the License.
*/
+import type { ClusterStatusType } from '@/api/cluster/types'
+import type { HostStatusType } from '@/api/host/types'
+import type { StateType } from '@/api/job/types'
+import type { TimeRangeType } from '@/api/metrics/types'
+import type { ServiceStatusType } from '@/api/service/types'
+import { CommonStatusTexts } from '@/enums/state'
+
+export type Status = ServiceStatusType | HostStatusType | ClusterStatusType
+
+export type StatusColorType = Record<Status, keyof typeof CommonStatusTexts>
+
export const API_RETRY_TIME = 3
export const API_EXPIRE_TIME = 30 * 1000
export const JOB_SCHEDULE_INTERVAL = 1000
export const MONITOR_SCHEDULE_INTERVAL = 10 * 1000
export const DEFAULT_PAGE_SIZE = 10
+export const POLLING_INTERVAL = 30000
+
+export const TIME_RANGES: TimeRangeType[] = ['1m', '5m', '15m', '30m', '1h',
'2h']
+export const HOST_STATUS = ['INSTALLING', 'SUCCESS', 'FAILED', 'UNKNOWN']
+export const COMPONENT_STATUS = ['INSTALLING', 'SUCCESS', 'FAILED', 'UNKNOWN']
+
+export const STATUS_COLOR: StatusColorType = {
+ 1: 'healthy',
+ 2: 'unhealthy',
+ 3: 'unknown'
+}
+
+export const JOB_STATUS: Record<StateType, string> = {
+ Pending: 'installing',
+ Processing: 'processing',
+ Failed: 'failed',
+ Canceled: 'canceled',
+ Successful: 'success'
+}