Browse Source

feat(订单管理): 添加退款详情查看功能并优化订单显示

refactor(订单商品列表): 调整折扣和数量显示逻辑
feat(红包管理): 新增红包详情查看对话框
fix(订单基础信息): 修正缺货处理文本显示
ylong 2 weeks ago
parent
commit
8282d459b6

+ 11 - 2
src/views/mallOrder/all/components/order-base-info.vue

@@ -64,9 +64,9 @@
                         <span class="label">用户备注:</span>
                         <span class="value" :style="{ color: detail.remark ? '#f56c6c' : '' }">{{ detail.remark || '无' }}</span>
                     </div>
-                    <div class="info-item warning-text" v-if="detail.shortageNote">
+                    <div class="info-item warning-text" v-if="detail.outOfStock">
                         <span class="label" style="color: #f56c6c;">如遇缺货:</span>
-                        <span class="value" style="color: #f56c6c;">{{ detail.shortageNote }}</span>
+                        <span class="value" style="color: #f56c6c;">{{ getOutOfStockText(detail.outOfStock) }}</span>
                     </div>
                 </div>
 
@@ -134,6 +134,15 @@ const props = defineProps({
 
     const activities = ref([]);
 
+    const getOutOfStockText = (value) => {
+        const map = {
+            1: '缺货时电话与我沟通',
+            2: '其他商品继续发货(缺货商品退款)',
+            3: '有缺货直接取消订单'
+        };
+        return map[value] || value;
+    };
+
     const viewLogistics = () => {
         EleMessage.info('物流详情功能开发中');
     };

+ 4 - 2
src/views/mallOrder/all/components/order-item.vue

@@ -75,7 +75,9 @@
                 <div>{{ order.waybillCode }}</div>
             </div>
             <div class="col-merged col-status">
-                <dict-data code="shop_order_status" type="text" :model-value="order.status" />
+                <el-button link type="primary" @click="$emit('view-refund-detail', order)" v-if="order.status == 6">退款中</el-button>
+                <dict-data v-else code="shop_order_status" type="text" :model-value="order.status" />
+
                 <el-button link type="primary" @click="$emit('view-detail', order)">[查看详情]</el-button>
             </div>
             <div class="col-merged col-action">
@@ -109,7 +111,7 @@
         }
     });
 
-    defineEmits(['view-detail', 'push-sms', 'refund', 'view-log', 'add-remark', 'manual-delivery']);
+    defineEmits(['view-detail', 'view-refund-detail', 'push-sms', 'refund', 'view-log', 'add-remark', 'manual-delivery']);
 
     const { copy } = useClipboard();
 

+ 8 - 9
src/views/mallOrder/all/components/order-product-list.vue

@@ -22,7 +22,7 @@
                             <div class="title">{{ row.bookName }}</div>
                             <div class="meta">ISBN: {{ row.isbn }}</div>
                             <div class="tags">
-                                <span class="discount-tag">回收折扣: {{ row.discount || 1 }}折</span>
+                                <span class="discount-tag">回收折扣: {{ row.recycleDiscount || 1 }}折</span>
                                 <span class="status-tag">(回收状态: {{ getRecycleStatusText(row.recycleStatus) }})</span>
                             </div>
                         </div>
@@ -46,7 +46,7 @@
                 <!-- Quantity -->
                 <el-table-column label="数量" width="80" align="center">
                     <template #default="{ row }">
-                        <span>x1</span>
+                        <span>x{{ row.num }}</span>
                     </template>
                 </el-table-column>
 
@@ -60,9 +60,8 @@
                 <!-- Discount -->
                 <el-table-column label="优惠" width="120" align="center">
                     <template #default="{ row }">
-                        <div v-if="row.discountAmount">
-                            <div>惊喜红包</div>
-                            <div>¥ {{ row.discountAmount }}</div>
+                        <div v-if="row.discountMoney > 0">
+                            <div style="color: red;">- ¥{{ row.discountMoney }}</div>
                         </div>
                         <div v-else>¥ 0</div>
                     </template>
