const $ = (id) => document.getElementById(id); function renderBodyPreview() { const raw = ($("body") && $("body").value) || ""; const el = $("bodyPreview"); if (!el) return; if (typeof marked !== "undefined" && marked.parse) { el.innerHTML = marked.parse(raw, { breaks: true }); } else { el.textContent = raw; } } const statusEl = $("status"); const rewriteBtn = $("rewriteBtn"); const wechatBtn = $("wechatBtn"); const imBtn = $("imBtn"); const coverUploadBtn = $("coverUploadBtn"); const coverUrlUploadBtn = $("coverUrlUploadBtn"); const coverGenerateBtn = $("coverGenerateBtn"); const saveCoverImageModelBtn = $("saveCoverImageModelBtn"); const coverImageModelInput = $("coverImageModel"); const coverModeManualBtn = $("coverModeManualBtn"); const coverModeAiBtn = $("coverModeAiBtn"); const coverManualSection = $("coverManualSection"); const coverAiSection = $("coverAiSection"); const coverAutoAfterRewrite = $("coverAutoAfterRewrite"); const coverPreview = $("coverPreview"); const coverPreviewWrap = $("coverPreviewWrap"); const logoutBtn = $("logoutBtn"); const clearDraftBtn = $("clearDraftBtn"); const targetBodyCharsInput = $("targetBodyChars"); const posterGenerateBtn = $("posterGenerateBtn"); const posterPreviewList = $("posterPreviewList"); const posterHint = $("posterHint"); const posterAutoInclude = $("posterAutoInclude"); const DRAFT_STORAGE_KEY = "aifagao:index:draft:v1"; let posterState = { signature: "", bodyMarkdownWithPosters: "", posters: [], }; let coverMode = "manual"; function setCoverMode(mode) { coverMode = mode === "ai" ? "ai" : "manual"; if (coverModeManualBtn) coverModeManualBtn.classList.toggle("is-active", coverMode === "manual"); if (coverModeAiBtn) coverModeAiBtn.classList.toggle("is-active", coverMode === "ai"); if (coverManualSection) { const hideManual = coverMode !== "manual"; coverManualSection.hidden = hideManual; coverManualSection.style.display = hideManual ? "none" : ""; } if (coverAiSection) { const hideAi = coverMode !== "ai"; coverAiSection.hidden = hideAi; coverAiSection.style.display = hideAi ? "none" : ""; } const hint = $("coverHint"); if (hint) { hint.textContent = coverMode === "manual" ? "当前为手动上传模式,可切换到 AI 自动生成。" : "当前为 AI 生成模式,也可切换回手动上传。"; } } function syncTargetCharChips() { const val = Number((targetBodyCharsInput && targetBodyCharsInput.value) || 0); document.querySelectorAll(".target-char-chip").forEach((btn) => { const n = Number(btn.getAttribute("data-target-chars") || 0); btn.classList.toggle("is-active", Number.isFinite(val) && val === n); }); } function countText(v) { return (v || "").trim().length; } /** 多选子项用顿号拼接,可选补充用分号接在末尾 */ function buildMultiPrompt(nameAttr, extraId) { const boxes = document.querySelectorAll(`input[name="${nameAttr}"]:checked`); const parts = Array.from(boxes).map((b) => (b.value || "").trim()).filter(Boolean); const extraEl = extraId ? $(extraId) : null; const extra = extraEl ? (extraEl.value || "").trim() : ""; let s = parts.join("、"); if (extra) s = s ? `${s};${extra}` : extra; return s; } function updateMultiDropdownSummary(nameAttr, summaryId, emptyHint) { const el = $(summaryId); if (!el) return; const parts = Array.from(document.querySelectorAll(`input[name="${nameAttr}"]:checked`)).map((b) => (b.value || "").trim(), ); el.textContent = parts.length ? parts.join("、") : emptyHint; } function initMultiDropdowns() { const pairs = [ { name: "audienceChip", summary: "audienceSummary", empty: "点击展开,选择目标读者…" }, { name: "toneChip", summary: "toneSummary", empty: "点击展开,选择语气风格…" }, ]; pairs.forEach(({ name, summary, empty }) => { updateMultiDropdownSummary(name, summary, empty); document.querySelectorAll(`input[name="${name}"]`).forEach((cb) => { cb.addEventListener("change", () => updateMultiDropdownSummary(name, summary, empty)); }); }); } function updateCounters() { $("sourceCount").textContent = `${countText($("sourceText").value)} 字`; $("summaryCount").textContent = `${countText($("summary").value)} 字`; $("bodyCount").textContent = `${countText($("body").value)} 字`; renderBodyPreview(); } function setLoading(button, loading, idleText, loadingText) { if (!button) return; button.disabled = loading; button.textContent = loading ? loadingText : idleText; } function setStatus(msg, danger = false) { statusEl.style.color = danger ? "#b42318" : "#0f5f3d"; statusEl.textContent = msg; } function saveDraftState() { try { const data = { sourceText: ($("sourceText") && $("sourceText").value) || "", titleHint: ($("titleHint") && $("titleHint").value) || "", audienceExtra: ($("audienceExtra") && $("audienceExtra").value) || "", toneExtra: ($("toneExtra") && $("toneExtra").value) || "", avoidWords: ($("avoidWords") && $("avoidWords").value) || "", keepPoints: ($("keepPoints") && $("keepPoints").value) || "", targetBodyChars: ($("targetBodyChars") && $("targetBodyChars").value) || "500", title: ($("title") && $("title").value) || "", summary: ($("summary") && $("summary").value) || "", body: ($("body") && $("body").value) || "", thumbMediaId: ($("thumbMediaId") && $("thumbMediaId").value) || "", coverStyleHint: ($("coverStyleHint") && $("coverStyleHint").value) || "", coverImageModel: (coverImageModelInput && coverImageModelInput.value) || "", coverAutoAfterRewrite: Boolean(coverAutoAfterRewrite && coverAutoAfterRewrite.checked), posterAutoInclude: Boolean(posterAutoInclude && posterAutoInclude.checked), audienceChipValues: Array.from(document.querySelectorAll('input[name="audienceChip"]:checked')).map((n) => n.value), toneChipValues: Array.from(document.querySelectorAll('input[name="toneChip"]:checked')).map((n) => n.value), }; window.localStorage.setItem(DRAFT_STORAGE_KEY, JSON.stringify(data)); } catch { // ignore } } function restoreDraftState() { try { const raw = window.localStorage.getItem(DRAFT_STORAGE_KEY); if (!raw) return; const data = JSON.parse(raw); if (!data || typeof data !== "object") return; const setVal = (id, val) => { const el = $(id); if (!el || typeof val !== "string") return; el.value = val; }; setVal("sourceText", data.sourceText || ""); setVal("titleHint", data.titleHint || ""); setVal("audienceExtra", data.audienceExtra || ""); setVal("toneExtra", data.toneExtra || ""); setVal("avoidWords", data.avoidWords || ""); setVal("keepPoints", data.keepPoints || ""); setVal("targetBodyChars", String(data.targetBodyChars || "500")); setVal("title", data.title || ""); setVal("summary", data.summary || ""); setVal("body", data.body || ""); setVal("thumbMediaId", data.thumbMediaId || ""); setVal("coverStyleHint", data.coverStyleHint || ""); if (coverImageModelInput && typeof data.coverImageModel === "string" && data.coverImageModel.trim()) { coverImageModelInput.value = data.coverImageModel; } if (coverAutoAfterRewrite) coverAutoAfterRewrite.checked = Boolean(data.coverAutoAfterRewrite); if (posterAutoInclude) posterAutoInclude.checked = Boolean(data.posterAutoInclude); const audienceSet = new Set(Array.isArray(data.audienceChipValues) ? data.audienceChipValues : []); document.querySelectorAll('input[name="audienceChip"]').forEach((el) => { el.checked = audienceSet.size ? audienceSet.has(el.value) : el.checked; }); const toneSet = new Set(Array.isArray(data.toneChipValues) ? data.toneChipValues : []); document.querySelectorAll('input[name="toneChip"]').forEach((el) => { el.checked = toneSet.size ? toneSet.has(el.value) : el.checked; }); } catch { // ignore } } function clearDraftState() { try { window.localStorage.removeItem(DRAFT_STORAGE_KEY); } catch { // ignore } const clearIds = [ "sourceText", "titleHint", "audienceExtra", "toneExtra", "avoidWords", "keepPoints", "title", "summary", "body", "thumbMediaId", "coverStyleHint", "coverUrl", ]; clearIds.forEach((id) => { const el = $(id); if (!el) return; el.value = ""; }); if ($("targetBodyChars")) $("targetBodyChars").value = "500"; if (coverAutoAfterRewrite) coverAutoAfterRewrite.checked = false; if (posterAutoInclude) posterAutoInclude.checked = false; if (coverImageModelInput) coverImageModelInput.value = ""; document.querySelectorAll('input[name="audienceChip"]').forEach((el) => { el.checked = false; }); document.querySelectorAll('input[name="toneChip"]').forEach((el) => { el.checked = false; }); if (coverPreviewWrap) coverPreviewWrap.hidden = true; if (coverPreview) coverPreview.src = ""; if ($("coverFile")) $("coverFile").value = ""; posterState = { signature: "", bodyMarkdownWithPosters: "", posters: [] }; renderPosterPreview([]); updateCounters(); syncTargetCharChips(); initMultiDropdowns(); initImageModelStatus(); if (posterHint) posterHint.textContent = "默认不生成海报,点击“生成段落海报”后再插入发布。"; setStatus("草稿已清除。"); } function buildPosterSignature() { const title = ($("title") && $("title").value.trim()) || ""; const summary = ($("summary") && $("summary").value.trim()) || ""; const body = ($("body") && $("body").value.trim()) || ""; return `${title}\n||\n${summary}\n||\n${body}`; } function renderPosterPreview(posters) { if (!posterPreviewList) return; posterPreviewList.innerHTML = ""; const list = Array.isArray(posters) ? posters : []; if (!list.length) { const empty = document.createElement("p"); empty.className = "muted small"; empty.textContent = "暂无段落海报预览。"; posterPreviewList.appendChild(empty); return; } list.forEach((item) => { const card = document.createElement("article"); card.className = "poster-card"; const img = document.createElement("img"); img.className = "poster-thumb"; img.alt = `段落 ${Number(item.paragraph_index || 0) + 1} 海报`; img.src = item.preview_data_url || ""; card.appendChild(img); const meta = document.createElement("div"); meta.className = "poster-meta"; const top = document.createElement("div"); top.className = "poster-topline"; top.textContent = `段落 ${Number(item.paragraph_index || 0) + 1} · ${item.note || "ai"}`; meta.appendChild(top); const excerpt = document.createElement("p"); excerpt.className = "poster-excerpt"; excerpt.textContent = item.paragraph_excerpt || ""; meta.appendChild(excerpt); if (item.wechat_url) { const link = document.createElement("a"); link.className = "poster-link"; link.href = item.wechat_url; link.target = "_blank"; link.rel = "noreferrer noopener"; link.textContent = "微信素材 URL"; meta.appendChild(link); } card.appendChild(meta); posterPreviewList.appendChild(card); }); } function markPosterStaleIfNeeded() { if (!posterState.signature || !posterHint) return; if (posterState.signature !== buildPosterSignature()) { posterHint.textContent = "正文已修改,如需海报请手动点击“生成段落海报”。"; } } async function postJSON(url, body) { const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const data = await res.json(); if (!res.ok) { const err = new Error(data.detail || "请求失败"); err.payload = data; throw err; } data._requestId = res.headers.get("X-Request-ID") || ""; return data; } function handleUpgradeRequired(err) { const data = (err && err.payload) || {}; const msg = (err && err.message) || data.detail || ""; if (!data.upgrade_required && !msg.includes("免费额度已用完") && !msg.includes("余额不足")) return false; setStatus("免费额度已用完,请前往升级页充值或升级 VIP 用户。", true); window.setTimeout(() => { window.location.href = "/upgrade"; }, 800); return true; } async function generatePosterMaterials({ silent = false } = {}) { const bodyMarkdown = (($("body") && $("body").value) || "").trim(); if (bodyMarkdown.length < 20) { throw new Error("正文太短,暂无法生成段落海报"); } if (!silent) setStatus("正在生成段落海报..."); if (posterHint) posterHint.textContent = "正在生成并上传段落海报..."; setLoading(posterGenerateBtn, true, "生成段落海报", "生成中..."); try { const data = await postJSON("/api/material/posters/generate", { title: $("title").value, summary: $("summary").value, body_markdown: $("body").value, image_model: (coverImageModelInput && coverImageModelInput.value.trim()) || "", upload_to_wechat: true, }); if (!data.ok) { const err = new Error(data.detail || "海报生成失败"); err.payload = data; throw err; } posterState = { signature: buildPosterSignature(), bodyMarkdownWithPosters: data.body_markdown_with_posters || $("body").value, posters: Array.isArray(data.posters) ? data.posters : [], }; renderPosterPreview(posterState.posters); const warnText = Array.isArray(data.warnings) && data.warnings.length ? `(提示:${data.warnings.join(";")})` : ""; if (posterHint) posterHint.textContent = `${data.detail || "海报生成完成"}${warnText}`; if (!silent) setStatus(`${data.detail || "海报生成完成"}${warnText}`); return data; } finally { setLoading(posterGenerateBtn, false, "生成段落海报", "生成中..."); } } function coverTitleForGeneration() { const generatedTitle = (($("title") && $("title").value) || "").trim(); const titleHint = (($("titleHint") && $("titleHint").value) || "").trim(); return generatedTitle || titleHint; } async function generateWechatCover({ silent = false } = {}) { const title = coverTitleForGeneration(); if (!title) { throw new Error("请先填写标题提示,或先改写生成标题"); } if (!silent) setStatus("正在按标题生成公众号封面..."); const hint = $("coverHint"); if (hint) hint.textContent = "正在生成 900×383 公众号封面并上传..."; setLoading(coverGenerateBtn, true, "按标题生成封面", "生成中..."); try { const data = await postJSON("/api/wechat/cover/generate", { title, summary: (($("summary") && $("summary").value) || "").trim(), style_hint: (($("coverStyleHint") && $("coverStyleHint").value) || "").trim(), image_model: (coverImageModelInput && coverImageModelInput.value.trim()) || "", upload_to_wechat: true, }); if (!data.ok) { const err = new Error(data.detail || "封面生成失败"); err.payload = data; throw err; } const mid = data.thumb_media_id || ""; if (mid && $("thumbMediaId")) $("thumbMediaId").value = mid; if (data.preview_data_url && coverPreview && coverPreviewWrap) { coverPreview.src = data.preview_data_url; coverPreviewWrap.hidden = false; } const warnText = Array.isArray(data.warnings) && data.warnings.length ? `(提示:${data.warnings.join(";")})` : ""; const detail = data.detail || "封面生成完成"; if (hint) hint.textContent = `${detail}${warnText}`; if (!silent) setStatus(`${detail}${warnText}`); return data; } finally { setLoading(coverGenerateBtn, false, "按标题生成封面", "生成中..."); } } async function fetchAuthMe() { const res = await fetch("/api/auth/me"); const data = await res.json(); if (!data.ok || !data.logged_in) { window.location.href = "/auth?next=/"; return null; } return data; } function renderWechatAccountSelect(me) { const sel = $("wechatAccountSelect"); const hint = $("wechatAccountStatus"); if (!sel) return; const list = Array.isArray(me.wechat_accounts) ? me.wechat_accounts : []; const activeId = me.active_wechat_account && me.active_wechat_account.id ? Number(me.active_wechat_account.id) : 0; sel.innerHTML = ""; if (!list.length) { const opt = document.createElement("option"); opt.value = ""; opt.textContent = "未绑定公众号"; sel.appendChild(opt); sel.disabled = true; if (hint) hint.textContent = "请先绑定公众号"; return; } sel.disabled = false; list.forEach((a) => { const opt = document.createElement("option"); opt.value = String(a.id); const name = (a.account_name || "未命名").trim(); const appid = (a.appid || "").trim(); opt.textContent = appid ? `${name} (${appid})` : name; if ((activeId && Number(a.id) === activeId) || a.active) opt.selected = true; sel.appendChild(opt); }); } function flashWechatAccountHint(msg, clearMs = 2600) { const hint = $("wechatAccountStatus"); if (!hint) return; hint.textContent = msg; if (clearMs > 0) { window.clearTimeout(flashWechatAccountHint._t); flashWechatAccountHint._t = window.setTimeout(() => { hint.textContent = ""; }, clearMs); } } const wechatAccountSelect = $("wechatAccountSelect"); if (wechatAccountSelect) { wechatAccountSelect.addEventListener("change", async () => { const id = Number(wechatAccountSelect.value || 0); if (!id) return; try { const out = await postJSON("/api/auth/wechat/switch", { account_id: id }); if (!out.ok) { flashWechatAccountHint(out.detail || "切换失败", 4000); const me = await fetchAuthMe(); if (me) renderWechatAccountSelect(me); return; } const me = await fetchAuthMe(); if (me) renderWechatAccountSelect(me); flashWechatAccountHint("已切换"); } catch (e) { flashWechatAccountHint(e.message || "切换失败", 4000); const me = await fetchAuthMe(); if (me) renderWechatAccountSelect(me); } }); } async function initWechatAccountSwitch() { const me = await fetchAuthMe(); if (me) renderWechatAccountSelect(me); } async function initImageModelStatus() { try { const me = await fetch("/api/auth/me").then((r) => r.json()); const active = me && me.active_ai_model ? me.active_ai_model : null; const imageModel = active && active.image_model ? String(active.image_model).trim() : ""; if (coverImageModelInput) { const current = (coverImageModelInput.value || "").trim(); if (!current) coverImageModelInput.value = imageModel || "wanx2.0-t2i-turbo"; } } catch { if (coverImageModelInput && !(coverImageModelInput.value || "").trim()) coverImageModelInput.value = "wanx2.0-t2i-turbo"; } } async function logoutAndGoAuth() { try { await postJSON("/api/auth/logout", {}); } catch { // 忽略退出接口异常,直接跳转认证页 } window.location.href = "/auth?next=/"; } if (logoutBtn) { logoutBtn.addEventListener("click", async () => { setLoading(logoutBtn, true, "退出登录", "退出中..."); await logoutAndGoAuth(); }); } if (clearDraftBtn) { clearDraftBtn.addEventListener("click", () => { clearDraftState(); }); } if (coverModeManualBtn) { coverModeManualBtn.addEventListener("click", () => setCoverMode("manual")); } if (coverModeAiBtn) { coverModeAiBtn.addEventListener("click", () => setCoverMode("ai")); } document.querySelectorAll(".target-char-chip").forEach((btn) => { btn.addEventListener("click", () => { const n = Number(btn.getAttribute("data-target-chars") || 0); if (!targetBodyCharsInput || !Number.isFinite(n) || n < 1) return; targetBodyCharsInput.value = String(n); syncTargetCharChips(); }); }); if (targetBodyCharsInput) { targetBodyCharsInput.addEventListener("input", syncTargetCharChips); } $("rewriteBtn").addEventListener("click", async () => { const sourceText = $("sourceText").value.trim(); const targetBodyChars = Number(($("targetBodyChars") && $("targetBodyChars").value) || 500); if (sourceText.length < 20) { setStatus("原始内容太短,至少 20 个字符", true); return; } if (!Number.isFinite(targetBodyChars) || targetBodyChars < 180 || targetBodyChars > 2200) { setStatus("改写目标字数需在 180~2200 之间", true); return; } setStatus("正在改写..."); setLoading(rewriteBtn, true, "改写并排版", "改写中..."); try { const audience = buildMultiPrompt("audienceChip", "audienceExtra") || "公众号读者"; const tone = buildMultiPrompt("toneChip", "toneExtra") || "专业、可信、可读性强"; const data = await postJSON("/api/rewrite", { source_text: sourceText, title_hint: $("titleHint").value, tone, audience, keep_points: $("keepPoints").value, avoid_words: $("avoidWords").value, target_body_chars: Math.round(targetBodyChars), }); $("title").value = data.title || ""; $("summary").value = data.summary || ""; $("body").value = data.body_markdown || ""; updateCounters(); saveDraftState(); const tr = data.trace || {}; if (data.mode === "fallback") { const note = (data.quality_notes || [])[0] || "当前为保底改写稿"; setStatus(`改写完成(保底模式):${note}`, true); } else if (tr.quality_soft_accept) { setStatus(`改写完成(有提示):${(data.quality_notes || []).join(";") || "请检查正文"}`); statusEl.style.color = "#9a3412"; } else { setStatus("改写完成。"); } if (posterHint) posterHint.textContent = "改写完成。默认不自动生成海报,可手动点击“生成段落海报”。"; if (coverAutoAfterRewrite && coverAutoAfterRewrite.checked) { try { setStatus("改写完成,正在按输出标题生成封面..."); await generateWechatCover({ silent: true }); setStatus("改写、封面与段落海报生成完成。"); } catch (coverErr) { if (handleUpgradeRequired(coverErr)) return; setStatus(`改写完成,封面未生成:${coverErr.message}`, true); } } } catch (e) { if (handleUpgradeRequired(e)) return; setStatus(`改写失败: ${e.message}`, true); } finally { setLoading(rewriteBtn, false, "改写并排版", "改写中..."); } }); $("wechatBtn").addEventListener("click", async () => { setStatus("正在发布到公众号草稿箱..."); setLoading(wechatBtn, true, "发布到公众号草稿箱", "发布中..."); try { let bodyForPublish = $("body").value; const autoInclude = Boolean(posterAutoInclude && posterAutoInclude.checked); if (autoInclude) { const stale = posterState.signature !== buildPosterSignature() || !posterState.bodyMarkdownWithPosters; if (!stale && posterState.bodyMarkdownWithPosters) { bodyForPublish = posterState.bodyMarkdownWithPosters; } else { setStatus("未检测到可用海报,本次仅发布文字;如需海报请先手动生成。", true); } } const data = await postJSON("/api/publish/wechat", { title: $("title").value, summary: $("summary").value, body_markdown: bodyForPublish, thumb_media_id: $("thumbMediaId") ? $("thumbMediaId").value.trim() : "", }); if (!data.ok) throw new Error(data.detail); setStatus("公众号草稿发布成功"); } catch (e) { setStatus(`公众号发布失败: ${e.message}`, true); if ((e.message || "").includes("请先登录")) { window.location.href = "/auth?next=/"; } else if ((e.message || "").includes("未绑定公众号")) { window.location.href = "/settings"; } } finally { setLoading(wechatBtn, false, "发布到公众号草稿箱", "发布中..."); } }); if (coverGenerateBtn) { coverGenerateBtn.addEventListener("click", async () => { try { await generateWechatCover({ silent: false }); saveDraftState(); } catch (e) { if (handleUpgradeRequired(e)) return; const hint = $("coverHint"); if (hint) hint.textContent = "AI 封面生成失败,请检查标题、模型或公众号配置。"; setStatus(`封面生成失败: ${e.message}`, true); if ((e.message || "").includes("请先登录")) { window.location.href = "/auth?next=/"; } else if ((e.message || "").includes("未绑定公众号")) { window.location.href = "/settings"; } } }); } if (saveCoverImageModelBtn) { saveCoverImageModelBtn.addEventListener("click", async () => { const value = (coverImageModelInput && coverImageModelInput.value.trim()) || ""; if (!value) { setStatus("请先填写文生图模型", true); return; } setLoading(saveCoverImageModelBtn, true, "保存模型", "保存中..."); try { const out = await postJSON("/api/auth/ai-models/image-model/update", { image_model: value }); if (!out.ok) { setStatus(out.detail || "保存失败", true); return; } setStatus("文生图模型已保存。"); saveDraftState(); } catch (e) { setStatus(e.message || "保存失败", true); } finally { setLoading(saveCoverImageModelBtn, false, "保存模型", "保存中..."); } }); } if (coverUploadBtn) { coverUploadBtn.addEventListener("click", async () => { const fileInput = $("coverFile"); const hint = $("coverHint"); const file = fileInput && fileInput.files && fileInput.files[0]; if (!file) { setStatus("请先选择封面图片再上传", true); return; } if (hint) hint.textContent = "正在上传封面..."; setLoading(coverUploadBtn, true, "上传封面并绑定", "上传中..."); try { const fd = new FormData(); fd.append("file", file); const res = await fetch("/api/wechat/cover/upload", { method: "POST", body: fd }); const data = await res.json(); if (!res.ok || !data.ok) throw new Error(data.detail || "封面上传失败"); const mid = data.data && data.data.thumb_media_id ? data.data.thumb_media_id : ""; if ($("thumbMediaId")) $("thumbMediaId").value = mid; if (hint) hint.textContent = `封面上传成功,已绑定 media_id:${mid}`; setStatus("封面上传成功,发布时将优先使用该封面。"); saveDraftState(); } catch (e) { if (hint) hint.textContent = "封面上传失败,请看状态提示。"; setStatus(`封面上传失败: ${e.message}`, true); if ((e.message || "").includes("请先登录")) { window.location.href = "/auth?next=/"; } else if ((e.message || "").includes("未绑定公众号")) { window.location.href = "/settings"; } } finally { setLoading(coverUploadBtn, false, "上传封面并绑定", "上传中..."); } }); } if (coverUrlUploadBtn) { coverUrlUploadBtn.addEventListener("click", async () => { const hint = $("coverHint"); const imageUrl = (($("coverUrl") && $("coverUrl").value) || "").trim(); if (!imageUrl) { setStatus("请先粘贴图片 URL", true); return; } if (hint) hint.textContent = "正在下载并上传 URL 图片..."; setLoading(coverUrlUploadBtn, true, "URL 上传并绑定", "上传中..."); try { const data = await postJSON("/api/wechat/cover/upload-by-url", { image_url: imageUrl }); if (!data.ok) throw new Error(data.detail || "URL 封面上传失败"); const mid = data.data && data.data.thumb_media_id ? data.data.thumb_media_id : ""; if ($("thumbMediaId")) $("thumbMediaId").value = mid; if (hint) hint.textContent = `URL 封面上传成功,已绑定 media_id:${mid}`; setStatus("URL 封面上传成功,发布时将优先使用该封面。"); if ($("coverUrl")) $("coverUrl").value = ""; saveDraftState(); } catch (e) { if (hint) hint.textContent = "URL 封面上传失败,请看状态提示。"; setStatus(`URL 封面上传失败: ${e.message}`, true); if ((e.message || "").includes("请先登录")) { window.location.href = "/auth?next=/"; } else if ((e.message || "").includes("未绑定公众号")) { window.location.href = "/settings"; } } finally { setLoading(coverUrlUploadBtn, false, "URL 上传并绑定", "上传中..."); } }); } if (posterGenerateBtn) { posterGenerateBtn.addEventListener("click", async () => { try { await generatePosterMaterials({ silent: false }); saveDraftState(); } catch (e) { if (handleUpgradeRequired(e)) return; setStatus(`海报生成失败: ${e.message}`, true); if (posterHint) posterHint.textContent = "海报生成失败,请检查配置后重试。"; if ((e.message || "").includes("请先登录")) { window.location.href = "/auth?next=/"; } else if ((e.message || "").includes("未绑定公众号")) { window.location.href = "/settings"; } } }); } $("imBtn").addEventListener("click", async () => { setStatus("正在发送到 IM..."); setLoading(imBtn, true, "发送到 IM", "发送中..."); try { const data = await postJSON("/api/publish/im", { title: $("title").value, body_markdown: $("body").value, }); if (!data.ok) throw new Error(data.detail); setStatus("IM 发送成功"); } catch (e) { setStatus(`IM 发送失败: ${e.message}`, true); } finally { setLoading(imBtn, false, "发送到 IM", "发送中..."); } }); ["sourceText", "title", "summary", "body"].forEach((id) => { $(id).addEventListener("input", updateCounters); $(id).addEventListener("input", saveDraftState); if (id !== "sourceText") $(id).addEventListener("input", markPosterStaleIfNeeded); }); ["titleHint", "audienceExtra", "toneExtra", "avoidWords", "keepPoints", "targetBodyChars", "thumbMediaId", "coverStyleHint"].forEach( (id) => { const el = $(id); if (el) el.addEventListener("input", saveDraftState); }, ); document.querySelectorAll('input[name="audienceChip"],input[name="toneChip"]').forEach((el) => { el.addEventListener("change", saveDraftState); }); if (coverAutoAfterRewrite) coverAutoAfterRewrite.addEventListener("change", saveDraftState); if (posterAutoInclude) posterAutoInclude.addEventListener("change", saveDraftState); if (coverImageModelInput) coverImageModelInput.addEventListener("input", saveDraftState); restoreDraftState(); updateCounters(); initMultiDropdowns(); initWechatAccountSwitch(); syncTargetCharChips(); renderPosterPreview([]); setCoverMode("manual"); initImageModelStatus(); window.addEventListener("beforeunload", saveDraftState); window.addEventListener("load", () => setCoverMode("manual"));