فهرست منبع

投诉页面优化

yjwang 3 هفته پیش
والد
کامیت
38477ea157
3فایلهای تغییر یافته به همراه218 افزوده شده و 53 حذف شده
  1. 10 1
      src/api/store/complaint.js
  2. 114 28
      src/components/ImageUpload/index.vue
  3. 94 24
      src/views/store/complaint/index.vue

+ 10 - 1
src/api/store/complaint.js

@@ -90,4 +90,13 @@ export function exportMsg(query) {
     method: 'get',
     params: query
   })
-}
+}
+
+// 查询用户投诉列表
+export function completeComplaint(data) {
+  return request({
+    url: 'store/complaint/completeComplaint',
+    method: 'post',
+    data: data
+  })
+}

+ 114 - 28
src/components/ImageUpload/index.vue

@@ -11,21 +11,20 @@
       name="file"
       :on-remove="handleRemove"
       :show-file-list="true"
-      
       :file-list="fileList"
       :on-preview="handlePictureCardPreview"
       :class="{hide: this.fileList.length >= this.limit}"
     >
       <i class="el-icon-plus"></i>
     </el-upload>
-    
+
     <!-- 上传提示 -->
-    <div class="el-upload__tip" slot="tip" v-if="showTip">
-      请上传
-      <template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
-      <template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
-      的文件
-    </div>
+    <!--    <div class="el-upload__tip" slot="tip" v-if="showTip">
+          请上传
+          <template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
+          <template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
+          的文件
+        </div> -->
 
     <el-dialog
       :visible.sync="dialogVisible"
@@ -44,18 +43,20 @@
 <script>
 import { getToken } from "@/utils/auth";
 
