完善交互

This commit is contained in:
huangdeliang 2021-06-03 10:36:21 +08:00
parent 953d188f50
commit ea8276e6a0
17 changed files with 348 additions and 113 deletions

View File

@ -3,11 +3,10 @@ package com.stdiet.web.controller.custom;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.stdiet.common.core.controller.BaseController; import com.stdiet.common.core.controller.BaseController;
import com.stdiet.custom.utils.WsUtils; 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.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller; 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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -39,16 +38,16 @@ public class WebSocketController extends BaseController {
} }
@Scheduled(fixedRate = 30000) // @Scheduled(fixedRate = 30000)
public void boardCast() { // public void boardCast() {
try { // try {
JSONObject heartBeat = new JSONObject(); // JSONObject heartBeat = new JSONObject();
heartBeat.put("type", WsUtils.WS_TYPE_HEART_BEAT); // heartBeat.put("type", WsUtils.WS_TYPE_HEART_BEAT);
heartBeat.put("msg", "ping"); // heartBeat.put("msg", "ping");
//
WebSocketServer.sendInfo(heartBeat.toJSONString(), null); // WebSocketServer.sendInfo(heartBeat.toJSONString(), null);
} catch (IOException e) { // } catch (IOException e) {
e.printStackTrace(); // e.printStackTrace();
} // }
} // }
} }

View File

@ -99,6 +99,11 @@
<artifactId>ikanalyzer</artifactId> <artifactId>ikanalyzer</artifactId>
<version>2012_u6</version> <version>2012_u6</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -89,4 +89,6 @@ public class SysServicesTopic {
List<SysServicesTopic> replys; List<SysServicesTopic> replys;
Integer count;
} }

View File

@ -18,4 +18,6 @@ public interface SysServicesTopicMapper {
int inserSysServicesTopicReply(SysServicesTopic topic); int inserSysServicesTopicReply(SysServicesTopic topic);
List<SysServicesTopic> selectSysServicesTopicSessionByTopicId(String topicId); List<SysServicesTopic> selectSysServicesTopicSessionByTopicId(String topicId);
List<SysServicesTopic> selectUnreadTopicCount(List<SysServicesTopic> topics);
} }

View File

@ -1,7 +1,10 @@
package com.stdiet.web.server; package com.stdiet.custom.server;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.stdiet.common.core.domain.model.LoginUser; 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 com.stdiet.custom.utils.WsUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@ -10,15 +13,17 @@ import org.springframework.stereotype.Component;
import javax.websocket.*; import javax.websocket.*;
import javax.websocket.server.ServerEndpoint; import javax.websocket.server.ServerEndpoint;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
@ServerEndpoint(value = "/ws") @ServerEndpoint(value = "/ws")
@Component @Component
@Slf4j @Slf4j
public class WebSocketServer { public class WebSocketServer {
// concurrent包的线程安全Set用来存放每个客户端对应的MyWebSocket对象 // concurrent包的线程安全Set用来存放每个客户端对应的MyWebSocket对象
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>(); private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
//private static ConcurrentHashMap<String,WebSocketServer> websocketList = new ConcurrentHashMap<>(); //private static ConcurrentHashMap<String,WebSocketServer> websocketList = new ConcurrentHashMap<>();
// 与某个客户端的连接会话需要通过它来给客户端发送数据 // 与某个客户端的连接会话需要通过它来给客户端发送数据
private Session session; private Session session;
@ -74,23 +79,6 @@ public class WebSocketServer {
log.info("有一连接关闭!"); 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 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();
}
}
} }

View File

@ -17,4 +17,6 @@ public interface ISysServicesTopicService {
SysServicesTopic inserSysServicesTopicComment(SysServicesTopic topic); SysServicesTopic inserSysServicesTopicComment(SysServicesTopic topic);
List<SysServicesTopic> selectSysServicesTopicSessionByTopicId(String topicId); List<SysServicesTopic> selectSysServicesTopicSessionByTopicId(String topicId);
List<SysServicesTopic> selectUnreadTopicCount(List<SysServicesTopic> topic);
} }

View File

