diff --git a/frontend/package.json b/frontend/package.json index ba8fa5a..c0ef179 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "test:data-process-list": "node scripts/regression-data-process-list.mjs", "test:data-process-wizard": "node scripts/regression-data-process-wizard.mjs", "test:dataset-task-tab": "node scripts/regression-dataset-task-tab.mjs", + "test:dataset-preview": "node scripts/regression-dataset-preview.mjs", "test:model-manage": "node scripts/regression-model-manage.mjs", "test:training-log-layout": "node scripts/regression-training-log-layout.mjs", "test:page-surface": "node scripts/regression-page-surface.mjs" diff --git a/frontend/scripts/regression-dataset-preview.mjs b/frontend/scripts/regression-dataset-preview.mjs new file mode 100644 index 0000000..dec2bd8 --- /dev/null +++ b/frontend/scripts/regression-dataset-preview.mjs @@ -0,0 +1,29 @@ +import fs from 'node:fs' +import path from 'node:path' + +const root = process.cwd() +const previewPath = path.join(root, 'src/views/dataset/DatasetPreviewView.vue') +const mockPath = path.join(root, 'src/mock/data.ts') +const source = fs.readFileSync(previewPath, 'utf8') +const mockSource = fs.readFileSync(mockPath, 'utf8') + +function expect(condition, message) { + if (!condition) throw new Error(message) +} + +expect(source.includes('class="preview-workspace"'), '预览页应使用文件与内容双栏工作区') +expect(source.includes('class="code-line"'), '内容查看器应提供逐行展示') +expect(source.includes('class="line-number"'), '内容查看器应显示行号') +expect(source.includes('previewLoading'), '切换文件时应提供独立加载状态') +expect(source.includes('previewError'), '内容加载失败时应提供错误状态') +expect(source.includes('aria-current'), '文件选中态应向辅助技术暴露') +expect(!source.includes('handleDownloadAll'), '页面不应保留整包下载逻辑') +expect(!source.includes('handleDelete'), '页面不应保留删除逻辑') +expect(!source.includes('router.back()'), '页面不应保留页头返回逻辑') +expect(mockSource.includes('files: ['), 'Mock 数据集应包含可验收的文件列表') +expect(mockSource.includes('mockDatasetPreviews'), 'Mock 数据应包含文件预览内容') +expect(mockSource.includes("'mock-jsonl'"), 'Mock 数据应为通用数据集提供模拟样本') +expect(mockSource.includes("'mock-readme'"), 'Mock 数据应为通用数据集提供模拟说明') +expect(mockSource.includes('.map((dataset)'), '所有 Mock 数据集都应自动补齐预览文件') + +console.log('dataset preview regression checks passed') diff --git a/frontend/src/mock/adapter.ts b/frontend/src/mock/adapter.ts index 1f185f7..7460a72 100644 --- a/frontend/src/mock/adapter.ts +++ b/frontend/src/mock/adapter.ts @@ -12,6 +12,7 @@ import { mockTrainedModels, mockLocalModels, mockDatasets, + mockDatasetPreviews, mockFineTuneList, mockCompareList, mockEvalList, @@ -129,7 +130,12 @@ async function handleMock(config: AxiosRequestConfig) { m = url.match(/^\/dataset-manage\/upload\/([^/]+)$/) if (m && method === 'post') return ok({ uploaded: true }) m = url.match(/^\/dataset-manage\/preview\/([^/]+)$/) - if (m && method === 'get') return ok(mockLogContent) + if (m && method === 'get') { + const fileId = decodeURIComponent(m[1]) + const fallbackKey = fileId.endsWith('-readme') ? 'mock-readme' : 'mock-jsonl' + const content = mockDatasetPreviews[fileId] ?? mockDatasetPreviews[fallbackKey] + return ok({ content }) + } // ==================== 训练任务 ==================== if (url === '/fine-tune' && method === 'get') return ok(mockFineTuneList) @@ -265,4 +271,4 @@ export function installMockAdapter(instance: AxiosInstance) { return fail(e.message || 'Mock 错误', 500, config) } } -} \ No newline at end of file +} diff --git a/frontend/src/mock/data.ts b/frontend/src/mock/data.ts index 0e2dc05..8a3a326 100644 --- a/frontend/src/mock/data.ts +++ b/frontend/src/mock/data.ts @@ -99,7 +99,22 @@ export const mockLocalModels = { // ============ 数据集 ============ export const mockDatasets: DatasetItem[] = [ - { id: 1, name: '金融问答-训练集', type: 'train', storage_type: 'local', source: 'upload', size: '128 MB', count: 8560, description: '金融领域问答对', create_time: '2025-12-20T08:00:00Z' }, + { + id: 1, + name: '金融问答-训练集', + type: 'train', + storage_type: 'local', + source: 'upload', + size: '128 MB', + count: 8560, + description: '面向金融领域问答场景的高质量指令微调数据集', + create_time: '2025-12-20T08:00:00Z', + files: [ + { id: 'finance-train-jsonl', name: 'finance_train.jsonl', size: '86.4 MB' }, + { id: 'finance-validation-jsonl', name: 'finance_validation.jsonl', size: '21.8 MB' }, + { id: 'dataset-readme', name: 'README.md', size: '4.2 KB' }, + ], + }, { id: 2, name: '法律文书-训练集', type: 'train', storage_type: 'local', source: 'upload', size: '256 MB', count: 15230, description: '法律文书数据集', create_time: '2025-12-25T10:30:00Z' }, { id: 3, name: '客服对话-训练集', type: 'train', storage_type: 'minio', source: 'upload', size: '512 MB', count: 24500, description: '客服对话记录', create_time: '2026-01-05T14:20:00Z' }, { id: 4, name: '金融评测集', type: 'eval', storage_type: 'local', source: 'upload', size: '32 MB', count: 1200, description: '金融领域评测', create_time: '2026-01-10T09:15:00Z' }, @@ -109,16 +124,81 @@ export const mockDatasets: DatasetItem[] = [ { id: 8, name: '通用指令构造集', type: 'train', storage_type: 'local', source: 'task', size: '148 MB', count: 12600, description: '由指令微调数据构造任务生成', create_time: '2026-07-09T01:42:00Z' }, { id: 9, name: '用户反馈脱敏集', type: 'test', storage_type: 'minio', source: 'task', size: '72 MB', count: 9340, description: '由敏感信息脱敏任务生成', create_time: '2026-07-09T09:18:00Z' }, { id: 10, name: '多轮对话增强集', type: 'eval', storage_type: 'local', source: 'task', size: '41 MB', count: 2780, description: '由多轮对话拼接任务生成', create_time: '2026-07-10T02:06:00Z' }, -] +].map((dataset) => ({ + ...dataset, + files: dataset.files?.length + ? dataset.files + : [ + { id: `dataset-${dataset.id}-samples`, name: 'dataset_samples.jsonl', size: dataset.size || '12.8 MB' }, + { id: `dataset-${dataset.id}-readme`, name: 'README.md', size: '3.8 KB' }, + ], +})) + +export const mockDatasetPreviews: Record = { + 'finance-train-jsonl': [ + '{"instruction":"什么是净资产收益率?","input":"","output":"净资产收益率(ROE)用于衡量企业运用自有资本获得收益的能力。"}', + '{"instruction":"解释市盈率的含义","input":"某公司股价为 36 元,每股收益为 3 元。","output":"市盈率为股价除以每股收益,本例中市盈率为 12 倍。"}', + '{"instruction":"央行降准通常会带来什么影响?","input":"","output":"降准通常会释放银行体系流动性,降低资金成本,并增强信贷投放能力。"}', + '{"instruction":"如何理解债券久期?","input":"","output":"久期衡量债券价格对利率变化的敏感程度,久期越长,价格波动通常越大。"}', + '{"instruction":"基金定投适合怎样的投资者?","input":"","output":"基金定投适合希望分散择时风险、进行中长期纪律性投资的投资者。"}', + '{"instruction":"资产负债率过高意味着什么?","input":"","output":"可能意味着企业偿债压力较大、财务风险较高,但仍需结合行业特点判断。"}', + '{"instruction":"通货膨胀如何影响实际收益率?","input":"","output":"实际收益率约等于名义收益率减去通货膨胀率。"}', + '{"instruction":"什么是流动比率?","input":"","output":"流动比率等于流动资产除以流动负债,用于衡量短期偿债能力。"}', + '{"instruction":"股票分红是否等于投资者获得额外收益?","input":"","output":"不完全等同,除息后股价通常会相应调整,应结合总回报判断。"}', + '{"instruction":"如何区分系统性风险与非系统性风险?","input":"","output":"系统性风险影响整个市场,非系统性风险主要与单个公司或行业有关。"}', + '{"instruction":"什么是复利?","input":"","output":"复利是将本金和此前产生的收益一并计入下一期收益计算。"}', + '{"instruction":"现金流量表主要反映什么?","input":"","output":"现金流量表反映企业经营、投资和筹资活动产生的现金流入与流出。"}', + ].join('\n'), + 'finance-validation-jsonl': [ + '{"instruction":"计算简单收益率","input":"买入价 100 元,卖出价 108 元,不考虑费用。","output":"简单收益率为 8%。"}', + '{"instruction":"什么是信用利差?","input":"","output":"信用利差是信用债收益率相对无风险基准收益率的差额。"}', + '{"instruction":"解释最大回撤","input":"","output":"最大回撤描述资产净值从历史高点到随后低点的最大跌幅。"}', + '{"instruction":"什么是风险溢价?","input":"","output":"风险溢价是投资者因承担额外风险而要求的超额预期收益。"}', + ].join('\n'), + 'dataset-readme': [ + '# 金融问答训练集', + '', + '本数据集用于金融领域指令微调与基础能力评测。', + '', + '## 文件说明', + '', + '- `finance_train.jsonl`:训练样本', + '- `finance_validation.jsonl`:验证样本', + '', + '文本编码:UTF-8', + ].join('\n'), + 'mock-jsonl': [ + '{"instruction":"请概括以下文本的核心观点","input":"人工智能正在提升企业的数据处理效率。","output":"人工智能能够帮助企业提升数据处理效率。"}', + '{"instruction":"将用户问题改写为更清晰的表达","input":"这个功能咋用?","output":"请说明该功能的具体使用步骤。"}', + '{"instruction":"判断文本情感倾向","input":"这次服务响应很及时,问题也解决了。","output":"正向"}', + '{"instruction":"提取文本中的关键实体","input":"远光软件于周一发布了新的模型管理平台。","output":["远光软件","周一","模型管理平台"]}', + '{"instruction":"生成简短回复","input":"您好,我想了解数据集上传支持哪些格式?","output":"您好,目前支持 JSON、JSONL、CSV、TXT 等常见格式。"}', + '{"instruction":"对以下内容进行分类","input":"如何重置账户密码?","output":"账户与安全"}', + '{"instruction":"找出句子中的时间信息","input":"系统将在 7 月 15 日凌晨 2 点进行升级。","output":"7 月 15 日凌晨 2 点"}', + '{"instruction":"补全客服回复","input":"用户反馈页面加载缓慢。","output":"已收到您的反馈,我们正在排查页面加载问题,请稍后重试。"}', + ].join('\n'), + 'mock-readme': [ + '# 数据集说明', + '', + '当前内容为前端 Mock 数据,用于预览页面布局与交互效果。', + '', + '## 文件结构', + '', + '- `dataset_samples.jsonl`:模拟的数据样本', + '- `README.md`:数据集使用说明', + '', + '文本编码:UTF-8', + ].join('\n'), +} // ============ 训练任务 ============ export const mockFineTuneList: FineTuneTask[] = [ - { id: 1, name: 'finance-sft-001', description: '金融领域 SFT 训练', status: 'completed', train_type: 'SFT', train_method: 'lora', template: 'qwen', base_model: 1, train_dataset_id: 1, gpus: [0], progress: 100, train_duration: '2小时18分钟', create_time: '2026-01-15T08:00:00Z' }, - { id: 2, name: 'legal-sft-002', description: '法律文书 SFT', status: 'completed', train_type: 'SFT', train_method: 'lora', template: 'qwen', base_model: 1, train_dataset_id: 2, gpus: [1], progress: 100, train_duration: '1小时46分钟', create_time: '2026-01-18T10:00:00Z' }, - { id: 3, name: 'medical-cpt-001', description: '医疗领域继续预训练', status: 'running', train_type: 'CPT', train_method: 'lora', template: 'qwen2_5', base_model: 2, train_dataset_id: 6, gpus: [0, 1], progress: 64, train_duration: '36分钟', create_time: '2026-02-05T09:00:00Z' }, - { id: 4, name: 'service-dpo-001', description: '客服对话偏好训练', status: 'pending', train_type: 'DPO', train_method: 'lora', template: 'qwen', base_model: 1, train_dataset_id: 3, gpus: [2], progress: 0, train_duration: '-', create_time: '2026-02-08T14:00:00Z' }, - { id: 5, name: 'finance-sft-002', description: '金融领域二轮微调', status: 'failed', train_type: 'SFT', train_method: 'lora', template: 'qwen', base_model: 1, train_dataset_id: 1, gpus: [3], progress: 32, train_duration: '18分钟', create_time: '2026-02-10T11:00:00Z' }, - { id: 6, name: 'general-sft-001', description: '通用能力微调', status: 'completed', train_type: 'SFT', train_method: 'full', template: 'llama3', base_model: 3, train_dataset_id: 3, gpus: [0, 2], progress: 100, train_duration: '3小时05分钟', create_time: '2026-02-12T13:00:00Z' }, + { id: 103942, name: 'finance-sft-001', description: '金融领域 SFT 训练', status: 'completed', train_type: 'SFT', train_method: 'lora', template: 'qwen', base_model: 1, train_dataset_id: 1, gpus: [0], progress: 100, train_duration: '2小时18分钟', create_time: '2026-01-15T08:00:00Z' }, + { id: 349102, name: 'legal-sft-002', description: '法律文书 SFT', status: 'completed', train_type: 'SFT', train_method: 'lora', template: 'qwen', base_model: 1, train_dataset_id: 2, gpus: [1], progress: 100, train_duration: '1小时46分钟', create_time: '2026-01-18T10:00:00Z' }, + { id: 849301, name: 'medical-cpt-001', description: '医疗领域继续预训练', status: 'running', train_type: 'CPT', train_method: 'lora', template: 'qwen2_5', base_model: 2, train_dataset_id: 6, gpus: [0, 1], progress: 64, train_duration: '36分钟', create_time: '2026-02-05T09:00:00Z' }, + { id: 593021, name: 'service-dpo-001', description: '客服对话偏好训练', status: 'pending', train_type: 'DPO', train_method: 'lora', template: 'qwen', base_model: 1, train_dataset_id: 3, gpus: [2], progress: 0, train_duration: '-', create_time: '2026-02-08T14:00:00Z' }, + { id: 201948, name: 'finance-sft-002', description: '金融领域二轮微调', status: 'failed', train_type: 'SFT', train_method: 'lora', template: 'qwen', base_model: 1, train_dataset_id: 1, gpus: [3], progress: 32, train_duration: '18分钟', create_time: '2026-02-10T11:00:00Z' }, + { id: 940212, name: 'general-sft-001', description: '通用能力微调', status: 'completed', train_type: 'SFT', train_method: 'full', template: 'llama3', base_model: 3, train_dataset_id: 3, gpus: [0, 2], progress: 100, train_duration: '3小时05分钟', create_time: '2026-02-12T13:00:00Z' }, ] // ============ 模型推理/对比 ============ diff --git a/frontend/src/views/dataset/DatasetPreviewView.vue b/frontend/src/views/dataset/DatasetPreviewView.vue index a96f6b2..c072d23 100644 --- a/frontend/src/views/dataset/DatasetPreviewView.vue +++ b/frontend/src/views/dataset/DatasetPreviewView.vue @@ -1,157 +1,697 @@