fix(frontend): 保持重新生成退出前详情
This commit is contained in:
@@ -103,7 +103,6 @@ const selectedPreviewIdsByFile = ref<Record<string, string>>({})
|
||||
const dirty = ref(false)
|
||||
const modelSubmitLoading = ref(false)
|
||||
let allowLeave = false
|
||||
|
||||
const {
|
||||
generation,
|
||||
results,
|
||||
@@ -119,7 +118,7 @@ const {
|
||||
} = useDataProcessGeneration({
|
||||
taskId,
|
||||
dirty,
|
||||
beforeGenerate: syncPreviewChanges,
|
||||
beforeGenerate: beforeStartGeneration,
|
||||
})
|
||||
const { enqueueSourceUpload, sourceUploading } = useDataProcessSourceUpload({
|
||||
taskId,
|
||||
@@ -134,7 +133,6 @@ const { enqueueSourceUpload, sourceUploading } = useDataProcessSourceUpload({
|
||||
const hasUnfinishedUploads = computed(() => uploadedFiles.value.some((file) => (
|
||||
file.status !== 'ready' || !file.sourceFileId
|
||||
)))
|
||||
|
||||
const modifiedPreviewCount = computed(() => previewItems.value.filter((item) => item.status !== 'original').length)
|
||||
const previewItemsByFile = computed(() => {
|
||||
const grouped = new Map<string, PreviewItem[]>()
|
||||
@@ -171,7 +169,6 @@ const primaryActionLabel = computed(() => {
|
||||
if (generation.status === 'failed') return '重新生成'
|
||||
return '开始生成'
|
||||
})
|
||||
|
||||
const primaryActionIcon = computed(() => {
|
||||
if (currentStepId.value === 'results') return 'fa-check'
|
||||
if (currentStepId.value === 'generate' && generation.status !== 'success') return 'fa-play'
|
||||
@@ -181,7 +178,6 @@ const primaryActionIcon = computed(() => {
|
||||
const previousStepLabel = computed(() => currentStep.value > 0
|
||||
? WIZARD_STEPS[currentStep.value - 1].title
|
||||
: '')
|
||||
|
||||
function goToStep(stepId: StepId) {
|
||||
const nextStepIndex = WIZARD_STEPS.findIndex((step) => step.id === stepId)
|
||||
if (nextStepIndex >= 0) currentStep.value = nextStepIndex
|
||||
@@ -280,24 +276,19 @@ function mapPreviewItem(item: DataProcessPreviewItem): PreviewItem {
|
||||
function previewAffectingOptions() {
|
||||
return previewAffectingOptionsFor(processType.value, structuredOptions.value, unstructuredOptions.value)
|
||||
}
|
||||
|
||||
function generationAffectingOptions() {
|
||||
return generationAffectingOptionsFor(processType.value, structuredOptions.value, unstructuredOptions.value)
|
||||
}
|
||||
|
||||
const generationOptionsSignature = computed(() => JSON.stringify(generationAffectingOptions()))
|
||||
|
||||
function buildPreviewConfigSignature() {
|
||||
return `${PREVIEW_MODEL_VERSION}:${processType.value}:${JSON.stringify(previewAffectingOptions())}`
|
||||
}
|
||||
|
||||
function buildPreviewSignature() {
|
||||
const filesSignature = uploadedFiles.value
|
||||
.map((file) => `${file.uid}:${file.name}:${file.size}:${file.checksumSha256 || file.count}`)
|
||||
.join('|')
|
||||
return `${buildPreviewConfigSignature()}:${filesSignature}`
|
||||
}
|
||||
|
||||
const {
|
||||
isRegeneration,
|
||||
originalProcessType,
|
||||
@@ -307,6 +298,9 @@ const {
|
||||
loadSource: loadRegenerationSource,
|
||||
confirmPreviewConfigChange,
|
||||
prepareRegeneration,
|
||||
confirmStartGeneration,
|
||||
returnToDetail,
|
||||
leaveWarning,
|
||||
} = useDataProcessRegeneration({
|
||||
task,
|
||||
processType,
|
||||
@@ -324,11 +318,14 @@ const {
|
||||
mapPreviewItem,
|
||||
resetDownstream,
|
||||
})
|
||||
const leaveWarningMessage = computed(() => regenerationPrepared.value
|
||||
? '仍有尚未完成的本地操作;已提交到服务端的修改不会因离开页面而撤销。'
|
||||
: '当前存在未保存修改,离开后这些修改将不会保留。')
|
||||
const leaveConfirmText = computed(() => regenerationPrepared.value ? '离开页面' : '放弃修改')
|
||||
|
||||
async function beforeStartGeneration() {
|
||||
return confirmStartGeneration(
|
||||
options => confirmDialogRef.value?.open(options) ?? Promise.resolve(false),
|
||||
syncPreviewChanges,
|
||||
)
|
||||
}
|
||||
const leaveWarningMessage = computed(() => leaveWarning(generation.status))
|
||||
const leaveConfirmText = computed(() => isRegeneration.value ? '返回详情' : '放弃修改')
|
||||
watch(
|
||||
[() => task.name, () => task.description, processType, structuredOptions, unstructuredOptions, externalSource],
|
||||
() => {
|
||||
@@ -531,9 +528,9 @@ async function nextFromModel() {
|
||||
const regenerated = await prepareRegeneration(taskPayload())
|
||||
taskId.value = String(regenerated.task.id)
|
||||
if (regenerated.preview_invalidated) {
|
||||
ElMessage.info('切分配置已变化,现有源文件将在下一步按新配置重新切分')
|
||||
ElMessage.info('切分配置已保存,后续将按新配置重新切分;点击“开始生成”前,原生成结果和已发布数据保持不变')
|
||||
} else if (regenerated.published_outputs_preserved) {
|
||||
ElMessage.info('已保留原切片;已发布的三个数据集将在重新发布前保持不变')
|
||||
ElMessage.info('重新生成配置已保存;点击“开始生成”前,原生成结果和已发布数据保持不变')
|
||||
}
|
||||
} else {
|
||||
const saved = taskId.value
|
||||
@@ -807,8 +804,7 @@ async function saveTask() {
|
||||
|
||||
async function handleCancel() {
|
||||
if (!dirty.value) {
|
||||
allowLeave = true
|
||||
router.back()
|
||||
await returnToPreviousPage()
|
||||
return
|
||||
}
|
||||
const confirmed = await confirmDialogRef.value?.open({
|
||||
@@ -819,7 +815,12 @@ async function handleCancel() {
|
||||
tone: 'danger',
|
||||
})
|
||||
if (!confirmed) return
|
||||
await returnToPreviousPage()
|
||||
}
|
||||
|
||||
async function returnToPreviousPage() {
|
||||
allowLeave = true
|
||||
if (isRegeneration.value && await returnToDetail()) return
|
||||
router.back()
|
||||
}
|
||||
|
||||
@@ -973,7 +974,7 @@ onMounted(() => {
|
||||
<el-button v-if="currentStep > 0" :disabled="previewBuilding || sourceUploading" @click="handleBack">
|
||||
<i class="fa fa-arrow-left" style="margin-right: 6px;" /> 返回:{{ previousStepLabel }}
|
||||
</el-button>
|
||||
<el-button v-else @click="handleCancel">取消</el-button>
|
||||
<el-button v-else @click="handleCancel">{{ isRegeneration ? '返回详情' : '取消' }}</el-button>
|
||||
</div>
|
||||
<div class="footer-center">
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@ import type { GenerationState, ResultItem } from './types'
|
||||
interface GenerationBindings {
|
||||
taskId: Ref<string | null>
|
||||
dirty: Ref<boolean>
|
||||
beforeGenerate?: () => Promise<void>
|
||||
beforeGenerate?: () => Promise<boolean | void>
|
||||
}
|
||||
|
||||
const RESULT_PAGE_SIZE = 500
|
||||
@@ -51,6 +51,7 @@ export function useDataProcessGeneration(bindings: GenerationBindings) {
|
||||
let generationTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let generationRun = 0
|
||||
let pollFailureCount = 0
|
||||
let generationStarting = false
|
||||
|
||||
function stopGenerationTimer() {
|
||||
generationRun += 1
|
||||
@@ -140,33 +141,39 @@ export function useDataProcessGeneration(bindings: GenerationBindings) {
|
||||
}
|
||||
|
||||
async function startGeneration() {
|
||||
if (generationStarting || generation.status === 'running') return
|
||||
const taskId = bindings.taskId.value
|
||||
if (!taskId) {
|
||||
ElMessage.error('任务尚未创建,请返回上一步重试')
|
||||
return
|
||||
}
|
||||
|
||||
stopGenerationTimer()
|
||||
const runId = generationRun
|
||||
generation.status = 'running'
|
||||
pollFailureCount = 0
|
||||
generation.progress = 0
|
||||
generation.message = '正在同步预览修改并启动后端处理,请稍候。'
|
||||
|
||||
generationStarting = true
|
||||
let runId: number | null = null
|
||||
try {
|
||||
await bindings.beforeGenerate?.()
|
||||
const canStart = await bindings.beforeGenerate?.()
|
||||
if (canStart === false) return
|
||||
stopGenerationTimer()
|
||||
const activeRunId = generationRun
|
||||
runId = activeRunId
|
||||
generation.status = 'running'
|
||||
pollFailureCount = 0
|
||||
generation.progress = 0
|
||||
generation.message = '正在同步预览修改并启动后端处理,请稍候。'
|
||||
const progress = await generateDataProcess(taskId)
|
||||
if (runId !== generationRun) return
|
||||
if (activeRunId !== generationRun) return
|
||||
if (progress.status === 'completed' || progress.status === 'failed' || progress.status === 'stopped') {
|
||||
await finishFromProgress(progress)
|
||||
return
|
||||
}
|
||||
applyProgress(progress)
|
||||
generationTimer = setTimeout(() => void pollGeneration(runId), POLL_INTERVAL_MS)
|
||||
generationTimer = setTimeout(() => void pollGeneration(activeRunId), POLL_INTERVAL_MS)
|
||||
} catch (error) {
|
||||
if (runId !== generationRun) return
|
||||
if (runId !== null && runId !== generationRun) return
|
||||
generation.status = 'failed'
|
||||
generation.message = error instanceof Error ? error.message : '启动数据处理失败,请重试。'
|
||||
} finally {
|
||||
generationStarting = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { computed, nextTick, ref, type Reactive, type Ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
getDataProcessPreview,
|
||||
getDataProcessSourceContent,
|
||||
@@ -81,6 +81,7 @@ async function loadAllPreviews(taskId: string, mapPreviewItem: RegenerationBindi
|
||||
|
||||
export function useDataProcessRegeneration(bindings: RegenerationBindings) {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const sourceTaskId = computed(() => (
|
||||
route.name === 'data-process-regenerate' ? String(route.params.id || '') : ''
|
||||
))
|
||||
@@ -169,7 +170,7 @@ export function useDataProcessRegeneration(bindings: RegenerationBindings) {
|
||||
if (!changed || currentSignature === confirmedPreviewConfigSignature.value) return true
|
||||
const confirmed = await openConfirm({
|
||||
title: '确认修改切分配置?',
|
||||
message: '修改预处理或切分配置将删除现有切片和生成结果;已发布三数据集暂时保留,直到重新发布后才会更新。',
|
||||
message: '修改预处理或切分配置后,将按新配置重新切分。在点击“开始生成”前,原生成结果和已发布数据会继续保留。',
|
||||
confirmText: '确认并继续',
|
||||
cancelText: '返回检查',
|
||||
tone: 'warning',
|
||||
@@ -209,13 +210,47 @@ export function useDataProcessRegeneration(bindings: RegenerationBindings) {
|
||||
}
|
||||
} catch (error) {
|
||||
initializationError.value = error instanceof Error
|
||||
? `任务已进入重新生成状态,但工作区恢复失败:${error.message}`
|
||||
: '任务已进入重新生成状态,但工作区恢复失败,请重试加载原任务'
|
||||
? `重新生成配置已保存,但工作区恢复失败:${error.message}`
|
||||
: '重新生成配置已保存,但工作区恢复失败,请重试加载原任务'
|
||||
throw error
|
||||
}
|
||||
return regenerated
|
||||
}
|
||||
|
||||
async function confirmStartGeneration(
|
||||
openConfirm: (options: ConfirmOptions) => Promise<boolean | undefined>,
|
||||
syncPreviewChanges: () => Promise<void>,
|
||||
) {
|
||||
if (isRegeneration.value) {
|
||||
const confirmed = await openConfirm({
|
||||
title: '开始重新生成?',
|
||||
message: '点击开始后,当前生成结果将被替换。已发布数据集会继续保留,直到重新发布。',
|
||||
confirmText: '开始生成',
|
||||
cancelText: '继续检查',
|
||||
tone: 'warning',
|
||||
})
|
||||
if (!confirmed) return false
|
||||
}
|
||||
await syncPreviewChanges()
|
||||
return true
|
||||
}
|
||||
|
||||
async function returnToDetail() {
|
||||
if (!sourceTaskId.value) return false
|
||||
await router.replace({ name: 'data-process-detail', params: { id: sourceTaskId.value } })
|
||||
return true
|
||||
}
|
||||
|
||||
function leaveWarning(generationStatus: string) {
|
||||
if (isRegeneration.value && regenerationPrepared.value && generationStatus === 'idle') {
|
||||
return '重新生成配置已保存,但尚未开始生成;现在返回详情不会替换原生成结果或已发布数据,可稍后继续。'
|
||||
}
|
||||
if (isRegeneration.value && regenerationPrepared.value) {
|
||||
return '重新生成已开始;离开页面不会停止服务端处理,已提交的修改不会撤销。'
|
||||
}
|
||||
return '当前存在未保存修改,离开后这些修改将不会保留。'
|
||||
}
|
||||
|
||||
return {
|
||||
sourceTaskId,
|
||||
isRegeneration,
|
||||
@@ -228,5 +263,8 @@ export function useDataProcessRegeneration(bindings: RegenerationBindings) {
|
||||
loadSource,
|
||||
confirmPreviewConfigChange,
|
||||
prepareRegeneration,
|
||||
confirmStartGeneration,
|
||||
returnToDetail,
|
||||
leaveWarning,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user