feat(sidepanel): 添加获取正在运行步骤的功能并更新自动执行逻辑
This commit is contained in:
+43
-7
@@ -1894,6 +1894,13 @@ function clearStopRequest() {
|
|||||||
stopRequested = false;
|
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 = {}) {
|
function getAutoRunStatusPayload(phase, payload = {}) {
|
||||||
const currentRun = payload.currentRun ?? autoRunCurrentRun;
|
const currentRun = payload.currentRun ?? autoRunCurrentRun;
|
||||||
const totalRuns = payload.totalRuns ?? autoRunTotalRuns;
|
const totalRuns = payload.totalRuns ?? autoRunTotalRuns;
|
||||||
@@ -1902,7 +1909,7 @@ function getAutoRunStatusPayload(phase, payload = {}) {
|
|||||||
? (payload.scheduledAt ?? payload.scheduledAutoRunAt ?? null)
|
? (payload.scheduledAt ?? payload.scheduledAutoRunAt ?? null)
|
||||||
: null;
|
: null;
|
||||||
const scheduledAt = rawScheduledAt === null ? null : Number(rawScheduledAt);
|
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 {
|
return {
|
||||||
autoRunning,
|
autoRunning,
|
||||||
@@ -1937,7 +1944,7 @@ async function broadcastAutoRunStatus(phase, payload = {}, extraState = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isAutoRunLockedState(state) {
|
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) {
|
function isAutoRunPausedState(state) {
|
||||||
@@ -2707,11 +2714,29 @@ async function completeStepFromBackground(step, payload = {}) {
|
|||||||
notifyStepComplete(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() {
|
async function markRunningStepsStopped() {
|
||||||
const state = await getState();
|
const state = await getState();
|
||||||
const runningSteps = Object.entries(state.stepStatuses || {})
|
const runningSteps = getRunningSteps(state.stepStatuses);
|
||||||
.filter(([, status]) => status === 'running')
|
|
||||||
.map(([step]) => Number(step));
|
|
||||||
|
|
||||||
for (const step of runningSteps) {
|
for (const step of runningSteps) {
|
||||||
await setStepStatus(step, 'stopped');
|
await setStepStatus(step, 'stopped');
|
||||||
@@ -3102,10 +3127,14 @@ async function autoRunLoop(totalRuns, options = {}) {
|
|||||||
let attemptRuns = Math.max(0, resumeAttemptRunsProcessed);
|
let attemptRuns = Math.max(0, resumeAttemptRunsProcessed);
|
||||||
let forceFreshTabsNextRun = false;
|
let forceFreshTabsNextRun = false;
|
||||||
let continueCurrentOnFirstAttempt = initialMode === 'continue';
|
let continueCurrentOnFirstAttempt = initialMode === 'continue';
|
||||||
|
const initialState = await getState();
|
||||||
|
const initialPhase = continueCurrentOnFirstAttempt && getRunningSteps(initialState.stepStatuses).length
|
||||||
|
? 'waiting_step'
|
||||||
|
: 'running';
|
||||||
|
|
||||||
await setState({
|
await setState({
|
||||||
autoRunSkipFailures,
|
autoRunSkipFailures,
|
||||||
...getAutoRunStatusPayload('running', {
|
...getAutoRunStatusPayload(initialPhase, {
|
||||||
currentRun: resumeCurrentRun,
|
currentRun: resumeCurrentRun,
|
||||||
totalRuns,
|
totalRuns,
|
||||||
attemptRun: resumeAttemptRunsProcessed,
|
attemptRun: resumeAttemptRunsProcessed,
|
||||||
@@ -3121,7 +3150,14 @@ async function autoRunLoop(totalRuns, options = {}) {
|
|||||||
let useExistingProgress = false;
|
let useExistingProgress = false;
|
||||||
|
|
||||||
if (continueCurrentOnFirstAttempt) {
|
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);
|
const resumeStep = getFirstUnfinishedStep(currentState.stepStatuses);
|
||||||
if (resumeStep && hasSavedProgress(currentState.stepStatuses)) {
|
if (resumeStep && hasSavedProgress(currentState.stepStatuses)) {
|
||||||
startStep = resumeStep;
|
startStep = resumeStep;
|
||||||
|
|||||||
+36
-6
@@ -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({
|
return openActionModal({
|
||||||
title: '启动自动',
|
title: '启动自动',
|
||||||
message: `检测到当前已有流程进度。继续当前会从步骤 ${startStep} 开始自动执行,重新开始会清空当前流程进度并从步骤 1 新开一轮。`,
|
message: `检测到当前已有流程进度。${continueMessage}重新开始会清空当前流程进度并从步骤 1 新开一轮。`,
|
||||||
actions: [
|
actions: [
|
||||||
{ id: null, label: '取消', variant: 'btn-ghost' },
|
{ id: null, label: '取消', variant: 'btn-ghost' },
|
||||||
{ id: 'restart', label: '重新开始', variant: 'btn-outline' },
|
{ id: 'restart', label: '重新开始', variant: 'btn-outline' },
|
||||||
@@ -270,6 +274,14 @@ function getFirstUnfinishedStep(state = latestState) {
|
|||||||
return null;
|
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) {
|
function hasSavedProgress(state = latestState) {
|
||||||
const statuses = getStepStatuses(state);
|
const statuses = getStepStatuses(state);
|
||||||
return Object.values(statuses).some((status) => status !== 'pending');
|
return Object.values(statuses).some((status) => status !== 'pending');
|
||||||
@@ -296,7 +308,7 @@ function syncAutoRunState(source = {}) {
|
|||||||
const autoRunning = source.autoRunning !== undefined
|
const autoRunning = source.autoRunning !== undefined
|
||||||
? Boolean(source.autoRunning)
|
? Boolean(source.autoRunning)
|
||||||
: (source.autoRunPhase !== undefined || source.phase !== undefined
|
: (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.autoRunning);
|
||||||
|
|
||||||
currentAutoRun = {
|
currentAutoRun = {
|
||||||
@@ -310,13 +322,17 @@ function syncAutoRunState(source = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isAutoRunLockedPhase() {
|
function isAutoRunLockedPhase() {
|
||||||
return currentAutoRun.phase === 'running' || currentAutoRun.phase === 'retrying';
|
return currentAutoRun.phase === 'running' || currentAutoRun.phase === 'waiting_step' || currentAutoRun.phase === 'retrying';
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAutoRunPausedPhase() {
|
function isAutoRunPausedPhase() {
|
||||||
return currentAutoRun.phase === 'waiting_email';
|
return currentAutoRun.phase === 'waiting_email';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAutoRunWaitingStepPhase() {
|
||||||
|
return currentAutoRun.phase === 'waiting_step';
|
||||||
|
}
|
||||||
|
|
||||||
function isAutoRunScheduledPhase() {
|
function isAutoRunScheduledPhase() {
|
||||||
return currentAutoRun.phase === 'scheduled';
|
return currentAutoRun.phase === 'scheduled';
|
||||||
}
|
}
|
||||||
@@ -615,6 +631,10 @@ function applyAutoRunStatus(payload = currentAutoRun) {
|
|||||||
autoContinueBar.style.display = 'none';
|
autoContinueBar.style.display = 'none';
|
||||||
btnAutoRun.innerHTML = `已计划${runLabel}`;
|
btnAutoRun.innerHTML = `已计划${runLabel}`;
|
||||||
break;
|
break;
|
||||||
|
case 'waiting_step':
|
||||||
|
autoContinueBar.style.display = 'none';
|
||||||
|
btnAutoRun.innerHTML = `等待中${runLabel}`;
|
||||||
|
break;
|
||||||
case 'waiting_email':
|
case 'waiting_email':
|
||||||
autoContinueBar.style.display = 'flex';
|
autoContinueBar.style.display = 'flex';
|
||||||
btnAutoRun.innerHTML = `已暂停${runLabel}`;
|
btnAutoRun.innerHTML = `已暂停${runLabel}`;
|
||||||
@@ -1192,6 +1212,15 @@ function updateStatusDisplay(state) {
|
|||||||
return;
|
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');
|
const running = Object.entries(state.stepStatuses).find(([, s]) => s === 'running');
|
||||||
if (running) {
|
if (running) {
|
||||||
displayStatus.textContent = `步骤 ${running[0]} 运行中...`;
|
displayStatus.textContent = `步骤 ${running[0]} 运行中...`;
|
||||||
@@ -1765,7 +1794,8 @@ btnAutoRun.addEventListener('click', async () => {
|
|||||||
|
|
||||||
if (shouldOfferAutoModeChoice()) {
|
if (shouldOfferAutoModeChoice()) {
|
||||||
const startStep = getFirstUnfinishedStep();
|
const startStep = getFirstUnfinishedStep();
|
||||||
const choice = await openAutoStartChoiceDialog(startStep);
|
const runningStep = getRunningSteps()[0] ?? null;
|
||||||
|
const choice = await openAutoStartChoiceDialog(startStep, { runningStep });
|
||||||
if (!choice) {
|
if (!choice) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2137,7 +2167,7 @@ chrome.runtime.onMessage.addListener((message) => {
|
|||||||
|
|
||||||
case 'AUTO_RUN_STATUS': {
|
case 'AUTO_RUN_STATUS': {
|
||||||
syncLatestState({
|
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,
|
autoRunPhase: message.payload.phase,
|
||||||
autoRunCurrentRun: message.payload.currentRun,
|
autoRunCurrentRun: message.payload.currentRun,
|
||||||
autoRunTotalRuns: message.payload.totalRuns,
|
autoRunTotalRuns: message.payload.totalRuns,
|
||||||
|
|||||||
Reference in New Issue
Block a user