| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- <template>
- <view class="page">
- <NavBar title="门店详情" :showBack="true" />
- <!-- 加载 -->
- <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>
- <!-- 错误 -->
- <view class="error-state" v-if="!loading && errorMsg">
- <view class="error-icon"></view>
- <text class="error-text">{{ errorMsg }}</text>
- <view class="retry-btn" @click="onRetry"><text>点击重试</text></view>
- </view>
- <scroll-view class="detail-scroll" scroll-y v-if="!loading && shop">
- <view class="detail-content">
- <view class="status-card">
- <view class="status-header">
- <text class="shop-name">{{ shop.name }}</text>
- <view class="shop-status" :class="getStatusClass(shop.status)">
- <view class="status-dot"></view>
- <text>{{ getStatusText(shop.status) }}</text>
- </view>
- </view>
- </view>
- <view class="info-card">
- <text class="section-title">基本信息</text>
- <view class="info-grid">
- <view class="info-item" v-for="f in infoFields" :key="f.label">
- <text class="label">{{ f.label }}</text>
- <text class="value">{{ shop[f.key] || '-' }}</text>
- </view>
- </view>
- </view>
- <view class="info-card">
- <text class="section-title">销售数据</text>
- <view class="sales-grid">
- <view class="sales-item">
- <text class="sales-value">¥{{ formatMoney(shop.todaySales) }}</text>
- <text class="sales-label">今日销售额</text>
- </view>
- <view class="sales-item">
- <text class="sales-value">{{ shop.todayOrders || 0 }}</text>
- <text class="sales-label">今日订单</text>
- </view>
- <view class="sales-item">
- <text class="sales-value">¥{{ formatMoney(shop.monthSales) }}</text>
- <text class="sales-label">本月销售额</text>
- </view>
- </view>
- </view>
- <view class="info-card" v-if="shop.devices && shop.devices.length > 0">
- <text class="section-title">门店设备 ({{ shop.devices.length }})</text>
- <view class="device-list">
- <view class="device-item" v-for="device in shop.devices" :key="device.id">
- <view class="device-info">
- <text class="device-name">{{ device.name }}</text>
- <text class="device-no">{{ device.deviceId }}</text>
- </view>
- <view class="device-status" :class="getDeviceStatusClass(device.status)">
- <view class="status-dot"></view>
- <text>{{ getDeviceStatusText(device.status) }}</text>
- </view>
- </view>
- </view>
- </view>
- <view class="info-card" v-else-if="!loading">
- <text class="section-title">门店设备</text>
- <text class="empty-hint">暂无设备</text>
- </view>
- </view>
- </scroll-view>
- </view>
- </template>
- <script setup lang="ts">
- import { ref } from 'vue';
- import { onLoad } from '@dcloudio/uni-app';
- import NavBar from '@/components/NavBar.vue';
- import { getShopDetail } from '@/api/shop';
- import { ShopStatusText, DeviceStatusText } from '@/utils/constants';
- import { formatMoney as formatMoneyUtil } from '@/utils/common';
- const shop = ref<any>(null);
- const loading = ref(true);
- const errorMsg = ref('');
- let lastId = '';
- const formatMoney = formatMoneyUtil;
- const infoFields = [
- { label: '门店地址', key: 'address' },
- { label: '联系电话', key: 'contactPhone' },
- { label: '负责人', key: 'contactName' },
- { label: '营业时间', key: 'businessHours' }
- ];
- const getStatusText = (status: number) => ShopStatusText[status] || '未知';
- const getDeviceStatusText = (status: number) => DeviceStatusText[status] || '未知';
- const getStatusClass = (status: number) => status === 1 ? 'active' : 'inactive';
- const getDeviceStatusClass = (status: number) => {
- if (status === 1) return 'online';
- if (status === 0) return 'offline';
- return 'maintenance';
- };
- const loadDetail = async (id?: string) => {
- if (!id) { errorMsg.value = '缺少门店参数'; loading.value = false; return; }
- lastId = id;
- loading.value = true;
- errorMsg.value = '';
- try {
- shop.value = await getShopDetail(id);
- } catch {
- errorMsg.value = '加载失败,请检查网络后重试';
- shop.value = null;
- } finally {
- loading.value = false;
- }
- };
- const onRetry = () => loadDetail(lastId);
- onLoad((options?: Record<string, string>) => loadDetail(options?.id));
- </script>
- <style lang="scss" scoped>
- .page { height: 100vh; background: $bg-color-page; display: flex; flex-direction: column; }
- .detail-scroll { flex: 1; height: 0; }
- .detail-content { padding: $spacing-2 $spacing-3; }
- // ====== Loading ======
- .load-tip { display: flex; justify-content: center; padding: 200rpx 0; }
- .dot-row { display: flex; gap: $spacing-1; }
- .pulse-dot { width: 10rpx; height: 10rpx; background: $text-color-placeholder; border-radius: 50%; animation: pulse 1.2s ease-in-out infinite;
- &:nth-child(2) { animation-delay: 0.2s; } &:nth-child(3) { animation-delay: 0.4s; }
- }
- @keyframes pulse { 0%,80%,100% { transform: scale(0.5); opacity: 0.4; } 40% { transform: scale(1); opacity: 1; } }
- // ====== Error ======
- .error-state { display: flex; flex-direction: column; align-items: center; padding: 200rpx $spacing-4;
- .error-icon { width: 80rpx; height: 80rpx; border-radius: 50%; background: $error-color-bg; margin-bottom: $spacing-3; position: relative;
- &::before { content: '!'; position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); font-size: 40rpx; font-weight: 700; color: $error-color; line-height: 1; }
- }
- .error-text { font-size: $font-size-base; color: $text-color-secondary; margin-bottom: $spacing-4; }
- .retry-btn { padding: $spacing-2 $spacing-6; background: $primary-color; border-radius: $radius-full; text { font-size: $font-size-base; color: $text-color-primary; font-weight: 500; } }
- }
- // ====== Cards ======
- .status-card {
- padding: $spacing-4; margin-bottom: $spacing-2; background: $bg-color-card; border-radius: $radius-md; box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
- .status-header { display: flex; justify-content: space-between; align-items: center;
- .shop-name { font-size: $font-size-lg; font-weight: 700; color: $text-color-primary; }
- .shop-status { display: flex; align-items: center; gap: 8rpx; padding: 6rpx 16rpx; border-radius: $radius-full; font-size: 22rpx; font-weight: 500; flex-shrink: 0;
- .status-dot { width: 10rpx; height: 10rpx; border-radius: 50%; }
- &.active { background: $success-color-bg; color: $success-color; .status-dot { background: $success-color; } }
- &.inactive { background: $bg-color-secondary; color: $text-color-muted; .status-dot { background: $text-color-placeholder; } }
- }
- }
- }
- .info-card {
- padding: $spacing-3 $spacing-4; margin-bottom: $spacing-2; background: $bg-color-card; border-radius: $radius-md; box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
- .section-title { display: block; font-size: $font-size-md; font-weight: 600; color: $text-color-primary; margin-bottom: 16rpx; padding-bottom: $spacing-2; border-bottom: 1rpx solid $border-color-light; }
- .empty-hint { display: block; font-size: $font-size-sm; color: $text-color-muted; padding: $spacing-4 0; text-align: center; }
- }
- .info-grid .info-item { display: flex; justify-content: space-between; align-items: center; padding: $spacing-2 0;
- .label { font-size: $font-size-base; color: $text-color-muted; }
- .value { font-size: $font-size-base; color: $text-color-primary; text-align: right; max-width: 60%; }
- }
- .sales-grid { display: flex; padding: 16rpx 0; background: $bg-color-page; border-radius: $radius-sm;
- .sales-item { flex: 1; display: flex; flex-direction: column; align-items: center;
- .sales-value { font-size: $font-size-md; font-weight: 700; color: $text-color-primary; margin-bottom: $spacing-1; }
- .sales-label { font-size: $font-size-xs; color: $text-color-muted; }
- }
- }
- .device-list .device-item { display: flex; justify-content: space-between; align-items: center; padding: $spacing-2 0;
- & + .device-item { border-top: 1rpx solid $border-color-light; }
- .device-info {
- .device-name { display: block; font-size: $font-size-base; color: $text-color-primary; font-weight: 500; margin-bottom: 4rpx; }
- .device-no { font-size: 22rpx; color: $text-color-muted; }
- }
- .device-status { display: flex; align-items: center; gap: 6rpx; padding: 6rpx 14rpx; border-radius: $radius-full; font-size: 20rpx; font-weight: 500; flex-shrink: 0;
- .status-dot { width: 10rpx; height: 10rpx; border-radius: 50%; }
- &.online { background: $success-color-bg; color: $success-color; .status-dot { background: $success-color; } }
- &.offline { background: $bg-color-secondary; color: $text-color-muted; .status-dot { background: $text-color-placeholder; } }
- &.maintenance { background: $warning-color-bg; color: $warning-color; .status-dot { background: $warning-color; } }
- }
- }
- </style>
|