Procházet zdrojové kódy

feat(mallOrder): 新增订单管理相关组件和页面

新增订单管理模块的多个组件,包括订单列表、订单详情、退款对话框、短信推送等功能。主要包含以下内容:

1. 添加订单列表页面及搜索组件
2. 实现订单详情弹窗展示订单基本信息、商品列表和物流信息
3. 新增退款功能对话框,支持选择退款商品和设置退款金额
4. 添加短信推送功能,可选择短信模板并查看历史记录
5. 实现订单表格头部组件和订单项组件
6. 添加投诉管理页面及相关组件
7. 实现新增包裹和协商对话框功能
ylong před 4 dny
rodič
revize
584e1156a2

+ 68 - 0
src/views/mallOrder/all/components/add-package-dialog.vue

@@ -0,0 +1,68 @@
+<template>
+    <ele-modal
+        :width="600"
+        v-model="visible"
+        title="新增额外包裹"
+        @open="handleOpen"
+        :body-style="{ padding: '20px' }"
+    >
+        <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+            <el-form-item label="物流公司" prop="company">
+                <el-select v-model="form.company" placeholder="请选择物流公司" style="width: 100%">
+                    <el-option label="顺丰速运" value="shunfeng" />
+                    <el-option label="圆通速递" value="yuantong" />
+                    <el-option label="中通快递" value="zhongtong" />
+                    <el-option label="韵达快递" value="yunda" />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="快递单号" prop="no">
+                <el-input v-model="form.no" placeholder="请输入快递单号" />
+            </el-form-item>
+            <el-form-item label="包裹备注" prop="remark">
+                <el-input v-model="form.remark" type="textarea" placeholder="请输入包裹备注" />
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button @click="visible = false">取消</el-button>
+            <el-button type="primary" @click="handleSubmit">确定</el-button>
+        </template>
+    </ele-modal>
+</template>
+
+<script setup>
+import { ref, reactive } from 'vue';
+import { EleMessage } from 'ele-admin-plus/es';
+
+const visible = defineModel({ type: Boolean });
+const formRef = ref(null);
+
+const form = reactive({
+    company: '',
+    no: '',
+    remark: ''
+});
+
+const rules = {
+    company: [{ required: true, message: '请选择物流公司', trigger: 'change' }],
+    no: [{ required: true, message: '请输入快递单号', trigger: 'blur' }]
+};
+
+const handleOpen = () => {
+    form.company = '';
+    form.no = '';
+    form.remark = '';
+};
+
+const handleSubmit = () => {
+    formRef.value.validate((valid) => {
+        if (valid) {
+            EleMessage.success('添加成功');
+            visible.value = false;
+        }
+    });
+};
+
+defineExpose({
+    handleOpen: () => { visible.value = true; }
+});
+</script>

+ 200 - 0
src/views/mallOrder/all/components/order-base-info.vue

@@ -0,0 +1,200 @@
+<template>
+    <div class="order-base-info">
+        <ele-card header="订单基本信息">
+            <div class="info-layout">
+                <!-- Left Column: Order Info -->
+                <div class="info-column">
+                    <div class="info-item">
+                        <span class="label">订单编号:</span>
+                        <span class="value">{{ detail.orderId }}</span>
+                    </div>
+                    <div class="info-item">
+                        <span class="label">订单来源:</span>
+                        <span class="value">{{ detail.source }}</span>
+                    </div>
+                    <div class="info-item">
+                        <span class="label">支付渠道:</span>
+                        <span class="value">{{ detail.payChannel }}</span>
+                    </div>
+                    <div class="info-item">
+                        <span class="label">交易状态:</span>
+                        <span class="value">{{ detail.status }}</span>
+                    </div>
+                    <div class="info-item">
+                        <span class="label">平台备注:</span>
+                        <span class="value">{{ detail.remark || '正常发货' }}</span>
+                    </div>
+                </div>
+
+                <!-- Center Column: User/Receiver Info -->
+                <div class="info-column">
+                    <div class="info-item">
+                        <span class="label">用户名:</span>
+                        <span class="value">{{ detail.buyer?.name }}</span>
+                    </div>
+                    <div class="info-item">
+                        <span class="label">收 件 人:</span>
+                        <span class="value">{{ detail.receiver?.name }}</span>
+                        <el-button link type="success" class="ml-2" @click="$emit('edit-receiver')">修改</el-button>
+                    </div>
+                    <div class="info-item">
+                        <span class="label">电 话:</span>
+                        <span class="value">{{ detail.receiver?.phone }}</span>
+                    </div>
+                    <div class="info-item">
+                        <span class="label">地 址:</span>
+                        <span class="value">{{ detail.receiver?.address }}</span>
+                    </div>
+                    <div class="info-item">
+                        <span class="label">用户备注:</span>
+                        <span class="value">{{ detail.buyerNote || '无' }}</span>
+                    </div>
+                    <div class="info-item warning-text" v-if="detail.shortageNote">
+                        <span class="label" style="color: #f56c6c;">如遇缺货:</span>
+                        <span class="value" style="color: #f56c6c;">{{ detail.shortageNote }}</span>
+                    </div>
+                </div>
+
+                <!-- Right Column: Logistics Info -->
+                <div class="info-column">
+                    <div class="info-item">
+                        <span class="label">发货物流:</span>
+                        <span class="value">{{ detail.logistics?.company || '-' }}</span>
+                    </div>
+                    <div class="info-item">
+                        <span class="label">发货单号:</span>
+                        <span class="value">{{ detail.logistics?.no || '-' }}</span>
+
+                        <el-popover placement="bottom" title="物流动态" :width="300" trigger="hover">
+                            <template #reference>
+                                <el-button link type="success" class="ml-2" v-if="detail.logistics?.no"
+                                    @click="viewLogistics">查看</el-button>
+                            </template>
+                            <el-timeline>
+                                <el-timeline-item v-for="(activity, index) in activities" :key="index"
+                                    :timestamp="activity.timestamp">
+                                    {{ activity.content }}
+                                </el-timeline-item>
+                            </el-timeline>
+                        </el-popover>
+                    </div>
+
+                    <div class="action-area mt-2">
+                        <el-button type="success" @click="$emit('add-package')">新增额外包裹 <el-icon class="el-icon--right">
+                                <Lightning />
+                            </el-icon></el-button>
+                    </div>
+
+                    <div class="status-tags mt-2">
+                        <el-tag :type="detail.isShipped ? 'success' : 'warning'" effect="dark" class="mr-2">
+                            {{ detail.isShipped ? '已发货' : '未发货' }}
+                        </el-tag>
+                        <el-tag :type="detail.isCompleted ? 'success' : 'warning'" effect="dark">
+                            {{ detail.isCompleted ? '已完成' : '未完成' }}
+                        </el-tag>
+                    </div>
+                </div>
+            </div>
+        </ele-card>
+    </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { Lightning } from '@element-plus/icons-vue';
+
+const props = defineProps({
+    detail: {
+        type: Object,
+        default: () => ({})
+    }
+});
+
+const emit = defineEmits(['add-package', 'edit-receiver']);
+
+const activities = [
+    {
+        content: '已签收,签收人是拍照签收',
+        timestamp: '2024-04-12 20:46',
+    },
+    {
+        content: '派送中',
+        timestamp: '2024-04-12 20:46',
+    },
+    {
+        content: '已发货',
+        timestamp: '2024-04-11 20:46',
+    },
+];
+
+const viewLogistics = () => {
+    // Logic to view full logistics
+};
+</script>
+
+<style lang="scss" scoped>
+.info-layout {
+    display: flex;
+    justify-content: space-between;
+    gap: 20px;
+
+    .info-column {
+        flex: 1;
+
+        .info-item {
+            margin-bottom: 12px;
+            font-size: 14px;
+            display: flex;
+
+            .label {
+                color: #606266;
+                width: 80px;
+                flex-shrink: 0;
+            }
+
+            .value {
+                color: #303133;
+                font-weight: 500;
+                flex: 1;
+            }
+        }
+
+        .warning-text {
+            color: #f56c6c;
+            font-weight: bold;
+        }
+    }
+}
+
+.status-box {
+    background-color: #fdf6ec;
+    border: 1px solid #faecd8;
+    color: #e6a23c;
+    padding: 10px;
+    border-radius: 4px;
+    font-size: 12px;
+    cursor: pointer;
+    margin-top: 10px;
+
+    .status-dot {
+        display: inline-block;
+        width: 6px;
+        height: 6px;
+        background-color: #e6a23c;
+        border-radius: 50%;
+        margin-right: 5px;
+    }
+}
+
+.ml-2 {
+    margin-left: 8px;
+}
+
+.mr-2 {
+    margin-right: 8px;
+}
+
+.mt-2 {
+    margin-top: 8px;
+}
+</style>

+ 202 - 0
src/views/mallOrder/all/components/order-detail.vue

