const DIALOG_STYLE_ID = "sces-batch-name-dialog-style"; const activeDialogs = new WeakMap< Document, { input: HTMLInputElement; promise: Promise; } >(); export function promptForBatchName(document: Document): Promise { const existingDialog = activeDialogs.get(document); if (existingDialog) { existingDialog.input.focus(); existingDialog.input.select(); return existingDialog.promise; } ensureDialogStyles(document); const dialogRoot = document.createElement("div"); dialogRoot.dataset.pluginBatchNameDialog = "root"; dialogRoot.setAttribute("role", "dialog"); dialogRoot.setAttribute("aria-modal", "true"); dialogRoot.setAttribute("aria-labelledby", "sces-batch-name-title"); applyOverlayStyles(dialogRoot); const dialogPanel = document.createElement("div"); applyPanelStyles(dialogPanel); const title = document.createElement("h2"); title.id = "sces-batch-name-title"; title.textContent = "提交批次"; applyTitleStyles(title); const description = document.createElement("p"); description.textContent = "请输入批次名称,便于后续在系统中识别和追踪。"; applyDescriptionStyles(description); const input = document.createElement("input"); input.type = "text"; input.dataset.pluginBatchNameInput = "input"; input.placeholder = "例如:618达人筛选第一批"; input.maxLength = 60; applyInputStyles(input); const errorText = document.createElement("p"); errorText.dataset.pluginBatchNameError = "text"; applyErrorStyles(errorText); const buttonRow = document.createElement("div"); applyButtonRowStyles(buttonRow); const cancelButton = document.createElement("button"); cancelButton.type = "button"; cancelButton.dataset.pluginBatchNameCancel = "button"; cancelButton.textContent = "取消"; applySecondaryButtonStyles(cancelButton); const confirmButton = document.createElement("button"); confirmButton.type = "button"; confirmButton.dataset.pluginBatchNameConfirm = "button"; confirmButton.textContent = "确认提交"; applyPrimaryButtonStyles(confirmButton); buttonRow.append(cancelButton, confirmButton); dialogPanel.append(title, description, input, errorText, buttonRow); dialogRoot.appendChild(dialogPanel); document.body.appendChild(dialogRoot); const dialogPromise = new Promise((resolve) => { const closeDialog = (value: string | null) => { activeDialogs.delete(document); dialogRoot.remove(); document.removeEventListener("keydown", handleDocumentKeydown, true); resolve(value); }; const submitValue = () => { const value = input.value.trim(); if (!value) { errorText.textContent = "请输入批次名称"; input.setAttribute("aria-invalid", "true"); input.focus(); return; } closeDialog(value); }; const handleDocumentKeydown = (event: KeyboardEvent) => { if (event.key === "Escape") { event.preventDefault(); closeDialog(null); return; } if (event.key === "Enter") { event.preventDefault(); submitValue(); } }; input.addEventListener("input", () => { if (!input.value.trim()) { return; } errorText.textContent = ""; input.removeAttribute("aria-invalid"); }); cancelButton.addEventListener("click", () => { closeDialog(null); }); confirmButton.addEventListener("click", () => { submitValue(); }); dialogRoot.addEventListener("click", (event) => { if (event.target === dialogRoot) { closeDialog(null); } }); document.addEventListener("keydown", handleDocumentKeydown, true); }); activeDialogs.set(document, { input, promise: dialogPromise }); input.focus(); return dialogPromise; } function ensureDialogStyles(document: Document): void { if (document.getElementById(DIALOG_STYLE_ID)) { return; } const style = document.createElement("style"); style.id = DIALOG_STYLE_ID; style.textContent = ` [data-plugin-batch-name-dialog="root"] { animation: sces-batch-name-fade-in 0.16s ease; } @keyframes sces-batch-name-fade-in { from { opacity: 0; } to { opacity: 1; } } `; document.head.appendChild(style); } function applyOverlayStyles(root: HTMLElement): void { root.style.position = "fixed"; root.style.inset = "0"; root.style.background = "rgba(15, 23, 42, 0.38)"; root.style.display = "flex"; root.style.alignItems = "center"; root.style.justifyContent = "center"; root.style.padding = "24px"; root.style.zIndex = "2147483647"; } function applyPanelStyles(panel: HTMLElement): void { panel.style.width = "min(420px, calc(100vw - 32px))"; panel.style.background = "#fffaf9"; panel.style.border = "1px solid rgba(127, 29, 45, 0.14)"; panel.style.borderRadius = "18px"; panel.style.boxShadow = "0 28px 70px rgba(15, 23, 42, 0.22)"; panel.style.padding = "24px"; panel.style.boxSizing = "border-box"; } function applyTitleStyles(title: HTMLElement): void { title.style.margin = "0"; title.style.color = "#4c0519"; title.style.fontSize = "20px"; title.style.fontWeight = "700"; title.style.lineHeight = "28px"; } function applyDescriptionStyles(description: HTMLElement): void { description.style.margin = "10px 0 0"; description.style.color = "#64748b"; description.style.fontSize = "13px"; description.style.lineHeight = "20px"; } function applyInputStyles(input: HTMLInputElement): void { input.style.width = "100%"; input.style.height = "42px"; input.style.marginTop = "18px"; input.style.padding = "0 14px"; input.style.boxSizing = "border-box"; input.style.border = "1px solid #d8c1c6"; input.style.borderRadius = "12px"; input.style.background = "#ffffff"; input.style.color = "#1f2937"; input.style.fontSize = "14px"; input.style.outline = "none"; } function applyErrorStyles(errorText: HTMLElement): void { errorText.style.minHeight = "20px"; errorText.style.margin = "8px 0 0"; errorText.style.color = "#b91c1c"; errorText.style.fontSize = "12px"; errorText.style.lineHeight = "18px"; } function applyButtonRowStyles(buttonRow: HTMLElement): void { buttonRow.style.display = "flex"; buttonRow.style.justifyContent = "flex-end"; buttonRow.style.gap = "10px"; buttonRow.style.marginTop = "18px"; } function applySecondaryButtonStyles(button: HTMLButtonElement): void { button.style.height = "36px"; button.style.padding = "0 16px"; button.style.border = "1px solid #d7dde6"; button.style.borderRadius = "10px"; button.style.background = "#ffffff"; button.style.color = "#334155"; button.style.fontWeight = "600"; button.style.cursor = "pointer"; } function applyPrimaryButtonStyles(button: HTMLButtonElement): void { button.style.height = "36px"; button.style.padding = "0 16px"; button.style.border = "1px solid #7f1d2d"; button.style.borderRadius = "10px"; button.style.background = "#7f1d2d"; button.style.color = "#ffffff"; button.style.fontWeight = "600"; button.style.cursor = "pointer"; }