feat: add batch submit toolbar action

This commit is contained in:
2026-04-22 13:54:51 +08:00
parent b75755e6a6
commit 3c672f8355
3 changed files with 120 additions and 20 deletions
+42 -14
View File
@@ -117,6 +117,9 @@ export function createMarketController(options: CreateMarketControllerOptions) {
} finally {
setToolbarBusyState(toolbar, false);
}
},
onSubmitBatch: async () => {
setToolbarExportStatus(toolbar, "批次提交功能开发中");
}
});
@@ -366,56 +369,81 @@ export function createMarketController(options: CreateMarketControllerOptions) {
let previousFingerprint = "";
let stablePassCount = 0;
for (let attempt = 0; attempt < 12; attempt += 1) {
for (let attempt = 0; attempt < 9; attempt += 1) {
await waitForDomSettled();
if (attempt > 0) {
await new Promise<void>((resolve) => {
options.window.setTimeout(resolve, 100);
options.window.setTimeout(
resolve,
previousFingerprint.includes("|missing:0") ? 25 : 50
);
});
await Promise.resolve();
}
collectCurrentPageSnapshots();
const nextFingerprint = readVisibleRowHydrationFingerprint();
if (!nextFingerprint) {
const hydrationSnapshot = readVisibleRowHydrationSnapshot();
if (!hydrationSnapshot.fingerprint) {
stablePassCount = 0;
previousFingerprint = "";
continue;
}
if (nextFingerprint === previousFingerprint) {
if (hydrationSnapshot.fingerprint === previousFingerprint) {
stablePassCount += 1;
} else {
previousFingerprint = nextFingerprint;
previousFingerprint = hydrationSnapshot.fingerprint;
stablePassCount = 1;
}
if (stablePassCount >= 3) {
if (hydrationSnapshot.missingDefaultFieldCount === 0 && stablePassCount >= 2) {
return;
}
}
}
function readVisibleRowHydrationFingerprint(): string {
function readVisibleRowHydrationSnapshot(): {
fingerprint: string;
missingDefaultFieldCount: number;
} {
const table = syncMarketTable(options.document);
if (!table || table.rows.length === 0) {
return "";
return {
fingerprint: "",
missingDefaultFieldCount: 0
};
}
return table.rows
.map((rowDom) => {
const parts = table.rows.map((rowDom) => {
const rowSnapshot = readRowSnapshot(rowDom);
const populatedFieldCount = Object.values(rowSnapshot.exportFields ?? {}).filter(
(value) => typeof value === "string" && value.trim().length > 0
).length;
const hasRepresentativeVideo = hasTextValue(
rowSnapshot.exportFields?.["代表视频"]
);
const hasPriceField =
hasTextValue(rowSnapshot.price21To60s) ||
hasTextValue(rowSnapshot.exportFields?.["21-60s报价"]);
const missingDefaultFieldCount =
Number(!hasRepresentativeVideo) + Number(!hasPriceField);
return [
rowSnapshot.authorId,
populatedFieldCount,
rowSnapshot.price21To60s?.trim() ? "price" : "no-price"
hasRepresentativeVideo ? "video" : "no-video",
hasPriceField ? "price" : "no-price",
`missing:${missingDefaultFieldCount}`
].join(":");
})
.join("|");
});
return {
fingerprint: parts.join("|"),
missingDefaultFieldCount: parts.reduce((count, part) => {
const match = part.match(/missing:(\d+)$/);
return count + Number(match?.[1] ?? 0);
}, 0)
};
}
function scheduleSync(): void {
+18 -1
View File
@@ -4,9 +4,11 @@ export interface PluginToolbarHandlers {
onApplyFilter(): Promise<void> | void;
onApplySort(): Promise<void> | void;
onExport(): Promise<void> | void;
onSubmitBatch(): Promise<void> | void;
}
export interface PluginToolbarDom {
batchSubmitButton: HTMLButtonElement;
exportButton: HTMLButtonElement;
exportCustomPagesInput: HTMLInputElement;
exportRangeSelect: HTMLSelectElement;
@@ -70,6 +72,11 @@ export function ensurePluginToolbar(
exportButton.dataset.pluginExport = "button";
exportButton.textContent = "导出CSV";
const batchSubmitButton = document.createElement("button");
batchSubmitButton.type = "button";
batchSubmitButton.dataset.pluginBatchSubmit = "button";
batchSubmitButton.textContent = "提交批次";
const exportRangeSelect = document.createElement("select");
exportRangeSelect.dataset.pluginExportRange = "select";
appendOption(exportRangeSelect, "current", "当前页");
@@ -98,7 +105,8 @@ export function ensurePluginToolbar(
sortApplyButton,
exportRangeSelect,
exportCustomPagesInput,
exportButton
exportButton,
batchSubmitButton
);
root.append(exportStatusText);
document.body.prepend(root);
@@ -112,8 +120,12 @@ export function ensurePluginToolbar(
exportButton.addEventListener("click", () => {
void handlers.onExport();
});
batchSubmitButton.addEventListener("click", () => {
void handlers.onSubmitBatch();
});
exportRangeSelect.addEventListener("change", () => {
syncCustomPagesInputVisibility({
batchSubmitButton,
exportButton,
exportCustomPagesInput,
exportRangeSelect,
@@ -129,6 +141,7 @@ export function ensurePluginToolbar(
});
const toolbarDom = {
batchSubmitButton,
exportButton,
exportCustomPagesInput,
exportRangeSelect,
@@ -159,6 +172,9 @@ function appendOption(
function readToolbarDom(root: HTMLElement): PluginToolbarDom {
const toolbarDom = {
batchSubmitButton: root.querySelector(
'[data-plugin-batch-submit="button"]'
) as HTMLButtonElement,
exportButton: root.querySelector(
'[data-plugin-export="button"]'
) as HTMLButtonElement,
@@ -255,6 +271,7 @@ export function setToolbarBusyState(
isBusy: boolean
): void {
[
toolbar.batchSubmitButton,
toolbar.exportButton,
toolbar.filterApplyButton,
toolbar.sortApplyButton,