283 lines
7.8 KiB
TypeScript
283 lines
7.8 KiB
TypeScript
import type { MarketExportScope, MarketExportTarget } from "./types";
|
|
|
|
export interface PluginToolbarHandlers {
|
|
onApplyFilter(): Promise<void> | void;
|
|
onApplySort(): Promise<void> | void;
|
|
onExport(): Promise<void> | void;
|
|
}
|
|
|
|
export interface PluginToolbarDom {
|
|
exportButton: HTMLButtonElement;
|
|
exportCustomPagesInput: HTMLInputElement;
|
|
exportRangeSelect: HTMLSelectElement;
|
|
exportStatusText: HTMLElement;
|
|
filterApplyButton: HTMLButtonElement;
|
|
personalFilterInput: HTMLInputElement;
|
|
root: HTMLElement;
|
|
singleFilterInput: HTMLInputElement;
|
|
sortApplyButton: HTMLButtonElement;
|
|
sortDirectionSelect: HTMLSelectElement;
|
|
sortFieldSelect: HTMLSelectElement;
|
|
}
|
|
|
|
export function ensurePluginToolbar(
|
|
document: Document,
|
|
handlers: PluginToolbarHandlers
|
|
): PluginToolbarDom {
|
|
const existingRoot = document.querySelector(
|
|
"[data-plugin-toolbar='root']"
|
|
) as HTMLElement | null;
|
|
if (existingRoot) {
|
|
return readToolbarDom(existingRoot);
|
|
}
|
|
|
|
const root = document.createElement("section");
|
|
root.dataset.pluginToolbar = "root";
|
|
|
|
const singleFilterInput = document.createElement("input");
|
|
singleFilterInput.type = "number";
|
|
singleFilterInput.step = "0.01";
|
|
singleFilterInput.dataset.pluginFilterSingle = "input";
|
|
|
|
const personalFilterInput = document.createElement("input");
|
|
personalFilterInput.type = "number";
|
|
personalFilterInput.step = "0.01";
|
|
personalFilterInput.dataset.pluginFilterPersonal = "input";
|
|
|
|
const filterApplyButton = document.createElement("button");
|
|
filterApplyButton.type = "button";
|
|
filterApplyButton.dataset.pluginFilterApply = "button";
|
|
filterApplyButton.textContent = "应用筛选";
|
|
|
|
const sortFieldSelect = document.createElement("select");
|
|
sortFieldSelect.dataset.pluginSortField = "select";
|
|
appendOption(sortFieldSelect, "", "不排序");
|
|
appendOption(sortFieldSelect, "singleVideoAfterSearchRate", "单视频看后搜率");
|
|
appendOption(sortFieldSelect, "personalVideoAfterSearchRate", "个人视频看后搜率");
|
|
|
|
const sortDirectionSelect = document.createElement("select");
|
|
sortDirectionSelect.dataset.pluginSortDirection = "select";
|
|
appendOption(sortDirectionSelect, "desc", "降序");
|
|
appendOption(sortDirectionSelect, "asc", "升序");
|
|
|
|
const sortApplyButton = document.createElement("button");
|
|
sortApplyButton.type = "button";
|
|
sortApplyButton.dataset.pluginSortApply = "button";
|
|
sortApplyButton.textContent = "应用排序";
|
|
|
|
const exportButton = document.createElement("button");
|
|
exportButton.type = "button";
|
|
exportButton.dataset.pluginExport = "button";
|
|
exportButton.textContent = "导出CSV";
|
|
|
|
const exportRangeSelect = document.createElement("select");
|
|
exportRangeSelect.dataset.pluginExportRange = "select";
|
|
appendOption(exportRangeSelect, "current", "当前页");
|
|
appendOption(exportRangeSelect, "first-5", "前5页");
|
|
appendOption(exportRangeSelect, "first-10", "前10页");
|
|
appendOption(exportRangeSelect, "all", "全部");
|
|
appendOption(exportRangeSelect, "custom", "自定义");
|
|
exportRangeSelect.value = "first-5";
|
|
|
|
const exportCustomPagesInput = document.createElement("input");
|
|
exportCustomPagesInput.type = "number";
|
|
exportCustomPagesInput.min = "1";
|
|
exportCustomPagesInput.step = "1";
|
|
exportCustomPagesInput.hidden = true;
|
|
exportCustomPagesInput.dataset.pluginExportCustomPages = "input";
|
|
|
|
const exportStatusText = document.createElement("span");
|
|
exportStatusText.dataset.pluginExportStatus = "text";
|
|
|
|
root.append(
|
|
singleFilterInput,
|
|
personalFilterInput,
|
|
filterApplyButton,
|
|
sortFieldSelect,
|
|
sortDirectionSelect,
|
|
sortApplyButton,
|
|
exportRangeSelect,
|
|
exportCustomPagesInput,
|
|
exportButton
|
|
);
|
|
root.append(exportStatusText);
|
|
document.body.prepend(root);
|
|
|
|
filterApplyButton.addEventListener("click", () => {
|
|
void handlers.onApplyFilter();
|
|
});
|
|
sortApplyButton.addEventListener("click", () => {
|
|
void handlers.onApplySort();
|
|
});
|
|
exportButton.addEventListener("click", () => {
|
|
void handlers.onExport();
|
|
});
|
|
exportRangeSelect.addEventListener("change", () => {
|
|
syncCustomPagesInputVisibility({
|
|
exportButton,
|
|
exportCustomPagesInput,
|
|
exportRangeSelect,
|
|
exportStatusText,
|
|
filterApplyButton,
|
|
personalFilterInput,
|
|
root,
|
|
singleFilterInput,
|
|
sortApplyButton,
|
|
sortDirectionSelect,
|
|
sortFieldSelect
|
|
});
|
|
});
|
|
|
|
const toolbarDom = {
|
|
exportButton,
|
|
exportCustomPagesInput,
|
|
exportRangeSelect,
|
|
exportStatusText,
|
|
filterApplyButton,
|
|
personalFilterInput,
|
|
root,
|
|
singleFilterInput,
|
|
sortApplyButton,
|
|
sortDirectionSelect,
|
|
sortFieldSelect
|
|
} satisfies PluginToolbarDom;
|
|
syncCustomPagesInputVisibility(toolbarDom);
|
|
|
|
return toolbarDom;
|
|
}
|
|
|
|
function appendOption(
|
|
select: HTMLSelectElement,
|
|
value: string,
|
|
label: string
|
|
): void {
|
|
const option = select.ownerDocument.createElement("option");
|
|
option.value = value;
|
|
option.textContent = label;
|
|
select.appendChild(option);
|
|
}
|
|
|
|
function readToolbarDom(root: HTMLElement): PluginToolbarDom {
|
|
const toolbarDom = {
|
|
exportButton: root.querySelector(
|
|
'[data-plugin-export="button"]'
|
|
) as HTMLButtonElement,
|
|
exportCustomPagesInput: root.querySelector(
|
|
'[data-plugin-export-custom-pages="input"]'
|
|
) as HTMLInputElement,
|
|
exportRangeSelect: root.querySelector(
|
|
'[data-plugin-export-range="select"]'
|
|
) as HTMLSelectElement,
|
|
exportStatusText: root.querySelector(
|
|
'[data-plugin-export-status="text"]'
|
|
) as HTMLElement,
|
|
filterApplyButton: root.querySelector(
|
|
'[data-plugin-filter-apply="button"]'
|
|
) as HTMLButtonElement,
|
|
personalFilterInput: root.querySelector(
|
|
'[data-plugin-filter-personal="input"]'
|
|
) as HTMLInputElement,
|
|
root,
|
|
singleFilterInput: root.querySelector(
|
|
'[data-plugin-filter-single="input"]'
|
|
) as HTMLInputElement,
|
|
sortApplyButton: root.querySelector(
|
|
'[data-plugin-sort-apply="button"]'
|
|
) as HTMLButtonElement,
|
|
sortDirectionSelect: root.querySelector(
|
|
'[data-plugin-sort-direction="select"]'
|
|
) as HTMLSelectElement,
|
|
sortFieldSelect: root.querySelector(
|
|
'[data-plugin-sort-field="select"]'
|
|
) as HTMLSelectElement
|
|
} satisfies PluginToolbarDom;
|
|
syncCustomPagesInputVisibility(toolbarDom);
|
|
return toolbarDom;
|
|
}
|
|
|
|
export function readToolbarExportTarget(
|
|
toolbar: PluginToolbarDom
|
|
): { error?: string; target?: MarketExportTarget } {
|
|
const scope = toolbar.exportRangeSelect.value as MarketExportScope;
|
|
|
|
if (scope === "all") {
|
|
return {
|
|
target: {
|
|
mode: "all"
|
|
}
|
|
};
|
|
}
|
|
|
|
if (scope === "current") {
|
|
return {
|
|
target: {
|
|
mode: "count",
|
|
pageCount: 1
|
|
}
|
|
};
|
|
}
|
|
|
|
if (scope === "first-5") {
|
|
return {
|
|
target: {
|
|
mode: "count",
|
|
pageCount: 5
|
|
}
|
|
};
|
|
}
|
|
|
|
if (scope === "first-10") {
|
|
return {
|
|
target: {
|
|
mode: "count",
|
|
pageCount: 10
|
|
}
|
|
};
|
|
}
|
|
|
|
const pageCount = Number(toolbar.exportCustomPagesInput.value);
|
|
if (!Number.isInteger(pageCount) || pageCount < 1) {
|
|
return {
|
|
error: "请输入有效页数"
|
|
};
|
|
}
|
|
|
|
return {
|
|
target: {
|
|
mode: "count",
|
|
pageCount
|
|
}
|
|
};
|
|
}
|
|
|
|
export function setToolbarBusyState(
|
|
toolbar: PluginToolbarDom,
|
|
isBusy: boolean
|
|
): void {
|
|
[
|
|
toolbar.exportButton,
|
|
toolbar.filterApplyButton,
|
|
toolbar.sortApplyButton,
|
|
toolbar.singleFilterInput,
|
|
toolbar.personalFilterInput,
|
|
toolbar.sortFieldSelect,
|
|
toolbar.sortDirectionSelect,
|
|
toolbar.exportRangeSelect,
|
|
toolbar.exportCustomPagesInput
|
|
].forEach((element) => {
|
|
element.disabled = isBusy;
|
|
});
|
|
}
|
|
|
|
export function setToolbarExportStatus(
|
|
toolbar: PluginToolbarDom,
|
|
text: string
|
|
): void {
|
|
toolbar.exportStatusText.textContent = text;
|
|
}
|
|
|
|
function syncCustomPagesInputVisibility(toolbar: PluginToolbarDom): void {
|
|
toolbar.exportCustomPagesInput.hidden =
|
|
toolbar.exportRangeSelect.value !== "custom";
|
|
}
|