Compare commits
2 Commits
8c1a0d785c
...
762d48d090
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
762d48d090 | ||
|
|
42f2efb449 |
@@ -41,15 +41,20 @@ assert.match(viewSource, /onBeforeRouteLeave\(async \(\) =>/, '路由离开确
|
|||||||
assert.doesNotMatch(viewSource, /window\.confirm|ElMessageBox/, '创建页仍在使用系统或 Element Plus 确认框')
|
assert.doesNotMatch(viewSource, /window\.confirm|ElMessageBox/, '创建页仍在使用系统或 Element Plus 确认框')
|
||||||
|
|
||||||
assert.match(viewSource, /const WIZARD_STEPS = \[/, '向导步骤尚未改为固定常量')
|
assert.match(viewSource, /const WIZARD_STEPS = \[/, '向导步骤尚未改为固定常量')
|
||||||
for (const title of ['创建任务', '上传文件', '数据预览', '开始生成', '结果编辑与保存']) {
|
for (const title of ['创建任务', '大模型选择', '上传文件', '数据预览', '开始生成', '结果编辑与保存']) {
|
||||||
assert.ok(viewSource.includes(`title: '${title}'`), `缺少固定步骤:${title}`)
|
assert.ok(viewSource.includes(`title: '${title}'`), `缺少固定步骤:${title}`)
|
||||||
}
|
}
|
||||||
assert.match(
|
assert.match(
|
||||||
viewSource,
|
viewSource,
|
||||||
/\{ id: 'create',[\s\S]*?\{ id: 'upload',[\s\S]*?\{ id: 'preview',[\s\S]*?\{ id: 'generate',[\s\S]*?\{ id: 'results'/,
|
/\{ id: 'create',[\s\S]*?\{ id: 'model',[\s\S]*?\{ id: 'upload',[\s\S]*?\{ id: 'preview',[\s\S]*?\{ id: 'generate',[\s\S]*?\{ id: 'results'/,
|
||||||
'五步向导顺序必须为创建任务、上传文件、数据预览、开始生成、结果编辑与保存',
|
'六步向导顺序必须为创建任务、大模型选择、上传文件、数据预览、开始生成、结果编辑与保存',
|
||||||
)
|
)
|
||||||
assert.doesNotMatch(viewSource, /steps\s*=\s*computed|all\.filter/, '步骤仍根据处理类型动态增减')
|
assert.doesNotMatch(viewSource, /steps\s*=\s*computed|all\.filter/, '步骤仍根据处理类型动态增减')
|
||||||
|
assert.match(
|
||||||
|
viewStyleSource,
|
||||||
|
/@media \(max-width: 1100px\)[\s\S]*?\.step-title\s*\{[\s\S]*?display:\s*none[\s\S]*?\.step-item\.is-active \.step-title\s*\{[\s\S]*?display:\s*block/,
|
||||||
|
'六步向导在中等宽度下没有收起非当前步骤标题',
|
||||||
|
)
|
||||||
assert.match(draftSource, /localStorage\.setItem\(DATA_PROCESS_DRAFT_STORAGE_KEY/, '草稿没有持久化')
|
assert.match(draftSource, /localStorage\.setItem\(DATA_PROCESS_DRAFT_STORAGE_KEY/, '草稿没有持久化')
|
||||||
assert.match(draftSource, /localStorage\.getItem\(DATA_PROCESS_DRAFT_STORAGE_KEY\)/, '草稿没有恢复读取')
|
assert.match(draftSource, /localStorage\.getItem\(DATA_PROCESS_DRAFT_STORAGE_KEY\)/, '草稿没有恢复读取')
|
||||||
assert.match(viewSource, /restoreDraft\(\)/, '页面没有恢复草稿')
|
assert.match(viewSource, /restoreDraft\(\)/, '页面没有恢复草稿')
|
||||||
@@ -57,6 +62,7 @@ assert.ok(viewSource.split('\n').length < 800, 'DataProcessCreateView 拆分后
|
|||||||
|
|
||||||
const expectedComponents = [
|
const expectedComponents = [
|
||||||
'TaskSetupStep.vue',
|
'TaskSetupStep.vue',
|
||||||
|
'ModelSelectionStep.vue',
|
||||||
'SourceUploadStep.vue',
|
'SourceUploadStep.vue',
|
||||||
'PreviewCompareStep.vue',
|
'PreviewCompareStep.vue',
|
||||||
'GenerationStep.vue',
|
'GenerationStep.vue',
|
||||||
@@ -82,7 +88,7 @@ for (const field of ['sourceStart', 'sourceEnd', 'originalContent', 'editedConte
|
|||||||
assert.ok(typesSource.includes(field), `PreviewItem 缺少字段:${field}`)
|
assert.ok(typesSource.includes(field), `PreviewItem 缺少字段:${field}`)
|
||||||
}
|
}
|
||||||
assert.match(typesSource, /sourceFileId/, 'PreviewItem 缺少来源文件标识')
|
assert.match(typesSource, /sourceFileId/, 'PreviewItem 缺少来源文件标识')
|
||||||
assert.match(typesSource, /export type StepId = 'create' \| 'upload' \| 'preview' \| 'generate' \| 'results'/, '步骤类型缺少独立上传步骤')
|
assert.match(typesSource, /export type StepId = 'create' \| 'model' \| 'upload' \| 'preview' \| 'generate' \| 'results'/, '步骤类型缺少独立大模型选择步骤')
|
||||||
assert.match(modelSource, /export function buildPreviewItems/, '缺少切片来源映射生成函数')
|
assert.match(modelSource, /export function buildPreviewItems/, '缺少切片来源映射生成函数')
|
||||||
assert.match(modelSource, /export function sourceLines/, '缺少源文件行偏移生成函数')
|
assert.match(modelSource, /export function sourceLines/, '缺少源文件行偏移生成函数')
|
||||||
assert.match(modelSource, /sourceFileId/, '切片生成没有写入来源文件标识')
|
assert.match(modelSource, /sourceFileId/, '切片生成没有写入来源文件标识')
|
||||||
@@ -102,10 +108,10 @@ for (const marker of [
|
|||||||
'preview-editor',
|
'preview-editor',
|
||||||
'scrollIntoView',
|
'scrollIntoView',
|
||||||
]) {
|
]) {
|
||||||
assert.ok(previewSource.includes(marker), `第三步缺少结构或行为:${marker}`)
|
assert.ok(previewSource.includes(marker), `第四步缺少结构或行为:${marker}`)
|
||||||
}
|
}
|
||||||
assert.match(previewSource, /sourceStart/, '第三步未使用来源起始偏移')
|
assert.match(previewSource, /sourceStart/, '第四步未使用来源起始偏移')
|
||||||
assert.match(previewSource, /sourceEnd/, '第三步未使用来源结束偏移')
|
assert.match(previewSource, /sourceEnd/, '第四步未使用来源结束偏移')
|
||||||
assert.match(previewSource, /filterable/, '文件选择器必须可搜索')
|
assert.match(previewSource, /filterable/, '文件选择器必须可搜索')
|
||||||
assert.match(previewSource, /当前文件/, '预览缺少当前文件切换器')
|
assert.match(previewSource, /当前文件/, '预览缺少当前文件切换器')
|
||||||
assert.doesNotMatch(previewSource, /located-badge|sync-label|已定位到/, '源文件栏不应显示冗余定位提示')
|
assert.doesNotMatch(previewSource, /located-badge|sync-label|已定位到/, '源文件栏不应显示冗余定位提示')
|
||||||
@@ -130,13 +136,14 @@ assert.match(previewSource, />保存修改<\/el-button>/, '编辑器缺少保存
|
|||||||
assert.match(previewSource, /\.editor-actions\s*\{[\s\S]*?justify-content:\s*flex-end/, '取消和保存按钮必须在编辑器右侧对齐')
|
assert.match(previewSource, /\.editor-actions\s*\{[\s\S]*?justify-content:\s*flex-end/, '取消和保存按钮必须在编辑器右侧对齐')
|
||||||
assert.doesNotMatch(previewSource, /item-token|item-status|modifiedOnly|仅看已修改/, '切片列表不应再显示 Token 或修改状态')
|
assert.doesNotMatch(previewSource, /item-token|item-status|modifiedOnly|仅看已修改/, '切片列表不应再显示 Token 或修改状态')
|
||||||
assert.match(previewSource, /\.preview-editor\s*\{[\s\S]*?flex:\s*1 1 auto[\s\S]*?overflow-y:\s*auto/, '编辑模式必须占据右侧剩余区域并可滚动')
|
assert.match(previewSource, /\.preview-editor\s*\{[\s\S]*?flex:\s*1 1 auto[\s\S]*?overflow-y:\s*auto/, '编辑模式必须占据右侧剩余区域并可滚动')
|
||||||
assert.match(previewSource, /@media \(max-width: 900px\)/, '第三步缺少窄屏上下布局')
|
assert.match(previewSource, /@media \(max-width: 900px\)/, '第四步缺少窄屏上下布局')
|
||||||
|
|
||||||
const taskSetupPath = path.join(createDir, 'TaskSetupStep.vue')
|
const taskSetupPath = path.join(createDir, 'TaskSetupStep.vue')
|
||||||
const structuredOptionsPath = path.join(createDir, 'StructuredOptionsPanel.vue')
|
const structuredOptionsPath = path.join(createDir, 'StructuredOptionsPanel.vue')
|
||||||
const unstructuredOptionsPath = path.join(createDir, 'UnstructuredOptionsPanel.vue')
|
const unstructuredOptionsPath = path.join(createDir, 'UnstructuredOptionsPanel.vue')
|
||||||
const datasetSplitEditorPath = path.join(createDir, 'DatasetSplitEditor.vue')
|
const datasetSplitEditorPath = path.join(createDir, 'DatasetSplitEditor.vue')
|
||||||
const generationOptionsPath = path.join(createDir, 'GenerationOptionsPanel.vue')
|
const generationOptionsPath = path.join(createDir, 'GenerationOptionsPanel.vue')
|
||||||
|
const modelSelectionPath = path.join(createDir, 'ModelSelectionStep.vue')
|
||||||
const sourceUploadPath = path.join(createDir, 'SourceUploadStep.vue')
|
const sourceUploadPath = path.join(createDir, 'SourceUploadStep.vue')
|
||||||
const [
|
const [
|
||||||
taskSetupSource,
|
taskSetupSource,
|
||||||
@@ -144,6 +151,7 @@ const [
|
|||||||
unstructuredOptionsSource,
|
unstructuredOptionsSource,
|
||||||
datasetSplitEditorSource,
|
datasetSplitEditorSource,
|
||||||
generationControlSource,
|
generationControlSource,
|
||||||
|
modelSelectionSource,
|
||||||
sourceUploadSource,
|
sourceUploadSource,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
readFile(taskSetupPath, 'utf8'),
|
readFile(taskSetupPath, 'utf8'),
|
||||||
@@ -151,6 +159,7 @@ const [
|
|||||||
readFile(unstructuredOptionsPath, 'utf8'),
|
readFile(unstructuredOptionsPath, 'utf8'),
|
||||||
readFile(datasetSplitEditorPath, 'utf8'),
|
readFile(datasetSplitEditorPath, 'utf8'),
|
||||||
readFile(generationOptionsPath, 'utf8'),
|
readFile(generationOptionsPath, 'utf8'),
|
||||||
|
readFile(modelSelectionPath, 'utf8'),
|
||||||
readFile(sourceUploadPath, 'utf8'),
|
readFile(sourceUploadPath, 'utf8'),
|
||||||
])
|
])
|
||||||
const taskSetupFeatureSource = [
|
const taskSetupFeatureSource = [
|
||||||
@@ -172,20 +181,29 @@ assert.match(unstructuredOptionsSource, /<DatasetSplitEditor/, '非结构化选
|
|||||||
for (const marker of ['<el-upload', '源数据上传', '数据源配置', 'uploadedFiles']) {
|
for (const marker of ['<el-upload', '源数据上传', '数据源配置', 'uploadedFiles']) {
|
||||||
assert.ok(!taskSetupFeatureSource.includes(marker), `第一步仍包含上传职责:${marker}`)
|
assert.ok(!taskSetupFeatureSource.includes(marker), `第一步仍包含上传职责:${marker}`)
|
||||||
}
|
}
|
||||||
assert.match(viewSource, /<SourceUploadStep\s+[\s\S]*?v-else-if="currentStepId === 'upload'"/, '第二步没有挂载独立上传组件')
|
assert.match(viewSource, /<ModelSelectionStep\s+[\s\S]*?v-else-if="currentStepId === 'model'"/, '第二步没有挂载独立大模型选择组件')
|
||||||
assert.match(viewSource, /if \(currentStepId\.value === 'create'\) return '继续:上传文件'/, '第一步主按钮没有指向上传文件')
|
assert.match(viewSource, /<SourceUploadStep\s+[\s\S]*?v-else-if="currentStepId === 'upload'"/, '第三步没有挂载独立上传组件')
|
||||||
assert.match(viewSource, /if \(currentStepId\.value === 'upload'\) return '继续:数据预览'/, '第二步主按钮没有指向数据预览')
|
assert.match(viewSource, /if \(currentStepId\.value === 'create'\) return '继续:选择大模型'/, '第一步主按钮没有指向大模型选择')
|
||||||
|
assert.match(viewSource, /if \(currentStepId\.value === 'model'\) return '继续:上传文件'/, '第二步主按钮没有指向上传文件')
|
||||||
|
assert.match(viewSource, /if \(currentStepId\.value === 'upload'\) return '继续:数据预览'/, '第三步主按钮没有指向数据预览')
|
||||||
assert.match(draftSource, /DATA_PROCESS_DRAFT_SCHEMA_VERSION = 6/, '安全草稿格式必须升级到 v6')
|
assert.match(draftSource, /DATA_PROCESS_DRAFT_SCHEMA_VERSION = 6/, '安全草稿格式必须升级到 v6')
|
||||||
|
|
||||||
const nextFromCreateStart = viewSource.indexOf('async function nextFromCreate()')
|
const nextFromCreateStart = viewSource.indexOf('async function nextFromCreate()')
|
||||||
const nextFromUploadStart = viewSource.indexOf('function nextFromUpload()', nextFromCreateStart)
|
const nextFromModelStart = viewSource.indexOf('async function nextFromModel()', nextFromCreateStart)
|
||||||
|
const nextFromUploadStart = viewSource.indexOf('function nextFromUpload()', nextFromModelStart)
|
||||||
const selectPreviewFileStart = viewSource.indexOf('function selectPreviewFile(', nextFromUploadStart)
|
const selectPreviewFileStart = viewSource.indexOf('function selectPreviewFile(', nextFromUploadStart)
|
||||||
assert.ok(nextFromCreateStart >= 0 && nextFromUploadStart > nextFromCreateStart, '缺少创建步骤与上传步骤的独立跳转函数')
|
assert.ok(
|
||||||
const nextFromCreateSource = viewSource.slice(nextFromCreateStart, nextFromUploadStart)
|
nextFromCreateStart >= 0 && nextFromModelStart > nextFromCreateStart && nextFromUploadStart > nextFromModelStart,
|
||||||
|
'缺少创建、大模型选择与上传步骤的独立跳转函数',
|
||||||
|
)
|
||||||
|
const nextFromCreateSource = viewSource.slice(nextFromCreateStart, nextFromModelStart)
|
||||||
|
const nextFromModelSource = viewSource.slice(nextFromModelStart, nextFromUploadStart)
|
||||||
const nextFromUploadSource = viewSource.slice(nextFromUploadStart, selectPreviewFileStart)
|
const nextFromUploadSource = viewSource.slice(nextFromUploadStart, selectPreviewFileStart)
|
||||||
assert.match(nextFromCreateSource, /taskSetupRef\.value\?\.validate\(\)/, '创建步骤继续前没有校验任务配置')
|
assert.match(nextFromCreateSource, /taskSetupRef\.value\?\.validate\(\)/, '创建步骤继续前没有校验任务配置')
|
||||||
assert.match(nextFromCreateSource, /goToStep\('upload'\)/, '创建步骤校验通过后没有进入上传文件')
|
assert.match(nextFromCreateSource, /goToStep\('model'\)/, '创建步骤校验通过后没有进入大模型选择')
|
||||||
assert.doesNotMatch(nextFromCreateSource, /uploadedFiles|buildPreviewItems/, '创建步骤仍在校验文件或提前生成预览')
|
assert.doesNotMatch(nextFromCreateSource, /uploadedFiles|buildPreviewItems/, '创建步骤仍在校验文件或提前生成预览')
|
||||||
|
assert.match(nextFromModelSource, /modelSelectionRef\.value\?\.validate\(\)/, '大模型选择步骤继续前没有校验模型配置')
|
||||||
|
assert.match(nextFromModelSource, /goToStep\('upload'\)/, '大模型选择完成后没有进入上传文件')
|
||||||
assert.match(nextFromUploadSource, /uploadedFiles\.value\.length === 0/, '上传步骤继续前没有校验源数据')
|
assert.match(nextFromUploadSource, /uploadedFiles\.value\.length === 0/, '上传步骤继续前没有校验源数据')
|
||||||
assert.match(nextFromUploadSource, /buildPreviewItems\(/, '上传步骤没有在进入预览前生成预览数据')
|
assert.match(nextFromUploadSource, /buildPreviewItems\(/, '上传步骤没有在进入预览前生成预览数据')
|
||||||
assert.match(nextFromUploadSource, /goToStep\('preview'\)/, '上传步骤完成后没有进入数据预览')
|
assert.match(nextFromUploadSource, /goToStep\('preview'\)/, '上传步骤完成后没有进入数据预览')
|
||||||
@@ -217,8 +235,6 @@ for (const option of [
|
|||||||
assert.ok(structuredOptionsSource.includes(option), `结构化预处理缺少选项:${option}`)
|
assert.ok(structuredOptionsSource.includes(option), `结构化预处理缺少选项:${option}`)
|
||||||
}
|
}
|
||||||
assert.ok(structuredOptionsSource.includes('生成选项'), '结构化配置缺少生成选项分类')
|
assert.ok(structuredOptionsSource.includes('生成选项'), '结构化配置缺少生成选项分类')
|
||||||
assert.ok(structuredOptionsSource.includes('语义丰富表达'), '生成选项缺少语义丰富表达开关')
|
|
||||||
assert.ok(structuredOptionsSource.includes('使用大模型将问答表述得更自然、柔和'), '语义丰富表达缺少辅助说明')
|
|
||||||
for (const splitName of ['训练集', '验证集', '测试集']) {
|
for (const splitName of ['训练集', '验证集', '测试集']) {
|
||||||
assert.ok(datasetSplitEditorSource.includes(splitName), `生成选项缺少数据集划分:${splitName}`)
|
assert.ok(datasetSplitEditorSource.includes(splitName), `生成选项缺少数据集划分:${splitName}`)
|
||||||
}
|
}
|
||||||
@@ -232,7 +248,6 @@ for (const splitField of ['train', 'validation', 'test']) {
|
|||||||
`数据集划分字段 ${splitField} 缺少 0~100 的整数限制`,
|
`数据集划分字段 ${splitField} 缺少 0~100 的整数限制`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
assert.match(structuredOptionsSource, /<el-switch[\s\S]*options\.semanticEnrichment/, '语义丰富表达必须使用开关控件')
|
|
||||||
assert.match(structuredOptionsSource, /<el-input-number[\s\S]*options\.qaPairsPerRow[\s\S]*:min="1"[\s\S]*:max="5"/, '每行生成数量必须限制在 1 到 5')
|
assert.match(structuredOptionsSource, /<el-input-number[\s\S]*options\.qaPairsPerRow[\s\S]*:min="1"[\s\S]*:max="5"/, '每行生成数量必须限制在 1 到 5')
|
||||||
assert.match(viewSource, /const structuredOptions = ref<StructuredProcessOptions>/, '父页面缺少结构化配置状态')
|
assert.match(viewSource, /const structuredOptions = ref<StructuredProcessOptions>/, '父页面缺少结构化配置状态')
|
||||||
assert.match(stateSource, /datasetSplit:\s*\{ train: 80, validation: 10, test: 10 \}/, '数据集划分默认值必须为 80/10/10')
|
assert.match(stateSource, /datasetSplit:\s*\{ train: 80, validation: 10, test: 10 \}/, '数据集划分默认值必须为 80/10/10')
|
||||||
@@ -252,12 +267,16 @@ for (const field of [
|
|||||||
assert.ok(typesSource.includes(field), `生成控制配置缺少字段:${field}`)
|
assert.ok(typesSource.includes(field), `生成控制配置缺少字段:${field}`)
|
||||||
assert.ok(implementationSource.includes(field), `父页面默认值或草稿状态缺少字段:${field}`)
|
assert.ok(implementationSource.includes(field), `父页面默认值或草稿状态缺少字段:${field}`)
|
||||||
}
|
}
|
||||||
assert.match(structuredOptionsSource, /GenerationOptionsPanel/, '结构化生成选项没有复用统一的大模型与质量筛选组件')
|
assert.match(structuredOptionsSource, /GenerationOptionsPanel/, '结构化生成选项没有复用统一的质量筛选组件')
|
||||||
assert.match(unstructuredOptionsSource, /GenerationOptionsPanel/, '非结构化生成选项没有复用统一的大模型与质量筛选组件')
|
assert.match(unstructuredOptionsSource, /GenerationOptionsPanel/, '非结构化生成选项没有复用统一的质量筛选组件')
|
||||||
assert.match(structuredOptionsSource, /:options="options"/, '结构化生成选项未接入统一配置组件')
|
assert.match(structuredOptionsSource, /:options="options"/, '结构化生成选项未接入统一配置组件')
|
||||||
assert.match(unstructuredOptionsSource, /:options="options"/, '非结构化生成选项未接入统一配置组件')
|
assert.match(unstructuredOptionsSource, /:options="options"/, '非结构化生成选项未接入统一配置组件')
|
||||||
assert.match(taskSetupFeatureSource, /<h3>大模型<\/h3>/, '大模型必须作为与生成选项平级的独立分类')
|
assert.doesNotMatch(taskSetupFeatureSource, /<h3>大模型<\/h3>|section="model"/, '第一步不应继续承载大模型配置')
|
||||||
assert.match(taskSetupFeatureSource, /section="model"/, '独立大模型分类没有挂载模型配置')
|
assert.match(modelSelectionSource, /<h3[^>]*>大模型选择<\/h3>/, '独立步骤缺少大模型选择标题')
|
||||||
|
assert.match(modelSelectionSource, /section="model"/, '独立步骤没有挂载模型配置')
|
||||||
|
assert.match(modelSelectionSource, /defineExpose\(\{ validate \}\)/, '独立大模型选择步骤没有暴露继续前校验')
|
||||||
|
assert.match(modelSelectionSource, /class="form-section"/, '大模型选择步骤没有沿用第一步的通栏表单分区')
|
||||||
|
assert.doesNotMatch(modelSelectionSource, /max-width:\s*980px/, '大模型选择步骤不应使用比第一步更窄的固定内容宽度')
|
||||||
assert.match(taskSetupFeatureSource, /section="quality"/, '质量筛选没有保留在生成选项分类中')
|
assert.match(taskSetupFeatureSource, /section="quality"/, '质量筛选没有保留在生成选项分类中')
|
||||||
assert.doesNotMatch(generationControlSource, /<h4>大模型<\/h4>/, '大模型不应继续作为生成选项内部子分类')
|
assert.doesNotMatch(generationControlSource, /<h4>大模型<\/h4>/, '大模型不应继续作为生成选项内部子分类')
|
||||||
for (const label of ['大模型', '数据生成模型', '默认提示语', '质量筛选', '过滤低质量内容', '过滤过短内容', '最少字数']) {
|
for (const label of ['大模型', '数据生成模型', '默认提示语', '质量筛选', '过滤低质量内容', '过滤过短内容', '最少字数']) {
|
||||||
@@ -267,15 +286,21 @@ assert.match(generationControlSource, /filterable/, '数据生成模型下拉必
|
|||||||
assert.match(generationControlSource, /maxlength="500"/, '默认提示语缺少合理的长度限制')
|
assert.match(generationControlSource, /maxlength="500"/, '默认提示语缺少合理的长度限制')
|
||||||
assert.match(generationControlSource, /\.model-field\s*\{[\s\S]*?display:\s*flex[\s\S]*?flex-direction:\s*column/, '大模型字段没有使用稳定的纵向表单布局')
|
assert.match(generationControlSource, /\.model-field\s*\{[\s\S]*?display:\s*flex[\s\S]*?flex-direction:\s*column/, '大模型字段没有使用稳定的纵向表单布局')
|
||||||
assert.match(generationControlSource, /\.generation-config-group\s*\{[\s\S]*?border:\s*1px solid #e2e5ec/, '大模型配置没有保留统一配置面板边框')
|
assert.match(generationControlSource, /\.generation-config-group\s*\{[\s\S]*?border:\s*1px solid #e2e5ec/, '大模型配置没有保留统一配置面板边框')
|
||||||
|
assert.match(generationControlSource, /\.model-config-group\s*\{[\s\S]*?padding:\s*0[\s\S]*?border:\s*0/, '独立大模型步骤仍存在嵌套卡片挤压')
|
||||||
|
assert.match(
|
||||||
|
generationControlSource,
|
||||||
|
/\.model-config-group \.advanced-settings-grid\s*\{[\s\S]*?grid-template-columns:\s*1fr/,
|
||||||
|
'大模型高级参数没有改为与第一步一致的纵向布局',
|
||||||
|
)
|
||||||
assert.match(stateSource, /const DEFAULT_GENERATION_PROMPT\s*=\s*['"][^'"]{40,}['"]/, '大模型配置缺少可直接使用的默认提示语')
|
assert.match(stateSource, /const DEFAULT_GENERATION_PROMPT\s*=\s*['"][^'"]{40,}['"]/, '大模型配置缺少可直接使用的默认提示语')
|
||||||
assert.equal((stateSource.match(/generationPrompt:\s*DEFAULT_GENERATION_PROMPT/g) || []).length, 2, '结构化与非结构化任务必须共用默认提示语')
|
assert.equal((stateSource.match(/generationPrompt:\s*DEFAULT_GENERATION_PROMPT/g) || []).length, 2, '结构化与非结构化任务必须共用默认提示语')
|
||||||
assert.match(generationControlSource, /v-if="options\.qualityFilterEnabled"/, '质量规则没有随总开关渐进显示')
|
assert.match(generationControlSource, /v-if="options\.qualityFilterEnabled"/, '质量规则没有随总开关渐进显示')
|
||||||
assert.match(generationControlSource, /v-if="options\.filterShortContent"/, '最少字数没有随短内容规则显示')
|
assert.match(generationControlSource, /v-if="options\.filterShortContent"/, '最少字数没有随短内容规则显示')
|
||||||
assert.match(generationControlSource, /:min="1"[\s\S]*:max="1000"/, '最少字数缺少 1 到 1000 的边界限制')
|
assert.match(generationControlSource, /:min="1"[\s\S]*:max="1000"/, '最少字数缺少 1 到 1000 的边界限制')
|
||||||
assert.match(taskSetupSource, /generationValidationMessage/, '生成模型与质量规则缺少继续前校验')
|
assert.match(taskSetupSource, /qualityValidationMessage/, '质量规则缺少继续前校验')
|
||||||
assert.match(viewSource, /useModelsStore/, '创建页没有加载模型列表')
|
assert.match(viewSource, /useModelsStore/, '创建页没有加载模型列表')
|
||||||
assert.match(viewSource, /model\.type === 'LLM'/, '数据生成模型列表没有排除非大模型')
|
assert.match(viewSource, /model\.type === 'LLM'/, '数据生成模型列表没有排除非大模型')
|
||||||
assert.match(viewSource, /:generation-models="generationModels"/, '创建页没有向生成选项传递模型列表')
|
assert.match(viewSource, /<ModelSelectionStep[\s\S]*?:models="generationModels"/, '创建页没有向独立大模型选择步骤传递模型列表')
|
||||||
|
|
||||||
assert.match(typesSource, /export interface UnstructuredProcessOptions/, '缺少非结构化处理选项类型')
|
assert.match(typesSource, /export interface UnstructuredProcessOptions/, '缺少非结构化处理选项类型')
|
||||||
for (const field of [
|
for (const field of [
|
||||||
@@ -312,11 +337,7 @@ for (const method of ['自动语义切分', '按标题和段落', '按固定长
|
|||||||
for (const label of ['切片长度', '重叠长度', '最小切片长度', '保护表格、代码和列表']) {
|
for (const label of ['切片长度', '重叠长度', '最小切片长度', '保护表格、代码和列表']) {
|
||||||
assert.ok(unstructuredOptionsSource.includes(label), `切分选项缺少配置:${label}`)
|
assert.ok(unstructuredOptionsSource.includes(label), `切分选项缺少配置:${label}`)
|
||||||
}
|
}
|
||||||
assert.ok(unstructuredOptionsSource.includes('高级设置'), '切分选项缺少渐进式高级设置入口')
|
assert.doesNotMatch(unstructuredOptionsSource, /advancedChunkSettingsOpen|>高级设置</, '切分核心参数不应再隐藏在高级设置中')
|
||||||
assert.match(unstructuredOptionsSource, /:aria-expanded="advancedChunkSettingsOpen"/, '高级设置入口缺少展开状态的可访问性标记')
|
|
||||||
assert.match(unstructuredOptionsSource, /v-if="advancedChunkSettingsOpen"/, '高级设置内容没有默认收起')
|
|
||||||
assert.match(unstructuredOptionsSource, /watch\(\(\) => props\.options\.chunkMethod,[\s\S]*?method === 'custom'[\s\S]*?advancedChunkSettingsOpen\.value = true/, '选择自定义分隔符时没有自动展开高级设置')
|
|
||||||
assert.match(unstructuredOptionsSource, /function revealValidation\(\)[\s\S]*?advancedChunkSettingsOpen\.value = true/, '非结构化面板没有暴露高级校验定位能力')
|
|
||||||
assert.match(taskSetupSource, /if \(chunkValidationMessage\.value\) \{[\s\S]*?revealValidation\(\)[\s\S]*?return false/, '高级切分配置校验失败时没有重新展开定位')
|
assert.match(taskSetupSource, /if \(chunkValidationMessage\.value\) \{[\s\S]*?revealValidation\(\)[\s\S]*?return false/, '高级切分配置校验失败时没有重新展开定位')
|
||||||
assert.match(unstructuredOptionsSource, /const preserveSpecialContentEnabled = computed/, '特殊内容保护没有合并为单一开关')
|
assert.match(unstructuredOptionsSource, /const preserveSpecialContentEnabled = computed/, '特殊内容保护没有合并为单一开关')
|
||||||
assert.match(unstructuredOptionsSource, /function updateSpecialContentProtection/, '特殊内容保护开关缺少更新逻辑')
|
assert.match(unstructuredOptionsSource, /function updateSpecialContentProtection/, '特殊内容保护开关缺少更新逻辑')
|
||||||
@@ -324,7 +345,7 @@ assert.match(unstructuredOptionsSource, /options\.chunkSize[\s\S]*?:min="200"[\s
|
|||||||
assert.match(unstructuredOptionsSource, /options\.chunkOverlap[\s\S]*?:min="0"[\s\S]*?:max="500"/, '重叠长度必须限制在 0 到 500 Token')
|
assert.match(unstructuredOptionsSource, /options\.chunkOverlap[\s\S]*?:min="0"[\s\S]*?:max="500"/, '重叠长度必须限制在 0 到 500 Token')
|
||||||
assert.match(unstructuredOptionsSource, /options\.minChunkSize[\s\S]*?:min="20"[\s\S]*?:max="500"/, '最小切片长度必须限制在 20 到 500 Token')
|
assert.match(unstructuredOptionsSource, /options\.minChunkSize[\s\S]*?:min="20"[\s\S]*?:max="500"/, '最小切片长度必须限制在 20 到 500 Token')
|
||||||
|
|
||||||
for (const label of ['语义丰富表达', '每个切片生成数量', '数据集划分']) {
|
for (const label of ['每个切片生成数量', '数据集划分']) {
|
||||||
assert.ok(taskSetupFeatureSource.includes(label), `非结构化生成选项缺少:${label}`)
|
assert.ok(taskSetupFeatureSource.includes(label), `非结构化生成选项缺少:${label}`)
|
||||||
}
|
}
|
||||||
for (const removedLabel of ['上下文范围', '问题类型', '跳过无法回答的内容']) {
|
for (const removedLabel of ['上下文范围', '问题类型', '跳过无法回答的内容']) {
|
||||||
@@ -862,4 +883,4 @@ assert.match(
|
|||||||
)
|
)
|
||||||
assert.match(previewSource, /height:\s*clamp\(560px,\s*calc\(100vh - 370px\),\s*720px\)/, '对照预览高度不足以展示切片正文')
|
assert.match(previewSource, /height:\s*clamp\(560px,\s*calc\(100vh - 370px\),\s*720px\)/, '对照预览高度不足以展示切片正文')
|
||||||
|
|
||||||
console.log('数据处理五步向导回归检查通过')
|
console.log('数据处理六步向导回归检查通过')
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ElMessage, type UploadFile } from 'element-plus'
|
|||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import AppConfirmDialog from '@/components/AppConfirmDialog.vue'
|
import AppConfirmDialog from '@/components/AppConfirmDialog.vue'
|
||||||
import TaskSetupStep from './create/TaskSetupStep.vue'
|
import TaskSetupStep from './create/TaskSetupStep.vue'
|
||||||
|
import ModelSelectionStep from './create/ModelSelectionStep.vue'
|
||||||
import SourceUploadStep from './create/SourceUploadStep.vue'
|
import SourceUploadStep from './create/SourceUploadStep.vue'
|
||||||
import PreviewCompareStep from './create/PreviewCompareStep.vue'
|
import PreviewCompareStep from './create/PreviewCompareStep.vue'
|
||||||
import GenerationStep from './create/GenerationStep.vue'
|
import GenerationStep from './create/GenerationStep.vue'
|
||||||
@@ -22,6 +23,7 @@ import { useDataProcessGeneration } from './create/useDataProcessGeneration'
|
|||||||
import { useModelsStore } from '@/stores/models'
|
import { useModelsStore } from '@/stores/models'
|
||||||
import type {
|
import type {
|
||||||
ExternalDataSource,
|
ExternalDataSource,
|
||||||
|
GenerationControlOptions,
|
||||||
PreviewItem,
|
PreviewItem,
|
||||||
ProcessType,
|
ProcessType,
|
||||||
StepId,
|
StepId,
|
||||||
@@ -35,15 +37,17 @@ const modelsStore = useModelsStore()
|
|||||||
const { list: modelList } = storeToRefs(modelsStore)
|
const { list: modelList } = storeToRefs(modelsStore)
|
||||||
const generationModels = computed(() => modelList.value.filter((model) => model.type === 'LLM'))
|
const generationModels = computed(() => modelList.value.filter((model) => model.type === 'LLM'))
|
||||||
const taskSetupRef = ref<InstanceType<typeof TaskSetupStep>>()
|
const taskSetupRef = ref<InstanceType<typeof TaskSetupStep>>()
|
||||||
|
const modelSelectionRef = ref<InstanceType<typeof ModelSelectionStep>>()
|
||||||
const confirmDialogRef = ref<InstanceType<typeof AppConfirmDialog>>()
|
const confirmDialogRef = ref<InstanceType<typeof AppConfirmDialog>>()
|
||||||
const PREVIEW_MODEL_VERSION = 'document-chunk-v2'
|
const PREVIEW_MODEL_VERSION = 'document-chunk-v2'
|
||||||
|
|
||||||
const WIZARD_STEPS = [
|
const WIZARD_STEPS = [
|
||||||
{ id: 'create', title: '创建任务', desc: '填写任务信息与处理配置' },
|
{ id: 'create', title: '创建任务', desc: '填写任务信息与处理配置' },
|
||||||
|
{ id: 'model', title: '大模型选择', desc: '选择生成模型并设置输出要求' },
|
||||||
{ id: 'upload', title: '上传文件', desc: '上传或接入待处理的源数据' },
|
{ id: 'upload', title: '上传文件', desc: '上传或接入待处理的源数据' },
|
||||||
{ id: 'preview', title: '数据预览', desc: '核对源文件与预览内容' },
|
{ id: 'preview', title: '数据预览', desc: '核对源文件与预览内容' },
|
||||||
{ id: 'generate', title: '开始生成', desc: '确认摘要并启动处理' },
|
{ id: 'generate', title: '开始生成', desc: '确认摘要并启动处理' },
|
||||||
{ id: 'results', title: '结果编辑与保存', desc: '检查、修改并保存结果' },
|
{ id: 'results', title: '编辑与保存', desc: '检查、修改并保存结果' },
|
||||||
] as const satisfies ReadonlyArray<{ id: StepId; title: string; desc: string }>
|
] as const satisfies ReadonlyArray<{ id: StepId; title: string; desc: string }>
|
||||||
const currentStep = ref(0)
|
const currentStep = ref(0)
|
||||||
const currentStepId = computed<StepId>(() => WIZARD_STEPS[currentStep.value]?.id ?? 'create')
|
const currentStepId = computed<StepId>(() => WIZARD_STEPS[currentStep.value]?.id ?? 'create')
|
||||||
@@ -51,6 +55,9 @@ const task = reactive({ name: '', description: '' })
|
|||||||
const processType = ref<ProcessType>('structured')
|
const processType = ref<ProcessType>('structured')
|
||||||
const structuredOptions = ref<StructuredProcessOptions>(createDefaultStructuredOptions())
|
const structuredOptions = ref<StructuredProcessOptions>(createDefaultStructuredOptions())
|
||||||
const unstructuredOptions = ref<UnstructuredProcessOptions>(createDefaultUnstructuredOptions())
|
const unstructuredOptions = ref<UnstructuredProcessOptions>(createDefaultUnstructuredOptions())
|
||||||
|
const modelSelectionOptions = computed<GenerationControlOptions>(() => (
|
||||||
|
processType.value === 'unstructured' ? unstructuredOptions.value : structuredOptions.value
|
||||||
|
))
|
||||||
const uploadedFiles = ref<UploadedDataFile[]>([])
|
const uploadedFiles = ref<UploadedDataFile[]>([])
|
||||||
|
|
||||||
const externalSource = reactive<ExternalDataSource>({
|
const externalSource = reactive<ExternalDataSource>({
|
||||||
@@ -119,7 +126,8 @@ const previewFiles = computed(() => uploadedFiles.value.map((file) => {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
const primaryActionLabel = computed(() => {
|
const primaryActionLabel = computed(() => {
|
||||||
if (currentStepId.value === 'create') return '继续:上传文件'
|
if (currentStepId.value === 'create') return '继续:选择大模型'
|
||||||
|
if (currentStepId.value === 'model') return '继续:上传文件'
|
||||||
if (currentStepId.value === 'upload') return '继续:数据预览'
|
if (currentStepId.value === 'upload') return '继续:数据预览'
|
||||||
if (currentStepId.value === 'preview') return '确认预览并继续'
|
if (currentStepId.value === 'preview') return '确认预览并继续'
|
||||||
if (currentStepId.value === 'results') return '保存任务'
|
if (currentStepId.value === 'results') return '保存任务'
|
||||||
@@ -144,6 +152,14 @@ function goToStep(stepId: StepId) {
|
|||||||
if (nextStepIndex >= 0) currentStep.value = nextStepIndex
|
if (nextStepIndex >= 0) currentStep.value = nextStepIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateModelSelectionOptions(value: GenerationControlOptions) {
|
||||||
|
if (processType.value === 'unstructured') {
|
||||||
|
unstructuredOptions.value = { ...unstructuredOptions.value, ...value }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
structuredOptions.value = { ...structuredOptions.value, ...value }
|
||||||
|
}
|
||||||
|
|
||||||
const { persistDraft, restoreDraft } = useDataProcessDraft({
|
const { persistDraft, restoreDraft } = useDataProcessDraft({
|
||||||
currentStepId,
|
currentStepId,
|
||||||
task,
|
task,
|
||||||
@@ -393,6 +409,12 @@ function resetSourceDataForProcessTypeChange() {
|
|||||||
async function nextFromCreate() {
|
async function nextFromCreate() {
|
||||||
const valid = await taskSetupRef.value?.validate()
|
const valid = await taskSetupRef.value?.validate()
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
|
goToStep('model')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function nextFromModel() {
|
||||||
|
const valid = await modelSelectionRef.value?.validate()
|
||||||
|
if (!valid) return
|
||||||
goToStep('upload')
|
goToStep('upload')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,6 +526,10 @@ async function handlePrimaryAction() {
|
|||||||
await nextFromCreate()
|
await nextFromCreate()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (currentStepId.value === 'model') {
|
||||||
|
await nextFromModel()
|
||||||
|
return
|
||||||
|
}
|
||||||
if (currentStepId.value === 'upload') {
|
if (currentStepId.value === 'upload') {
|
||||||
nextFromUpload()
|
nextFromUpload()
|
||||||
return
|
return
|
||||||
@@ -596,27 +622,31 @@ onMounted(() => {
|
|||||||
<div class="wizard-main-inner">
|
<div class="wizard-main-inner">
|
||||||
<div class="wizard-steps-container">
|
<div class="wizard-steps-container">
|
||||||
<div class="custom-wizard-steps">
|
<div class="custom-wizard-steps">
|
||||||
<div
|
<template v-for="(step, index) in WIZARD_STEPS" :key="step.id">
|
||||||
v-for="(step, index) in WIZARD_STEPS"
|
<div
|
||||||
:key="step.id"
|
v-if="index !== 0"
|
||||||
class="step-item"
|
class="step-connector"
|
||||||
:class="{
|
:class="{ 'is-active': currentStep >= index }"
|
||||||
'is-active': currentStep === index,
|
></div>
|
||||||
'is-completed': currentStep > index
|
<div
|
||||||
}"
|
class="step-item"
|
||||||
:aria-current="currentStep === index ? 'step' : undefined"
|
:class="{
|
||||||
>
|
'is-active': currentStep === index,
|
||||||
<div v-if="index !== 0" class="step-connector"></div>
|
'is-completed': currentStep > index
|
||||||
<div class="step-node">
|
}"
|
||||||
<div class="step-icon">
|
:aria-current="currentStep === index ? 'step' : undefined"
|
||||||
<i v-if="currentStep > index" class="fa fa-check" />
|
>
|
||||||
<span v-else>{{ index + 1 }}</span>
|
<div class="step-node">
|
||||||
</div>
|
<div class="step-icon">
|
||||||
<div class="step-text">
|
<i v-if="currentStep > index" class="fa fa-check" />
|
||||||
<div class="step-title">{{ step.title }}</div>
|
<span v-else>{{ index + 1 }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="step-text">
|
||||||
|
<div class="step-title">{{ step.title }}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -629,7 +659,14 @@ onMounted(() => {
|
|||||||
v-model:process-type="processType"
|
v-model:process-type="processType"
|
||||||
v-model:structured-options="structuredOptions"
|
v-model:structured-options="structuredOptions"
|
||||||
v-model:unstructured-options="unstructuredOptions"
|
v-model:unstructured-options="unstructuredOptions"
|
||||||
:generation-models="generationModels"
|
/>
|
||||||
|
|
||||||
|
<ModelSelectionStep
|
||||||
|
v-else-if="currentStepId === 'model'"
|
||||||
|
ref="modelSelectionRef"
|
||||||
|
:options="modelSelectionOptions"
|
||||||
|
:models="generationModels"
|
||||||
|
@update:options="updateModelSelectionOptions"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SourceUploadStep
|
<SourceUploadStep
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { GenerationControlOptions } from './types'
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
options: GenerationControlOptions
|
options: GenerationControlOptions
|
||||||
models: ModelItem[]
|
models?: ModelItem[]
|
||||||
section: 'model' | 'quality'
|
section: 'model' | 'quality'
|
||||||
validationMessage?: string
|
validationMessage?: string
|
||||||
}>()
|
}>()
|
||||||
@@ -59,7 +59,7 @@ function sectionValidationMessage() {
|
|||||||
@update:model-value="updateField('generationModelId', $event)"
|
@update:model-value="updateField('generationModelId', $event)"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="model in models"
|
v-for="model in models || []"
|
||||||
:key="model.id"
|
:key="model.id"
|
||||||
:label="model.name"
|
:label="model.name"
|
||||||
:value="model.id"
|
:value="model.id"
|
||||||
@@ -234,7 +234,22 @@ function sectionValidationMessage() {
|
|||||||
.model-config-group {
|
.model-config-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 20px;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-config-group .advanced-settings-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-config-group .config-field {
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-field {
|
.model-field {
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import type { ModelItem } from '@/types'
|
||||||
|
import type { GenerationControlOptions } from './types'
|
||||||
|
import GenerationOptionsPanel from './GenerationOptionsPanel.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
options: GenerationControlOptions
|
||||||
|
models: ModelItem[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:options': [value: GenerationControlOptions]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const validationAttempted = ref(false)
|
||||||
|
const modelValidationMessage = computed(() => (
|
||||||
|
props.options.generationModelId === '' ? '请选择数据生成模型' : ''
|
||||||
|
))
|
||||||
|
|
||||||
|
function validate() {
|
||||||
|
validationAttempted.value = true
|
||||||
|
if (!modelValidationMessage.value) return true
|
||||||
|
ElMessage.error(modelValidationMessage.value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ validate })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="model-selection-step" aria-labelledby="model-selection-title">
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title-row">
|
||||||
|
<div>
|
||||||
|
<h3 id="model-selection-title">大模型选择</h3>
|
||||||
|
<p>选择本次数据生成使用的模型,并设置统一的输出要求</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="model-form-content">
|
||||||
|
<GenerationOptionsPanel
|
||||||
|
:options="options"
|
||||||
|
:models="models"
|
||||||
|
section="model"
|
||||||
|
:validation-message="validationAttempted ? modelValidationMessage : ''"
|
||||||
|
@update:options="emit('update:options', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.model-selection-step {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
padding: 0 0 26px;
|
||||||
|
margin-bottom: 26px;
|
||||||
|
border-bottom: 1px solid #edf0f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0 0 5px;
|
||||||
|
color: #2f3747;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
color: #8a93a3;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-form-content {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ModelItem } from '@/types'
|
|
||||||
import type {
|
import type {
|
||||||
GenerationControlOptions,
|
GenerationControlOptions,
|
||||||
PreprocessOption,
|
PreprocessOption,
|
||||||
@@ -10,9 +9,8 @@ import GenerationOptionsPanel from './GenerationOptionsPanel.vue'
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
options: StructuredProcessOptions
|
options: StructuredProcessOptions
|
||||||
generationModels: ModelItem[]
|
|
||||||
validationAttempted: boolean
|
validationAttempted: boolean
|
||||||
generationValidationMessage: string
|
qualityValidationMessage: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -89,9 +87,8 @@ function updatePreprocessOptions(value: Array<string | number | boolean>) {
|
|||||||
<div class="generation-option-list">
|
<div class="generation-option-list">
|
||||||
<GenerationOptionsPanel
|
<GenerationOptionsPanel
|
||||||
:options="options"
|
:options="options"
|
||||||
:models="generationModels"
|
|
||||||
section="quality"
|
section="quality"
|
||||||
:validation-message="validationAttempted ? generationValidationMessage : ''"
|
:validation-message="validationAttempted ? qualityValidationMessage : ''"
|
||||||
@update:options="updateGenerationOptions"
|
@update:options="updateGenerationOptions"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -116,23 +113,6 @@ function updatePreprocessOptions(value: Array<string | number | boolean>) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-section model-options-section">
|
|
||||||
<div class="section-title-row">
|
|
||||||
<div>
|
|
||||||
<h3>大模型</h3>
|
|
||||||
<p>选择数据生成模型,并设置模型输出内容的要求</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="generation-option-list">
|
|
||||||
<GenerationOptionsPanel
|
|
||||||
:options="options"
|
|
||||||
:models="generationModels"
|
|
||||||
section="model"
|
|
||||||
:validation-message="validationAttempted ? generationValidationMessage : ''"
|
|
||||||
@update:options="updateGenerationOptions"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
||||||
import type { ModelItem } from '@/types'
|
|
||||||
import type {
|
import type {
|
||||||
GenerationControlOptions,
|
GenerationControlOptions,
|
||||||
ProcessType,
|
ProcessType,
|
||||||
@@ -17,7 +16,6 @@ const props = defineProps<{
|
|||||||
processType: ProcessType
|
processType: ProcessType
|
||||||
structuredOptions: StructuredProcessOptions
|
structuredOptions: StructuredProcessOptions
|
||||||
unstructuredOptions: UnstructuredProcessOptions
|
unstructuredOptions: UnstructuredProcessOptions
|
||||||
generationModels: ModelItem[]
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -52,10 +50,9 @@ const activeGenerationOptions = computed<GenerationControlOptions | null>(() =>
|
|||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
const generationValidationMessage = computed(() => {
|
const qualityValidationMessage = computed(() => {
|
||||||
const options = activeGenerationOptions.value
|
const options = activeGenerationOptions.value
|
||||||
if (!options) return ''
|
if (!options) return ''
|
||||||
if (options.generationModelId === '') return '请选择数据生成模型'
|
|
||||||
if (options.qualityFilterEnabled && !options.filterLowQuality && !options.filterShortContent) {
|
if (options.qualityFilterEnabled && !options.filterLowQuality && !options.filterShortContent) {
|
||||||
return '开启质量筛选后,请至少选择一项筛选规则'
|
return '开启质量筛选后,请至少选择一项筛选规则'
|
||||||
}
|
}
|
||||||
@@ -112,8 +109,8 @@ async function validate() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (generationValidationMessage.value) {
|
if (qualityValidationMessage.value) {
|
||||||
ElMessage.error(generationValidationMessage.value)
|
ElMessage.error(qualityValidationMessage.value)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -210,9 +207,8 @@ defineExpose({ validate })
|
|||||||
<StructuredOptionsPanel
|
<StructuredOptionsPanel
|
||||||
v-if="processType === 'structured'"
|
v-if="processType === 'structured'"
|
||||||
:options="structuredOptions"
|
:options="structuredOptions"
|
||||||
:generation-models="generationModels"
|
|
||||||
:validation-attempted="validationAttempted"
|
:validation-attempted="validationAttempted"
|
||||||
:generation-validation-message="generationValidationMessage"
|
:quality-validation-message="qualityValidationMessage"
|
||||||
@update:options="emit('update:structuredOptions', $event)"
|
@update:options="emit('update:structuredOptions', $event)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -220,9 +216,8 @@ defineExpose({ validate })
|
|||||||
v-if="processType === 'unstructured'"
|
v-if="processType === 'unstructured'"
|
||||||
ref="unstructuredOptionsPanelRef"
|
ref="unstructuredOptionsPanelRef"
|
||||||
:options="unstructuredOptions"
|
:options="unstructuredOptions"
|
||||||
:generation-models="generationModels"
|
|
||||||
:validation-attempted="validationAttempted"
|
:validation-attempted="validationAttempted"
|
||||||
:generation-validation-message="generationValidationMessage"
|
:quality-validation-message="qualityValidationMessage"
|
||||||
:chunk-validation-message="chunkValidationMessage"
|
:chunk-validation-message="chunkValidationMessage"
|
||||||
@update:options="emit('update:unstructuredOptions', $event)"
|
@update:options="emit('update:unstructuredOptions', $event)"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import type { ModelItem } from '@/types'
|
|
||||||
import type {
|
import type {
|
||||||
ChunkMethod,
|
ChunkMethod,
|
||||||
GenerationControlOptions,
|
GenerationControlOptions,
|
||||||
@@ -12,9 +11,8 @@ import GenerationOptionsPanel from './GenerationOptionsPanel.vue'
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
options: UnstructuredProcessOptions
|
options: UnstructuredProcessOptions
|
||||||
generationModels: ModelItem[]
|
|
||||||
validationAttempted: boolean
|
validationAttempted: boolean
|
||||||
generationValidationMessage: string
|
qualityValidationMessage: string
|
||||||
chunkValidationMessage: string
|
chunkValidationMessage: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -269,9 +267,8 @@ defineExpose({ revealValidation })
|
|||||||
<div class="generation-option-list">
|
<div class="generation-option-list">
|
||||||
<GenerationOptionsPanel
|
<GenerationOptionsPanel
|
||||||
:options="options"
|
:options="options"
|
||||||
:models="generationModels"
|
|
||||||
section="quality"
|
section="quality"
|
||||||
:validation-message="validationAttempted ? generationValidationMessage : ''"
|
:validation-message="validationAttempted ? qualityValidationMessage : ''"
|
||||||
@update:options="updateGenerationOptions"
|
@update:options="updateGenerationOptions"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -301,23 +298,6 @@ defineExpose({ revealValidation })
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-section model-options-section">
|
|
||||||
<div class="section-title-row">
|
|
||||||
<div>
|
|
||||||
<h3>大模型</h3>
|
|
||||||
<p>选择数据生成模型,并设置模型输出内容的要求</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="generation-option-list">
|
|
||||||
<GenerationOptionsPanel
|
|
||||||
:options="options"
|
|
||||||
:models="generationModels"
|
|
||||||
section="model"
|
|
||||||
:validation-message="validationAttempted ? generationValidationMessage : ''"
|
|
||||||
@update:options="updateGenerationOptions"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
max-width: 860px;
|
max-width: 1040px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
}
|
}
|
||||||
@@ -45,11 +45,7 @@
|
|||||||
.step-item {
|
.step-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex: 1;
|
flex: none;
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
flex: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-connector {
|
.step-connector {
|
||||||
@@ -60,14 +56,14 @@
|
|||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-item.is-completed .step-connector,
|
.step-connector.is-active {
|
||||||
.step-item.is-active .step-connector {
|
|
||||||
background-color: #5146e5;
|
background-color: #5146e5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-node {
|
.step-node {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +145,30 @@
|
|||||||
min-width: 160px;
|
min-width: 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.custom-wizard-steps {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-connector {
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-node {
|
||||||
|
gap: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-title {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item.is-active .step-title {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
.wizard-main {
|
.wizard-main {
|
||||||
padding: 24px 20px;
|
padding: 24px 20px;
|
||||||
@@ -168,9 +188,7 @@
|
|||||||
|
|
||||||
.step-title {
|
.step-title {
|
||||||
max-width: 72px;
|
max-width: 72px;
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.35;
|
line-height: 1.35;
|
||||||
white-space: normal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-footer {
|
.wizard-footer {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export type ProcessType = 'structured' | 'unstructured' | 'external'
|
export type ProcessType = 'structured' | 'unstructured' | 'external'
|
||||||
|
|
||||||
export type StepId = 'create' | 'upload' | 'preview' | 'generate' | 'results'
|
export type StepId = 'create' | 'model' | 'upload' | 'preview' | 'generate' | 'results'
|
||||||
|
|
||||||
export type PreprocessOption =
|
export type PreprocessOption =
|
||||||
| 'clean_invalid'
|
| 'clean_invalid'
|
||||||
|
|||||||
@@ -194,28 +194,32 @@ onMounted(loadData)
|
|||||||
<main v-loading="loading" class="wizard-main">
|
<main v-loading="loading" class="wizard-main">
|
||||||
<div class="wizard-steps-container" aria-label="评测任务创建步骤">
|
<div class="wizard-steps-container" aria-label="评测任务创建步骤">
|
||||||
<div class="custom-wizard-steps">
|
<div class="custom-wizard-steps">
|
||||||
<div
|
<template v-for="(step, index) in WIZARD_STEPS" :key="step.title">
|
||||||
v-for="(step, index) in WIZARD_STEPS"
|
<div
|
||||||
:key="step.title"
|
v-if="index !== 0"
|
||||||
class="step-item"
|
class="step-connector"
|
||||||
:class="{
|
:class="{ 'is-active': currentStep >= index }"
|
||||||
'is-active': currentStep === index,
|
></div>
|
||||||
'is-completed': currentStep > index,
|
<div
|
||||||
}"
|
class="step-item"
|
||||||
:aria-current="currentStep === index ? 'step' : undefined"
|
:class="{
|
||||||
>
|
'is-active': currentStep === index,
|
||||||
<div v-if="index !== 0" class="step-connector"></div>
|
'is-completed': currentStep > index,
|
||||||
<div class="step-node">
|
}"
|
||||||
<div class="step-icon" aria-hidden="true">
|
:aria-current="currentStep === index ? 'step' : undefined"
|
||||||
<i v-if="currentStep > index" class="fa fa-check" />
|
>
|
||||||
<span v-else>{{ index + 1 }}</span>
|
<div class="step-node">
|
||||||
</div>
|
<div class="step-icon" aria-hidden="true">
|
||||||
<div class="step-text">
|
<i v-if="currentStep > index" class="fa fa-check" />
|
||||||
<div class="step-title">{{ step.title }}</div>
|
<span v-else>{{ index + 1 }}</span>
|
||||||
<div class="step-description">{{ step.description }}</div>
|
</div>
|
||||||
|
<div class="step-text">
|
||||||
|
<div class="step-title">{{ step.title }}</div>
|
||||||
|
<div class="step-description">{{ step.description }}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -308,14 +312,10 @@ onMounted(loadData)
|
|||||||
|
|
||||||
.step-item {
|
.step-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: none;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-item:first-child {
|
|
||||||
flex: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-connector {
|
.step-connector {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
@@ -324,8 +324,7 @@ onMounted(loadData)
|
|||||||
transition: background-color 0.2s ease;
|
transition: background-color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-item.is-active .step-connector,
|
.step-connector.is-active {
|
||||||
.step-item.is-completed .step-connector {
|
|
||||||
background: #5146e5;
|
background: #5146e5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user