feat: 搭建应用基础框架

包含应用入口、根组件、路由配置(含登录守卫)、主布局、全局样式、ECharts 插件、类型定义、常量映射表及静态资源。
This commit is contained in:
caoxiaozhu
2026-07-10 16:44:49 +08:00
parent 855308e5fa
commit 8aa67003c8
11 changed files with 1112 additions and 0 deletions

7
frontend/src/App.vue Normal file
View File

@@ -0,0 +1,7 @@
<script setup lang="ts">
// 根组件,仅承载路由出口
</script>
<template>
<router-view />
</template>

View File

@@ -0,0 +1,106 @@
import type { DimensionType } from '@/types'
/** 各指标类型的评估方法选项 */
export const EVAL_METHODS: Record<
DimensionType | string,
{ value: string; name: string; desc: string }[]
> = {
classification: [
{ value: 'standard', name: '标准匹配', desc: '根据预设标准评估' },
{ value: 'sentiment', name: '情感分析', desc: '识别文本情感倾向' },
{ value: 'custom', name: '自定义评分器', desc: '自定义评估规则' },
],
metric: [
{ value: 'metric_standard', name: '综合评测', desc: '根据预设标准评分' },
{ value: 'semantic', name: '语义相似度', desc: '计算语义相似程度' },
{ value: 'custom', name: '自定义评分器', desc: '自定义评估规则' },
],
text_similarity: [
{ value: 'bleu_4', name: 'BLEU-4', desc: 'N-gram 精确度评估' },
{ value: 'cosine', name: 'Cosine', desc: '余弦相似度' },
{ value: 'rouge_1', name: 'ROUGE-1', desc: 'unigram 重叠评估' },
{ value: 'rouge_2', name: 'ROUGE-2', desc: 'bigram 重叠评估' },
{ value: 'rouge_4', name: 'ROUGE-4', desc: '4-gram 重叠评估' },
],
}
/** 各评估方法的预置 Prompt 模板 */
export const EVAL_METHOD_PROMPTS: Record<string, string> = {
standard: `# 角色
你是一位专业的对话评估专家,擅长根据提供的标准对助理在对话中的最终反应进行评估,并确定其是否[[Pass]]或[[Fail]]。
## 技能
### 技能 1: 对话回顾
- 通读整个对话以理解上下文和背景信息。
- 确保全面理解对话的意图和用户的需求。
### 技能 2: 最终回答识别
- 从对话中准确识别出助理给出的最后一个回答。
- 确保关注的是最终的回答,而不是中间的部分。
### 技能 3: 标准应用
- 仔细审查每个评价标准。
- 将助理的最终回答与标准的各个方面进行详细比较。
### 技能 4: 逐步推理
- 记录每个标准以及最终响应如何满足或不满足该标准。
- 提供详细的证据,并解释为什么最终答复满足或不满足期望。
### 技能 5: 结果判定
- 根据逐步推理的结果,判断最终回答是[[Pass]]或[[Fail]]
- 提供明确的结论并解释理由。
## 输出格式
以下列格式提供结果:
- **分步推理:**[详细推理在这里]
- **最终结果:**[[Pass]]或[[Fail]]
## 限制
- 只针对对话中的最终回答进行评估。
- 在评估过程中,确保遵循提供的标准,避免主观判断。`,
sentiment: `# 角色
你是一位专业的情感分析专家,擅长识别文本中的情感倾向,并给出[[Positive]]、[[Neutral]]或[[Negative]]的判断。
## 技能
### 技能 1: 文本理解
- 通读输入文本,理解其内容和语境。
### 技能 2: 情感识别
- 识别文本中表达的情感词汇、语气和情感强度。
- 分析上下文中的情感线索。
### 技能 3: 情感判定
- 根据识别到的情感信息,判断整体情感倾向。
## 输出格式
- **情感分析:**[分析过程]
- **最终结果:**[[Positive]]/[[Neutral]]/[[Negative]]`,
metric_standard: `# 角色
你是一位专业的模型评估专家擅长根据预设标准对模型输出进行综合评分0-5分
## 评估标准
请从以下维度对模型输出进行评分:
1. 准确性:回答是否准确无误
2. 完整性:是否完整覆盖了问题的各个方面
3. 连贯性:回答是否逻辑清晰、结构合理
4. 可读性:语言表达是否流畅、易懂
## 输出格式
- **评分理由:**[详细说明]
- **最终评分:**[0-5 的分数]`,
semantic: `# 角色
你是一位专业的语义相似度评估专家,擅长计算两段文本之间的语义相似程度。
## 任务
请评估生成文本与参考文本在语义上的相似程度,给出 0-1 之间的分数。
## 输出格式
- **相似度分析:**[分析过程]
- **相似度分数:**[0-1 的分数]`,
custom: `# 自定义评估器
请在此编写您的自定义评估 Prompt...`,
}