@@ -85,13 +84,13 @@
 
             <!-- Summary Footer -->
             <div class="summary-footer">
-                <div class="summary-row" v-if="detail.discountMoney > 0">
-                    <span>余额优惠:</span>
-                    <span class="amount red">¥ {{ detail.discountMoney }}</span>
+                <div class="summary-row" v-if="detail.discountList.length > 0" v-for="(item, index) in detail.discountList" :key="index">
+                    <span>{{ item.discountActivityMsg || '-' }}:</span>
+                    <span class="amount red">- ¥{{ item.discountMoney }}</span>
                 </div>
                 <div class="summary-row" v-if="detail.reduceMoney > 0">
                     <span>分享降价:</span>
-                    <span class="amount red">¥ {{ detail.reduceMoney }}</span>
+                    <span class="amount red">- ¥{{ detail.reduceMoney }}</span>
                 </div>
                 <div class="summary-row">
                     <span>运费:</span>

+ 8 - 0
src/views/mallOrder/all/index.vue

@@ -16,6 +16,7 @@
             <div class="order-list" v-loading="loading">
                 <div v-if="list.length === 0" class="empty-text">暂无数据</div>
                 <order-item v-for="order in list" :key="order.orderId" :order="order" @view-detail="openDetail"
+                    @view-refund-detail="openRefundDetail"
                     @push-sms="openSms" @refund="openRefund" @view-log="openLog" @add-remark="openRemark" @manual-delivery="openManualDelivery" />
             </div>
 
@@ -29,6 +30,7 @@
 
         <!-- Dialogs -->
         <order-detail ref="detailRef" @push-sms="openSms" @refund="openRefund" @add-package="openAddPackage" />
+        <refund-detail-dialog ref="refundDetailRef" />
         <push-sms-dialog ref="smsRef" />
         <refund-dialog ref="refundRef" @success="fetchData" />
         <add-package-dialog ref="packageRef" />
@@ -42,6 +44,7 @@
     import { ref, reactive, onMounted } from 'vue';
     import PageSearch from './components/page-search.vue';
     import OrderDetail from './components/order-detail.vue';
+    import RefundDetailDialog from '@/views/mallOrder/refund/components/refund-detail-dialog.vue';
     import PushSmsDialog from './components/push-sms-dialog.vue';
     import RefundDialog from './components/refund-dialog.vue';
     import AddPackageDialog from './components/add-package-dialog.vue';
@@ -68,6 +71,7 @@
 
     const list = ref([]);
     const detailRef = ref(null);
+    const refundDetailRef = ref(null);
     const smsRef = ref(null);
     const refundRef = ref(null);
     const packageRef = ref(null);
@@ -124,6 +128,10 @@
         detailRef.value?.handleOpen(row);
     };
 
+    const openRefundDetail = (row) => {
+        refundDetailRef.value?.handleOpen(row);
+    };
+
     const openSms = (row) => {
         smsRef.value?.handleOpen(row);
     };

+ 2 - 10
src/views/mallOrder/refund/components/refund-detail-dialog.vue

@@ -119,8 +119,8 @@
                         </div>
                     </div>
                     <div class="info-item">
-                        <span class="label">商品总价:</span>
-                        <span class="value">¥{{ totalAmount }}</span>
+                        <span class="label">申请退款:</span>
+                        <span class="value">¥{{ form.refundMoney || form.refundMoney }}</span>
                     </div>
                     <div class="info-item">
                         <span class="label">邮费:</span>
@@ -245,14 +245,6 @@
         return form.value.detailList.reduce((sum, item) => sum + (Number(item.refundNum) || 0), 0);
     });
 
