Răsfoiți Sursa

feat(sidepanel): 添加获取正在运行步骤的功能并更新自动执行逻辑

QLHazyCoder 4 luni în urmă
părinte
comite
b31459b3b8
2 a modificat fișierele cu 79 adăugiri și 13 ștergeri
  1. 43 7
      background.js
  2. 36 6
      sidepanel/sidepanel.js

+ 43 - 7
background.js

@@ -1894,6 +1894,13 @@ function clearStopRequest() {
   stopRequested = false;
 }
 
+function getRunningSteps(statuses = {}) {
+  return Object.entries({ ...DEFAULT_STATE.stepStatuses, ...statuses })
+    .filter(([, status]) => status === 'running')
+    .map(([step]) => Number(step))
+    .sort((a, b) => a - b);
+}
+
 function getAutoRunStatusPayload(phase, payload = {}) {
   const currentRun = payload.currentRun ?? autoRunCurrentRun;
   const totalRuns = payload.totalRuns ?? autoRunTotalRuns;
@@ -1902,7 +1909,7 @@ function getAutoRunStatusPayload(phase, payload = {}) {
     ? (payload.scheduledAt ?? payload.scheduledAutoRunAt ?? null)
     : null;
   const scheduledAt = rawScheduledAt === null ? null : Number(rawScheduledAt);
-  const autoRunning = phase === 'scheduled' || phase === 'running' || phase === 'waiting_email' || phase === 'retrying';
+  const autoRunning = phase === 'scheduled' || phase === 'running' || phase === 'waiting_step' || phase === 'waiting_email' || phase === 'retrying';
 
   return {
     autoRunning,
@@ -1937,7 +1944,7 @@ async function broadcastAutoRunStatus(phase, payload = {}, extraState = {}) {
 }
 
 function isAutoRunLockedState(state) {
-  return Boolean(state.autoRunning) && (state.autoRunPhase === 'running' || state.autoRunPhase === 'retrying');
+  return Boolean(state.autoRunning) && (state.autoRunPhase === 'running' || state.autoRunPhase === 'waiting_step' || state.autoRunPhase === 'retrying');
 }
 
 function isAutoRunPausedState(state) {
@@ -2707,11 +2714,29 @@ async function completeStepFromBackground(step, payload = {}) {
   notifyStepComplete(step, payload);
 }
 
+async function waitForRunningStepsToFinish(payload = {}) {
+  let currentState = await getState();
+  let runningSteps = getRunningSteps(currentState.stepStatuses);
+  if (!runningSteps.length) {
+    return currentState;
+  }
+
+  await addLog(`自动继续:检测到步骤 ${runningSteps.join(', ')} 正在运行,等待完成后再继续自动流程...`, 'info');
+  await broadcastAutoRunStatus('waiting_step', payload);
+
+  while (runningSteps.length) {
+    await sleepWithStop(250);
+    currentState = await getState();
+    runningSteps = getRunningSteps(currentState.stepStatuses);
+  }
+
+  await addLog('自动继续:当前运行步骤已结束,准备按最新进度继续自动流程...', 'info');
+  return currentState;
+}
+
 async function markRunningStepsStopped() {
   const state = await getState();
-  const runningSteps = Object.entries(state.stepStatuses || {})
-    .filter(([, status]) => status === 'running')
-    .map(([step]) => Number(step));
+  const runningSteps = getRunningSteps(state.stepStatuses);
 
   for (const step of runningSteps) {
     await setStepStatus(step, 'stopped');
@@ -3102,10 +3127,14 @@ async function autoRunLoop(totalRuns, options = {}) {
   let attemptRuns = Math.max(0, resumeAttemptRunsProcessed);
   let forceFreshTabsNextRun = false;
   let continueCurrentOnFirstAttempt = initialMode === 'continue';
+  const initialState = await getState();
+  const initialPhase = continueCurrentOnFirstAttempt && getRunningSteps(initialState.stepStatuses).length
+    ? 'waiting_step'
+    : 'running';
 
   await setState({
     autoRunSkipFailures,
-    ...getAutoRunStatusPayload('running', {
+    ...getAutoRunStatusPayload(initialPhase, {
       currentRun: resumeCurrentRun,
       totalRuns,
       attemptRun: resumeAttemptRunsProcessed,
@@ -3121,7 +3150,14 @@ async function autoRunLoop(totalRuns, options = {}) {
     let useExistingProgress = false;
 
     if (continueCurrentOnFirstAttempt) {
-      const currentState = await getState();
+      let currentState = await getState();
+      if (getRunningSteps(currentState.stepStatuses).length) {
+        currentState = await waitForRunningStepsToFinish({
+          currentRun: targetRun,
+          totalRuns,
+          attemptRun: attemptRuns,
+        });
+      }
       const resumeStep = getFirstUnfinishedStep(currentState.stepStatuses);
       if (resumeStep && hasSavedProgress(currentState.stepStatuses)) {
         startStep = resumeStep;

+ 36 - 6
sidepanel/sidepanel.js

@@ -228,10 +228,14 @@ function openActionModal({ title, message, actions }) {
   });
 }
 
-function openAutoStartChoiceDialog(startStep) {
+function openAutoStartChoiceDialog(startStep, options = {}) {
+  const runningStep = Number.isInteger(options.runningStep) ? options.runningStep : null;
+  const continueMessage = runningStep
+    ? `继续当前会先等待步骤 ${runningStep} 完成,再按最新进度自动执行。`
+    : `继续当前会从步骤 ${startStep} 开始自动执行。`;
   return openActionModal({
     title: '启动自动',
-    message: `检测到当前已有流程进度。继续当前会从步骤 ${startStep} 开始自动执行,重新开始会清空当前流程进度并从步骤 1 新开一轮。`,
+    message: `检测到当前已有流程进度。${continueMessage}重新开始会清空当前流程进度并从步骤 1 新开一轮。`,
     actions: [
       { id: null, label: '取消', variant: 'btn-ghost' },
       { id: 'restart', label: '重新开始', variant: 'btn-outline' },
@@ -270,6 +274,14 @@ function getFirstUnfinishedStep(state = latestState) {
   return null;
 }
 
+function getRunningSteps(state = latestState) {
+  const statuses = getStepStatuses(state);
+  return Object.entries(statuses)
+    .filter(([, status]) => status === 'running')
+    .map(([step]) => Number(step))
+    .sort((a, b) => a - b);
+}
+
 function hasSavedProgress(state = latestState) {
   const statuses = getStepStatuses(state);
   return Object.values(statuses).some((status) => status !== 'pending');
@@ -296,7 +308,7 @@ function syncAutoRunState(source = {}) {
   const autoRunning = source.autoRunning !== undefined
     ? Boolean(source.autoRunning)
     : (source.autoRunPhase !== undefined || source.phase !== undefined
-      ? ['scheduled', 'running', 'waiting_email', 'retrying'].includes(phase)
+      ? ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying'].includes(phase)
       : currentAutoRun.autoRunning);
 
   currentAutoRun = {
@@ -310,13 +322,17 @@ function syncAutoRunState(source = {}) {
 }
 
 function isAutoRunLockedPhase() {
-  return currentAutoRun.phase === 'running' || currentAutoRun.phase === 'retrying';
+  return currentAutoRun.phase === 'running' || currentAutoRun.phase === 'waiting_step' || currentAutoRun.phase === 'retrying';
 }
 
 function isAutoRunPausedPhase() {
   return currentAutoRun.phase === 'waiting_email';
 }
 
+function isAutoRunWaitingStepPhase() {
+  return currentAutoRun.phase === 'waiting_step';
+}
+
 function isAutoRunScheduledPhase() {
   return currentAutoRun.phase === 'scheduled';
 }
@@ -615,6 +631,10 @@ function applyAutoRunStatus(payload = currentAutoRun) {
       autoContinueBar.style.display = 'none';
       btnAutoRun.innerHTML = `已计划${runLabel}`;
       break;
+    case 'waiting_step':
+      autoContinueBar.style.display = 'none';
+      btnAutoRun.innerHTML = `等待中${runLabel}`;
+      break;
     case 'waiting_email':
       autoContinueBar.style.display = 'flex';
       btnAutoRun.innerHTML = `已暂停${runLabel}`;
@@ -1192,6 +1212,15 @@ function updateStatusDisplay(state) {
     return;
   }
 
+  if (isAutoRunWaitingStepPhase()) {
+    const runningSteps = getRunningSteps(state);
+    displayStatus.textContent = runningSteps.length
+      ? `自动等待步骤 ${runningSteps.join(', ')} 完成后继续${getAutoRunLabel()}`
+      : `自动正在按最新进度准备继续${getAutoRunLabel()}`;
+    statusBar.classList.add('running');
+    return;
+  }
+
   const running = Object.entries(state.stepStatuses).find(([, s]) => s === 'running');
   if (running) {
     displayStatus.textContent = `步骤 ${running[0]} 运行中...`;
@@ -1765,7 +1794,8 @@ btnAutoRun.addEventListener('click', async () => {
 
     if (shouldOfferAutoModeChoice()) {
       const startStep = getFirstUnfinishedStep();
-      const choice = await openAutoStartChoiceDialog(startStep);
+      const runningStep = getRunningSteps()[0] ?? null;
+      const choice = await openAutoStartChoiceDialog(startStep, { runningStep });
       if (!choice) {
         return;
       }
@@ -2137,7 +2167,7 @@ chrome.runtime.onMessage.addListener((message) => {
 
     case 'AUTO_RUN_STATUS': {
       syncLatestState({
-        autoRunning: ['scheduled', 'running', 'waiting_email', 'retrying'].includes(message.payload.phase),
+        autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying'].includes(message.payload.phase),
         autoRunPhase: message.payload.phase,
         autoRunCurrentRun: message.payload.currentRun,
         autoRunTotalRuns: message.payload.totalRuns,