shop-sales.vue 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. <template>
  2. <view class="page">
  3. <NavBar title="门店销售" :showBack="true" />
  4. <scroll-view class="list-scroll" scroll-y @scrolltolower="loadMore">
  5. <view class="card-set">
  6. <view class="card" v-for="(item, idx) in list" :key="item.shopId">
  7. <view class="card-head">
  8. <view class="rank-badge" :class="'top' + Math.min(idx + 1, 3)"><text>{{ idx + 1 }}</text></view>
  9. <view class="card-center">
  10. <text class="card-name">{{ item.shopName }}</text>
  11. <text class="card-city" v-if="item.city">{{ item.city }}</text>
  12. </view>
  13. <text class="card-amount">{{ formatMoney(item.totalAmount || 0) }}</text>
  14. </view>
  15. <view class="card-foot">
  16. <text class="foot-stat">订单 {{ item.orderCount || 0 }}</text>
  17. <text class="foot-stat">利润 {{ formatMoney(item.profit || 0) }}</text>
  18. </view>
  19. </view>
  20. </view>
  21. <view class="load-tip" v-if="loading"><view class="dot-row"><view class="pulse-dot"></view><view class="pulse-dot"></view><view class="pulse-dot"></view></view></view>
  22. <view class="end-tip" v-if="!hasMore && list.length > 0"><text>没有更多了</text></view>
  23. </scroll-view>
  24. </view>
  25. </template>
  26. <script setup lang="ts">
  27. import { ref, onMounted } from 'vue';
  28. import NavBar from '@/components/NavBar.vue';
  29. import { get } from '@/utils/request';
  30. import { formatMoney, formatDate } from '@/utils/common';
  31. const list = ref<any[]>([]); const loading = ref(false); const hasMore = ref(true); const page = ref(1);
  32. const loadData = async () => {
  33. loading.value = true;
  34. try {
  35. const end = formatDate(new Date()); const start = formatDate(new Date(Date.now()-30*86400000));
  36. const res = await get('/statistics/shop/list', { page: page.value, pageSize: 20, startDate: start, endDate: end, sortBy: 'totalAmount', sortOrder: 'desc' });
  37. if (page.value === 1) list.value = res.list || []; else list.value = [...list.value, ...(res.list || [])];
  38. hasMore.value = list.value.length < (res.total || 0);
  39. } catch { }
  40. loading.value = false;
  41. };
  42. const loadMore = () => { if (loading.value || !hasMore.value) return; page.value++; loadData(); };
  43. onMounted(() => loadData());
  44. </script>
  45. <style lang="scss" scoped>
  46. .page { height: 100vh; background: $bg-color-page; display: flex; flex-direction: column; }
  47. .list-scroll { flex: 1; height: 0; }
  48. .card-set { padding: 20rpx 24rpx; display: flex; flex-direction: column; gap: 16rpx; }
  49. .card { background: $bg-color-card; border-radius: $radius-md; padding: 20rpx 24rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.05); }
  50. .card-head { display: flex; align-items: center; margin-bottom: 12rpx; }
  51. .rank-badge { width: 44rpx; height: 44rpx; border-radius: 10rpx; display: flex; align-items: center; justify-content: center; margin-right: 16rpx; flex-shrink: 0; }
  52. .rank-badge text { font-size: 24rpx; font-weight: 700; color: #fff; }
  53. .rank-badge.top1 { background: #f59e0b; } .rank-badge.top2 { background: #94a3b8; } .rank-badge.top3 { background: #cd853f; }
  54. .card-center { flex: 1; }
  55. .card-name { font-size: 28rpx; font-weight: 500; color: $text-color-primary; display: block; }
  56. .card-city { font-size: 22rpx; color: $text-color-muted; }
  57. .card-amount { font-size: 28rpx; font-weight: 700; color: $primary-color-dark; white-space: nowrap; }
  58. .card-foot { display: flex; gap: 24rpx; }
  59. .foot-stat { font-size: 22rpx; color: $text-color-muted; }
  60. .load-tip { display: flex; justify-content: center; padding: 36rpx 0; }
  61. .dot-row { display: flex; gap: 8rpx; }
  62. .pulse-dot { width: 9rpx; height: 9rpx; background: $text-color-placeholder; border-radius: 50%; animation: pulse 1.2s ease-in-out infinite; }
  63. .pulse-dot:nth-child(2) { animation-delay: 0.2s; } .pulse-dot:nth-child(3) { animation-delay: 0.4s; }
  64. @keyframes pulse { 0%,80%,100% { transform: scale(0.5); opacity: 0.4; } 40% { transform: scale(1); opacity: 1; } }
  65. .end-tip { text-align: center; padding: 36rpx; font-size: 24rpx; color: $text-color-muted; }
  66. </style>