From cfe970bd45eef8c14f247be4412706c723793bf7 Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Wed, 2 Jun 2021 14:06:21 +0800
Subject: [PATCH 01/14] =?UTF-8?q?=E5=BC=80=E5=8F=91ws=E9=93=BE=E6=8E=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../custom/WebSocketController.java           |  54 ++++++++
 .../stdiet/web/server/WebSocketServer.java    | 122 ++++++++++++++++++
 .../java/com/stdiet/custom/utils/WsUtils.java |   8 ++
 stdiet-framework/pom.xml                      |  17 +++
 .../framework/config/SecurityConfig.java      |   1 +
 .../framework/config/WebsocketConfig.java     |  18 +++
 stdiet-ui/src/main.js                         |  15 ++-
 stdiet-ui/src/utils/websocket.js              |  57 ++++++++
 8 files changed, 286 insertions(+), 6 deletions(-)
 create mode 100644 stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java
 create mode 100644 stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java
 create mode 100644 stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java
 create mode 100644 stdiet-framework/src/main/java/com/stdiet/framework/config/WebsocketConfig.java
 create mode 100644 stdiet-ui/src/utils/websocket.js

diff --git a/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java
new file mode 100644
index 000000000..128b8d067
--- /dev/null
+++ b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java
@@ -0,0 +1,54 @@
+package com.stdiet.web.controller.custom;
+
+import com.alibaba.fastjson.JSONObject;
+import com.stdiet.common.core.controller.BaseController;
+import com.stdiet.custom.utils.WsUtils;
+import com.stdiet.web.server.WebSocketServer;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Controller
+@EnableScheduling
+@RequestMapping("/ws/api")
+public class WebSocketController extends BaseController {
+
+    @ResponseBody
+    @RequestMapping("/push/{cid}")
+    public Map pushToWeb(@PathVariable String cid, String message) {
+        if (message == null) {
+            message = "我是消息44";
+        }
+        Map result = new HashMap();
+        try {
+            WebSocketServer.sendInfo(message, cid);
+            result.put("code", 200);
+            result.put("msg", "success");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return result;
+
+    }
+
+    @Scheduled(fixedRate = 30000)
+    public void boardCast() {
+        try {
+            JSONObject heartBeat = new JSONObject();
+            heartBeat.put("type", WsUtils.WS_TYPE_HEART_BEAT);
+            heartBeat.put("msg", "ping");
+
+            WebSocketServer.sendInfo(heartBeat.toJSONString(), null);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java b/stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java
new file mode 100644
index 000000000..8dfbb8deb
--- /dev/null
+++ b/stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java
@@ -0,0 +1,122 @@
+package com.stdiet.web.server;
+
+import com.alibaba.fastjson.JSONObject;
+import com.stdiet.common.core.domain.model.LoginUser;
+import com.stdiet.custom.utils.WsUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.stereotype.Component;
+
+import javax.websocket.*;
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+@ServerEndpoint(value = "/ws")
+@Component
+@Slf4j
+public class WebSocketServer {
+
+    // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
+    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
+    //private static ConcurrentHashMap<String,WebSocketServer> websocketList = new ConcurrentHashMap<>();
+    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
+    private Session session;
+    // 接收sid
+    private String sid = "";
+
+    public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
+        return webSocketSet;
+    }
+
+    /**
+     * 群发自定义消息
+     */
+    public static void sendInfo(String message, String sid) throws IOException {
+        log.info("推送消息到窗口" + sid + ",推送内容:" + message + "目标:" + webSocketSet.size());
+        for (WebSocketServer item : webSocketSet) {
+            try {
+                // 这里可以设定只推送给这个sid的,为null则全部推送
+                if (sid == null) {
+                    item.sendMessage(message);
+                } else if (item.sid.equals(sid)) {
+                    item.sendMessage(message);
+                }
+            } catch (IOException e) {
+                // 清理断开的连接
+                webSocketSet.remove(item);
+                continue;
+            }
+        }
+    }
+
+    // * 连接建立成功调用的方法*
+    @OnOpen
+    public void onOpen(Session session) {
+        this.session = session;
+        this.sid = String.valueOf(getUserId(session));
+
+        webSocketSet.add(this); // 加入set中
+
+        try {
+            JSONObject object = new JSONObject();
+            object.put("type", WsUtils.WS_TYPE_SYSTEM_MESSAGE);
+            object.put("msg", "连接成功");
+            sendMessage(object.toJSONString());
+        } catch (IOException e) {
+            log.error("websocket IO异常");
+        }
+    }
+
+    @OnClose
+    public void onClose() {
+        webSocketSet.remove(this); // 从set中删除
+        log.info("有一连接关闭!");
+    }
+
+    // */
+    /// **
+    // * 收到客户端消息后调用的方法
+    // *
+    // * @param message 客户端发送过来的消息*//*
+    @OnMessage
+    public void onMessage(String message, Session session) {
+        log.info("收到来自窗口" + sid + "的信息:" + message);
+//        // 群发消息
+//        for (WebSocketServer item : webSocketSet) {
+//            try {
+//                item.sendMessage(message);
+//            } catch (IOException e) {
+//                e.printStackTrace();
+//            }
+//        }
+    }
+
+    /**
+     * @param session
+     * @param error
+     */
+    @OnError
+    public void onError(Session session, Throwable error) {
+        log.error("发生错误");
+        error.printStackTrace();
+    }
+
+    /**
+     * 实现服务器主动推送
+     */
+    public void sendMessage(String message) throws IOException {
+        log.info("服务器消息推送:" + message);
+        this.session.getBasicRemote().sendText(message);
+    }
+
+    public Long getUserId(Session session) {
+        try {
+            return ((LoginUser) ((UsernamePasswordAuthenticationToken) session.getUserPrincipal()).getPrincipal()).getUser().getUserId();
+        } catch (Exception e) {
+            return 0L;
+        }
+    }
+
+
+}
\ No newline at end of file
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java b/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java
new file mode 100644
index 000000000..6d8e8f43c
--- /dev/null
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java
@@ -0,0 +1,8 @@
+package com.stdiet.custom.utils;
+
+public class WsUtils {
+
+    public static final String WS_TYPE_HEART_BEAT = "WS_TYPE_HEART_BEAT";
+
+    public static final String WS_TYPE_SYSTEM_MESSAGE = "WS_TYPE_SYSTEM_MESSAGE";
+}
diff --git a/stdiet-framework/pom.xml b/stdiet-framework/pom.xml
index 6db2e7872..c94845c5d 100644
--- a/stdiet-framework/pom.xml
+++ b/stdiet-framework/pom.xml
@@ -59,6 +59,23 @@
             <artifactId>stdiet-system</artifactId>
         </dependency>
 
+<!--        websocket-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/stdiet-framework/src/main/java/com/stdiet/framework/config/SecurityConfig.java b/stdiet-framework/src/main/java/com/stdiet/framework/config/SecurityConfig.java
index af34f5aa1..07f5dc900 100644
--- a/stdiet-framework/src/main/java/com/stdiet/framework/config/SecurityConfig.java
+++ b/stdiet-framework/src/main/java/com/stdiet/framework/config/SecurityConfig.java
@@ -102,6 +102,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
                         "/custom/wxUserInfo/wx/**",
                         "/custom/wxUserLog/wx/**",
                         "/wx/**",
+//                        "/ws/**",
                         "/wap/**",
                         "/investigate/**",
                         "/common/customerUploadFile",
diff --git a/stdiet-framework/src/main/java/com/stdiet/framework/config/WebsocketConfig.java b/stdiet-framework/src/main/java/com/stdiet/framework/config/WebsocketConfig.java
new file mode 100644
index 000000000..795c5a971
--- /dev/null
+++ b/stdiet-framework/src/main/java/com/stdiet/framework/config/WebsocketConfig.java
@@ -0,0 +1,18 @@
+package com.stdiet.framework.config;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+@Configuration
+@ConditionalOnWebApplication
+public class WebsocketConfig {
+
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }
+
+
+}
diff --git a/stdiet-ui/src/main.js b/stdiet-ui/src/main.js
index 28ac40c5a..79469f5f8 100644
--- a/stdiet-ui/src/main.js
+++ b/stdiet-ui/src/main.js
@@ -18,12 +18,15 @@ import "./permission"; // permission control
 import { getDicts } from "@/api/system/dict/data";
 import { getConfigKey } from "@/api/system/config";
 import VueScrollTo from "vue-scrollto";
-import VueResource from "vue-resource"
-import HighchartsVue from 'highcharts-vue'
-import Highcharts from 'highcharts'
+import VueResource from "vue-resource";
+import HighchartsVue from "highcharts-vue";
+import Highcharts from "highcharts";
+import { init } from "@/utils/websocket";
 //图片导出模块
-import exportingInit from 'highcharts/modules/exporting'
-exportingInit(Highcharts)
+import exportingInit from "highcharts/modules/exporting";
+exportingInit(Highcharts);
+
+init();
 
 import {
   addDateRange,
@@ -111,7 +114,7 @@ Vue.use(VueScrollTo, {
   y: true
 });
 
-Vue.use(VueResource)
+Vue.use(VueResource);
 
 new Vue({
   el: "#app",
diff --git a/stdiet-ui/src/utils/websocket.js b/stdiet-ui/src/utils/websocket.js
new file mode 100644
index 000000000..1098bc150
--- /dev/null
+++ b/stdiet-ui/src/utils/websocket.js
@@ -0,0 +1,57 @@
+import { getToken } from "./auth";
+
+const { protocol, hostname, origin, port } = window.location;
+const wsProtocol = protocol.startsWith("https") ? "wss" : "ws";
+const url = `${wsProtocol}://${hostname}${
+  port === "80" || port === "443" ? "" : `:${8091}`
+}/ws`;
+let ws = undefined;
+let intervalRef = undefined;
+
+function handleOnMessageReceive(event) {
+  console.log({ event });
+}
+
+function connect() {
+  try {
+    ws = new WebSocket(url);
+
+    ws.onopen = event => {
+      console.log("ws连接成功");
+      intervalRef && clearInterval(intervalRef);
+
+      document.addEventListener("message", handleOnMessageReceive);
+    };
+
+    ws.onmessage = event => {
+      console.log({ event });
+
+      const dataObj = JSON.parse(event.data || "{}");
+
+      if (dataObj.type && dataObj.type !== "WS_TYPE_HEART_BEAT") {
+        window.postMessage(dataObj, origin);
+      }
+    };
+
+    ws.onerror = event => {
+      console.log({ event });
+      ws = undefined;
+      document.removeEventListener("message", handleOnMessageReceive);
+    };
+
+    ws.onclose = event => {
+      ws = undefined;
+      document.removeEventListener("message", handleOnMessageReceive);
+    };
+  } catch (error) {
+    console.log(error);
+    console.log("浏览器不支持websocket");
+  }
+}
+
+export function init() {
+  intervalRef = setInterval(() => {
+    console.log("尝试连接websocket");
+    !ws && connect();
+  }, 10000);
+}

From a4252af6ed90b91a8531c49cf22be3830a0c5ed9 Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Wed, 2 Jun 2021 14:11:15 +0800
Subject: [PATCH 02/14] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dws?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 stdiet-ui/src/utils/websocket.js | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/stdiet-ui/src/utils/websocket.js b/stdiet-ui/src/utils/websocket.js
index 1098bc150..93ef5146a 100644
--- a/stdiet-ui/src/utils/websocket.js
+++ b/stdiet-ui/src/utils/websocket.js
@@ -1,9 +1,7 @@
-import { getToken } from "./auth";
-
 const { protocol, hostname, origin, port } = window.location;
 const wsProtocol = protocol.startsWith("https") ? "wss" : "ws";
 const url = `${wsProtocol}://${hostname}${
-  port === "80" || port === "443" ? "" : `:${8091}`
+  hostname === "localhost" ? `:${8091}` : ""
 }/ws`;
 let ws = undefined;
 let intervalRef = undefined;

From 953d188f5004945b3ceb5bc282501cdb167f824d Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Wed, 2 Jun 2021 14:13:03 +0800
Subject: [PATCH 03/14] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=AB=AF=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 stdiet-ui/src/utils/websocket.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/stdiet-ui/src/utils/websocket.js b/stdiet-ui/src/utils/websocket.js
index 93ef5146a..94a85698b 100644
--- a/stdiet-ui/src/utils/websocket.js
+++ b/stdiet-ui/src/utils/websocket.js
@@ -1,7 +1,7 @@
 const { protocol, hostname, origin, port } = window.location;
 const wsProtocol = protocol.startsWith("https") ? "wss" : "ws";
 const url = `${wsProtocol}://${hostname}${
-  hostname === "localhost" ? `:${8091}` : ""
+  hostname === "localhost" ? ":8091" : ""
 }/ws`;
 let ws = undefined;
 let intervalRef = undefined;

From ea8276e6a0d72c8ad21cc161c11bce659992372e Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Thu, 3 Jun 2021 10:36:21 +0800
Subject: [PATCH 04/14] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BA=A4=E4=BA=92?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../custom/WebSocketController.java           |  27 ++--
 stdiet-custom/pom.xml                         |   5 +
 .../custom/domain/SysServicesTopic.java       |   2 +
 .../custom/mapper/SysServicesTopicMapper.java |   2 +
 .../custom}/server/WebSocketServer.java       |  59 ++++++---
 .../service/ISysServicesTopicService.java     |   2 +
 .../impl/SysServicesTopicServiceImp.java      |  79 +++++++++++-
 .../java/com/stdiet/custom/utils/WsUtils.java |   8 ++
 .../mapper/custom/SysServicesTopicMapper.xml  |  18 +++
 stdiet-ui/src/layout/components/Navbar.vue    |  49 ++++---
 stdiet-ui/src/main.js                         |   1 +
 stdiet-ui/src/store/actions.js                |   3 +
 stdiet-ui/src/store/getters.js                |   3 +-
 stdiet-ui/src/store/modules/global.js         |   3 +-
 stdiet-ui/src/store/modules/message.js        |  37 ++++--
 stdiet-ui/src/utils/websocket.js              |  41 ++++--
 .../custom/message/messageBrowser/index.vue   | 122 ++++++++++++++----
 17 files changed, 348 insertions(+), 113 deletions(-)
 rename {stdiet-admin/src/main/java/com/stdiet/web => stdiet-custom/src/main/java/com/stdiet/custom}/server/WebSocketServer.java (70%)

diff --git a/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java
index 128b8d067..4ae0335ec 100644
--- a/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java
+++ b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java
@@ -3,11 +3,10 @@ package com.stdiet.web.controller.custom;
 import com.alibaba.fastjson.JSONObject;
 import com.stdiet.common.core.controller.BaseController;
 import com.stdiet.custom.utils.WsUtils;
-import com.stdiet.web.server.WebSocketServer;
+import com.stdiet.custom.server.WebSocketServer;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
@@ -39,16 +38,16 @@ public class WebSocketController extends BaseController {
 
     }
 
-    @Scheduled(fixedRate = 30000)
-    public void boardCast() {
-        try {
-            JSONObject heartBeat = new JSONObject();
-            heartBeat.put("type", WsUtils.WS_TYPE_HEART_BEAT);
-            heartBeat.put("msg", "ping");
-
-            WebSocketServer.sendInfo(heartBeat.toJSONString(), null);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
+//    @Scheduled(fixedRate = 30000)
+//    public void boardCast() {
+//        try {
+//            JSONObject heartBeat = new JSONObject();
+//            heartBeat.put("type", WsUtils.WS_TYPE_HEART_BEAT);
+//            heartBeat.put("msg", "ping");
+//
+//            WebSocketServer.sendInfo(heartBeat.toJSONString(), null);
+//        } catch (IOException e) {
+//            e.printStackTrace();
+//        }
+//    }
 }
diff --git a/stdiet-custom/pom.xml b/stdiet-custom/pom.xml
index 5db28a2cc..a7e81b668 100644
--- a/stdiet-custom/pom.xml
+++ b/stdiet-custom/pom.xml
@@ -99,6 +99,11 @@
             <artifactId>ikanalyzer</artifactId>
             <version>2012_u6</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.tomcat.embed</groupId>
+            <artifactId>tomcat-embed-websocket</artifactId>
+        </dependency>
+
 
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/domain/SysServicesTopic.java b/stdiet-custom/src/main/java/com/stdiet/custom/domain/SysServicesTopic.java
index f465d7522..3ce77b0bd 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/domain/SysServicesTopic.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/domain/SysServicesTopic.java
@@ -89,4 +89,6 @@ public class SysServicesTopic {
 
     List<SysServicesTopic> replys;
 
+    Integer count;
+
 }
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/mapper/SysServicesTopicMapper.java b/stdiet-custom/src/main/java/com/stdiet/custom/mapper/SysServicesTopicMapper.java
index 333417f81..0217a94e8 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/mapper/SysServicesTopicMapper.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/mapper/SysServicesTopicMapper.java
@@ -18,4 +18,6 @@ public interface SysServicesTopicMapper {
     int inserSysServicesTopicReply(SysServicesTopic topic);
 
     List<SysServicesTopic> selectSysServicesTopicSessionByTopicId(String topicId);
+
+    List<SysServicesTopic> selectUnreadTopicCount(List<SysServicesTopic> topics);
 }
diff --git a/stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java b/stdiet-custom/src/main/java/com/stdiet/custom/server/WebSocketServer.java
similarity index 70%
rename from stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java
rename to stdiet-custom/src/main/java/com/stdiet/custom/server/WebSocketServer.java
index 8dfbb8deb..7bc506c04 100644
--- a/stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/server/WebSocketServer.java
@@ -1,7 +1,10 @@
-package com.stdiet.web.server;
+package com.stdiet.custom.server;
 
 import com.alibaba.fastjson.JSONObject;
 import com.stdiet.common.core.domain.model.LoginUser;
+import com.stdiet.common.utils.spring.SpringUtils;
+import com.stdiet.custom.domain.SysServicesTopic;
+import com.stdiet.custom.service.ISysServicesTopicService;
 import com.stdiet.custom.utils.WsUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -10,15 +13,17 @@ import org.springframework.stereotype.Component;
 import javax.websocket.*;
 import javax.websocket.server.ServerEndpoint;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.CopyOnWriteArraySet;
 
 @ServerEndpoint(value = "/ws")
 @Component
 @Slf4j
 public class WebSocketServer {
-
     // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
+
     //private static ConcurrentHashMap<String,WebSocketServer> websocketList = new ConcurrentHashMap<>();
     // 与某个客户端的连接会话,需要通过它来给客户端发送数据
     private Session session;
@@ -74,23 +79,6 @@ public class WebSocketServer {
         log.info("有一连接关闭!");
     }
 
-    // */
-    /// **
-    // * 收到客户端消息后调用的方法
-    // *
-    // * @param message 客户端发送过来的消息*//*
-    @OnMessage
-    public void onMessage(String message, Session session) {
-        log.info("收到来自窗口" + sid + "的信息:" + message);
-//        // 群发消息
-//        for (WebSocketServer item : webSocketSet) {
-//            try {
-//                item.sendMessage(message);
-//            } catch (IOException e) {
-//                e.printStackTrace();
-//            }
-//        }
-    }
 
     /**
      * @param session
@@ -118,5 +106,38 @@ public class WebSocketServer {
         }
     }
 
+    @OnMessage
+    public void onMessage(String message, Session session) {
+        log.info("收到来自窗口" + sid + "的信息:" + message);
+        try {
+            String sid = String.valueOf(getUserId(session));
+            if (sid.equals("0")) {
+                return;
+            }
+            JSONObject resultObj = new JSONObject();
+            if (message.equals(WsUtils.WS_GET_UNREAD_COUNT)) {
+                SysServicesTopic topic = new SysServicesTopic();
+                topic.setUid(sid);
+                List<SysServicesTopic> statusList = new ArrayList<>();
+                statusList.add(topic);
+                ISysServicesTopicService servicesTopicService = SpringUtils.getBean(ISysServicesTopicService.class);
+                List<SysServicesTopic> result = servicesTopicService.selectUnreadTopicCount(statusList);
+
+                JSONObject dataObj = new JSONObject();
+                dataObj.put("count", result.get(0).getCount());
+
+                resultObj.put("type", WsUtils.WS_TYPE_MESSAGE_COUNT);
+                resultObj.put("msg", "未读消息数");
+                resultObj.put("data", dataObj);
+
+            } else if (message.equals(WsUtils.WS_PING)) {
+
+            }
+            WebSocketServer.sendInfo(resultObj.toJSONString(), sid);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
 
 }
\ No newline at end of file
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/service/ISysServicesTopicService.java b/stdiet-custom/src/main/java/com/stdiet/custom/service/ISysServicesTopicService.java
index 1b9b14291..054424584 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/service/ISysServicesTopicService.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/service/ISysServicesTopicService.java
@@ -17,4 +17,6 @@ public interface ISysServicesTopicService {
     SysServicesTopic inserSysServicesTopicComment(SysServicesTopic topic);
 
     List<SysServicesTopic> selectSysServicesTopicSessionByTopicId(String topicId);
+
+    List<SysServicesTopic> selectUnreadTopicCount(List<SysServicesTopic> topic);
 }
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysServicesTopicServiceImp.java b/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysServicesTopicServiceImp.java
index dcecfdd49..941518c5b 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysServicesTopicServiceImp.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysServicesTopicServiceImp.java
@@ -1,15 +1,19 @@
 package com.stdiet.custom.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
 import com.stdiet.common.utils.DateUtils;
 import com.stdiet.common.utils.uuid.UUID;
 import com.stdiet.custom.domain.SysCustomer;
 import com.stdiet.custom.domain.SysServicesTopic;
 import com.stdiet.custom.mapper.SysCustomerMapper;
 import com.stdiet.custom.mapper.SysServicesTopicMapper;
+import com.stdiet.custom.server.WebSocketServer;
 import com.stdiet.custom.service.ISysServicesTopicService;
+import com.stdiet.custom.utils.WsUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -40,35 +44,58 @@ public class SysServicesTopicServiceImp implements ISysServicesTopicService {
 
         List<SysServicesTopic> statusList = new ArrayList<>();
 
+        String customerId = String.valueOf(customer.getId());
         SysServicesTopic customerStatus = new SysServicesTopic();
-        customerStatus.setUid(String.valueOf(customer.getId()));
+        customerStatus.setUid(customerId);
         customerStatus.setRole("customer");
         customerStatus.setRead(1);
         customerStatus.setTopicId(topic.getTopicId());
         statusList.add(customerStatus);
 
+        String dieticianId = String.valueOf(customer.getMainDietitian());
         SysServicesTopic dieticianStatus = new SysServicesTopic();
-        dieticianStatus.setUid(String.valueOf(customer.getMainDietitian()));
+        dieticianStatus.setUid(dieticianId);
         dieticianStatus.setRole("dietician");
         dieticianStatus.setRead(0);
         dieticianStatus.setTopicId(topic.getTopicId());
         statusList.add(dieticianStatus);
 
+        String afterSaleId = String.valueOf(customer.getAfterDietitian());
         SysServicesTopic afterSaleStatus = new SysServicesTopic();
-        afterSaleStatus.setUid(String.valueOf(customer.getAfterDietitian()));
+        afterSaleStatus.setUid(afterSaleId);
         afterSaleStatus.setRole("after_sale");
         afterSaleStatus.setRead(0);
         afterSaleStatus.setTopicId(topic.getTopicId());
         statusList.add(afterSaleStatus);
 
+        String dieticianAssistantId = String.valueOf(customer.getAssistantDietitian());
         SysServicesTopic dieticianAssistantStatus = new SysServicesTopic();
-        dieticianAssistantStatus.setUid(String.valueOf(customer.getAssistantDietitian()));
+        dieticianAssistantStatus.setUid(dieticianAssistantId);
         dieticianAssistantStatus.setRole("dietician_assistant");
         dieticianAssistantStatus.setRead(0);
         dieticianAssistantStatus.setTopicId(topic.getTopicId());
         statusList.add(dieticianAssistantStatus);
 
-        servicesTopicMapper.insertSysServicesTopicStatus(statusList);
+        int rows = servicesTopicMapper.insertSysServicesTopicStatus(statusList);
+        if (rows > 0) {
+            try {
+                List<SysServicesTopic> counts = servicesTopicMapper.selectUnreadTopicCount(statusList);
+                for (int i = 0; i < counts.size(); i++) {
+                    topic.setId(statusList.get(i).getId());
+                    JSONObject dataObj = new JSONObject();
+                    dataObj.put("count", counts.get(i).getCount());
+                    dataObj.put("data", topic);
+
+                    JSONObject msgObj = new JSONObject();
+                    msgObj.put("type", WsUtils.WS_TYPE_MESSAGE_COUNT);
+                    msgObj.put("msg", "未读消息数");
+                    msgObj.put("data", dataObj);
+                    WebSocketServer.sendInfo(msgObj.toJSONString(), counts.get(i).getUid());
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
 
         topic.setId(customerStatus.getId());
         topic.setUid(null);
@@ -94,10 +121,13 @@ public class SysServicesTopicServiceImp implements ISysServicesTopicService {
             status.setTopicId(topic.getTopicId());
             status.setRole(topic.getRole());
             servicesTopicMapper.updateSysServicesTopicStatus(status);
+
+            afterReply(topic);
         }
         return topic;
     }
 
+
     @Override
     public SysServicesTopic inserSysServicesTopicComment(SysServicesTopic topic) {
         String uuid = java.util.UUID.randomUUID().toString().replace("-", "");
@@ -110,12 +140,51 @@ public class SysServicesTopicServiceImp implements ISysServicesTopicService {
             status.setTopicId(topic.getTopicId());
             status.setRole(topic.getRole());
             servicesTopicMapper.updateSysServicesTopicStatus(status);
+
+            afterReply(topic);
         }
         return topic;
     }
 
+    public void afterReply(SysServicesTopic topic) {
+        SysCustomer customer = sysCustomerMapper.selectSysCustomerById(Long.parseLong(topic.getFromUid()));
+
+        List<SysServicesTopic> statusList = new ArrayList<>();
+        SysServicesTopic dieticianStatus = new SysServicesTopic();
+        dieticianStatus.setUid(String.valueOf(customer.getMainDietitian()));
+        statusList.add(dieticianStatus);
+        SysServicesTopic afterSaleStatus = new SysServicesTopic();
+        afterSaleStatus.setUid(String.valueOf(customer.getAfterDietitian()));
+        statusList.add(afterSaleStatus);
+        SysServicesTopic dieticianAssistantStatus = new SysServicesTopic();
+        dieticianAssistantStatus.setUid(String.valueOf(customer.getAssistantDietitian()));
+        statusList.add(dieticianAssistantStatus);
+
+        try {
+            List<SysServicesTopic> counts = servicesTopicMapper.selectUnreadTopicCount(statusList);
+            for (int i = 0; i < counts.size(); i++) {
+                JSONObject dataObj = new JSONObject();
+                dataObj.put("count", counts.get(i).getCount());
+                dataObj.put("topicId", topic.getTopicId());
+
+                JSONObject msgObj = new JSONObject();
+                msgObj.put("type", WsUtils.WS_TYPE_NEW_CUSTOMER_REPLY);
+                msgObj.put("msg", "新客户回复");
+                msgObj.put("data", dataObj);
+                WebSocketServer.sendInfo(msgObj.toJSONString(), counts.get(i).getUid());
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
     @Override
     public List<SysServicesTopic> selectSysServicesTopicSessionByTopicId(String topicId) {
         return servicesTopicMapper.selectSysServicesTopicSessionByTopicId(topicId);
     }
+
+    @Override
+    public List<SysServicesTopic> selectUnreadTopicCount(List<SysServicesTopic> statusList) {
+        return servicesTopicMapper.selectUnreadTopicCount(statusList);
+    }
 }
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java b/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java
index 6d8e8f43c..a1b2286bf 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java
@@ -5,4 +5,12 @@ public class WsUtils {
     public static final String WS_TYPE_HEART_BEAT = "WS_TYPE_HEART_BEAT";
 
     public static final String WS_TYPE_SYSTEM_MESSAGE = "WS_TYPE_SYSTEM_MESSAGE";
+
+    public static final String WS_TYPE_MESSAGE_COUNT = "WS_TYPE_MESSAGE_COUNT";
+
+    public static final String WS_TYPE_NEW_CUSTOMER_REPLY = "WS_TYPE_NEW_CUSTOMER_REPLY";
+
+    public static final String WS_GET_UNREAD_COUNT = "GET_UNREAD_COUNT";
+
+    public static final String WS_PING = "ping";
 }
diff --git a/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml b/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
index 688494714..a1dc84685 100644
--- a/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
+++ b/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
@@ -225,5 +225,23 @@
         </trim>
     </insert>
 
+    <resultMap type="SysServicesTopic" id="SysServicesCountResult">
+        <result column="uid" property="uid"/>
+        <result column="count" property="count"/>
+    </resultMap>
 
+    <select id="selectUnreadTopicCount" parameterType="java.util.List" resultMap="SysServicesCountResult">
+        <foreach collection="list" item="status" index="index">
+            <choose>
+                <when test="index == 0">
+                    SELECT COUNT(*) AS count, uid FROM sys_services_topic_status WHERE `read` = 0 AND uid =
+                    #{status.uid}
+                </when>
+                <otherwise>
+                    UNION ALL SELECT COUNT(*) AS count, uid FROM sys_services_topic_status WHERE `read` = 0 AND uid =
+                    #{status.uid}
+                </otherwise>
+            </choose>
+        </foreach>
+    </select>
 </mapper>
\ No newline at end of file
diff --git a/stdiet-ui/src/layout/components/Navbar.vue b/stdiet-ui/src/layout/components/Navbar.vue
index af5333114..cb6a5d8b8 100644
--- a/stdiet-ui/src/layout/components/Navbar.vue
+++ b/stdiet-ui/src/layout/components/Navbar.vue
@@ -11,26 +11,11 @@
 
     <div class="right-menu">
       <template v-if="device !== 'mobile'">
-        <!-- <search id="header-search" class="right-menu-item" /> -->
-
-        <!-- <el-tooltip content="源码地址" effect="dark" placement="bottom">
-          <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
-        </el-tooltip> -->
-
-        <!-- <el-tooltip content="文档地址" effect="dark" placement="bottom">
-          <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
-        </el-tooltip> -->
-
-        <!-- <screenfull id="screenfull" class="right-menu-item hover-effect" /> -->
-
-        <!-- <el-tooltip content="布局大小" effect="dark" placement="bottom">
-          <size-select id="size-select" class="right-menu-item hover-effect" />
-        </el-tooltip> -->
         <div
           class="right-menu-item hover-effect badge_style"
           @click="handleOnMessageClick"
         >
-          <el-badge :value="12">
+          <el-badge :value="msgUnreadCount">
             <el-tooltip content="消息" effect="dark" placement="bottom">
               <em class="el-icon-message" :style="{ fontSize: '28px' }" />
             </el-tooltip>
@@ -66,25 +51,30 @@
 import { mapGetters, mapActions } from "vuex";
 import Breadcrumb from "@/components/Breadcrumb";
 import Hamburger from "@/components/Hamburger";
-// import Screenfull from "@/components/Screenfull";
-// import SizeSelect from "@/components/SizeSelect";
-// import Search from "@/components/HeaderSearch";
-// import RuoYiGit from "@/components/RuoYi/Git";
-// import RuoYiDoc from "@/components/RuoYi/Doc";
+import { keys } from "@/utils/websocket";
 
 export default {
   components: {
     Breadcrumb,
     Hamburger,
-    // Screenfull,
-    // SizeSelect,
-    // Search,
+  },
+  data() {
+    return {
+      msgNum: 0,
+    };
   },
   created() {
     this.init();
   },
+  mounted() {
+    window.postMessage({ type: keys.GET_UNREAD_COUNT }, window.location.origin);
+    window.addEventListener("message", this.handleOnMessage);
+  },
+  unmounted() {
+    window.removeEventListener("message", this.handleOnMessage);
+  },
   computed: {
-    ...mapGetters(["sidebar", "avatar", "device"]),
+    ...mapGetters(["sidebar", "avatar", "device", "msgUnreadCount"]),
     setting: {
       get() {
         return this.$store.state.settings.showSettings;
@@ -98,7 +88,14 @@ export default {
     },
   },
   methods: {
-    ...mapActions(["init"]),
+    ...mapActions(["init", "updateUnreadCount"]),
+    handleOnMessage({ data }) {
+      if (data.type === keys.WS_TYPE_MESSAGE_COUNT) {
+        this.updateUnreadCount({
+          msgUnreadCount: data.data.count,
+        });
+      }
+    },
     toggleSideBar() {
       this.$store.dispatch("app/toggleSideBar");
     },
diff --git a/stdiet-ui/src/main.js b/stdiet-ui/src/main.js
index 79469f5f8..4b1842ad8 100644
--- a/stdiet-ui/src/main.js
+++ b/stdiet-ui/src/main.js
@@ -26,6 +26,7 @@ import { init } from "@/utils/websocket";
 import exportingInit from "highcharts/modules/exporting";
 exportingInit(Highcharts);
 
+// websocket 初始化
 init();
 
 import {
diff --git a/stdiet-ui/src/store/actions.js b/stdiet-ui/src/store/actions.js
index cb89bf4f6..19c8af3a6 100644
--- a/stdiet-ui/src/store/actions.js
+++ b/stdiet-ui/src/store/actions.js
@@ -1,6 +1,9 @@
 const actions = {
   async init({ dispatch }, payload) {
     dispatch("global/init", payload);
+  },
+  async updateUnreadCount({ commit }, payload) {
+    commit("global/save", payload);
   }
 };
 
diff --git a/stdiet-ui/src/store/getters.js b/stdiet-ui/src/store/getters.js
index ccedcde6d..b615ad51c 100644
--- a/stdiet-ui/src/store/getters.js
+++ b/stdiet-ui/src/store/getters.js
@@ -9,12 +9,11 @@ const getters = {
   name: state => state.user.name,
   introduction: state => state.user.introduction,
   roles: state => state.user.roles,
-  // roles: state => ["dietician"],
   permissions: state => state.user.permissions,
   userId: state => state.user.userId,
-  // userId: state => 131,
   userRemark: state => state.user.remark,
   permission_routes: state => state.permission.routes,
+  msgUnreadCount: state => state.global.msgUnreadCount,
   //
   nutritionistIdOptions: state => state.global.nutritionistIdOptions,
   nutriAssisIdOptions: state => state.global.nutriAssisIdOptions,
diff --git a/stdiet-ui/src/store/modules/global.js b/stdiet-ui/src/store/modules/global.js
index 989310a29..18189eaf8 100644
--- a/stdiet-ui/src/store/modules/global.js
+++ b/stdiet-ui/src/store/modules/global.js
@@ -9,7 +9,8 @@ const oriState = {
   plannerIdOptions: [],
   plannerAssisIdOptions: [],
   operatorIdOptions: [],
-  operatorAssisIdOptions: []
+  operatorAssisIdOptions: [],
+  msgUnreadCount: 0,
 };
 
 const mutations = {
diff --git a/stdiet-ui/src/store/modules/message.js b/stdiet-ui/src/store/modules/message.js
index bd01aa685..20270f7d4 100644
--- a/stdiet-ui/src/store/modules/message.js
+++ b/stdiet-ui/src/store/modules/message.js
@@ -6,6 +6,7 @@ import {
 } from "@/api/custom/message";
 
 const oriState = {
+  pageNum: 1,
   topicList: [],
   detailData: {},
   selTopicId: ""
@@ -28,23 +29,35 @@ const mutations = {
 };
 
 const actions = {
-  async init({ rootGetters, commit, dispatch }, payload) {
+  async init({ dispatch }, payload) {
+    dispatch("fetchTopicListApi", {});
+  },
+  async fetchTopicListApi({ dispatch, commit, rootGetters, state }, payload) {
     const {
       roles: [role],
       userId
     } = rootGetters;
-    const result = await fetchTopicList({ role, uid: userId });
+    const { detailData, pageNum, topicList } = state;
+    const result = await fetchTopicList({
+      role,
+      uid: userId,
+      pageSize: 20,
+      pageNum
+    });
     if (result.code === 200) {
-      const [defTopic] = result.rows;
-
-      dispatch("fetchTopicDetailActions", {
-        topicId: defTopic.topicId,
-        id: defTopic.id
-      });
-
-      commit("save", {
-        topicList: result.rows
-      });
+      if (!detailData.topicId) {
+        const [defTopic] = result.rows;
+        dispatch("fetchTopicDetailActions", {
+          topicId: defTopic.topicId,
+          id: defTopic.id
+        });
+      }
+      if (result.rows.length) {
+        commit("save", {
+          pageNum: pageNum + 1,
+          topicList: [...topicList, ...result.rows]
+        });
+      }
     }
   },
   async fetchTopicDetailActions({ commit }, payload) {
diff --git a/stdiet-ui/src/utils/websocket.js b/stdiet-ui/src/utils/websocket.js
index 94a85698b..5f944280b 100644
--- a/stdiet-ui/src/utils/websocket.js
+++ b/stdiet-ui/src/utils/websocket.js
@@ -6,8 +6,10 @@ const url = `${wsProtocol}://${hostname}${
 let ws = undefined;
 let intervalRef = undefined;
 
-function handleOnMessageReceive(event) {
-  console.log({ event });
+function handleOnMessageReceive({ data = {} }) {
+  if (data.type && data.type === keys.GET_UNREAD_COUNT) {
+    ws.send(keys.GET_UNREAD_COUNT);
+  }
 }
 
 function connect() {
@@ -18,11 +20,15 @@ function connect() {
       console.log("ws连接成功");
       intervalRef && clearInterval(intervalRef);
 
-      document.addEventListener("message", handleOnMessageReceive);
+      setInterval(() => {
+        ws.send("ping");
+      }, 30000);
+
+      window.addEventListener("message", handleOnMessageReceive);
     };
 
     ws.onmessage = event => {
-      console.log({ event });
+      // console.log({ event });
 
       const dataObj = JSON.parse(event.data || "{}");
 
@@ -32,24 +38,35 @@ function connect() {
     };
 
     ws.onerror = event => {
-      console.log({ event });
+      // console.log({ event });
       ws = undefined;
-      document.removeEventListener("message", handleOnMessageReceive);
+      window.removeEventListener("message", handleOnMessageReceive);
     };
 
     ws.onclose = event => {
       ws = undefined;
-      document.removeEventListener("message", handleOnMessageReceive);
+      window.removeEventListener("message", handleOnMessageReceive);
     };
   } catch (error) {
-    console.log(error);
+    // console.log(error);
+    ws = undefined;
+    init();
     console.log("浏览器不支持websocket");
   }
 }
 
 export function init() {
-  intervalRef = setInterval(() => {
-    console.log("尝试连接websocket");
-    !ws && connect();
-  }, 10000);
+  !ws && connect();
+
+  !ws &&
+    (intervalRef = setInterval(() => {
+      console.log("尝试连接websocket");
+      !ws && connect();
+    }, 10000));
 }
+
+export const keys = {
+  GET_UNREAD_COUNT: "GET_UNREAD_COUNT",
+  WS_TYPE_MESSAGE_COUNT: "WS_TYPE_MESSAGE_COUNT",
+  WS_TYPE_NEW_CUSTOMER_REPLY: "WS_TYPE_NEW_CUSTOMER_REPLY"
+};
diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
index 3e0ba46ea..23aefbb2d 100644
--- a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
+++ b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
@@ -1,28 +1,35 @@
 <template>
   <div class="message_browser_wrapper">
-    <div class="topic_list">
-      <div
-        v-for="topic in topicList"
-        :key="topic.topicId"
-        :class="`topic_item ${
-          selTopicId === topic.topicId ? 'topic_item_sel' : ''
-        }`"
-        @click="handleOnTopicClick(topic)"
-      >
-        <div class="topic_status topic_status_read" />
-        <div class="topic_item_content">
-          <div class="topic_content">{{ topic.content }}</div>
-          <div class="topic_user_name">by {{ topic.name }}</div>
-        </div>
-        <div class="topic_info">
-          <el-tag size="small">{{ topicTypeDict[topic.topicType] }}</el-tag>
-          <div class="topic_time">{{ formatDate(topic.createTime) }}</div>
+    <div class="topic_list" @scroll="handleOnScroll">
+      <div v-if="topicList && topicList.length">
+        <div
+          v-for="topic in topicList"
+          :key="topic.topicId"
+          :class="`topic_item ${
+            selTopicId === topic.topicId ? 'topic_item_sel' : ''
+          }`"
+          @click="handleOnTopicClick(topic)"
+        >
+          <div
+            :class="`topic_status ${
+              topic.read ? 'topic_status_read' : 'topic_status_unread'
+            }`"
+          />
+          <div class="topic_item_content">
+            <div class="topic_content">{{ topic.content }}</div>
+            <div class="topic_user_name">by {{ topic.name }}</div>
+          </div>
+          <div class="topic_info">
+            <el-tag size="small">{{ topicTypeDict[topic.topicType] }}</el-tag>
+            <div class="topic_time">{{ formatDate(topic.createTime) }}</div>
+          </div>
         </div>
       </div>
+      <div v-else class="topic_list_empty">暂无消息</div>
     </div>
     <div class="topic_detail">
       <div class="topic_detail_list">
-        <div class="topic_detail_title">
+        <div class="topic_detail_title" v-if="!!detailData.content">
           <div>{{ detailData.content }}</div>
           <div class="content_time" :style="{ marginTop: '4px' }">
             {{ formatDate(detailData.createTime) }}
@@ -50,7 +57,12 @@
         >
           回复:{{ replyTarget }}
         </div>
-        <el-input type="textarea" :rows="3" v-model="replyContent" />
+        <el-input
+          type="textarea"
+          :rows="3"
+          v-model="replyContent"
+          :disabled="!detailData.content"
+        />
         <div class="send_btn_zone">
           <el-button
             type="primary"
@@ -65,9 +77,10 @@
   </div>
 </template>
 <script>
-import { createNamespacedHelpers } from "vuex";
+import { createNamespacedHelpers, mapActions as globalMapActions } from "vuex";
 import Comment from "./Comment";
 import dayjs from "dayjs";
+import { keys } from "@/utils/websocket";
 const {
   mapActions,
   mapState,
@@ -91,10 +104,61 @@ export default {
   created() {
     this.init();
   },
+  mounted() {
+    window.addEventListener("message", this.handleOnMessage);
+  },
+  unmounted() {
+    window.removeEventListener("message", this.handleOnMessage);
+  },
   computed: {
     ...mapState(["topicList", "selTopicId", "detailData"]),
   },
   methods: {
+    handleOnScroll({ target }) {
+      if (
+        target.clientHeight + parseInt(target.scrollTop) ===
+        target.scrollHeight
+      ) {
+        this.fetchTopicListApi();
+      }
+    },
+    handleOnMessage({ data }) {
+      if (data.type === keys.WS_TYPE_MESSAGE_COUNT) {
+        const { data: tData } = data.data;
+        const time = dayjs(tData.createTime).format("YYYY-MM-DD HH:mm:ss");
+        const newTopicList = [
+          {
+            id: tData.id,
+            content: tData.content,
+            createTime: time,
+            img: tData.img,
+            topicId: tData.topicId,
+            role: "customer",
+            uid: tData.uid,
+            updateTime: time,
+            topicType: tData.topicType,
+            read: tData.read,
+          },
+          ...this.topicList,
+        ];
+        this.save({
+          topicList: newTopicList,
+        });
+      } else if (data.type === keys.WS_TYPE_NEW_CUSTOMER_REPLY) {
+        const { count, topicId } = data.data;
+        this.updateUnreadCount({
+          msgUnreadCount: count,
+        });
+        if (this.selTopicId === topicId) {
+          const tarTopic = this.topicList.find(
+            (obj) => obj.topicId === topicId
+          );
+          if (tarTopic) {
+            this.fetchTopicDetailActions({ topicId, id: tarTopic.id });
+          }
+        }
+      }
+    },
     formatDate(date) {
       return dayjs(date).format("MM-DD HH:mm");
     },
@@ -147,8 +211,14 @@ export default {
         this.$message.error("请选择回复对象");
       }
     },
-    ...mapActions(["init", "fetchTopicDetailActions", "postTopicReplyActions"]),
-    ...mapMutations(["clean"]),
+    ...mapActions([
+      "init",
+      "fetchTopicDetailActions",
+      "postTopicReplyActions",
+      "fetchTopicListApi",
+    ]),
+    ...mapMutations(["clean", "save"]),
+    ...globalMapActions(["updateUnreadCount"]),
   },
 };
 </script>
@@ -157,6 +227,7 @@ export default {
   display: flex;
   .topic_list {
     flex: 2;
+    overflow: auto;
 
     .topic_item {
       display: flex;
@@ -223,6 +294,13 @@ export default {
     .topic_item_sel {
       background: #dedede;
     }
+
+    .topic_list_empty {
+      height: 100px;
+      text-align: center;
+      line-height: 100px;
+      color: #8c8c8c;
+    }
   }
 
   .topic_detail {

From a3ea4edcf4b88caa3820389f30f93b6cc3eb15a3 Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Thu, 3 Jun 2021 11:24:29 +0800
Subject: [PATCH 05/14] =?UTF-8?q?=E8=B0=83=E6=95=B4ws=E8=BF=9E=E6=8E=A5?=
 =?UTF-8?q?=E6=97=B6=E6=9C=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 stdiet-ui/src/layout/components/Navbar.vue |  8 +++++---
 stdiet-ui/src/main.js                      |  4 ----
 stdiet-ui/src/utils/websocket.js           | 16 +++++++++++++---
 3 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/stdiet-ui/src/layout/components/Navbar.vue b/stdiet-ui/src/layout/components/Navbar.vue
index cb6a5d8b8..ee333af6c 100644
--- a/stdiet-ui/src/layout/components/Navbar.vue
+++ b/stdiet-ui/src/layout/components/Navbar.vue
@@ -28,8 +28,8 @@
         trigger="click"
       >
         <div class="avatar-wrapper">
-          <img :src="avatar" class="user-avatar" />
-          <i class="el-icon-caret-bottom" />
+          <img :src="avatar" class="user-avatar" alt="avatar" />
+          <em class="el-icon-caret-bottom" />
         </div>
         <el-dropdown-menu slot="dropdown">
           <router-link to="/user/profile">
@@ -51,7 +51,7 @@
 import { mapGetters, mapActions } from "vuex";
 import Breadcrumb from "@/components/Breadcrumb";
 import Hamburger from "@/components/Hamburger";
-import { keys } from "@/utils/websocket";
+import { keys, websocketInit, beforeUnmount } from "@/utils/websocket";
 
 export default {
   components: {
@@ -65,12 +65,14 @@ export default {
   },
   created() {
     this.init();
+    websocketInit();
   },
   mounted() {
     window.postMessage({ type: keys.GET_UNREAD_COUNT }, window.location.origin);
     window.addEventListener("message", this.handleOnMessage);
   },
   unmounted() {
+    beforeUnmount();
     window.removeEventListener("message", this.handleOnMessage);
   },
   computed: {
diff --git a/stdiet-ui/src/main.js b/stdiet-ui/src/main.js
index 4b1842ad8..f73283d27 100644
--- a/stdiet-ui/src/main.js
+++ b/stdiet-ui/src/main.js
@@ -21,14 +21,10 @@ import VueScrollTo from "vue-scrollto";
 import VueResource from "vue-resource";
 import HighchartsVue from "highcharts-vue";
 import Highcharts from "highcharts";
-import { init } from "@/utils/websocket";
 //图片导出模块
 import exportingInit from "highcharts/modules/exporting";
 exportingInit(Highcharts);
 
-// websocket 初始化
-init();
-
 import {
   addDateRange,
   download,
diff --git a/stdiet-ui/src/utils/websocket.js b/stdiet-ui/src/utils/websocket.js
index 5f944280b..7d6554c69 100644
--- a/stdiet-ui/src/utils/websocket.js
+++ b/stdiet-ui/src/utils/websocket.js
@@ -41,21 +41,31 @@ function connect() {
       // console.log({ event });
       ws = undefined;
       window.removeEventListener("message", handleOnMessageReceive);
+
+      websocketInit();
     };
 
     ws.onclose = event => {
+      // console.log(event);
       ws = undefined;
       window.removeEventListener("message", handleOnMessageReceive);
+      if (event.reason !== "unmount") {
+        websocketInit();
+      }
     };
   } catch (error) {
     // console.log(error);
+    // console.log("浏览器不支持websocket");
     ws = undefined;
-    init();
-    console.log("浏览器不支持websocket");
+    websocketInit();
   }
 }
 
-export function init() {
+export function beforeUnmount(code) {
+  ws && ws.close(code, "unmount");
+}
+
+export function websocketInit() {
   !ws && connect();
 
   !ws &&

From 13cb91cd927df87f3cb54d5de3e452caef3ab394 Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Thu, 3 Jun 2021 14:10:24 +0800
Subject: [PATCH 06/14] =?UTF-8?q?=E8=B0=83=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 stdiet-ui/src/layout/components/Navbar.vue                  | 2 +-
 stdiet-ui/src/utils/websocket.js                            | 2 ++
 stdiet-ui/src/views/custom/message/messageBrowser/index.vue | 6 +++---
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/stdiet-ui/src/layout/components/Navbar.vue b/stdiet-ui/src/layout/components/Navbar.vue
index ee333af6c..2eaf0ebec 100644
--- a/stdiet-ui/src/layout/components/Navbar.vue
+++ b/stdiet-ui/src/layout/components/Navbar.vue
@@ -28,7 +28,7 @@
         trigger="click"
       >
         <div class="avatar-wrapper">
-          <img :src="avatar" class="user-avatar" alt="avatar" />
+          <img :src="avatar" class="user-avatar" />
           <em class="el-icon-caret-bottom" />
         </div>
         <el-dropdown-menu slot="dropdown">
diff --git a/stdiet-ui/src/utils/websocket.js b/stdiet-ui/src/utils/websocket.js
index 7d6554c69..62dde69ec 100644
--- a/stdiet-ui/src/utils/websocket.js
+++ b/stdiet-ui/src/utils/websocket.js
@@ -39,6 +39,7 @@ function connect() {
 
     ws.onerror = event => {
       // console.log({ event });
+      ws.close();
       ws = undefined;
       window.removeEventListener("message", handleOnMessageReceive);
 
@@ -56,6 +57,7 @@ function connect() {
   } catch (error) {
     // console.log(error);
     // console.log("浏览器不支持websocket");
+    ws.close();
     ws = undefined;
     websocketInit();
   }
diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
index 23aefbb2d..aa9041f94 100644
--- a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
+++ b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
@@ -91,9 +91,9 @@ export default {
   data() {
     return {
       topicTypeDict: {
-        0: "建议",
-        1: "食谱",
-        2: "咨询",
+        0: "食材",
+        1: "身体",
+        2: "环境",
       },
       replyTarget: "",
       replyContent: "",

From f682efe7589d437cf6f07e3325d15a1bf26d1b72 Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Thu, 3 Jun 2021 14:26:02 +0800
Subject: [PATCH 07/14] =?UTF-8?q?=E9=80=80=E5=87=BA=E5=85=B3=E9=97=ADws?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 stdiet-ui/src/layout/components/Navbar.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/stdiet-ui/src/layout/components/Navbar.vue b/stdiet-ui/src/layout/components/Navbar.vue
index 2eaf0ebec..e26a0168d 100644
--- a/stdiet-ui/src/layout/components/Navbar.vue
+++ b/stdiet-ui/src/layout/components/Navbar.vue
@@ -72,7 +72,7 @@ export default {
     window.addEventListener("message", this.handleOnMessage);
   },
   unmounted() {
-    beforeUnmount();
+    beforeUnmount(1000);
     window.removeEventListener("message", this.handleOnMessage);
   },
   computed: {

From d495f47304117c3ec824d8091bd38651dbb56705 Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Thu, 3 Jun 2021 17:37:59 +0800
Subject: [PATCH 08/14] =?UTF-8?q?=E6=8E=A5=E5=85=A5=E5=AE=A2=E6=88=B7?=
 =?UTF-8?q?=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/main/filters/local/application.yml    |  2 +-
 stdiet-ui/src/store/modules/message.js        | 49 +++++++++++++++++--
 stdiet-ui/src/views/custom/message/index.vue  |  4 +-
 .../custom/message/messageBrowser/index.vue   | 28 +++++++++--
 .../views/custom/message/userInfo/index.vue   | 38 ++++++++++++++
 5 files changed, 110 insertions(+), 11 deletions(-)
 create mode 100644 stdiet-ui/src/views/custom/message/userInfo/index.vue

diff --git a/stdiet-admin/src/main/filters/local/application.yml b/stdiet-admin/src/main/filters/local/application.yml
index b3bead739..6932d7b76 100644
--- a/stdiet-admin/src/main/filters/local/application.yml
+++ b/stdiet-admin/src/main/filters/local/application.yml
@@ -83,7 +83,7 @@ spring:
     druid:
       # 主库数据源
       master:
-        url: jdbc:mysql://47.115.23.82:3306/stdiet?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+        url: jdbc:mysql://47.115.23.82:3306/stdiet_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
         password: gzDxPaZcSiXJpi2N
         username: root
       slave:
diff --git a/stdiet-ui/src/store/modules/message.js b/stdiet-ui/src/store/modules/message.js
index 20270f7d4..e4f2bcb64 100644
--- a/stdiet-ui/src/store/modules/message.js
+++ b/stdiet-ui/src/store/modules/message.js
@@ -1,3 +1,6 @@
+import { getCustomerPhysicalSignsByCusId } from "@/api/custom/customer";
+import { dealHealthy } from "@/utils/healthyData";
+
 import {
   fetchTopicList,
   postTopicReply,
@@ -9,7 +12,11 @@ const oriState = {
   pageNum: 1,
   topicList: [],
   detailData: {},
-  selTopicId: ""
+  selTopicId: "",
+  healthyData: {},
+  healthDataLoading: false,
+  healthyDataType: 0,
+  avoidFoodIds: []
 };
 
 const mutations = {
@@ -46,10 +53,12 @@ const actions = {
     });
     if (result.code === 200) {
       if (!detailData.topicId) {
+        // 默认展示第一个
         const [defTopic] = result.rows;
         dispatch("fetchTopicDetailActions", {
           topicId: defTopic.topicId,
-          id: defTopic.id
+          id: defTopic.id,
+          uid: defTopic.uid
         });
       }
       if (result.rows.length) {
@@ -60,9 +69,15 @@ const actions = {
       }
     }
   },
-  async fetchTopicDetailActions({ commit }, payload) {
-    const { topicId, id } = payload;
+  async fetchTopicDetailActions({ commit, dispatch, state }, payload) {
+    const { topicId, id, uid } = payload;
+    const { healthyData } = state;
     commit("save", { selTopicId: topicId });
+    // 客户信息
+    if (healthyData.customerId !== parseInt(uid)) {
+      dispatch("getHealthyData", { cusId: uid });
+    }
+    //
     const result = await fetchTopicDetail({ topicId, id });
     if (result.code === 200) {
       commit("save", { detailData: result.data[0] });
@@ -89,11 +104,35 @@ const actions = {
       if (tarTopic) {
         dispatch("fetchTopicDetailActions", {
           topicId: tarTopic.topicId,
-          id: tarTopic.id
+          id: tarTopic.id,
+          uid: tarTopic.uid
         });
       }
     }
     return result;
+  },
+  async getHealthyData({ commit }, payload) {
+    commit("save", { healthDataLoading: true });
+    const healthyDataResult = await getCustomerPhysicalSignsByCusId(
+      payload.cusId
+    );
+    let healthyData = undefined,
+      healthyDataType = 0;
+    if (healthyDataResult.code === 200) {
+      if (!healthyDataResult.data.customerHealthy) {
+        throw new Error("客户还没填写健康评估表");
+      }
+      healthyDataType = healthyDataResult.data.type;
+      healthyData = dealHealthy(healthyDataResult.data.customerHealthy);
+    } else {
+      throw new Error(healthyDataResult.msg);
+    }
+    commit("save", {
+      healthDataLoading: false,
+      healthyDataType,
+      healthyData,
+      avoidFoodIds: (healthyData.avoidFood || []).map(obj => obj.id)
+    });
   }
 };
 
diff --git a/stdiet-ui/src/views/custom/message/index.vue b/stdiet-ui/src/views/custom/message/index.vue
index a2239526f..e6650663f 100644
--- a/stdiet-ui/src/views/custom/message/index.vue
+++ b/stdiet-ui/src/views/custom/message/index.vue
@@ -1,17 +1,19 @@
 <template>
   <div class="user_message_wrapper">
     <MessageBrowser />
-    <div class="info_zone"></div>
+    <UserInfo />
   </div>
 </template>
 <script>
 import MessageBrowser from "./messageBrowser/index";
+import UserInfo from "./userInfo/index";
 export default {
   data() {
     return {};
   },
   components: {
     MessageBrowser,
+    UserInfo,
   },
   created() {},
   computed: {},
diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
index aa9041f94..836d84e1a 100644
--- a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
+++ b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
@@ -16,7 +16,9 @@
             }`"
           />
           <div class="topic_item_content">
-            <div class="topic_content">{{ topic.content }}</div>
+            <div class="topic_content" :style="{ width: `${itemWidth}px` }">
+              {{ topic.content }}
+            </div>
             <div class="topic_user_name">by {{ topic.name }}</div>
           </div>
           <div class="topic_info">
@@ -98,6 +100,7 @@ export default {
       replyTarget: "",
       replyContent: "",
       replyObj: {},
+      itemWidth: 160,
     };
   },
   components: { Comment },
@@ -106,6 +109,14 @@ export default {
   },
   mounted() {
     window.addEventListener("message", this.handleOnMessage);
+
+    setTimeout(() => {
+      const itemElm = document.querySelector(".topic_item");
+      if (itemElm) {
+        console.log(itemElm);
+        this.itemWidth = itemElm.clientWidth - 32 - 20 - 80;
+      }
+    }, 100);
   },
   unmounted() {
     window.removeEventListener("message", this.handleOnMessage);
@@ -154,7 +165,12 @@ export default {
             (obj) => obj.topicId === topicId
           );
           if (tarTopic) {
-            this.fetchTopicDetailActions({ topicId, id: tarTopic.id });
+            console.log({ tarTopic });
+            this.fetchTopicDetailActions({
+              topicId,
+              id: tarTopic.id,
+              uid: tarTopic.uid,
+            });
           }
         }
       }
@@ -166,7 +182,11 @@ export default {
       this.replyTarget = "";
       this.replyContent = "";
       this.replyObj = {};
-      this.fetchTopicDetailActions({ topicId: data.topicId, id: data.id });
+      this.fetchTopicDetailActions({
+        topicId: data.topicId,
+        id: data.id,
+        uid: data.uid,
+      });
     },
     handleOnReplyTopic(data) {
       this.replyTarget = "主题";
@@ -265,7 +285,7 @@ export default {
         flex: 1 0 0;
 
         .topic_content {
-          width: 260px;
+          width: 100px;
           overflow: hidden;
           white-space: nowrap;
           text-overflow: ellipsis;
diff --git a/stdiet-ui/src/views/custom/message/userInfo/index.vue b/stdiet-ui/src/views/custom/message/userInfo/index.vue
new file mode 100644
index 000000000..ba447aeb3
--- /dev/null
+++ b/stdiet-ui/src/views/custom/message/userInfo/index.vue
@@ -0,0 +1,38 @@
+<template>
+  <div v-loading="healthDataLoading">
+    <HealthyView
+      dev
+      :data="healthyDataType === 0 ? healthyData : {}"
+      v-show="healthyDataType === 0"
+    />
+    <BodySignView
+      dev
+      :data="healthyDataType === 1 ? healthyData : {}"
+      v-show="healthyDataType === 1"
+    />
+  </div>
+</template>
+<script>
+import { createNamespacedHelpers } from "vuex";
+import HealthyView from "@/components/HealthyView";
+import BodySignView from "@/components/BodySignView";
+const {
+  mapActions,
+  mapState,
+  mapMutations,
+  mapGetters,
+} = createNamespacedHelpers("message");
+export default {
+  name: "SignUserInfo",
+  components: {
+    HealthyView,
+    BodySignView,
+  },
+  data() {
+    return {};
+  },
+  computed: {
+    ...mapState(["healthyData", "healthyDataType", "healthDataLoading"]),
+  },
+};
+</script>

From 672ca49a55f5c2bf8e5fc5dfe77765d1c2d9e977 Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Thu, 3 Jun 2021 18:31:22 +0800
Subject: [PATCH 09/14] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 stdiet-admin/src/main/filters/local/application.yml            | 2 +-
 .../stdiet/custom/service/impl/SysServicesTopicServiceImp.java | 2 +-
 stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue  | 1 +
 stdiet-ui/src/views/custom/message/messageBrowser/index.vue    | 3 ++-
 4 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/stdiet-admin/src/main/filters/local/application.yml b/stdiet-admin/src/main/filters/local/application.yml
index 6932d7b76..b3bead739 100644
--- a/stdiet-admin/src/main/filters/local/application.yml
+++ b/stdiet-admin/src/main/filters/local/application.yml
@@ -83,7 +83,7 @@ spring:
     druid:
       # 主库数据源
       master:
-        url: jdbc:mysql://47.115.23.82:3306/stdiet_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+        url: jdbc:mysql://47.115.23.82:3306/stdiet?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
         password: gzDxPaZcSiXJpi2N
         username: root
       slave:
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysServicesTopicServiceImp.java b/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysServicesTopicServiceImp.java
index 941518c5b..1e57ffabd 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysServicesTopicServiceImp.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysServicesTopicServiceImp.java
@@ -171,7 +171,7 @@ public class SysServicesTopicServiceImp implements ISysServicesTopicService {
                 msgObj.put("type", WsUtils.WS_TYPE_NEW_CUSTOMER_REPLY);
                 msgObj.put("msg", "新客户回复");
                 msgObj.put("data", dataObj);
-                WebSocketServer.sendInfo(msgObj.toJSONString(), counts.get(i).getUid());
+                WebSocketServer.sendInfo(msgObj.toJSONString(), statusList.get(i).getUid());
             }
         } catch (IOException e) {
             e.printStackTrace();
diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue b/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue
index db43c3cd8..04f56f3e1 100644
--- a/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue
+++ b/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue
@@ -86,6 +86,7 @@ export default {
 
       .reply_btn {
         margin-left: 16px;
+        color: #1890ff;
         cursor: pointer;
       }
     }
diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
index 836d84e1a..2e90a4659 100644
--- a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
+++ b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
@@ -57,7 +57,7 @@
         <div
           :style="{ marginBottom: '8px', fontSize: '12px', color: '#8c8c8c' }"
         >
-          回复:{{ replyTarget }}
+          回复 to:{{ replyTarget }}
         </div>
         <el-input
           type="textarea"
@@ -339,6 +339,7 @@ export default {
 
         .reply_btn {
           margin-left: 16px;
+          color: #1890ff;
           cursor: pointer;
         }
       }

From 8a9a55ff8e0a0a088735abbab88be483e485292c Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Thu, 3 Jun 2021 18:47:17 +0800
Subject: [PATCH 10/14] =?UTF-8?q?=E5=85=88=E5=85=B3=E6=8E=89=E8=87=AA?=
 =?UTF-8?q?=E5=90=AF=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 stdiet-ui/src/utils/websocket.js | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/stdiet-ui/src/utils/websocket.js b/stdiet-ui/src/utils/websocket.js
index 62dde69ec..2cf24bf16 100644
--- a/stdiet-ui/src/utils/websocket.js
+++ b/stdiet-ui/src/utils/websocket.js
@@ -43,23 +43,23 @@ function connect() {
       ws = undefined;
       window.removeEventListener("message", handleOnMessageReceive);
 
-      websocketInit();
+      // websocketInit();
     };
 
     ws.onclose = event => {
       // console.log(event);
       ws = undefined;
       window.removeEventListener("message", handleOnMessageReceive);
-      if (event.reason !== "unmount") {
-        websocketInit();
-      }
+      // if (event.reason !== "unmount") {
+        // websocketInit();
+      // }
     };
   } catch (error) {
     // console.log(error);
     // console.log("浏览器不支持websocket");
     ws.close();
     ws = undefined;
-    websocketInit();
+    // websocketInit();
   }
 }
 

From 422431c77d3cf614873f39be6b3d3a33f25cfcf7 Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Fri, 4 Jun 2021 11:57:17 +0800
Subject: [PATCH 11/14] =?UTF-8?q?=E8=BF=9B=E7=B2=89=E7=BB=9F=E8=AE=A1?=
 =?UTF-8?q?=E5=AF=BC=E5=87=BA=E4=BF=AE=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../resources/mapper/custom/SysWxFanStatisticsMapper.xml  | 2 +-
 stdiet-ui/src/components/RecipesPlanDrawer/index.vue      | 7 +++----
 stdiet-ui/src/views/custom/customer/index.vue             | 8 ++++----
 3 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/stdiet-custom/src/main/resources/mapper/custom/SysWxFanStatisticsMapper.xml b/stdiet-custom/src/main/resources/mapper/custom/SysWxFanStatisticsMapper.xml
index 0e8f7664e..ce3a124cf 100644
--- a/stdiet-custom/src/main/resources/mapper/custom/SysWxFanStatisticsMapper.xml
+++ b/stdiet-custom/src/main/resources/mapper/custom/SysWxFanStatisticsMapper.xml
@@ -67,7 +67,7 @@
             </if>
         ) AS wfs ON wd.wechat_account = wfs.wx_id
         LEFT JOIN (SELECT id, wx_account, wx_phone FROM sys_wx_sale_account) AS wsa ON wd.wechat_account = wsa.id
-        LEFT JOIN (SELECT dict_label, dict_value FROM sys_dict_data WHERE dict_type = 'fan_channel') AS cn ON cn.dict_value = wd.account_id
+        LEFT JOIN (SELECT dict_label, dict_value FROM sys_dict_data WHERE dict_type = 'cus_account') AS cn ON cn.dict_value = wd.account_id
         LEFT JOIN sys_user AS su ON su.user_id = wd.user_id
         WHERE wd.del_flag = 0
         ORDER BY wd.sale_group_id, wd.user_id, wd.wechat_account ASC
diff --git a/stdiet-ui/src/components/RecipesPlanDrawer/index.vue b/stdiet-ui/src/components/RecipesPlanDrawer/index.vue
index 66535901b..60e7b60b1 100644
--- a/stdiet-ui/src/components/RecipesPlanDrawer/index.vue
+++ b/stdiet-ui/src/components/RecipesPlanDrawer/index.vue
@@ -75,14 +75,14 @@
             {{ `${scope.row.startDate} 至 ${scope.row.endDate}` }}
           </template>
         </el-table-column>
-        <el-table-column label="订阅情况" align="center">
+        <el-table-column label="订阅情况" align="center" width="80">
           <template slot-scope="scope">
             <el-tag :type="scope.row.subscribed ? 'success' : 'danger'">
               {{ scope.row.subscribed ? "已订阅" : "未订阅" }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="发送" align="center" width="80">
+        <el-table-column label="发送" align="center" width="60">
           <template slot-scope="scope">
             <el-switch
               v-model="!!scope.row.sendFlag"
@@ -90,7 +90,7 @@
             />
           </template>
         </el-table-column>
-        <el-table-column label="操作" align="center" width="160">
+        <el-table-column label="操作" align="center" width="140">
           <template slot-scope="scope">
             <el-button
               type="text"
@@ -102,7 +102,6 @@
               {{ `${scope.row.recipesId ? "编辑" : "制作"}` }}
             </el-button>
             <el-button
-              v-if="scope.row.reviewStatus === 1"
               type="text"
               icon="el-icon-delete"
               @click="handleOnDelete(scope.row)"
diff --git a/stdiet-ui/src/views/custom/customer/index.vue b/stdiet-ui/src/views/custom/customer/index.vue
index 9b3e48b51..48f628f07 100644
--- a/stdiet-ui/src/views/custom/customer/index.vue
+++ b/stdiet-ui/src/views/custom/customer/index.vue
@@ -246,7 +246,7 @@
         </template>
       </el-table-column>
       <el-table-column
-        label="外食热量统计"
+        label="外食热量"
         align="center"
         v-hasPermi="['custom:foodHeatStatistics:list']"
       >
@@ -413,8 +413,8 @@
               </el-select>
             </el-form-item>
           </el-col>
-          
-          
+
+
         </el-form>
       </el-row>
       <div slot="footer" class="dialog-footer">
@@ -798,7 +798,7 @@ export default {
         if(this.form.fansTime == undefined || this.form.fansTime == null){
             this.form.mainDietitian = null;
             return;
-        }      
+        }
         this.autoSelectNutritionist();
     },
     fanTimeAutoSelectNutritionist(fansTime){

From ea422f63f9cc542e427929d34d0ee956103d7dd2 Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Sat, 5 Jun 2021 15:05:32 +0800
Subject: [PATCH 12/14] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=95=8C=E9=9D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../custom/domain/SysServicesTopic.java       |   5 +
 .../mapper/custom/SysServicesTopicMapper.xml  |  19 +-
 stdiet-ui/src/store/modules/message.js        |  22 ++-
 stdiet-ui/src/views/custom/customer/index.vue | 171 ++++++++++--------
 .../custom/message/messageBrowser/Comment.vue |  17 +-
 .../custom/message/messageBrowser/index.vue   |  50 ++---
 6 files changed, 173 insertions(+), 111 deletions(-)

diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/domain/SysServicesTopic.java b/stdiet-custom/src/main/java/com/stdiet/custom/domain/SysServicesTopic.java
index 3ce77b0bd..ca31d6095 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/domain/SysServicesTopic.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/domain/SysServicesTopic.java
@@ -85,6 +85,11 @@ public class SysServicesTopic {
     String toName;
     String name;
 
+    String avatar;
+    String fromAvatar;
+    String toAvatar;
+
+
     List<SysServicesTopic> comments;
 
     List<SysServicesTopic> replys;
diff --git a/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml b/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
index a1dc84685..bfbd1d630 100644
--- a/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
+++ b/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
@@ -25,7 +25,7 @@
         SELECT * FROM (
         SELECT topic_id, id, `read`, create_time, update_time, 'customer' AS role FROM sys_services_topic_status WHERE role = #{role} AND uid = #{uid}
         ) AS status
-        LEFT JOIN sys_services_topic USING(topic_id)
+        LEFT JOIN sys_services_topic USING(topic_id) WHERE del_flag = 0
         ORDER BY `read` ASC, update_time DESC
     </select>
 
@@ -39,6 +39,7 @@
         <result column="img" property="img" typeHandler="com.stdiet.custom.typehandler.ArrayJsonHandler"/>
         <result column="create_time" property="createTime"/>
         <association property="name" column="{uid=uid,role=role}" select="selectUserInfo"/>
+        <association property="avatar" column="{uid=uid,role=role}" select="selectUserAvatar"/>
         <association property="comments" column="topic_id" select="selectServicesTopicCommentByTopicId"/>
     </resultMap>
 
@@ -53,7 +54,9 @@
         <result column="img" property="img" typeHandler="com.stdiet.custom.typehandler.ArrayJsonHandler"/>
         <result column="create_time" property="createTime"/>
         <association property="fromName" column="{uid=from_uid,role=from_role}" select="selectUserInfo"/>
+        <association property="fromAvatar" column="{uid=from_uid,role=from_role}" select="selectUserAvatar"/>
         <association property="toName" column="{uid=to_uid,role=to_role}" select="selectUserInfo"/>
+<!--        <association property="toAvatar" column="{uid=to_uid,role=to_role}" select="selectUserAvatar"/>-->
         <association property="replys" column="id"
                      select="selectServicesTopicCommentReplyByCommentId"/>
     </resultMap>
@@ -71,7 +74,9 @@
         <result column="img" property="img" typeHandler="com.stdiet.custom.typehandler.ArrayJsonHandler"/>
         <result column="create_time" property="createTime"/>
         <association property="fromName" column="{uid=from_uid,role=from_role}" select="selectUserInfo"/>
+        <association property="fromAvatar" column="{uid=from_uid,role=from_role}" select="selectUserAvatar"/>
         <association property="toName" column="{uid=to_uid,role=to_role}" select="selectUserInfo"/>
+<!--        <association property="toAvatar" column="{uid=to_uid,role=to_role}" select="selectUserAvatar"/>-->
     </resultMap>
 
 
@@ -103,6 +108,18 @@
         </choose>
     </select>
 
+    <!--    查询头像-->
+    <select id="selectUserAvatar" parameterType="java.util.Map" resultType="String">
+        <choose>
+            <when test="_parameter.get('role') == 'customer'">
+                select avatar_url from sys_wx_user_info where cus_id = #{uid}
+            </when>
+            <otherwise>
+                select IF(avatar != '', CONCAT("https://api.stdiet.top/prod-api", avatar), '') as avatar from sys_user where user_id = #{uid}
+            </otherwise>
+        </choose>
+    </select>
+
     <!--    插入问题-->
     <insert id="insertSysServicesTopic" parameterType="SysServicesTopic" useGeneratedKeys="true"
             keyProperty="id" keyColumn="id">
diff --git a/stdiet-ui/src/store/modules/message.js b/stdiet-ui/src/store/modules/message.js
index e4f2bcb64..e3643e1c5 100644
--- a/stdiet-ui/src/store/modules/message.js
+++ b/stdiet-ui/src/store/modules/message.js
@@ -75,7 +75,7 @@ const actions = {
     commit("save", { selTopicId: topicId });
     // 客户信息
     if (healthyData.customerId !== parseInt(uid)) {
-      dispatch("getHealthyData", { cusId: uid });
+      dispatch("getHealthyData", { cusId: uid, callback: payload.callback });
     }
     //
     const result = await fetchTopicDetail({ topicId, id });
@@ -116,22 +116,26 @@ const actions = {
     const healthyDataResult = await getCustomerPhysicalSignsByCusId(
       payload.cusId
     );
-    let healthyData = undefined,
-      healthyDataType = 0;
+    const newState = {};
     if (healthyDataResult.code === 200) {
       if (!healthyDataResult.data.customerHealthy) {
-        throw new Error("客户还没填写健康评估表");
+        // throw new Error("客户还没填写健康评估表");
+        payload.callback && payload.callback("客户还没填写健康评估表");
+      } else {
+        newState.healthyDataType = healthyDataResult.data.type;
+        newState.healthyData = dealHealthy(
+          healthyDataResult.data.customerHealthy
+        );
+        newState.avoidFoodIds = (newState.healthyData.avoidFood || []).map(
+          obj => obj.id
+        );
       }
-      healthyDataType = healthyDataResult.data.type;
-      healthyData = dealHealthy(healthyDataResult.data.customerHealthy);
     } else {
       throw new Error(healthyDataResult.msg);
     }
     commit("save", {
       healthDataLoading: false,
-      healthyDataType,
-      healthyData,
-      avoidFoodIds: (healthyData.avoidFood || []).map(obj => obj.id)
+      ...newState
     });
   }
 };
diff --git a/stdiet-ui/src/views/custom/customer/index.vue b/stdiet-ui/src/views/custom/customer/index.vue
index 48f628f07..217efab90 100644
--- a/stdiet-ui/src/views/custom/customer/index.vue
+++ b/stdiet-ui/src/views/custom/customer/index.vue
@@ -27,7 +27,12 @@
         </el-select>
       </el-form-item> -->
       <el-form-item label="进粉渠道" prop="channelId">
-        <el-select v-model="queryParams.channelId" filterable clearable placeholder="请选择">
+        <el-select
+          v-model="queryParams.channelId"
+          filterable
+          clearable
+          placeholder="请选择"
+        >
           <el-option
             v-for="dict in accountIdOptions"
             :key="dict.dictValue"
@@ -84,24 +89,24 @@
         </el-select>
       </el-form-item>
 
-    <el-form-item label="病史体征" prop="physicalSignsId">
-          <el-select
-            v-model="queryParams.physicalSignsId"
-            filterable
-            clearable
-            allow-create
-            default-first-option
-            placeholder="请选择病史体征"
+      <el-form-item label="病史体征" prop="physicalSignsId">
+        <el-select
+          v-model="queryParams.physicalSignsId"
+          filterable
+          clearable
+          allow-create
+          default-first-option
+          placeholder="请选择病史体征"
+        >
+          <el-option
+            v-for="physicalSign in physicalSignsList"
+            :key="physicalSign.id"
+            :label="physicalSign.name"
+            :value="physicalSign.id"
           >
-            <el-option
-              v-for="physicalSign in physicalSignsList"
-              :key="physicalSign.id"
-              :label="physicalSign.name"
-              :value="physicalSign.id"
-            >
-            </el-option>
-          </el-select>
-        </el-form-item>
+          </el-option>
+        </el-select>
+      </el-form-item>
 
       <el-form-item>
         <el-button type="cyan" icon="el-icon-search" @click="handleQuery"
@@ -330,8 +335,18 @@
       <el-row :gutter="15">
         <el-form ref="form" :model="form" :rules="rules" label-width="100px">
           <el-col :span="12">
-            <el-form-item label="进粉渠道" prop="channelId" style="width:400px">
-              <el-select v-model="form.channelId" placeholder="请选择" filterable clearable @change="channelAutoSelectNutritionist">
+            <el-form-item
+              label="进粉渠道"
+              prop="channelId"
+              style="width: 400px"
+            >
+              <el-select
+                v-model="form.channelId"
+                placeholder="请选择"
+                filterable
+                clearable
+                @change="channelAutoSelectNutritionist"
+              >
                 <el-option
                   v-for="dict in accountIdOptions"
                   :key="dict.dictValue"
@@ -356,12 +371,12 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="客户名字" prop="name" style="width:300px">
+            <el-form-item label="客户名字" prop="name" style="width: 300px">
               <el-input v-model.trim="form.name" placeholder="请输入名字" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="手机号" prop="phone" style="width:300px">
+            <el-form-item label="手机号" prop="phone" style="width: 300px">
               <el-input v-model.trim="form.phone" placeholder="请输入手机号" />
             </el-form-item>
           </el-col>
@@ -413,8 +428,6 @@
               </el-select>
             </el-form-item>
           </el-col>
-
-
         </el-form>
       </el-row>
       <div slot="footer" class="dialog-footer">
@@ -508,7 +521,7 @@ export default {
         assistantDietitian: null,
         afterDietitian: null,
         salesman: null,
-        physicalSignsId: null
+        physicalSignsId: null,
       },
       // 表单参数
       form: {},
@@ -551,7 +564,7 @@ export default {
         },
       },
       //病史体征
-      physicalSignsList:[]
+      physicalSignsList: [],
     };
   },
   created() {
@@ -573,8 +586,8 @@ export default {
       }
     });
     this.getList();
-    listPhysicalSigns().then(response => {
-          this.physicalSignsList = response.rows;
+    listPhysicalSigns().then((response) => {
+      this.physicalSignsList = response.rows;
     });
   },
   computed: {
@@ -789,52 +802,68 @@ export default {
         })
         .catch(function () {});
     },
-    channelAutoSelectNutritionist(channelValue){
-        this.form.fansChannel = channelValue == "" ? null : channelValue;
-        if(channelValue == undefined || channelValue == null || channelValue == ""){
-            this.form.mainDietitian = null;
-            return;
-        }
-        if(this.form.fansTime == undefined || this.form.fansTime == null){
-            this.form.mainDietitian = null;
-            return;
-        }
-        this.autoSelectNutritionist();
+    channelAutoSelectNutritionist(channelValue) {
+      this.form.fansChannel = channelValue == "" ? null : channelValue;
+      if (
+        channelValue == undefined ||
+        channelValue == null ||
+        channelValue == ""
+      ) {
+        this.form.mainDietitian = null;
+        return;
+      }
+      if (this.form.fansTime == undefined || this.form.fansTime == null) {
+        this.form.mainDietitian = null;
+        return;
+      }
+      this.autoSelectNutritionist();
     },
-    fanTimeAutoSelectNutritionist(fansTime){
-        this.form.fansTime = fansTime;
-        if(fansTime == undefined || fansTime == null){
-            this.form.mainDietitian = null;
-            return;
-        }
-        if(this.form.fansChannel == undefined || this.form.fansChannel == null || this.form.fansChannel == ""){
-            this.form.mainDietitian = null;
-            return;
-        }
-        this.autoSelectNutritionist();
+    fanTimeAutoSelectNutritionist(fansTime) {
+      this.form.fansTime = fansTime;
+      if (fansTime == undefined || fansTime == null) {
+        this.form.mainDietitian = null;
+        return;
+      }
+      if (
+        this.form.fansChannel == undefined ||
+        this.form.fansChannel == null ||
+        this.form.fansChannel == ""
+      ) {
+        this.form.mainDietitian = null;
+        return;
+      }
+      this.autoSelectNutritionist();
     },
-    autoSelectNutritionist(){
-        getLiveSchedulByTime({'fanChannel':this.form.fansChannel,'liveStartTimeString':encodeURIComponent(this.form.fansTime)}).then((response) => {
-              if (response.code === 200) {
-                 let live = response.data;
-                 if(live != undefined && live != null && live.liveNutritionistId != null && this.nutritionistIdOptions != null){
-                    let mainDietitian = null;
-                     this.nutritionistIdOptions.forEach((item,index) => {
-                        if(live.liveNutritionistId == item.dictValue){
-                           mainDietitian = live.liveNutritionistId;
-                        }
-                        if(index == this.nutritionistIdOptions.length - 1){
-                          this.form.mainDietitian = mainDietitian;
-                        }
-                     });
-                 }else{
-                   this.form.mainDietitian = null;
-                 }
-              }else{
-                this.form.mainDietitian = null;
+    autoSelectNutritionist() {
+      getLiveSchedulByTime({
+        fanChannel: this.form.fansChannel,
+        liveStartTimeString: encodeURIComponent(this.form.fansTime),
+      }).then((response) => {
+        if (response.code === 200) {
+          let live = response.data;
+          if (
+            live != undefined &&
+            live != null &&
+            live.liveNutritionistId != null &&
+            this.nutritionistIdOptions != null
+          ) {
+            let mainDietitian = null;
+            this.nutritionistIdOptions.forEach((item, index) => {
+              if (live.liveNutritionistId == item.dictValue) {
+                mainDietitian = live.liveNutritionistId;
               }
-        });
-    }
+              if (index == this.nutritionistIdOptions.length - 1) {
+                this.form.mainDietitian = mainDietitian;
+              }
+            });
+          } else {
+            this.form.mainDietitian = null;
+          }
+        } else {
+          this.form.mainDietitian = null;
+        }
+      });
+    },
   },
 };
 </script>
diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue b/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue
index 04f56f3e1..94a390e71 100644
--- a/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue
+++ b/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue
@@ -1,7 +1,9 @@
 <template>
-  <div class="topic_comment_item">
+  <div class="topic_comment_item" @click="handOnClick(data)">
     <div class="comment_avatar">
-      <el-avatar size="medium">{{ data.fromName.substr(-1) }}</el-avatar>
+      <el-avatar size="medium" :src="data.fromAvatar || ''">{{
+        data.fromName.substr(-1)
+      }}</el-avatar>
     </div>
     <div class="comment_content">
       <div class="content_title">
@@ -10,11 +12,7 @@
       <div class="content_type">{{ data.content }}</div>
       <div class="content_time">
         {{ formatDate(data.createTime) }}
-        <div
-          v-if="data.fromUid !== userId.toString()"
-          class="reply_btn"
-          @click="handOnClick(data)"
-        >
+        <div v-if="data.fromUid !== userId.toString()" class="reply_btn">
           回复
         </div>
       </div>
@@ -30,7 +28,7 @@ export default {
     return {
       roleDict: {
         customer: "客户",
-        dietician: "主营养师",
+        dietician: "主任营养师",
         after_sale: "售后营养师",
         dietician_assistant: "营养师助理",
       },
@@ -61,6 +59,7 @@ export default {
 .topic_comment_item {
   margin: 12px;
   display: flex;
+  cursor: pointer;
 
   .comment_avatar {
     flex: 0 0 36px;
@@ -87,7 +86,7 @@ export default {
       .reply_btn {
         margin-left: 16px;
         color: #1890ff;
-        cursor: pointer;
+        // cursor: pointer;
       }
     }
   }
diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
index 2e90a4659..dac7a0d1b 100644
--- a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
+++ b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue
@@ -31,12 +31,19 @@
     </div>
     <div class="topic_detail">
       <div class="topic_detail_list">
-        <div class="topic_detail_title" v-if="!!detailData.content">
-          <div>{{ detailData.content }}</div>
-          <div class="content_time" :style="{ marginTop: '4px' }">
-            {{ formatDate(detailData.createTime) }}
-            <div class="reply_btn" @click="handleOnReplyTopic(detailData)">
-              回复
+        <div
+          class="topic_detail_title"
+          v-if="!!detailData.content"
+          @click="handleOnReplyTopic(detailData)"
+        >
+          <el-avatar :src="detailData.avatar">{{
+            detailData.name.substr(-1)
+          }}</el-avatar>
+          <div :style="{ marginLeft: '8px' }">
+            <div>{{ detailData.content }}</div>
+            <div class="content_time" :style="{ marginTop: '4px' }">
+              {{ formatDate(detailData.createTime) }}
+              <div class="reply_btn">回复</div>
             </div>
           </div>
         </div>
@@ -83,12 +90,8 @@ import { createNamespacedHelpers, mapActions as globalMapActions } from "vuex";
 import Comment from "./Comment";
 import dayjs from "dayjs";
 import { keys } from "@/utils/websocket";
-const {
-  mapActions,
-  mapState,
-  mapMutations,
-  mapGetters,
-} = createNamespacedHelpers("message");
+const { mapActions, mapState, mapMutations, mapGetters } =
+  createNamespacedHelpers("message");
 export default {
   data() {
     return {
@@ -179,14 +182,17 @@ export default {
       return dayjs(date).format("MM-DD HH:mm");
     },
     handleOnTopicClick(data) {
-      this.replyTarget = "";
-      this.replyContent = "";
-      this.replyObj = {};
-      this.fetchTopicDetailActions({
-        topicId: data.topicId,
-        id: data.id,
-        uid: data.uid,
-      });
+      if (data.topicId !== this.selTopicId) {
+        this.replyTarget = "";
+        this.replyContent = "";
+        this.replyObj = {};
+        this.fetchTopicDetailActions({
+          topicId: data.topicId,
+          id: data.id,
+          uid: data.uid,
+          callback: (err) => this.$message.error(err),
+        });
+      }
     },
     handleOnReplyTopic(data) {
       this.replyTarget = "主题";
@@ -340,11 +346,13 @@ export default {
         .reply_btn {
           margin-left: 16px;
           color: #1890ff;
-          cursor: pointer;
+          // cursor: pointer;
         }
       }
 
       .topic_detail_title {
+        display: flex;
+        cursor: pointer;
       }
 
       .comment_reply_item {

From e452f5a90442a7978864e9a2e029ca484882840b Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Sat, 5 Jun 2021 15:38:10 +0800
Subject: [PATCH 13/14] =?UTF-8?q?=E7=AE=A1=E7=90=86=E8=80=85=E8=A7=86?=
 =?UTF-8?q?=E8=A7=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../mapper/custom/SysServicesTopicMapper.xml  | 28 +++++++++++++------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml b/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
index bfbd1d630..11075cdef 100644
--- a/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
+++ b/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
@@ -22,11 +22,22 @@
     <!--    根据userId和角色查询问题列表-->
     <select id="selectSysServicesTopicByUserIdAndRole" parameterType="SysServicesTopic"
             resultMap="SysServicesTopicResult">
-        SELECT * FROM (
-        SELECT topic_id, id, `read`, create_time, update_time, 'customer' AS role FROM sys_services_topic_status WHERE role = #{role} AND uid = #{uid}
-        ) AS status
-        LEFT JOIN sys_services_topic USING(topic_id) WHERE del_flag = 0
-        ORDER BY `read` ASC, update_time DESC
+        <choose>
+            <when test="role == 'admin' or role == 'manager'">
+                select topic_id, topic_type, content, uid, create_time, img, 1 as `read`, '0' as id, 'customer' as role
+                from sys_services_topic where del_flag = 0 order by create_time desc
+            </when>
+            <otherwise>
+                SELECT * FROM (
+                SELECT topic_id, id, `read`, create_time, update_time, 'customer' AS role FROM sys_services_topic_status
+                WHERE role = #{role} AND uid = #{uid}
+                ) AS status
+                LEFT JOIN sys_services_topic USING(topic_id) WHERE del_flag = 0
+                ORDER BY `read` ASC, update_time DESC
+            </otherwise>
+        </choose>
+
+
     </select>
 
     <!--    查询主题-->
@@ -56,7 +67,7 @@
         <association property="fromName" column="{uid=from_uid,role=from_role}" select="selectUserInfo"/>
         <association property="fromAvatar" column="{uid=from_uid,role=from_role}" select="selectUserAvatar"/>
         <association property="toName" column="{uid=to_uid,role=to_role}" select="selectUserInfo"/>
-<!--        <association property="toAvatar" column="{uid=to_uid,role=to_role}" select="selectUserAvatar"/>-->
+        <!--        <association property="toAvatar" column="{uid=to_uid,role=to_role}" select="selectUserAvatar"/>-->
         <association property="replys" column="id"
                      select="selectServicesTopicCommentReplyByCommentId"/>
     </resultMap>
@@ -76,7 +87,7 @@
         <association property="fromName" column="{uid=from_uid,role=from_role}" select="selectUserInfo"/>
         <association property="fromAvatar" column="{uid=from_uid,role=from_role}" select="selectUserAvatar"/>
         <association property="toName" column="{uid=to_uid,role=to_role}" select="selectUserInfo"/>
-<!--        <association property="toAvatar" column="{uid=to_uid,role=to_role}" select="selectUserAvatar"/>-->
+        <!--        <association property="toAvatar" column="{uid=to_uid,role=to_role}" select="selectUserAvatar"/>-->
     </resultMap>
 
 
@@ -115,7 +126,8 @@
                 select avatar_url from sys_wx_user_info where cus_id = #{uid}
             </when>
             <otherwise>
-                select IF(avatar != '', CONCAT("https://api.stdiet.top/prod-api", avatar), '') as avatar from sys_user where user_id = #{uid}
+                select IF(avatar != '', CONCAT("https://api.stdiet.top/prod-api", avatar), '') as avatar from sys_user
+                where user_id = #{uid}
             </otherwise>
         </choose>
     </select>

From f6d1151db22159f5aa91e97e285971e0b7f801a8 Mon Sep 17 00:00:00 2001
From: huangdeliang <huangdeliang@skieer.com>
Date: Sat, 5 Jun 2021 15:42:24 +0800
Subject: [PATCH 14/14] =?UTF-8?q?=E7=AE=A1=E7=90=86=E8=80=85=E8=A7=86?=
 =?UTF-8?q?=E8=A7=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/main/resources/mapper/custom/SysServicesTopicMapper.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml b/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
index 11075cdef..4afeb6edb 100644
--- a/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
+++ b/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml
@@ -23,7 +23,7 @@
     <select id="selectSysServicesTopicByUserIdAndRole" parameterType="SysServicesTopic"
             resultMap="SysServicesTopicResult">
         <choose>
-            <when test="role == 'admin' or role == 'manager'">
+            <when test="role == 'admin' or role == 'manager' or role == 'admin-dev'">
                 select topic_id, topic_type, content, uid, create_time, img, 1 as `read`, '0' as id, 'customer' as role
                 from sys_services_topic where del_flag = 0 order by create_time desc
             </when>