messageList.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <template>
  2. <!-- 聊天虚拟列表 -->
  3. <z-paging
  4. ref="pagingRef"
  5. v-model="messageList"
  6. use-chat-record-mode
  7. use-virtual-list
  8. cell-height-mode="dynamic"
  9. default-page-size="20"
  10. :auto-clean-list-when-reload="false"
  11. safe-area-inset-bottom
  12. bottom-bg-color="#f8f8f8"
  13. :back-to-top-style="backToTopStyle"
  14. :auto-show-back-to-top="showNewMessageTip"
  15. @backToTopClick="onBackToTopClick"
  16. @scrolltoupper="onScrollToUpper"
  17. @query="queryList"
  18. >
  19. <template #top>
  20. <!-- 撑一下顶部导航 -->
  21. <view :style="{ height: sys_navBar + 'px' }"></view>
  22. </template>
  23. <!-- style="transform: scaleY(-1)"必须写,否则会导致列表倒置!!! -->
  24. <!-- 注意不要直接在chat-item组件标签上设置style,因为在微信小程序中是无效的,请包一层view -->
  25. <template #cell="{ item, index }">
  26. <view style="transform: scaleY(-1)">
  27. <!-- 消息渲染 -->
  28. <MessageListItem
  29. :message="item"
  30. :message-index="index"
  31. :message-list="messageList"
  32. ></MessageListItem>
  33. </view>
  34. </template>
  35. <!-- 底部聊天输入框 -->
  36. <template #bottom>
  37. <slot name="bottom"></slot>
  38. </template>
  39. <!-- 查看最新消息 -->
  40. <template #backToTop>
  41. <text>有新消息</text>
  42. </template>
  43. </z-paging>
  44. </template>
  45. <script setup>
  46. import MessageListItem from '@/pages/chat/components/messageListItem.vue';
  47. import { reactive, ref } from 'vue';
  48. import KeFuApi from '@/sheep/api/promotion/kefu';
  49. import { isEmpty } from '@/sheep/helper/utils';
  50. import sheep from '@/sheep';
  51. import { formatDate } from '@/sheep/util';
  52. const sys_navBar = sheep.$platform.navbar;
  53. const messageList = ref([]); // 消息列表
  54. const showNewMessageTip = ref(false); // 显示有新消息提示
  55. const refreshMessage = ref(false); // 更新消息列表
  56. const backToTopStyle = reactive({
  57. width: '100px',
  58. 'background-color': '#fff',
  59. 'border-radius': '30px',
  60. 'box-shadow': '0 2px 4px rgba(0, 0, 0, 0.1)',
  61. display: 'flex',
  62. justifyContent: 'center',
  63. alignItems: 'center',
  64. }); // 返回顶部样式
  65. const queryParams = reactive({
  66. pageNo: 1, // 只用于触底计算
  67. pageSize: 20,
  68. createTime: undefined,
  69. });
  70. const pagingRef = ref(null); // 虚拟列表
  71. const queryList = async (pageNo, pageSize) => {
  72. // 组件加载时会自动触发此方法,因此默认页面加载时会自动触发,无需手动调用
  73. // 这里的pageNo和pageSize会自动计算好,直接传给服务器即可
  74. queryParams.pageNo = pageNo;
  75. queryParams.pageSize = pageSize;
  76. await getMessageList();
  77. };
  78. // 获得消息分页列表
  79. const getMessageList = async () => {
  80. const { data } = await KeFuApi.getKefuMessageList(queryParams);
  81. if (isEmpty(data)) {
  82. pagingRef.value.completeByNoMore([], true);
  83. return;
  84. }
  85. if (queryParams.pageNo > 1 && refreshMessage.value) {
  86. const newMessageList = [];
  87. for (const message of data) {
  88. if (messageList.value.some((val) => val.id === message.id)) {
  89. continue;
  90. }
  91. newMessageList.push(message);
  92. }
  93. // 新消息追加到开头
  94. messageList.value = [...newMessageList, ...messageList.value];
  95. pagingRef.value.updateCache(); // 更新缓存
  96. refreshMessage.value = false; // 更新好后重置状态
  97. return;
  98. }
  99. // 设置最后一次历史查询的最后一条消息的 createTime
  100. queryParams.createTime = formatDate(data.at(-1).createTime);
  101. pagingRef.value.completeByNoMore(data, false);
  102. };
  103. /** 刷新消息列表 */
  104. const refreshMessageList = async (message = undefined) => {
  105. if (typeof message !== 'undefined') {
  106. // 追加数据
  107. pagingRef.value.addChatRecordData([message], false);
  108. } else {
  109. queryParams.createTime = undefined;
  110. refreshMessage.value = true;
  111. await getMessageList();
  112. }
  113. // 若已是第一页则不做处理
  114. if (queryParams.pageNo > 1) {
  115. showNewMessageTip.value = true;
  116. } else {
  117. onScrollToUpper();
  118. }
  119. };
  120. /** 滚动到最新消息 */
  121. const onBackToTopClick = (event) => {
  122. event(false); // 禁用默认操作
  123. pagingRef.value.scrollToBottom();
  124. };
  125. /** 监听滚动到底部事件(因为 scroll 翻转了顶就是底) */
  126. const onScrollToUpper = () => {
  127. // 若已是第一页则不做处理
  128. if (queryParams.pageNo === 1) {
  129. return;
  130. }
  131. showNewMessageTip.value = false;
  132. };
  133. defineExpose({ getMessageList, refreshMessageList });
  134. </script>