feat(sidepanel): implement multi-select and delete functionality for account records

- Added a new feature to allow users to select multiple account records and delete them.
- Introduced a filter system to categorize account records based on their status (success, failed, stopped, retry).
- Enhanced the UI with a toolbar for selection and deletion actions, including a confirmation modal for deletions.
- Updated the CSS for better styling of the new toolbar and selection states.
- Implemented backend support for deleting selected records and syncing the remaining records.
- Added unit tests to ensure the new functionality works as expected.
This commit is contained in:
QLHazyCoder
2026-04-19 01:12:17 +08:00
parent 49707ffa74
commit 774ead30f7
9 changed files with 916 additions and 112 deletions
+54
View File
@@ -254,6 +254,30 @@
return normalizeAccountRunHistory(nextHistory);
}
function deleteAccountRunHistoryEntries(history, recordIds = []) {
const normalizedHistory = normalizeAccountRunHistory(history);
const normalizedIds = Array.isArray(recordIds)
? recordIds
.map((value) => String(value || '').trim().toLowerCase())
.filter(Boolean)
: [];
if (!normalizedIds.length) {
return {
deletedCount: 0,
nextHistory: normalizedHistory,
};
}
const selectedIds = new Set(normalizedIds);
const nextHistory = normalizedHistory.filter((record) => !selectedIds.has(buildRecordId(record.recordId || record.email)));
return {
deletedCount: normalizedHistory.length - nextHistory.length,
nextHistory: normalizeAccountRunHistory(nextHistory),
};
}
async function appendAccountRunHistoryRecord(status, stateOverride = null, reason = '') {
const state = stateOverride || await getState();
const record = buildAccountRunHistoryRecord(state, status, reason);
@@ -384,12 +408,42 @@
};
}
async function deleteAccountRunHistoryRecords(recordIds = [], stateOverride = null) {
const state = stateOverride || await getState();
const history = await getPersistedAccountRunHistory();
const { deletedCount, nextHistory } = deleteAccountRunHistoryEntries(history, recordIds);
if (!deletedCount) {
return {
deletedCount: 0,
remainingCount: history.length,
};
}
await setPersistedAccountRunHistory(nextHistory);
try {
const filePath = await syncAccountRunHistorySnapshot(nextHistory, state);
if (filePath) {
await addLog(`账号记录快照已同步到本地:${filePath}`, 'info');
}
} catch (err) {
await addLog(getErrorMessage(err), 'warn');
}
return {
deletedCount,
remainingCount: nextHistory.length,
};
}
return {
appendAccountRunRecord,
appendAccountRunHistoryRecord,
buildAccountRunHistoryRecord,
buildAccountRunHistorySnapshotPayload,
clearAccountRunHistory,
deleteAccountRunHistoryRecords,
getPersistedAccountRunHistory,
normalizeAccountRunHistory,
normalizeAccountRunHistoryRecord,
+14
View File
@@ -13,6 +13,7 @@
cancelScheduledAutoRun,
checkIcloudSession,
clearAccountRunHistory,
deleteAccountRunHistoryRecords,
clearAutoRunTimerAlarm,
clearLuckmailRuntimeState,
clearStopRequest,
@@ -300,6 +301,19 @@
return { ok: true, ...result };
}
case 'DELETE_ACCOUNT_RUN_HISTORY_RECORDS': {
const state = await getState();
if (isAutoRunLockedState(state)) {
throw new Error('自动流程运行中,当前不能删除邮箱记录。');
}
if (typeof deleteAccountRunHistoryRecords !== 'function') {
return { ok: true, deletedCount: 0, remainingCount: 0 };
}
const recordIds = Array.isArray(message.payload?.recordIds) ? message.payload.recordIds : [];
const result = await deleteAccountRunHistoryRecords(recordIds, state);
return { ok: true, ...result };
}
case 'EXECUTE_STEP': {
clearStopRequest();
if (message.source === 'sidepanel') {