View File

@@ -0,0 +1,144 @@
/**
* 全局常量与映射表
*/
/** 模型类型 */
export const MODEL_TYPE_MAP: Record<string, string> = {
LLM: '大语言模型',
CV: '计算机视觉',
NLP: '自然语言处理',
Embedding: '向量模型',
Other: '其他',
}
/** 模型用途 */
export const PURPOSE_MAP: Record<string, { text: string; type: string }> = {
training: { text: '训练', type: 'primary' },
inference: { text: '推理', type: 'success' },
evaluation: { text: '评测', type: 'warning' },
}
/** 模型来源 */
export const MODEL_SOURCE_MAP: Record<string, string> = {
local: '本地模型',
api: '在线模型',
online: '在线模型',
}
/** 训练方式 */
export const TRAIN_TYPE_MAP: Record<string, string> = {
SFT: 'SFT',
DPO: 'DPO',
CPT: 'CPT',
}
/** 训练方法 */
export const TRAIN_METHOD_MAP: Record<string, string> = {
lora: 'LoRA',
qlora: 'QLoRA',
full: '全量微调',
prefix: 'Prefix Tuning',
adapter: 'Adapter',
peft: 'PEFT',
adalora: 'AdaLoRA',
longlora: 'LongLoRA',
dpo: 'DPO',
cpt: 'CPT',
}
/** 数据集类型 */
export const DATASET_TYPE_MAP: Record<string, string> = {
train: '训练数据',
test: '测试数据',
eval: '评测数据',
val: '验证数据',
other: '其他',
}
/** 存储位置 */
export const STORAGE_MAP: Record<string, string> = {
local: '本地存储',
minio: 'MinIO',
cloud: '云存储',
}
/** 评测维度指标类型 */
export const DIMENSION_TYPE_MAP: Record<string, string> = {
classification: '大模型-分类型',
metric: '大模型-指标型',
text_similarity: '规则-文本相似度',
}
/** 学习率调度器类型 */
export const LR_SCHEDULER_OPTIONS = [
{ label: 'cosine', value: 'cosine' },
{ label: 'linear', value: 'linear' },
{ label: 'constant', value: 'constant' },
]
/** 训练模板分组(按模型系列) */
export const TEMPLATE_GROUPS: { label: string; options: { label: string; value: string }[] }[] = [
{
label: 'Qwen 系列',
options: [
{ label: 'qwen', value: 'qwen' },
{ label: 'qwen2', value: 'qwen2' },
{ label: 'qwen2_5', value: 'qwen2_5' },
],
},
{
label: 'Llama 系列',
options: [
{ label: 'llama2', value: 'llama2' },
{ label: 'llama3', value: 'llama3' },
{ label: 'llama3_1', value: 'llama3_1' },
],
},
{
label: 'DeepSeek 系列',
options: [
{ label: 'deepseek', value: 'deepseek' },
{ label: 'deepseek2', value: 'deepseek2' },
],
},
{
label: '其他系列',
options: [
{ label: 'glm4', value: 'glm4' },
{ label: 'chatglm3', value: 'chatglm3' },
{ label: 'gemma', value: 'gemma' },
{ label: 'gemma2', value: 'gemma2' },
{ label: 'phi', value: 'phi' },
{ label: 'intern2', value: 'intern2' },
{ label: 'mistral', value: 'mistral' },
{ label: 'default', value: 'default' },
],
},
]
/** 默认工具列表(其他工具页) */
export const DEFAULT_TOOLS = [
{ id: 'data-generate', name: '数据生成', icon: 'fa-database', description: '基于LLM生成微调数据集' },
{ id: 'json2jsonl', name: 'JSON转JSONL', icon: 'fa-code', description: '将JSON文件转换为JSONL格式' },
{ id: 'md-convert', name: '转换Markdown', icon: 'fa-file-text', description: '将Markdown文件转换为训练数据' },
]
/** 自定义工具可选图标 */
export const TOOL_ICONS = [
'fa-cog', 'fa-database', 'fa-code', 'fa-file-text', 'fa-wrench', 'fa-cube',
'fa-line-chart', 'fa-bar-chart', 'fa-server', 'fa-cogs', 'fa-tasks', 'fa-trophy',
'fa-sliders', 'fa-rocket', 'fa-cloud', 'fa-cloud-download', 'fa-microchip',
'fa-hdd-o', 'fa-globe', 'fa-info-circle', 'fa-question-circle-o', 'fa-user-o',
'fa-lock-o', 'fa-sign-in', 'fa-sign-out', 'fa-search', 'fa-plus', 'fa-refresh',
'fa-times', 'fa-check', 'fa-pencil', 'fa-trash', 'fa-play', 'fa-arrow-left',
'fa-arrow-down', 'fa-arrow-up',
]
/** 会话超时5 分钟 */
export const SESSION_TIMEOUT = 5 * 60 * 1000
/** 系统监控刷新间隔 */
export const METRICS_INTERVAL = 30000
/** 训练进度刷新间隔 */
export const TRAINING_REFRESH_INTERVAL = 5000

