diff --git a/web/src/App.vue b/web/src/App.vue
new file mode 100644
index 0000000..ec9e6ae
--- /dev/null
+++ b/web/src/App.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+ HPC 集群管理
+
+
+
+
+ 任务列表
+
+
+
+ 任务历史
+
+
+
+ 提交任务
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/api/client.ts b/web/src/api/client.ts
new file mode 100644
index 0000000..a892414
--- /dev/null
+++ b/web/src/api/client.ts
@@ -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
+ ElMessage.error(apiError.error || '请求失败')
+ } else {
+ ElMessage.error('无法连接到后端服务')
+ }
+ return Promise.reject(error)
+ }
+)
+
+export default apiClient
diff --git a/web/src/api/jobs.ts b/web/src/api/jobs.ts
new file mode 100644
index 0000000..0a05300
--- /dev/null
+++ b/web/src/api/jobs.ts
@@ -0,0 +1,22 @@
+import apiClient from './client'
+import type { ApiResponse, SubmitJobRequest, JobResponse, JobListResponse, JobHistoryParams } from '@/types/jobs'
+
+export function submitJob(data: SubmitJobRequest): Promise> {
+ return apiClient.post('/jobs/submit', data)
+}
+
+export function getJobs(params?: { page?: number; page_size?: number }): Promise> {
+ return apiClient.get('/jobs', { params })
+}
+
+export function getJobHistory(params?: JobHistoryParams): Promise> {
+ return apiClient.get('/jobs/history', { params })
+}
+
+export function getJob(id: string): Promise> {
+ return apiClient.get(`/jobs/${id}`)
+}
+
+export function cancelJob(id: string): Promise> {
+ return apiClient.delete(`/jobs/${id}`)
+}
diff --git a/web/src/main.ts b/web/src/main.ts
new file mode 100644
index 0000000..7e0fdcf
--- /dev/null
+++ b/web/src/main.ts
@@ -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')
diff --git a/web/src/router/index.ts b/web/src/router/index.ts
new file mode 100644
index 0000000..5150b49
--- /dev/null
+++ b/web/src/router/index.ts
@@ -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