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>