Files
guzhujushiBlog/frontend/friend.js
T

100 lines
4.3 KiB
JavaScript
Raw 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.
/* =====================================================================
* 好友模块(friend.js
* 好友申请 / 状态展示 / 博主审批弹窗。
* ===================================================================== */
import { state } from "./state.js";
import { escapeHtml, formatDate, openModal, showToast } from "./utils.js";
import { api } from "./api.js";
import { openLoginModal } from "./auth.js";
import { renderAuthArea } from "./router.js";
export async function applyFriend() {
if (!state.user) { openLoginModal(); showToast("请先登录后再申请好友", "info"); return; }
try {
await api("/api/friend/apply", { method: "POST" });
state.friendStatus = "pending";
renderAuthArea();
showToast("好友申请已提交,等待博主审批", "success");
} catch (err) {
showToast(err.message, "error");
}
}
// 记录当前打开的审批弹窗引用,避免重复打开时弹窗叠加
let manageModal = null;
export function openFriendManageModal() {
// 先关闭上一次打开的审批弹窗
if (manageModal) { try { manageModal.close(); } catch (e) { /* 忽略 */ } }
const body = document.createElement("div");
body.innerHTML = '<p class="muted">加载中…</p>';
manageModal = openModal({ title: "好友申请管理", content: body });
renderApplications(body);
}
export async function openFriendListModal() {
const body = document.createElement("div");
body.innerHTML = '<p class="muted">加载中…</p>';
const modal = openModal({ title: "好友列表", content: body });
try {
const friends = await api("/api/friend/list");
if (!friends.length) { body.innerHTML = '<p class="empty">还没有好友</p>'; return; }
body.innerHTML = friends.map((f) => `
<div class="friend-row">
${f.avatar ? `<img class="friend-avatar" src="${escapeHtml(f.avatar)}" alt="">` : '<span class="friend-avatar friend-avatar-empty"></span>'}
<div class="friend-info">
<strong>${escapeHtml(f.username)}</strong>
<span class="muted">${escapeHtml(f.bio || `成为好友于 ${formatDate(f.friend_since)}`)}</span>
</div>
</div>`).join("");
} catch (err) {
body.innerHTML = "";
showToast(err.message, "error");
}
}
export async function renderApplications(body) {
try {
const records = await api("/api/friend/applications");
if (!records.length) { body.innerHTML = '<p class="empty">暂无申请记录</p>'; return; }
body.innerHTML = "";
const STATUS_TEXT = { pending: "待审批", accepted: "已通过", rejected: "已拒绝" };
for (const record of records) {
const row = document.createElement("div");
row.className = "app-row";
const statusText = STATUS_TEXT[record.status] || record.status;
row.innerHTML = `
<span class="app-user">${escapeHtml(record.username || "未知用户")}</span>
<span class="app-email muted">${escapeHtml(record.email || "")}</span>
<span class="app-time muted">${formatDate(record.created_time)}</span>
<span class="app-status">${statusText}</span>`;
if (record.status === "pending") {
const approveBtn = document.createElement("button");
approveBtn.className = "btn btn-primary btn-sm"; approveBtn.textContent = "通过";
approveBtn.addEventListener("click", async () => {
try {
await api(`/api/friend/${record.id}/approve`, { method: "POST" });
showToast("已通过好友申请", "success");
await renderApplications(body); // 原地刷新当前弹窗内容,不再叠加新弹窗
} catch (err) { showToast(err.message, "error"); }
});
const rejectBtn = document.createElement("button");
rejectBtn.className = "btn btn-outline btn-sm"; rejectBtn.textContent = "拒绝";
rejectBtn.addEventListener("click", async () => {
try {
await api(`/api/friend/${record.id}/reject`, { method: "POST" });
showToast("已拒绝好友申请");
await renderApplications(body); // 原地刷新当前弹窗内容,不再叠加新弹窗
} catch (err) { showToast(err.message, "error"); }
});
row.appendChild(approveBtn);
row.appendChild(rejectBtn);
}
body.appendChild(row);
}
} catch (err) {
body.innerHTML = "";
showToast(err.message, "error");
}
}