diff --git a/apps/daas/src/i18n/langs/en.js b/apps/daas/src/i18n/langs/en.js index 21d7ddb00..d84c19511 100644 --- a/apps/daas/src/i18n/langs/en.js +++ b/apps/daas/src/i18n/langs/en.js @@ -224,6 +224,45 @@ export default { dashboard_error: 'Number of tasks with errors in verification', dashboard_no_data_here: 'There is no data here~', dashboard_no_statistics: 'No {0} statistics yet', + // Dashboard ODH + dashboard_odh_active_tasks: 'Active Tasks', + dashboard_odh_running: 'Running', + dashboard_odh_error: 'Error', + dashboard_odh_max_lag: 'Max Lag', + dashboard_odh_min_lag: 'Min Lag', + dashboard_odh_total_throughput: 'Total Throughput', + dashboard_odh_events_sec: 'events/sec', + dashboard_odh_peak: 'Peak', + dashboard_odh_data: 'Data', + dashboard_odh_vs_last_hour: 'vs last hour', + dashboard_odh_connected_dbs: 'Connected DBs', + dashboard_odh_sources: 'sources', + dashboard_odh_tbls: 'tbls', + dashboard_odh_api_requests: 'API Requests', + dashboard_odh_failed: 'Failed', + dashboard_odh_rate: 'Rate', + dashboard_odh_avg_time: 'Avg Time', + dashboard_odh_system_trends: 'System Trends', + dashboard_odh_throughput_chart: 'Throughput (events/sec)', + dashboard_odh_api_chart: 'API Requests (req/sec)', + dashboard_odh_no_data_in_range: 'No data in this time range', + dashboard_odh_top_tasks: 'Top Tasks', + dashboard_odh_most_lagging: 'Most Lagging', + dashboard_odh_highest_throughput: 'Highest Throughput', + dashboard_odh_top_n: 'Top {0}', + dashboard_odh_task_name: 'Task Name', + dashboard_odh_latency: 'Latency', + dashboard_odh_throughput: 'Throughput', + dashboard_odh_no_task_data: 'No task data', + dashboard_odh_agent_cluster: 'Agent Cluster Status', + dashboard_odh_nodes_total: '{0} Nodes Total', + dashboard_odh_agent_name: 'Agent Name', + dashboard_odh_status: 'Status', + dashboard_odh_cpu_usage: 'CPU Usage', + dashboard_odh_memory_usage: 'Memory Usage', + dashboard_odh_running_tasks: 'Running Tasks', + dashboard_odh_no_agents: 'No agents found', + dashboard_odh_just_now: 'Just now', // 元数据管理 metadata_db: 'Owned library', metadata_change_name: 'Rename', diff --git a/apps/daas/src/i18n/langs/zh-CN.js b/apps/daas/src/i18n/langs/zh-CN.js index 36e6a33dd..e8937ed25 100644 --- a/apps/daas/src/i18n/langs/zh-CN.js +++ b/apps/daas/src/i18n/langs/zh-CN.js @@ -217,6 +217,45 @@ export default { dashboard_error: '校验出错的任务数', dashboard_no_data_here: '这里没用数据哦~', dashboard_no_statistics: '暂无{0}统计', + // Dashboard ODH + dashboard_odh_active_tasks: '活跃任务', + dashboard_odh_running: '运行中', + dashboard_odh_error: '错误', + dashboard_odh_max_lag: '最大延迟', + dashboard_odh_min_lag: '最小延迟', + dashboard_odh_total_throughput: '总吞吐量', + dashboard_odh_events_sec: '事件/秒', + dashboard_odh_peak: '峰值', + dashboard_odh_data: '数据', + dashboard_odh_vs_last_hour: '较上一小时', + dashboard_odh_connected_dbs: '已连接数据库', + dashboard_odh_sources: '个数据源', + dashboard_odh_tbls: '张表', + dashboard_odh_api_requests: 'API 请求', + dashboard_odh_failed: '失败', + dashboard_odh_rate: '错误率', + dashboard_odh_avg_time: '平均耗时', + dashboard_odh_system_trends: '系统趋势', + dashboard_odh_throughput_chart: '吞吐量(事件/秒)', + dashboard_odh_api_chart: 'API 请求(请求/秒)', + dashboard_odh_no_data_in_range: '该时间范围内暂无数据', + dashboard_odh_top_tasks: '任务排行', + dashboard_odh_most_lagging: '延迟最高', + dashboard_odh_highest_throughput: '吞吐量最高', + dashboard_odh_top_n: '前 {0}', + dashboard_odh_task_name: '任务名称', + dashboard_odh_latency: '延迟', + dashboard_odh_throughput: '吞吐量', + dashboard_odh_no_task_data: '暂无任务数据', + dashboard_odh_agent_cluster: 'Agent 集群状态', + dashboard_odh_nodes_total: '共 {0} 个节点', + dashboard_odh_agent_name: 'Agent 名称', + dashboard_odh_status: '状态', + dashboard_odh_cpu_usage: 'CPU 使用率', + dashboard_odh_memory_usage: '内存使用率', + dashboard_odh_running_tasks: '运行中任务', + dashboard_odh_no_agents: '暂无 Agent', + dashboard_odh_just_now: '刚刚', // 元数据管理 metadata_db: '所属库', metadata_change_name: '改名', diff --git a/apps/daas/src/i18n/langs/zh-TW.js b/apps/daas/src/i18n/langs/zh-TW.js index 3a81b1147..a740da68c 100644 --- a/apps/daas/src/i18n/langs/zh-TW.js +++ b/apps/daas/src/i18n/langs/zh-TW.js @@ -217,6 +217,45 @@ export default { dashboard_error: '校驗出錯的任務數', dashboard_no_data_here: '這裡沒用數據哦~', dashboard_no_statistics: '暫無{0}統計', + // Dashboard ODH + dashboard_odh_active_tasks: '活躍任務', + dashboard_odh_running: '運行中', + dashboard_odh_error: '錯誤', + dashboard_odh_max_lag: '最大延遲', + dashboard_odh_min_lag: '最小延遲', + dashboard_odh_total_throughput: '總吞吐量', + dashboard_odh_events_sec: '事件/秒', + dashboard_odh_peak: '峰值', + dashboard_odh_data: '數據', + dashboard_odh_vs_last_hour: '較上一小時', + dashboard_odh_connected_dbs: '已連接資料庫', + dashboard_odh_sources: '個資料源', + dashboard_odh_tbls: '張表', + dashboard_odh_api_requests: 'API 請求', + dashboard_odh_failed: '失敗', + dashboard_odh_rate: '錯誤率', + dashboard_odh_avg_time: '平均耗時', + dashboard_odh_system_trends: '系統趨勢', + dashboard_odh_throughput_chart: '吞吐量(事件/秒)', + dashboard_odh_api_chart: 'API 請求(請求/秒)', + dashboard_odh_no_data_in_range: '該時間範圍內暫無數據', + dashboard_odh_top_tasks: '任務排行', + dashboard_odh_most_lagging: '延遲最高', + dashboard_odh_highest_throughput: '吞吐量最高', + dashboard_odh_top_n: '前 {0}', + dashboard_odh_task_name: '任務名稱', + dashboard_odh_latency: '延遲', + dashboard_odh_throughput: '吞吐量', + dashboard_odh_no_task_data: '暫無任務數據', + dashboard_odh_agent_cluster: 'Agent 叢集狀態', + dashboard_odh_nodes_total: '共 {0} 個節點', + dashboard_odh_agent_name: 'Agent 名稱', + dashboard_odh_status: '狀態', + dashboard_odh_cpu_usage: 'CPU 使用率', + dashboard_odh_memory_usage: '記憶體使用率', + dashboard_odh_running_tasks: '運行中任務', + dashboard_odh_no_agents: '暫無 Agent', + dashboard_odh_just_now: '剛剛', // 元數據管理 metadata_db: '所屬庫', metadata_change_name: '改名', diff --git a/apps/daas/src/views/dashboard/Dashboard.vue b/apps/daas/src/views/dashboard/Dashboard.vue index b17ecd7c1..2077ea20d 100644 --- a/apps/daas/src/views/dashboard/Dashboard.vue +++ b/apps/daas/src/views/dashboard/Dashboard.vue @@ -1,926 +1,827 @@ - diff --git a/packages/api/src/core/task.ts b/packages/api/src/core/task.ts index da3b5f449..e92082105 100644 --- a/packages/api/src/core/task.ts +++ b/packages/api/src/core/task.ts @@ -442,3 +442,89 @@ export function checkTaskMemoryHeap(taskId: string) { isSafe: boolean }>(`${BASE_URL}/checkTaskMemoryHeap/${taskId}`) } + +// ── Task Dashboard ────────────────────────────────────────────────── + +export interface TaskDashboardQuery { + type: string + step: number + dashboardType: string + top: number + startAt: number + endAt: number +} + +export interface TaskDashboardActiveTasks { + total: number + running: number + error: number + maxLag: number + minLag: number +} + +export interface TaskDashboardThroughput { + current: number + peak: number + dataRate: number + changeRate: number +} + +export interface TaskDashboardConnectedDb { + id: string + name: string + tableCount: number +} + +export interface TaskDashboardApiRequests { + total: number + failed: number + errorRate: number + avgTime: number +} + +export interface TaskDashboardTrendSeries { + ts: number[] + values: number[] +} + +export interface TaskDashboardTopTask { + taskId: string + taskName: string + syncType?: string + latency: number + throughput: number +} + +export interface TaskDashboardVo { + query: TaskDashboardQuery + summary: { + activeTasks: TaskDashboardActiveTasks + totalThroughput: TaskDashboardThroughput + connectedDbs: { + total: number + items: TaskDashboardConnectedDb[] + } + apiRequests: TaskDashboardApiRequests + } + trends: { + throughput: TaskDashboardTrendSeries + apiRequests: TaskDashboardTrendSeries + } + tops: { + topLaggingTasks: TaskDashboardTopTask[] + topThroughputTasks: TaskDashboardTopTask[] + } +} + +export interface TaskDashboardParams { + type?: 'minute' | 'hours' | 'days' + step?: number + dashboardType?: string + top?: number +} + +export function fetchTaskDashboard(params?: TaskDashboardParams) { + return requestClient.get(`${BASE_URL}/dashboard`, { + params, + }) +} diff --git a/packages/assets/styles/utilities.scss b/packages/assets/styles/utilities.scss index 2245899bb..fc9010025 100644 --- a/packages/assets/styles/utilities.scss +++ b/packages/assets/styles/utilities.scss @@ -12852,6 +12852,10 @@ grid-template-columns: repeat(3, minmax(0, 1fr)); } +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + .grid-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } @@ -13189,3 +13193,19 @@ .bg-components-kbd-bg-gray { background-color: var(--color-components-kbd-bg-gray); } + +.text-blue-500 { + color: var(--color-blue-500); +} + +.text-indigo-500 { + color: var(--color-indigo-500); +} + +.text-teal-500 { + color: var(--color-teal-500); +} + +.text-purple-500 { + color: var(--color-purple-500); +} diff --git a/packages/dag/src/EditorView.vue b/packages/dag/src/EditorView.vue index c7eb66260..e1a9f3339 100644 --- a/packages/dag/src/EditorView.vue +++ b/packages/dag/src/EditorView.vue @@ -83,12 +83,13 @@ const init = async () => { if (taskId) { await initNodeType() - await dataflowStore.fetchDataflow(taskId) - // nextTick(() => { - // setTimeout(() => { - // canvasRef.value.fitViewWithOffset({ duration: 0, maxZoom: 1 }) - // }, 0) - // }) + const task = await dataflowStore.fetchDataflow(taskId) + + if (!task) { + ElMessage.error(t('packages_dag_mixins_editor_renwubucunzai')) + handlePageReturn() + return + } } else { let syncType let targetRoute diff --git a/packages/dag/src/composables/useCanvasOperation.ts b/packages/dag/src/composables/useCanvasOperation.ts index 1c09b5038..16df9ff3d 100644 --- a/packages/dag/src/composables/useCanvasOperation.ts +++ b/packages/dag/src/composables/useCanvasOperation.ts @@ -1771,8 +1771,22 @@ export function useCanvasOperation() { connHeartbeat: 'heartbeatTable', } + const listRoute = computed(() => { + const name = route.name as string + const map = { + DataflowEditor: 'dataflowList', + TaskMonitor: 'dataflowList', + MigrateEditor: 'migrateList', + MigrationMonitor: 'migrateList', + } + + return map[name] + }) + const handlePageReturn = () => { - const listRoute = listRouteMap[dataflow.value.syncType] + const routeName = dataflow.value.syncType + ? listRouteMap[dataflow.value.syncType] + : listRoute.value if (!dataflowStore.dag.nodes.length && dataflow.value.id) { Modal.confirm( @@ -1787,13 +1801,13 @@ export function useCanvasOperation() { deleteTask(dataflow.value.id) } router.push({ - name: listRoute, + name: routeName, }) window.name = '' }) } else { router.push({ - name: listRoute, + name: routeName, }) window.name = '' } diff --git a/packages/dag/src/stores/dataflow.store.ts b/packages/dag/src/stores/dataflow.store.ts index 120085cbb..d12c8777a 100644 --- a/packages/dag/src/stores/dataflow.store.ts +++ b/packages/dag/src/stores/dataflow.store.ts @@ -268,6 +268,10 @@ export const useDataflowStore = defineStore('dataflow', () => { dag.value.nodes = nodes dag.value.edges = edges + + return response + } catch (error) { + console.error(error) } finally { taskLoading.value = false } diff --git a/packages/styles/src/design/tokens/default.css b/packages/styles/src/design/tokens/default.css index b0c941fc2..ad78f83b1 100644 --- a/packages/styles/src/design/tokens/default.css +++ b/packages/styles/src/design/tokens/default.css @@ -40,6 +40,7 @@ --color-blue-50: oklch(97% 0.014 254.604); --color-blue-200: oklch(88.2% 0.059 254.128); --color-blue-300: oklch(80.9% 0.105 251.813); + --color-blue-500: oklch(62.3% 0.214 259.815); --color-blue-600: oklch(54.6% 0.245 262.881); --color-blue-800: oklch(42.4% 0.199 265.638); --color-blue-950: oklch(28.2% 0.091 267.935); @@ -49,6 +50,9 @@ --color-orange-600: oklch(64.6% 0.222 41.116); --color-orange-800: oklch(47% 0.157 37.304); --color-orange-950: oklch(26.6% 0.079 36.259); + --color-indigo-500: oklch(58.5% 0.233 277.117); + --color-teal-500: oklch(70.4% 0.14 182.503); + --color-purple-500: oklch(62.7% 0.265 303.9); --text-dark: #1d2129; --text-normal: #333c4a; diff --git a/packages/types/src/daas-components.d.ts b/packages/types/src/daas-components.d.ts index 9d31fd0f4..b53aa470e 100644 --- a/packages/types/src/daas-components.d.ts +++ b/packages/types/src/daas-components.d.ts @@ -90,7 +90,9 @@ declare module 'vue' { ElTreeV2: typeof import('element-plus/es')['ElTreeV2'] ElUpload: typeof import('element-plus/es')['ElUpload'] IFluentFolderLink16Regular: typeof import('~icons/fluent/folder-link16-regular')['default'] + ILucideActivity: typeof import('~icons/lucide/activity')['default'] ILucideAlertCircle: typeof import('~icons/lucide/alert-circle')['default'] + ILucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default'] ILucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] ILucideArrowRight: typeof import('~icons/lucide/arrow-right')['default'] ILucideBadgeAlert: typeof import('~icons/lucide/badge-alert')['default'] @@ -99,6 +101,7 @@ declare module 'vue' { ILucideCalendarDays: typeof import('~icons/lucide/calendar-days')['default'] ILucideCheck: typeof import('~icons/lucide/check')['default'] ILucideCheckCheck: typeof import('~icons/lucide/check-check')['default'] + ILucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] ILucideChevronDown: typeof import('~icons/lucide/chevron-down')['default'] ILucideChevronLeft: typeof import('~icons/lucide/chevron-left')['default'] ILucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] @@ -124,6 +127,7 @@ declare module 'vue' { ILucideFolder: typeof import('~icons/lucide/folder')['default'] ILucideFolderClosed: typeof import('~icons/lucide/folder-closed')['default'] ILucideFolderOpen: typeof import('~icons/lucide/folder-open')['default'] + ILucideFoldVertical: typeof import('~icons/lucide/fold-vertical')['default'] ILucideGitBranch: typeof import('~icons/lucide/git-branch')['default'] ILucideGitCompareArrows: typeof import('~icons/lucide/git-compare-arrows')['default'] ILucideGithub: typeof import('~icons/lucide/github')['default'] @@ -165,14 +169,17 @@ declare module 'vue' { ILucideTable: typeof import('~icons/lucide/table')['default'] ILucideTag: typeof import('~icons/lucide/tag')['default'] ILucideTrash2: typeof import('~icons/lucide/trash2')['default'] + ILucideTrendingUp: typeof import('~icons/lucide/trending-up')['default'] ILucideTriangleAlert: typeof import('~icons/lucide/triangle-alert')['default'] ILucideUndo: typeof import('~icons/lucide/undo')['default'] ILucideUndo2: typeof import('~icons/lucide/undo2')['default'] + ILucideUnfoldVertical: typeof import('~icons/lucide/unfold-vertical')['default'] ILucideUpload: typeof import('~icons/lucide/upload')['default'] ILucideUserRound: typeof import('~icons/lucide/user-round')['default'] ILucideWandSparkles: typeof import('~icons/lucide/wand-sparkles')['default'] ILucideWorkflow: typeof import('~icons/lucide/workflow')['default'] ILucideX: typeof import('~icons/lucide/x')['default'] + ILucideXCircle: typeof import('~icons/lucide/x-circle')['default'] ILucideZoomIn: typeof import('~icons/lucide/zoom-in')['default'] ILucideZoomOut: typeof import('~icons/lucide/zoom-out')['default'] IMingcuteAddFill: typeof import('~icons/mingcute/add-fill')['default'] @@ -288,7 +295,9 @@ declare global { const ElTreeV2: typeof import('element-plus/es')['ElTreeV2'] const ElUpload: typeof import('element-plus/es')['ElUpload'] const IFluentFolderLink16Regular: typeof import('~icons/fluent/folder-link16-regular')['default'] + const ILucideActivity: typeof import('~icons/lucide/activity')['default'] const ILucideAlertCircle: typeof import('~icons/lucide/alert-circle')['default'] + const ILucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default'] const ILucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] const ILucideArrowRight: typeof import('~icons/lucide/arrow-right')['default'] const ILucideBadgeAlert: typeof import('~icons/lucide/badge-alert')['default'] @@ -297,6 +306,7 @@ declare global { const ILucideCalendarDays: typeof import('~icons/lucide/calendar-days')['default'] const ILucideCheck: typeof import('~icons/lucide/check')['default'] const ILucideCheckCheck: typeof import('~icons/lucide/check-check')['default'] + const ILucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] const ILucideChevronDown: typeof import('~icons/lucide/chevron-down')['default'] const ILucideChevronLeft: typeof import('~icons/lucide/chevron-left')['default'] const ILucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] @@ -322,6 +332,7 @@ declare global { const ILucideFolder: typeof import('~icons/lucide/folder')['default'] const ILucideFolderClosed: typeof import('~icons/lucide/folder-closed')['default'] const ILucideFolderOpen: typeof import('~icons/lucide/folder-open')['default'] + const ILucideFoldVertical: typeof import('~icons/lucide/fold-vertical')['default'] const ILucideGitBranch: typeof import('~icons/lucide/git-branch')['default'] const ILucideGitCompareArrows: typeof import('~icons/lucide/git-compare-arrows')['default'] const ILucideGithub: typeof import('~icons/lucide/github')['default'] @@ -363,14 +374,17 @@ declare global { const ILucideTable: typeof import('~icons/lucide/table')['default'] const ILucideTag: typeof import('~icons/lucide/tag')['default'] const ILucideTrash2: typeof import('~icons/lucide/trash2')['default'] + const ILucideTrendingUp: typeof import('~icons/lucide/trending-up')['default'] const ILucideTriangleAlert: typeof import('~icons/lucide/triangle-alert')['default'] const ILucideUndo: typeof import('~icons/lucide/undo')['default'] const ILucideUndo2: typeof import('~icons/lucide/undo2')['default'] + const ILucideUnfoldVertical: typeof import('~icons/lucide/unfold-vertical')['default'] const ILucideUpload: typeof import('~icons/lucide/upload')['default'] const ILucideUserRound: typeof import('~icons/lucide/user-round')['default'] const ILucideWandSparkles: typeof import('~icons/lucide/wand-sparkles')['default'] const ILucideWorkflow: typeof import('~icons/lucide/workflow')['default'] const ILucideX: typeof import('~icons/lucide/x')['default'] + const ILucideXCircle: typeof import('~icons/lucide/x-circle')['default'] const ILucideZoomIn: typeof import('~icons/lucide/zoom-in')['default'] const ILucideZoomOut: typeof import('~icons/lucide/zoom-out')['default'] const IMingcuteAddFill: typeof import('~icons/mingcute/add-fill')['default']