feat(data-process): 接入重新生成配置流程

This commit is contained in:
caoxiaozhu
2026-07-25 22:41:06 +08:00
parent 396d3f6f47
commit 06e7455630
12 changed files with 648 additions and 145 deletions

View File

@@ -14,9 +14,12 @@ import { DEFAULT_SOURCE_TEXT, estimateTokenCount } from './create/previewModel'
import {
createDefaultStructuredOptions,
createDefaultUnstructuredOptions,
generationAffectingOptionsFor,
previewAffectingOptionsFor,
} from './create/dataProcessCreateState'
import { useDataProcessGeneration } from './create/useDataProcessGeneration'
import { useDataProcessPreviewBuild } from './create/useDataProcessPreviewBuild'
import { useDataProcessRegeneration } from './create/useDataProcessRegeneration'
import {
mapDataProcessSourceFile,
useDataProcessSourceUpload,
@@ -51,7 +54,7 @@ import type {
const router = useRouter()
const modelsStore = useModelsStore()
const { list: modelList } = storeToRefs(modelsStore)
const { list: modelList, loaded: modelsLoaded } = storeToRefs(modelsStore)
// 候选范围与模型管理保持一致,不在数据处理页面重复定义模型过滤规则。
const generationModels = computed(() => modelList.value)
const taskSetupRef = ref<InstanceType<typeof TaskSetupStep>>()
@@ -98,6 +101,7 @@ const selectedPreviewFileId = ref<string | null>(null)
const selectedPreviewId = ref<string | null>(null)
const selectedPreviewIdsByFile = ref<Record<string, string>>({})
const dirty = ref(false)
const modelSubmitLoading = ref(false)
let allowLeave = false
const {
@@ -236,7 +240,7 @@ function taskPayload() {
return {
name: task.name.trim(),
description: task.description.trim(),
process_type: processType.value,
process_type: originalProcessType.value || processType.value,
config: toBackendConfig(),
}
}
@@ -274,82 +278,11 @@ function mapPreviewItem(item: DataProcessPreviewItem): PreviewItem {
}
function previewAffectingOptions() {
if (processType.value === 'structured') {
return {
preprocessOptions: structuredOptions.value.preprocessOptions,
}
}
if (processType.value === 'unstructured') {
const {
preprocessOptions,
chunkMethod,
chunkSize,
chunkOverlap,
minChunkSize,
semanticBreakpointPercentile,
preserveTables,
preserveCodeBlocks,
preserveLists,
} = unstructuredOptions.value
return {
preprocessOptions,
chunkMethod,
chunkSize,
chunkOverlap,
minChunkSize,
semanticBreakpointPercentile,
preserveTables,
preserveCodeBlocks,
preserveLists,
}
}
return null
return previewAffectingOptionsFor(processType.value, structuredOptions.value, unstructuredOptions.value)
}
function generationAffectingOptions() {
if (processType.value === 'structured') {
const {
semanticEnrichment, qaPairsPerRow, datasetSplit, generationModelId, generationPrompt,
temperature, maxTokens, jsonMode,
qualityFilterEnabled, filterLowQuality, filterShortContent, minOutputLength,
} = structuredOptions.value
return {
semanticEnrichment, qaPairsPerRow, datasetSplit, generationModelId, generationPrompt,
temperature, maxTokens, jsonMode,
qualityFilterEnabled, filterLowQuality, filterShortContent, minOutputLength,
}
}
if (processType.value === 'unstructured') {
const {
semanticEnrichment,
qaPairsPerChunk,
datasetSplit,
generationModelId,
generationPrompt,
temperature,
maxTokens,
jsonMode,
qualityFilterEnabled,
filterLowQuality,
filterShortContent,
minOutputLength,
} = unstructuredOptions.value
return {
semanticEnrichment,
qaPairsPerChunk,
datasetSplit,
generationModelId,
generationPrompt,
temperature,
maxTokens,
jsonMode,
qualityFilterEnabled,
filterLowQuality,
filterShortContent,
minOutputLength,
}
}
return null
return generationAffectingOptionsFor(processType.value, structuredOptions.value, unstructuredOptions.value)
}
const generationOptionsSignature = computed(() => JSON.stringify(generationAffectingOptions()))
@@ -365,19 +298,58 @@ function buildPreviewSignature() {
return `${buildPreviewConfigSignature()}:${filesSignature}`
}
const {
isRegeneration,
originalProcessType,
regenerationPrepared,
hydrating,
initializationError,
loadSource: loadRegenerationSource,
confirmPreviewConfigChange,
prepareRegeneration,
} = useDataProcessRegeneration({
task,
processType,
structuredOptions,
unstructuredOptions,
uploadedFiles,
previewItems,
selectedPreviewFileId,
selectedPreviewId,
selectedPreviewIdsByFile,
previewSignature,
dirty,
buildPreviewConfigSignature,
buildPreviewSignature,
mapPreviewItem,
resetDownstream,
})
const leaveWarningMessage = computed(() => regenerationPrepared.value
? '仍有尚未完成的本地操作;已提交到服务端的修改不会因离开页面而撤销。'
: '当前存在未保存修改,离开后这些修改将不会保留。')
const leaveConfirmText = computed(() => regenerationPrepared.value ? '离开页面' : '放弃修改')
watch(
[() => task.name, () => task.description, processType, structuredOptions, unstructuredOptions, externalSource],
() => { dirty.value = true },
() => {
if (!hydrating.value) dirty.value = true
},
{ deep: true },
)
watch(processType, (nextType, previousType) => {
if (hydrating.value) return
if (isRegeneration.value && originalProcessType.value && nextType !== originalProcessType.value) {
processType.value = originalProcessType.value
return
}
if (nextType === previousType || uploadedFiles.value.length === 0) return
resetSourceDataForProcessTypeChange()
ElMessage.info('处理类型已变更,请重新上传或拉取匹配的源数据')
})
watch(generationOptionsSignature, (currentSignature, previousSignature) => {
if (hydrating.value) return
if (currentSignature === previousSignature) return
resetDownstream()
})
@@ -531,21 +503,50 @@ function resetSourceDataForProcessTypeChange() {
async function nextFromCreate() {
const valid = await taskSetupRef.value?.validate()
if (!valid) return
const confirmed = await confirmPreviewConfigChange((options) => (
confirmDialogRef.value?.open(options) ?? Promise.resolve(false)
))
if (!confirmed) return
goToStep('model')
}
async function nextFromModel() {
const valid = await modelSelectionRef.value?.validate()
if (!valid) return
if (modelSubmitLoading.value) return
modelSubmitLoading.value = true
try {
const saved = taskId.value
? await updateDataProcessTask(taskId.value, taskPayload())
: await createDataProcessTask(taskPayload())
taskId.value = String(saved.id)
await modelsStore.load(true)
if (!modelsLoaded.value) {
ElMessage.error('模型列表加载失败,未提交任何修改,请稍后重试')
return
}
const modelId = modelSelectionOptions.value.generationModelId
const modelExists = generationModels.value.some(model => String(model.id) === String(modelId))
if (modelId !== '' && !modelExists) {
ElMessage.error(isRegeneration.value ? '原任务使用的模型已删除,请重新选择模型' : '所选模型已删除,请重新选择')
return
}
const valid = await modelSelectionRef.value?.validate()
if (!valid) return
if (isRegeneration.value) {
const regenerated = await prepareRegeneration(taskPayload())
taskId.value = String(regenerated.task.id)
if (regenerated.preview_invalidated) {
ElMessage.info('切分配置已变化,现有源文件将在下一步按新配置重新切分')
} else if (regenerated.published_outputs_preserved) {
ElMessage.info('已保留原切片;已发布的三个数据集将在重新发布前保持不变')
}
} else {
const saved = taskId.value
? await updateDataProcessTask(taskId.value, taskPayload())
: await createDataProcessTask(taskPayload())
taskId.value = String(saved.id)
}
dirty.value = true
goToStep('upload')
} catch {
// 请求层已展示名称冲突或配置非法等具体原因。
} finally {
modelSubmitLoading.value = false
}
}
@@ -812,8 +813,8 @@ async function handleCancel() {
}
const confirmed = await confirmDialogRef.value?.open({
title: '确认离开当前页面?',
message: '当前存在未保存修改,离开后这些修改将不会保留。',
confirmText: '放弃修改',
message: leaveWarningMessage.value,
confirmText: leaveConfirmText.value,
cancelText: '继续编辑',
tone: 'danger',
})
@@ -830,8 +831,8 @@ onBeforeRouteLeave(async () => {
if (allowLeave || !dirty.value) return true
const confirmed = await confirmDialogRef.value?.open({
title: '确认离开当前页面?',
message: '当前存在未保存修改,离开后这些修改将不会保留。',
confirmText: '放弃修改',
message: leaveWarningMessage.value,
confirmText: leaveConfirmText.value,
cancelText: '继续编辑',
tone: 'danger',
})
@@ -845,6 +846,7 @@ onBeforeUnmount(() => {
onMounted(() => {
localStorage.removeItem('yg-data-process-create-draft')
void modelsStore.load(true)
if (isRegeneration.value) void loadRegenerationSource()
})
</script>
@@ -882,15 +884,20 @@ onMounted(() => {
</div>
</div>
<div class="wizard-content">
<div class="wizard-content" v-loading="hydrating">
<div v-if="initializationError" class="initialization-error" role="alert">
<el-alert type="error" :closable="false" :title="initializationError" show-icon />
<el-button type="primary" :loading="hydrating" @click="loadRegenerationSource">重试加载原任务</el-button>
</div>
<TaskSetupStep
v-if="currentStepId === 'create'"
v-else-if="currentStepId === 'create'"
ref="taskSetupRef"
v-model:name="task.name"
v-model:description="task.description"
v-model:process-type="processType"
v-model:structured-options="structuredOptions"
v-model:unstructured-options="unstructuredOptions"
:process-type-locked="isRegeneration"
/>
<ModelSelectionStep
@@ -974,8 +981,8 @@ onMounted(() => {
<el-button
class="wizard-primary-action"
type="primary"
:loading="generation.status === 'running' || (currentStepId === 'upload' && (sourceUploading || previewBuilding))"
:disabled="(currentStepId === 'generate' && generation.status === 'running') || previewBuilding || sourceUploading || (currentStepId === 'upload' && hasUnfinishedUploads)"
:loading="modelSubmitLoading || generation.status === 'running' || (currentStepId === 'upload' && (sourceUploading || previewBuilding))"
:disabled="hydrating || modelSubmitLoading || Boolean(initializationError) || (currentStepId === 'generate' && generation.status === 'running') || previewBuilding || sourceUploading || (currentStepId === 'upload' && hasUnfinishedUploads)"
@click="handlePrimaryAction"
>
{{ primaryActionLabel }} <i class="fa" :class="primaryActionIcon" style="margin-left: 6px;" />