This commit is contained in:
Daniel
2026-04-28 11:50:55 +08:00
parent 1bbabc2a78
commit 2724e69b4f
20 changed files with 3881 additions and 554 deletions

View File

@@ -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"));

View File

@@ -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, "注册", "注册中...");
}
});
}

View File

@@ -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

View File

@@ -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();

File diff suppressed because it is too large Load Diff