Переглянути джерело

feat(退款详情): 优化退款详情对话框的交互和显示逻辑

重构退款详情对话框组件,主要变更包括:
- 使用 el-countdown 组件替换原有倒计时显示
- 根据退款类型区分显示不同的流程步骤和提示信息
- 增加退款金额确认对话框
- 优化状态显示和操作按钮的可用性逻辑
ylong 2 тижнів тому
батько
коміт
f1de80a80b
1 змінених файлів з 353 додано та 262 видалено
  1. 353 262
      src/views/mallOrder/refund/components/refund-detail-dialog.vue

+ 353 - 262
src/views/mallOrder/refund/components/refund-detail-dialog.vue

@@ -8,27 +8,32 @@
                     <div class="status-info">
                         <div class="text-xl font-bold mb-2 flex items-center">
                             <span>{{ getStatusText(form.status) }}</span>
-                            <span class="text-orange-500 text-sm ml-3 font-normal"
-                                v-if="['1', '2', '3', '7', '8', '9'].includes(String(form.status))">还剩 {{ form.countDown
-                                    || '1天11时19分' }}</span>
                         </div>
+
+                        <!-- 倒计时组件 -->
+                        <div v-if="deadline > 0" class="flex items-center text-red-500 text-sm mb-3">
+                            <span class="mr-2">剩余</span>
+                            <el-countdown format="DD [天] HH [时] mm [分]" :value="deadline" :value-style="{ fontSize: '14px', color: '#ef4444', lineHeight: '1' }" />
+                        </div>
+
                         <div class="text-sm text-gray-500 space-y-1">
                             <template v-if="['1', '3'].includes(String(form.status))">
-                                <div>请及时联系买家协商退货事宜</div>
+                                <div>{{
+                                    isRefundOnly
+                                        ? '请及时联系买家协商退款事宜'
+                                        : '请及时联系买家协商退货事宜'
+                                }}</div>
                                 <div v-if="form.refundType == 1">该退款为卖家原因退款,若您同意买家退货,退货运费将由您承担。</div>
                             </template>
 
-                            <template v-else-if="String(form.status) === '8'">
-                                <div>收到买家退货时,请验货后同意退款</div>
-                                <div>如果买家在超时结束前未退货,退货申请将自动关闭</div>
-                            </template>
-
-                            <template v-else-if="String(form.status) === '9'">
-                                <div>买家已退货,收到买家退货时,请验货后同意退款</div>
+                            <template v-else-if="
+                                String(form.status) === '7' && String(form.refundType) === '1'
+                            ">
+                                <div>买家已发货,请及时验货并处理退款</div>
                             </template>
 
                             <template v-else-if="String(form.status) === '7'">
-                                <div>因买家信誉良好,平台已优先垫付退款给买家</div>
+                                <div>买家已发货,请及时验货并处理退款</div>
                             </template>
 
                             <template v-else-if="String(form.status) === '2'">
@@ -36,20 +41,31 @@
                                 <div>如果买家超时未响应,退款申请将自动关闭</div>
                             </template>
 
-                            <template v-else-if="String(form.status) === '10'">
+                            <template v-else-if="String(form.status) === '9'">
                                 <div>退款成功时间:{{ form.finishTime }}</div>
-                                <div>退款金额:¥ {{ form.refundMoneyFinal || form.refundMoney }}元</div>
-                                <div>退款规则:{{ form.shopStatus === '1' ? '符合未发货秒退' : '不符合未发货秒退' }}
+                                <div>退款金额:¥
+                                    {{
+                                        form.refundMoneyFinal ||
+                                        form.refundMoney
+                                    }}元</div>
+                                <div>退款规则:符合未发货秒退
                                 </div>
                             </template>
                         </div>
                     </div>
