index.vue 5.3 KB

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