@ -1,15 +1,19 @@
package com.stdiet.custom.service.impl; package com.stdiet.custom.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.stdiet.common.utils.DateUtils; import com.stdiet.common.utils.DateUtils;
import com.stdiet.common.utils.uuid.UUID; import com.stdiet.common.utils.uuid.UUID;
import com.stdiet.custom.domain.SysCustomer; import com.stdiet.custom.domain.SysCustomer;
import com.stdiet.custom.domain.SysServicesTopic; import com.stdiet.custom.domain.SysServicesTopic;
import com.stdiet.custom.mapper.SysCustomerMapper; import com.stdiet.custom.mapper.SysCustomerMapper;
import com.stdiet.custom.mapper.SysServicesTopicMapper; import com.stdiet.custom.mapper.SysServicesTopicMapper;
import com.stdiet.custom.server.WebSocketServer;
import com.stdiet.custom.service.ISysServicesTopicService; import com.stdiet.custom.service.ISysServicesTopicService;
import com.stdiet.custom.utils.WsUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -40,35 +44,58 @@ public class SysServicesTopicServiceImp implements ISysServicesTopicService {
List<SysServicesTopic> statusList = new ArrayList<>(); List<SysServicesTopic> statusList = new ArrayList<>();
String customerId = String.valueOf(customer.getId());
SysServicesTopic customerStatus = new SysServicesTopic(); SysServicesTopic customerStatus = new SysServicesTopic();
customerStatus.setUid(String.valueOf(customer.getId())); customerStatus.setUid(customerId);
customerStatus.setRole("customer"); customerStatus.setRole("customer");
customerStatus.setRead(1); customerStatus.setRead(1);
customerStatus.setTopicId(topic.getTopicId()); customerStatus.setTopicId(topic.getTopicId());
statusList.add(customerStatus); statusList.add(customerStatus);
String dieticianId = String.valueOf(customer.getMainDietitian());
SysServicesTopic dieticianStatus = new SysServicesTopic(); SysServicesTopic dieticianStatus = new SysServicesTopic();
dieticianStatus.setUid(String.valueOf(customer.getMainDietitian())); dieticianStatus.setUid(dieticianId);
dieticianStatus.setRole("dietician"); dieticianStatus.setRole("dietician");
dieticianStatus.setRead(0); dieticianStatus.setRead(0);
dieticianStatus.setTopicId(topic.getTopicId()); dieticianStatus.setTopicId(topic.getTopicId());
statusList.add(dieticianStatus); statusList.add(dieticianStatus);
String afterSaleId = String.valueOf(customer.getAfterDietitian());
SysServicesTopic afterSaleStatus = new SysServicesTopic(); SysServicesTopic afterSaleStatus = new SysServicesTopic();
afterSaleStatus.setUid(String.valueOf(customer.getAfterDietitian())); afterSaleStatus.setUid(afterSaleId);
afterSaleStatus.setRole("after_sale"); afterSaleStatus.setRole("after_sale");
afterSaleStatus.setRead(0); afterSaleStatus.setRead(0);
afterSaleStatus.setTopicId(topic.getTopicId()); afterSaleStatus.setTopicId(topic.getTopicId());
statusList.add(afterSaleStatus); statusList.add(afterSaleStatus);
String dieticianAssistantId = String.valueOf(customer.getAssistantDietitian());
SysServicesTopic dieticianAssistantStatus = new SysServicesTopic(); SysServicesTopic dieticianAssistantStatus = new SysServicesTopic();
dieticianAssistantStatus.setUid(String.valueOf(customer.getAssistantDietitian())); dieticianAssistantStatus.setUid(dieticianAssistantId);
dieticianAssistantStatus.setRole("dietician_assistant"); dieticianAssistantStatus.setRole("dietician_assistant");
dieticianAssistantStatus.setRead(0); dieticianAssistantStatus.setRead(0);
dieticianAssistantStatus.setTopicId(topic.getTopicId()); dieticianAssistantStatus.setTopicId(topic.getTopicId());
statusList.add(dieticianAssistantStatus); 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.setId(customerStatus.getId());
topic.setUid(null); topic.setUid(null);
@ -94,10 +121,13 @@ public class SysServicesTopicServiceImp implements ISysServicesTopicService {
status.setTopicId(topic.getTopicId()); status.setTopicId(topic.getTopicId());
status.setRole(topic.getRole()); status.setRole(topic.getRole());
servicesTopicMapper.updateSysServicesTopicStatus(status); servicesTopicMapper.updateSysServicesTopicStatus(status);
afterReply(topic);
} }
return topic; return topic;
} }
@Override @Override
public SysServicesTopic inserSysServicesTopicComment(SysServicesTopic topic) { public SysServicesTopic inserSysServicesTopicComment(SysServicesTopic topic) {
String uuid = java.util.UUID.randomUUID().toString().replace("-", ""); String uuid = java.util.UUID.randomUUID().toString().replace("-", "");
@ -110,12 +140,51 @@ public class SysServicesTopicServiceImp implements ISysServicesTopicService {
status.setTopicId(topic.getTopicId()); status.setTopicId(topic.getTopicId());
status.setRole(topic.getRole()); status.setRole(topic.getRole());
servicesTopicMapper.updateSysServicesTopicStatus(status); servicesTopicMapper.updateSysServicesTopicStatus(status);
afterReply(topic);
} }
return 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 @Override
public List<SysServicesTopic> selectSysServicesTopicSessionByTopicId(String topicId) { public List<SysServicesTopic> selectSysServicesTopicSessionByTopicId(String topicId) {
return servicesTopicMapper.selectSysServicesTopicSessionByTopicId(topicId); return servicesTopicMapper.selectSysServicesTopicSessionByTopicId(topicId);
} }
@Override
public List<SysServicesTopic> selectUnreadTopicCount(List<SysServicesTopic> statusList) {
return servicesTopicMapper.selectUnreadTopicCount(statusList);
}
} }

