fix:优化当前的项目
This commit is contained in:
@@ -18,6 +18,8 @@ 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");
|
||||
@@ -26,11 +28,13 @@ 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: "",
|
||||
@@ -125,6 +129,123 @@ function setStatus(msg, danger = false) {
|
||||
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()) || "";
|
||||
@@ -183,7 +304,7 @@ function renderPosterPreview(posters) {
|
||||
function markPosterStaleIfNeeded() {
|
||||
if (!posterState.signature || !posterHint) return;
|
||||
if (posterState.signature !== buildPosterSignature()) {
|
||||
posterHint.textContent = "正文已修改,发布前会自动重建段落海报。";
|
||||
posterHint.textContent = "正文已修改,如需海报请手动点击“生成段落海报”。";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,11 +315,26 @@ async function postJSON(url, body) {
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.detail || "请求失败");
|
||||
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) {
|
||||
@@ -212,9 +348,14 @@ async function generatePosterMaterials({ silent = false } = {}) {
|
||||
title: $("title").value,
|
||||
summary: $("summary").value,
|
||||
body_markdown: $("body").value,
|
||||
image_model: (coverImageModelInput && coverImageModelInput.value.trim()) || "",
|
||||
upload_to_wechat: true,
|
||||
});
|
||||
if (!data.ok) throw new Error(data.detail || "海报生成失败");
|
||||
if (!data.ok) {
|
||||
const err = new Error(data.detail || "海报生成失败");
|
||||
err.payload = data;
|
||||
throw err;
|
||||
}
|
||||
|
||||
posterState = {
|
||||
signature: buildPosterSignature(),
|
||||
@@ -252,9 +393,14 @@ async function generateWechatCover({ silent = false } = {}) {
|
||||
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) throw new Error(data.detail || "封面生成失败");
|
||||
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) {
|
||||
@@ -353,6 +499,20 @@ async function initWechatAccountSwitch() {
|
||||
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", {});
|
||||
@@ -369,6 +529,12 @@ if (logoutBtn) {
|
||||
});
|
||||
}
|
||||
|
||||
if (clearDraftBtn) {
|
||||
clearDraftBtn.addEventListener("click", () => {
|
||||
clearDraftState();
|
||||
});
|
||||
}
|
||||
|
||||
if (coverModeManualBtn) {
|
||||
coverModeManualBtn.addEventListener("click", () => setCoverMode("manual"));
|
||||
}
|
||||
@@ -419,6 +585,7 @@ $("rewriteBtn").addEventListener("click", async () => {
|
||||
$("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] || "当前为保底改写稿";
|
||||
@@ -429,23 +596,19 @@ $("rewriteBtn").addEventListener("click", async () => {
|
||||
} else {
|
||||
setStatus("改写完成。");
|
||||
}
|
||||
try {
|
||||
setStatus("改写完成,正在生成段落海报...");
|
||||
await generatePosterMaterials({ silent: true });
|
||||
setStatus("改写与段落海报生成完成。");
|
||||
} catch (posterErr) {
|
||||
setStatus(`改写完成,段落海报未生成:${posterErr.message}`, true);
|
||||
}
|
||||
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, "改写并排版", "改写中...");
|
||||
@@ -460,15 +623,10 @@ $("wechatBtn").addEventListener("click", async () => {
|
||||
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) {
|
||||
if (!stale && posterState.bodyMarkdownWithPosters) {
|
||||
bodyForPublish = posterState.bodyMarkdownWithPosters;
|
||||
} else {
|
||||
setStatus("未检测到可用海报,本次仅发布文字;如需海报请先手动生成。", true);
|
||||
}
|
||||
}
|
||||
const data = await postJSON("/api/publish/wechat", {
|
||||
@@ -495,7 +653,9 @@ 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);
|
||||
@@ -508,6 +668,30 @@ if (coverGenerateBtn) {
|
||||
});
|
||||
}
|
||||
|
||||
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");
|
||||
@@ -529,6 +713,7 @@ if (coverUploadBtn) {
|
||||
if ($("thumbMediaId")) $("thumbMediaId").value = mid;
|
||||
if (hint) hint.textContent = `封面上传成功,已绑定 media_id:${mid}`;
|
||||
setStatus("封面上传成功,发布时将优先使用该封面。");
|
||||
saveDraftState();
|
||||
} catch (e) {
|
||||
if (hint) hint.textContent = "封面上传失败,请看状态提示。";
|
||||
setStatus(`封面上传失败: ${e.message}`, true);
|
||||
@@ -561,6 +746,7 @@ if (coverUrlUploadBtn) {
|
||||
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);
|
||||
@@ -579,7 +765,9 @@ 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("请先登录")) {
|
||||
@@ -610,13 +798,30 @@ $("imBtn").addEventListener("click", async () => {
|
||||
|
||||
["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"));
|
||||
|
||||
Reference in New Issue
Block a user