-                    <div style="min-width:600px" class="pt-2">
-                        <el-steps :active="currentStep" align-center finish-status="success">
-                            <el-step title="买家申请退货退款" />
-                            <el-step title="卖家处理退货申请" />
-                            <el-step title="买家退货" />
-                            <el-step title="退款完毕" />
+                    <div style="min-width: 600px" class="pt-2">
+                        <el-steps :active="currentStep" align-center finish-status="success" :process-status="processStatus">
+                            <template v-if="isRefundOnly">
+                                <el-step :title="`买家申请${getRefundTypeText(form.refundType)}`" />
+                                <el-step title="卖家处理退款申请" />
+                                <el-step title="退款完毕" />
+                            </template>
+                            <template v-else>
+                                <el-step title="买家申请退货退款" />
+                                <el-step title="卖家处理退货申请" />
+                                <el-step title="买家退货" />
+                                <el-step title="退款完毕" />
+                            </template>
                         </el-steps>
                     </div>
                 </div>
@@ -70,7 +86,7 @@
                     </div>
                     <div class="info-item">
                         <span class="label">申请件数:</span>
-                        <span class="value">{{ form.totalNum || refundCount }}</span>
+                        <span class="value">{{ form.totalNum }}</span>
                     </div>
                     <div class="info-item">
                         <span class="label">退款原因:</span>
@@ -78,11 +94,15 @@
                     </div>
                     <div class="info-item">
                         <span class="label">要求:</span>
-                        <span class="value">{{ getRefundTypeText(form.refundType) }}</span>
+                        <span class="value">{{
+                            getRefundTypeText(form.refundType)
+                            }}</span>
                     </div>
                     <div class="info-item">
                         <span class="label">货物状态:</span>
-                        <span class="value">{{ getShopStatusText(form.shopStatus) }}</span>
+                        <span class="value">{{
+                            getShopStatusText(form.shopStatus)
+                            }}</span>
                     </div>
                     <div class="info-item">
                         <span class="label">买家留言:</span>
@@ -105,8 +125,10 @@
                             <el-image :src="prod.cover" class="w-16 h-16 rounded mr-2" fit="cover" />
                             <div class="flex-1">
                                 <div class="text-sm font-bold truncate-2-lines">{{ prod.bookName }}</div>
-                                <div class="text-xs text-gray-500 mt-1">{{ prod.isbn }}</div>
-                                <div class="text-xs text-gray-500 mt-1">{{ prod.price }} × {{ prod.refundNum }}</div>
+                                <div class="text-xs text-gray-500 mt-1">{{
+                                    prod.isbn
+                                    }}</div>
+                                <div class="text-xs text-gray-500 mt-1">{{ prod.payPrice }} × {{ prod.num }}</div>
                             </div>
                         </div>
                     </div>
@@ -115,12 +137,12 @@
                         <span class="label">买家:</span>
                         <div class="flex items-center">
                             <el-avatar :size="20" :src="form.avatar" class="mr-2" />
-                            <span class="value text-blue-500">{{ form.userNick }}</span>
+                            <span class="value text-blue-500">{{ form.userNick }}/{{ form.userId }}</span>
                         </div>
                     </div>
                     <div class="info-item">
                         <span class="label">申请退款:</span>
-                        <span class="value">¥{{ form.refundMoney || form.refundMoney }}</span>
+                        <span class="value">¥{{ form.refundMoney || '0.00' }}</span>
                     </div>
                     <div class="info-item">
                         <span class="label">邮费:</span>
@@ -136,7 +158,9 @@
                     </div>
                     <div class="info-item">
                         <span class="label">订单编号:</span>
-                        <span class="value text-blue-500">{{ form.originOrderId }}</span>
+                        <span class="value text-blue-500">{{
+                            form.originOrderId
+                            }}</span>
                         <el-icon class="copy-icon" @click="handleCopy(form.originOrderId)">
                             <CopyDocument />
                         </el-icon>
@@ -174,18 +198,27 @@
                             </div>
                             <div class="content-wrapper flex-1">
                                 <div class="header flex justify-between items-start mb-1">
