feat:暂停动画以及暂停正确响应
This commit is contained in:
@@ -43,31 +43,6 @@ const stepExecutionStatus = ref<Record<string, StepExecutionStatus>>({})
|
||||
// 用于标记暂停时的"最后动作完成"状态
|
||||
const isPausing = ref(false) // 正在请求暂停(等待当前动作完成)
|
||||
|
||||
// ==================== 步骤版本追踪 ====================
|
||||
// 步骤版本信息接口
|
||||
interface StepVersionInfo {
|
||||
stepId: string // 步骤ID
|
||||
stepIndex: number // 步骤索引(0-based)
|
||||
originalHash: string // 原始配置hash(初始化时生成)
|
||||
currentHash: string // 当前配置hash(编辑后更新)
|
||||
isModified: boolean // 是否已修改
|
||||
}
|
||||
|
||||
// 重执行配置接口
|
||||
interface ReExecuteConfig {
|
||||
shouldReExecute: boolean // 是否需要重新执行
|
||||
startFromStepIndex: number // 从哪个步骤开始(-1表示从头开始)
|
||||
modifiedSteps: string[] // 被修改的步骤ID列表
|
||||
}
|
||||
|
||||
// 步骤版本追踪
|
||||
const stepVersions = ref<Record<string, StepVersionInfo>>({})
|
||||
const reExecuteConfig = ref<ReExecuteConfig>({
|
||||
shouldReExecute: false,
|
||||
startFromStepIndex: -1,
|
||||
modifiedSteps: []
|
||||
})
|
||||
|
||||
// Check if step is ready to execute (has TaskProcess data)
|
||||
const isStepReady = (step: IRawStepTask) => {
|
||||
return step.TaskProcess && step.TaskProcess.length > 0
|
||||
@@ -130,155 +105,6 @@ const stepsReadyStatus = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
// ==================== 步骤版本追踪函数 ====================
|
||||
/**
|
||||
* 生成步骤配置的hash(用于检测修改)
|
||||
* @param step 步骤对象
|
||||
* @returns hash字符串
|
||||
*/
|
||||
function generateStepHash(step: IRawStepTask): string {
|
||||
// 只考虑TaskProcess中的Description字段
|
||||
// 因为这是用户可以编辑的部分
|
||||
const processDescriptions = step.TaskProcess.map(p => `${p.ID}:${p.Description}`).join('|')
|
||||
|
||||
// 简单hash算法
|
||||
let hash = 0
|
||||
for (let i = 0; i < processDescriptions.length; i++) {
|
||||
const char = processDescriptions.charCodeAt(i)
|
||||
hash = (hash << 5) - hash + char
|
||||
hash = hash & hash // Convert to 32bit integer
|
||||
}
|
||||
return Math.abs(hash).toString(36)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化步骤版本(在任务加载完成后调用)
|
||||
*/
|
||||
function initializeStepVersions() {
|
||||
const steps = collaborationProcess.value
|
||||
stepVersions.value = {}
|
||||
|
||||
steps.forEach((step, index) => {
|
||||
const stepId = step.Id || step.StepName || `step-${index}`
|
||||
const hash = generateStepHash(step)
|
||||
|
||||
stepVersions.value[stepId] = {
|
||||
stepId,
|
||||
stepIndex: index,
|
||||
originalHash: hash,
|
||||
currentHash: hash,
|
||||
isModified: false
|
||||
}
|
||||
})
|
||||
|
||||
// 重置重执行配置
|
||||
reExecuteConfig.value = {
|
||||
shouldReExecute: false,
|
||||
startFromStepIndex: -1,
|
||||
modifiedSteps: []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新步骤版本(编辑保存后调用)
|
||||
*/
|
||||
function updateStepVersion(stepId: string) {
|
||||
const step = collaborationProcess.value.find(s => (s.Id || s.StepName) === stepId)
|
||||
|
||||
if (step && stepVersions.value[stepId]) {
|
||||
const newHash = generateStepHash(step)
|
||||
const versionInfo = stepVersions.value[stepId]
|
||||
|
||||
versionInfo.currentHash = newHash
|
||||
versionInfo.isModified = newHash !== versionInfo.originalHash
|
||||
|
||||
// 重新计算重执行配置
|
||||
calculateReExecuteConfig()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算重执行配置
|
||||
*/
|
||||
function calculateReExecuteConfig() {
|
||||
const modifiedSteps: string[] = []
|
||||
let minModifiedIndex = Infinity
|
||||
|
||||
// 找出所有被修改的步骤
|
||||
Object.values(stepVersions.value).forEach(versionInfo => {
|
||||
if (versionInfo.isModified) {
|
||||
modifiedSteps.push(versionInfo.stepId)
|
||||
minModifiedIndex = Math.min(minModifiedIndex, versionInfo.stepIndex)
|
||||
}
|
||||
})
|
||||
|
||||
// 设置重执行配置
|
||||
if (modifiedSteps.length > 0) {
|
||||
reExecuteConfig.value = {
|
||||
shouldReExecute: true,
|
||||
startFromStepIndex: minModifiedIndex,
|
||||
modifiedSteps
|
||||
}
|
||||
} else {
|
||||
reExecuteConfig.value = {
|
||||
shouldReExecute: false,
|
||||
startFromStepIndex: -1,
|
||||
modifiedSteps: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置步骤版本标记
|
||||
*/
|
||||
function resetStepVersions() {
|
||||
Object.values(stepVersions.value).forEach(versionInfo => {
|
||||
versionInfo.originalHash = versionInfo.currentHash
|
||||
versionInfo.isModified = false
|
||||
})
|
||||
reExecuteConfig.value = {
|
||||
shouldReExecute: false,
|
||||
startFromStepIndex: -1,
|
||||
modifiedSteps: []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理指定步骤索引之后的所有执行结果
|
||||
* @param fromStepIndex 起始步骤索引(该索引之后的结果将被清理)
|
||||
*/
|
||||
function clearExecutionResultsAfter(fromStepIndex: number) {
|
||||
const steps = collaborationProcess.value
|
||||
const currentResults = agentsStore.executePlan
|
||||
|
||||
// 找出需要清理的步骤名称
|
||||
const stepsToClear = new Set<string>()
|
||||
for (let i = fromStepIndex; i < steps.length; i++) {
|
||||
const step = steps[i]
|
||||
if (!step) continue
|
||||
if (step.StepName) {
|
||||
stepsToClear.add(step.StepName)
|
||||
}
|
||||
if (step.OutputObject) {
|
||||
stepsToClear.add(step.OutputObject)
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤掉需要清理的结果
|
||||
const filteredResults = currentResults.filter(result => !stepsToClear.has(result.NodeId))
|
||||
|
||||
// 更新store
|
||||
agentsStore.setExecutePlan(filteredResults)
|
||||
|
||||
// 重置步骤执行状态
|
||||
Object.keys(stepExecutionStatus.value).forEach(stepName => {
|
||||
const stepIndex = steps.findIndex(s => s.StepName === stepName)
|
||||
if (stepIndex >= fromStepIndex) {
|
||||
stepExecutionStatus.value[stepName] = StepExecutionStatus.READY
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听步骤数据变化,更新步骤状态并动态追加新步骤
|
||||
*/
|
||||
@@ -317,11 +143,6 @@ watch(
|
||||
const totalStepsCount = collaborationProcess.value.length
|
||||
const currentStep = executionProgress.value.currentStep || 1
|
||||
executionProgress.value.totalSteps = totalStepsCount
|
||||
|
||||
// 使用 Notification 显示追加成功通知
|
||||
// info('步骤追加成功', `${stepName} (${currentStep}/${totalStepsCount})`, {
|
||||
// duration: 2000
|
||||
// })
|
||||
} else {
|
||||
console.warn(`⚠️ 追加步骤失败: ${stepName} - 执行ID不存在或已结束`)
|
||||
// 追加失败,移除标记以便重试
|
||||
@@ -346,11 +167,6 @@ watch(
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 初始化步骤版本(首次加载时)
|
||||
if (newSteps.length > 0 && Object.keys(stepVersions.value).length === 0) {
|
||||
initializeStepVersions()
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
@@ -396,15 +212,6 @@ function handleSaveEdit(stepId: string, processId: string, value: string) {
|
||||
const process = step.TaskProcess.find(p => p.ID === processId)
|
||||
if (process) {
|
||||
process.Description = value
|
||||
|
||||
// 更新步骤版本
|
||||
updateStepVersion(stepId)
|
||||
|
||||
// 显示修改提示
|
||||
const versionInfo = stepVersions.value[stepId]
|
||||
if (versionInfo?.isModified) {
|
||||
warning('步骤已修改', `步骤 "${step.StepName}" 已修改,继续执行时将从该步骤重新开始`)
|
||||
}
|
||||
}
|
||||
}
|
||||
editMap[key] = false
|
||||
@@ -649,9 +456,11 @@ async function executeNextReadyBatch() {
|
||||
|
||||
switch (event.type) {
|
||||
case 'step_start':
|
||||
// 使用全局步骤索引计算当前步骤
|
||||
const globalStepIndex = collaborationProcess.value.findIndex(s => s.StepName === event.step_name)
|
||||
executionProgress.value = {
|
||||
currentStep: (event.step_index || 0) + 1,
|
||||
totalSteps: event.total_steps || collaborationProcess.value.length,
|
||||
currentStep: globalStepIndex >= 0 ? globalStepIndex + 1 : (event.step_index || 0) + 1,
|
||||
totalSteps: collaborationProcess.value.length,
|
||||
currentAction: 0,
|
||||
totalActions: 0,
|
||||
currentStepName: event.step_name
|
||||
@@ -662,24 +471,20 @@ async function executeNextReadyBatch() {
|
||||
currentProgressNotificationId.value = showProgress(
|
||||
'任务执行中',
|
||||
executionProgress.value.currentStep,
|
||||
executionProgress.value.totalSteps || 1
|
||||
executionProgress.value.totalSteps
|
||||
)
|
||||
updateProgressDetail(
|
||||
currentProgressNotificationId.value,
|
||||
`步骤 ${executionProgress.value.currentStep}/${
|
||||
executionProgress.value.totalSteps || 1
|
||||
}`,
|
||||
`步骤 ${executionProgress.value.currentStep}/${executionProgress.value.totalSteps}`,
|
||||
`正在执行: ${event.step_name}`
|
||||
)
|
||||
} else {
|
||||
updateProgressDetail(
|
||||
currentProgressNotificationId.value,
|
||||
`步骤 ${executionProgress.value.currentStep}/${
|
||||
executionProgress.value.totalSteps || 1
|
||||
}`,
|
||||
`步骤 ${executionProgress.value.currentStep}/${executionProgress.value.totalSteps}`,
|
||||
`正在执行: ${event.step_name}`,
|
||||
executionProgress.value.currentStep,
|
||||
executionProgress.value.totalSteps || 1
|
||||
executionProgress.value.totalSteps
|
||||
)
|
||||
}
|
||||
break
|
||||
@@ -689,22 +494,32 @@ async function executeNextReadyBatch() {
|
||||
? ` [并行 ${event.batch_info!.batch_size} 个动作]`
|
||||
: ''
|
||||
|
||||
const stepIndexForAction = event.step_index || 0
|
||||
const totalStepsValue =
|
||||
executionProgress.value.totalSteps || collaborationProcess.value.length
|
||||
// 使用全局步骤索引
|
||||
const globalStepIndexForAction = collaborationProcess.value.findIndex(s => s.StepName === event.step_name)
|
||||
const stepIndexForAction = globalStepIndexForAction >= 0 ? globalStepIndexForAction + 1 : (event.step_index || 0) + 1
|
||||
const totalStepsValue = collaborationProcess.value.length
|
||||
|
||||
executionProgress.value = {
|
||||
...executionProgress.value,
|
||||
currentAction: event.completed_actions,
|
||||
totalActions: event.total_actions
|
||||
}
|
||||
|
||||
// 检测是否正在暂停(等待当前动作完成)
|
||||
if (isPausing.value) {
|
||||
// 当前动作完成,完成暂停
|
||||
isPaused.value = true
|
||||
isPausing.value = false
|
||||
success('已暂停', '已暂停执行,可稍后继续')
|
||||
}
|
||||
|
||||
// 更新详细进度信息,显示动作级别进度
|
||||
if (currentProgressNotificationId.value) {
|
||||
updateProgressDetail(
|
||||
currentProgressNotificationId.value,
|
||||
`步骤 ${stepIndexForAction + 1}/${totalStepsValue}`,
|
||||
`步骤 ${stepIndexForAction}/${totalStepsValue}`,
|
||||
`${event.step_name} - 动作 ${event.completed_actions}/${event.total_actions} 完成${parallelInfo}`,
|
||||
stepIndexForAction + 1,
|
||||
stepIndexForAction,
|
||||
totalStepsValue
|
||||
)
|
||||
}
|
||||
@@ -880,46 +695,7 @@ function removeNotification(id: string) {
|
||||
// Pause/Resume handler
|
||||
async function handlePauseResume() {
|
||||
if (isPaused.value) {
|
||||
// Resume execution
|
||||
|
||||
// 检查是否需要重新执行(有步骤被修改)
|
||||
if (reExecuteConfig.value.shouldReExecute) {
|
||||
const startStepIndex = reExecuteConfig.value.startFromStepIndex
|
||||
const startStep = collaborationProcess.value[startStepIndex]
|
||||
|
||||
try {
|
||||
// 确认对话框
|
||||
await ElMessageBox.confirm(
|
||||
`检测到 ${reExecuteConfig.value.modifiedSteps.length} 个步骤已被修改\n` +
|
||||
`将从步骤 "${startStep?.StepName}" 重新开始执行\n\n` +
|
||||
`是否继续?`,
|
||||
'检测到步骤修改',
|
||||
{
|
||||
confirmButtonText: '从修改步骤重新执行',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
|
||||
// 清理执行结果
|
||||
clearExecutionResultsAfter(startStepIndex)
|
||||
|
||||
// 重置暂停状态
|
||||
isPaused.value = false
|
||||
isPausing.value = false
|
||||
isStreaming.value = false
|
||||
|
||||
// 从指定步骤重新执行
|
||||
await reExecuteFromStep(startStepIndex)
|
||||
|
||||
// 重置修改标记
|
||||
resetStepVersions()
|
||||
} catch {
|
||||
// 用户取消
|
||||
info('已取消', '已取消重新执行')
|
||||
}
|
||||
} else {
|
||||
// 没有修改,正常恢复执行
|
||||
// Resume execution - 正常恢复执行
|
||||
try {
|
||||
if (websocket.connected) {
|
||||
await websocket.send('resume_execution', {
|
||||
@@ -936,7 +712,6 @@ async function handlePauseResume() {
|
||||
error('恢复失败', '恢复执行失败')
|
||||
// 恢复失败时,保持原状态不变(仍然是暂停状态)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Pause execution
|
||||
try {
|
||||
@@ -949,10 +724,9 @@ async function handlePauseResume() {
|
||||
goal: agentsStore.agentRawPlan.data?.['General Goal'] || ''
|
||||
})
|
||||
|
||||
// 收到成功响应后,设置完全暂停状态
|
||||
isPaused.value = true
|
||||
isPausing.value = false
|
||||
success('已暂停', '已暂停执行,可稍后继续')
|
||||
// 注意:不立即设置 isPaused = true
|
||||
// 而是等待当前动作完成后,在 action_complete 事件中设置
|
||||
// 这样可以确保在动作真正完成后才显示"已暂停"
|
||||
} else {
|
||||
warning('无法暂停', 'WebSocket未连接,无法暂停')
|
||||
isPausing.value = false
|
||||
@@ -977,35 +751,6 @@ async function handleExecuteButtonClick() {
|
||||
await handleRun()
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定步骤重新执行(支持边填充边执行)
|
||||
* @param fromStepIndex 起始步骤索引
|
||||
*/
|
||||
async function reExecuteFromStep(fromStepIndex: number) {
|
||||
const steps = collaborationProcess.value
|
||||
|
||||
// 设置执行状态
|
||||
loading.value = true
|
||||
isStreaming.value = true
|
||||
|
||||
// 重置将要执行的步骤的状态
|
||||
for (let i = fromStepIndex; i < steps.length; i++) {
|
||||
const step = steps[i]
|
||||
if (step) {
|
||||
const stepName = step.StepName || step.Id || ''
|
||||
stepExecutionStatus.value[stepName] = StepExecutionStatus.READY
|
||||
}
|
||||
}
|
||||
|
||||
// 使用批量执行模式,会自动处理依赖和边填充边执行
|
||||
await executeNextReadyBatch()
|
||||
|
||||
success('重新执行完成', '所有步骤已重新执行完成')
|
||||
loading.value = false
|
||||
isPaused.value = false
|
||||
isStreaming.value = false
|
||||
}
|
||||
|
||||
async function handleRun() {
|
||||
// Check if there are ready steps
|
||||
const readySteps = stepsReadyStatus.value.ready
|
||||
@@ -1302,15 +1047,23 @@ defineExpose({
|
||||
class="animate-spin"
|
||||
/>
|
||||
|
||||
<!-- 流式传输中且未Pause:显示Pause图标 -->
|
||||
<!-- 流式传输中且正在暂停(等待当前动作完成):显示Loading图标 -->
|
||||
<svg-icon
|
||||
v-else-if="isStreaming && !isPaused"
|
||||
v-else-if="isStreaming && isPausing"
|
||||
icon-class="loading"
|
||||
size="20px"
|
||||
class="btn-icon animate-spin"
|
||||
/>
|
||||
|
||||
<!-- 流式传输中且未暂停:显示Pause图标 -->
|
||||
<svg-icon
|
||||
v-else-if="isStreaming && !isPaused && !isPausing"
|
||||
icon-class="Pause"
|
||||
size="20px"
|
||||
class="btn-icon"
|
||||
/>
|
||||
|
||||
<!-- 流式传输中且已Pause:显示播放/Resume图标 -->
|
||||
<!-- 流式传输中且已暂停:显示播放/Resume图标 -->
|
||||
<svg-icon
|
||||
v-else-if="isStreaming && isPaused"
|
||||
icon-class="video-play"
|
||||
@@ -1323,6 +1076,7 @@ defineExpose({
|
||||
|
||||
<span v-if="showExecuteText && !isStreaming" class="btn-text">任务执行</span>
|
||||
<span v-else-if="isStreaming && isPaused" class="btn-text">继续执行</span>
|
||||
<span v-else-if="isStreaming && isPausing" class="btn-text">暂停中...</span>
|
||||
<span v-else-if="isStreaming" class="btn-text">暂停执行</span>
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user