Эх сурвалжийг харах

refactor(mallOrder/complaint): 重构投诉管理模块以移除字典依赖并增强兼容性

ylong 1 долоо хоног өмнө
parent
commit
348f0c8588

+ 34 - 13
src/views/mallOrder/complaint/components/complain-item.vue

@@ -1,28 +1,28 @@
 <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" />
+        <el-avatar shape="square" :size="40" fit="cover" :src="displayAvatar" />
 
         <div class="flex-1 flex flex-col ml-2">
             <div class="flex justify-between">
                 <ele-text size="small" type="info">{{
-                    item.userName
+                    item.userName || item.nickName || '用户'
                 }}</ele-text>
                 <ele-text size="small" type="info">{{
                     item.createTime || ''
                 }}</ele-text>
             </div>
             <div class="mt-2">
-                <ele-text>{{ item.description || '' }}</ele-text>
+                <ele-text>{{ displayContent }}</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="
+            <div v-if="displayImages && displayImages.length" class="mt-2 flex flex-wrap">
+                <el-image v-for="(img, imgIndex) in displayImages" :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"
+                    " :src="img" fit="cover" :preview-src-list="displayImages" :initial-index="imgIndex"
                     preview-teleported />
             </div>
         </div>
@@ -31,16 +31,37 @@
 </template>
 
 <script setup>
+import { computed } from 'vue';
 import Logo from '@/assets/logo.png';
