From f105cb369c4f1301b4d047b75b3dc91b3d70453b Mon Sep 17 00:00:00 2001
From: aaron <>
Date: Fri, 15 May 2026 20:22:12 +0800
Subject: [PATCH] 1
---
miniprogram/pages/fund-detail/index.js | 183 ++++++++++++++++++++++-
miniprogram/pages/fund-detail/index.wxml | 62 ++++++++
miniprogram/pages/fund-detail/index.wxss | 158 +++++++++++++++++++
3 files changed, 400 insertions(+), 3 deletions(-)
diff --git a/miniprogram/pages/fund-detail/index.js b/miniprogram/pages/fund-detail/index.js
index 46dd4cd..f57630b 100644
--- a/miniprogram/pages/fund-detail/index.js
+++ b/miniprogram/pages/fund-detail/index.js
@@ -1,6 +1,10 @@
-const { get } = require("../../utils/api");
+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);
}
@@ -30,7 +34,17 @@ Page({
data: {
id: null,
record: null,
- loading: false
+ canManage: false,
+ editOpen: false,
+ editForm: {},
+ editImageUrls: [],
+ editCategories: FUND_EXPENSE_CATEGORIES,
+ editIncomeClass: "",
+ editExpenseClass: "active expense",
+ loading: false,
+ saving: false,
+ deleting: false,
+ uploadingImages: false
},
onLoad(options) {
@@ -49,7 +63,9 @@ Page({
this.setData({ loading: true });
try {
const record = await get(`/api/fund/${id}`);
- this.setData({ record: normalizeRecord(record) });
+ 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 {
@@ -62,5 +78,166 @@ Page({
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();
+ }
}
});
diff --git a/miniprogram/pages/fund-detail/index.wxml b/miniprogram/pages/fund-detail/index.wxml
index 3b3127a..ac08975 100644
--- a/miniprogram/pages/fund-detail/index.wxml
+++ b/miniprogram/pages/fund-detail/index.wxml
@@ -4,6 +4,10 @@
{{record.signed_amount_text}}
{{record.category}} · {{record.record_date}}
{{record.image_count_text}}
+
+ 编辑
+ {{deleting ? "删除中..." : "删除"}}
+
@@ -71,3 +75,61 @@
未找到班费记录
+
+
+
+
+
+ FUND RECORD
+ 修改班费记录
+
+ ×
+
+
+
+ 收支类型
+
+ 入账
+ 出账
+
+
+
+ 金额
+
+
+
+ 小类型
+
+ {{editForm.category}}
+
+
+
+ 日期
+
+ {{editForm.record_date}}
+
+
+
+ 备注
+
+
+
+
+ 小票图片
+ {{uploadingImages ? "上传中..." : "添加图片"}}
+
+ 最多 6 张,可拍照或从相册选择。
+
+
+
+ ×
+
+
+
+
+
+
+
+
+
+
diff --git a/miniprogram/pages/fund-detail/index.wxss b/miniprogram/pages/fund-detail/index.wxss
index 2cd623e..8bafe8c 100644
--- a/miniprogram/pages/fund-detail/index.wxss
+++ b/miniprogram/pages/fund-detail/index.wxss
@@ -21,6 +21,31 @@
font-weight: 650;
}
+.fund-action-bar {
+ position: relative;
+ display: flex;
+ gap: 16rpx;
+ margin-top: 26rpx;
+}
+
+.fund-action-button {
+ min-width: 132rpx;
+ height: 58rpx;
+ border: 1rpx solid rgba(255, 248, 237, 0.22);
+ border-radius: 999rpx;
+ background: rgba(255, 255, 255, 0.12);
+ color: #fff8ed;
+ font-size: 25rpx;
+ font-weight: 720;
+ line-height: 58rpx;
+ text-align: center;
+}
+
+.fund-action-button.danger {
+ background: rgba(255, 248, 237, 0.9);
+ color: #9a3a2f;
+}
+
.fund-detail-row {
display: flex;
align-items: center;
@@ -58,3 +83,136 @@
background: #efe0ca;
box-shadow: 0 14rpx 32rpx rgba(68, 39, 27, 0.08);
}
+
+.edit-mask {
+ position: fixed;
+ inset: 0;
+ z-index: 50;
+ display: flex;
+ align-items: flex-end;
+ background: rgba(47, 33, 28, 0.42);
+}
+
+.edit-panel {
+ width: 100%;
+ max-height: 88vh;
+ overflow-y: auto;
+ box-sizing: border-box;
+ padding: 30rpx 28rpx calc(34rpx + env(safe-area-inset-bottom));
+ border-radius: 34rpx 34rpx 0 0;
+ background: #fffaf3;
+ box-shadow: 0 -18rpx 60rpx rgba(47, 33, 28, 0.18);
+}
+
+.edit-head {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 24rpx;
+ margin-bottom: 26rpx;
+}
+
+.edit-kicker {
+ color: #9d8068;
+ font-size: 20rpx;
+ font-weight: 760;
+ letter-spacing: 1rpx;
+}
+
+.edit-title {
+ margin-top: 6rpx;
+ color: #2f211c;
+ font-size: 34rpx;
+ font-weight: 780;
+}
+
+.edit-close {
+ width: 54rpx;
+ height: 54rpx;
+ border-radius: 999rpx;
+ background: #f1e4d4;
+ color: #6b1f2b;
+ font-size: 42rpx;
+ line-height: 48rpx;
+ text-align: center;
+}
+
+.fund-type-switch {
+ display: flex;
+ gap: 12rpx;
+ margin-top: 12rpx;
+ padding: 8rpx;
+ border-radius: 24rpx;
+ background: #f1e4d4;
+}
+
+.fund-type {
+ flex: 1;
+ height: 72rpx;
+ border-radius: 20rpx;
+ color: #7f7065;
+ font-size: 27rpx;
+ font-weight: 700;
+ line-height: 72rpx;
+ text-align: center;
+}
+
+.fund-type.active {
+ background: #fffaf3;
+ color: #6b1f2b;
+}
+
+.fund-type.income {
+ color: #1f7a4d;
+}
+
+.fund-type.expense {
+ color: #9a3a2f;
+}
+
+.edit-label-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.edit-image-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 12rpx;
+ margin-top: 16rpx;
+}
+
+.edit-image-cell {
+ position: relative;
+ overflow: hidden;
+ height: 180rpx;
+ border-radius: 18rpx;
+ background: #efe0ca;
+}
+
+.edit-image-cell image {
+ width: 100%;
+ height: 100%;
+}
+
+.edit-image-remove {
+ position: absolute;
+ top: 8rpx;
+ right: 8rpx;
+ width: 40rpx;
+ height: 40rpx;
+ border-radius: 999rpx;
+ background: rgba(47, 33, 28, 0.72);
+ color: #fff;
+ font-size: 32rpx;
+ line-height: 36rpx;
+ text-align: center;
+}
+
+.edit-submit-row {
+ display: grid;
+ grid-template-columns: 1fr 1.35fr;
+ gap: 16rpx;
+ margin-top: 28rpx;
+}