const { del, get, put, uploadFile } = require("../../utils/api"); const { hasManagePermission } = require("../../utils/permissions"); const { showError } = require("../../utils/page-helpers"); const FUND_INCOME_CATEGORIES = ["班费收取", "活动赞助", "其他收入"]; const FUND_EXPENSE_CATEGORIES = ["聚餐", "活动物资", "场地费", "交通费", "礼品", "其他支出"]; function formatAmount(value) { return Number(value || 0).toFixed(2); } function formatDateTime(value) { if (!value) return ""; return String(value).replace("T", " ").slice(0, 16); } function normalizeRecord(record) { const isIncome = record.type === "income"; const imageUrls = Array.isArray(record.image_urls) ? record.image_urls : []; return { ...record, amount_text: formatAmount(record.amount), signed_amount_text: `${isIncome ? "+" : "-"}¥${formatAmount(record.amount)}`, type_text: isIncome ? "收入" : "支出", type_class: isIncome ? "income" : "expense", image_urls: imageUrls, image_count_text: imageUrls.length ? `${imageUrls.length} 张凭证` : "未上传凭证", created_at_text: formatDateTime(record.created_at), updated_at_text: formatDateTime(record.updated_at) }; } Page({ data: { id: null, record: null, canManage: false, editOpen: false, editForm: {}, editImageUrls: [], editCategories: FUND_EXPENSE_CATEGORIES, editIncomeClass: "", editExpenseClass: "active expense", loading: false, saving: false, deleting: false, uploadingImages: false }, onLoad(options) { wx.setNavigationBarTitle({ title: "班费详情" }); this.setData({ id: options.id || null }); this.load(options.id); }, async onPullDownRefresh() { await this.load(this.data.id); wx.stopPullDownRefresh(); }, async load(id) { if (!id) return; this.setData({ loading: true }); try { const record = await get(`/api/fund/${id}`); const user = getApp().globalData.user || wx.getStorageSync("auth_user"); const canManage = hasManagePermission(user, record.class_id, "fund"); this.setData({ record: normalizeRecord(record), canManage }); } catch (error) { showError(error, "加载班费详情失败"); } finally { this.setData({ loading: false }); } }, previewImage(event) { const current = event.currentTarget.dataset.src; const urls = this.data.record && this.data.record.image_urls ? this.data.record.image_urls : []; if (!current || !urls.length) return; wx.previewImage({ current, urls }); }, noop() {}, openEdit() { const record = this.data.record; if (!record || !this.data.canManage) return; const categories = record.type === "income" ? FUND_INCOME_CATEGORIES : FUND_EXPENSE_CATEGORIES; this.setData({ editOpen: true, editForm: { type: record.type, amount: String(record.amount || ""), category: record.category || categories[0], description: record.description || "", record_date: record.record_date || "" }, editImageUrls: [...record.image_urls], editCategories: categories, editIncomeClass: record.type === "income" ? "active income" : "", editExpenseClass: record.type === "expense" ? "active expense" : "" }); }, closeEdit() { if (this.data.saving || this.data.uploadingImages) return; this.setData({ editOpen: false }); }, onEditInput(event) { const field = event.currentTarget.dataset.field; this.setData({ [`editForm.${field}`]: event.detail.value }); }, setEditFundType(event) { const type = event.currentTarget.dataset.type; const categories = type === "income" ? FUND_INCOME_CATEGORIES : FUND_EXPENSE_CATEGORIES; this.setData({ "editForm.type": type, "editForm.category": categories[0], editCategories: categories, editIncomeClass: type === "income" ? "active income" : "", editExpenseClass: type === "expense" ? "active expense" : "" }); }, onEditCategoryChange(event) { const index = Number(event.detail.value); this.setData({ "editForm.category": this.data.editCategories[index] }); }, onEditDateChange(event) { this.setData({ "editForm.record_date": event.detail.value }); }, chooseEditImages() { if (this.data.uploadingImages) return; if (this.data.editImageUrls.length >= 6) { wx.showToast({ title: "最多 6 张图片", icon: "none" }); return; } wx.chooseMedia({ count: 6 - this.data.editImageUrls.length, mediaType: ["image"], sourceType: ["album", "camera"], success: async (res) => { const paths = (res.tempFiles || []).map((item) => item.tempFilePath); if (!paths.length) return; this.setData({ uploadingImages: true }); try { const uploaded = [...this.data.editImageUrls]; for (const path of paths) { const result = await uploadFile("/api/upload/image", path, {}, "file"); if (result && result.url) uploaded.push(result.url); } this.setData({ editImageUrls: uploaded.slice(0, 6) }); } catch (error) { showError(error, "上传图片失败"); } finally { this.setData({ uploadingImages: false }); } } }); }, removeEditImage(event) { const index = Number(event.currentTarget.dataset.index); this.setData({ editImageUrls: this.data.editImageUrls.filter((_, itemIndex) => itemIndex !== index) }); }, async saveEdit() { if (this.data.saving || this.data.uploadingImages) return; const form = this.data.editForm; const amount = Number(form.amount); if (!amount || amount <= 0) { wx.showToast({ title: "请输入有效金额", icon: "none" }); return; } if (!form.category) { wx.showToast({ title: "请选择分类", icon: "none" }); return; } if (!form.record_date) { wx.showToast({ title: "请选择日期", icon: "none" }); return; } this.setData({ saving: true }); try { const updated = await put(`/api/fund/${this.data.id}`, { type: form.type, amount, category: form.category, description: form.description || null, image_urls: this.data.editImageUrls, record_date: form.record_date }); this.setData({ record: normalizeRecord(updated), editOpen: false }); this.refreshPreviousPage(); wx.showToast({ title: "已更新", icon: "success" }); } catch (error) { showError(error, "保存失败"); } finally { this.setData({ saving: false }); } }, deleteRecord() { if (this.data.deleting || !this.data.canManage || !this.data.record) return; wx.showModal({ title: "删除班费记录", content: "删除后无法恢复,确认删除这条班费记录?", confirmText: "删除", confirmColor: "#b42318", success: async (res) => { if (!res.confirm) return; this.setData({ deleting: true }); try { await del(`/api/fund/${this.data.id}`); this.refreshPreviousPage(); wx.showToast({ title: "已删除", icon: "success" }); setTimeout(() => wx.navigateBack(), 500); } catch (error) { showError(error, "删除失败"); } finally { this.setData({ deleting: false }); } } }); }, refreshPreviousPage() { const pages = getCurrentPages(); const previousPage = pages[pages.length - 2]; if (previousPage && previousPage.load) { previousPage.load(); } } });