-defineProps({
+
+const props = defineProps({
     item: {
         type: Object,
-        default: () => ({
-            userType: '用户',
-            createTime: '',
-            description: '',
-            fileUrls: []
-        })
+        default: () => ({})
     }
 });
+
+const displayAvatar = computed(() => {
+    // userType: 1-用户 2-商家/管理员
+    if (props.item.userType === '1') {
+        return props.item.imgPath || props.item.avatar || props.item.userAvatar || Logo;
+    }
+    return Logo;
+});
+
+const displayImages = computed(() => {
+    const imgs = props.item.imgList || props.item.fileUrls || props.item.imgs || [];
+    if (typeof imgs === 'string') {
+        try {
+            return JSON.parse(imgs);
+        } catch (e) {
+            return imgs.split(',').filter(i => i);
+        }
+    }
+    return Array.isArray(imgs) ? imgs : [];
+});
+
+const displayContent = computed(() => {
+    return props.item.description || props.item.content || '';
+});
 </script>

+ 15 - 8
src/views/mallOrder/complaint/components/negotiate-dialog.vue

@@ -97,16 +97,23 @@
 
     // 处理文件URL变更
     const handleFileUrlsChange = (value) => {
-        if (value) {
-            try {
-                // ImageUpload组件返回的是JSON字符串,需要解析
-                form.fileUrls = JSON.parse(value);
-            } catch (e) {
-                // 单个文件的情况,直接作为字符串处理
+        if (!value) {
+            form.fileUrls = [];
+            return;
+        }
+        
+        try {
+            // ImageUpload组件返回的是JSON字符串,需要解析
+            const parsed = JSON.parse(value);
+            form.fileUrls = Array.isArray(parsed) ? parsed : [parsed];
+        } catch (e) {
+            // 尝试逗号分割
+            if (typeof value === 'string' && value.includes(',')) {
+                form.fileUrls = value.split(',').filter(item => item);
+            } else {
+                 // 单个文件的情况,直接作为字符串处理
                 form.fileUrls = [value];
             }
-        } else {
-            form.fileUrls = [];
         }
     };
 

+ 39 - 80
src/views/mallOrder/complaint/components/page-edit.vue

@@ -1,33 +1,19 @@
 <!-- 编辑弹窗 -->
 <template>
-    <ele-modal
-        form
-        :width="960"
-        v-model="visible"
-        title="投诉状态及操作"
-        @open="handleOpen"
-    >
+    <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 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-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"
-                />
+                <complain-item v-for="(item, index) in disposeLogList" :key="index" :item="item" />
             </div>
             <div class="flex-1 complain-detail">
                 <div class="common-title">投诉详情</div>
@@ -54,12 +40,7 @@
                     </div>
                     <div class="detail-item">
                         <span class="label">订单编号:</span>
-                        <el-tooltip
-                            content="跳转到订单详情<br/>商品最多展示三个"
-                            placement="top"
-                            effect="light"
-                            raw-content
-                        >
+                        <el-tooltip content="跳转到订单详情<br/>商品最多展示三个" placement="top" effect="light" raw-content>
                             <span class="value link" @click="goToOrder(form.orderId)">{{ form.orderId }}</span>
                         </el-tooltip>
                     </div>
@@ -70,12 +51,14 @@
                 <!-- 商品列表 -->
                 <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" />
+                        <el-image :src="item.image || item.cover" class="product-img" fit="cover" />
                         <div class="product-info">
-                            <div class="product-title">{{ item.title }}</div>
+                            <div class="product-title">{{ item.title || item.bookName }}</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>
+                                <span class="product-spec">{{ item.spec || '' }}</span>
+                                <span class="product-qty"
+                                    :class="{ 'text-red': (item.quantity || item.num || 1) > 1 }">X{{ item.quantity ||
+                                    item.num || 1 }}</span>
                             </div>
                         </div>
                     </div>
@@ -89,11 +72,7 @@
     </ele-modal>
 
     <!-- 协商弹窗 -->
-    <negotiate-dialog
-        v-model:visible="negotiateVisible"
-        :id="currentId"
-        @submit="submitNegotiate"
-    />
+    <negotiate-dialog v-model:visible="negotiateVisible" :id="currentId" @submit="submitNegotiate" />
 </template>
 
 <script setup>
@@ -102,21 +81,19 @@
     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 map = {
+            '1': '待处理',
+            '2': '处理中',
+            '3': '已完结'
+        };
+        return map[status] || '协商中';
     };
 
     /** 弹窗是否打开 */
@@ -149,7 +126,7 @@
     // 获取投诉详情
     const getComplaintDetail = (id) => {
         proxy.$http
-            .get(`/order/orderComplaintsLog/getInfo/${id}`)
+            .get(`/shop/orderComplaintsLog/getInfo/${id}`)
             .then((res) => {
                 if (res.data.code === 200) {
                     const data = res.data.data || {};
@@ -164,28 +141,9 @@
 
                     // 处理协商历史
                     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
-                        }
-                    ];
+
+                    // 商品列表
+                    goodsList.value = data.goodsList || [];
                 } else {
                     EleMessage.error(res.data.msg || '获取投诉详情失败');
                 }
@@ -214,7 +172,7 @@
     const submitNegotiate = (formData) => {
         // 调用协商API
         proxy.$http
-            .post('/order/orderComplaintsLog/dispose', formData)
+            .post('/shop/orderComplaintsLog/dispose', formData)
             .then((res) => {
                 if (res.data.code === 200) {
                     EleMessage.success('协商成功');
@@ -247,7 +205,7 @@
             .then(() => {
                 // 调用完结API
                 proxy.$http
-                    .post(`/order/orderComplaintsLog/over/${currentId.value}`)
+                    .post(`/shop/orderComplaintsLog/over/${currentId.value}`)
                     .then((res) => {
                         if (res.data.code === 200) {
                             EleMessage.success('投诉已完结');
@@ -266,7 +224,7 @@
                 // 用户取消操作
             });
     };
-    
+
     // 跳转到订单详情
     const goToOrder = (orderId) => {
         if (!orderId) return;
@@ -294,43 +252,44 @@
     .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;
@@ -339,13 +298,13 @@
                     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;
@@ -353,22 +312,22 @@
                         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;
                             }

+ 9 - 6
src/views/mallOrder/complaint/components/page-search.vue

@@ -36,18 +36,21 @@
             colProps: { span: 6 }
         },
         {
-            type: 'dictSelect',
+            type: 'input',
             label: '投诉原因',
             prop: 'reason',
-            props: { code: 'optimization_content' },
-            colProps: { span: 3 }
+            colProps: { span: 4 }
         },
         {
-            type: 'dictSelect',
+            type: 'select',
             label: '投诉状态',
             prop: 'status',
-            props: { code: 'complain_status' },
-            colProps: { span: 3 }
+            options: [
+                { label: '待处理', value: '1' },
+                { label: '处理中', value: '2' },
+                { label: '已完结', value: '3' }
+            ],
+            colProps: { span: 4 }
         }
     ]);
 

+ 25 - 10
src/views/mallOrder/complaint/index.vue

@@ -17,8 +17,8 @@
                     </el-tag>
                     <div class="demo-image__preview mt-2">
                         <el-image
-                            v-for="item in row.imgList"
-                            :key="item"
+                            v-for="(item, index) in getImgList(row)"
+                            :key="index"
                             style="
                                 width: 70px;
                                 height: 70px;
@@ -27,8 +27,8 @@
                             "
                             :src="item"
                             fit="cover"
-                            :preview-src-list="row.imgList"
-                            :initial-index="row.imgList.indexOf(item)"
+                            :preview-src-list="getImgList(row)"
+                            :initial-index="index"
                             preview-teleported
                         />
                     </div>
@@ -56,10 +56,27 @@
     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 statusMap = {
+        '1': '待处理',
+        '2': '处理中',
+        '3': '已完结'
+    };
+
+    /** 获取图片列表 */
+    const getImgList = (row) => {
+        const imgs = row.imgList || row.fileUrls || row.imgs || [];
+        if (typeof imgs === 'string') {
+            try {
+                return JSON.parse(imgs);
+            } catch (e) {
+                return imgs.split(',').filter(i => i);
+            }
+        }
+        return Array.isArray(imgs) ? imgs : [];
+    };
 
     /** 表格列配置 */
     const columns = ref([
@@ -90,9 +107,7 @@
             label: '投诉状态',
             prop: 'status',
             align: 'center',
-            formatter: (row) =>
-                statusDicts.value.find((d) => d.dictValue == row.status)
-                    ?.dictLabel
+            formatter: (row) => statusMap[row.status] || row.status
         },
         {
             columnKey: 'action',
@@ -107,7 +122,7 @@
     const pageRef = ref(null);
 
     const pageConfig = reactive({
-        pageUrl: '/order/orderComplaintsLog/pagelist',
+        pageUrl: '/shop/orderComplaintsLog/pagelist',
         fileName: '投诉管理',
         cacheKey: 'mallOrderComplainTable'
     });

+ 4 - 4
src/views/mallOrder/refund/components/refund-item.vue

@@ -6,10 +6,10 @@
             <el-tag type="success" size="small" class="mr-2" effect="plain">
                 {{ getRefundTypeText(item.refundType) }}
             </el-tag>
-            <span class="mr-4 text-gray-600">退款编号: {{ item.refundOrderId }}</span>
-            <span class="mr-4 text-gray-600">原订单编号: {{ item.originOrderId }}</span>
-            <span class="mr-4 text-gray-600">申请时间: {{ item.createTime }}</span>
-            <span class="mr-4 text-gray-600" v-if="item.cancelStatus == 2">
+            <span class="mr-4 text-black-600">退款编号: {{ item.refundOrderId }}</span>
+            <span class="mr-4 text-black-600">原订单编号: {{ item.originOrderId }}</span>
+            <span class="mr-4 text-black-600">申请时间: {{ item.createTime }}</span>
+            <span class="mr-4 text-black-600" v-if="item.cancelStatus == 2">
                 <el-tag type="info" size="small">已撤销</el-tag>
             </span>
         </div>