+import { Loading } from 'element-ui';
+
 export default {
   props: {
     value: [String, Object, Array],
     // 图片数量限制
     limit: {
       type: Number,
-      default: 5,
+      default: 10,
     },
     // 大小限制(MB)
     fileSize: {
-       type: Number,
-      default: 5,
+      type: Number,
+      default: 500,
     },
     // 文件类型, 例如['png', 'jpg', 'jpeg']
     fileType: {
@@ -70,11 +71,12 @@ export default {
   },
   data() {
     return {
+      finalQuality:1,
       dialogImageUrl: "",
       dialogVisible: false,
       hideUpload: false,
       baseUrl: process.env.VUE_APP_BASE_API,
-      uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/uploadOSS", // 上传的图片服务器地址
+      uploadImgUrl: process.env.VUE_APP_BASE_API+"/common/uploadOSS", // 上传的图片服务器地址
       headers: {
         Authorization: "Bearer " + getToken(),
       },
@@ -91,9 +93,9 @@ export default {
           this.fileList = list.map(item => {
             if (typeof item === "string") {
               if (item.indexOf(this.baseUrl) === -1) {
-                  item = { name: this.baseUrl + item, url: this.baseUrl + item };
+                item = { name: item, url: item };
               } else {
-                  item = { name: item, url: item };
+                item = { name: item, url: item };
               }
             }
             return item;
@@ -124,6 +126,7 @@ export default {
     },
     // 上传成功回调
     handleUploadSuccess(res) {
+      console.log(res)
       this.fileList.push({ name: res.url, url: res.url });
       this.$emit("input", this.listToString(this.fileList));
       this.loading.close();
@@ -151,17 +154,99 @@ export default {
         );
         return false;
       }
-      if (this.fileSize) {
-        const isLt = file.size / 1024 / 1024 < this.fileSize;
-        if (!isLt) {
-          this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
-          return false;
+      return new Promise((resolve, reject) => {
+        if (file.size / 1024 / 1024 > 3) {
+          this.$message.error('上传的图片不能超过3MB');
+          reject();
+          return;
         }
-      }
-      this.loading = this.$loading({
-        lock: true,
-        text: "上传中",
-        background: "rgba(0, 0, 0, 0.7)",
+        if (file.size / 1024 / 1024 > 1) {
+          const loadingInstance = Loading.service({ text: '图片内存过大正在压缩图片...' });
+          // 文件大于1MB时进行压缩
+          this.compressImage(file).then((compressedFile) => {
+            loadingInstance.close();
+            if (compressedFile.size / 1024 > 1000) {
+              this.$message.error('图片压缩后仍大于1000KB');
+              reject();
+            } else {
+              // this.$message.success(`图片压缩成功,最终质量为: ${this.finalQuality.toFixed(2)}`);
+              console.log(`图片压缩成功,最终质量为: ${this.finalQuality.toFixed(2)}`);
+              console.log(`最终内存大小为: ${(compressedFile.size/1024).toFixed(2)}KB`);
+              resolve(compressedFile);
+            }
+          }).catch((err) => {
+            loadingInstance.close();
+            console.error(err);
+            reject();
+          });
+        } else {
+          resolve(file);
+        }
+        this.loading = this.$loading({
+          lock: true,
+          text: "上传中",
+          background: "rgba(0, 0, 0, 0.7)",
+        });
+      });
+      // if (this.fileSize) {
+      //   const isLt = file.size / 1024  < this.fileSize;
+      //   if (!isLt) {
+      //     this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} KB!`);
+      //     return false;
+      //   }
+      // }
+
+    },
+    compressImage(file) {
+      return new Promise((resolve, reject) => {
+        const reader = new FileReader();
+        reader.readAsDataURL(file);
+        reader.onload = (event) => {
+          const img = new Image();
+          img.src = event.target.result;
+          img.onload = () => {
+            const canvas = document.createElement('canvas');
+            const ctx = canvas.getContext('2d');
+            const width = img.width;
+            const height = img.height;
+            canvas.width = width;
+            canvas.height = height;
+            ctx.drawImage(img, 0, 0, width, height);
+
+            let quality = 1; // 初始压缩质量
+            let dataURL = canvas.toDataURL('image/jpeg', quality);
+
+            // 逐步压缩,直到图片大小小于500KB并且压缩质量不再降低
+            while (dataURL.length / 1024 > 500 && quality > 0.1) {
+              quality -= 0.01;
+              dataURL = canvas.toDataURL('image/jpeg', quality);
+            }
+            this.finalQuality = quality; // 存储最终的压缩质量
+
+            if (dataURL.length / 1024 > 1000) {
+              reject(new Error('压缩后图片仍然大于1000KB'));
+              return;
+            }
+
+            const arr = dataURL.split(',');
+            const mime = arr[0].match(/:(.*?);/)[1];
+            const bstr = atob(arr[1]);
+            let n = bstr.length;
+            const u8arr = new Uint8Array(n);
+            while (n--) {
+              u8arr[n] = bstr.charCodeAt(n);
+            }
+            const compressedFile = new Blob([u8arr], { type: mime });
+            compressedFile.name = file.name;
+            resolve(compressedFile);
+          };
+          img.onerror = (error) => {
+            reject(error);
+          };
+        };
+        reader.onerror = (error) => {
+          reject(error);
+        };
       });
     },
     // 文件个数超出
@@ -178,6 +263,7 @@ export default {
     },
     // 预览
     handlePictureCardPreview(file) {
+      console.log(file)
       this.dialogImageUrl = file.url;
       this.dialogVisible = true;
     },
@@ -196,17 +282,17 @@ export default {
 <style scoped lang="scss">
 // .el-upload--picture-card 控制加号部分
 ::v-deep.hide .el-upload--picture-card {
-    display: none;
+  display: none;
 }
 // 去掉动画效果
 ::v-deep .el-list-enter-active,
 ::v-deep .el-list-leave-active {
-    transition: all 0s;
+  transition: all 0s;
 }
 
 ::v-deep .el-list-enter, .el-list-leave-active {
-    opacity: 0;
-    transform: translateY(0);
+  opacity: 0;
+  transform: translateY(0);
 }
 </style>
 

+ 94 - 24
src/views/store/complaint/index.vue

@@ -72,24 +72,46 @@
         </template>
       </el-table-column>
       <el-table-column label="投诉时间" align="center" prop="createTime" />
-      <el-table-column 
-        label="店铺是否处理"
+      <el-table-column
+        label="店铺消息是否处理"
         align="center"
         prop="isHandlePlatform"
         :render-header="renderHandleHeader"
       >
         <template slot-scope="scope">
           <el-tag
-            :type="(scope.row.isHandleStore == 1)? 'success' : 'warning'"
+            :type="(scope.row.isHandleStore === 1)? 'success' : 'warning'"
             disable-transitions
           >
-            {{ formatHandleStatus(scope.row.isHandle) }}
+            {{ formatHandleStatus(scope.row.isHandleStore) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="处理状态"
+        align="center"
+        prop="isHandlePlatform"
+        :render-header="renderHandleHeader"
+      >
+        <template slot-scope="scope">
+          <el-tag
+            :type="(scope.row.isProcessCompleted === 1)? 'success' : 'danger'"
+            disable-transitions
+          >
+            {{ formatHandleProcessCompleted(scope.row.isProcessCompleted) }}
           </el-tag>
         </template>
       </el-table-column>
       <el-table-column label="备注" align="center" prop="remarks" />
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-check"
+            @click="handlecompleteComplaint(scope.row)"
+            v-if="scope.row.isProcessCompleted !== 1"
+          >完成投诉</el-button>
           <el-button
             size="mini"
             type="text"
@@ -113,7 +135,7 @@
       <!-- 修改tab-click事件处理器名称 -->
       <el-tabs v-model="activeTab" type="card" @tab-click="handleClick">
         <!-- 投诉详情标签页 -->
-        <el-tab-pane label="投诉详情" name="complaint">
+        <el-tab-pane :label="complaintType === 1?'咨询详情':'投诉详情'" name="complaint">
           <el-form ref="form" :model="form" :rules="rules" label-width="80px">
             <el-form-item label="投诉内容" prop="name">
               <el-input v-model="form.name" :disabled="true" />
@@ -158,7 +180,7 @@
                 <span>添加回复</span>
               </div>
               <el-form :model="replyForm" ref="replyForm" label-width="80px">
-                <el-form-item label="回复内容" prop="content" :rules="[{ required: true, message: '请输入回复内容', trigger: 'blur' }]">
+                <el-form-item label="回复内容" prop="content" :rules="[{ required: true, message: '请输入回复内容', trigger: 'blur' }]" >
                   <el-input
                     type="textarea"
                     v-model="replyForm.content"
@@ -166,9 +188,10 @@
                     :rows="3"
                     maxlength="500"
                     show-word-limit
+                    :disabled="isProcessCompleted === 1"
                   />
                 </el-form-item>
-                
+
                 <!-- 添加图片上传功能 -->
                 <el-form-item label="上传图片" prop="images">
                   <!-- <div class="image-upload-container">
@@ -189,12 +212,26 @@
                       </div>
                     </el-upload>
                   </div> -->
-                  <ImageUpload v-model="replyForm.images" type="image" :num="4" :width="150" :height="150"/>  
-
+                  <ImageUpload v-model="replyForm.images" type="image" :num="4" :width="150" :height="150" v-if="isProcessCompleted !== 1"/>
+                  <el-upload
+                    ref="imageUpload"
+                    :action="uploadUrl"
+                    :show-file-list="false"
+                    :file-list="replyImageList"
+                    :on-success="handleSuccess"
+                    :before-upload="beforeUpload"
+                    :limit="4"
+                    list-type="picture-card"
+                    accept="image/*"
+                    :disabled="isProcessCompleted === 1"
+                    v-else
+                  >
+                    <i class="el-icon-plus"></i>
+                  </el-upload>
                 </el-form-item>
 
                 <el-form-item>
-                  <el-button type="primary" size="small" @click="submitReply" :loading="replyLoading">
+                  <el-button type="primary" size="small" @click="submitReply" :loading="replyLoading" :disabled="isProcessCompleted === 1">
                     <i class="el-icon-s-promotion"></i> 发送回复
                   </el-button>
                 </el-form-item>
@@ -206,12 +243,12 @@
               <div slot="header">
                 <span>回复记录 ({{ replyTotal }}条)</span>
               </div>
-              
+
               <div v-loading="replyLoading" class="reply-list">
                 <div v-if="replyList.length === 0" class="empty-replies">
                   <el-empty description="暂无回复记录" :image-size="100"></el-empty>
                 </div>
-                
+
                 <div v-else>
                   <div v-for="reply in replyList" :key="reply.id" class="reply-item">
                     <div class="reply-header">
@@ -231,13 +268,13 @@
                     <div class="reply-content">
                       {{ reply.content }}
                     </div>
-                    
+
                     <!-- 显示回复中的图片 -->
                     <div class="reply-images" v-if="reply.images && reply.images !== ''">
                       <div v-for="(url, index) in imageUrls(reply.images)" :key="index" class="reply-image-container">
-                        <el-image 
-                          :src="url" 
-                          :preview-src-list="imageUrls(reply.images)" 
+                        <el-image
+                          :src="url"
+                          :preview-src-list="imageUrls(reply.images)"
                           style="width: 80px; height: 80px;"
                           fit="cover"
                         ></el-image>
@@ -304,7 +341,17 @@
 </template>
 
 <script>
-import { listComplaint, getComplaint, delComplaint, addComplaint, updateComplaint, exportComplaint,listMsg,addMsg } from "@/api/store/complaint";
+import {
+  listComplaint,
+  getComplaint,
+  delComplaint,
+  addComplaint,
+  updateComplaint,
+  exportComplaint,
+  listMsg,
+  addMsg,
+  completeComplaint
+} from '@/api/store/complaint'
 import ImageUpload from '@/components/ImageUpload/index';
 export default {
   name: "Complaint",
@@ -313,6 +360,8 @@ export default {
   },
   data() {
     return {
+      complaintType:0,
+      isProcessCompleted:0,
       replyImageList: [],
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       complaintId:null,
@@ -456,7 +505,10 @@ export default {
       return String(urls).split(",");
     },
     formatHandleStatus(status) {
-      return status == 1 ? '已处理' : '未处理';
+      return status === 1 ? '已回复' : '未回复';
+    },
+    formatHandleProcessCompleted(processCompleted) {
+      return processCompleted === 1 ? '已完成' : '处理中';
     },
     /** 查询用户投诉列表 */
     getList() {
@@ -534,6 +586,8 @@ export default {
     },
     /** 修改按钮操作 */
     handleUpdate(row) {
+      this.complaintType = row.complaintType;
+      this.isProcessCompleted = row.isProcessCompleted;
       this.reset();
       this.resetReplyData();
       // const id = row.id || this.ids
@@ -643,7 +697,7 @@ export default {
               this.replyImageList = []; // 重置图片列表
               this.getReplyList()
             });
-            
+
           }, 500);
         }
       });
@@ -655,7 +709,7 @@ export default {
         content: reply.content,
         images: reply.images || '' // 包含图片数据
       };
-      this.editReplyImageList = reply.images ? 
+      this.editReplyImageList = reply.images ?
         reply.images.split(',').map((url, index) => ({
           uid: index,
           name: `image-${index}`,
@@ -663,7 +717,7 @@ export default {
         })) : [];
       this.editReplyVisible = true;
     },
-    
+
     // 更新回复
     updateReply() {
       this.$refs.editReplyForm.validate(valid => {
@@ -671,7 +725,7 @@ export default {
           this.replyLoading = true;
           // const imageUrls = this.editReplyImageList.map(file => file.url || file.response?.url).filter(url => url);
           // this.editReplyForm.images = imageUrls.join(',');
-          
+
           // 模拟API调用
           setTimeout(() => {
             const index = this.replyList.findIndex(item => item.id === this.editReplyForm.id);
@@ -720,7 +774,7 @@ export default {
           this.msgError(res.msg);
         }
     },
-    
+
     beforeUpload(file) {
       const isLt1M = file.size / 1024 / 1024 < 1;
       if (!isLt1M) {
@@ -728,6 +782,22 @@ export default {
       }
       return   isLt1M;
     },
+    handlecompleteComplaint(row) {
+      this.$confirm('是否完成当前投诉处理, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        completeComplaint({id:row.id}).then(response => {
+          this.getList();
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        });
+      });
+    }
   }
 };
 </script>
@@ -811,4 +881,4 @@ export default {
   margin-right: 10px;
   margin-bottom: 10px;
 }
-</style>
+</style>