-    const totalAmount = computed(() => {
-        if (!form.value.detailList) return '0.00';
-        const sum = form.value.detailList.reduce((acc, item) => {
-            return acc + (Number(item.price) * Number(item.refundNum));
-        }, 0);
-        return sum.toFixed(2);
-    });
-
     // 当前步骤
     const currentStep = computed(() => {
         const status = Number(form.value.status);

+ 111 - 0
src/views/marketing/redPacket/components/RedPacketDetailDialog.vue

@@ -0,0 +1,111 @@
+<template>
+    <ele-modal v-model="visible" :title="dialogTitle" :width="700" :footer="null" :close-on-click-modal="false">
+        <div class="detail-content" v-loading="loading">
+            <el-descriptions :column="2" border>
+                <el-descriptions-item label="活动名称">{{ detail.couponName || '-' }}</el-descriptions-item>
+                <el-descriptions-item label="红包类型">
+                    {{ detail.type === '2' || detail.type === 2 ? '惊喜红包' : '普通红包' }}
+                </el-descriptions-item>
+                <el-descriptions-item label="活动状态">
+                    <dict-data code="red_packet_status" :model-value="detail.status" type="tag"></dict-data>
+                </el-descriptions-item>
+                <el-descriptions-item label="红包数量">{{ detail.totalNum || 0 }} 张</el-descriptions-item>
+                <el-descriptions-item label="红包面额">
+                    <template v-if="detail.type === '2' || detail.type === 2">
+                        {{ detail.minMoney || 0 }} - {{ detail.maxMoney || 0 }} 元(随机)
+                    </template>
+                    <template v-else>
+                        {{ detail.faceMoney || 0 }} 元
+                    </template>
+                </el-descriptions-item>
+                <el-descriptions-item label="使用门槛">
+                    <template v-if="!detail.thresholdMoney || detail.thresholdMoney === 0">
+                        无门槛
+                    </template>
+                    <template v-else>
+                        满 {{ detail.thresholdMoney }} 元可用
+                    </template>
+                </el-descriptions-item>
+                <el-descriptions-item label="是否可以叠加使用">
+                    {{ detail.stackType === '1' || detail.stackType === 1 ? '是' : '否' }}
+                </el-descriptions-item>
+                <el-descriptions-item label="每人限领">{{ detail.limitedPerUser || 1 }} 张</el-descriptions-item>
+                <el-descriptions-item label="使用商品">
+                    <template v-if="!detail.scope || detail.scope === '1' || detail.scope === 1">
+                        所有商品
+                    </template>
+                    <template v-else>
+                        指定商品
+                    </template>
+                </el-descriptions-item>
+                <el-descriptions-item label="有效期类型" :span="2">
+                    <template v-if="!detail.effectDays || detail.effectDays === 0">
+                        固定日期范围
+                    </template>
+                    <template v-else>
+                        领取后 {{ detail.effectDays }} 天有效
+                    </template>
+                </el-descriptions-item>
+                <el-descriptions-item label="有效期起始时间" v-if="!detail.effectDays || detail.effectDays === 0">
+                    {{ detail.effectStartTime || '-' }}
+                </el-descriptions-item>
+                <el-descriptions-item label="有效期结束时间" v-if="!detail.effectDays || detail.effectDays === 0">
+                    {{ detail.effectEndTime || '-' }}
+                </el-descriptions-item>
+                <el-descriptions-item label="剩余数量">{{ detail.restNum || 0 }} 张</el-descriptions-item>
+                <el-descriptions-item label="口令" v-if="detail.type === '2' || detail.type === 2">
+                    {{ detail.commandWord || '-' }}
+                </el-descriptions-item>
+            </el-descriptions>
+        </div>
+    </ele-modal>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import request from '@/utils/request';
+
+const visible = ref(false);
+const loading = ref(false);
+const detail = ref({});
+const dialogTitle = ref('红包详情');
+
+const open = async (id) => {
+    visible.value = true;
+    loading.value = true;
+    detail.value = {};
+    
+    try {
+        const res = await request.get('/shop/couponInfo/getInfo/' + id);
+        if (res.data.code === 200) {
+            detail.value = res.data.data || {};
+            const typeName = (detail.value.type === '2' || detail.value.type === 2) ? '惊喜红包' : '普通红包';
+            dialogTitle.value = `${typeName}详情`;
+        } else {
+            console.error('获取红包详情失败:', res.data.msg);
+        }
+    } catch (error) {
+        console.error('获取红包详情失败:', error);
+    } finally {
+        loading.value = false;
+    }
+};
+
+defineExpose({ open });
+</script>
+
+<style scoped>
+.detail-content {
+    padding: 10px 0;
+}
+
+:deep(.el-descriptions__label) {
+    width: 140px;
+    font-weight: 500;
+    background-color: #fafafa;
+}
+
+:deep(.el-descriptions__content) {
+    color: #303133;
+}
+</style>

+ 23 - 18
src/views/marketing/redPacket/components/RedPacketDialog.vue

@@ -3,11 +3,11 @@
         <el-form ref="formRef" :model="form" :rules="rules" label-width="100px" @submit.prevent>
             <el-form-item label="红包名称" prop="couponName">
                 <el-input v-model="form.couponName" placeholder="请输入红包名称" maxlength="20" show-word-limit
-                    :disabled="isEditActive" />
+                    :disabled="isEditActive || isViewMode" />
             </el-form-item>
 
             <el-form-item label="红包数量" prop="totalNum">
-                <el-input-number v-model="form.totalNum" :min="1" :max="100000" placeholder="请输入" class="!w-[200px]" />
+                <el-input-number v-model="form.totalNum" :min="1" :max="100000" placeholder="请输入" class="!w-[200px]" :disabled="isViewMode" />
                 <span class="ml-2 text-gray-500">张</span>
             </el-form-item>
 
@@ -16,19 +16,19 @@
                     <template v-if="type === '2'">
                         <el-form-item prop="minMoney" class="mb-0 !mr-0">
                             <el-input-number v-model="form.minMoney" :min="0.01" :precision="2" :controls="false"
-                                placeholder="请输入" class="!w-[100px]" :disabled="isEditActive" />
+                                placeholder="请输入" class="!w-[100px]" :disabled="isEditActive || isViewMode" />
                         </el-form-item>
                         <span>-</span>
                         <el-form-item prop="maxMoney" class="mb-0 !mr-0">
                             <el-input-number v-model="form.maxMoney" :min="0.01" :precision="2" :controls="false"
-                                placeholder="请输入" class="!w-[100px]" :disabled="isEditActive" />
+                                placeholder="请输入" class="!w-[100px]" :disabled="isEditActive || isViewMode" />
                         </el-form-item>
                         <span class="text-gray-500">元随机数字</span>
                     </template>
                     <template v-else>
                         <el-form-item prop="faceMoney" class="mb-0 !mr-0">
                             <el-input-number v-model="form.faceMoney" :min="0.01" :precision="2" :controls="false"
-                                placeholder="请输入" class="!w-[150px]" :disabled="isEditActive" />
+                                placeholder="请输入" class="!w-[150px]" :disabled="isEditActive || isViewMode" />
                         </el-form-item>
                         <span class="text-gray-500">元</span>
                     </template>
@@ -36,32 +36,32 @@
             </el-form-item>
 
             <el-form-item label="使用门槛" prop="thresholdType">
-                <el-radio-group v-model="form.thresholdType" :disabled="isEditActive">
+                <el-radio-group v-model="form.thresholdType" :disabled="isEditActive || isViewMode">
                     <el-radio label="none">无门槛</el-radio>
                     <el-radio label="full">满</el-radio>
                 </el-radio-group>
                 <el-form-item v-if="form.thresholdType === 'full'" prop="thresholdMoney"
                     class="inline-block ml-2 mb-0 !mr-0">
                     <el-input-number v-model="form.thresholdMoney" :min="0.01" :precision="2" :controls="false"
-                        placeholder="请输入" class="!w-[100px]" :disabled="isEditActive" />
+                        placeholder="请输入" class="!w-[100px]" :disabled="isEditActive || isViewMode" />
                     <span class="ml-2 text-gray-500">元 可用</span>
                 </el-form-item>
             </el-form-item>
 
             <el-form-item label="可否叠加使用" prop="stackType">
-                <el-radio-group v-model="form.stackType" :disabled="isEditActive">
+                <el-radio-group v-model="form.stackType" :disabled="isEditActive || isViewMode">
                     <el-radio label="0">否</el-radio>
                     <el-radio label="1">是</el-radio>
                 </el-radio-group>
             </el-form-item>
 
             <el-form-item label="使用商品" prop="scope">
-                <el-radio-group v-model="form.scope" :disabled="isEditActive">
+                <el-radio-group v-model="form.scope" :disabled="isEditActive || isViewMode">
                     <el-radio :label="'1'">所有商品</el-radio>
                     <el-radio :label="'2'">指定商品</el-radio>
                 </el-radio-group>
                 <div v-if="form.scope === '2'" class="mt-2">
-                    <el-button type="primary" link @click="openProductSelect" :disabled="isEditActive">
+                    <el-button type="primary" link @click="openProductSelect" :disabled="isEditActive || isViewMode">
                         已选择 {{ form.products ? form.products.length : 0 }} 个商品
                     </el-button>
                 </div>
@@ -70,22 +70,22 @@
             <el-form-item label="有效期" required>
                 <div class="flex flex-col space-y-4 w-full">
                     <div class="flex items-center">
-                        <el-radio v-model="form.validityType" label="range" :disabled="isEditActive" @change="handleValidityTypeChange"
+                        <el-radio v-model="form.validityType" label="range" :disabled="isEditActive || isViewMode" @change="handleValidityTypeChange"
                             class="!mr-4">日期范围</el-radio>
                         <el-form-item prop="validityRange" class="mb-0">
                             <el-date-picker v-model="form.validityRange" type="daterange" range-separator="至"
                                 start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:ss"
-                                :disabled="isEditActive || form.validityType !== 'range'" style="width: 320px;" />
+                                :disabled="isEditActive || isViewMode || form.validityType !== 'range'" style="width: 320px;" />
                         </el-form-item>
                     </div>
 
                     <div class="flex items-center">
-                        <el-radio v-model="form.validityType" label="days" :disabled="isEditActive" @change="handleValidityTypeChange"
+                        <el-radio v-model="form.validityType" label="days" :disabled="isEditActive || isViewMode" @change="handleValidityTypeChange"
                             class="!mr-4">有效日期</el-radio>
                         <div class="flex items-center">
                             <el-form-item prop="effectDays" class="mb-0 !mr-2">
                                 <el-input-number v-model="form.effectDays" placeholder="请输入" style="width: 200px;"
-                                    :min="1" :disabled="isEditActive || form.validityType !== 'days'" />
+                                    :min="1" :disabled="isEditActive || isViewMode || form.validityType !== 'days'" />
                             </el-form-item>
                             <span>天</span>
                         </div>
@@ -95,12 +95,12 @@
 
             <el-form-item label="每人限领" prop="limitedPerUser">
                 <el-input-number v-model="form.limitedPerUser" :min="1" placeholder="请输入" class="!w-[200px]"
-                    :disabled="isEditActive" />
+                    :disabled="isEditActive || isViewMode" />
                 <span class="ml-2 text-gray-500">张</span>
             </el-form-item>
 
             <el-form-item v-if="type === '2'" label="口令" prop="commandWord">
-                <el-input v-model="form.commandWord" placeholder="请输入口令" :disabled="isEditActive" />
+                <el-input v-model="form.commandWord" placeholder="请输入口令" :disabled="isEditActive || isViewMode" />
                 <div class="text-gray-400 text-xs mt-1">
                     用户搜索后弹出惊喜红包
                 </div>
@@ -111,7 +111,7 @@
         <template #footer>
             <div class="flex justify-end items-center">
                 <el-button @click="visible = false">取消</el-button>
-                <el-button type="primary" :loading="loading" @click="handleSubmit">
+                <el-button v-if="!isViewMode" type="primary" :loading="loading" @click="handleSubmit">
                     {{ isEdit ? '保存' : '立即添加' }}
                 </el-button>
             </div>
@@ -134,6 +134,7 @@
     const visible = ref(false);
     const type = ref('1'); // '1' (Common) | '2' (Surprise)
     const dialogData = ref(null);
+    const isViewMode = ref(false);
 
     const formRef = ref(null);
     const loading = ref(false);
@@ -144,6 +145,9 @@
 
     const dialogTitle = computed(() => {
         const typeName = (type.value === '2') ? '惊喜红包' : '普通红包';
+        if (isViewMode.value) {
+            return `查看${typeName}详情`;
+        }
         return isEdit.value ? `编辑${typeName}` : `新建${typeName}`;
     });
 
@@ -185,9 +189,10 @@
         }
     };
 
