index.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <template>
  2. <s-layout class="chat-wrap" :title="!isReconnecting ? '连接客服成功' : '会话重连中'" navbar="inner">
  3. <!-- 覆盖头部导航栏背景颜色 -->
  4. <view class="page-bg" :style="{ height: sys_navBar + 'px' }"></view>
  5. <!-- 聊天区域 -->
  6. <MessageList ref="messageListRef">
  7. <template #bottom>
  8. <message-input v-model="chat.msg" @on-tools="onTools" @send-message="onSendMessage" :auto-focus="false"
  9. :show-char-count="true" :max-length="500"></message-input>
  10. </template>
  11. </MessageList>
  12. <!-- 聊天工具 -->
  13. <tools-popup :show-tools="chat.showTools" :tools-mode="chat.toolsMode" @close="handleToolsClose" @on-emoji="onEmoji"
  14. @image-select="onSelect" @on-show-select="onShowSelect">
  15. <message-input v-model="chat.msg" @on-tools="onTools" @send-message="onSendMessage" :auto-focus="false"
  16. :show-char-count="true" :max-length="500"></message-input>
  17. </tools-popup>
  18. <!-- 商品订单选择 -->
  19. <SelectPopup :mode="chat.selectMode" :show="chat.showSelect" @select="onSelect" @close="chat.showSelect = false" />
  20. </s-layout>
  21. </template>
  22. <script setup>
  23. import MessageList from '@/pages/chat/components/messageList.vue';
  24. import { reactive, ref, toRefs } from 'vue';
  25. import sheep from '@/sheep';
  26. import ToolsPopup from '@/pages/chat/components/toolsPopup.vue';
  27. import MessageInput from '@/pages/chat/components/messageInput.vue';
  28. import SelectPopup from '@/pages/chat/components/select-popup.vue';
  29. import {
  30. KeFuMessageContentTypeEnum,
  31. WebSocketMessageTypeConstants,
  32. } from '@/pages/chat/util/constants';
  33. import FileApi from '@/sheep/api/infra/file';
  34. import KeFuApi from '@/sheep/api/promotion/kefu';
  35. import { useWebSocket } from '@/sheep/hooks/useWebSocket';
  36. import { jsonParse } from '@/sheep/util';
  37. const sys_navBar = sheep.$platform.navbar;
  38. const chat = reactive({
  39. msg: '',
  40. scrollInto: '',
  41. showTools: false,
  42. toolsMode: '',
  43. showSelect: false,
  44. selectMode: '',
  45. });
  46. // 发送消息
  47. async function onSendMessage() {
  48. if (!chat.msg) return;
  49. try {
  50. const data = {
  51. contentType: KeFuMessageContentTypeEnum.TEXT,
  52. content: JSON.stringify({ text: chat.msg }),
  53. };
  54. await KeFuApi.sendKefuMessage(data);
  55. chat.msg = '';
  56. } finally {
  57. chat.showTools = false;
  58. }
  59. }
  60. const messageListRef = ref();
  61. //======================= 聊天工具相关 start =======================
  62. function handleToolsClose() {
  63. chat.showTools = false;
  64. chat.toolsMode = '';
  65. }
  66. function onEmoji(item) {
  67. chat.msg += item.name;
  68. }
  69. // 点击工具栏开关
  70. function onTools(mode) {
  71. if (isReconnecting.value) {
  72. sheep.$helper.toast('您已掉线!请返回重试');
  73. return;
  74. }
  75. // 第二次点击关闭
  76. if (chat.showTools && chat.toolsMode === mode) {
  77. handleToolsClose();
  78. return;
  79. }
  80. // 切换工具栏
  81. if (chat.showTools && chat.toolsMode !== mode) {
  82. chat.showTools = false;
  83. chat.toolsMode = '';
  84. }
  85. // 延迟打开等一下过度效果
  86. setTimeout(() => {
  87. chat.toolsMode = mode;
  88. chat.showTools = true;
  89. }, 200);
  90. }
  91. function onShowSelect(mode) {
  92. chat.showTools = false;
  93. chat.showSelect = true;
  94. chat.selectMode = mode;
  95. }
  96. async function onSelect({ type, data }) {
  97. let msg;
  98. switch (type) {
  99. case 'image':
  100. const res = await FileApi.uploadFile(data.tempFiles[0].path);
  101. msg = {
  102. contentType: KeFuMessageContentTypeEnum.IMAGE,
  103. content: JSON.stringify({ picUrl: res.data }),
  104. };
  105. break;
  106. case 'goods':
  107. msg = {
  108. contentType: KeFuMessageContentTypeEnum.PRODUCT,
  109. content: JSON.stringify(data),
  110. };
  111. break;
  112. case 'order':
  113. msg = {
  114. contentType: KeFuMessageContentTypeEnum.ORDER,
  115. content: JSON.stringify(data),
  116. };
  117. break;
  118. }
  119. if (msg) {
  120. // 发送消息
  121. // scrollBottom();
  122. await KeFuApi.sendKefuMessage(msg);
  123. await messageListRef.value.refreshMessageList();
  124. chat.showTools = false;
  125. chat.showSelect = false;
  126. chat.selectMode = '';
  127. }
  128. }
  129. //======================= 聊天工具相关 end =======================
  130. const { options } = useWebSocket({
  131. // 连接成功
  132. onConnected: async () => { },
  133. // 收到消息
  134. onMessage: async (data) => {
  135. const type = data.type;
  136. if (!type) {
  137. console.error('未知的消息类型:' + data);
  138. return;
  139. }
  140. // 2.2 消息类型:KEFU_MESSAGE_TYPE
  141. if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_TYPE) {
  142. // 刷新消息列表
  143. await messageListRef.value.refreshMessageList(jsonParse(data.content));
  144. return;
  145. }
  146. // 2.3 消息类型:KEFU_MESSAGE_ADMIN_READ
  147. if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_ADMIN_READ) {
  148. console.log('管理员已读消息');
  149. // 更新消息已读状态
  150. sheep.$helper.toast('客服已读您的消息');
  151. }
  152. },
  153. });
  154. const isReconnecting = toRefs(options).isReconnecting; // 重连状态
  155. </script>
  156. <style scoped lang="scss">
  157. .chat-wrap {
  158. .page-bg {
  159. width: 100%;
  160. position: absolute;
  161. top: 0;
  162. left: 0;
  163. background-color: var(--ui-BG-Main);
  164. z-index: 1;
  165. }
  166. .status {
  167. position: relative;
  168. box-sizing: border-box;
  169. z-index: 3;
  170. height: 70rpx;
  171. padding: 0 30rpx;
  172. background: var(--ui-BG-Main-opacity-1);
  173. display: flex;
  174. align-items: center;
  175. font-size: 30rpx;
  176. font-weight: 400;
  177. color: var(--ui-BG-Main);
  178. }
  179. }
  180. </style>