@@ -0,0 +1,202 @@
+<template>
+    <ele-modal
+        :width="1200"
+        v-model="visible"
+        title="订单详情"
+        @open="handleOpen"
+        :body-style="{
+            maxHeight: '80vh',
+            overflow: 'auto',
+            padding: '20px',
+            backgroundColor: '#f5f7fa'
+        }"
+        top="5vh"
+    >
+        <!-- Status Steps -->
+        <ele-card class="mb-4">
+            <el-steps :active="activeStep" align-center finish-status="success">
+                <el-step title="提交订单" :description="detail.createTime" />
+                <el-step title="支付订单" :description="detail.payTime" />
+                <el-step title="平台发货" :description="detail.deliveryTime" />
+                <el-step title="确认收货" :description="detail.receiveTime" />
+                <el-step title="完成评价" :description="detail.finishTime" />
+            </el-steps>
+        </ele-card>
+
+        <!-- Order Base Info Component -->
+        <order-base-info 
+            :detail="detail" 
+            @add-package="openAddPackage"
+            @edit-receiver="handleEditReceiver"
+            class="mb-4"
+        />
+
+        <!-- Product List Component -->
+        <order-product-list 
+            :products="detail.products"
+            :payment="detail.payment"
+            class="mb-4"
+        />
+
+        <!-- Action Buttons Footer -->
+        <template #footer>
+            <div class="dialog-footer">
+                <el-button @click="visible = false">关闭</el-button>
+                <el-button type="success" @click="openPushSms">推送短信</el-button>
+                <el-button type="warning" @click="openRefund">缺货退款</el-button>
+            </div>
+        </template>
+
+        <!-- Sub Dialogs -->
+        <add-package-dialog ref="packageRef" />
+        <push-sms-dialog ref="smsRef" />
+        <refund-dialog ref="refundRef" />
+    </ele-modal>
+</template>
+
+<script setup>
+import { ref, reactive, computed } from 'vue';
+import { EleMessage } from 'ele-admin-plus/es';
+import AddPackageDialog from './add-package-dialog.vue';
+import PushSmsDialog from './push-sms-dialog.vue';
+import RefundDialog from './refund-dialog.vue';
+import OrderBaseInfo from './order-base-info.vue';
+import OrderProductList from './order-product-list.vue';
+
+const visible = defineModel({ type: Boolean });
+const detail = ref({});
+const packageRef = ref(null);
+const smsRef = ref(null);
+const refundRef = ref(null);
+
+const activeStep = computed(() => {
+    const status = detail.value.status;
+    if (status === '等待买家付款') return 0;
+    if (status === '等待卖家发货') return 1;
+    if (status === '等待买家确认收货') return 2;
+    if (status === '交易成功') return 4;
+    return 1; // Default
+});
+
+const handleOpen = (data) => {
+    // Enhanced Mock Data logic
+    // In a real app, this would fetch from API using data.orderId
+    
+    // Base Mock Data
+    const baseData = {
+        orderId: '4066879393210936038',
+        createTime: '2024-10-04 12:01:09',
+        payTime: '2024-10-04 12:05:00',
+        deliveryTime: '2024-10-05 09:30:00',
+        receiveTime: '',
+        finishTime: '',
+        source: '微信',
+        payChannel: '微信支付',
+        status: '等待买家确认收货',
+        remark: '正常发货',
+        buyer: {
+            name: '书嗨565652323',
+            id: 'u123456'
+        },
+        receiver: {
+            name: '书嗨',
+            phone: '17512688011',
+            address: '河南省/鹤壁市/浚县/黄河路街道'
+        },
+        buyerNote: '发新一点的',
+        shortageNote: '缺货时与我电话沟通',
+        logistics: {
+            company: '韵达快递',
+            no: '463855119742',
+            status: '已发货'
+        },
+        isShipped: true,
+        isCompleted: false,
+        
+        // Payment Info
+        payment: {
+            total: 2.9,
+            shipping: 2.9,
+            discountTotal: 2.9,
+            redPacket: 2.9,
+            shareDiscount: 2.9
+        },
+        
+        // Products
+        products: [
+            {
+                image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
+                title: '毛泽东思想和中国特色社会主义理论体系概论(2023年版)',
+                isbn: '9787040599039',
+                condition: '中等',
+                discount: 1,
+                recycleStatus: '正在回收',
+                refundStatus: '未确认收货',
+                qty: 1,
+                price: 2.9,
+                discountAmount: 0.9,
+                actualPayment: 2,
+                weight: 0.5
+            },
+            {
+                image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
+                title: '毛泽东思想和中国特色社会主义理论体系概论(2023年版)',
+                isbn: '9787040599039',
+                condition: '中等',
+                discount: 1,
+                recycleStatus: '正在回收',
+                refundStatus: '未确认收货',
+                qty: 1,
+                price: 2.9,
+                discountAmount: 0,
+                actualPayment: 2.9,
+                weight: 0.5
+            }
+        ]
+    };
+
+    // If passed data has specific overrides, merge them (simple merge)
+    if (data && data.orderId) {
+        detail.value = { ...baseData, ...data };
+        // Ensure complex objects are not lost if not present in passed data
+        if (!data.products) detail.value.products = baseData.products;
+        if (!data.payment) detail.value.payment = baseData.payment;
+        if (!data.receiver) detail.value.receiver = baseData.receiver;
+    } else {
+        detail.value = baseData;
+    }
+};
+
+const openAddPackage = () => {
+    packageRef.value?.handleOpen();
+};
+
+const openPushSms = () => {
+    smsRef.value?.handleOpen(detail.value);
+};
+
+const openRefund = () => {
+    refundRef.value?.handleOpen(detail.value);
+};
+
+const handleEditReceiver = () => {
+    EleMessage.info('修改收货人信息功能待开发');
+};
+
+defineExpose({
+    handleOpen: (data) => {
+        visible.value = true;
+        handleOpen(data);
+    }
+});
+</script>
+
+<style scoped>
+.mb-4 {
+    margin-bottom: 16px;
+}
+.dialog-footer {
+    display: flex;
+    justify-content: flex-end;
+}
+</style>

+ 288 - 0
src/views/mallOrder/all/components/order-item.vue

