623 lines
22 KiB
JavaScript
623 lines
22 KiB
JavaScript
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 coverModeManualBtn = $("coverModeManualBtn");
|
||
const coverModeAiBtn = $("coverModeAiBtn");
|
||
const coverManualSection = $("coverManualSection");
|
||
const coverAiSection = $("coverAiSection");
|
||
const coverAutoAfterRewrite = $("coverAutoAfterRewrite");
|
||
const coverPreview = $("coverPreview");
|
||
const coverPreviewWrap = $("coverPreviewWrap");
|
||
const logoutBtn = $("logoutBtn");
|
||
const targetBodyCharsInput = $("targetBodyChars");
|
||
const posterGenerateBtn = $("posterGenerateBtn");
|
||
const posterPreviewList = $("posterPreviewList");
|
||
const posterHint = $("posterHint");
|
||
const posterAutoInclude = $("posterAutoInclude");
|
||
|
||
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 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) throw new Error(data.detail || "请求失败");
|
||
data._requestId = res.headers.get("X-Request-ID") || "";
|
||
return data;
|
||
}
|
||
|
||
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,
|
||
upload_to_wechat: true,
|
||
});
|
||
if (!data.ok) throw new Error(data.detail || "海报生成失败");
|
||
|
||
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(),
|
||
upload_to_wechat: true,
|
||
});
|
||
if (!data.ok) throw new Error(data.detail || "封面生成失败");
|
||
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 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 (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();
|
||
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("改写完成。");
|
||
}
|
||
try {
|
||
setStatus("改写完成,正在生成段落海报...");
|
||
await generatePosterMaterials({ silent: true });
|
||
setStatus("改写与段落海报生成完成。");
|
||
} catch (posterErr) {
|
||
setStatus(`改写完成,段落海报未生成:${posterErr.message}`, true);
|
||
}
|
||
if (coverAutoAfterRewrite && coverAutoAfterRewrite.checked) {
|
||
try {
|
||
setStatus("改写完成,正在按输出标题生成封面...");
|
||
await generateWechatCover({ silent: true });
|
||
setStatus("改写、封面与段落海报生成完成。");
|
||
} catch (coverErr) {
|
||
setStatus(`改写完成,封面未生成:${coverErr.message}`, true);
|
||
}
|
||
}
|
||
} catch (e) {
|
||
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) {
|
||
try {
|
||
await generatePosterMaterials({ silent: true });
|
||
} catch (posterErr) {
|
||
setStatus(`海报生成失败,本次仅发布文字:${posterErr.message}`, true);
|
||
}
|
||
}
|
||
if (posterState.bodyMarkdownWithPosters) {
|
||
bodyForPublish = posterState.bodyMarkdownWithPosters;
|
||
}
|
||
}
|
||
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 });
|
||
} catch (e) {
|
||
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 (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("封面上传成功,发布时将优先使用该封面。");
|
||
} 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 = "";
|
||
} 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 });
|
||
} catch (e) {
|
||
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);
|
||
if (id !== "sourceText") $(id).addEventListener("input", markPosterStaleIfNeeded);
|
||
});
|
||
|
||
updateCounters();
|
||
initMultiDropdowns();
|
||
initWechatAccountSwitch();
|
||
syncTargetCharChips();
|
||
renderPosterPreview([]);
|
||
setCoverMode("manual");
|
||
window.addEventListener("load", () => setCoverMode("manual"));
|