hku-class/miniprogram/pages/fund-detail/index.js
2026-05-15 20:22:12 +08:00

244 lines
7.5 KiB
JavaScript

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