@@ -0,0 +1,288 @@
+<template>
+    <div class="order-item">
+        <!-- Order Header -->
+        <div class="order-header">
+            <el-checkbox v-model="order.checked" style="margin-right: 12px;" />
+            <span class="mr-4">订单编号: {{ order.orderId }} <el-button link type="primary" size="small"
+                    @click="handleCopy(order.orderId)">复制</el-button></span>
+            <span class="mr-4">创建时间: {{ order.createTime }}</span>
+            <span class="mr-4">支付渠道: {{ order.payChannel }}</span>
+            <span class="mr-4">订单来源: {{ order.source }}</span>
+            <span class="flag-icon" v-if="order.flag"><el-icon>
+                    <Flag />
+                </el-icon></span>
+            <span class="urge-tag" v-if="order.isUrge">催发货</span>
+        </div>
+
+        <!-- Order Body -->
+        <div class="order-body">
+            <!-- Products Column -->
+            <div class="col-products-wrapper">
+                <div v-for="(product, idx) in order.products" :key="idx" class="product-row">
+                    <div class="col-product product-info mr-2">
+                        <el-image :src="product.image" class="product-img" fit="cover" />
+                        <div class="product-detail">
+                            <div class="product-title">{{ product.title }}</div>
+                            <div class="product-isbn">ISBN: <span class="link">{{ product.isbn }}</span>
+                                <el-icon class="cursor-pointer" @click="handleCopy(product.isbn)">
+                                    <CopyDocument />
+                                </el-icon> (品相: {{ product.condition }})
+                            </div>
+                            <div class="product-tags">
+                                <span class="tag">回收折扣: {{ product.discount }}折</span>
+                                <span class="tag success">(回收状态: {{ product.recycleStatus }})</span>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col-price">¥ {{ product.price }}</div>
+                    <div class="col-qty">x{{ product.qty }}</div>
+                    <div class="col-aftersales">
+                        <span
+                            :class="{ 'text-blue': product.refundStatus === '退款成功', 'text-red': product.refundStatus === '退款取消' }">{{
+                                product.refundStatus }}</span>
+                    </div>
+                </div>
+            </div>
+
+            <!-- Merged Columns -->
+            <div class="col-merged col-buyer">
+                <div class="buyer-info">
+                    <el-avatar :size="30" :src="order.buyer.avatar" />
+                    <div class="buyer-name">{{ order.buyer.name }}</div>
+                    <div class="buyer-phone">{{ order.buyer.phone }}</div>
+                </div>
+            </div>
+            <div class="col-merged col-payment">
+                <div class="payment-amount">¥ {{ order.payment.total }}</div>
+                <div class="shipping-fee">(含邮费: ¥ {{ order.payment.shipping }})</div>
+            </div>
+            <div class="col-merged col-logistics">
+                <div>{{ order.logistics.company }}</div>
+                <div>{{ order.logistics.no }}</div>
+            </div>
+            <div class="col-merged col-status">
+                <div :class="getStatusColor(order.status)">{{ order.status }}</div>
+                <el-button link type="primary" @click="$emit('view-detail', order)">[查看详情]</el-button>
+            </div>
+            <div class="col-merged col-action">
+                <el-button link type="primary" @click="$emit('push-sms', order)">[推送短信]</el-button>
+                <el-button link type="warning" @click="$emit('refund', order)">[缺货退款]</el-button>
+            </div>
+        </div>
+
+        <!-- Buyer Note -->
+        <div class="buyer-note" v-if="order.buyerNote">
+            <span class="note-label">买家备注:</span>
+            <span class="note-content">{{ order.buyerNote }}</span>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { Flag, CopyDocument } from '@element-plus/icons-vue';
+import { EleMessage } from 'ele-admin-plus/es';
+import { useClipboard } from '@vueuse/core';
+
+const props = defineProps({
+    order: {
+        type: Object,
+        required: true
+    }
+});
+
+defineEmits(['view-detail', 'push-sms', 'refund']);
+
+const { copy } = useClipboard();
+
+const handleCopy = async (text) => {
+    try {
+        await copy(text);
+        EleMessage.success('复制成功');
+    } catch (e) {
+        EleMessage.error('复制失败');
+    }
+};
+
+const getStatusColor = (status) => {
+    if (status === '退款成功') return 'text-red';
+    if (status === '等待买家确认收货') return 'text-red';
+    return '';
+};
+</script>
+
+<style lang="scss" scoped>
+.order-item {
+    border: 1px solid #ebeef5;
+    margin-bottom: 15px;
+
+    .order-header {
+        background-color: #f0f9eb; // Light blue/greenish background like screenshot
+        padding: 8px 15px;
+        font-size: 13px;
+        color: #606266;
+        display: flex;
+        align-items: center;
+
+        .urge-tag {
+            background-color: #f56c6c;
+            color: #fff;
+            padding: 1px 5px;
+            border-radius: 2px;
+            font-size: 12px;
+            margin-left: 10px;
+        }
+    }
+
+    .order-body {
+        display: flex;
+        border-top: 1px solid #ebeef5;
+
+        .col-products-wrapper {
+            flex: 4; // Takes up 4 columns worth of space (Product, Price, Qty, Aftersales)
+            display: flex;
+            flex-direction: column;
+        }
+
+        .product-row {
+            display: flex;
+            border-bottom: 1px solid #ebeef5;
+
+            &:last-child {
+                border-bottom: none;
+            }
+
+            padding: 10px 0;
+
+            .col-product {
+                flex: 1.5;
+            }
+
+            .col-price {
+                flex: 0.5;
+            }
+
+            .col-qty {
+                flex: 0.5;
+            }
+
+            .col-aftersales {
+                flex: 0.5;
+            }
+        }
+
+        .col-merged {
+            border-left: 1px solid #ebeef5;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            padding: 10px;
+            text-align: center;
+            font-size: 13px;
+        }
+    }
+
+    .buyer-note {
+        background-color: #fff0f0;
+        padding: 8px 15px;
+        font-size: 12px;
+        color: #f56c6c;
+        border-top: 1px solid #ebeef5;
+
+        .note-label {
+            font-weight: bold;
+            margin-right: 5px;
+        }
+    }
+}
+
+// Column Widths
+.col-product {
+    flex: 1.5;
+    text-align: left !important;
+    padding-left: 15px !important;
+}
+
+.col-price {
+    flex: 0.5;
+}
+
+.col-qty {
+    flex: 0.5;
+}
+
+.col-aftersales {
+    flex: 0.5;
+}
+
+.col-buyer {
+    flex: 0.8;
+}
+
+.col-payment {
+    flex: 0.8;
+}
+
+.col-logistics {
+    flex: 1;
+}
+
+.col-status {
+    flex: 0.8;
+}
+
+.col-action {
+    flex: 0.8;
+}
+
+// Product Styles
+.product-info {
+    display: flex;
+    align-items: flex-start;
+
+    .product-img {
+        width: 60px;
+        height: 60px;
+        margin-right: 10px;
+        border-radius: 2px;
+    }
+
+    .product-detail {
+        font-size: 13px;
+
+        .product-title {
+            color: #303133;
+            margin-bottom: 4px;
+        }
+
+        .product-isbn {
+            color: #909399;
+            margin-bottom: 4px;
+
+            .link {
+                color: #409eff;
+                cursor: pointer;
+            }
+        }
+
+        .product-tags {
+            .tag {
+                color: #67c23a;
+                margin-right: 5px;
+
+                &.success {
+                    color: #67c23a;
+                }
+            }
+        }
+    }
+}
+
+.text-red {
+    color: #f56c6c;
+}
+
+.text-blue {
+    color: #409eff;
+}
+</style>

+ 202 - 0
src/views/mallOrder/all/components/order-product-list.vue

