Преглед на файлове

feat: admin-h5 首页新增本月概览,展示当月收益/消费/订单/洗车人数/新增会员/退款

dashboard 接口补充 monthRefundAmount 与 monthWashUsers,其余当月字段后端已有;统计口径跟随当前选中站点。
skyline преди 3 седмици
родител
ревизия
ba3450f63b

+ 93 - 0
admin-h5/src/pages/index/index.vue

@@ -108,6 +108,40 @@
         </view>
       </view>
 
+      <!-- 本月概览 -->
+      <view class="section-header">
+        <view class="section-dot"></view>
+        <text class="section-title">本月概览</text>
+        <text class="section-sub">{{ monthLabel }}</text>
+      </view>
+
+      <view class="month-grid">
+        <view class="month-card">
+          <text class="month-value highlight">¥{{ formatAmount(monthIncome) }}</text>
+          <text class="month-label">当月收益</text>
+        </view>
+        <view class="month-card">
+          <text class="month-value">¥{{ formatAmount(monthConsumption) }}</text>
+          <text class="month-label">当月消费</text>
+        </view>
+        <view class="month-card">
+          <text class="month-value">{{ monthOrders }}</text>
+          <text class="month-label">当月订单</text>
+        </view>
+        <view class="month-card">
+          <text class="month-value">{{ monthWashUsers }}</text>
+          <text class="month-label">当月洗车人数</text>
+        </view>
+        <view class="month-card">
+          <text class="month-value">{{ monthMembers }}</text>
+          <text class="month-label">当月新增会员</text>
+        </view>
+        <view class="month-card">
+          <text class="month-value">¥{{ formatAmount(monthRefund) }}</text>
+          <text class="month-label">当月退款</text>
+        </view>
+      </view>
+
       <!-- 快捷入口标题 -->
       <view class="section-header">
         <view class="section-dot"></view>
@@ -201,6 +235,18 @@ const todayMembers = ref(0)
 const totalDevices = ref(0)
 const onlineDevices = ref(0)
 
+const monthIncome = ref(0)
+const monthConsumption = ref(0)
+const monthOrders = ref(0)
+const monthWashUsers = ref(0)
+const monthMembers = ref(0)
+const monthRefund = ref(0)
+
+const monthLabel = computed(() => {
+  const d = new Date()
+  return `${d.getFullYear()}年${d.getMonth() + 1}月`
+})
+
 const todayDate = computed(() => {
   const d = new Date()
   return `${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日`
@@ -277,6 +323,12 @@ const loadData = async (stationId) => {
       avgOrderPrice.value = data.avgOrderPrice || 0
       avgDuration.value = Math.round((data.avgOrderDuration || 0) / 60 * 10) / 10
       todayMembers.value = data.todayRegisteredMembers || 0
+      monthIncome.value = data.monthIncome || 0
+      monthConsumption.value = data.monthConsumptionAmount || 0
+      monthOrders.value = data.monthWashOrders || 0
+      monthWashUsers.value = data.monthWashUsers || 0
+      monthMembers.value = data.monthRegisteredMembers || 0
+      monthRefund.value = data.monthRefundAmount || 0
     }
 
     if (deviceRes && deviceRes.code === 200) {
@@ -496,6 +548,47 @@ onPullDownRefresh(async () => {
   font-weight: 600;
   color: #1A1A1A;
 }
+.section-sub {
+  font-size: 22rpx;
+  color: #999999;
+}
+
+/* ===== Month Overview ===== */
+.month-grid {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16rpx;
+  margin-bottom: 40rpx;
+}
+.month-card {
+  flex: 0 0 calc(50% - 8rpx);
+  box-sizing: border-box;
+  background: #FFFFFF;
+  border-radius: 20rpx;
+  padding: 28rpx 24rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  gap: 10rpx;
+  transition: box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1), transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
+}
+.month-card:active {
+  transform: translateY(-2rpx);
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
+}
+.month-value {
+  font-size: 34rpx;
+  font-weight: 700;
+  color: #1A1A1A;
+  line-height: 1.2;
+}
+.month-value.highlight {
+  color: #C6171E;
+}
+.month-label {
+  font-size: 24rpx;
+  color: #999999;
+}
 
 /* ===== Quick Actions ===== */
 .actions-row {

+ 10 - 0
car-wash-entity/src/main/java/com/kym/entity/vo/DashboardVo.java

@@ -32,6 +32,11 @@ public class DashboardVo {
      */
     private Integer monthConsumptionAmount;
 
+    /**
+     * 当月退款金额
+     */
+    private Integer monthRefundAmount;
+
     /**
      * 累计消费金额
      */
@@ -72,6 +77,11 @@ public class DashboardVo {
      */
     private Integer monthWashOrders;
 
+    /**
+     * 当月洗车人数(去重用户数)
+     */
+    private Integer monthWashUsers;
+
     /**
      * 累计洗车订单数量
      */

+ 8 - 0
car-wash-service/src/main/java/com/kym/service/impl/StatServiceImpl.java

@@ -191,6 +191,12 @@ public class StatServiceImpl implements StatService {
                 .lt(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).plusMonths(1).atTime(LocalTime.MIN))
                 .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
         var monthIncome = monthRechargeAndCrossIncome - monthRefund - monthCrossExpend;
+        // 当月洗车人数(去重用户)
+        var monthWashUsers = (int) monthOrders.stream()
+                .map(WashOrder::getUserId)
+                .filter(java.util.Objects::nonNull)
+                .distinct()
+                .count();
         var monthRegistrations = userService.countMonthRegister(LocalDate.now());
         var monthOrdersMap = washOrderService.countMonthOrders(LocalDate.now());
 
@@ -224,8 +230,10 @@ public class StatServiceImpl implements StatService {
 
         dashboardVo.setMonthIncome(monthIncome);
         dashboardVo.setMonthConsumptionAmount(monthConsumptionAmount);
+        dashboardVo.setMonthRefundAmount(monthRefund);
         dashboardVo.setMonthRegisteredMembers(monthRegistrations.getOrDefault(stationId, 0));
         dashboardVo.setMonthWashOrders(monthOrdersMap.getOrDefault(stationId, 0));
+        dashboardVo.setMonthWashUsers(monthWashUsers);
 
         dashboardVo.setTotalIncome(totalIncome);
         dashboardVo.setTotalConsumptionAmount(totalConsumptionAmount);