lmx 2 days ago
parent
commit
8b15809c9f

+ 21 - 0
src/api/user/complaintMsg.js

@@ -0,0 +1,21 @@
+import request from '@/utils/request'
+
+// 查询投诉消息回复列表
+export function listComplaintMsg(query) {
+  return request({
+    url: '/user/msg/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 新增用户投诉
+export function addComplaintMsg(data) {
+  return request({
+    url: '/user/msg',
+    method: 'post',
+    data: data
+  })
+}
+
+

+ 36 - 36
src/views/hisStore/storeProduct/index.vue

@@ -351,7 +351,7 @@
       </el-table-column>
       <el-table-column label="同步状态" align="center" prop="pushStatus" >
         <template slot-scope="scope">
-          <el-tag 
+          <el-tag
             :type="scope.row.pushStatus === 1 ? 'success' : scope.row.pushStatus === 2 ? 'danger' : 'warning'"
           >
             {{ scope.row.pushStatus === 0 ? '推送中' : scope.row.pushStatus === 1 ? '成功' : scope.row.pushStatus === 2 ? '失败' : '未同步' }}
@@ -406,16 +406,16 @@
             <el-radio :label="item.dictValue" v-for="item in isDisplayOptions" >{{item.dictLabel}}</el-radio>
           </el-radio-group>
         </el-form-item>
-        <!-- <el-form-item label="所属公司" prop="companyId">
-          <el-select style="width: 220px" filterable multiple v-model="form1.companyId" placeholder="请选择公司名" clearable size="small">
-            <el-option
-              v-for="item in companyOptions"
-              :key="item.companyId"
-              :label="item.companyName"
-              :value="item.companyId"
-            />
-          </el-select>
-        </el-form-item> -->
+<!--        <el-form-item label="所属公司" prop="companyId">-->
+<!--          <el-select style="width: 220px" filterable multiple v-model="form1.companyId" placeholder="请选择公司名" clearable size="small">-->
+<!--            <el-option-->
+<!--              v-for="item in companyOptions"-->
+<!--              :key="item.companyId"-->
+<!--              :label="item.companyName"-->
+<!--              :value="item.companyId"-->
+<!--            />-->
+<!--          </el-select>-->
+<!--        </el-form-item>-->
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitForm1">确 定</el-button>
@@ -866,16 +866,16 @@
             />
           </el-select>
         </el-form-item>
-        <!-- <el-form-item label="所属公司">
-          <el-select style="width: 240px" v-model="form.companyIds" multiple placeholder="请选择企业" clearable size="small" >
-            <el-option
-              v-for="item in companyOptions"
-              :key="item.companyId"
-              :label="item.companyName"
-              :value="item.companyId"
-            />
-          </el-select>
-        </el-form-item> -->
+<!--        <el-form-item label="所属公司">-->
+<!--          <el-select style="width: 240px" v-model="form.companyIds" multiple placeholder="请选择企业" clearable size="small" >-->
+<!--            <el-option-->
+<!--              v-for="item in companyOptions"-->
+<!--              :key="item.companyId"-->
+<!--              :label="item.companyName"-->
+<!--              :value="item.companyId"-->
+<!--            />-->
+<!--          </el-select>-->
+<!--        </el-form-item>-->
         <el-form-item label="所属店铺" prop="storeId" v-if="medicalMallConfig.isStores">
           <el-select style="width: 240px" v-model="form.storeId" placeholder="请选择店铺" clearable size="small" >
             <el-option
@@ -1127,7 +1127,7 @@ export default {
         productType: null,
         isShow: "1",
         barCode:null,
-        companyIds: null,
+        // companyIds: null,
         storeIds: null,
         drugRegCertNo: null,
         commonName: null,
@@ -1184,9 +1184,9 @@ export default {
         prescribeName: [
           { required: true, message: "处方药不能为空", trigger: "blur" }
         ],
-        companyIds: [
-          { required: true, message: "销售公司不能为空", trigger: "blur" }
-        ],
+        // companyIds: [
+        //   { required: true, message: "销售公司不能为空", trigger: "blur" }
+        // ],
         // 药品相关字段校验(仅在是药品时必填)
         drugImage: [
           { required: true, message: "药品展示图不能为空", trigger: "blur" }
@@ -1248,7 +1248,7 @@ export default {
       param.productId = this.ids;
       param.goodsStatus = this.form1.isShow;
       param.goodsIsShow = this.form1.isDisplay;
-      param.companyIds = this.companyId+''
+      // param.companyIds = this.companyId+''
       batchModify(param).then(res=>{
         if(res.code === 200){
           this.$message.success("批量修改成功");
@@ -1511,7 +1511,7 @@ export default {
         prescribeFactory: null,
         prescribeName: null,
         isDisplay:"1",
-        companyIds:[],
+        // companyIds:[],
         isDrug: "1", // 是否药品
         drugImage: null, // 药品展示图
         drugRegCertNo: null, // 药品注册证书编号
@@ -1555,7 +1555,7 @@ export default {
     /** 搜索按钮操作 */
     handleQuery() {
       this.queryParams.pageNum = 1;
-      this.queryParams.companyIds = this.companyId +''
+      // this.queryParams.companyIds = this.companyId +''
       this.getList();
     },
     /** 重置按钮操作 */
@@ -1616,10 +1616,10 @@ export default {
             that.attrs.push(data);
           });
         }
-        // 组装companyIds
-        if (response.data.companyIds != null && response.data.companyIds != undefined && response.data.companyIds.length > 0) {
-          this.form.companyIds = response.data.companyIds.split(',').map(Number);
-        }
+        // // 组装companyIds
+        // if (response.data.companyIds != null && response.data.companyIds != undefined && response.data.companyIds.length > 0) {
+        //   this.form.companyIds = response.data.companyIds.split(',').map(Number);
+        // }
         setTimeout(() => {
           that.generate();
         }, 200);
@@ -1676,10 +1676,10 @@ export default {
           if(this.form.specType === 1 && this.manyFormValidate.length===0){
             return this.$message.warning('请点击生成规格!');
           }
-          // 组装companyIds
-          if (this.form.companyIds != null && this.form.companyIds != undefined) {
-            this.form.companyIds = this.form.companyIds.join(',');
-          }
+          // // 组装companyIds
+          // if (this.form.companyIds != null && this.form.companyIds != undefined) {
+          //   this.form.companyIds = this.form.companyIds.join(',');
+          // }
           addOrEdit(this.form).then(response => {
             if (response.code === 200) {
               this.msgSuccess("修改成功");

+ 29 - 29
src/views/hisStore/storeProductAudit/index.vue

@@ -37,16 +37,16 @@
               />
         </el-select>
       </el-form-item>
-      <el-form-item label="所属公司">
-        <el-select style="width: 240px" v-model="companyId" multiple placeholder="请选择企业" clearable size="small" >
-          <el-option
-            v-for="item in companyOptions"
-            :key="item.companyId"
-            :label="item.companyName"
-            :value="item.companyId"
-          />
-        </el-select>
-      </el-form-item>
+<!--      <el-form-item label="所属公司">-->
+<!--        <el-select style="width: 240px" v-model="companyId" multiple placeholder="请选择企业" clearable size="small" >-->
+<!--          <el-option-->
+<!--            v-for="item in companyOptions"-->
+<!--            :key="item.companyId"-->
+<!--            :label="item.companyName"-->
+<!--            :value="item.companyId"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
       <el-form-item label="所属店铺">
         <el-select style="width: 240px" v-model="storeId" multiple placeholder="请选择企业" clearable size="small" >
           <el-option
@@ -623,16 +623,16 @@
                   />
             </el-select>
         </el-form-item>
-        <el-form-item label="所属公司">
-          <el-select style="width: 240px" v-model="form.companyIds" multiple placeholder="请选择企业" clearable size="small" >
-            <el-option
-                    v-for="item in companyOptions"
-                    :key="item.companyId"
-                    :label="item.companyName"
-                    :value="item.companyId"
-                  />
-            </el-select>
-        </el-form-item>
+<!--        <el-form-item label="所属公司">-->
+<!--          <el-select style="width: 240px" v-model="form.companyIds" multiple placeholder="请选择企业" clearable size="small" >-->
+<!--            <el-option-->
+<!--                    v-for="item in companyOptions"-->
+<!--                    :key="item.companyId"-->
+<!--                    :label="item.companyName"-->
+<!--                    :value="item.companyId"-->
+<!--                  />-->
+<!--            </el-select>-->
+<!--        </el-form-item>-->
         <!-- 所属店铺 -->
         <el-form-item label="所属店铺">
           <el-select style="width: 240px" v-model="form.storeId" placeholder="请选择店铺" clearable size="small" >
@@ -848,7 +848,7 @@ export default {
         productType: null,
         isShow: "1",
         barCode:null,
-        companyIds: null
+        // companyIds: null
       },
       // 表单参数
       form: {},
@@ -895,9 +895,9 @@ export default {
         prescribeName: [
           { required: true, message: "处方药不能为空", trigger: "blur" }
         ],
-        companyIds: [
-          { required: true, message: "销售公司不能为空", trigger: "blur" }
-        ],
+        // companyIds: [
+        //   { required: true, message: "销售公司不能为空", trigger: "blur" }
+        // ],
         // 药品相关字段校验(仅在是药品时必填)
         drugImage: [
           { required: true, message: "药品展示图不能为空", trigger: "blur" }
@@ -1232,7 +1232,7 @@ export default {
         prescribeFactory: null,
         prescribeName: null,
         isDisplay:"1",
-        companyIds:[],
+        // companyIds:[],
         isDrug: "0", // 是否药品
         drugImage: null, // 药品展示图
         drugRegCertNo: null, // 药品注册证书编号
@@ -1276,7 +1276,7 @@ export default {
     /** 搜索按钮操作 */
     handleQuery() {
       this.queryParams.pageNum = 1;
-      this.queryParams.companyIds = this.companyId +''
+      // this.queryParams.companyIds = this.companyId +''
       this.getList();
     },
     /** 重置按钮操作 */
@@ -1330,9 +1330,9 @@ export default {
           });
         }
         // 组装companyIds
-        if (response.data.companyIds != null && response.data.companyIds != undefined && response.data.companyIds.length > 0) {
-          this.form.companyIds = response.data.companyIds.split(',').map(Number);
-        }
+        // if (response.data.companyIds != null && response.data.companyIds != undefined && response.data.companyIds.length > 0) {
+        //   this.form.companyIds = response.data.companyIds.split(',').map(Number);
+        // }
         setTimeout(() => {
           that.generate();
         }, 200);

+ 94 - 1
src/views/user/complaint/index.vue

@@ -106,6 +106,12 @@
       <el-table-column label="备注" align="center" prop="remark" />
       <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-chat-dot-round"
+            @click="reply(scope.row)"
+          >回复</el-button>
           <el-button
             size="mini"
             type="text"
@@ -168,16 +174,75 @@
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+
+    <el-dialog
+      title="投诉消息回复"
+      :visible.sync="replyVisible"
+      width="50%"
+      :before-close="handleClose"
+      center>
+      <el-form label-width="80px">
+        <el-row>
+          <el-col>
+            <el-form-item label="投诉标题">
+              <el-input v-model="complaint.title" disabled></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col>
+            <el-form-item label="投诉内容">
+              <el-input type="textarea" v-model="complaint.content" :rows="2" disabled></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="凭证图片">
+              <el-image
+                style="width: 100px; height: 100px"
+                :src="replyInfo.url"
+                :preview-src-list="replyInfo.urlList">
+              </el-image>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="投诉类型">
+              <span v-for="item in typeList" :key="item.type">
+                 <template v-if="item.type === complaint.type">
+                    {{ item.value }}
+                </template>
+              </span>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <replyIndex v-if="isReplyOpen" ref="replyRef" :complaint-id="complaintId"></replyIndex>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="replyClose">关 闭</el-button>
+      </span>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import { listComplaint, getComplaint, delComplaint, addComplaint, updateComplaint, exportComplaint } from "@/api/user/complaint";
-
+import Index from '@/views/user/complaint/reply/index.vue'
 export default {
   name: "Complaint",
   data() {
     return {
+      isReplyOpen:false,
+      complaint:{
+        title:"",
+        content:"",
+        type:'1',
+      },
+      replyInfo:{
+        url:null,
+        urlList:[],
+      },
+      complaintId:null,
       srcList:[],
       typeList:[
         {
@@ -191,6 +256,7 @@ export default {
           value:'其它'
         },
       ],
+      replyVisible:false,
       // 遮罩层
       loading: true,
       // 导出遮罩层
@@ -230,6 +296,9 @@ export default {
       }
     };
   },
+  components:{
+    replyIndex: Index
+  },
   created() {
     this.getList();
   },
@@ -352,6 +421,30 @@ export default {
     imageClick(url){
       this.srcList=[];
       this.srcList.push(url)
+    },
+    handleClose(done) {
+      this.$confirm('确认关闭?')
+        .then(_ => {
+          this.isReplyOpen=false;
+          done();
+        })
+        .catch(_ => {});
+    },
+    //消息回复页面
+    reply(row){
+      this.isReplyOpen=true;
+      this.complaintId=row.id;
+      this.replyInfo.url = row.images;
+      this.replyInfo.urlList=[];
+      this.replyInfo.urlList.push(row.images);
+      this.complaint.title = row.title;
+      this.complaint.content = row.content;
+      this.complaint.type = row.type;
+      this.replyVisible = true;
+    },
+    replyClose(){
+      this.isReplyOpen=false;
+      this.replyVisible = false;
     }
   }
 };

+ 297 - 0
src/views/user/complaint/reply/index.vue

@@ -0,0 +1,297 @@
+<template>
+  <div class="reply-container">
+    <el-divider content-position="left">沟通记录</el-divider>
+    <div
+      class="message-container"
+      @scroll="handleScroll"
+    >
+      <div v-if="loadingHistory" class="loading-history">加载历史消息中...</div>
+
+      <div v-if="noMoreHistory" class="no-more-history">已加载全部历史消息</div>
+
+      <div
+        v-for="msg in messageList"
+        :key="msg.id"
+        :class="['message-item',
+  msg.sender === 'user' ? 'message-user-left' :
+  msg.sender === 'store' ? 'message-store-left' :
+  'message-system-right'
+]"
+      >
+        <div class="sender">
+          <span style="font-size: 13px;font-weight: bold;">{{ msg.sender === 'user' ? '用户' : msg.sender === 'store' ? '店铺' : '平台' }}</span>
+          <span class="time">{{ msg.time }}</span>
+        </div>
+        <div class="content">{{ msg.content }}</div>
+      </div>
+    </div>
+
+    <el-divider content-position="left">消息回复</el-divider>
+    <div class="reply-area">
+      <el-input
+        v-model="replyContent"
+        :rows="3"
+        class="reply-input"
+        placeholder="请输入回复内容..."
+        type="textarea"
+        @keyup.enter.native="sendReply"
+      ></el-input>
+      <el-button
+        class="send-btn"
+        type="primary"
+        @click="sendReply"
+      >
+        发 送
+      </el-button>
+    </div>
+  </div>
+</template>
+
+
+<script>
+import { listComplaintMsg,addComplaintMsg } from '@/api/user/complaintMsg'
+
+export default ({
+  name: 'Reply',
+  props: {
+    complaintId: {
+      type: [String, Number],
+      required: true
+    }
+  },
+  data() {
+    return {
+      messageList: [],
+      replyContent: '',
+      nextId: 1,
+
+      // 分页参数
+      pageNum: 1,
+      pageSize: 5,
+      total: 0,
+      loadingHistory: false,
+      noMoreHistory: false,
+      initialLoaded: false
+    }
+  },
+  methods: {
+    sendReply() {
+      const content = this.replyContent.trim()
+      if (!content) return
+      //请求消息发送接口
+      addComplaintMsg({
+        complaintId: this.complaintId,
+        content: content,
+      }).then(response => {
+        this.messageList.push({
+          id: this.nextId++,
+          sender: 'system',
+          content,
+          time: this.getFormattedTime()
+        })
+        this.replyContent = ''
+        this.scrollToBottom()
+      })
+    },
+    getFormattedTime() {
+      const now = new Date()
+      const year = now.getFullYear()
+      const month = (now.getMonth() + 1).toString().padStart(2, '0')
+      const day = now.getDate().toString().padStart(2, '0')
+      const hours = now.getHours().toString().padStart(2, '0')
+      const minutes = now.getMinutes().toString().padStart(2, '0')
+      return `${year}-${month}-${day} ${hours}:${minutes}`
+    },
+
+    handleScroll(e) {
+      const scrollTop = e.target.scrollTop
+      if (scrollTop <= 10 && !this.loadingHistory && !this.noMoreHistory) {
+        this.loadHistoryMessages()
+      }
+    },
+
+    loadHistoryMessages() {
+      if (this.loadingHistory || this.noMoreHistory) {
+        return Promise.resolve()
+      }
+
+      this.loadingHistory = true
+      return new Promise((resolve, reject) => {
+        listComplaintMsg({
+          pageNum: this.pageNum,
+          pageSize: this.pageSize,
+          complaintId: this.complaintId
+        }).then(response => {
+          if (!response) {
+            this.$message.error('接口响应异常,请重试')
+            resolve()
+            return
+          }
+
+          if (response.code === 200) {
+            const { rows = [], total = 0 } = response
+            this.total = total
+
+            const formattedList = rows.map(msg => ({
+              id: msg.id || this.nextId++,
+              sender: msg.sendType === 1 ? 'user' : msg.sendType === 2 ? 'system' : 'store',
+              content: msg.content || '(无内容)',
+              time: msg.createTime || this.getFormattedTime()
+            })).reverse()
+
+            if (this.pageNum === 1) {
+              this.messageList = formattedList
+            } else {
+              this.messageList.unshift(...formattedList)
+            }
+
+            if (total === 0 || this.pageNum * this.pageSize >= total) {
+              this.noMoreHistory = true
+            } else {
+              this.pageNum++
+            }
+          } else {
+            this.$message.error(`加载失败:${response.msg || '未知错误'}`)
+          }
+          resolve()
+        }).catch(error => {
+          console.error('请求历史消息失败:', error)
+          this.$message.error('网络异常或接口请求失败,请检查网络后重试')
+          reject(error)
+        }).finally(() => {
+          this.loadingHistory = false
+          this.initialLoaded = true
+        })
+      })
+    },
+    scrollToBottom() {
+      this.$nextTick(() => {
+        const messageContainer = document.querySelector('.message-container')
+        if (messageContainer) {
+          messageContainer.scrollTop = messageContainer.scrollHeight
+        }
+      })
+    }
+  },
+  mounted() {
+    this.loadHistoryMessages().then(() => {
+      this.scrollToBottom()
+    })
+  }
+})
+</script>
+<style scoped>
+.reply-container {
+  padding: 20px;
+}
+
+.message-container {
+  border: 1px solid #e6e6e6;
+  width: 100%;
+  height: 300px;
+  overflow-y: auto;
+  padding: 15px;
+  border-radius: 4px;
+  margin-bottom: 15px;
+  box-sizing: border-box;
+  background: #fff;
+  transform: translateZ(0);
+}
+
+.no-message {
+  text-align: center;
+  color: #999;
+  font-size: 14px;
+  padding: 50px 0;
+}
+
+.loading-history {
+  text-align: center;
+  color: #666;
+  font-size: 12px;
+  padding: 8px 0;
+}
+
+.no-more-history {
+  text-align: center;
+  color: #999;
+  font-size: 12px;
+  padding: 8px 0;
+}
+
+.message-item {
+  margin: 0 0 15px 0;
+  max-width: 80%;
+}
+
+.message-user-left .content {
+  background-color: #e4e4e4;
+  color: #333;
+  border-radius: 6px;
+}
+
+.message-store-left .content {
+  background-color: #448e17;
+  color: white;
+  border-radius: 6px;
+}
+
+.message-system-right .content {
+  background-color: #0f64b5;
+  color: white;
+  border-radius: 6px;
+}
+
+.sender {
+  font-size: 12px;
+  margin-bottom: 5px;
+  color: #888;
+  display: flex;
+  justify-content: space-between;
+}
+
+.time {
+  font-size: 11px;
+}
+
+.content {
+  padding: 8px 12px;
+  border-radius: 6px;
+  line-height: 1.5;
+  word-break: break-all;
+}
+
+.reply-area {
+  border: 1px solid #e6e6e6;
+  padding: 15px;
+  border-radius: 4px;
+  overflow: hidden;
+}
+
+.reply-input {
+  margin-bottom: 10px;
+}
+
+.send-btn {
+  margin-top: 20px;
+  float: right;
+}
+
+.message-container::-webkit-scrollbar {
+  width: 5px;
+  height: 5px;
+}
+
+.message-container::-webkit-scrollbar-track {
+  background: transparent;
+}
+
+.message-container::-webkit-scrollbar-thumb {
+  background: #e0e0e0;
+  border-radius: 3px;
+}
+
+.message-container::-webkit-scrollbar-thumb:hover {
+  background: #ccc;
+}
+</style>