diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/frontend/public/favicon.ico
@@ -0,0 +1 @@
+
diff --git a/frontend/public/logo.png b/frontend/public/logo.png
new file mode 100644
index 0000000..1de89ad
Binary files /dev/null and b/frontend/public/logo.png differ
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
new file mode 100644
index 0000000..856a59f
--- /dev/null
+++ b/frontend/src/App.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/frontend/src/constants/dimension.ts b/frontend/src/constants/dimension.ts
new file mode 100644
index 0000000..7258d24
--- /dev/null
+++ b/frontend/src/constants/dimension.ts
@@ -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 = {
+ 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...`,
+}
diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts
new file mode 100644
index 0000000..48eb5b8
--- /dev/null
+++ b/frontend/src/constants/index.ts
@@ -0,0 +1,144 @@
+/**
+ * 全局常量与映射表
+ */
+
+/** 模型类型 */
+export const MODEL_TYPE_MAP: Record = {
+ LLM: '大语言模型',
+ CV: '计算机视觉',
+ NLP: '自然语言处理',
+ Embedding: '向量模型',
+ Other: '其他',
+}
+
+/** 模型用途 */
+export const PURPOSE_MAP: Record = {
+ training: { text: '训练', type: 'primary' },
+ inference: { text: '推理', type: 'success' },
+ evaluation: { text: '评测', type: 'warning' },
+}
+
+/** 模型来源 */
+export const MODEL_SOURCE_MAP: Record = {
+ local: '本地模型',
+ api: '在线模型',
+ online: '在线模型',
+}
+
+/** 训练方式 */
+export const TRAIN_TYPE_MAP: Record = {
+ SFT: 'SFT',
+ DPO: 'DPO',
+ CPT: 'CPT',
+}
+
+/** 训练方法 */
+export const TRAIN_METHOD_MAP: Record = {
+ 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 = {
+ train: '训练数据',
+ test: '测试数据',
+ eval: '评测数据',
+ val: '验证数据',
+ other: '其他',
+}
+
+/** 存储位置 */
+export const STORAGE_MAP: Record = {
+ local: '本地存储',
+ minio: 'MinIO',
+ cloud: '云存储',
+}
+
+/** 评测维度指标类型 */
+export const DIMENSION_TYPE_MAP: Record = {
+ 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
diff --git a/frontend/src/layouts/MainLayout.vue b/frontend/src/layouts/MainLayout.vue
new file mode 100644
index 0000000..ff87d99
--- /dev/null
+++ b/frontend/src/layouts/MainLayout.vue
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
new file mode 100644
index 0000000..c2516ea
--- /dev/null
+++ b/frontend/src/main.ts
@@ -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')
diff --git a/frontend/src/plugins/echarts.ts b/frontend/src/plugins/echarts.ts
new file mode 100644
index 0000000..7be7f7e
--- /dev/null
+++ b/frontend/src/plugins/echarts.ts
@@ -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,
+])
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
new file mode 100644
index 0000000..4f1d313
--- /dev/null
+++ b/frontend/src/router/index.ts
@@ -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
diff --git a/frontend/src/styles/index.scss b/frontend/src/styles/index.scss
new file mode 100644
index 0000000..7ad1238
--- /dev/null
+++ b/frontend/src/styles/index.scss
@@ -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;
+}
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
new file mode 100644
index 0000000..258ac9b
--- /dev/null
+++ b/frontend/src/types/index.ts
@@ -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
+}