fix: bug
This commit is contained in:
@@ -16,8 +16,51 @@ 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);
|
||||
@@ -82,6 +125,68 @@ function setStatus(msg, danger = false) {
|
||||
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",
|
||||
@@ -94,6 +199,78 @@ async function postJSON(url, body) {
|
||||
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();
|
||||
@@ -117,10 +294,10 @@ function renderWechatAccountSelect(me) {
|
||||
if (!list.length) {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = "";
|
||||
opt.textContent = "暂无公众号";
|
||||
opt.textContent = "未绑定公众号";
|
||||
sel.appendChild(opt);
|
||||
sel.disabled = true;
|
||||
if (hint) hint.textContent = "请先在「公众号设置」绑定";
|
||||
if (hint) hint.textContent = "请先绑定公众号";
|
||||
return;
|
||||
}
|
||||
sel.disabled = false;
|
||||
@@ -192,6 +369,13 @@ if (logoutBtn) {
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -245,6 +429,22 @@ $("rewriteBtn").addEventListener("click", async () => {
|
||||
} 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 {
|
||||
@@ -256,10 +456,25 @@ $("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: $("body").value,
|
||||
body_markdown: bodyForPublish,
|
||||
thumb_media_id: $("thumbMediaId") ? $("thumbMediaId").value.trim() : "",
|
||||
});
|
||||
if (!data.ok) throw new Error(data.detail);
|
||||
@@ -276,6 +491,23 @@ $("wechatBtn").addEventListener("click", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
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");
|
||||
@@ -311,6 +543,54 @@ if (coverUploadBtn) {
|
||||
});
|
||||
}
|
||||
|
||||
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", "发送中...");
|
||||
@@ -328,11 +608,15 @@ $("imBtn").addEventListener("click", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
["sourceText", "summary", "body"].forEach((id) => {
|
||||
["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"));
|
||||
|
||||
Reference in New Issue
Block a user