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"));
|
||||
|
||||
@@ -66,6 +66,33 @@ if (loginBtn) {
|
||||
|
||||
if (registerBtn) {
|
||||
registerBtn.addEventListener("click", async () => {
|
||||
await authAction("/api/auth/register", registerBtn, "注册", "注册中...", "注册成功,正在跳转...");
|
||||
setLoading(registerBtn, true, "注册", "注册中...");
|
||||
try {
|
||||
const data = await postJSON("/api/auth/register", fields());
|
||||
if (!data.ok) {
|
||||
setStatus(data.detail || "注册失败", true);
|
||||
return;
|
||||
}
|
||||
const code = (data.reset_code || "").trim();
|
||||
if (code) {
|
||||
const msg =
|
||||
`注册成功!请务必保存你的重置码(找回密码唯一凭证):\n\n${code}\n\n` +
|
||||
"请立即复制并妥善保管,点击“确定”后继续进入系统。";
|
||||
window.alert(msg);
|
||||
try {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
await navigator.clipboard.writeText(code);
|
||||
}
|
||||
} catch {
|
||||
// 忽略复制失败
|
||||
}
|
||||
}
|
||||
setStatus("注册成功,正在跳转...");
|
||||
window.location.href = nextPath();
|
||||
} catch (e) {
|
||||
setStatus(e.message || "请求异常", true);
|
||||
} finally {
|
||||
setLoading(registerBtn, false, "注册", "注册中...");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,32 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="AIcreat">
|
||||
<rect width="64" height="64" rx="14" fill="#0891b2"/>
|
||||
<path d="M20 46l10-28h4l10 28h-5l-2-6H27l-2 6h-5zm9-10h14l-7-20-7 20z" fill="#fff"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="AI发糕">
|
||||
<defs>
|
||||
<linearGradient id="appBg" x1="10" y1="8" x2="55" y2="58" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFB23F"/>
|
||||
<stop offset="0.54" stop-color="#FF8428"/>
|
||||
<stop offset="1" stop-color="#FF5A16"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="cakeTop" cx="42%" cy="28%" r="72%">
|
||||
<stop stop-color="#FFFDF2"/>
|
||||
<stop offset="0.68" stop-color="#FFF0C6"/>
|
||||
<stop offset="1" stop-color="#F6D48F"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="cakeSide" x1="19" y1="31" x2="47" y2="54" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFF0BF"/>
|
||||
<stop offset="1" stop-color="#F0B85B"/>
|
||||
</linearGradient>
|
||||
<filter id="softShadow" x="-20%" y="-20%" width="140%" height="150%">
|
||||
<feDropShadow dx="0" dy="5" stdDeviation="3" flood-color="#9A3F0B" flood-opacity="0.28"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="5" y="5" width="54" height="54" rx="13" fill="url(#appBg)"/>
|
||||
<g filter="url(#softShadow)">
|
||||
<path d="M16.5 30.5c0-8 6.5-14.2 15.5-14.2s15.5 6.2 15.5 14.2v14.2c0 5.1-6.4 8.7-15.5 8.7s-15.5-3.6-15.5-8.7V30.5z" fill="url(#cakeSide)"/>
|
||||
<path d="M13.3 29.5c0-7.9 7.7-14.3 18.7-14.3s18.7 6.4 18.7 14.3c0 6.3-7.7 11.3-18.7 11.3s-18.7-5-18.7-11.3z" fill="url(#cakeTop)"/>
|
||||
<path d="M18.2 33.4c3 3.3 7.8 5.1 13.8 5.1s10.8-1.8 13.8-5.1" fill="none" stroke="#E1B260" stroke-width="2.2" stroke-linecap="round"/>
|
||||
<ellipse cx="24.3" cy="27" rx="1.9" ry="3.2" transform="rotate(-38 24.3 27)" fill="#E33D18"/>
|
||||
<ellipse cx="31.9" cy="25.7" rx="1.9" ry="3.3" transform="rotate(-61 31.9 25.7)" fill="#F05321"/>
|
||||
<ellipse cx="39.7" cy="27.1" rx="1.9" ry="3.2" transform="rotate(41 39.7 27.1)" fill="#D93616"/>
|
||||
<ellipse cx="28.4" cy="20.6" rx="1.6" ry="2.8" transform="rotate(47 28.4 20.6)" fill="#F15A24"/>
|
||||
<ellipse cx="36.6" cy="20.8" rx="1.6" ry="2.8" transform="rotate(-45 36.6 20.8)" fill="#E7471C"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 243 B After Width: | Height: | Size: 2.0 KiB |
@@ -43,7 +43,7 @@ function renderAccounts(me) {
|
||||
if (!list.length) {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = "";
|
||||
opt.textContent = "暂无公众号,请先绑定";
|
||||
opt.textContent = "未绑定公众号";
|
||||
sel.appendChild(opt);
|
||||
return;
|
||||
}
|
||||
@@ -56,16 +56,44 @@ function renderAccounts(me) {
|
||||
});
|
||||
}
|
||||
|
||||
function renderModels(me) {
|
||||
const sel = $("modelSelect");
|
||||
if (!sel) return;
|
||||
const list = Array.isArray(me.ai_models) ? me.ai_models : [];
|
||||
const active = me.active_ai_model && me.active_ai_model.id ? Number(me.active_ai_model.id) : 0;
|
||||
sel.innerHTML = "";
|
||||
if (!list.length) {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = "";
|
||||
opt.textContent = "暂无模型配置,请先新增";
|
||||
sel.appendChild(opt);
|
||||
return;
|
||||
}
|
||||
list.forEach((m) => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = String(m.id);
|
||||
opt.textContent = `${m.model_name} (${m.model})`;
|
||||
if ((active && m.id === active) || m.active) opt.selected = true;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
const me = await authMe();
|
||||
if (!me) return;
|
||||
renderAccounts(me);
|
||||
renderModels(me);
|
||||
}
|
||||
|
||||
const accountSelect = $("accountSelect");
|
||||
const bindBtn = $("bindBtn");
|
||||
const deleteWechatBtn = $("deleteWechatBtn");
|
||||
const logoutBtn = $("logoutBtn");
|
||||
const changePwdBtn = $("changePwdBtn");
|
||||
const deleteAccountBtn = $("deleteAccountBtn");
|
||||
const modelSelect = $("modelSelect");
|
||||
const saveModelBtn = $("saveModelBtn");
|
||||
const deleteModelBtn = $("deleteModelBtn");
|
||||
|
||||
if (accountSelect) {
|
||||
accountSelect.addEventListener("change", async () => {
|
||||
@@ -85,6 +113,32 @@ if (accountSelect) {
|
||||
});
|
||||
}
|
||||
|
||||
if (deleteWechatBtn) {
|
||||
deleteWechatBtn.addEventListener("click", async () => {
|
||||
const id = Number((accountSelect && accountSelect.value) || 0);
|
||||
if (!id) {
|
||||
setStatus("请先选择要删除的公众号", true);
|
||||
return;
|
||||
}
|
||||
const sure = window.confirm("确定删除当前公众号绑定吗?删除后不可恢复。");
|
||||
if (!sure) return;
|
||||
setLoading(deleteWechatBtn, true, "删除当前公众号", "删除中...");
|
||||
try {
|
||||
const out = await postJSON("/api/auth/wechat/delete", { account_id: id });
|
||||
if (!out.ok) {
|
||||
setStatus(out.detail || "删除失败", true);
|
||||
return;
|
||||
}
|
||||
setStatus("公众号账号已删除。");
|
||||
await refresh();
|
||||
} catch (e) {
|
||||
setStatus(e.message || "删除失败", true);
|
||||
} finally {
|
||||
setLoading(deleteWechatBtn, false, "删除当前公众号", "删除中...");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (bindBtn) {
|
||||
bindBtn.addEventListener("click", async () => {
|
||||
setLoading(bindBtn, true, "绑定并设为当前账号", "绑定中...");
|
||||
@@ -113,6 +167,79 @@ if (bindBtn) {
|
||||
});
|
||||
}
|
||||
|
||||
if (modelSelect) {
|
||||
modelSelect.addEventListener("change", async () => {
|
||||
const id = Number(modelSelect.value || 0);
|
||||
if (!id) return;
|
||||
try {
|
||||
const out = await postJSON("/api/auth/ai-models/switch", { model_id: id });
|
||||
if (!out.ok) {
|
||||
setStatus(out.detail || "模型切换失败", true);
|
||||
return;
|
||||
}
|
||||
setStatus("已切换当前模型。");
|
||||
await refresh();
|
||||
} catch (e) {
|
||||
setStatus(e.message || "模型切换失败", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (saveModelBtn) {
|
||||
saveModelBtn.addEventListener("click", async () => {
|
||||
setLoading(saveModelBtn, true, "保存并设为当前模型", "保存中...");
|
||||
try {
|
||||
const out = await postJSON("/api/auth/ai-models/add", {
|
||||
model_name: ($("modelName") && $("modelName").value.trim()) || "",
|
||||
api_key: ($("apiKey") && $("apiKey").value.trim()) || "",
|
||||
base_url: ($("baseUrl") && $("baseUrl").value.trim()) || "",
|
||||
model: ($("modelValue") && $("modelValue").value.trim()) || "",
|
||||
timeout_sec: Number((($("timeoutSec") && $("timeoutSec").value) || "120").trim()),
|
||||
max_output_tokens: Number((($("maxOutputTokens") && $("maxOutputTokens").value) || "8192").trim()),
|
||||
max_retries: Number((($("maxRetries") && $("maxRetries").value) || "0").trim()),
|
||||
});
|
||||
if (!out.ok) {
|
||||
setStatus(out.detail || "模型保存失败", true);
|
||||
return;
|
||||
}
|
||||
setStatus("模型配置已保存并设为当前。");
|
||||
if ($("apiKey")) $("apiKey").value = "";
|
||||
if ($("modelName")) $("modelName").value = "";
|
||||
await refresh();
|
||||
} catch (e) {
|
||||
setStatus(e.message || "模型保存失败", true);
|
||||
} finally {
|
||||
setLoading(saveModelBtn, false, "保存并设为当前模型", "保存中...");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (deleteModelBtn) {
|
||||
deleteModelBtn.addEventListener("click", async () => {
|
||||
const id = Number((modelSelect && modelSelect.value) || 0);
|
||||
if (!id) {
|
||||
setStatus("请先选择要删除的模型", true);
|
||||
return;
|
||||
}
|
||||
const sure = window.confirm("确定删除当前模型配置吗?删除后不可恢复。");
|
||||
if (!sure) return;
|
||||
setLoading(deleteModelBtn, true, "删除当前模型", "删除中...");
|
||||
try {
|
||||
const out = await postJSON("/api/auth/ai-models/delete", { model_id: id });
|
||||
if (!out.ok) {
|
||||
setStatus(out.detail || "模型删除失败", true);
|
||||
return;
|
||||
}
|
||||
setStatus("模型配置已删除。");
|
||||
await refresh();
|
||||
} catch (e) {
|
||||
setStatus(e.message || "模型删除失败", true);
|
||||
} finally {
|
||||
setLoading(deleteModelBtn, false, "删除当前模型", "删除中...");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (logoutBtn) {
|
||||
logoutBtn.addEventListener("click", async () => {
|
||||
setLoading(logoutBtn, true, "退出登录", "退出中...");
|
||||
@@ -150,4 +277,45 @@ if (changePwdBtn) {
|
||||
});
|
||||
}
|
||||
|
||||
if (deleteAccountBtn) {
|
||||
deleteAccountBtn.addEventListener("click", async () => {
|
||||
const pwd = ($("deletePassword") && $("deletePassword").value) || "";
|
||||
const rkey = ($("deleteResetKey") && $("deleteResetKey").value.trim()) || "";
|
||||
if (!pwd) {
|
||||
setStatus("请输入注销校验密码", true);
|
||||
return;
|
||||
}
|
||||
if (!rkey) {
|
||||
setStatus("请输入注销校验重置码", true);
|
||||
return;
|
||||
}
|
||||
const sure = window.confirm("确定注销账户吗?将清空此账号所有业务数据,操作不可恢复。");
|
||||
if (!sure) return;
|
||||
const confirmText = window.prompt("为防止误删,请输入「注销账户」后确认:", "");
|
||||
if ((confirmText || "").trim() !== "注销账户") {
|
||||
setStatus("二次确认未通过,已取消注销。", true);
|
||||
return;
|
||||
}
|
||||
setLoading(deleteAccountBtn, true, "注销账户", "注销中...");
|
||||
try {
|
||||
const out = await postJSON("/api/auth/account/delete", {
|
||||
password: pwd,
|
||||
reset_key: rkey,
|
||||
});
|
||||
if (!out.ok) {
|
||||
setStatus(out.detail || "注销失败", true);
|
||||
return;
|
||||
}
|
||||
setStatus("账号已注销,正在返回登录页。");
|
||||
window.setTimeout(() => {
|
||||
window.location.href = "/auth?next=/";
|
||||
}, 900);
|
||||
} catch (e) {
|
||||
setStatus(e.message || "注销失败", true);
|
||||
} finally {
|
||||
setLoading(deleteAccountBtn, false, "注销账户", "注销中...");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
refresh();
|
||||
|
||||
1874
app/static/style.css
1874
app/static/style.css
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user