前端页面总算能显示出来了, 功能还没有开始做
This commit is contained in:
parent
e43ac40027
commit
bf574b3dc5
1546
frontend/package-lock.json
generated
Normal file
1546
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
frontend/package.json
Normal file
25
frontend/package.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "ml-platform-frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.2",
|
||||
"dayjs": "^1.11.10",
|
||||
"element-plus": "^2.4.3",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.3.9",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.3",
|
||||
"@vitejs/plugin-vue": "^4.5.1",
|
||||
"typescript": "^5.3.2",
|
||||
"vite": "^4.5.0",
|
||||
"vue-tsc": "^1.8.24"
|
||||
}
|
||||
}
|
||||
68
frontend/src/App.vue
Normal file
68
frontend/src/App.vue
Normal file
@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<el-config-provider :locale="zhCn">
|
||||
<div class="app-container">
|
||||
<!-- 侧边栏 -->
|
||||
<el-menu
|
||||
class="sidebar"
|
||||
:router="true"
|
||||
:default-active="$route.path"
|
||||
>
|
||||
<el-menu-item index="/data">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span>数据管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/model">
|
||||
<el-icon><Monitor /></el-icon>
|
||||
<span>模型管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/system">
|
||||
<el-icon><Setting /></el-icon>
|
||||
<span>系统监控</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<div class="main-content">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Document, Monitor, Setting } from '@element-plus/icons-vue'
|
||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #2c3e50;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 200px;
|
||||
height: 100%;
|
||||
border-right: solid 1px #e6e6e6;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
67
frontend/src/api/data.ts
Normal file
67
frontend/src/api/data.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import request from '@/utils/request'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
import type {
|
||||
PreprocessMethod,
|
||||
FeatureMethod,
|
||||
ProcessRequest,
|
||||
CSVRequest,
|
||||
DatasetInfo
|
||||
} from '@/types/data'
|
||||
|
||||
// 获取预处理方法列表
|
||||
export const getPreprocessMethods = (): Promise<AxiosResponse<PreprocessMethod[]>> => {
|
||||
return request({
|
||||
url: '/data/preprocessing/methods',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取预处理方法详情
|
||||
export const getPreprocessMethodDetails = (methodName: string): Promise<AxiosResponse> => {
|
||||
return request({
|
||||
url: `/data/preprocessing/method/${methodName}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取特征工程方法列表
|
||||
export const getFeatureMethods = (): Promise<AxiosResponse<FeatureMethod[]>> => {
|
||||
return request({
|
||||
url: '/data/feature/methods',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取特征工程方法详情
|
||||
export const getFeatureMethodDetails = (methodName: string): Promise<AxiosResponse> => {
|
||||
return request({
|
||||
url: `/data/feature/method/${methodName}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 处理数据集
|
||||
export const processDataset = (data: ProcessRequest): Promise<AxiosResponse> => {
|
||||
return request({
|
||||
url: '/data/process',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取可用数据集列表
|
||||
export const getDatasets = (): Promise<AxiosResponse<DatasetInfo[]>> => {
|
||||
return request({
|
||||
url: '/data/datasets',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 读取CSV文件
|
||||
export const readCSV = (data: CSVRequest): Promise<AxiosResponse> => {
|
||||
return request({
|
||||
url: '/data/csv',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
7
frontend/src/api/system.ts
Normal file
7
frontend/src/api/system.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// 健康检查
|
||||
export const checkHealth = (): Promise<AxiosResponse> => {
|
||||
return request({
|
||||
url: '/health',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
175
frontend/src/components/data/DataPreview.vue
Normal file
175
frontend/src/components/data/DataPreview.vue
Normal file
@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="数据预览"
|
||||
width="80%"
|
||||
>
|
||||
<el-tabs v-model="activeTab">
|
||||
<!-- 训练集预览 -->
|
||||
<el-tab-pane label="训练集" name="train">
|
||||
<div v-loading="loading.train">
|
||||
<el-descriptions title="数据集信息" :column="3" border>
|
||||
<el-descriptions-item label="行数">
|
||||
{{ dataInfo.train?.info?.rows || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="列数">
|
||||
{{ dataInfo.train?.info?.columns || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="内存占用">
|
||||
{{ dataInfo.train?.info?.memory_usage || '-' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="mt-4">
|
||||
<h4>数据预览</h4>
|
||||
<el-table
|
||||
:data="dataInfo.train?.head || []"
|
||||
style="width: 100%"
|
||||
max-height="400"
|
||||
border
|
||||
>
|
||||
<el-table-column
|
||||
v-for="col in getColumns(dataInfo.train?.head)"
|
||||
:key="col"
|
||||
:prop="col"
|
||||
:label="col"
|
||||
/>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<h4>数据统计</h4>
|
||||
<el-table
|
||||
:data="getDescribeData(dataInfo.train?.describe)"
|
||||
style="width: 100%"
|
||||
border
|
||||
>
|
||||
<el-table-column prop="metric" label="统计量" width="180" />
|
||||
<el-table-column
|
||||
v-for="col in getDescribeColumns(dataInfo.train?.describe)"
|
||||
:key="col"
|
||||
:prop="col"
|
||||
:label="col"
|
||||
/>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 验证集预览 -->
|
||||
<el-tab-pane label="验证集" name="val">
|
||||
<!-- 与训练集类似的内容 -->
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 测试集预览 -->
|
||||
<el-tab-pane label="测试集" name="test">
|
||||
<!-- 与训练集类似的内容 -->
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import type { DatasetInfo } from '@/types/data'
|
||||
import { readCSV } from '@/api/data'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
dataset: DatasetInfo | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const activeTab = ref('train')
|
||||
const loading = ref({
|
||||
train: false,
|
||||
val: false,
|
||||
test: false
|
||||
})
|
||||
|
||||
const dataInfo = ref({
|
||||
train: null,
|
||||
val: null,
|
||||
test: null
|
||||
})
|
||||
|
||||
// 监听对话框显示状态
|
||||
watch(() => props.visible, (val) => {
|
||||
dialogVisible.value = val
|
||||
if (val && props.dataset) {
|
||||
loadData('train')
|
||||
}
|
||||
})
|
||||
|
||||
// 监听对话框关闭
|
||||
watch(dialogVisible, (val) => {
|
||||
emit('update:visible', val)
|
||||
})
|
||||
|
||||
// 监听标签页切换
|
||||
watch(activeTab, (val) => {
|
||||
if (!dataInfo.value[val]) {
|
||||
loadData(val)
|
||||
}
|
||||
})
|
||||
|
||||
// 加载数据
|
||||
const loadData = async (type: 'train' | 'val' | 'test') => {
|
||||
if (!props.dataset) return
|
||||
|
||||
const filePath = props.dataset.output_files[type]
|
||||
if (!filePath) return
|
||||
|
||||
loading.value[type] = true
|
||||
try {
|
||||
const { data } = await readCSV({
|
||||
data_path: filePath,
|
||||
head: 10,
|
||||
tail: 5,
|
||||
info: true,
|
||||
describe: true
|
||||
})
|
||||
dataInfo.value[type] = data.data
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
loading.value[type] = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取表格列
|
||||
const getColumns = (data: any[] = []) => {
|
||||
return data.length > 0 ? Object.keys(data[0]) : []
|
||||
}
|
||||
|
||||
// 获取统计数据列
|
||||
const getDescribeColumns = (describe: any = {}) => {
|
||||
return Object.keys(describe)
|
||||
}
|
||||
|
||||
// 转换统计数据为表格格式
|
||||
const getDescribeData = (describe: any = {}) => {
|
||||
if (!describe) return []
|
||||
|
||||
const metrics = ['count', 'mean', 'std', 'min', '25%', '50%', '75%', 'max']
|
||||
return metrics.map(metric => ({
|
||||
metric,
|
||||
...Object.fromEntries(
|
||||
Object.entries(describe).map(([col, stats]: [string, any]) => [
|
||||
col,
|
||||
typeof stats === 'object' ? stats[metric] : null
|
||||
])
|
||||
)
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
192
frontend/src/components/data/DatasetList.vue
Normal file
192
frontend/src/components/data/DatasetList.vue
Normal file
@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div class="dataset-list">
|
||||
<el-table :data="datasets" style="width: 100%">
|
||||
<el-table-column prop="input_file" label="数据集" width="200">
|
||||
<template #default="{ row }">
|
||||
{{ getFileName(row.input_file) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="timestamp" label="处理时间" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ formatTime(row.timestamp) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="数据处理">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-for="method in row.process_methods"
|
||||
:key="method.method_name"
|
||||
class="mx-1"
|
||||
>
|
||||
{{ method.method_name }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="特征工程">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-for="method in row.feature_methods"
|
||||
:key="method.method_name"
|
||||
type="success"
|
||||
class="mx-1"
|
||||
>
|
||||
{{ method.method_name }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="数据划分">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip effect="dark" placement="top">
|
||||
<template #content>
|
||||
<div>训练集: {{ (1 - row.split_params.test_size - row.split_params.val_size) * 100 }}%</div>
|
||||
<div>验证集: {{ row.split_params.val_size * 100 }}%</div>
|
||||
<div>测试集: {{ row.split_params.test_size * 100 }}%</div>
|
||||
</template>
|
||||
<el-progress :percentage="100" :format="() => '查看详情'" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="200">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handlePreview(row)"
|
||||
>
|
||||
预览
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
link
|
||||
@click="handleDownload(row)"
|
||||
>
|
||||
下载
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
link
|
||||
@click="handleDetails(row)"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 预览对话框 -->
|
||||
<DataPreview
|
||||
v-model:visible="previewVisible"
|
||||
:dataset="selectedDataset"
|
||||
/>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog
|
||||
v-model="detailsVisible"
|
||||
title="数据处理详情"
|
||||
width="60%"
|
||||
>
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="step in selectedDataset?.steps"
|
||||
:key="step.step"
|
||||
:type="getStepType(step.step)"
|
||||
>
|
||||
<h4>{{ getStepTitle(step) }}</h4>
|
||||
<p>数据形状: {{ step.shape[0] }} × {{ step.shape[1] }}</p>
|
||||
<template v-if="step.method">
|
||||
<p>处理方法: {{ step.method }}</p>
|
||||
<p>参数配置:</p>
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item
|
||||
v-for="(value, key) in step.params"
|
||||
:key="key"
|
||||
:label="key"
|
||||
>
|
||||
{{ value }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import type { DatasetInfo } from '@/types/data'
|
||||
import DataPreview from './DataPreview.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const props = defineProps<{
|
||||
datasets: DatasetInfo[]
|
||||
}>()
|
||||
|
||||
const previewVisible = ref(false)
|
||||
const detailsVisible = ref(false)
|
||||
const selectedDataset = ref<DatasetInfo | null>(null)
|
||||
|
||||
// 获取文件名
|
||||
const getFileName = (path: string) => {
|
||||
return path.split('/').pop() || path
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (timestamp: string) => {
|
||||
return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
// 获取步骤类型
|
||||
const getStepType = (step: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'load_data': 'primary',
|
||||
'cleaning': 'success',
|
||||
'feature_engineering': 'warning'
|
||||
}
|
||||
return typeMap[step] || 'info'
|
||||
}
|
||||
|
||||
// 获取步骤标题
|
||||
const getStepTitle = (step: DatasetInfo['steps'][0]) => {
|
||||
const titleMap: Record<string, string> = {
|
||||
'load_data': '数据加载',
|
||||
'cleaning': '数据清洗',
|
||||
'feature_engineering': '特征工程'
|
||||
}
|
||||
return titleMap[step.step] || step.step
|
||||
}
|
||||
|
||||
// 处理预览
|
||||
const handlePreview = (dataset: DatasetInfo) => {
|
||||
selectedDataset.value = dataset
|
||||
previewVisible.value = true
|
||||
}
|
||||
|
||||
// 处理下载
|
||||
const handleDownload = (dataset: DatasetInfo) => {
|
||||
// 实现文件下载逻辑
|
||||
ElMessage.success('开始下载数据集')
|
||||
}
|
||||
|
||||
// 处理详情查看
|
||||
const handleDetails = (dataset: DatasetInfo) => {
|
||||
selectedDataset.value = dataset
|
||||
detailsVisible.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dataset-list {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
14
frontend/src/main.ts
Normal file
14
frontend/src/main.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
app.use(ElementPlus)
|
||||
|
||||
app.mount('#app')
|
||||
37
frontend/src/router/index.ts
Normal file
37
frontend/src/router/index.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/data'
|
||||
},
|
||||
{
|
||||
path: '/data',
|
||||
name: 'Data',
|
||||
component: () => import('@/views/data/DatasetView.vue'),
|
||||
meta: {
|
||||
title: '数据管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/model',
|
||||
name: 'Model',
|
||||
component: () => import('@/views/model/ModelView.vue'),
|
||||
meta: {
|
||||
title: '模型管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/system',
|
||||
name: 'System',
|
||||
component: () => import('@/views/system/SystemView.vue'),
|
||||
meta: {
|
||||
title: '系统监控'
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
||||
89
frontend/src/types/data.ts
Normal file
89
frontend/src/types/data.ts
Normal file
@ -0,0 +1,89 @@
|
||||
// 预处理方法
|
||||
export interface PreprocessMethod {
|
||||
name: string
|
||||
description: string
|
||||
method: string[]
|
||||
}
|
||||
|
||||
// 特征工程方法
|
||||
export interface FeatureMethod {
|
||||
name: string
|
||||
description: string
|
||||
method: string[]
|
||||
}
|
||||
|
||||
// 方法参数
|
||||
export interface MethodParameter {
|
||||
name: string
|
||||
type: string
|
||||
default: any
|
||||
description: string
|
||||
}
|
||||
|
||||
// 方法详情
|
||||
export interface MethodDetail {
|
||||
name: string
|
||||
description: string
|
||||
principle: string
|
||||
advantages: string[]
|
||||
disadvantages: string[]
|
||||
applicable_scenarios: string[]
|
||||
parameters: MethodParameter[]
|
||||
}
|
||||
|
||||
// 数据处理请求
|
||||
export interface ProcessRequest {
|
||||
input_path: string
|
||||
output_dir: string
|
||||
preprocessing: {
|
||||
method: string
|
||||
params: Record<string, any>
|
||||
}[]
|
||||
feature_methods: {
|
||||
method: string
|
||||
params: Record<string, any>
|
||||
}[]
|
||||
split_params: {
|
||||
train: number
|
||||
val: number
|
||||
test: number
|
||||
}
|
||||
}
|
||||
|
||||
// CSV读取请求
|
||||
export interface CSVRequest {
|
||||
data_path: string
|
||||
head?: number
|
||||
tail?: number
|
||||
info?: boolean
|
||||
describe?: boolean
|
||||
}
|
||||
|
||||
// 数据集信息
|
||||
export interface DatasetInfo {
|
||||
input_file: string
|
||||
timestamp: string
|
||||
process_methods: {
|
||||
method_name: string
|
||||
params: Record<string, any>
|
||||
}[]
|
||||
feature_methods: {
|
||||
method_name: string
|
||||
params: Record<string, any>
|
||||
}[]
|
||||
split_params: {
|
||||
test_size: number
|
||||
val_size: number
|
||||
}
|
||||
steps: {
|
||||
step: string
|
||||
method?: string
|
||||
params?: Record<string, any>
|
||||
shape: [number, number]
|
||||
}[]
|
||||
output_files: {
|
||||
train: string
|
||||
validation: string
|
||||
test: string
|
||||
}
|
||||
}
|
||||
67
frontend/src/utils/request.ts
Normal file
67
frontend/src/utils/request.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import axios from 'axios'
|
||||
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// 创建axios实例
|
||||
const service: AxiosInstance = axios.create({
|
||||
baseURL: '/',
|
||||
timeout: 50000
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: AxiosRequestConfig) => {
|
||||
// 在这里可以添加token等认证信息
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
console.error('Request error:', error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
const { status, data } = response
|
||||
|
||||
if (status === 200) {
|
||||
if (data.status === 'success') {
|
||||
return data
|
||||
} else {
|
||||
ElMessage.error(data.message || '请求失败')
|
||||
return Promise.reject(new Error(data.message || '请求失败'))
|
||||
}
|
||||
} else {
|
||||
ElMessage.error('请求失败')
|
||||
return Promise.reject(new Error('请求失败'))
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
// 添加详细的错误处理
|
||||
if (error.response) {
|
||||
const { status, data } = error.response
|
||||
switch (status) {
|
||||
case 422:
|
||||
ElMessage.error('请求参数验证失败: ' + (data.details?.[0]?.msg || '未知错误'))
|
||||
break
|
||||
case 404:
|
||||
ElMessage.error('请求的资源不存在')
|
||||
break
|
||||
case 500:
|
||||
ElMessage.error('服务器内部错误: ' + (data.details || data.message || '未知错误'))
|
||||
break
|
||||
default:
|
||||
ElMessage.error(error.message || '请求失败')
|
||||
}
|
||||
} else if (error.request) {
|
||||
ElMessage.error('无法连接到服务器')
|
||||
} else {
|
||||
ElMessage.error('请求配置错误')
|
||||
}
|
||||
console.error('Response error:', error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
||||
30
frontend/src/views/data/DatasetView.vue
Normal file
30
frontend/src/views/data/DatasetView.vue
Normal file
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div class="dataset-view">
|
||||
<h2>数据集管理</h2>
|
||||
<DatasetList :datasets="datasets" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getDatasets } from '@/api/data'
|
||||
import DatasetList from '@/components/data/DatasetList.vue'
|
||||
import type { DatasetInfo } from '@/types/data'
|
||||
|
||||
const datasets = ref<DatasetInfo[]>([])
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const { data } = await getDatasets()
|
||||
datasets.value = data.datasets
|
||||
} catch (error) {
|
||||
console.error('Failed to load datasets:', error)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dataset-view {
|
||||
min-height: 100%;
|
||||
}
|
||||
</style>
|
||||
31
frontend/tsconfig.json
Normal file
31
frontend/tsconfig.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
/* Path Alias */
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
10
frontend/tsconfig.node.json
Normal file
10
frontend/tsconfig.node.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
34
frontend/vite.config.ts
Normal file
34
frontend/vite.config.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { resolve } from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src')
|
||||
}
|
||||
},
|
||||
server: {
|
||||
host: true,
|
||||
port: 5003,
|
||||
proxy: {
|
||||
'/data': {
|
||||
target: 'http://10.0.0.202:8992',
|
||||
changeOrigin: true
|
||||
},
|
||||
'/model': {
|
||||
target: 'http://10.0.0.202:8992',
|
||||
changeOrigin: true
|
||||
},
|
||||
'/system': {
|
||||
target: 'http://10.0.0.202:8992',
|
||||
changeOrigin: true
|
||||
},
|
||||
'/health': {
|
||||
target: 'http://10.0.0.202:8992',
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user