App.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <script>
  2. import store from './store';
  3. import http from './common/request';
  4. import * as enums from './common/enums';
  5. import * as wsApi from './common/wssocket';
  6. import UNI_APP from '@/.env.js'
  7. export default {
  8. data() {
  9. return {
  10. audioTip: null
  11. }
  12. },
  13. methods: {
  14. init() {
  15. // 加载数据
  16. store.dispatch("load").then(() => {
  17. // 初始化websocket
  18. this.initWebSocket();
  19. }).catch((e) => {
  20. console.log(e);
  21. this.exit();
  22. })
  23. },
  24. initWebSocket() {
  25. let loginInfo = uni.getStorageSync("loginInfo")
  26. wsApi.init();
  27. wsApi.connect(UNI_APP.WS_URL, loginInfo.accessToken);
  28. wsApi.onConnect(() => {
  29. // 加载离线消息
  30. this.pullPrivateOfflineMessage(store.state.chatStore.privateMsgMaxId);
  31. this.pullGroupOfflineMessage(store.state.chatStore.groupMsgMaxId);
  32. });
  33. wsApi.onMessage((cmd, msgInfo) => {
  34. if (cmd == 2) {
  35. // 异地登录,强制下线
  36. uni.showModal({
  37. content: '您已在其他地方登陆,将被强制下线',
  38. showCancel: false,
  39. })
  40. this.exit();
  41. } else if (cmd == 3) {
  42. // 私聊消息
  43. this.handlePrivateMessage(msgInfo);
  44. } else if (cmd == 4) {
  45. // 群聊消息
  46. this.handleGroupMessage(msgInfo);
  47. }
  48. });
  49. wsApi.onClose((res) => {
  50. // 1000是客户端正常主动关闭
  51. if (res.code != 1000) {
  52. // 重新连接
  53. uni.showToast({
  54. title: '连接已断开,尝试重新连接...',
  55. icon: 'none',
  56. })
  57. let loginInfo = uni.getStorageSync("loginInfo")
  58. wsApi.reconnect(UNI_APP.WS_URL, loginInfo.accessToken);
  59. }
  60. })
  61. },
  62. pullPrivateOfflineMessage(minId) {
  63. store.commit("loadingPrivateMsg",true)
  64. http({
  65. url: "/message/private/pullOfflineMessage?minId=" + minId,
  66. method: 'GET'
  67. }).catch(()=>{
  68. store.commit("loadingPrivateMsg",false)
  69. })
  70. },
  71. pullGroupOfflineMessage(minId) {
  72. store.commit("loadingGroupMsg",true)
  73. http({
  74. url: "/message/group/pullOfflineMessage?minId=" + minId,
  75. method: 'GET'
  76. }).catch(()=>{
  77. store.commit("loadingGroupMsg",false)
  78. })
  79. },
  80. handlePrivateMessage(msg) {
  81. // 消息加载标志
  82. if (msg.type == enums.MESSAGE_TYPE.LOADDING) {
  83. store.commit("loadingPrivateMsg", JSON.parse(msg.content))
  84. return;
  85. }
  86. // 消息已读处理,清空已读数量
  87. if (msg.type == enums.MESSAGE_TYPE.READED) {
  88. store.commit("resetUnreadCount", {
  89. type: 'PRIVATE',
  90. targetId: msg.recvId
  91. })
  92. return;
  93. }
  94. // 消息回执处理,改消息状态为已读
  95. if (msg.type == enums.MESSAGE_TYPE.RECEIPT) {
  96. store.commit("readedMessage", { friendId: msg.sendId })
  97. return;
  98. }
  99. // 标记这条消息是不是自己发的
  100. msg.selfSend = msg.sendId == store.state.userStore.userInfo.id;
  101. // 好友id
  102. let friendId = msg.selfSend ? msg.recvId : msg.sendId;
  103. this.loadFriendInfo(friendId).then((friend) => {
  104. this.insertPrivateMessage(friend, msg);
  105. })
  106. },
  107. insertPrivateMessage(friend, msg) {
  108. // webrtc 信令
  109. if (msg.type >= enums.MESSAGE_TYPE.RTC_CALL_VOICE &&
  110. msg.type <= enums.MESSAGE_TYPE.RTC_CANDIDATE) {
  111. // #ifdef MP-WEIXIN
  112. // 小程序不支持音视频
  113. return;
  114. // #endif
  115. // 被呼叫,弹出视频页面
  116. if(msg.type == enums.MESSAGE_TYPE.RTC_CALL_VOICE
  117. || msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO){
  118. let mode = msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO? "video":"voice";
  119. let pages = getCurrentPages();
  120. let curPage = pages[pages.length-1].route;
  121. if(curPage != "pages/chat/chat-video"){
  122. const friendInfo = encodeURIComponent(JSON.stringify(friend));
  123. uni.navigateTo({
  124. url: `/pages/chat/chat-video?mode=${mode}&friend=${friendInfo}&isHost=false`
  125. })
  126. }
  127. }
  128. setTimeout(() => {
  129. uni.$emit('WS_RTC',msg);
  130. },500)
  131. return;
  132. }
  133. let chatInfo = {
  134. type: 'PRIVATE',
  135. targetId: friend.id,
  136. showName: friend.nickName,
  137. headImage: friend.headImage
  138. };
  139. // 打开会话
  140. store.commit("openChat", chatInfo);
  141. // 插入消息
  142. store.commit("insertMessage", msg);
  143. // 播放提示音
  144. !msg.selfSend && this.playAudioTip();
  145. },
  146. handleGroupMessage(msg) {
  147. // 消息加载标志
  148. if (msg.type == enums.MESSAGE_TYPE.LOADDING) {
  149. store.commit("loadingGroupMsg",JSON.parse(msg.content))
  150. return;
  151. }
  152. // 消息已读处理
  153. if (msg.type == enums.MESSAGE_TYPE.READED) {
  154. // 我已读对方的消息,清空已读数量
  155. let chatInfo = {
  156. type: 'GROUP',
  157. targetId: msg.groupId
  158. }
  159. store.commit("resetUnreadCount", chatInfo)
  160. return;
  161. }
  162. // 消息回执处理
  163. if (msg.type == enums.MESSAGE_TYPE.RECEIPT) {
  164. // 更新消息已读人数
  165. let msgInfo = {
  166. id: msg.id,
  167. groupId: msg.groupId,
  168. readedCount: msg.readedCount,
  169. receiptOk: msg.receiptOk
  170. };
  171. store.commit("updateMessage", msgInfo)
  172. return;
  173. }
  174. // 标记这条消息是不是自己发的
  175. msg.selfSend = msg.sendId == store.state.userStore.userInfo.id;
  176. this.loadGroupInfo(msg.groupId).then((group) => {
  177. // 插入群聊消息
  178. this.insertGroupMessage(group, msg);
  179. })
  180. },
  181. insertGroupMessage(group, msg) {
  182. let chatInfo = {
  183. type: 'GROUP',
  184. targetId: group.id,
  185. showName: group.remark,
  186. headImage: group.headImageThumb
  187. };
  188. // 打开会话
  189. store.commit("openChat", chatInfo);
  190. // 插入消息
  191. store.commit("insertMessage", msg);
  192. // 播放提示音
  193. !msg.selfSend && this.playAudioTip();
  194. },
  195. loadFriendInfo(id) {
  196. return new Promise((resolve, reject) => {
  197. let friend = store.getters.findFriend(id);
  198. if (friend) {
  199. resolve(friend);
  200. } else {
  201. http({
  202. url: `/friend/find/${id}`,
  203. method: 'GET'
  204. }).then((friend) => {
  205. store.commit("addFriend", friend);
  206. resolve(friend)
  207. })
  208. }
  209. });
  210. },
  211. loadGroupInfo(id) {
  212. return new Promise((resolve, reject) => {
  213. let group = store.state.groupStore.groups.find((g) => g.id == id);
  214. if (group) {
  215. resolve(group);
  216. } else {
  217. http({
  218. url: `/group/find/${id}`,
  219. method: 'GET'
  220. }).then((group) => {
  221. resolve(group)
  222. store.commit("addGroup", group);
  223. })
  224. }
  225. });
  226. },
  227. exit() {
  228. console.log("exit");
  229. wsApi.close(1000);
  230. uni.removeStorageSync("loginInfo");
  231. uni.reLaunch({
  232. url: "/pages/login/login"
  233. })
  234. store.dispatch("unload");
  235. },
  236. playAudioTip() {
  237. // 音频播放无法成功
  238. // this.audioTip = uni.createInnerAudioContext();
  239. // this.audioTip.src = "/static/audio/tip.wav";
  240. // this.audioTip.play();
  241. }
  242. },
  243. onLaunch() {
  244. // 登录状态校验
  245. if (uni.getStorageSync("loginInfo")) {
  246. // 初始化
  247. this.init()
  248. } else {
  249. // 跳转到登录页
  250. uni.navigateTo({
  251. url: "/pages/login/login"
  252. })
  253. }
  254. }
  255. }
  256. </script>
  257. <style lang="scss">
  258. @import url('./static/icon/iconfont.css');
  259. .tab-page {
  260. // #ifdef H5
  261. height: calc(100vh - 46px - 50px); // h5平台100vh是包含了顶部和底部,需要减去
  262. // #endif
  263. // #ifndef H5
  264. height: calc(100vh);
  265. // #endif
  266. background-color: #f8f8f8;
  267. }
  268. .page {
  269. // #ifdef H5
  270. height: calc(100vh - 45px); // h5平台100vh是包含了顶部,需要减去
  271. // #endif
  272. // #ifndef H5
  273. height: calc(100vh);
  274. // #endif
  275. background-color: #f8f8f8;
  276. }
  277. </style>