feat(web): add API client, router, and app layout
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
39
web/src/App.vue
Normal file
39
web/src/App.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<el-config-provider :locale="zhCn">
|
||||
<el-container style="height: 100vh">
|
||||
<el-aside width="200px" style="border-right: 1px solid #e6e6e6">
|
||||
<div style="padding: 16px; font-size: 16px; font-weight: 600; text-align: center">
|
||||
HPC 集群管理
|
||||
</div>
|
||||
<el-menu :router="true" :default-active="route.path">
|
||||
<el-menu-item index="/jobs">
|
||||
<el-icon><List /></el-icon>
|
||||
<span>任务列表</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/jobs/history">
|
||||
<el-icon><Timer /></el-icon>
|
||||
<span>任务历史</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/jobs/submit">
|
||||
<el-icon><CirclePlus /></el-icon>
|
||||
<span>提交任务</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<router-view />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
import { List, Timer, CirclePlus } from '@element-plus/icons-vue'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
onMounted(() => {
|
||||
document.title = 'HPC 集群管理'
|
||||
})
|
||||
</script>
|
||||
24
web/src/api/client.ts
Normal file
24
web/src/api/client.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import axios from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type { ApiResponse } from '@/types/jobs'
|
||||
|
||||
const apiClient = axios.create({
|
||||
baseURL: '/api/v1',
|
||||
})
|
||||
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => {
|
||||
return response.data
|
||||
},
|
||||
(error) => {
|
||||
if (error.response?.data) {
|
||||
const apiError = error.response.data as ApiResponse<unknown>
|
||||
ElMessage.error(apiError.error || '请求失败')
|
||||
} else {
|
||||
ElMessage.error('无法连接到后端服务')
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default apiClient
|
||||
22
web/src/api/jobs.ts
Normal file
22
web/src/api/jobs.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import apiClient from './client'
|
||||
import type { ApiResponse, SubmitJobRequest, JobResponse, JobListResponse, JobHistoryParams } from '@/types/jobs'
|
||||
|
||||
export function submitJob(data: SubmitJobRequest): Promise<ApiResponse<JobResponse>> {
|
||||
return apiClient.post('/jobs/submit', data)
|
||||
}
|
||||
|
||||
export function getJobs(params?: { page?: number; page_size?: number }): Promise<ApiResponse<JobListResponse>> {
|
||||
return apiClient.get('/jobs', { params })
|
||||
}
|
||||
|
||||
export function getJobHistory(params?: JobHistoryParams): Promise<ApiResponse<JobListResponse>> {
|
||||
return apiClient.get('/jobs/history', { params })
|
||||
}
|
||||
|
||||
export function getJob(id: string): Promise<ApiResponse<JobResponse>> {
|
||||
return apiClient.get(`/jobs/${id}`)
|
||||
}
|
||||
|
||||
export function cancelJob(id: string): Promise<ApiResponse<{ message: string }>> {
|
||||
return apiClient.delete(`/jobs/${id}`)
|
||||
}
|
||||
6
web/src/main.ts
Normal file
6
web/src/main.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createApp } from 'vue'
|
||||
import 'element-plus/dist/index.css'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
createApp(App).use(router).mount('#app')
|
||||
41
web/src/router/index.ts
Normal file
41
web/src/router/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/jobs',
|
||||
},
|
||||
{
|
||||
path: '/jobs',
|
||||
name: 'JobsList',
|
||||
component: () => import('@/views/Jobs/List.vue'),
|
||||
meta: { title: '任务列表' },
|
||||
},
|
||||
{
|
||||
path: '/jobs/history',
|
||||
name: 'JobsHistory',
|
||||
component: () => import('@/views/Jobs/History.vue'),
|
||||
meta: { title: '任务历史' },
|
||||
},
|
||||
{
|
||||
path: '/jobs/submit',
|
||||
name: 'JobsSubmit',
|
||||
component: () => import('@/views/Jobs/Submit.vue'),
|
||||
meta: { title: '提交任务' },
|
||||
},
|
||||
{
|
||||
path: '/jobs/:id',
|
||||
name: 'JobsDetail',
|
||||
component: () => import('@/views/Jobs/Detail.vue'),
|
||||
meta: { title: '任务详情' },
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
redirect: '/jobs',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
export default router
|
||||
Reference in New Issue
Block a user