Files
hudi-service/service-web/client/src/pages/overview/Overview.tsx
2025-05-16 19:00:55 +08:00

635 lines
18 KiB
TypeScript

import React from 'react'
import {amisRender, commonInfo, crudCommonOptions, readOnlyDialogOptions} from '../../util/amis.tsx'
const color = (number: number) => {
let color = 'text-success'
if (number > 30) {
color = 'text-primary'
}
if (number > 90) {
color = 'text-danger'
}
return color
}
const versionDetailDialog = (variable: string, target: string) => {
return {
disabledOn: `${variable} === 0`,
type: 'action',
label: '详情',
level: 'link',
size: 'sm',
actionType: 'dialog',
dialog: {
title: '详情',
actions: [],
size: 'md',
closeOnEsc: false,
closeOnOutside: false,
body: [
{
type: 'service',
api: {
method: 'get',
url: `${commonInfo.baseUrl}/overview/version_detail`,
data: {
target: target,
version: '${version}',
},
},
body: [
{
type: 'table',
source: '${items}',
affixHeader: false,
columns: [
{
label: 'Flink job id',
fixed: 'left',
type: 'wrapper',
size: 'none',
body: [
{
type: 'tpl',
tpl: '${id}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${id}',
tooltip: '复制 ID',
},
],
},
{
label: '别名',
type: 'wrapper',
fixed: 'left',
size: 'none',
className: 'nowrap',
body: [
{
type: 'tpl',
tpl: '${alias}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${alias}',
tooltip: '复制别名',
},
],
},
],
},
],
},
],
},
}
}
const tableDetailDialog = (variable: string, targetList: any) => {
return {
disabledOn: `${variable} === 0`,
type: 'action',
label: '详情',
level: 'link',
size: 'sm',
actionType: 'dialog',
dialog: {
title: '详情',
size: 'md',
...readOnlyDialogOptions(),
body: [
{
type: 'table',
source: `\${${targetList}}`,
affixHeader: false,
columns: [
{
label: 'Flink job id',
fixed: 'left',
type: 'wrapper',
size: 'none',
body: [
{
type: 'tpl',
tpl: '${id}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${id}',
tooltip: '复制 ID',
},
],
},
{
label: '别名',
type: 'wrapper',
fixed: 'left',
size: 'none',
className: 'nowrap',
body: [
{
type: 'tpl',
tpl: '${alias}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${alias}',
tooltip: '复制别名',
},
],
},
],
},
],
},
}
}
const overviewYarnJob = (cluster: string, search: string, queue: string | undefined, yarnQueue: string) => {
return {
className: 'text-base leading-none',
type: 'table-view',
border: false,
padding: '0 10px 0 15px',
trs: [
{
tds: [
{
body: `${cluster}`,
},
{
padding: '0px',
body: queue === undefined ? {} : {
type: 'service',
api: `${commonInfo.baseUrl}/overview/queue?queue=${queue}`,
interval: 10000,
silentPolling: true,
body: [
{
type: 'tpl',
tpl: '${size}',
},
],
},
width: 100,
align: 'center',
},
{
padding: '0px',
width: 200,
body: {
type: 'service',
api: {
method: 'get',
url: `${commonInfo.baseUrl}/overview/yarn-cluster`,
data: {
cluster: cluster,
queue: yarnQueue,
},
// @ts-ignore
adaptor: function (payload, response) {
let rootUsed = (payload['data']['root']['usedCapacity'] * 100 / payload['data']['root']['capacity'])
let targetUsed = (payload['data']['target']['absoluteUsedCapacity'] * 100 / payload['data']['target']['absoluteMaxCapacity'])
return {
...payload,
data: {
...payload.data,
rootUsed: rootUsed,
rootUsedColor: color(rootUsed),
targetUsed: targetUsed,
targetUsedColor: color(targetUsed),
},
}
},
},
interval: 10000,
silentPolling: true,
body: {
type: 'table-view',
border: false,
trs: [
{
tds: [
{
body: {
type: 'tpl',
tpl: '<span class="font-bold ${rootUsedColor}">${ROUND(rootUsed, 0)}%</span>',
},
width: 100,
align: 'center',
},
{
body: {
type: 'tpl',
tpl: '<span class="font-bold ${targetUsedColor}">${ROUND(targetUsed, 0)}%</span>',
},
width: 100,
align: 'center',
},
],
},
],
},
},
},
{
padding: '0px',
width: 200,
body: {
type: 'service',
api: {
url: `${commonInfo.baseUrl}/overview/yarn-job`,
data: {
cluster: cluster,
search: search,
},
},
interval: 10000,
silentPolling: true,
body: {
type: 'table-view',
border: false,
trs: [
{
tds: [
{
body: {
type: 'tpl',
tpl: '<span class=\'font-bold text-cyan-300\'>${scheduling}</span>',
},
width: 100,
align: 'center',
},
{
body: {
type: 'tpl',
tpl: '<span class="font-bold text-success">${running}</span>',
},
width: 100,
align: 'center',
},
],
},
],
},
},
},
],
},
],
}
}
const Overview: React.FC = () => {
return (
<div className="hudi-overview">
{amisRender(
{
type: 'wrapper',
body: [
{
type: 'service',
// language=JavaScript
dataProvider: 'const timer = setInterval(() => {\n setData({date: new Date().toLocaleString()})\n}, 1000)\nreturn () => {\n clearInterval(timer)\n}',
body: [
'当前时间:',
{
type: 'tpl',
className: 'font-bold',
tpl: '${date}',
},
],
},
{type: 'divider'},
{
type: 'crud',
title: '同步表数量',
api: `${commonInfo.baseUrl}/overview`,
...crudCommonOptions(),
interval: 60000,
columns: [
{
name: 'type',
label: '表类型',
},
{
name: 'total',
label: '总表数',
width: 100,
align: 'center',
},
{
name: 'focus',
label: '重点表',
className: 'text-danger font-bold',
width: 100,
align: 'center',
},
{
label: '普通表',
type: 'tpl',
tpl: '${total - focus}',
width: 100,
align: 'center',
},
],
},
{
type: 'crud',
title: '同步表数量',
api: `${commonInfo.baseUrl}/overview/sync_running_status`,
...crudCommonOptions(),
interval: 10000,
columns: [
{
name: 'type',
label: '类型',
},
{
name: 'total',
label: '任务数',
width: 100,
align: 'center',
},
{
name: 'running',
label: '运行中',
width: 100,
align: 'center',
},
{
name: 'stopped',
label: '已停止',
className: 'text-danger font-bold',
width: 100,
align: 'center',
},
{
type: 'operation',
label: '操作',
width: 100,
align: 'center',
buttons: [
tableDetailDialog('stopped', 'list'),
],
},
],
},
{
className: 'pl-2 my-5',
type: 'wrapper',
size: 'none',
body: {
type: 'tpl',
tpl: '同步集群资源用量情况',
},
},
{
type: 'table-view',
border: false,
trs: [
{
background: '#F9F9F9',
tds: [
{
bold: true,
body: '集群',
},
{
bold: true,
body: '集群资源',
width: 100,
align: 'center',
},
{
bold: true,
body: '队列资源',
width: 100,
align: 'center',
},
{
bold: true,
body: '调度中',
width: 100,
align: 'center',
},
{
bold: true,
body: '运行中',
width: 100,
align: 'center',
},
],
},
],
},
overviewYarnJob(commonInfo.clusters.sync_names(), 'Sync', undefined, 'default'),
{type: 'divider'},
{
className: 'pl-2 my-5',
type: 'wrapper',
size: 'none',
body: [
{
type: 'tpl',
tpl: '压缩集群资源用量情况',
},
{
className: 'mt-2',
type: 'service',
api: `${commonInfo.baseUrl}/overview/queue?queue=compaction-queue-pre`,
interval: 10000,
silentPolling: true,
body: [
{
type: 'tpl',
tpl: '预调度队列:<span class="font-bold">${size}</span>',
},
],
},
],
},
{
type: 'table-view',
border: false,
bold: true,
trs: [
{
background: '#F9F9F9',
tds: [
{
bold: true,
body: '集群',
},
{
bold: true,
body: '队列',
width: 100,
align: 'center',
},
{
bold: true,
body: '集群资源',
width: 100,
align: 'center',
},
{
bold: true,
body: '队列资源',
width: 100,
align: 'center',
},
{
bold: true,
body: '调度中',
width: 100,
align: 'center',
},
{
bold: true,
body: '运行中',
width: 100,
align: 'center',
},
],
},
],
},
// @ts-ignore
...Object.keys(commonInfo.clusters.compaction).map(name => overviewYarnJob(name, 'Compaction', `compaction-queue-${name}`, commonInfo.clusters.compaction[name])),
{type: 'divider'},
{
type: 'service',
api: `${commonInfo.baseUrl}/overview/version`,
interval: 10000,
silentPolling: true,
body: [
{
type: 'table',
title: '跨天情况 (${version})',
source: '${items}',
...crudCommonOptions(),
headerToolbar: [
'${version}',
],
columns: [
{
name: 'type',
label: '类型',
},
{
name: 'unReceive',
label: '未接收',
width: 100,
align: 'center',
},
{
type: 'operation',
label: '操作',
width: 100,
align: 'center',
buttons: [
versionDetailDialog('unReceive', 'unReceive_${key}'),
],
},
{
name: 'unScheduled',
label: '未跨天',
className: 'text-danger font-bold',
width: 100,
align: 'center',
},
{
type: 'operation',
label: '操作',
width: 100,
align: 'center',
buttons: [
versionDetailDialog('unScheduled', 'unScheduled_${key}'),
],
},
],
},
],
},
{
type: 'crud',
title: '调度策略',
api: `${commonInfo.baseUrl}/overview/schedule_jobs`,
...crudCommonOptions(),
interval: 60000,
loadDataOnce: true,
columns: [
{
name: 'job',
label: '策略描述',
},
{
name: 'trigger',
label: 'Cron表达式',
className: 'font-mono',
width: 250,
},
],
},
{
type: 'crud',
title: '监控指标运行进度',
api: `${commonInfo.baseUrl}/overview/monitor_progress`,
...crudCommonOptions(),
interval: 2000,
loadDataOnce: true,
columns: [
{
name: 'name',
label: '名称',
width: 150,
},
{
name: 'running',
label: '状态',
type: 'mapping',
width: 70,
map: {
'true': '运行中',
'false': '未运行',
},
},
{
label: '进度',
type: 'progress',
value: '${ROUND(progress * 100)}',
map: 'bg-primary',
},
],
},
],
},
)}
</div>
)
}
export default Overview