View File

@@ -0,0 +1,116 @@
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import AppSidebar from '@/components/AppSidebar.vue'
import AppHeader from '@/components/AppHeader.vue'
import { useSystemStore } from '@/stores/system'
import { useModelsStore } from '@/stores/models'
const systemStore = useSystemStore()
const modelsStore = useModelsStore()
const route = useRoute()
onMounted(() => {
// 启动顶部栏系统监控轮询
systemStore.start()
// 预加载模型列表缓存(列表页渲染模型名用)
modelsStore.load()
})
onUnmounted(() => {
systemStore.stop()
})
</script>
<template>
<div class="main-layout">
<AppSidebar />
<div class="layout-main">
<AppHeader />
<main class="layout-content">
<div
class="page-canvas"
:class="{ 'is-self-surface': route.meta.pageSurface === 'self' }"
>
<router-view v-slot="{ Component }">
<component :is="Component" />
</router-view>
</div>
</main>
</div>
</div>
</template>
<style scoped lang="scss">
.main-layout {
display: flex;
height: 100vh;
overflow: hidden;
}
.layout-main {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
height: 100vh;
}
.layout-content {
flex: 1;
overflow-y: auto;
padding: 8px 8px 16px;
background-color: var(--app-shell-bg);
display: flex;
flex-direction: column;
min-height: 0;
&:has(.data-table-page),
&:has(.eval-page),
&:has(.manage-card-container) {
overflow-y: hidden;
}
&:has(.create-wizard-layout) {
overflow-y: hidden;
.page-canvas {
flex: 1 1 auto;
min-height: 0;
}
}
}
.page-canvas {
flex: 1 0 auto;
display: flex;
flex-direction: column;
width: 100%;
min-width: 0;
padding: 24px;
border-radius: 16px;
background-color: var(--app-page-bg);
box-shadow: var(--card-shadow);
}
.page-canvas.is-self-surface {
padding: 0;
border-radius: 0;
background-color: transparent;
box-shadow: none;
}
.page-canvas:has(.has-fixed-footer) {
margin-bottom: 56px;
}
/* 路由直接输出 PageCard或显式声明根卡片宿主时复用外层白色画布 */
.page-canvas:not(.is-self-surface) > :deep(.page-card),
.page-canvas:not(.is-self-surface) > :deep(.page-card-host > .page-card) {
margin-bottom: 0;
background-color: transparent;
border-radius: 0 !important;
box-shadow: none !important;
}
</style>

25
frontend/src/main.ts Normal file
View File

@@ -0,0 +1,25 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import 'element-plus/dist/index.css'
// Font Awesome 图标
import '@/assets/font-awesome/css/font-awesome.min.css'
// ECharts按需引入+ vue-echarts 组件
import '@/plugins/echarts'
import VChart from 'vue-echarts'
import App from './App.vue'
import router from './router'
import './styles/index.scss'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.use(ElementPlus, { locale: zhCn })
app.component('VChart', VChart)
app.mount('#app')

