Files
AIcreat/app/static/app.js

310 lines
10 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 logoutBtn = $("logoutBtn");
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;
}
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 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();
});
}
$("rewriteBtn").addEventListener("click", async () => {
const sourceText = $("sourceText").value.trim();
if (sourceText.length < 20) {
setStatus("原始内容太短,至少 20 个字符", 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,
});
$("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("改写完成。");
}
} catch (e) {
setStatus(`改写失败: ${e.message}`, true);
} finally {
setLoading(rewriteBtn, false, "改写并排版", "改写中...");
}
});
$("wechatBtn").addEventListener("click", async () => {
setStatus("正在发布到公众号草稿箱...");
setLoading(wechatBtn, true, "发布到公众号草稿箱", "发布中...");
try {
const data = await postJSON("/api/publish/wechat", {
title: $("title").value,
summary: $("summary").value,
body_markdown: $("body").value,
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 (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, "上传封面并绑定", "上传中...");
}
});
}
$("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", "summary", "body"].forEach((id) => {
$(id).addEventListener("input", updateCounters);
});
updateCounters();
initMultiDropdowns();
initWechatAccountSwitch();