@@ -0,0 +1,202 @@
+<template>
+    <div class="order-product-list">
+        <ele-card header="商品详情">
+            <el-table :data="products" border style="width: 100%">
+                <!-- Image -->
+                <el-table-column label="图片" width="80" align="center">
+                    <template #default="{ row }">
+                        <el-image 
+                            :src="row.image" 
+                            style="width: 60px; height: 60px" 
+                            fit="cover" 
+                            :preview-src-list="[row.image]"
+                            preview-teleported
+                        />
+                    </template>
+                </el-table-column>
+
+                <!-- Product Info -->
+                <el-table-column label="商品信息" min-width="250">
+                    <template #default="{ row }">
+                        <div class="product-info">
+                            <div class="title">{{ row.title }}</div>
+                            <div class="meta">ISBN: {{ row.isbn }}</div>
+                            <div class="tags">
+                                <span class="discount-tag">回收折扣: {{ row.discount }}折</span>
+                                <span class="status-tag">(回收状态: {{ row.recycleStatus }})</span>
+                            </div>
+                        </div>
+                    </template>
+                </el-table-column>
+
+                <!-- Specs -->
+                <el-table-column label="规格" width="100" align="center">
+                    <template #default="{ row }">
+                        <span>({{ row.condition }})</span>
+                    </template>
+                </el-table-column>
+
+                <!-- Status -->
+                <el-table-column label="状态" width="120" align="center">
+                    <template #default="{ row }">
+                        <span :class="getStatusColor(row.refundStatus)">{{ row.refundStatus || '未确认收货' }}</span>
+                    </template>
+                </el-table-column>
+
+                <!-- Quantity -->
+                <el-table-column label="数量" width="80" align="center">
+                    <template #default="{ row }">
+                        <span>x{{ row.qty }}</span>
+                    </template>
+                </el-table-column>
+
+                <!-- Unit Price -->
+                <el-table-column label="单价" width="100" align="center">
+                    <template #default="{ row }">
+                        <span>¥ {{ row.price }}</span>
+                    </template>
+                </el-table-column>
+
+                <!-- Discount -->
+                <el-table-column label="优惠" width="120" align="center">
+                    <template #default="{ row }">
+                        <div v-if="row.discountAmount">
+                            <div>惊喜红包</div>
+                            <div>¥ {{ row.discountAmount }}</div>
+                        </div>
+                        <div v-else>¥ 0</div>
+                    </template>
+                </el-table-column>
+
+                <!-- Actual Payment -->
+                <el-table-column label="实收款" width="100" align="center">
+                    <template #default="{ row }">
+                        <span>¥ {{ row.actualPayment }}</span>
+                    </template>
+                </el-table-column>
+
+                <!-- Weight -->
+                <el-table-column label="重量" width="80" align="center">
+                    <template #default="{ row }">
+                        <span>{{ row.weight }}kg</span>
+                    </template>
+                </el-table-column>
+            </el-table>
+
+            <!-- Summary Footer -->
+            <div class="summary-footer">
+                <div class="summary-row" v-if="payment.discountTotal > 0">
+                    <span>余额优惠:</span>
+                    <span class="amount red">¥ {{ payment.discountTotal }}</span>
+                </div>
+                <div class="summary-row" v-if="payment.redPacket > 0">
+                    <span>惊喜红包:</span>
+                    <span class="amount red">¥ {{ payment.redPacket }}</span>
+                </div>
+                <div class="summary-row" v-if="payment.shareDiscount > 0">
+                    <span>分享降价:</span>
+                    <span class="amount red">¥ {{ payment.shareDiscount }}</span>
+                </div>
+                <div class="summary-row">
+                    <span>运费:</span>
+                    <span class="amount red">¥ {{ payment.shipping }}</span>
+                </div>
+                <div class="summary-row total">
+                    <span>订单实付金额:</span>
+                    <span class="amount red large">¥ {{ payment.total }}</span>
+                </div>
+            </div>
+        </ele-card>
+    </div>
+</template>
+
+<script setup>
+const props = defineProps({
+    products: {
+        type: Array,
+        default: () => []
+    },
+    payment: {
+        type: Object,
+        default: () => ({
+            total: 0,
+            shipping: 0,
+            discountTotal: 0,
+            redPacket: 0,
+            shareDiscount: 0
+        })
+    }
+});
+
+const getStatusColor = (status) => {
+    if (status === '退款成功') return 'text-green'; // Or red based on design
+    if (status === '未确认收货') return 'text-green';
+    return '';
+};
+</script>
+
+<style lang="scss" scoped>
+.product-info {
+    font-size: 13px;
+    .title {
+        color: #333;
+        margin-bottom: 4px;
+        line-height: 1.4;
+    }
+    .meta {
+        color: #999;
+        margin-bottom: 4px;
+    }
+    .tags {
+        font-size: 12px;
+        .discount-tag {
+            color: #67c23a;
+            margin-right: 5px;
+        }
+        .status-tag {
+            color: #67c23a;
+        }
+    }
+}
+
+.text-green { color: #67c23a; }
+.text-red { color: #f56c6c; }
+
+.summary-footer {
+    margin-top: 15px;
+    padding: 15px;
+    background-color: #f8fcfb; // Light greenish tint matching the screenshot bottom area
+    display: flex;
+    flex-direction: column;
+    align-items: flex-end;
+    
+    .summary-row {
+        margin-bottom: 5px;
+        font-size: 14px;
+        color: #606266;
+        
+        .amount {
+            margin-left: 10px;
+            font-weight: 500;
+            width: 80px;
+            text-align: right;
+            display: inline-block;
+            
+            &.red {
+                color: #f56c6c;
+            }
+            
+            &.large {
+                font-size: 16px;
+                font-weight: bold;
+            }
+        }
+        
+        &.total {
+            margin-top: 5px;
+            padding-top: 5px;
+            border-top: 1px dashed #e4e7ed;
+        }
+    }
+}
+</style>

+ 74 - 0
src/views/mallOrder/all/components/order-table-header.vue

@@ -0,0 +1,74 @@
+<template>
+    <div class="custom-table-header">
+        <div class="th-item col-product">商品信息</div>
+        <div class="th-item col-price">单价</div>
+        <div class="th-item col-qty">数量</div>
+        <div class="th-item col-aftersales">售后</div>
+        <div class="th-item col-buyer">买家</div>
+        <div class="th-item col-payment">实收款</div>
+        <div class="th-item col-logistics">发货物流</div>
+        <div class="th-item col-status">交易状态</div>
+        <div class="th-item col-action">操作</div>
+    </div>
+</template>
+
+<script setup>
+</script>
+
+<style lang="scss" scoped>
+.custom-table-header {
+    display: flex;
+    background-color: #f5f7fa;
+    padding: 10px 0;
+    font-weight: bold;
+    color: #606266;
+    margin-top: 10px;
+    border: 1px solid #ebeef5;
+    border-bottom: none;
+
+    .th-item {
+        text-align: center;
+        padding: 0 5px;
+        font-size: 13px;
+    }
+}
+
+// Column Widths - Keep consistent with OrderItem
+.col-product {
+    flex: 1.5;
+    text-align: left !important;
+    padding-left: 15px !important;
+}
+
+.col-price {
+    flex: 0.5;
+}
+
+.col-qty {
+    flex: 0.5;
+}
+
+.col-aftersales {
+    flex: 0.5;
+}
+
+.col-buyer {
+    flex: 0.8;
+}
+
+.col-payment {
+    flex: 0.8;
+}
+
+.col-logistics {
+    flex: 1;
+}
+
+.col-status {
+    flex: 0.8;
+}
+
+.col-action {
+    flex: 0.8;
+}
+</style>

+ 116 - 0
src/views/mallOrder/all/components/page-search.vue

@@ -0,0 +1,116 @@
+<template>
+    <ele-card :body-style="{ paddingBottom: '0' }">
+        <el-form :model="where" label-width="110px" @keyup.enter="search">
+            <el-row :gutter="15">
+                <el-col :lg="6" :md="12">
+                    <el-form-item label="订单编号">
+                        <el-input v-model="where.orderId" placeholder="请输入" clearable />
+                    </el-form-item>
+                </el-col>
+                <el-col :lg="6" :md="12">
+                    <el-form-item label="收件人姓名">
+                        <el-input v-model="where.receiverName" placeholder="请输入" clearable />
+                    </el-form-item>
+                </el-col>
+                <el-col :lg="6" :md="12">
+                    <el-form-item label="收件人手机号">
+                        <el-input v-model="where.receiverPhone" placeholder="请输入" clearable />
+                    </el-form-item>
+                </el-col>
+                <el-col :lg="6" :md="12">
+                    <el-form-item label="发货物流单号">
+                        <el-input v-model="where.logisticsNo" placeholder="请输入" clearable />
+                    </el-form-item>
+                </el-col>
+                <el-col :lg="6" :md="12">
+                    <el-form-item label="退货物流单号">
+                        <el-input v-model="where.returnLogisticsNo" placeholder="请输入" clearable />
+                    </el-form-item>
+                </el-col>
+                <el-col :lg="6" :md="12">
+                    <el-form-item label="全部订单状态">
+                        <el-select v-model="where.status" placeholder="请选择" clearable>
+                            <el-option v-for="item in statusDicts" :key="item.dictValue" :label="item.dictLabel"
+                                :value="item.dictValue" />
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :lg="6" :md="12">
+                    <el-form-item label="搜索备注关键字">
+                        <el-input v-model="where.keyword" placeholder="请输入" clearable />
+                    </el-form-item>
+                </el-col>
+                <el-col :lg="6" :md="12">
+                    <el-form-item label="催发货订单">
+                        <el-select v-model="where.urgeStatus" placeholder="请选择" clearable>
+                            <el-option label="催发货" value="1" />
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :lg="6" :md="12">
+                    <el-form-item label="下单时间">
+                        <el-date-picker v-model="dateRange" type="daterange" range-separator="到"
+                            start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD"
+                            style="width: 100%" @change="handleDateChange" />
+                    </el-form-item>
+                </el-col>
+
+                <el-col :lg="6" :md="12" class="mb-4">
+                    <el-button type="primary" @click="search">查询</el-button>
+                    <el-button @click="reset">重置</el-button>
+                </el-col>
+            </el-row>
+        </el-form>
+    </ele-card>
+</template>
+
+<script setup>
+import { reactive, ref } from 'vue';
+import { useDictData } from '@/utils/use-dict-data';
+
+const emit = defineEmits(['search']);
+
+// 字典
+const [statusDicts] = useDictData(['order_status']);
+
+const where = reactive({
+    orderId: '',
+    receiverName: '',
+    receiverPhone: '',
+    logisticsNo: '',
+    returnLogisticsNo: '',
+    status: '',
+    keyword: '',
+    urgeStatus: '',
+    startTime: '',
+    endTime: ''
+});
+
+const dateRange = ref([]);
+
+const handleDateChange = (val) => {
+    if (val && val.length === 2) {
+        where.startTime = val[0];
+        where.endTime = val[1];
+    } else {
+        where.startTime = '';
+        where.endTime = '';
+    }
+};
+
+const search = () => {
+    emit('search', { ...where });
+};
+
+const reset = () => {
+    Object.keys(where).forEach(key => where[key] = '');
+    dateRange.value = [];
+    search();
+};
+</script>
+
+<style scoped>
+.mb-4 {
+    margin-bottom: 16px;
+}
+</style>

+ 209 - 0
src/views/mallOrder/all/components/push-sms-dialog.vue

@@ -0,0 +1,209 @@
+<template>
+    <ele-modal
+        form
+        :width="800"
+        v-model="visible"
+        title="推送短信"
+        @open="handleOpen"
+    >
+        <el-form
+            ref="formRef"
+            :model="form"
+            :rules="rules"
+            label-width="80px"
+            @submit.prevent=""
+        >
+            <el-form-item label="短信类型" prop="type">
+                <el-radio-group v-model="form.type" @change="handleChangeType">
+                    <el-radio
+                        :value="item.type"
+                        v-for="item in smsContentList"
+                        :key="item.type"
+                        >{{ item.typeName }}</el-radio
+                    >
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item label="短信预览" prop="remark">
+                <el-input
+                    :rows="4"
+                    type="textarea"
+                    v-model="form.remark"
+                    disabled
+                    placeholder="请输入短信预览"
+                />
+            </el-form-item>
+            <el-form-item label="推送历史" prop="records">
+                <ele-data-table
+                    row-key="id"
+                    :columns="columns"
+                    :data="smsRecords"
+                    height="200px"
+                    :show-overflow-tooltip="true"
+                />
+            </el-form-item>
+        </el-form>
+
+        <template #footer>
+            <el-button @click="handleCancel">关闭</el-button>
+            <el-button type="primary" @click="handleSubmit">确定</el-button>
+        </template>
+    </ele-modal>
+</template>
+
+<script setup>
+import { ref, reactive, getCurrentInstance } from 'vue';
+import { EleMessage } from 'ele-admin-plus/es';
+
+const { proxy } = getCurrentInstance();
+const emit = defineEmits(['success']);
+
+/** 弹窗是否打开 */
+const visible = defineModel({ type: Boolean });
+
+/** 表单引用 */
+const formRef = ref();
+
+/** SMS记录数据 */
+const smsRecords = ref([]);
+
+/** 短信内容列表 */
+const smsContentList = ref([]);
+
+const form = reactive({
+    orderId: null,
+    type: '',
+    remark: ''
+});
+
+/** 短信类型变化 */
+const handleChangeType = (val) => {
+    const found = smsContentList.value.find((item) => item.type === val);
+    if (found) {
+        form.remark = found.smsContent;
+    }
+};
+
+/** 表单验证规则 */
+const rules = reactive({
+    type: [
+        {
+            required: true,
+            message: '请选择短信类型',
+            trigger: 'change'
+        }
+    ],
+    remark: [
+        {
+            required: true,
+            message: '请输入短信预览',
+            type: 'string',
+            trigger: 'blur'
+        }
+    ]
+});
+
+// 表格列配置
+const columns = reactive([
+    { label: '发送人', prop: 'createName', width: 100, align: 'center' },
+    { label: '发送内容', prop: 'smsContent', align: 'center', minWidth: 200 },
+    { label: '发送时间', prop: 'createTime', width: 160, align: 'center' },
+    {
+        label: '发送状态',
+        prop: 'status',
+        width: 100,
+        align: 'center',
+        formatter: (row) => (row.status == 1 ? '成功' : '失败')
+    }
+]);
+
+/** 关闭弹窗 */
+const handleCancel = () => {
+    visible.value = false;
+    resetForm();
+};
+
+/** 重置表单 */
+const resetForm = () => {
+    form.orderId = null;
+    form.type = '';
+    form.remark = '';
+    formRef.value?.resetFields();
+    smsRecords.value = [];
+};
+
+/** 弹窗打开事件 */
+const handleOpen = (row) => {
+    resetForm();
+    // 模拟获取数据,实际开发中可以使用 row.orderId 调用接口
+    if (row) {
+        form.orderId = row.orderId || 123;
+        getSmsLogInfo(form.orderId);
+    } else {
+        // 如果没有 row 传入,也可以默认加载 mock 数据方便预览
+        getSmsLogInfo(123);
+    }
+    visible.value = true;
+};
+
+/** 获取短信记录信息 (Mock) */
+const getSmsLogInfo = (orderId) => {
+    // 模拟 API 请求延迟
+    setTimeout(() => {
+        // Mock data
+        smsContentList.value = [
+            { type: 'urge', typeName: '催发货提醒', smsContent: '亲,您的订单已发货,请注意查收。' },
+            { type: 'receive', typeName: '收货提醒', smsContent: '亲,您的宝贝已送达,请确认收货。' },
+            { type: 'good_review', typeName: '好评提醒', smsContent: '亲,如果您对宝贝满意,请给个好评哦~' }
+        ];
+
+        smsRecords.value = [
+            {
+                id: 1,
+                createName: '管理员',
+                smsContent: '亲,您的订单已发货,请注意查收。',
+                createTime: '2023-10-27 10:00:00',
+                status: 1
+            },
+            {
+                id: 2,
+                createName: '客服小美',
+                smsContent: '亲,您的宝贝已送达,请确认收货。',
+                createTime: '2023-10-28 14:30:00',
+                status: 1
+            }
+        ];
+    }, 100);
+};
+
+/** 提交表单 */
+const handleSubmit = () => {
+    formRef.value?.validate(async (valid) => {
+        if (!valid) return;
+        
+        // 模拟提交
+        setTimeout(() => {
+            EleMessage.success('短信发送成功');
+            // 添加一条记录到 mock 列表,模拟实时更新
+            smsRecords.value.unshift({
+                id: Date.now(),
+                createName: '当前用户',
+                smsContent: form.remark,
+                createTime: new Date().toLocaleString(),
+                status: 1
+            });
+            // 这里的业务逻辑是发送后不一定马上关闭,或者可以关闭
+            // 参考 send-SMS.vue 是关闭的
+            handleCancel();
+            emit('success');
+        }, 500);
+    });
+};
+
+defineExpose({
+    handleOpen
+});
+</script>
+
+<style scoped>
+/* 可以在这里添加样式微调 */
+</style>

+ 234 - 0
src/views/mallOrder/all/components/refund-dialog.vue

@@ -0,0 +1,234 @@
+<template>
+    <ele-modal
+        :width="800"
+        v-model="visible"
+        title="缺货退款"
+        @open="handleOpen"
+        :body-style="{ padding: '20px' }"
+    >
+        <div class="refund-container">
+            <div class="section-title">选择要退款的商品及数量</div>
+            
+            <el-table
+                ref="tableRef"
+                :data="order?.products || []"
+                style="width: 100%; border: 1px solid #ebeef5; border-bottom: none;"
+                @selection-change="handleSelectionChange"
+                header-row-class-name="custom-header"
+            >
+                <el-table-column type="selection" width="55" />
+                <el-table-column label="商品信息">
+                    <template #default="{ row }">
+                        <div class="product-info">
+                            <el-image :src="row.image" class="product-img" fit="cover" />
+                            <span class="product-title">{{ row.title }}</span>
+                        </div>
+                    </template>
+                </el-table-column>
+                <el-table-column label="数量" width="150">
+                    <template #default="{ row }">
+                        <el-input-number 
+                            v-model="row.refundQty" 
+                            :min="1" 
+                            :max="row.qty" 
+                            size="small" 
+                            controls-position="right"
+                            style="width: 100px;"
+                        />
+                    </template>
+                </el-table-column>
+                <el-table-column label="单价" width="120">
+                    <template #default="{ row }">
+                        <span>¥{{ row.price }}</span>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <!-- Fill empty rows to mimic design height if needed, but el-table auto height is better. 
+                 Design shows empty grid lines, which el-table does by default if data is short? No.
+                 We'll stick to standard table. -->
+
+            <div class="refund-summary mt-4">
+                <div class="summary-item">
+                    <span class="label">退还商品:</span>
+                    <span class="value">{{ productRefundAmount.toFixed(2) }} 元</span>
+                    <span class="sub-text">(共{{ selectedCount }}件)</span>
+                </div>
+                <div class="summary-item items-center">
+                    <span class="label">退还运费:</span>
+                    <el-input 
+                        v-model="shippingRefund" 
+                        style="width: 100px; margin-right: 10px;" 
+                        placeholder="请输入"
+                    />
+                    <span class="label">元</span>
+                    <span class="warning-text">(不超过{{ maxShippingFee }}元)</span>
+                </div>
+                <div class="summary-item">
+                    <span class="label">退款总额:</span>
+                    <span class="total-amount">{{ totalRefundAmount.toFixed(2) }} 元</span>
+                    <span class="sub-text">(原订单总额{{ originalTotal.toFixed(2) }}元 新订单总额{{ newTotal.toFixed(2) }}元)</span>
+                </div>
+                <div class="summary-item mt-2">
+                    <el-checkbox v-model="sendCoupon">
+                        <span class="font-bold">发送“缺货退款购书红包”</span>
+                    </el-checkbox>
+                </div>
+            </div>
+        </div>
+
+        <template #footer>
+            <el-button @click="visible = false">取消</el-button>
+            <el-button type="primary" @click="handleSubmit">确定并退款</el-button>
+        </template>
+    </ele-modal>
+</template>
+
+<script setup>
+import { ref, reactive, computed, watch } from 'vue';
+import { EleMessage } from 'ele-admin-plus/es';
+
+const visible = defineModel({ type: Boolean });
+const order = ref(null);
+const selectedRows = ref([]);
+const shippingRefund = ref(0);
+const sendCoupon = ref(true);
+const maxShippingFee = ref(5);
+
+const handleOpen = (row) => {
+    // Deep copy to avoid mutating original list directly until save
+    if (row) {
+        order.value = JSON.parse(JSON.stringify(row));
+        // Initialize refundQty for each product
+        if (order.value.products) {
+            order.value.products.forEach(p => {
+                p.refundQty = p.qty; // Default to max qty
+            });
+        }
+        shippingRefund.value = 0;
+        sendCoupon.value = true;
+        selectedRows.value = [];
+    }
+    visible.value = true;
+};
+
+const handleSelectionChange = (val) => {
+    selectedRows.value = val;
+};
+
+// Computed
+const productRefundAmount = computed(() => {
+    return selectedRows.value.reduce((sum, item) => {
+        return sum + (item.price * (item.refundQty || 0));
+    }, 0);
+});
+
+const selectedCount = computed(() => {
+    return selectedRows.value.reduce((sum, item) => {
+        return sum + (item.refundQty || 0);
+    }, 0);
+});
+
+const totalRefundAmount = computed(() => {
+    const shipping = parseFloat(shippingRefund.value) || 0;
+    return productRefundAmount.value + shipping;
+});
+
+const originalTotal = computed(() => {
+    return order.value?.payment?.total || 0;
+});
+
+const newTotal = computed(() => {
+    const val = originalTotal.value - totalRefundAmount.value;
+    return val > 0 ? val : 0;
+});
+
+const handleSubmit = () => {
+    if (selectedRows.value.length === 0 && parseFloat(shippingRefund.value) <= 0) {
+        EleMessage.warning('请选择退款商品或输入退款运费');
+        return;
+    }
+    
+    const shipping = parseFloat(shippingRefund.value) || 0;
+    if (shipping > maxShippingFee.value) {
+        EleMessage.error(`退还运费不能超过${maxShippingFee.value}元`);
+        return;
+    }
+
+    EleMessage.success('退款申请已提交');
+    visible.value = false;
+};
+
+defineExpose({
+    handleOpen
+});
+</script>
+
+<style scoped lang="scss">
+.refund-container {
+    .section-title {
+        font-weight: bold;
+        margin-bottom: 10px;
+        font-size: 14px;
+        color: #303133;
+    }
+    
+    .product-info {
+        display: flex;
+        align-items: center;
+        .product-img {
+            width: 50px;
+            height: 50px;
+            margin-right: 10px;
+            border-radius: 4px;
+        }
+        .product-title {
+            font-size: 13px;
+            line-height: 1.4;
+        }
+    }
+
+    .refund-summary {
+        font-size: 14px;
+        color: #606266;
+        
+        .summary-item {
+            display: flex;
+            align-items: center;
+            margin-bottom: 10px;
+            line-height: 24px;
+            
+            .label {
+                font-weight: bold;
+                color: #303133;
+            }
+            
+            .value {
+                color: #303133;
+                font-weight: bold;
+            }
+            
+            .total-amount {
+                color: #f56c6c;
+                font-weight: bold;
+                font-size: 16px;
+            }
+            
+            .sub-text {
+                color: #909399;
+                margin-left: 5px;
+                font-size: 13px;
+            }
+            
+            .warning-text {
+                color: #303133;
+                font-weight: bold;
+            }
+        }
+    }
+}
+:deep(.custom-header) th {
+    background-color: #f5f7fa !important;
+    color: #606266;
+    font-weight: normal;
+}
+</style>

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

@@ -0,0 +1,198 @@
+<template>
+    <ele-page flex-table>
+        <page-search @search="handleSearch" />
+
+        <ele-card :body-style="{ padding: '10px' }">
+            <!-- Tabs -->
+            <el-tabs v-model="activeTab" @tab-click="handleTabClick">
+                <el-tab-pane label="全部订单状态" name="all" />
+                <el-tab-pane label="等待买家付款" name="1" />
+                <el-tab-pane label="等待卖家发货" name="2" />
+                <el-tab-pane label="等待买家确认收货" name="3" />
+                <el-tab-pane label="交易成功" name="4" />
+                <el-tab-pane label="退款成功" name="5" />
+                <el-tab-pane label="退款中" name="6" />
+                <el-tab-pane label="订单取消" name="7" />
+                <el-tab-pane label="预警订单/催发货" name="warning" />
+            </el-tabs>
+
+            <!-- Custom Table Header -->
+            <order-table-header />
+
+            <!-- Order List -->
+            <div class="order-list" v-loading="loading">
+                <order-item v-for="order in list" :key="order.orderId" :order="order" @view-detail="openDetail"
+                    @push-sms="openSms" @refund="openRefund" />
+            </div>
+
+            <!-- Pagination -->
+            <div class="pagination-wrapper">
+                <el-pagination v-model:current-page="pageParams.page" v-model:page-size="pageParams.limit"
+                    :total="total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
+                    @current-change="handlePageChange" />
+            </div>
+        </ele-card>
+
+        <!-- Dialogs -->
+        <order-detail ref="detailRef" />
+        <push-sms-dialog ref="smsRef" />
+        <refund-dialog ref="refundRef" />
+        <add-package-dialog ref="packageRef" />
+    </ele-page>
+</template>
+
+<script setup>
+import { ref, reactive } from 'vue';
+import PageSearch from './components/page-search.vue';
+import OrderDetail from './components/order-detail.vue';
+import PushSmsDialog from './components/push-sms-dialog.vue';
+import RefundDialog from './components/refund-dialog.vue';
+import AddPackageDialog from './components/add-package-dialog.vue';
+import OrderTableHeader from './components/order-table-header.vue';
+import OrderItem from './components/order-item.vue';
+
+const activeTab = ref('all');
+const loading = ref(false);
+const total = ref(10);
+const pageParams = reactive({
+    page: 1,
+    limit: 10
+});
+
+const list = ref([
+    {
+        orderId: '4066879393210936038',
+        createTime: '2024-10-04 12:01:09',
+        payChannel: '微信支付',
+        source: '微信',
+        flag: true,
+        isUrge: false,
+        checked: false,
+        buyer: {
+            avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
+            name: '用户44541',
+            phone: '1894984dsaddq'
+        },
+        payment: {
+            total: 2.9,
+            shipping: 0
+        },
+        logistics: {
+            company: '韵达快递',
+            no: '46382090456249',
+            status: '已发货'
+        },
+        status: '退款成功',
+        buyerNote: '',
+        products: [
+            {
+                image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
+                title: '毛泽东思想和中国特色社会主义理论体系概论(2023年版)',
+                isbn: '9787040599012',
+                condition: '中等',
+                price: 2.9,
+                qty: 1,
+                discount: 1,
+                recycleStatus: '正在回收',
+                refundStatus: '退款成功'
+            }
+        ]
+    },
+    {
+        orderId: '4066879393210936039',
+        createTime: '2024-10-04 12:06:09',
+        payChannel: '微信支付',
+        source: '微信',
+        flag: false,
+        isUrge: true,
+        checked: false,
+        buyer: {
+            avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
+            name: '用户44541',
+            phone: '1894984dsaddq'
+        },
+        payment: {
+            total: 5.8,
+            shipping: 0
+        },
+        logistics: {
+            company: '韵达快递',
+            no: '46382090456248',
+            status: '已发货'
+        },
+        status: '等待买家确认收货',
+        buyerNote: '您好,请确保书籍正版,且无破损、不缺页。请不要随书夹带任何票据,有问题留言不要打电话。请尽快发货,谢谢啦~',
+        products: [
+            {
+                image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
+                title: '毛泽东思想和中国特色社会主义理论体系概论(2023年版)',
+                isbn: '9787040599012',
+                condition: '中等',
+                price: 2.9,
+                qty: 1,
+                discount: 1,
+                recycleStatus: '正在回收',
+                refundStatus: ''
+            },
+            {
+                image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
+                title: '毛泽东思想和中国特色社会主义理论体系概论(2023年版)',
+                isbn: '9787040599012',
+                condition: '中等',
+                price: 2.9,
+                qty: 2,
+                discount: 1,
+                recycleStatus: '正在回收',
+                refundStatus: '退款取消'
+            }
+        ]
+    }
+]);
+
+const detailRef = ref(null);
+const smsRef = ref(null);
+const refundRef = ref(null);
+const packageRef = ref(null);
+
+const handleSearch = (params) => {
+    console.log('Search:', params);
+    // Mock reload
+    loading.value = true;
+    setTimeout(() => { loading.value = false; }, 500);
+};
+
+const handleTabClick = (tab) => {
+    console.log('Tab:', tab.props.name);
+    handleSearch({});
+};
+
+const handleSizeChange = (val) => {
+    pageParams.limit = val;
+    handleSearch({});
+};
+
+const handlePageChange = (val) => {
+    pageParams.page = val;
+    handleSearch({});
+};
+
+const openDetail = (row) => {
+    detailRef.value?.handleOpen(row);
+};
+
+const openSms = (row) => {
+    smsRef.value?.handleOpen(row);
+};
+
+const openRefund = (row) => {
+    refundRef.value?.handleOpen(row);
+};
+</script>
+
+<style lang="scss" scoped>
+.pagination-wrapper {
+    margin-top: 15px;
+    display: flex;
+    justify-content: flex-end;
+}
+</style>

+ 46 - 0
src/views/mallOrder/complaint/components/complain-item.vue

@@ -0,0 +1,46 @@
+<template>
+    <div class="complain-item flex mt-2 pr-4">
+        <el-avatar shape="square" :size="40" fit="cover" :src="item.userType === '1' ? item.imgPath : Logo" />
+
+        <div class="flex-1 flex flex-col ml-2">
+            <div class="flex justify-between">
+                <ele-text size="small" type="info">{{
+                    item.userName
+                }}</ele-text>
+                <ele-text size="small" type="info">{{
+                    item.createTime || ''
+                }}</ele-text>
+            </div>
+            <div class="mt-2">
+                <ele-text>{{ item.description || '' }}</ele-text>
+            </div>
+            <!-- 显示图片列表 -->
+            <div v-if="item.imgList && item.imgList.length" class="mt-2 flex flex-wrap">
+                <el-image v-for="(img, imgIndex) in item.imgList" :key="imgIndex" style="
+                        width: 70px;
+                        height: 70px;
+                        border-radius: 5px;
+                        margin-right: 8px;
+                        margin-bottom: 8px;
+                    " :src="img" fit="cover" :preview-src-list="item.imgList" :initial-index="imgIndex"
+                    preview-teleported />
+            </div>
+        </div>
+    </div>
+    <el-divider style="margin: 10px 0" />
+</template>
+
+<script setup>
+import Logo from '@/assets/logo.png';
+defineProps({
+    item: {
+        type: Object,
+        default: () => ({
+            userType: '用户',
+            createTime: '',
+            description: '',
+            fileUrls: []
+        })
+    }
+});
+</script>

+ 149 - 0
src/views/mallOrder/complaint/components/negotiate-dialog.vue

@@ -0,0 +1,149 @@
+<template>
+    <el-dialog
+        v-model="dialogVisible"
+        title="协商(留言)"
+        width="500px"
+        :close-on-click-modal="false"
+        :close-on-press-escape="false"
+    >
+        <div>
+            <div class="mb-2">说明:</div>
+            <el-input
+                v-model="form.description"
+                type="textarea"
+                :rows="5"
+                placeholder="请输入"
+            />
+
+            <div class="mt-4 mb-2">附件:</div>
+            <ImageUpload
+                v-model="fileUrls"
+                multiple
+                :limit="3"
+                @change="handleFileUrlsChange"
+            />
+        </div>
+        <template #footer>
+            <span class="dialog-footer">
+                <el-button @click="handleCancel">取消</el-button>
+                <el-button type="primary" @click="handleSubmit">确定</el-button>
+            </span>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup>
+    import {
+        ref,
+        reactive,
+        defineEmits,
+        defineProps,
+        defineExpose,
+        watch
+    } from 'vue';
+    import { EleMessage } from 'ele-admin-plus/es';
+    import ImageUpload from '@/components/ImageUpload/index.vue';
+
+    const props = defineProps({
+        visible: {
+            type: Boolean,
+            default: false
+        },
+        id: {
+            type: [Number, String],
+            default: ''
+        }
+    });
+
+    const emit = defineEmits(['update:visible', 'submit', 'cancel']);
+
+    // 对话框可见性
+    const dialogVisible = ref(false);
+
+    // 监听visible属性变化
+    watch(
+        () => props.visible,
+        (val) => {
+            dialogVisible.value = val;
+            if (val) {
+                // 重置表单
+                resetForm();
+            }
+        }
+    );
+
+    // 监听dialogVisible变化
+    watch(dialogVisible, (val) => {
+        emit('update:visible', val);
+    });
+
+    // 表单数据
+    const form = reactive({
+        id: '',
+        description: '',
+        fileUrls: []
+    });
+
+    // 文件URL字符串,用于ImageUpload组件
+    const fileUrls = ref('');
+
+    // 重置表单
+    const resetForm = () => {
+        form.id = props.id;
+        form.description = '';
+        form.fileUrls = [];
+        fileUrls.value = '';
+    };
+
+    // 处理文件URL变更
+    const handleFileUrlsChange = (value) => {
+        if (value) {
+            try {
+                // ImageUpload组件返回的是JSON字符串,需要解析
+                form.fileUrls = JSON.parse(value);
+            } catch (e) {
+                // 单个文件的情况,直接作为字符串处理
+                form.fileUrls = [value];
+            }
+        } else {
+            form.fileUrls = [];
+        }
+    };
+
+    // 取消
+    const handleCancel = () => {
+        dialogVisible.value = false;
+        emit('cancel');
+    };
+
+    // 提交
+    const handleSubmit = () => {
+        if (!form.description.trim()) {
+            EleMessage.warning('请输入协商内容');
+            return;
+        }
+
+        // 准备提交数据
+        const submitData = {
+            id: form.id,
+            description: form.description,
+            fileUrls: form.fileUrls
+        };
+
+        // 发送提交事件
+        emit('submit', submitData);
+    };
+
+    // 对外暴露方法
+    defineExpose({
+        resetForm
+    });
+</script>
+
+<style lang="scss" scoped>
+    .upload-demo {
+        .el-upload {
+            margin-right: 10px;
+        }
+    }
+</style>

+ 381 - 0
src/views/mallOrder/complaint/components/page-edit.vue

@@ -0,0 +1,381 @@
+<!-- 编辑弹窗 -->
+<template>
+    <ele-modal
+        form
+        :width="960"
+        v-model="visible"
+        title="投诉状态及操作"
+        @open="handleOpen"
+    >
+        <div class="flex w-full">
+            <div style="flex: 1.5">
+                <el-tag size="large">{{ getStatusText(form.status) }}</el-tag>
+
+                <ele-text style="margin-top: 15px"
+                    >1.请点击协商(留言)与对方协商。</ele-text
+                >
+                <ele-text>2.也可点击完结,投诉完结。</ele-text>
+                <el-divider />
+                <el-button @click="handleNegotiate">协商(留言)</el-button>
+                <el-button type="danger" @click="handleComplete"
+                    >完结</el-button
+                >
+                <el-divider />
+
+                <div class="common-title">协商历史</div>
+                <complain-item
+                    v-for="(item, index) in disposeLogList"
+                    :key="index"
+                    :item="item"
+                />
+            </div>
+            <div class="flex-1 complain-detail">
+                <div class="common-title">投诉详情</div>
+                <div class="detail-content mt-6">
+                    <div class="detail-item">
+                        <span class="label">投诉编号:</span>
+                        <span class="value">{{ form.id }}</span>
+                    </div>
+                    <div class="detail-item">
+                        <span class="label">投诉原因:</span>
+                        <span class="value">{{ form.reason }}</span>
+                    </div>
+                    <div class="detail-item">
+                        <span class="label">投诉说明:</span>
+                        <span class="value">{{ form.description }}</span>
+                    </div>
+                    <div class="detail-item">
+                        <span class="label">投诉时间:</span>
+                        <span class="value">{{ form.createTime }}</span>
+                    </div>
+                    <div class="detail-item">
+                        <span class="label">发 起 人:</span>
+                        <span class="value">{{ form.userName }}</span>
+                    </div>
+                    <div class="detail-item">
+                        <span class="label">订单编号:</span>
+                        <el-tooltip
+                            content="跳转到订单详情<br/>商品最多展示三个"
+                            placement="top"
+                            effect="light"
+                            raw-content
+                        >
+                            <span class="value link" @click="goToOrder(form.orderId)">{{ form.orderId }}</span>
+                        </el-tooltip>
+                    </div>
+                </div>
+
+                <el-divider />
+
+                <!-- 商品列表 -->
+                <div class="product-list">
+                    <div v-for="(item, index) in goodsList" :key="index" class="product-item">
+                        <el-image :src="item.image" class="product-img" fit="cover" />
+                        <div class="product-info">
+                            <div class="product-title">{{ item.title }}</div>
+                            <div class="product-meta">
+                                <span class="product-spec">{{ item.spec }}</span>
+                                <span class="product-qty" :class="{ 'text-red': item.quantity > 1 }">X{{ item.quantity }}</span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <template #footer>
+            <el-button @click="handleCancel">关闭</el-button>
+        </template>
+    </ele-modal>
+
+    <!-- 协商弹窗 -->
+    <negotiate-dialog
+        v-model:visible="negotiateVisible"
+        :id="currentId"
+        @submit="submitNegotiate"
+    />
+</template>
+
+<script setup>
+    import { ref, reactive, nextTick, getCurrentInstance } from 'vue';
+    import complainItem from './complain-item.vue';
+    import negotiateDialog from './negotiate-dialog.vue';
+    import { EleMessage } from 'ele-admin-plus/es';
+    import { ElMessageBox } from 'element-plus';
+    import { useDictData } from '@/utils/use-dict-data';
+    import Logo from '@/assets/logo.png';
+
+    const { proxy } = getCurrentInstance();
+    const emit = defineEmits(['refresh']);
+
+    // 获取投诉状态字典
+    const [statusDicts] = useDictData(['complain_status']);
+
+    // 获取状态文本
+    const getStatusText = (status) => {
+        return (
+            statusDicts.value.find((d) => d.dictValue == status)?.dictLabel ||
+            '协商中'
+        );
+    };
+
+    /** 弹窗是否打开 */
+    const visible = defineModel({ type: Boolean });
+
+    /** 关闭弹窗 */
+    const handleCancel = () => {
+        visible.value = false;
+    };
+
+    // 当前投诉ID
+    const currentId = ref(null);
+    // 处理记录列表
+    const disposeLogList = ref([]);
+    // 商品列表
+    const goodsList = ref([]);
+
+    /** 弹窗打开事件 */
+    const handleOpen = (row) => {
+        if (row && row.id) {
+            currentId.value = row.id;
+            visible.value = true;
+            getComplaintDetail(row.id);
+        } else {
+            visible.value = true;
+            nextTick(() => console.log('打开'));
+        }
+    };
+
+    // 获取投诉详情
+    const getComplaintDetail = (id) => {
+        proxy.$http
+            .get(`/order/orderComplaintsLog/getInfo/${id}`)
+            .then((res) => {
+                if (res.data.code === 200) {
+                    const data = res.data.data || {};
+                    // 映射API返回的数据到表单
+                    form.id = data.id || '';
+                    form.userName = data.userName || '-';
+                    form.orderId = data.orderId || '';
+                    form.reason = data.reason || '';
+                    form.description = data.description || '';
+                    form.createTime = data.createTime || '';
+                    form.status = data.status || '';
+
+                    // 处理协商历史
+                    disposeLogList.value = data.disposeLogList || [];
+                    
+                    // 模拟商品数据 (因为后端可能没返回)
+                    goodsList.value = [
+                        {
+                            image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
+                            title: '新版中日交流标准日本语初级上下册第二版日语零基础入门',
+                            spec: '品相 (一般)',
+                            quantity: 1
+                        },
+                        {
+                            image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
+                            title: '新版中日交流标准日本语初级上下册第二版日语零基础入门',
+                            spec: '品相 (一般)',
+                            quantity: 2
+                        },
+                         {
+                            image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
+                            title: '新版中日交流标准日本语初级上下册第二版日语零基础入门',
+                            spec: '品相 (一般)',
+                            quantity: 1
+                        }
+                    ];
+                } else {
+                    EleMessage.error(res.data.msg || '获取投诉详情失败');
+                }
+            })
+            .catch((err) => {
+                console.error('获取投诉详情失败', err);
+                EleMessage.error('获取投诉详情失败');
+            });
+    };
+
+    // 协商弹窗相关
+    const negotiateVisible = ref(false);
+
+    // 处理协商
+    const handleNegotiate = () => {
+        if (!currentId.value) {
+            EleMessage.warning('请先选择投诉记录');
+            return;
+        }
+
+        // 打开协商弹窗
+        negotiateVisible.value = true;
+    };
+
+    // 提交协商
+    const submitNegotiate = (formData) => {
+        // 调用协商API
+        proxy.$http
+            .post('/order/orderComplaintsLog/dispose', formData)
+            .then((res) => {
+                if (res.data.code === 200) {
+                    EleMessage.success('协商成功');
+                    negotiateVisible.value = false;
+
+                    // 刷新详情,更新协商历史
+                    getComplaintDetail(currentId.value);
+                } else {
+                    EleMessage.error(res.data.msg || '协商失败');
+                }
+            })
+            .catch((err) => {
+                console.error('协商失败', err);
+                EleMessage.error('协商失败');
+            });
+    };
+
+    // 处理完结
+    const handleComplete = () => {
+        if (!currentId.value) {
+            EleMessage.warning('请先选择投诉记录');
+            return;
+        }
+
+        ElMessageBox.confirm('确认完结此投诉?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+        })
+            .then(() => {
+                // 调用完结API
+                proxy.$http
+                    .post(`/order/orderComplaintsLog/over/${currentId.value}`)
+                    .then((res) => {
+                        if (res.data.code === 200) {
+                            EleMessage.success('投诉已完结');
+                            emit('refresh'); // 通知父组件刷新列表
+                            handleCancel(); // 关闭弹窗
+                        } else {
+                            EleMessage.error(res.data.msg || '操作失败');
+                        }
+                    })
+                    .catch((err) => {
+                        console.error('完结投诉失败', err);
+                        EleMessage.error('完结投诉失败');
+                    });
+            })
+            .catch(() => {
+                // 用户取消操作
+            });
+    };
+    
+    // 跳转到订单详情
+    const goToOrder = (orderId) => {
+        if (!orderId) return;
+        // 由于暂无订单详情页,暂时提示
+        EleMessage.info(`功能开发中,订单号: ${orderId}`);
+        // 实际开发中应该是: router.push({ path: '/mallOrder/detail', query: { id: orderId } })
+    };
+
+    const form = reactive({
+        id: '',
+        userName: '',
+        orderId: '',
+        reason: '',
+        description: '',
+        createTime: '',
+        status: ''
+    });
+
+    defineExpose({
+        handleOpen
+    });
+</script>
+
+<style lang="scss">
+    .complain-detail {
+        border-left: 1px solid #e6e6e6;
+        padding-left: 15px;
+        
+        .detail-item {
+            margin-bottom: 15px;
+            font-size: 14px;
+            display: flex;
+            
+            .label {
+                color: #666;
+                width: 80px;
+                flex-shrink: 0;
+            }
+            
+            .value {
+                color: #333;
+                flex: 1;
+                
+                &.link {
+                    color: #409eff;
+                    cursor: pointer;
+                    &:hover {
+                        text-decoration: underline;
+                    }
+                }
+            }
+        }
+        
+        .product-list {
+            .product-item {
+                display: flex;
+                margin-bottom: 15px;
+                padding-bottom: 15px;
+                border-bottom: 1px solid #f5f5f5;
+                
+                &:last-child {
+                    border-bottom: none;
+                }
+                
+                .product-img {
+                    width: 80px;
+                    height: 80px;
+                    border-radius: 4px;
+                    margin-right: 10px;
+                    flex-shrink: 0;
+                    background-color: #f5f5f5;
+                }
+                
+                .product-info {
+                    flex: 1;
+                    display: flex;
+                    flex-direction: column;
+                    justify-content: space-between;
+                    
+                    .product-title {
+                        font-weight: bold;
+                        color: #333;
+                        font-size: 14px;
+                        line-height: 1.4;
+                        margin-bottom: 5px;
+                    }
+                    
+                    .product-meta {
+                        display: flex;
+                        justify-content: space-between;
+                        align-items: flex-end;
+                        
+                        .product-spec {
+                            color: #999;
+                            font-size: 12px;
+                        }
+                        
+                        .product-qty {
+                            font-size: 14px;
+                            font-weight: bold;
+                            color: #333;
+                            
+                            &.text-red {
+                                color: #ff4d4f;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+</style>

+ 69 - 0
src/views/mallOrder/complaint/components/page-search.vue

@@ -0,0 +1,69 @@
+<!-- 搜索表单 -->
+<template>
+    <ele-card :body-style="{ paddingBottom: '8px' }">
+        <ProSearch
+            :items="formItems"
+            ref="searchRef"
+            @search="search"
+            :initKeys="initKeys"
+        />
+    </ele-card>
+</template>
+
+<script setup>
+    import { reactive, ref, defineEmits } from 'vue';
+    import ProSearch from '@/components/CommonPage/ProSearch2.vue';
+
+    const emit = defineEmits(['search']);
+
+    const formItems = reactive([
+        { type: 'input', label: '订单编号', prop: 'orderId' },
+        { type: 'input', label: '投诉编号', prop: 'id' },
+        {
+            type: 'datetimerange',
+            label: '投诉时间',
+            prop: 'time',
+            props: {
+                format: 'YYYY-MM-DD HH:mm:ss',
+                valueFormat: 'YYYY-MM-DD HH:mm:ss',
+                onChange: (value) => {
+                    searchRef.value?.setData({
+                        createTimeStart: value ? value[0] : '',
+                        createTimeEnd: value ? value[1] : ''
+                    });
+                }
+            },
+            colProps: { span: 6 }
+        },
+        {
+            type: 'dictSelect',
+            label: '投诉原因',
+            prop: 'reason',
+            props: { code: 'optimization_content' },
+            colProps: { span: 3 }
+        },
+        {
+            type: 'dictSelect',
+            label: '投诉状态',
+            prop: 'status',
+            props: { code: 'complain_status' },
+            colProps: { span: 3 }
+        }
+    ]);
+
+    const initKeys = reactive({
+        id: '',
+        orderId: '',
+        reason: '',
+        time: [],
+        createTimeStart: '',
+        createTimeEnd: '',
+        status: ''
+    });
+
+    const searchRef = ref(null);
+    /** 搜索 */
+    const search = (data) => {
+        emit('search', { ...data });
+    };
+</script>

+ 125 - 0
src/views/mallOrder/complaint/index.vue

@@ -0,0 +1,125 @@
+<template>
+    <ele-page flex-table>
+        <page-search @search="reload" />
+        <common-table
+            ref="pageRef"
+            :pageConfig="pageConfig"
+            :columns="columns"
+            :tools="false"
+        >
+            <template #baseinfo="{ row }">
+                <div class="flex flex-col items-start">
+                    <el-tag size="large" class="w-full">
+                        <el-text class="flex-1">投诉编号:{{ row.id }}</el-text>
+                        <el-text class="flex-1"
+                            >订单编号:{{ row.orderId }}</el-text
+                        >
+                    </el-tag>
+                    <div class="demo-image__preview mt-2">
+                        <el-image
+                            v-for="item in row.imgList"
+                            :key="item"
+                            style="
+                                width: 70px;
+                                height: 70px;
+                                border-radius: 5px;
+                                margin-right: 8px;
+                            "
+                            :src="item"
+                            fit="cover"
+                            :preview-src-list="row.imgList"
+                            :initial-index="row.imgList.indexOf(item)"
+                            preview-teleported
+                        />
+                    </div>
+                </div>
+            </template>
+            <template #action="{ row }">
+                <div>
+                    <el-button
+                        type="primary"
+                        link
+                        v-permission="'optimization:complain:detail'"
+                        @click="handleDetail(row)"
+                    >
+                        详情
+                    </el-button>
+                </div>
+            </template>
+        </common-table>
+        <page-edit ref="pageEditRef" @refresh="reload" />
+    </ele-page>
+</template>
+
+<script setup>
+    import { ref, reactive } from 'vue';
+    import CommonTable from '@/components/CommonPage/CommonTable.vue';
+    import pageSearch from './components/page-search.vue';
+    import pageEdit from './components/page-edit.vue';
+    import { useDictData } from '@/utils/use-dict-data';
+
+    defineOptions({ name: 'MallOrderComplaintList' });
+    const [statusDicts] = useDictData(['complain_status']);
+
+    /** 表格列配置 */
+    const columns = ref([
+        {
+            label: '基础信息',
+            prop: 'baseinfo',
+            align: 'center',
+            minWidth: 240,
+            slot: 'baseinfo'
+        },
+        {
+            label: '投诉人',
+            prop: 'userId',
+            align: 'center'
+        },
+        {
+            label: '投诉时间',
+            prop: 'createTime',
+            align: 'center',
+            width: 180
+        },
+        {
+            label: '投诉原因',
+            prop: 'reason',
+            align: 'center'
+        },
+        {
+            label: '投诉状态',
+            prop: 'status',
+            align: 'center',
+            formatter: (row) =>
+                statusDicts.value.find((d) => d.dictValue == row.status)
+                    ?.dictLabel
+        },
+        {
+            columnKey: 'action',
+            label: '操作',
+            width: 120,
+            align: 'center',
+            slot: 'action'
+        }
+    ]);
+
+    /** 页面组件实例 */
+    const pageRef = ref(null);
+
+    const pageConfig = reactive({
+        pageUrl: '/order/orderComplaintsLog/pagelist',
+        fileName: '投诉管理',
+        cacheKey: 'mallOrderComplainTable'
+    });
+
+    //刷新表格
+    function reload(where) {
+        pageRef.value?.reload(where);
+    }
+
+    //处理
+    const pageEditRef = ref(null);
+    function handleDetail(row) {
+        pageEditRef.value?.handleOpen(row);
+    }
+</script>

+ 0 - 0
src/views/mallOrder/refund/index.vue