View File

@ -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_HEART_BEAT = "WS_TYPE_HEART_BEAT";
public static final String WS_TYPE_SYSTEM_MESSAGE = "WS_TYPE_SYSTEM_MESSAGE"; 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";
} }

View File

@ -225,5 +225,23 @@
</trim> </trim>
</insert> </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> </mapper>

View File

@ -11,26 +11,11 @@
<div class="right-menu"> <div class="right-menu">
<template v-if="device !== 'mobile'"> <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 <div
class="right-menu-item hover-effect badge_style" class="right-menu-item hover-effect badge_style"
@click="handleOnMessageClick" @click="handleOnMessageClick"
> >
<el-badge :value="12"> <el-badge :value="msgUnreadCount">
<el-tooltip content="消息" effect="dark" placement="bottom"> <el-tooltip content="消息" effect="dark" placement="bottom">
<em class="el-icon-message" :style="{ fontSize: '28px' }" /> <em class="el-icon-message" :style="{ fontSize: '28px' }" />
</el-tooltip> </el-tooltip>
@ -66,25 +51,30 @@
import { mapGetters, mapActions } from "vuex"; import { mapGetters, mapActions } from "vuex";
import Breadcrumb from "@/components/Breadcrumb"; import Breadcrumb from "@/components/Breadcrumb";
import Hamburger from "@/components/Hamburger"; import Hamburger from "@/components/Hamburger";
// import Screenfull from "@/components/Screenfull"; import { keys } from "@/utils/websocket";
// import SizeSelect from "@/components/SizeSelect";
// import Search from "@/components/HeaderSearch";
// import RuoYiGit from "@/components/RuoYi/Git";
// import RuoYiDoc from "@/components/RuoYi/Doc";
export default { export default {
components: { components: {
Breadcrumb, Breadcrumb,
Hamburger, Hamburger,
// Screenfull, },
// SizeSelect, data() {
// Search, return {
msgNum: 0,
};
}, },
created() { created() {
this.init(); 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: { computed: {
...mapGetters(["sidebar", "avatar", "device"]), ...mapGetters(["sidebar", "avatar", "device", "msgUnreadCount"]),
setting: { setting: {
get() { get() {
return this.$store.state.settings.showSettings; return this.$store.state.settings.showSettings;
@ -98,7 +88,14 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions(["init"]), ...mapActions(["init", "updateUnreadCount"]),
handleOnMessage({ data }) {
if (data.type === keys.WS_TYPE_MESSAGE_COUNT) {
this.updateUnreadCount({
msgUnreadCount: data.data.count,
});
}
},
toggleSideBar() { toggleSideBar() {
this.$store.dispatch("app/toggleSideBar"); this.$store.dispatch("app/toggleSideBar");
}, },

View File

@ -26,6 +26,7 @@ import { init } from "@/utils/websocket";
import exportingInit from "highcharts/modules/exporting"; import exportingInit from "highcharts/modules/exporting";
exportingInit(Highcharts); exportingInit(Highcharts);
// websocket 初始化
init(); init();
import { import {

View File

@ -1,6 +1,9 @@
const actions = { const actions = {
async init({ dispatch }, payload) { async init({ dispatch }, payload) {
dispatch("global/init", payload); dispatch("global/init", payload);
},
async updateUnreadCount({ commit }, payload) {
commit("global/save", payload);
} }
}; };

View File

@ -9,12 +9,11 @@ const getters = {
name: state => state.user.name, name: state => state.user.name,
introduction: state => state.user.introduction, introduction: state => state.user.introduction,
roles: state => state.user.roles, roles: state => state.user.roles,
// roles: state => ["dietician"],
permissions: state => state.user.permissions, permissions: state => state.user.permissions,
userId: state => state.user.userId, userId: state => state.user.userId,
// userId: state => 131,
userRemark: state => state.user.remark, userRemark: state => state.user.remark,
permission_routes: state => state.permission.routes, permission_routes: state => state.permission.routes,
msgUnreadCount: state => state.global.msgUnreadCount,
// //
nutritionistIdOptions: state => state.global.nutritionistIdOptions, nutritionistIdOptions: state => state.global.nutritionistIdOptions,
nutriAssisIdOptions: state => state.global.nutriAssisIdOptions, nutriAssisIdOptions: state => state.global.nutriAssisIdOptions,

View File

@ -9,7 +9,8 @@ const oriState = {
plannerIdOptions: [], plannerIdOptions: [],
plannerAssisIdOptions: [], plannerAssisIdOptions: [],
operatorIdOptions: [], operatorIdOptions: [],
operatorAssisIdOptions: [] operatorAssisIdOptions: [],
msgUnreadCount: 0,
}; };
const mutations = { const mutations = {

View File

@ -6,6 +6,7 @@ import {
} from "@/api/custom/message"; } from "@/api/custom/message";
const oriState = { const oriState = {
pageNum: 1,
topicList: [], topicList: [],
detailData: {}, detailData: {},
selTopicId: "" selTopicId: ""
@ -28,23 +29,35 @@ const mutations = {
}; };
const actions = { const actions = {
async init({ rootGetters, commit, dispatch }, payload) { async init({ dispatch }, payload) {
dispatch("fetchTopicListApi", {});
},
async fetchTopicListApi({ dispatch, commit, rootGetters, state }, payload) {
const { const {
roles: [role], roles: [role],
userId userId
} = rootGetters; } = 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) { if (result.code === 200) {
const [defTopic] = result.rows; if (!detailData.topicId) {
const [defTopic] = result.rows;
dispatch("fetchTopicDetailActions", { dispatch("fetchTopicDetailActions", {
topicId: defTopic.topicId, topicId: defTopic.topicId,
id: defTopic.id id: defTopic.id
}); });
}
commit("save", { if (result.rows.length) {
topicList: result.rows commit("save", {
}); pageNum: pageNum + 1,
topicList: [...topicList, ...result.rows]
});
}
} }
}, },
async fetchTopicDetailActions({ commit }, payload) { async fetchTopicDetailActions({ commit }, payload) {

View File

@ -6,8 +6,10 @@ const url = `${wsProtocol}://${hostname}${
let ws = undefined; let ws = undefined;
let intervalRef = undefined; let intervalRef = undefined;
function handleOnMessageReceive(event) { function handleOnMessageReceive({ data = {} }) {
console.log({ event }); if (data.type && data.type === keys.GET_UNREAD_COUNT) {
ws.send(keys.GET_UNREAD_COUNT);
}
} }
function connect() { function connect() {
@ -18,11 +20,15 @@ function connect() {
console.log("ws连接成功"); console.log("ws连接成功");
intervalRef && clearInterval(intervalRef); intervalRef && clearInterval(intervalRef);
document.addEventListener("message", handleOnMessageReceive); setInterval(() => {
ws.send("ping");
}, 30000);
window.addEventListener("message", handleOnMessageReceive);
}; };
ws.onmessage = event => { ws.onmessage = event => {
console.log({ event }); // console.log({ event });
const dataObj = JSON.parse(event.data || "{}"); const dataObj = JSON.parse(event.data || "{}");
@ -32,24 +38,35 @@ function connect() {
}; };
ws.onerror = event => { ws.onerror = event => {
console.log({ event }); // console.log({ event });
ws = undefined; ws = undefined;
document.removeEventListener("message", handleOnMessageReceive); window.removeEventListener("message", handleOnMessageReceive);
}; };
ws.onclose = event => { ws.onclose = event => {
ws = undefined; ws = undefined;
document.removeEventListener("message", handleOnMessageReceive); window.removeEventListener("message", handleOnMessageReceive);
}; };
} catch (error) { } catch (error) {
console.log(error); // console.log(error);
ws = undefined;
init();
console.log("浏览器不支持websocket"); console.log("浏览器不支持websocket");
} }
} }
export function init() { export function init() {
intervalRef = setInterval(() => { !ws && connect();
console.log("尝试连接websocket");
!ws && connect(); !ws &&
}, 10000); (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"
};

View File

@ -1,28 +1,35 @@
<template> <template>
<div class="message_browser_wrapper"> <div class="message_browser_wrapper">
<div class="topic_list"> <div class="topic_list" @scroll="handleOnScroll">
<div <div v-if="topicList && topicList.length">
v-for="topic in topicList" <div
:key="topic.topicId" v-for="topic in topicList"
:class="`topic_item ${ :key="topic.topicId"
selTopicId === topic.topicId ? 'topic_item_sel' : '' :class="`topic_item ${
}`" selTopicId === topic.topicId ? 'topic_item_sel' : ''
@click="handleOnTopicClick(topic)" }`"
> @click="handleOnTopicClick(topic)"
<div class="topic_status topic_status_read" /> >
<div class="topic_item_content"> <div
<div class="topic_content">{{ topic.content }}</div> :class="`topic_status ${
<div class="topic_user_name">by {{ topic.name }}</div> topic.read ? 'topic_status_read' : 'topic_status_unread'
</div> }`"
<div class="topic_info"> />
<el-tag size="small">{{ topicTypeDict[topic.topicType] }}</el-tag> <div class="topic_item_content">
<div class="topic_time">{{ formatDate(topic.createTime) }}</div> <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> </div>
<div v-else class="topic_list_empty">暂无消息</div>
</div> </div>
<div class="topic_detail"> <div class="topic_detail">
<div class="topic_detail_list"> <div class="topic_detail_list">
<div class="topic_detail_title"> <div class="topic_detail_title" v-if="!!detailData.content">
<div>{{ detailData.content }}</div> <div>{{ detailData.content }}</div>
<div class="content_time" :style="{ marginTop: '4px' }"> <div class="content_time" :style="{ marginTop: '4px' }">
{{ formatDate(detailData.createTime) }} {{ formatDate(detailData.createTime) }}
@ -50,7 +57,12 @@
> >
回复{{ replyTarget }} 回复{{ replyTarget }}
</div> </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"> <div class="send_btn_zone">
<el-button <el-button
type="primary" type="primary"
@ -65,9 +77,10 @@
</div> </div>
</template> </template>
<script> <script>
import { createNamespacedHelpers } from "vuex"; import { createNamespacedHelpers, mapActions as globalMapActions } from "vuex";
import Comment from "./Comment"; import Comment from "./Comment";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { keys } from "@/utils/websocket";
const { const {
mapActions, mapActions,
mapState, mapState,
@ -91,10 +104,61 @@ export default {
created() { created() {
this.init(); this.init();
}, },
mounted() {
window.addEventListener("message", this.handleOnMessage);
},
unmounted() {
window.removeEventListener("message", this.handleOnMessage);
},
computed: { computed: {
...mapState(["topicList", "selTopicId", "detailData"]), ...mapState(["topicList", "selTopicId", "detailData"]),
}, },
methods: { 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) { formatDate(date) {
return dayjs(date).format("MM-DD HH:mm"); return dayjs(date).format("MM-DD HH:mm");
}, },
@ -147,8 +211,14 @@ export default {
this.$message.error("请选择回复对象"); this.$message.error("请选择回复对象");
} }
}, },
...mapActions(["init", "fetchTopicDetailActions", "postTopicReplyActions"]), ...mapActions([
...mapMutations(["clean"]), "init",
"fetchTopicDetailActions",
"postTopicReplyActions",
"fetchTopicListApi",
]),
...mapMutations(["clean", "save"]),
...globalMapActions(["updateUnreadCount"]),
}, },
}; };
</script> </script>
@ -157,6 +227,7 @@ export default {
display: flex; display: flex;
.topic_list { .topic_list {
flex: 2; flex: 2;
overflow: auto;
.topic_item { .topic_item {
display: flex; display: flex;
@ -223,6 +294,13 @@ export default {
.topic_item_sel { .topic_item_sel {
background: #dedede; background: #dedede;
} }
.topic_list_empty {
height: 100px;
text-align: center;
line-height: 100px;
color: #8c8c8c;
}
} }
.topic_detail { .topic_detail {