View File

@@ -0,0 +1,24 @@
/**
* ECharts 按需引入
* 仅注册训练曲线所需的模块,避免引入全量包以减小体积
*/
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { LineChart } from 'echarts/charts'
import {
GridComponent,
TooltipComponent,
LegendComponent,
DataZoomComponent,
MarkLineComponent,
} from 'echarts/components'
use([
CanvasRenderer,
LineChart,
GridComponent,
TooltipComponent,
LegendComponent,
DataZoomComponent,
MarkLineComponent,
])

View File

@@ -0,0 +1,239 @@
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const routes: RouteRecordRaw[] = [
{
path: '/login',
name: 'login',
component: () => import('@/views/login/LoginView.vue'),
meta: { title: '登录', public: true },
},
{
path: '/',
component: () => import('@/layouts/MainLayout.vue'),
redirect: '/fine-tune',
children: [
// 服务看板
{
path: 'dashboard',
name: 'dashboard',
component: () => import('@/components/PlaceholderView.vue'),
meta: { title: '服务看板' },
},
// 模型调优
{
path: 'fine-tune',
name: 'fine-tune',
component: () => import('@/views/fine-tune/FineTuneListView.vue'),
meta: { title: '模型微调', pageSurface: 'self' },
},
{
path: 'fine-tune/create',
name: 'fine-tune-create',
component: () => import('@/views/fine-tune/FineTuneCreateView.vue'),
meta: { title: '创建训练任务' },
},
// 模型评测
{
path: 'model-eval',
name: 'model-eval',
component: () => import('@/views/eval/EvalView.vue'),
meta: { title: '模型评测', pageSurface: 'self' },
},
{
path: 'model-eval/create',
name: 'model-eval-create',
component: () => import('@/views/eval/EvalCreateView.vue'),
meta: { title: '新建评测' },
},
{
path: 'model-eval/dimension/create',
name: 'dimension-create',
component: () => import('@/views/eval/DimensionCreateView.vue'),
meta: { title: '添加评测维度' },
},
{
path: 'model-eval/dimension/:id/edit',
name: 'dimension-edit',
component: () => import('@/views/eval/DimensionCreateView.vue'),
meta: { title: '编辑评测维度' },
},
// 模型推理
{
path: 'model-inference',
name: 'model-inference',
component: () => import('@/views/inference/InferenceListView.vue'),
meta: { title: '模型推理', pageSurface: 'self' },
},
{
path: 'model-inference/create',
name: 'model-inference-create',
component: () => import('@/views/inference/InferenceCreateView.vue'),
meta: { title: '新建推理' },
},
{
path: 'model-inference/chat/:id',
name: 'inference-chat',
component: () => import('@/views/inference/InferenceChatView.vue'),
meta: { title: '模型对话', pageSurface: 'self' },
},
// 模型对比
{
path: 'model-compare/chat/:id',
name: 'compare-chat',
component: () => import('@/views/compare/CompareChatView.vue'),
meta: { title: '模型对比' },
},
{
path: 'model-compare/result',
name: 'compare-result',
component: () => import('@/views/compare/CompareResultView.vue'),
meta: { title: '对比结果' },
},
// 模型管理
{
path: 'model-manage',
name: 'model-manage',
component: () => import('@/views/model/ModelManageView.vue'),
meta: { title: '模型管理', pageSurface: 'self' },
},
{
path: 'model-manage/create',
name: 'model-manage-create',
component: () => import('@/views/model/ModelCreateView.vue'),
meta: { title: '添加模型' },
},
{
path: 'model-manage/:id/edit',
name: 'model-manage-edit',
component: () => import('@/views/model/ModelCreateView.vue'),
meta: { title: '编辑模型' },
},
{
path: 'model-manage/merge',
name: 'merge-weights',
component: () => import('@/views/model/MergeWeightsView.vue'),
meta: { title: '合并权重' },
},
// 数据处理
{
path: 'data-process',
name: 'data-process',
component: () => import('@/views/data-process/DataProcessListView.vue'),
meta: { title: '数据处理', pageSurface: 'self' },
},
{
path: 'data-process/create',
name: 'data-process-create',
component: () => import('@/views/data-process/DataProcessCreateView.vue'),
meta: { title: '新建数据处理任务', pageSurface: 'self' },
},
// 数据集管理
{
path: 'dataset',
name: 'dataset',
component: () => import('@/views/dataset/DatasetListView.vue'),
meta: { title: '数据集管理', pageSurface: 'self' },
},
{
path: 'dataset/create',
name: 'dataset-create',
component: () => import('@/views/dataset/DatasetCreateView.vue'),
meta: { title: '上传数据集' },
},
{
path: 'dataset/:id/edit',
name: 'dataset-edit',
component: () => import('@/views/dataset/DatasetCreateView.vue'),
meta: { title: '编辑数据集' },
},
{
path: 'dataset/:id/preview',
name: 'dataset-preview',
component: () => import('@/views/dataset/DatasetPreviewView.vue'),
meta: { title: '数据集预览' },
},
// 其他工具
{
path: 'tools',
name: 'tools',
component: () => import('@/views/tools/ToolsView.vue'),
meta: { title: '其他工具' },
},
{
path: 'tools/create',
name: 'tools-create',
component: () => import('@/views/tools/ToolCreateView.vue'),
meta: { title: '添加自定义工具' },
},
{
path: 'tools/:id/edit',
name: 'tools-edit',
component: () => import('@/views/tools/ToolCreateView.vue'),
meta: { title: '编辑自定义工具' },
},
// 数据类型转换
{
path: 'data-convert',
name: 'data-convert',
component: () => import('@/components/PlaceholderView.vue'),
meta: { title: '数据类型转换' },
},
// 系统设置
{
path: 'hardware',
name: 'hardware',
component: () => import('@/views/system/HardwareView.vue'),
meta: { title: '平台性能' },
},
{
path: 'logs',
name: 'logs',
component: () => import('@/views/system/LogsView.vue'),
meta: { title: '查看日志' },
},
{
path: 'training-log/:id',
name: 'training-log',
component: () => import('@/views/system/TrainingLogView.vue'),
meta: { title: '训练日志' },
},
],
},
{
path: '/:pathMatch(.*)*',
redirect: '/fine-tune',
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
// 全局守卫:登录校验 + 会话超时
router.beforeEach((to, _from, next) => {
const auth = useAuthStore()
document.title = to.meta.title ? `${to.meta.title} - 远光软件微调平台` : '远光软件微调平台'
if (to.meta.public) {
// 已登录访问登录页则跳转主页
if (to.name === 'login' && auth.isLoggedIn) {
next('/fine-tune')
return
}
next()
return
}
if (!auth.isLoggedIn) {
next({ name: 'login' })
return
}
// 续期会话
auth.refresh()
next()
})
export default router

View File

@@ -0,0 +1,190 @@
/* Element Plus 主题变量覆盖:使用原生 CSS 变量替代 SCSS 编译以解决加载卡顿和 FOUC 问题 */
:root {
/* 全局页面层级:灰色仅作为外层留白,业务页面统一使用白色画布 */
--app-shell-bg: #f3f5f8;
--app-page-bg: #ffffff;
--app-surface-bg: #ffffff;
--el-bg-color-page: var(--app-shell-bg);
/* Element Plus 基础颜色覆盖 */
--el-color-primary: #4f46e5;
--el-color-primary-light-3: #818cf8;
--el-color-primary-light-5: #a5b4fc;
--el-color-primary-light-7: #c7d2fe;
--el-color-primary-light-8: #e0e7ff;
--el-color-primary-light-9: #eef2ff;
--el-color-primary-dark-2: #4338ca;
--el-color-success: #10b981;
--el-color-warning: #f59e0b;
--el-color-danger: #ef4444;
--el-color-info: #64748b;
/* Element Plus 圆角覆盖 */
--el-border-radius-base: 8px;
--el-border-radius-small: 6px;
--el-border-radius-round: 20px;
/* 项目自定义 CSS 变量 */
--primary-color: var(--el-color-primary);
--primary-hover: var(--el-color-primary-light-3);
--danger-color: var(--el-color-danger);
--success-color: var(--el-color-success);
--sidebar-bg: #f9fafb; /* Light Gray */
--sidebar-text: #606266; /* Regular Text */
--sidebar-active-bg: #f1f5f9; /* Active Item Bg */
--sidebar-active-text: #303133; /* Darker Text for Active */
--card-shadow: 0 4px 24px rgba(0, 0, 0, 0.04);
--header-height: 60px;
}
html,
body,
#app {
height: 100%;
margin: 0;
padding: 0;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, 'PingFang SC', sans-serif;
background-color: var(--app-shell-bg);
color: var(--el-text-color-primary);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
box-sizing: border-box;
}
/* 滚动条美化 */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-thumb {
background-color: rgba(148, 163, 184, 0.4);
border-radius: 4px;
transition: all 0.3s;
}
::-webkit-scrollbar-thumb:hover {
background-color: rgba(148, 163, 184, 0.7);
}
::-webkit-scrollbar-track {
background: transparent;
}
/* 覆盖 Element Plus 的一些默认样式,使其更高级 */
/* 1. 卡片 (el-card) 无边框,柔和阴影 */
.el-card {
background-color: var(--app-surface-bg);
border: none !important;
box-shadow: var(--card-shadow) !important;
border-radius: 12px !important;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
/* 2. 表格 (el-table) 现代化 */
.el-table {
--el-table-border-color: #f1f5f9;
--el-table-header-bg-color: #f8fafc;
--el-table-header-text-color: #475569;
border-radius: 8px;
overflow: hidden;
}
.el-table th.el-table__cell {
font-weight: 600 !important;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.5px;
border-bottom: 1px solid var(--el-table-border-color) !important;
}
.el-table td.el-table__cell {
padding: 12px 0 !important;
}
.el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell {
background-color: #f8fafc !important;
}
.el-table::before {
display: none; /* 移除底部基础边框 */
}
/* 3. 按钮动效 */
.el-button {
font-weight: 500;
transition: all 0.2s ease;
}
.el-button:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.el-button:active {
transform: translateY(0);
}
.el-button.is-link:hover {
transform: none;
box-shadow: none;
}
/* 4. 标签 (el-tag) 圆润化 */
.el-tag {
border-radius: 6px;
border: none !important;
font-weight: 500;
}
.el-tag--primary {
background-color: rgba(79, 70, 229, 0.1);
color: #4f46e5;
}
.el-tag--success {
background-color: rgba(16, 185, 129, 0.1);
color: #10b981;
}
.el-tag--warning {
background-color: rgba(245, 158, 11, 0.1);
color: #d97706;
}
.el-tag--danger {
background-color: rgba(239, 68, 68, 0.1);
color: #ef4444;
}
.el-tag--info {
background-color: rgba(100, 116, 139, 0.1);
color: #475569;
}
/* 5. 分页组件 */
.el-pagination {
--el-pagination-bg-color: #ffffff;
--el-pagination-hover-color: var(--primary-color);
margin-top: 16px;
justify-content: flex-end;
}
.el-pagination.is-background .el-pager li {
border-radius: 6px;
background-color: #f1f5f9;
border: none;
}
.el-pagination.is-background .el-pager li.is-active {
background-color: var(--primary-color);
color: #fff;
box-shadow: 0 2px 8px rgba(79, 70, 229, 0.3);
}
/* 6. 模态框 (Dialog) */
.el-dialog {
border-radius: 16px !important;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important;
}
.el-dialog__header {
margin-right: 0 !important;
padding-bottom: 16px !important;
border-bottom: 1px solid #f1f5f9;
}
.el-dialog__title {
font-weight: 600 !important;
font-size: 16px !important;
}

260
frontend/src/types/index.ts Normal file
View File

@@ -0,0 +1,260 @@
/**
* 全局业务类型定义
*/
/** 通用分页/列表响应暂用数组,后端未分页 */
// ============ 模型管理 ============
export type ModelType = 'LLM' | 'CV' | 'NLP' | 'Embedding' | 'Other'
export type ModelPurpose = 'training' | 'inference' | 'evaluation'
export type ModelSource = 'local' | 'api' | 'online'
export interface TrainMethod {
name: string
}
/** 数据库配置模型 */
export interface ModelItem {
id: number | string
name: string
type: ModelType | string
purpose: ModelPurpose | string
model_source: ModelSource | string
description?: string
path?: string
api_url?: string
api_key?: string
online_model_name?: string
create_time?: string
}
/** 已训练模型 */
export interface TrainedModel {
id: number | string
name: string
train_methods?: TrainMethod[]
base_model_path?: string
create_time?: string
merged?: boolean
merging?: boolean
merged_path?: string
}
/** 创建/编辑模型请求体 */
export interface ModelForm {
id?: number | string
name: string
type: ModelType | string
purpose: ModelPurpose | string
model_source: ModelSource | string
description?: string
path?: string
api_url?: string
api_key?: string
online_model_name?: string
}
// ============ 数据集 ============
export type DatasetType = 'train' | 'test' | 'eval' | 'val' | 'other'
export type DatasetStorage = 'local' | 'cloud' | 'minio'
export interface DatasetFile {
id?: string
name: string
size?: string | number
[k: string]: any
}
export interface DatasetItem {
id: number | string
name: string
type: DatasetType | string
storage_type: DatasetStorage | string
size?: string | number
count?: number
description?: string
create_time?: string
files?: DatasetFile[]
}
// ============ 训练任务 ============
export type TrainType = 'SFT' | 'DPO' | 'CPT'
export type TrainMethodType = 'lora' | 'full'
export type TaskStatus = 'pending' | 'running' | 'completed' | 'failed' | string
export interface FineTuneTask {
id: number | string
name: string
description?: string
status: TaskStatus
train_type: TrainType | string
train_method?: TrainMethodType | string
template?: string
base_model: number | string
train_dataset_id?: number | string
output_model_name?: string
gpus?: number[]
batch_size?: number
learning_rate?: number
n_epochs?: number
save_steps?: number
lr_scheduler_type?: string
max_length?: number
warmup_ratio?: number
weight_decay?: number
lora_alpha?: number
lora_dropout?: number
lora_rank?: number
progress?: number
process_id?: number
train_duration?: string
create_time?: string
}
export interface TrainingProgress {
status?: string
progress?: number
step?: string
speed?: string
eta?: string
}
// ============ 模型推理 / 对比 (model-compare) ============
export interface LoadedModel {
model_id?: number | string
model_name?: string
status?: string
pid?: number
port?: number
}
export interface CompareTask {
id: number | string
model_name?: string
name?: string
description?: string
status?: string
models?: CompareModelRef[] | string
load_status?: string | { loaded_models: LoadedModel[] }
create_time?: string
}
export interface CompareModelRef {
model_id: number | string
model_name: string
model_path: string
gpu_id: number
source?: string
port?: number
}
// ============ 模型评测 ============
export type EvalType = 'custom' | 'baseline'
export type DimensionType = 'classification' | 'metric' | 'text_similarity'
export interface EvalTask {
id: number | string
eval_task_name?: string
model_name?: string
model_id?: number | string
dataset?: string
metric?: string
score?: number
status?: string
create_time?: string
}
export interface Dimension {
id: number | string
name: string
type: DimensionType | string
description?: string
eval_model?: string
eval_method?: string | string[]
eval_prompt?: string
is_active?: boolean
is_default?: boolean
bleu_n?: number
output_precision?: number
score_min?: number
score_max?: number
pass_threshold?: number
create_time?: string
}
// ============ 系统监控 ============
export interface GpuInfo {
name: string
gpu_percent: number
memory_used_gb: number
memory_total_gb: number
temperature: number
power_w: number
fan_speed?: number
clock_mhz?: number
driver_version?: string
}
export interface SystemInfo {
cpu?: {
percent: number
cores: number
percents?: number[]
}
memory?: {
used_gb: number
total_gb: number
percent: number
available_gb: number
cached_gb: number
}
disk?: {
used_gb: number
total_gb: number
percent: number
}
gpu?: GpuInfo[]
network?: {
download_mb?: number
upload_mb?: number
}
system?: {
uptime_seconds?: number
process_count?: number
os?: string
}
}
export interface HealthMetrics {
cpu_percent?: number
memory_percent?: number
disk_percent?: number
}
// ============ 日志 ============
export interface LogFile {
file: string
name: string
size: string
pid?: number
date?: string
}
export interface LogContent {
content: string
file: string
size: string
}
export interface TrainingLogFile extends LogFile {
pid?: number
}
// ============ 其他工具 ============
export interface CustomTool {
id: string
name: string
description: string
url: string
icon: string
}