|
|
@@ -1,10 +1,9 @@
|
|
|
<template>
|
|
|
<view class="page">
|
|
|
- <!-- 黄色导航栏 -->
|
|
|
- <NavBar :title="navTitle" :showBack="true" />
|
|
|
+ <NavBar title="订单详情" :showBack="true" />
|
|
|
|
|
|
- <!-- 加载状态 -->
|
|
|
- <view class="load-tip" v-if="!order">
|
|
|
+ <!-- 加载 -->
|
|
|
+ <view class="load-tip" v-if="loading">
|
|
|
<view class="dot-row">
|
|
|
<view class="pulse-dot"></view>
|
|
|
<view class="pulse-dot"></view>
|
|
|
@@ -12,21 +11,26 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <scroll-view class="detail-scroll" scroll-y v-if="order">
|
|
|
+ <!-- 错误 -->
|
|
|
+ <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 && order">
|
|
|
<!-- 状态区域 -->
|
|
|
<view class="status-section">
|
|
|
<view class="status-row">
|
|
|
<view class="status-tag" :class="getPayStatusClass(order.payStatus)">
|
|
|
<text>{{ order.payStatusLabel || getPayStatusText(order.payStatus) }}</text>
|
|
|
</view>
|
|
|
- <text class="order-no-text">订单{{ order.orderNo }}</text>
|
|
|
+ <text class="order-no">订单 {{ order.orderNo }}</text>
|
|
|
</view>
|
|
|
- <text class="status-desc" :class="getPayStatusClass(order.payStatus)">
|
|
|
- {{ getStatusDesc(order.status, order.payStatus) }}
|
|
|
- </text>
|
|
|
+ <text class="status-desc" :class="getPayStatusClass(order.payStatus)">{{ getStatusDesc(order.status, order.payStatus) }}</text>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 视频区域 -->
|
|
|
+ <!-- 视频 -->
|
|
|
<view class="video-section" v-if="order.videoUrl">
|
|
|
<video class="order-video" :src="order.videoUrl" controls></video>
|
|
|
</view>
|
|
|
@@ -36,40 +40,43 @@
|
|
|
<text class="section-title">订单明细</text>
|
|
|
<view class="product-list" v-if="order.products && order.products.length > 0">
|
|
|
<view class="product-item" v-for="item in order.products" :key="item.productId">
|
|
|
- <image
|
|
|
- class="product-img"
|
|
|
- :src="item.pic || '/static/images/default-product.png'"
|
|
|
- mode="aspectFill"
|
|
|
- />
|
|
|
+ <image class="product-img" :src="item.pic || '/static/images/default-product.png'" mode="aspectFill" />
|
|
|
<view class="product-main">
|
|
|
<text class="product-name">{{ item.productName }}</text>
|
|
|
- <text class="product-meta">数量 x{{ item.productNum }}</text>
|
|
|
- <text class="product-meta">单价 ¥{{ formatMoney(item.price) }}</text>
|
|
|
+ <text class="product-meta">x{{ item.productNum }}</text>
|
|
|
</view>
|
|
|
- <view class="product-right">
|
|
|
- <text class="deal-price">¥{{ formatMoney(item.price * item.productNum) }}</text>
|
|
|
+ <view class="product-price">
|
|
|
+ <text class="price-unit">¥{{ formatMoney(item.price) }}</text>
|
|
|
+ <text class="price-total">¥{{ formatMoney(item.price * item.productNum) }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="product-empty" v-else>
|
|
|
+ <view class="empty-doc-icon"></view>
|
|
|
<text>暂无商品明细</text>
|
|
|
</view>
|
|
|
- <view class="amount-row">
|
|
|
- <text class="amount-label">实付款</text>
|
|
|
- <text class="amount-value">¥{{ formatMoney(order.paidAmount) }}</text>
|
|
|
+
|
|
|
+ <view class="amount-section">
|
|
|
+ <view class="amount-row" v-if="order.discountAmount > 0">
|
|
|
+ <text class="amount-label">优惠金额</text>
|
|
|
+ <text class="amount-value discount">-¥{{ formatMoney(order.discountAmount) }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="amount-row total">
|
|
|
+ <text class="amount-label">实付款</text>
|
|
|
+ <text class="amount-value">¥{{ formatMoney(order.paidAmount) }}</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 订单信息 -->
|
|
|
<view class="section">
|
|
|
<text class="section-title">订单信息</text>
|
|
|
+
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">订单编号</text>
|
|
|
<view class="info-value-row">
|
|
|
- <text class="info-value">{{ order.orderNo }}</text>
|
|
|
- <view class="copy-btn" @click="copyText(order.orderNo)">
|
|
|
- <text class="copy-btn-text">复制</text>
|
|
|
- </view>
|
|
|
+ <text class="info-value mono">{{ order.orderNo }}</text>
|
|
|
+ <view class="copy-btn" @click="copyText(order.orderNo)"><text>复制</text></view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
@@ -90,35 +97,37 @@
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">设备编号</text>
|
|
|
- <text class="info-value">{{ order.deviceId || '-' }}</text>
|
|
|
- </view>
|
|
|
- <view class="info-row" v-if="order.deviceAddress">
|
|
|
- <text class="info-label">设备地址</text>
|
|
|
- <text class="info-value">{{ order.deviceAddress }}</text>
|
|
|
+ <text class="info-value mono">{{ order.deviceId || '-' }}</text>
|
|
|
</view>
|
|
|
<view class="info-row" v-if="order.phone">
|
|
|
- <text class="info-label">联系消费者</text>
|
|
|
+ <text class="info-label">消费者</text>
|
|
|
<view class="info-value-row">
|
|
|
<text class="info-value">{{ formatPhone(order.phone) }}</text>
|
|
|
- <view class="action-btn call-btn" @click="callPhone(order.phone)">
|
|
|
- <text class="action-btn-text">呼叫</text>
|
|
|
- </view>
|
|
|
+ <view class="action-btn call-btn" @click="callPhone(order.phone)"><text>呼叫</text></view>
|
|
|
</view>
|
|
|
</view>
|
|
|
+ <view class="info-row" v-if="order.userName">
|
|
|
+ <text class="info-label">用户名</text>
|
|
|
+ <text class="info-value">{{ order.userName }}</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
+
|
|
|
+ <!-- 底部占位 -->
|
|
|
+ <view class="bottom-spacer" v-if="canRefund(order)"></view>
|
|
|
</scroll-view>
|
|
|
|
|
|
- <!-- 底部操作 -->
|
|
|
+ <!-- 退款操作 -->
|
|
|
<view class="bottom-bar" v-if="order && canRefund(order)">
|
|
|
- <view class="refund-btn" @click="handleRefund">
|
|
|
- <text class="refund-btn-text">退款</text>
|
|
|
+ <view class="refund-btn" :class="{ disabled: isRefunding }" @click="handleRefund">
|
|
|
+ <view class="loading-spinner" v-if="isRefunding"></view>
|
|
|
+ <text class="refund-btn-text">{{ isRefunding ? '处理中' : '退款' }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, computed } from 'vue';
|
|
|
+import { ref } from 'vue';
|
|
|
import { onLoad } from '@dcloudio/uni-app';
|
|
|
import NavBar from '@/components/NavBar.vue';
|
|
|
import { getOrderDetail, handleRefund as refundOrder } from '@/api/order';
|
|
|
@@ -139,32 +148,30 @@ interface OrderDetail {
|
|
|
payStatus: string;
|
|
|
payStatusLabel?: string;
|
|
|
paidAmount: number;
|
|
|
+ discountAmount?: number;
|
|
|
createTime: string;
|
|
|
payTime?: string;
|
|
|
shopName?: string;
|
|
|
deviceId?: string;
|
|
|
deviceName?: string;
|
|
|
- deviceAddress?: string;
|
|
|
+ userName?: string;
|
|
|
phone?: string;
|
|
|
videoUrl?: string;
|
|
|
products?: OrderProduct[];
|
|
|
}
|
|
|
|
|
|
const order = ref<OrderDetail | null>(null);
|
|
|
+const loading = ref(true);
|
|
|
+const errorMsg = ref('');
|
|
|
+const isRefunding = ref(false);
|
|
|
+let lastId = '';
|
|
|
+
|
|
|
const formatMoney = formatMoneyUtil;
|
|
|
const formatPhone = formatPhoneUtil;
|
|
|
|
|
|
-const navTitle = computed(() => {
|
|
|
- return `${order.value?.deviceId || '订单'}-订单详情`;
|
|
|
-});
|
|
|
-
|
|
|
const getPayStatusText = (payStatus: string): string => {
|
|
|
- const map: Record<string, string> = {
|
|
|
- 'UNPAID': '未支付',
|
|
|
- 'PAID': '已支付',
|
|
|
- 'REFUND': '已退款'
|
|
|
- };
|
|
|
- return map[payStatus] || '未知';
|
|
|
+ const map: Record<string, string> = { UNPAID: '未支付', PAID: '已支付', REFUND: '已退款' };
|
|
|
+ return map[payStatus] || payStatus || '未知';
|
|
|
};
|
|
|
|
|
|
const getPayStatusClass = (payStatus: string): 'success' | 'danger' | 'warning' => {
|
|
|
@@ -175,26 +182,20 @@ const getPayStatusClass = (payStatus: string): 'success' | 'danger' | 'warning'
|
|
|
|
|
|
const getStatusDesc = (status: number, payStatus: string): string => {
|
|
|
if (payStatus === 'PAID') return '交易成功';
|
|
|
- if (payStatus === 'REFUND') return '已退款';
|
|
|
- if (status === 1) return '等待支付';
|
|
|
- if (status === 0) return '已取消';
|
|
|
- if (status === 3) return '已关闭';
|
|
|
+ if (payStatus === 'REFUND') return '已退款,款项将退回原支付账户';
|
|
|
+ if (status === 0) return '等待消费者支付';
|
|
|
+ if (status === 2) return '订单已取消';
|
|
|
+ if (status === 3) return '订单已关闭';
|
|
|
return '';
|
|
|
};
|
|
|
|
|
|
const canRefund = (o: OrderDetail): boolean => {
|
|
|
- return o.payStatus === 'PAID' && o.status === 2;
|
|
|
+ return o.payStatus === 'PAID' && o.status === 1;
|
|
|
};
|
|
|
|
|
|
const copyText = (text: string) => {
|
|
|
- if (!text) {
|
|
|
- showToast('无内容可复制');
|
|
|
- return;
|
|
|
- }
|
|
|
- uni.setClipboardData({
|
|
|
- data: text,
|
|
|
- success: () => showToast('已复制', 'success')
|
|
|
- });
|
|
|
+ if (!text) { showToast('无内容可复制'); return; }
|
|
|
+ uni.setClipboardData({ data: text, success: () => showToast('已复制', 'success') });
|
|
|
};
|
|
|
|
|
|
const callPhone = (phone: string) => {
|
|
|
@@ -203,30 +204,39 @@ const callPhone = (phone: string) => {
|
|
|
|
|
|
const loadDetail = async (id?: string) => {
|
|
|
if (!id) {
|
|
|
- showToast('订单ID不存在');
|
|
|
+ errorMsg.value = '缺少订单参数';
|
|
|
+ loading.value = false;
|
|
|
return;
|
|
|
}
|
|
|
+ lastId = id;
|
|
|
+ loading.value = true;
|
|
|
+ errorMsg.value = '';
|
|
|
try {
|
|
|
order.value = await getOrderDetail(id);
|
|
|
} catch {
|
|
|
- showToast('加载订单详情失败');
|
|
|
+ errorMsg.value = '加载订单详情失败,请检查网络后重试';
|
|
|
+ order.value = null;
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+const onRetry = () => loadDetail(lastId);
|
|
|
+
|
|
|
const handleRefund = async () => {
|
|
|
- if (!order.value) return;
|
|
|
- const confirmed = await showConfirm('确认对该订单进行退款操作?');
|
|
|
+ if (isRefunding.value || !order.value) return;
|
|
|
+ const confirmed = await showConfirm('确认对该订单进行全额退款?');
|
|
|
if (!confirmed) return;
|
|
|
+
|
|
|
+ isRefunding.value = true;
|
|
|
try {
|
|
|
await refundOrder(order.value.id, '商家申请退款');
|
|
|
- showToast('退款处理成功', 'success');
|
|
|
- setTimeout(() => {
|
|
|
- const pages = getCurrentPages();
|
|
|
- const currentPage = pages[pages.length - 1] as { options?: Record<string, string> };
|
|
|
- loadDetail(currentPage?.options?.id);
|
|
|
- }, 1000);
|
|
|
+ showToast('退款成功', 'success');
|
|
|
+ setTimeout(() => loadDetail(lastId), 1200);
|
|
|
} catch {
|
|
|
- showToast('退款处理失败');
|
|
|
+ showToast('退款失败,请重试');
|
|
|
+ } finally {
|
|
|
+ isRefunding.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -250,23 +260,23 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue',
|
|
|
.detail-scroll {
|
|
|
flex: 1;
|
|
|
height: 0;
|
|
|
- padding-bottom: 140rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.bottom-spacer {
|
|
|
+ height: 120rpx;
|
|
|
}
|
|
|
|
|
|
// ====== Loading ======
|
|
|
.load-tip {
|
|
|
display: flex;
|
|
|
justify-content: center;
|
|
|
- padding: 120rpx 0;
|
|
|
+ padding: 200rpx 0;
|
|
|
}
|
|
|
|
|
|
-.dot-row {
|
|
|
- display: flex;
|
|
|
- gap: 8rpx;
|
|
|
-}
|
|
|
+.dot-row { display: flex; gap: $spacing-1; }
|
|
|
|
|
|
.pulse-dot {
|
|
|
- width: 9rpx; height: 9rpx;
|
|
|
+ width: 10rpx; height: 10rpx;
|
|
|
background: $text-color-placeholder;
|
|
|
border-radius: 50%;
|
|
|
animation: pulse 1.2s ease-in-out infinite;
|
|
|
@@ -279,41 +289,74 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue',
|
|
|
40% { transform: scale(1); opacity: 1; }
|
|
|
}
|
|
|
|
|
|
-// ====== Status section ======
|
|
|
+// ====== 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;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .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; }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ====== Status ======
|
|
|
.status-section {
|
|
|
+ padding: $spacing-4 $spacing-4 $spacing-3;
|
|
|
background: $bg-color-card;
|
|
|
- padding: $spacing-3 $spacing-4;
|
|
|
margin-bottom: $spacing-2;
|
|
|
|
|
|
.status-row {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- margin-bottom: $spacing-1;
|
|
|
+ margin-bottom: 12rpx;
|
|
|
|
|
|
.status-tag {
|
|
|
- padding: 4rpx $spacing-2;
|
|
|
+ padding: 4rpx 16rpx;
|
|
|
border-radius: $radius-sm;
|
|
|
- font-size: $font-size-sm;
|
|
|
+ font-size: 24rpx;
|
|
|
font-weight: 500;
|
|
|
- margin-right: $spacing-2;
|
|
|
-
|
|
|
- &.success {
|
|
|
- background: $success-color-bg;
|
|
|
- color: $success-color;
|
|
|
- }
|
|
|
-
|
|
|
- &.danger {
|
|
|
- background: $error-color-bg;
|
|
|
- color: $error-color;
|
|
|
- }
|
|
|
-
|
|
|
- &.warning {
|
|
|
- background: $warning-color-bg;
|
|
|
- color: $warning-color;
|
|
|
- }
|
|
|
+ margin-right: 16rpx;
|
|
|
+
|
|
|
+ &.success { background: $success-color-bg; color: $success-color; }
|
|
|
+ &.danger { background: $error-color-bg; color: $error-color; }
|
|
|
+ &.warning { background: $warning-color-bg; color: $warning-color; }
|
|
|
}
|
|
|
|
|
|
- .order-no-text {
|
|
|
+ .order-no {
|
|
|
font-size: $font-size-md;
|
|
|
font-weight: 600;
|
|
|
color: $text-color-primary;
|
|
|
@@ -321,108 +364,137 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue',
|
|
|
}
|
|
|
|
|
|
.status-desc {
|
|
|
- font-size: 26rpx;
|
|
|
+ font-size: $font-size-sm;
|
|
|
|
|
|
&.success { color: $success-color; }
|
|
|
- &.danger { color: $error-color; }
|
|
|
+ &.danger { color: $error-color; }
|
|
|
&.warning { color: $warning-color; }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ====== Video ======
|
|
|
.video-section {
|
|
|
- background: $bg-color-card;
|
|
|
- padding: 20rpx $spacing-4;
|
|
|
margin-bottom: $spacing-2;
|
|
|
|
|
|
.order-video {
|
|
|
width: 100%;
|
|
|
height: 400rpx;
|
|
|
- border-radius: $radius-base;
|
|
|
background: $bg-color-secondary;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// ====== Section card ======
|
|
|
+// ====== Section ======
|
|
|
.section {
|
|
|
- background: $bg-color-card;
|
|
|
margin-bottom: $spacing-2;
|
|
|
padding: $spacing-3 $spacing-4;
|
|
|
+ background: $bg-color-card;
|
|
|
|
|
|
.section-title {
|
|
|
display: block;
|
|
|
font-size: $font-size-md;
|
|
|
font-weight: 600;
|
|
|
color: $text-color-primary;
|
|
|
- margin-bottom: $spacing-2;
|
|
|
+ margin-bottom: $spacing-3;
|
|
|
+ padding-bottom: $spacing-2;
|
|
|
+ border-bottom: 1rpx solid $border-color-light;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ====== Product list ======
|
|
|
-.product-list {
|
|
|
- .product-item {
|
|
|
- display: flex;
|
|
|
- align-items: flex-start;
|
|
|
- padding: $spacing-2 0;
|
|
|
- border-bottom: 1rpx solid $border-color-light;
|
|
|
+.product-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ padding: $spacing-2 0;
|
|
|
+ border-bottom: 1rpx solid $border-color-light;
|
|
|
|
|
|
- &:last-child {
|
|
|
- border-bottom: none;
|
|
|
- }
|
|
|
+ &:last-child { border-bottom: none; }
|
|
|
|
|
|
- .product-img {
|
|
|
- width: 120rpx;
|
|
|
- height: 120rpx;
|
|
|
- border-radius: $radius-sm;
|
|
|
- background: $bg-color-page;
|
|
|
- margin-right: 20rpx;
|
|
|
- flex-shrink: 0;
|
|
|
+ .product-img {
|
|
|
+ width: 100rpx;
|
|
|
+ height: 100rpx;
|
|
|
+ border-radius: $radius-sm;
|
|
|
+ background: $bg-color-page;
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-right: 16rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .product-main {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .product-name {
|
|
|
+ display: block;
|
|
|
+ font-size: $font-size-base;
|
|
|
+ color: $text-color-primary;
|
|
|
+ margin-bottom: 4rpx;
|
|
|
}
|
|
|
|
|
|
- .product-main {
|
|
|
- flex: 1;
|
|
|
-
|
|
|
- .product-name {
|
|
|
- display: block;
|
|
|
- font-size: $font-size-base;
|
|
|
- color: $text-color-primary;
|
|
|
- margin-bottom: $spacing-1;
|
|
|
- }
|
|
|
-
|
|
|
- .product-meta {
|
|
|
- display: block;
|
|
|
- font-size: $font-size-sm;
|
|
|
- color: $text-color-muted;
|
|
|
- margin-bottom: 4rpx;
|
|
|
- }
|
|
|
+ .product-meta {
|
|
|
+ font-size: $font-size-sm;
|
|
|
+ color: $text-color-muted;
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ .product-price {
|
|
|
+ text-align: right;
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
|
- .product-right {
|
|
|
- display: flex;
|
|
|
- align-items: flex-end;
|
|
|
+ .price-unit {
|
|
|
+ display: block;
|
|
|
+ font-size: $font-size-sm;
|
|
|
+ color: $text-color-muted;
|
|
|
+ margin-bottom: 4rpx;
|
|
|
+ }
|
|
|
|
|
|
- .deal-price {
|
|
|
- font-size: $font-size-sm;
|
|
|
- color: $text-color-muted;
|
|
|
- }
|
|
|
+ .price-total {
|
|
|
+ font-size: $font-size-base;
|
|
|
+ font-weight: 600;
|
|
|
+ color: $text-color-primary;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.product-empty {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
padding: $spacing-5 0;
|
|
|
- text-align: center;
|
|
|
- font-size: $font-size-sm;
|
|
|
- color: $text-color-placeholder;
|
|
|
+
|
|
|
+ .empty-doc-icon {
|
|
|
+ width: 48rpx;
|
|
|
+ height: 56rpx;
|
|
|
+ border: 2.5rpx solid $text-color-placeholder;
|
|
|
+ border-radius: 4rpx;
|
|
|
+ margin-bottom: $spacing-2;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 16rpx;
|
|
|
+ left: 10rpx;
|
|
|
+ right: 10rpx;
|
|
|
+ height: 2.5rpx;
|
|
|
+ background: $text-color-placeholder;
|
|
|
+ border-radius: 1rpx;
|
|
|
+ box-shadow: 0 10rpx 0 $text-color-placeholder, 0 20rpx 0 $text-color-placeholder;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ text { font-size: $font-size-sm; color: $text-color-placeholder; }
|
|
|
+}
|
|
|
+
|
|
|
+// ====== Amount ======
|
|
|
+.amount-section {
|
|
|
+ margin-top: $spacing-2;
|
|
|
+ padding-top: $spacing-2;
|
|
|
+ border-top: 1rpx solid $border-color-light;
|
|
|
}
|
|
|
|
|
|
.amount-row {
|
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
align-items: center;
|
|
|
- padding: $spacing-2 0 0;
|
|
|
- border-top: 1rpx solid $border-color-light;
|
|
|
- margin-top: $spacing-2;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 8rpx 0;
|
|
|
|
|
|
.amount-label {
|
|
|
font-size: $font-size-base;
|
|
|
@@ -430,23 +502,29 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue',
|
|
|
}
|
|
|
|
|
|
.amount-value {
|
|
|
- font-size: $font-size-lg;
|
|
|
- font-weight: 700;
|
|
|
+ font-size: $font-size-base;
|
|
|
+ font-weight: 600;
|
|
|
color: $text-color-primary;
|
|
|
+
|
|
|
+ &.discount { color: $error-color; font-weight: 500; }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.total {
|
|
|
+ padding-top: 16rpx;
|
|
|
+
|
|
|
+ .amount-value { font-size: $font-size-lg; font-weight: 700; }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// ====== Info rows ======
|
|
|
+// ====== Info ======
|
|
|
.info-row {
|
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
padding: $spacing-3 0;
|
|
|
border-bottom: 1rpx solid $border-color-light;
|
|
|
|
|
|
- &:last-child {
|
|
|
- border-bottom: none;
|
|
|
- }
|
|
|
+ &:last-child { border-bottom: none; }
|
|
|
|
|
|
.info-label {
|
|
|
font-size: $font-size-base;
|
|
|
@@ -458,78 +536,83 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue',
|
|
|
font-size: $font-size-base;
|
|
|
color: $text-color-primary;
|
|
|
text-align: right;
|
|
|
+
|
|
|
+ &.mono { letter-spacing: 0.02em; }
|
|
|
}
|
|
|
|
|
|
.info-value-row {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: $spacing-2;
|
|
|
+ gap: 16rpx;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// ====== Action buttons ======
|
|
|
+// ====== Buttons ======
|
|
|
.copy-btn, .action-btn {
|
|
|
- padding: 8rpx $spacing-3;
|
|
|
+ padding: 6rpx 20rpx;
|
|
|
border-radius: $radius-sm;
|
|
|
flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
|
|
|
+ text { font-size: 22rpx; line-height: 1; }
|
|
|
&:active { opacity: 0.7; }
|
|
|
}
|
|
|
|
|
|
.copy-btn {
|
|
|
background: $bg-color-page;
|
|
|
|
|
|
- .copy-btn-text {
|
|
|
- font-size: $font-size-xs;
|
|
|
- color: $text-color-secondary;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.action-btn {
|
|
|
- .action-btn-text {
|
|
|
- font-size: $font-size-xs;
|
|
|
- font-weight: 500;
|
|
|
- }
|
|
|
+ text { color: $text-color-secondary; }
|
|
|
}
|
|
|
|
|
|
.call-btn {
|
|
|
background: $primary-color-bg;
|
|
|
|
|
|
- .action-btn-text {
|
|
|
- color: $primary-color-dark;
|
|
|
- }
|
|
|
+ text { color: $primary-color-dark; font-weight: 500; }
|
|
|
}
|
|
|
|
|
|
-// ====== Bottom bar ======
|
|
|
+// ====== Bottom ======
|
|
|
.bottom-bar {
|
|
|
position: fixed;
|
|
|
bottom: 0;
|
|
|
left: 0;
|
|
|
right: 0;
|
|
|
- background: $bg-color-card;
|
|
|
padding: $spacing-3 $spacing-4;
|
|
|
- padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
|
|
+ padding-bottom: calc($spacing-3 + env(safe-area-inset-bottom));
|
|
|
+ background: $bg-color-card;
|
|
|
border-top: 1rpx solid $border-color-light;
|
|
|
- flex-shrink: 0;
|
|
|
+}
|
|
|
|
|
|
- .refund-btn {
|
|
|
- width: 100%;
|
|
|
- background: $primary-color;
|
|
|
- border-radius: $radius-xl;
|
|
|
- padding: 28rpx 0;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- transition: opacity 0.15s ease;
|
|
|
+.refund-btn {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ gap: $spacing-2;
|
|
|
+ width: 100%;
|
|
|
+ padding: 26rpx 0;
|
|
|
+ border-radius: $radius-xl;
|
|
|
+ background: $primary-color;
|
|
|
+ transition: opacity 0.15s ease;
|
|
|
|
|
|
- &:active { opacity: 0.8; }
|
|
|
+ &:active { opacity: 0.8; }
|
|
|
|
|
|
- .refund-btn-text {
|
|
|
- font-size: $font-size-lg;
|
|
|
- font-weight: 600;
|
|
|
- color: $text-color-primary;
|
|
|
- }
|
|
|
+ &.disabled { opacity: 0.6; pointer-events: none; }
|
|
|
+
|
|
|
+ .refund-btn-text {
|
|
|
+ font-size: $font-size-md;
|
|
|
+ font-weight: 600;
|
|
|
+ color: $text-color-primary;
|
|
|
}
|
|
|
}
|
|
|
-</style>
|
|
|
|
|
|
+.loading-spinner {
|
|
|
+ width: 32rpx;
|
|
|
+ height: 32rpx;
|
|
|
+ border: 3rpx solid rgba(30, 41, 59, 0.15);
|
|
|
+ border-top-color: $text-color-primary;
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: spin 0.8s linear infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes spin { to { transform: rotate(360deg); } }
|
|
|
+</style>
|