-    const open = (newType, data) => {
+    const open = (newType, data, viewMode = false) => {
         type.value = newType;
         dialogData.value = data;
+        isViewMode.value = viewMode;
         visible.value = true;
 
         resetForm();

+ 6 - 3
src/views/marketing/redPacket/index.vue

@@ -82,7 +82,7 @@
                 <template #action="{ row }">
                     <div class="flex flex-col space-y-1 text-xs">
                         <div class="flex space-x-2">
-                            <el-button type="primary" link @click="handleViewLink(row)">查看链接</el-button>
+                            <el-button type="primary" link @click="handleViewDetail(row)">查看详情</el-button>
                             <el-button type="warning" link @click="handleCopyData(row)">复制</el-button>
                             <el-button type="primary" link @click="handleEdit(row)">修改</el-button>
                         </div>
@@ -98,6 +98,7 @@
         </div>
 
         <RedPacketDialog ref="redPacketDialogRef" @success="reload" />
+        <RedPacketDetailDialog ref="redPacketDetailDialogRef" />
         <RedPacketDataDialog ref="redPacketDataDialogRef" />
         <RedPacketLinkDialog ref="redPacketLinkDialogRef" />
         <RedPacketLogDialog ref="redPacketLogDialogRef" />
@@ -110,6 +111,7 @@
     import { ElMessageBox } from "element-plus/es";
     import CommonTable from '@/components/CommonPage/CommonTable.vue';
     import RedPacketDialog from './components/RedPacketDialog.vue';
+    import RedPacketDetailDialog from './components/RedPacketDetailDialog.vue';
     import RedPacketDataDialog from './components/RedPacketDataDialog.vue';
     import RedPacketLinkDialog from './components/RedPacketLinkDialog.vue';
     import RedPacketLogDialog from './components/RedPacketLogDialog.vue';
@@ -125,6 +127,7 @@
 
     // Dialog Refs
     const redPacketDialogRef = ref(null);
+    const redPacketDetailDialogRef = ref(null);
     const redPacketDataDialogRef = ref(null);
     const redPacketLinkDialogRef = ref(null);
     const redPacketLogDialogRef = ref(null);
@@ -197,8 +200,8 @@
         redPacketDialogRef.value?.open(String(row.type), { ...row });
     };
 
-    const handleViewLink = (row) => {
-        redPacketLinkDialogRef.value?.open(row.id);
+    const handleViewDetail = (row) => {
+        redPacketDetailDialogRef.value?.open(row.id);
     };
 
     const handleCopyData = (row) => {