index copy.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <template>
  2. <Theme>
  3. <view class="user-chat__wrap">
  4. <!-- navbar -->
  5. <view class="user-chat__navbar">
  6. <Navbar fixed leftIconColor="var(--black)" id="chat_nav" leftShow>
  7. <template #center>
  8. <view class="nav_title">
  9. <trans _t="网购助手" />
  10. </view>
  11. </template>
  12. </Navbar>
  13. </view>
  14. <!-- list box -->
  15. <view class="message_body">
  16. <MessageList
  17. ref="messageListRef"
  18. :lower-threshold="lowerThreshold"
  19. :height="listHeight ? `${listHeight - tabbarHeight}px` : ''"
  20. :listHeight="listHeight ? listHeight : 0"
  21. :tabbarHeight="tabbarHeight"
  22. :data-list="dataList"
  23. :loading="isLoading"
  24. @on-scroll="handleScroll"
  25. @on-scroll-top="handleScrollTop"
  26. @on-scroll-bottom="handleScrollBottom"
  27. ></MessageList>
  28. </view>
  29. <view
  30. class="new_count"
  31. :style="{ bottom: footerHeightInfo.height + tabbarHeight + 5 + 'px' }"
  32. v-show="newMessageCount > 0"
  33. @click="messageListRef && messageListRef.computeScrollHeight()"
  34. >
  35. <text>{{ newMessageCount }} {{ $t("条新消息") }}</text>
  36. </view>
  37. <footer-input
  38. ref="footerInputRef"
  39. v-model="message"
  40. :bottomHeight="tabbarHeight"
  41. @heights="getFooterHeight"
  42. @submit="onMessageSend"
  43. @imageUp="imageUp"
  44. @linechange="inputHeightChange"
  45. />
  46. <Tabbar page="chat" @getTabbarHeight="getTabbarHeight" />
  47. </view>
  48. </Theme>
  49. </template>
  50. <script setup>
  51. import { onUnload, onLoad } from "@dcloudio/uni-app";
  52. import {
  53. ref,
  54. reactive,
  55. getCurrentInstance,
  56. nextTick,
  57. computed,
  58. provide,
  59. } from "vue";
  60. import { storeToRefs } from "pinia";
  61. import { t } from "@/locale";
  62. import { getUuid } from "@/utils";
  63. import Navbar from "@/components/navbar";
  64. import Tabbar from "@/components/tabbar";
  65. import MessageList from "./modules/messageList.vue";
  66. import footerInput from "@/components/footer_input";
  67. import { useSocketStore, useUserStore, useTabbarStore } from "@/store";
  68. import useListHeight from "./hooks/useListHeight";
  69. import useMessageData from "./hooks/useMessageData";
  70. const useScoket = useSocketStore();
  71. const useUser = useUserStore();
  72. const useTabbar = useTabbarStore();
  73. const { getuserInfo } = storeToRefs(useUser);
  74. const { navbarNodeInfo, footerHeightInfo, listHeight } = useListHeight();
  75. const {
  76. useMessage,
  77. channel,
  78. channelData,
  79. dataList,
  80. newMessageCount,
  81. lowerThreshold,
  82. distanceFromBottom,
  83. isLoading,
  84. isOpenning,
  85. fetchHistoryMessageList,
  86. openService,
  87. } = useMessageData();
  88. uni.$on("on-service-open", () => {
  89. messageListRef.value && messageListRef.value.updateInitState();
  90. openService();
  91. });
  92. uni.hideTabBar();
  93. provide("channel", channel);
  94. provide("channelData", channelData);
  95. const messageListRef = ref(null);
  96. const footerInputRef = ref(null);
  97. const footerInputLineCount = ref(1);
  98. const message = ref("");
  99. const tabbarHeight = ref(0);
  100. onLoad(() => {
  101. useMessage.updateUnreadCount(channel.value, -1);
  102. useMessage.updateGlobalMapUnreadCount("serviceChannel", -1);
  103. });
  104. onUnload(() => {
  105. useScoket.send({
  106. event: "pusher:unsubscribe",
  107. data: { channel: channel.value },
  108. });
  109. useMessage.updateUnreadCount(channel.value, -1);
  110. useMessage.updateGlobalMapUnreadCount("serviceChannel", -1);
  111. });
  112. function handleScroll(e) {
  113. const { detail } = e;
  114. const distanceForBottom =
  115. detail.scrollHeight - detail.scrollTop - listHeight.value;
  116. distanceFromBottom.value = distanceForBottom > 0 ? distanceForBottom : 0;
  117. if (distanceFromBottom.value <= lowerThreshold.value) {
  118. useMessage.updateUnreadCount(channel.value, -1);
  119. }
  120. }
  121. // 滚动顶部回调
  122. function handleScrollTop() {
  123. fetchHistoryMessageList();
  124. }
  125. function handleScrollBottom() {
  126. useMessage.updateUnreadCount(channel.value, -1);
  127. }
  128. // 图片
  129. const imageUp = (url) => {
  130. const msg = {
  131. type: "image",
  132. url: url,
  133. };
  134. sendMessage(msg, () => {
  135. nextTick(() => {
  136. messageListRef.value && messageListRef.value.computeScrollHeight();
  137. });
  138. });
  139. };
  140. // 消息发送:发送按钮回调(手机键盘右下角确认按钮回调&电脑键盘回车)
  141. function onMessageSend() {
  142. if (!message.value) return;
  143. const msg = {
  144. type: "text",
  145. text: message.value,
  146. };
  147. sendMessage(msg);
  148. }
  149. // 消息发送: 快捷问题
  150. function onQuestionSend(messageText) {
  151. if (!(channelData.value && channelData.value.openInfo.status == 1)) return;
  152. if (!messageText) return;
  153. const msg = {
  154. type: "text",
  155. text: messageText,
  156. };
  157. sendMessage(msg, () => {
  158. messageListRef.value && messageListRef.value.computeScrollHeight();
  159. });
  160. }
  161. provide("onQuestionSend", onQuestionSend);
  162. function sendMessage(msg, callback) {
  163. const messageData = reactive({
  164. loading: true,
  165. status: -1,
  166. send: 1,
  167. msg: msg,
  168. });
  169. useMessage.updateMessageList(channel.value, messageData, "push");
  170. useMessage
  171. .sendMessage(msg)
  172. .then((data) => {
  173. messageData.loading = false;
  174. messageData.status = 1;
  175. for (let key in data) {
  176. if (key != "msg") {
  177. messageData[key] = data[key];
  178. }
  179. }
  180. useMessage.updateGlobalMapLasttime(`serviceChannel`, data.indate);
  181. })
  182. .catch(() => {
  183. messageData.loading = false;
  184. messageData.status = 0;
  185. });
  186. callback && callback();
  187. /* if (footerInputLineCount.value == 1) {
  188. messageListRef.value && messageListRef.value.computeScrollHeight();
  189. } */
  190. footerInputRef.value && footerInputRef.value.clear();
  191. }
  192. // 底部高度
  193. const getFooterHeight = (res) => {
  194. footerHeightInfo.value = {
  195. ...res,
  196. };
  197. };
  198. const getTabbarHeight = (height) => {
  199. tabbarHeight.value = height;
  200. };
  201. // 输入框高度发送变化
  202. const inputHeightChange = (res) => {
  203. footerInputLineCount.value = res.detail.lineCount;
  204. };
  205. const leftClick = () => {
  206. uni.switchTab({ url: "/pages/index/index" });
  207. useTabbar.getPageCur("index");
  208. };
  209. </script>
  210. <style scoped lang="less">
  211. .user-chat__wrap {
  212. display: flex;
  213. flex-direction: column;
  214. height: 100vh;
  215. background-color: #fff;
  216. padding-bottom: constant(safe-area-inset-bottom);
  217. padding-bottom: env(safe-area-inset-bottom);
  218. }
  219. .nav_title {
  220. color: var(--black);
  221. font-size: 32rpx;
  222. font-weight: 500;
  223. }
  224. .bg_logo {
  225. position: absolute;
  226. // .size(128rpx);
  227. color: var(--inputBg);
  228. opacity: 0.06;
  229. top: 0;
  230. left: 50%;
  231. font-weight: bold;
  232. transform: translateX(-50%);
  233. line-height: 88rpx;
  234. text-transform: uppercase;
  235. font-family: "HarmonyOS_Sans";
  236. text-wrap: nowrap;
  237. }
  238. .message_body {
  239. width: 100%;
  240. height: auto;
  241. }
  242. .new_count {
  243. position: fixed;
  244. bottom: 0;
  245. right: 16rpx;
  246. z-index: 20;
  247. height: 52rpx;
  248. padding: 0 20rpx;
  249. border-radius: 26rpx;
  250. background-color: rgba(0, 0, 0, 1);
  251. color: #fff;
  252. font-size: 24rpx;
  253. line-height: 52rpx;
  254. }
  255. </style>
  256. <style lang="less">
  257. .message__input :deep(.uni-textarea-wrapper .uni-textarea-placeholder),
  258. .message__input :deep(.uni-textarea-wrapper .uni-textarea-line),
  259. .message__input :deep(.uni-textarea-wrapper .uni-textarea-compute),
  260. .message__input :deep(.uni-textarea-wrapper .uni-textarea-textarea) {
  261. top: 50%;
  262. transform: translateY(-50%);
  263. }
  264. </style>