feat: 添加自动运行风险提示功能,优化用户确认流程
This commit is contained in:
+102
-21
@@ -131,6 +131,9 @@ const AUTO_STEP_DELAY_MIN_SECONDS = 0;
|
|||||||
const AUTO_STEP_DELAY_MAX_SECONDS = 600;
|
const AUTO_STEP_DELAY_MAX_SECONDS = 600;
|
||||||
const DEFAULT_LOCAL_CPA_STEP9_MODE = 'submit';
|
const DEFAULT_LOCAL_CPA_STEP9_MODE = 'submit';
|
||||||
const AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-auto-skip-failures-prompt-dismissed';
|
const AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-auto-skip-failures-prompt-dismissed';
|
||||||
|
const AUTO_RUN_FALLBACK_RISK_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-auto-run-fallback-risk-prompt-dismissed';
|
||||||
|
const AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS = 15;
|
||||||
|
const AUTO_RUN_FALLBACK_RISK_RECOMMENDED_THREAD_INTERVAL_MINUTES = 5;
|
||||||
|
|
||||||
let latestState = null;
|
let latestState = null;
|
||||||
let currentAutoRun = {
|
let currentAutoRun = {
|
||||||
@@ -351,29 +354,26 @@ async function openConfirmModal({ title, message, confirmLabel = '确认', confi
|
|||||||
return choice === 'confirm';
|
return choice === 'confirm';
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAutoSkipFailuresPromptDismissed() {
|
async function openConfirmModalWithOption({
|
||||||
return localStorage.getItem(AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY) === '1';
|
title,
|
||||||
}
|
message,
|
||||||
|
confirmLabel = '确认',
|
||||||
function setAutoSkipFailuresPromptDismissed(dismissed) {
|
confirmVariant = 'btn-primary',
|
||||||
if (dismissed) {
|
optionLabel = '不再提示',
|
||||||
localStorage.setItem(AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY, '1');
|
optionChecked = false,
|
||||||
} else {
|
optionDisabled = false,
|
||||||
localStorage.removeItem(AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY);
|
}) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function openAutoSkipFailuresConfirmModal() {
|
|
||||||
const result = await openActionModal({
|
const result = await openActionModal({
|
||||||
title: '兜底说明',
|
title,
|
||||||
message: '开启后,当某一轮失败且无法继续时,会直接放弃当前线程,重新开新一轮,直到补足目标运行次数。线程间隔只在开启兜底且运行次数大于 1 时生效。',
|
message,
|
||||||
actions: [
|
actions: [
|
||||||
{ id: null, label: '取消', variant: 'btn-ghost' },
|
{ id: null, label: '取消', variant: 'btn-ghost' },
|
||||||
{ id: 'confirm', label: '确认开启', variant: 'btn-primary' },
|
{ id: 'confirm', label: confirmLabel, variant: confirmVariant },
|
||||||
],
|
],
|
||||||
option: {
|
option: {
|
||||||
label: '不再提示',
|
label: optionLabel,
|
||||||
checked: false,
|
checked: optionChecked,
|
||||||
|
disabled: optionDisabled,
|
||||||
},
|
},
|
||||||
buildResult: (choice, meta) => ({
|
buildResult: (choice, meta) => ({
|
||||||
choice,
|
choice,
|
||||||
@@ -383,7 +383,69 @@ async function openAutoSkipFailuresConfirmModal() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
confirmed: result?.choice === 'confirm',
|
confirmed: result?.choice === 'confirm',
|
||||||
dismissPrompt: Boolean(result?.optionChecked),
|
optionChecked: Boolean(result?.optionChecked),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPromptDismissed(storageKey) {
|
||||||
|
return localStorage.getItem(storageKey) === '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPromptDismissed(storageKey, dismissed) {
|
||||||
|
if (dismissed) {
|
||||||
|
localStorage.setItem(storageKey, '1');
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(storageKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAutoSkipFailuresPromptDismissed() {
|
||||||
|
return isPromptDismissed(AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAutoSkipFailuresPromptDismissed(dismissed) {
|
||||||
|
setPromptDismissed(AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY, dismissed);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAutoRunFallbackRiskPromptDismissed() {
|
||||||
|
return isPromptDismissed(AUTO_RUN_FALLBACK_RISK_PROMPT_DISMISSED_STORAGE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAutoRunFallbackRiskPromptDismissed(dismissed) {
|
||||||
|
setPromptDismissed(AUTO_RUN_FALLBACK_RISK_PROMPT_DISMISSED_STORAGE_KEY, dismissed);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openAutoSkipFailuresConfirmModal() {
|
||||||
|
const result = await openConfirmModalWithOption({
|
||||||
|
title: '兜底说明',
|
||||||
|
message: '开启后,当某一轮失败且无法继续时,会直接放弃当前线程,重新开新一轮,直到补足目标运行次数。线程间隔只在开启兜底且运行次数大于 1 时生效。',
|
||||||
|
confirmLabel: '确认开启',
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
confirmed: result.confirmed,
|
||||||
|
dismissPrompt: result.optionChecked,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldWarnAutoRunFallbackRisk(totalRuns, autoRunSkipFailures) {
|
||||||
|
return Boolean(autoRunSkipFailures) && totalRuns >= AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openAutoRunFallbackRiskConfirmModal(totalRuns, fallbackThreadIntervalMinutes) {
|
||||||
|
const intervalLabel = Number.isFinite(fallbackThreadIntervalMinutes)
|
||||||
|
? `${fallbackThreadIntervalMinutes} 分钟`
|
||||||
|
: '未设置';
|
||||||
|
|
||||||
|
const result = await openConfirmModalWithOption({
|
||||||
|
title: '自动运行风险提醒',
|
||||||
|
message: `当前设置为 ${totalRuns} 轮自动化,已开启兜底,线程间隔为 ${intervalLabel}。次数太多,可能会因为 IP 短时间注册过多原因而都失败。建议控制在 ${AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS} 轮以下,并将线程间隔设置在 ${AUTO_RUN_FALLBACK_RISK_RECOMMENDED_THREAD_INTERVAL_MINUTES} 分钟以上。是否继续?`,
|
||||||
|
confirmLabel: '继续',
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
confirmed: result.confirmed,
|
||||||
|
dismissPrompt: result.optionChecked,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2488,7 +2550,7 @@ btnAutoStartClose?.addEventListener('click', () => resolveModalChoice(null));
|
|||||||
// Auto Run
|
// Auto Run
|
||||||
btnAutoRun.addEventListener('click', async () => {
|
btnAutoRun.addEventListener('click', async () => {
|
||||||
try {
|
try {
|
||||||
const totalRuns = Math.min(50, Math.max(1, parseInt(inputRunCount.value, 10) || 1));
|
const totalRuns = getRunCountValue();
|
||||||
let mode = 'restart';
|
let mode = 'restart';
|
||||||
|
|
||||||
if (shouldOfferAutoModeChoice()) {
|
if (shouldOfferAutoModeChoice()) {
|
||||||
@@ -2501,6 +2563,25 @@ btnAutoRun.addEventListener('click', async () => {
|
|||||||
mode = choice;
|
mode = choice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const autoRunSkipFailures = inputAutoSkipFailures.checked;
|
||||||
|
const fallbackThreadIntervalMinutes = normalizeAutoRunThreadIntervalMinutes(
|
||||||
|
inputAutoSkipFailuresThreadIntervalMinutes.value
|
||||||
|
);
|
||||||
|
inputAutoSkipFailuresThreadIntervalMinutes.value = String(fallbackThreadIntervalMinutes);
|
||||||
|
|
||||||
|
if (
|
||||||
|
shouldWarnAutoRunFallbackRisk(totalRuns, autoRunSkipFailures)
|
||||||
|
&& !isAutoRunFallbackRiskPromptDismissed()
|
||||||
|
) {
|
||||||
|
const result = await openAutoRunFallbackRiskConfirmModal(totalRuns, fallbackThreadIntervalMinutes);
|
||||||
|
if (!result.confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.dismissPrompt) {
|
||||||
|
setAutoRunFallbackRiskPromptDismissed(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
btnAutoRun.disabled = true;
|
btnAutoRun.disabled = true;
|
||||||
inputRunCount.disabled = true;
|
inputRunCount.disabled = true;
|
||||||
const delayEnabled = inputAutoDelayEnabled.checked;
|
const delayEnabled = inputAutoDelayEnabled.checked;
|
||||||
@@ -2515,7 +2596,7 @@ btnAutoRun.addEventListener('click', async () => {
|
|||||||
payload: {
|
payload: {
|
||||||
totalRuns,
|
totalRuns,
|
||||||
delayMinutes,
|
delayMinutes,
|
||||||
autoRunSkipFailures: inputAutoSkipFailures.checked,
|
autoRunSkipFailures,
|
||||||
mode,
|
mode,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user