-                                    <span class="name font-bold text-gray-800">{{ activity.userName ||
-                                        (activity.userType === '2' ?
-                                            '客服' : '用户') }}</span>
-                                    <span class="time text-xs text-gray-400">{{ activity.createTime }}</span>
+                                    <span class="name font-bold text-gray-800">{{
+                                        activity.userName ||
+                                        (activity.userType === '2'
+                                            ? '客服'
+                                            : '用户')
+                                    }}</span>
+                                    <span class="time text-xs text-gray-400">{{
+                                        activity.createTime
+                                        }}</span>
                                 </div>
                                 <div class="main-content text-sm text-gray-600">
                                     <div v-if="activity.title" class="font-bold mb-1 text-gray-900">{{ activity.title }}
                                     </div>
-                                    <div class="whitespace-pre-wrap mb-2">{{ activity.content }}</div>
-
-                                    <div v-if="activity.imgList && activity.imgList.length"
-                                        class="mt-2 flex flex-wrap gap-2">
+                                    <div class="whitespace-pre-wrap mb-2">{{
+                                        activity.content
+                                        }}</div>
+
+                                    <div v-if="
+                                        activity.imgList &&
+                                        activity.imgList.length
+                                    " class="mt-2 flex flex-wrap gap-2">
                                         <el-image v-for="(img, i) in activity.imgList" :key="i" :src="img"
                                             class="w-20 h-20 rounded border border-gray-200"
                                             :preview-src-list="activity.imgList" fit="cover" />
@@ -198,274 +231,332 @@
             </div>
 
             <div class="policy-tip mt-4 text-xs text-gray-400">
-                · 如果您同意,请点击“同意退货”,将正确退货地址发给买家。<br>
-                · 如果您拒绝,买家可以申请客服介入。<br>
-                · 如果您逾期未处理申请,视作同意买家申请。系统会自动将当前交易的退货地址发给买家<br>
+                <template v-if="isRefundOnly">
+                    · 如果您同意,请点击“同意退款”,退款将立即退到买家账户。<br />
+                    · 如果您拒绝,买家可以申请客服介入。<br />
+                    · 如果您逾期未处理申请,视作同意买家申请。<br />
+                </template>
+                <template v-else>
+                    · 如果您同意,请点击“同意退货”,将正确退货地址发给买家。<br />
+                    · 如果您拒绝,买家可以申请客服介入。<br />
+                    ·
+                    如果您逾期未处理申请,视作同意买家申请。系统会自动将当前交易的退货地址发给买家<br />
+                </template>
             </div>
-
         </div>
 
         <template #footer>
             <div class="flex justify-center gap-4">
-                <el-button>备注</el-button>
-                <el-button @click="handleRefuse" :disabled="!canOperate">拒绝退货申请</el-button>
-                <el-button @click="handleAgree" :disabled="!canOperate">同意退货</el-button>
-                <el-button type="primary" @click="handleNegotiate" :disabled="!canOperate">与买家协商</el-button>
+                <el-button :disabled="!canOperate && !['4', '5'].includes(String(form.status))">备注</el-button>
+                <el-button @click="handleRefuse" :disabled="!canOperate">{{
+                    isRefundOnly ? '拒绝退款申请' : '拒绝退货申请'
+                    }}</el-button>
+                <el-button @click="handleAgree" :disabled="!canOperate">{{
+                    isRefundOnly ? '同意退款' : '同意退货'
+                    }}</el-button>
+                <el-button type="primary" @click="handleNegotiate" :disabled="!canOperate && !['4', '5'].includes(String(form.status))">与买家协商</el-button>
             </div>
         </template>
         <negotiation-apply-dialog v-model="negotiationDialogVisible" :refund-order-id="form.refundOrderId"
             :max-refund-amount="Number(form.refundMoney) || 0" @success="handleNegotiationSuccess" />
         <refuse-dialog ref="refuseDialogRef" @success="handleNegotiationSuccess" />
         <agree-dialog ref="agreeDialogRef" @success="handleNegotiationSuccess" />
