const { del, get } = require("../../utils/api"); const { getModule } = require("../../utils/modules"); const { hasManagePermission } = require("../../utils/permissions"); const { ensureModuleOpen, getActiveClassId, showError } = require("../../utils/page-helpers"); const ENDPOINTS = { announcements: "/api/announcements/", directory: "/api/directory/", timeline: "/api/timeline/", votes: "/api/votes/", schedule: "/api/schedule/", fund: "/api/fund/" }; function formatDateTime(value) { if (!value) return ""; return String(value).replace("T", " ").slice(0, 16); } function formatScheduleTime(item) { const start = formatDateTime(item.start_time); const end = formatDateTime(item.end_time); if (item.type === "deadline") return `截止 ${start}`; if (!end) return start; if (start.slice(0, 10) === end.slice(0, 10)) { return `${start} - ${end.slice(11)}`; } return `${start} - ${end}`; } function scheduleTypeText(type) { return { course: "课程", deadline: "截止日", activity: "活动" }[type] || type || "排期"; } Page({ data: { moduleKey: "", title: "功能", moduleIcon: "项", items: [], canManage: false, canPostTimeline: false, isTimeline: false, isDirectory: false, isSchedule: false, isFund: false, isVotes: false, fundStats: null, directoryStats: null, needsRefresh: false, loading: false }, onLoad(options) { const moduleKey = options.module || ""; const module = getModule(moduleKey); const app = getApp(); const classId = getActiveClassId(); const title = module ? module.title : "功能"; const icon = module ? module.icon : "项"; this.setData({ moduleKey, title, moduleIcon: icon, isTimeline: moduleKey === "timeline", isDirectory: moduleKey === "directory", isSchedule: moduleKey === "schedule", isFund: moduleKey === "fund", isVotes: moduleKey === "votes", canPostTimeline: moduleKey === "timeline", canManage: ["announcements", "votes", "schedule", "fund"].includes(moduleKey) && hasManagePermission(app.globalData.user, classId, moduleKey) }); wx.setNavigationBarTitle({ title }); if (!module || !ensureModuleOpen(moduleKey)) return; this.load(); }, onShow() { if (this.data.moduleKey && getModule(this.data.moduleKey)) { this.setData({ needsRefresh: false }); this.load(); } }, async onPullDownRefresh() { await this.load(); wx.stopPullDownRefresh(); }, async load() { const classId = getActiveClassId(); const endpoint = ENDPOINTS[this.data.moduleKey]; if (!endpoint) return; this.setData({ loading: true }); try { let stats = null; if (this.data.moduleKey === "fund") { stats = await get("/api/fund/statistics", { class_id: classId }); } let directoryStats = null; if (this.data.moduleKey === "directory") { directoryStats = await get("/api/directory/stats", { class_id: classId }); } const res = await get(endpoint, { page_size: 20, class_id: classId }); const rawItems = Array.isArray(res) ? res : res.items || []; const currentUser = getApp().globalData.user || {}; const currentUserId = currentUser.id; const formatAmount = (value) => Number(value || 0).toFixed(2); const items = rawItems.map((item) => ({ ...item, can_delete: this.data.moduleKey === "timeline" && item.author_id === currentUserId, initial: String(item.name || item.author_name || this.data.title || "项").slice(0, 1), member_role_text: item.membership_role === "teacher" ? "老师" : "同学", member_role_class: item.membership_role === "teacher" ? "teacher" : "student", show_student_id: item.membership_role !== "teacher" && item.student_id, schedule_day: item.start_time ? String(item.start_time).slice(8, 10) : "", schedule_month: item.start_time ? `${String(item.start_time).slice(5, 7)}月` : "", schedule_time_text: this.data.moduleKey === "schedule" ? formatScheduleTime(item) : "", schedule_type_text: this.data.moduleKey === "schedule" ? scheduleTypeText(item.type) : "", vote_status_text: item.status === "open" ? "进行中" : "已关闭", vote_type_text: item.vote_type === "multiple" ? `多选,最多 ${item.max_choices || 1} 项` : "单选", vote_action_text: item.has_voted ? "已参与" : "待参与", vote_pill_class: item.has_voted ? "done" : "", vote_options_text: Array.isArray(item.options) ? `${item.options.length} 个选项` : "", committee_text: item.committee_role ? ` · ${item.committee_role}` : "", fund_type_text: item.type === "income" ? "收入" : "支出", fund_type_class: item.type === "income" ? "income" : "expense", amount_text: formatAmount(item.amount), image_urls: Array.isArray(item.image_urls) ? item.image_urls : [] })); const fundStats = stats ? { ...stats, total_income_text: formatAmount(stats.total_income), total_expense_text: formatAmount(stats.total_expense), balance_text: formatAmount(stats.balance) } : null; this.setData({ items, fundStats, directoryStats }); } catch (error) { if (error.message === "该功能当前未开放") { wx.redirectTo({ url: `/pages/module-unavailable/index?title=${encodeURIComponent(this.data.title)}` }); return; } showError(error); } finally { this.setData({ loading: false }); } }, openItem(event) { const id = event.currentTarget.dataset.id; const key = this.data.moduleKey; if (!id) return; if (key === "directory") { wx.navigateTo({ url: `/pages/member-detail/index?id=${id}` }); return; } if (key === "schedule") { wx.navigateTo({ url: `/pages/schedule-detail/index?id=${id}` }); return; } if (key === "timeline") { wx.navigateTo({ url: `/pages/timeline-detail/index?id=${id}` }); return; } if (key === "votes") { wx.navigateTo({ url: `/pages/vote-detail/index?id=${id}` }); return; } if (key === "fund") { wx.navigateTo({ url: `/pages/fund-detail/index?id=${id}` }); } }, previewImage(event) { const current = event.currentTarget.dataset.src; const postId = Number(event.currentTarget.dataset.postId); const post = this.data.items.find((item) => item.id === postId); const urls = post && post.image_urls ? post.image_urls : []; if (!current || !urls.length) return; wx.previewImage({ current, urls }); }, previewFundImage(event) { const current = event.currentTarget.dataset.src; const recordId = Number(event.currentTarget.dataset.recordId); const record = this.data.items.find((item) => item.id === recordId); const urls = record && record.image_urls ? record.image_urls : []; if (!current || !urls.length) return; wx.previewImage({ current, urls }); }, openTimelineActions(event) { const id = event.currentTarget.dataset.id; wx.showActionSheet({ itemList: ["删除动态"], itemColor: "#b42318", success: () => { wx.showModal({ title: "删除动态", content: "删除后无法恢复,确认删除这条动态?", confirmText: "删除", confirmColor: "#b42318", success: async (res) => { if (!res.confirm) return; try { await del(`/api/timeline/${id}`); wx.showToast({ title: "已删除", icon: "success" }); this.load(); } catch (error) { showError(error, "删除失败"); } } }); } }); }, openManage() { wx.navigateTo({ url: `/pages/manage/index?module=${this.data.moduleKey}` }); }, openTimelineCreate() { wx.navigateTo({ url: "/pages/timeline-create/index" }); }, openCreate() { if (this.data.canPostTimeline) { this.openTimelineCreate(); return; } if (this.data.canManage) { this.openManage(); } } });