diff --git a/pages/message/benefit-entry.vue b/pages/message/benefit-entry.vue
index a400db9..4771700 100644
--- a/pages/message/benefit-entry.vue
+++ b/pages/message/benefit-entry.vue
@@ -43,7 +43,7 @@
折扣
-
+
{{ Number(entry.discount).toFixed(1) }}
@@ -143,7 +143,7 @@
折扣
-
+
{{ Number(entry.discount).toFixed(1) }}
@@ -319,8 +319,40 @@
@@ -369,10 +401,67 @@ const todoExecutorPickerVisible = ref(false);
const todoExecutorTarget = ref(null);
const todoExecutorKeyword = ref("");
const todoExecutorRefreshing = ref(false);
+const benefitDetailVisible = ref(false);
const todoTypeOptions = Object.entries(TODO_EVENT_TYPE_LABELS).map(([value, label]) => ({ value, label }));
let searchTimer = null;
let entryKey = 0;
+function toggleBenefitDetail() {
+ benefitDetailVisible.value = !benefitDetailVisible.value;
+}
+
+const benefitDetailItems = computed(() => entries.value.flatMap((entry) => {
+ if (entry.isPackage) {
+ return (entry.projects || [])
+ .filter((project) => project.selected)
+ .map((project) => ({
+ key: `${entry.key}_${project.key}`,
+ name: project.project?.projectName || "未命名权益",
+ quantity: Math.max(0, toNumber(project.usageCount, 1)),
+ discount: toNumber(project.discount),
+ totalAmount: Math.max(0, toNumber(project.totalPrice)),
+ isFree: false,
+ }));
+ }
+
+ if (!entry.project?._id) return [];
+ return [{
+ key: entry.key,
+ name: entry.project.projectName || "未命名权益",
+ quantity: Math.max(0, toNumber(entry.usageCount, 1)),
+ discount: toNumber(entry.discount),
+ totalAmount: Math.max(0, toNumber(entry.isFree ? 0 : entry.totalPrice)),
+ isFree: Boolean(entry.isFree),
+ }];
+}));
+
+const benefitSummary = computed(() => entries.value.reduce((summary, entry) => {
+ if (entry.isPackage) {
+ summary.selectedCount += 1;
+ const originalAmount = toNumber(entry.packagePrice || entry.price) * Math.max(0, toNumber(entry.usageCount, 1));
+ const totalAmount = entry.isFree ? 0 : Math.max(0, toNumber(entry.totalPrice));
+ summary.originalAmount += originalAmount;
+ summary.totalAmount += totalAmount;
+ summary.savedAmount += Math.max(0, originalAmount - totalAmount);
+ return summary;
+ }
+
+ if (!entry.project?._id) return summary;
+ summary.selectedCount += 1;
+ const usageCount = Math.max(0, toNumber(entry.usageCount, 1));
+ const originalAmount = toNumber(entry.price) * usageCount;
+ const totalAmount = entry.isFree ? 0 : Math.max(0, toNumber(entry.totalPrice));
+ summary.originalAmount += originalAmount;
+ summary.totalAmount += totalAmount;
+ summary.savedAmount += Math.max(0, originalAmount - totalAmount);
+ return summary;
+}, {
+ selectedCount: 0,
+ originalAmount: 0,
+ savedAmount: 0,
+ totalAmount: 0,
+}));
+
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
@@ -1755,6 +1844,18 @@ async function submitEntries() {
width: 100%;
}
+.discount-picker.discount-disabled {
+ opacity: 0.62;
+ pointer-events: none;
+}
+
+.discount-picker.discount-disabled .number-input {
+ border-color: #d1d5db;
+ background: #f3f4f6;
+ color: #9ca3af;
+ -webkit-text-fill-color: #9ca3af;
+}
+
.field-row {
display: flex;
gap: 20rpx;
@@ -1876,17 +1977,163 @@ async function submitEntries() {
}
.footer-bar {
+ position: relative;
display: flex;
+ align-items: center;
gap: 20rpx;
- padding: 20rpx 24rpx;
- background: rgba(245, 246, 248, 0.96);
+ padding: 14rpx 24rpx 18rpx;
+ background: #fff;
border-top: 1rpx solid #eef2f7;
- backdrop-filter: blur(12px);
+ box-shadow: 0 -4rpx 16rpx rgba(15, 23, 42, 0.04);
box-sizing: border-box;
+ z-index: 10;
}
-.footer-bar button {
+.footer-summary {
flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 14rpx;
+ min-width: 0;
+}
+
+.total-summary {
+ display: flex;
+ flex-direction: row;
+ align-items: baseline;
+ gap: 6rpx;
+ min-width: 0;
+ white-space: nowrap;
+}
+
+.summary-label {
+ color: #6b7280;
+ font-size: 22rpx;
+ line-height: 1.2;
+ white-space: nowrap;
+}
+
+.summary-saving {
+ color: #16a34a;
+}
+
+.summary-total-value {
+ color: #e85c2f;
+ font-size: 32rpx;
+ font-weight: 700;
+ line-height: 1.2;
+ white-space: nowrap;
+}
+
+.detail-trigger {
+ flex-shrink: 0;
+ padding-left: 14rpx;
+ border-left: 1rpx solid #edf0f3;
+ color: #6b7280;
+ font-size: 24rpx;
+ line-height: 1.2;
+ white-space: nowrap;
+}
+
+.benefit-detail-panel {
+ position: absolute;
+ right: 0;
+ bottom: 100%;
+ left: 0;
+ max-height: calc(100vh - 220rpx);
+ overflow: hidden;
+ padding: 20rpx 24rpx 18rpx;
+ border-top: 1rpx solid #eef2f7;
+ border-radius: 24rpx 24rpx 0 0;
+ background: #fff;
+ box-shadow: 0 -8rpx 24rpx rgba(15, 23, 42, 0.08);
+ box-sizing: border-box;
+ opacity: 0;
+ visibility: hidden;
+ pointer-events: none;
+ transform: translateY(100%);
+ transform-origin: bottom center;
+ transition:
+ opacity 180ms ease,
+ transform 180ms ease,
+ visibility 180ms ease;
+}
+
+.benefit-detail-panel-visible {
+ opacity: 1;
+ visibility: visible;
+ pointer-events: auto;
+ transform: translateY(0);
+}
+
+.detail-header,
+.detail-row {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+ min-height: 60rpx;
+ color: #6b7280;
+ font-size: 27rpx;
+}
+
+.detail-header {
+ min-height: 48rpx;
+ color: #9ca3af;
+ font-size: 24rpx;
+}
+
+.detail-project-name {
+ flex: 1;
+ min-width: 0;
+}
+
+.detail-project-name-value {
+ overflow: hidden;
+ color: #374151;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.detail-quantity,
+.detail-discount {
+ flex: 0 0 72rpx;
+ text-align: center;
+ white-space: nowrap;
+}
+
+.detail-amount {
+ flex: 0 0 130rpx;
+ color: #374151;
+ text-align: right;
+ white-space: nowrap;
+}
+
+.detail-empty {
+ padding: 22rpx 0;
+ color: #9ca3af;
+ font-size: 26rpx;
+ text-align: center;
+}
+
+.detail-row-total {
+ margin-top: 8rpx;
+ padding-top: 14rpx;
+ border-top: 1rpx solid #eef2f7;
+ color: #1f2937;
+ font-weight: 600;
+}
+
+.detail-row-total .detail-amount {
+ color: #e85c2f;
+ font-weight: 700;
+}
+
+.footer-actions {
+ flex: 0 0 250rpx;
+}
+
+.footer-actions button {
+ width: 100%;
min-height: 88rpx;
padding: 0;
display: flex;
@@ -1897,12 +2144,6 @@ async function submitEntries() {
line-height: 1.2;
}
-.cancel-button {
- color: #0877f1;
- background: #fff;
- border: 1rpx solid #0877f1;
-}
-
.submit-button {
color: #fff;
background: linear-gradient(135deg, #0f84ff 0%, #0877f1 100%);