+        <confirm-refund-dialog ref="confirmRefundDialogRef" @success="handleNegotiationSuccess" />
     </ele-modal>
 </template>
 
 <script setup>
-    import { ref, reactive, computed } from 'vue';
-    import { EleMessage } from 'ele-admin-plus/es';
-    import { CopyDocument } from '@element-plus/icons-vue';
-    import { useClipboard } from '@vueuse/core';
-    import request from '@/utils/request';
-    import NegotiationApplyDialog from './negotiation-apply-dialog.vue';
-    import RefuseDialog from './refuse-dialog.vue';
-    import AgreeDialog from './agree-dialog.vue';
-
-    const visible = defineModel({ type: Boolean });
-    const loading = ref(false);
-    const form = ref({});
-    const historyList = ref([]);
-    const { copy } = useClipboard();
-    const negotiationDialogVisible = ref(false);
-    const refuseDialogRef = ref(null);
-    const agreeDialogRef = ref(null);
-
-    const refundCount = computed(() => {
-        if (form.value.totalNum) return form.value.totalNum;
-        if (!form.value.detailList) return 0;
-        return form.value.detailList.reduce((sum, item) => sum + (Number(item.refundNum) || 0), 0);
-    });
-
-    // 当前步骤
-    const currentStep = computed(() => {
-        const status = Number(form.value.status);
-        // 1-申请退款
-        if (status === 1) return 1;
-        // 2-协商中待用户确认 3-协商中待商家确认 -> 都在协商/处理中
-        if (status === 2 || status === 3) return 2;
-        // 7-卖家已发货 -> 可能是退货流程的一部分
-        if (status === 7) return 3;
-        // 4-审核通过 5-审核驳回 6-超时关闭 -> 结束状态
-        if ([4, 5, 6].includes(status)) return 4;
+import { ref, computed } from 'vue';
+import { EleMessage } from 'ele-admin-plus/es';
+import { CopyDocument } from '@element-plus/icons-vue';
+import { useClipboard } from '@vueuse/core';
+import request from '@/utils/request';
+import NegotiationApplyDialog from './negotiation-apply-dialog.vue';
+import RefuseDialog from './refuse-dialog.vue';
+import AgreeDialog from './agree-dialog.vue';
+import ConfirmRefundDialog from './confirm-refund-dialog.vue';
+
+const visible = defineModel({ type: Boolean });
+const loading = ref(false);
+const form = ref({});
+const historyList = ref([]);
+const { copy } = useClipboard();
+const negotiationDialogVisible = ref(false);
+const refuseDialogRef = ref(null);
+const agreeDialogRef = ref(null);
+const confirmRefundDialogRef = ref(null);
+
+const deadline = ref(0);
+
+const isRefundOnly = computed(() => ['0', '2'].includes(String(form.value.refundType)));
+
+const refundCount = computed(() => {
+    if (form.value.totalNum) return form.value.totalNum;
+    if (!form.value.detailList) return 0;
+    return form.value.detailList.reduce(
+        (sum, item) => sum + (Number(item.refundNum) || 0),
+        0
+    );
+});
+
+// 进度条状态
+const processStatus = computed(() => {
+    const status = Number(form.value.status);
+    if ([5, 6].includes(status)) {
+        return 'error';
+    }
+    return 'process';
+});
+
+// 当前步骤
+const currentStep = computed(() => {
+    const status = Number(form.value.status);
+    if (isRefundOnly.value) {
+        // 1:申请, 2,3:协商中, 5:驳回, 6:超时关闭 -> 停在第2步(卖家处理)
+        if ([1, 2, 3, 5, 6].includes(status)) return 1;
+        // 4:审核通过, 7:买家已发货, 8:确认收货 -> 停在第3步(退款完毕前)
+        if ([4, 7, 8].includes(status)) return 2;
+        // 9:退款成功 -> 全绿(第3步完成)
+        if (status === 9) return 3;
         return 1;
-    });
-
-    // 是否可操作(仅在待商家处理状态下可用)
-    const canOperate = computed(() => {
-        // 1: 申请退款, 3: 协商中待商家确认
-        return ['1', '3'].includes(String(form.value.status));
-    });
-
-    const handleOpen = (row) => {
-        if (row && row.refundOrderId) {
-            visible.value = true;
-            loading.value = true;
-
-            request.get(`/shop/shopOrder/getRefundInfo/${row.refundOrderId}`)
-                .then(res => {
-                    if (res.data.code === 200) {
-                        const data = res.data.data || {};
-                        form.value = data;
-                        if (!form.value.orderTime) {
-                            form.value.orderTime = form.value.createTime;
-                        }
-                        // 处理协商历史数据
-                        if (data.complaintsLogList && data.complaintsLogList.length) {
-                            historyList.value = data.complaintsLogList;
-                        } else {
-                            historyList.value = [];
-                        }
+    }
+
+    // 退货退款
+    // 1:申请, 2,3:协商中, 5:驳回, 6:超时关闭 -> 停在第2步(卖家处理)
+    if ([1, 2, 3, 5, 6].includes(status)) return 1;
+    // 4:审核通过(商家同意退货,等待买家退货) -> 停在第3步(买家退货)
+    if (status === 4) return 2;
+    // 7:买家已发货, 8:确认收货 -> 停在第4步(退款完毕前)
+    if ([7, 8].includes(status)) return 3;
+    // 9:退款成功 -> 全绿(第4步完成)
+    if (status === 9) return 4;
+    return 1;
+});
+
+// 是否可操作主流程(拒绝、同意)
+const canOperate = computed(() => {
+    return ['1', '3'].includes(String(form.value.status));
+});
+
+const handleOpen = (row) => {
+    if (row && row.refundOrderId) {
+        visible.value = true;
+        loading.value = true;
+
+        request
+            .get(`/shop/shopOrder/getRefundInfo/${row.refundOrderId}`)
+            .then((res) => {
+                if (res.data.code === 200) {
+                    const data = res.data.data || {};
+                    form.value = data;
+                    if (!form.value.orderTime) {
+                        form.value.orderTime = form.value.createTime;
+                    }
+                    if (data.restSecond) {
+                        deadline.value = Date.now() + data.restSecond * 1000;
+                    } else {
+                        deadline.value = 0;
+                    }
+                    // 处理协商历史数据
+                    if (
+                        data.complaintsLogList &&
+                        data.complaintsLogList.length
+                    ) {
+                        historyList.value = data.complaintsLogList;
                     } else {
-                        EleMessage.error(res.data.msg || '获取详情失败');
-                        fallbackToRow(row);
+                        historyList.value = [];
                     }
-                })
-                .catch(e => {
-                    console.error(e);
-                    EleMessage.error('获取详情失败');
+                } else {
+                    EleMessage.error(res.data.msg || '获取详情失败');
                     fallbackToRow(row);
-                })
-                .finally(() => {
-                    loading.value = false;
-                });
-        } else if (row) {
-            visible.value = true;
-            fallbackToRow(row);
-        }
-    };
-
-    const fallbackToRow = (row) => {
-        form.value = JSON.parse(JSON.stringify(row));
-        if (!form.value.orderTime) {
-            form.value.orderTime = form.value.createTime;
-        }
-        historyList.value = [];
-
-        // Fallback时也添加初始申请记录
-        const startEvent = {
-            createTime: form.value.createTime,
-            userName: form.value.userNick,
-            userType: '1',
-            imgPath: form.value.avatar || '',
-            title: '发起了退款申请',
-            content: `货物状态:${getShopStatusText(form.value.shopStatus)}\n原因:${form.value.refundReason}\n金额:¥${form.value.refundMoney}\n说明:${form.value.description || '无'}`,
-            imgList: []
-        };
-        historyList.value.push(startEvent);
-    };
-
-    const handleCopy = async (text) => {
-        try {
-            await copy(text);
-            EleMessage.success('复制成功');
-        } catch (e) {
-            EleMessage.error('复制失败');
-        }
-    };
-
-    const mockHistory = (row) => {
-        // 移除模拟数据
-    };
-
-    const getRefundTypeText = (type) => {
-        const map = { '0': '极速退款', '1': '退货退款', '2': '仅退款' };
-        return map[type] || type;
-    };
+                }
+            })
+            .catch((e) => {
+                console.error(e);
+                EleMessage.error('获取详情失败');
+                fallbackToRow(row);
+            })
+            .finally(() => {
+                loading.value = false;
+            });
+    } else if (row) {
+        visible.value = true;
+        fallbackToRow(row);
+    }
+};
 
-    const getStatusText = (status) => {
-        const map = {
-            '1': '请处理退货申请', // 原 申请退款
-            '2': '请等待买家响应', // 协商中待用户确认
-            '3': '请处理退货申请', // 协商中待商家确认
-            '4': '审核通过',
-            '5': '审核驳回',
-            '6': '超时关闭',
-            '7': '拦截快递后处理退款', // 卖家已发货
-            '8': '请等待买家退货',
-            '9': '请确认收货',
-            '10': '退款成功'
-        };
-        return map[status] || status;
+const fallbackToRow = (row) => {
+    form.value = JSON.parse(JSON.stringify(row));
+    if (!form.value.orderTime) {
+        form.value.orderTime = form.value.createTime;
+    }
+    if (form.value.restSecond) {
+        deadline.value = Date.now() + form.value.restSecond * 1000;
+    } else {
+        deadline.value = 0;
+    }
+    historyList.value = [];
+
+    // Fallback时也添加初始申请记录
+    const startEvent = {
+        createTime: form.value.createTime,
+        userName: form.value.userNick,
+        userType: '1',
+        imgPath: form.value.avatar || '',
+        title: '发起了退款申请',
+        content: `货物状态:${getShopStatusText(form.value.shopStatus)}\n原因:${form.value.refundReason}\n金额:¥${form.value.refundMoney}\n说明:${form.value.description || '无'}`,
+        imgList: []
     };
-
-    const getShopStatusText = (status) => {
-        const map = { '1': '未收到货', '2': '已收到货' };
-        return map[status] || '-';
+    historyList.value.push(startEvent);
+};
+
+const handleCopy = async (text) => {
+    try {
+        await copy(text);
+        EleMessage.success('复制成功');
+    } catch (e) {
+        EleMessage.error('复制失败');
+    }
+};
+
+const mockHistory = (row) => {
+    // 移除模拟数据
+};
+
+const getRefundTypeText = (type) => {
+    const map = { 0: '极速退款', 1: '退货退款', 2: '仅退款' };
+    return map[type] || type;
+};
+
+const getStatusText = (status) => {
+    const key = String(status);
+    const map = {
+        1: '申请退款',
+        2: '协商中待用户确认',
+        3: '协商中待商家确认',
+        4: '审核通过',
+        5: '审核驳回',
+        6: '超时关闭',
+        7: '买家已发货',
+        8: '确认收货',
+        9: '退款成功'
     };
+    return map[key] || status;
+};
 
-    const handleNegotiate = () => {
-        negotiationDialogVisible.value = true;
-    };
+const getShopStatusText = (status) => {
+    const map = { 1: '未收到货', 2: '已收到货' };
+    return map[status] || '-';
+};
 
-    const handleNegotiationSuccess = () => {
-        // Refresh data
-        handleOpen(form.value);
-    };
+const handleNegotiate = () => {
+    negotiationDialogVisible.value = true;
+};
 
-    const handleRefuse = () => {
-        if (refuseDialogRef.value) {
-            refuseDialogRef.value.open(form.value.refundOrderId);
-        }
-    };
+const handleNegotiationSuccess = () => {
+    handleOpen(form.value);
+};
 
-    const handleAgree = () => {
-        if (agreeDialogRef.value) {
-            agreeDialogRef.value.open(form.value.refundOrderId);
-        }
-    };
+const handleRefuse = () => {
+    if (refuseDialogRef.value) {
+        refuseDialogRef.value.open(form.value.refundOrderId);
+    }
+};
+
+const handleAgree = () => {
+    if (isRefundOnly.value) {
+        confirmRefundDialogRef.value?.open(
+            form.value.refundOrderId,
+            form.value.refundMoneyFinal || form.value.refundMoney
+        );
+        return;
+    }
+    agreeDialogRef.value?.open(form.value.refundOrderId);
+};
 
-    const handleLeaveMessage = () => {
-        EleMessage.info('点击了我要留言');
-    };
+const handleLeaveMessage = () => {
+    EleMessage.info('点击了我要留言');
+};
 
-    defineExpose({
-        handleOpen
-    });
+defineExpose({
+    handleOpen
+});
 </script>
 
 <style scoped lang="scss">
-    .status-bar {
-        background: #fff;
-        padding: 15px;
-        border-bottom: 1px solid #eee;
-    }
-
-    .detail-container {
-        gap: 20px;
+.status-bar {
+    background: #fff;
+    padding: 15px;
+    border-bottom: 1px solid #eee;
+}
 
-        .section {
-            background: #fff;
-
-            .section-title {
-                font-size: 16px;
-                font-weight: bold;
-                margin-bottom: 15px;
-                color: #333;
-                padding-left: 10px;
-                border-left: 3px solid #409eff;
-                line-height: 1;
-            }
+.detail-container {
+    gap: 20px;
 
-            .info-item {
-                margin-bottom: 10px;
-                font-size: 13px;
-                display: flex;
+    .section {
+        background: #fff;
 
-                .label {
-                    color: #999;
-                    width: 70px;
-                    flex-shrink: 0;
-                }
+        .section-title {
+            font-size: 16px;
+            font-weight: bold;
+            margin-bottom: 15px;
+            color: #333;
+            padding-left: 10px;
+            border-left: 3px solid #409eff;
+            line-height: 1;
+        }
 
-                .value {
-                    color: #333;
-                    flex: 1;
-                    word-break: break-all;
-                }
+        .info-item {
+            margin-bottom: 10px;
+            font-size: 13px;
+            display: flex;
 
-                .copy-icon {
-                    cursor: pointer;
-                    color: #409eff;
-                    margin-left: 5px;
-                    font-size: 14px;
-                }
+            .label {
+                color: #999;
+                width: 70px;
+                flex-shrink: 0;
             }
-        }
 
-        .transaction-info {
-            padding: 0 10px;
-            border-left: 1px solid #eee;
-            border-right: 1px solid #eee;
-        }
+            .value {
+                color: #333;
+                flex: 1;
+                word-break: break-all;
+            }
 
-        .history-info {
-            padding-left: 10px;
+            .copy-icon {
+                cursor: pointer;
+                color: #409eff;
+                margin-left: 5px;
+                font-size: 14px;
+            }
         }
     }
 
-    .history-card {
-        background: #f9f9f9;
-        padding: 10px;
-        border-radius: 4px;
+    .transaction-info {
+        padding: 0 10px;
+        border-left: 1px solid #eee;
+        border-right: 1px solid #eee;
+    }
 
-        .detail-rows {
-            line-height: 1.6;
-        }
+    .history-info {
+        padding-left: 10px;
     }
+}
+
+.history-card {
+    background: #f9f9f9;
+    padding: 10px;
+    border-radius: 4px;
 
-    .truncate-2-lines {
-        display: -webkit-box;
-        -webkit-line-clamp: 2;
-        -webkit-box-orient: vertical;
-        overflow: hidden;
+    .detail-rows {
+        line-height: 1.6;
     }
-</style>
+}
+
+.truncate-2-lines {
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+    overflow: hidden;
+}
+</style>