From 3637bac4e462d781d6e6f7feab1e4909d40e1cbd Mon Sep 17 00:00:00 2001 From: jiangjunjie Date: Mon, 28 Sep 2026 16:29:52 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/chat-new.vue | 228 +++++++++++++++++++----------------- 1 file changed, 120 insertions(+), 108 deletions(-) diff --git a/src/components/chat-new.vue b/src/components/chat-new.vue index 6d250d3..fef639b 100644 --- a/src/components/chat-new.vue +++ b/src/components/chat-new.vue @@ -17,6 +17,8 @@ import imageService from '@/utils/imageService' import { messageSend } from '@/utils/api' import { Post } from '@/utils/https' import { SJHLCmdList } from '@/utils/constant' +import MeScroll from 'mescroll.js' +import 'mescroll.js/mescroll.min.css' defineOptions({ name: 'chat-new', @@ -42,6 +44,9 @@ const sessionId = inject('sessionId', ref('')) const userId = inject('userId', ref('')) const isLostMessage = inject('isLostMessage', ref(null)) const mainContainerRef = ref(null) +const webSocketChannel = '95460000' // 投诉直连渠道号 +// mescroll 实例(非响应式) +let mescroll = null const appraiseShow = ref(false) // 有帮助/没帮助评价弹层 const baseHandle = ref('') // 评价弹层基础数据(含被点按钮 event) @@ -53,7 +58,6 @@ function appraiseIsClose(val, param) { // 参考 liics/chat-main 的 tokenExist:token 获取成功后才渲染聊天主内容 const historylist = ref([]) -const historyLoading = ref(false) const historyReady = ref(true) const introReadyCount = ref(0) const businessHandleRef = ref(null) @@ -221,14 +225,6 @@ function getMainEl() { return mainContainerRef.value || store?.mescrollContent || null } -function scrollMainTo(top) { - const el = getMainEl() - if (!el) return false - const value = Math.max(0, Number(top) || 0) - el.scrollTop = value - return Math.abs((el.scrollTop || 0) - value) < 2 || value === 0 -} - let smoothScrollToken = 0 function smoothScrollMainToBottom(duration = 300) { @@ -323,10 +319,6 @@ function onMainScroll(event) { const top = event?.target?.scrollTop if (top == null) return persistScrollTop(top, { allowZero: true }) - // 上滑到顶部附近时加载更多历史记录 - if (top <= 60 && !nolist.value && !historyLoading.value) { - getChatHistory() - } } onBeforeRouteLeave(() => { @@ -378,105 +370,137 @@ function getHeaderUrl(context, payload) { }); }); } -const getChatHistory = async (isAuto = false) => { +function initMescroll() { + let isLostMessageFlag = + isLostMessage.value && (isLostMessage.value == '1' || isLostMessage.value == '3') ? true : false + + // 投诉直连渠道首次进入需要刷新 + const isTouSuZhiLian = store.source === webSocketChannel + isLostMessageFlag = isTouSuZhiLian ? true : isLostMessageFlag + + // 创建 MeScroll 对象(下拉加载历史记录,参考 chat-main) + mescroll = new MeScroll(mainContainerRef.value, { + down: { + isBounce: false, + auto: isLostMessageFlag, + callback: getChatHistory, + htmlContent: '

没有数据了

', + hardwareClass: 'mescroll-hardware', + warpClass: 'mescroll-downwarp', // 下拉刷新的布局容器样式 + offset: 70, // 触发刷新的距离,默认80 + mustToTop: false, + autoShowLoading: true, // 自动执行下拉回调时是否显示下拉刷新进度 + textInOffset: '下拉加载历史记录', // 下拉的距离在offset范围内的提示文本 + textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本 + textLoading: '加载中 ...', // 加载中的提示文本 + onMoving: upOnMoving, + }, + up: { + use: false, + }, + }) +} + +function upOnMoving() { + nolist.value = false +} + +// 下拉回调 拉取历史记录 +function getChatHistory() { const params = { page: pageNum.value, pageSize: pageSize.value, sessionId: sessionId.value || store?.sessionId, userId: userId.value || store?.userId, } - historyLoading.value = true - - Post('history/chatInfo', params) - .then((response) => { - historyLoading.value = false - historyReady.value = true - if (response.code && response.code != 0) { - showToast(response.msg) - return - } - - // 成功返回 - if (response.data?.list?.length > 0) { - const dataList = response.data.list - pageNum.value++ - - // 不满一页,说明已经无更多消息 - if (dataList.length < pageSize.value) { - nolist.value = true - nolistTxt.value = '没有更多数据了' + setTimeout(() => { + Post('history/chatInfo', params) + .then((response) => { + if (response.code && response.code != 0) { + showToast(response.msg) + return } - // 生成 VIEW_ID,大写,避免污染源数据 - dataList.forEach((val) => { - val.VIEW_ID = 'msg' + val.timestamp - }) + // 成功返回 + if (response.data?.list?.length > 0) { + const dataList = response.data.list - // 获取当前最顶部的 VIEW_ID(注意写在 concat 前面) - let topMsg = historylist.value[0] + pageNum.value++ + // 先隐藏下拉刷新的状态 + mescroll && mescroll.endSuccess() - // 处理隐藏的数据 - if (topMsg && !topMsg.msgText) { - topMsg = historylist.value[1] - if (topMsg && !topMsg.msgText) { - topMsg = historylist.value[2] + // 不满一页,说明已经无更多消息 + if (dataList.length < pageSize.value) { + nolist.value = true + nolistTxt.value = '没有更多数据了' } - } - // 设置列表数据(历史消息也走同一套字段归一,保证 msgType=01 卡片能显示) - const normalizedList = store.normalizeHistoryMessages - ? store.normalizeHistoryMessages(dataList) - : dataList - historylist.value = normalizedList.concat(historylist.value) - _.map(historylist.value, (item) => { - // 历史记录不展示数据互联 - if (SJHLCmdList.includes(item.cmd) && !_.isEmpty(item.VIEW_ID)) { - item.msgText = '' - } - // 历史问句去掉 & 符号 - if (item.fromFlag === 1 && item.msgText && item.msgText.indexOf('&') !== -1) { - item.msgText = item.msgText.replace(/&/g, '') - } - }) + // 生成 VIEW_ID,大写,避免污染源数据 + dataList.forEach((val) => { + val.VIEW_ID = 'msg' + val.timestamp + }) - // 更新未读状态 - updateReadStatus(dataList) + // 获取当前最顶部的 VIEW_ID(注意写在 concat 前面) + let topMsg = historylist.value[0] - nextTick(() => { - if (isAuto) { - // 参考 chat-main:有未读自动加载时滚到底部并提示未读 - if (isLostMessage.value === 3) { - showToast('您有新消息未读,请及时查看历史消息') + // 处理隐藏的数据 + if (topMsg && !topMsg.msgText) { + topMsg = historylist.value[1] + if (topMsg && !topMsg.msgText) { + topMsg = historylist.value[2] } - setTimeout(() => { - scrollMainTo(99999) - }, 150) - } else if (topMsg) { - const view = document.querySelector('#' + topMsg.VIEW_ID) - console.log(topMsg.VIEW_ID + ' 节点离页面顶部的距离=' + view?.offsetTop) - - setTimeout(() => { - if (view) scrollMainTo(view.offsetTop - 20) - }, 150) - } else { - // 首次手动上拉加载:停在顶部 - setTimeout(() => { - scrollMainTo(0) - }, 150) } - }) - } else { + + // 设置列表数据(历史消息也走同一套字段归一,保证 msgType=01 卡片能显示) + const normalizedList = store.normalizeHistoryMessages + ? store.normalizeHistoryMessages(dataList) + : dataList + historylist.value = normalizedList.concat(historylist.value) + _.map(historylist.value, (item) => { + // 历史记录不展示数据互联 + if (SJHLCmdList.includes(item.cmd) && !_.isEmpty(item.VIEW_ID)) { + item.msgText = '' + } + // 历史问句去掉 & 符号 + if (item.fromFlag === 1 && item.msgText && item.msgText.indexOf('&') !== -1) { + item.msgText = item.msgText.replace(/&/g, '') + } + }) + + // 更新未读状态 + updateReadStatus(dataList) + + nextTick(() => { + if (pageNum.value <= 2) { + if (isLostMessage.value === 3) { + showToast('您有新消息未读,请及时查看历史消息') + } + setTimeout(() => { + mescroll && mescroll.scrollTo(99999, 0) + }, 150) + } else if (topMsg) { + // 保持顶部消息的位置 + const view = document.querySelector('#' + topMsg.VIEW_ID) + console.log(topMsg.VIEW_ID + ' 节点离页面顶部的距离=' + view?.offsetTop) + + setTimeout(() => { + if (view) mescroll.scrollTo(view.offsetTop - 20, 0) + }, 150) + } + }) + } else { + mescroll && mescroll.endErr() + nolist.value = true + nolistTxt.value = pageNum.value === 1 ? '当前还没有您的聊天记录' : '没有更多数据了' + } + }) + .catch(() => { + pageNum.value-- + mescroll && mescroll.endErr() nolist.value = true - nolistTxt.value = pageNum.value === 1 ? '当前还没有您的聊天记录' : '没有更多数据了' - } - }) - .catch(() => { - // 网络异常不置 nolist,用户再次上滑可重试(chat-main 靠下拉手势重置 nolist,这里等价放开) - historyLoading.value = false - historyReady.value = true - pageNum.value-- - }) + }) + }, 600) } onMounted(async () => { getHeaderUrl() @@ -484,16 +508,12 @@ onMounted(async () => { if (mainContainerRef.value) { store?.setMescrollContent?.(mainContainerRef.value) } + // 下拉加载历史记录初始化(有未读/投诉直连渠道时 down.auto 自动拉第一页,参考 chat-main) + initMescroll() const persisted = readPersistedScrollTop() if (persisted != null && persisted > 0) { shouldRestoreScroll.value = true restoreScrollPosition() - } else { - // 参考 chat-main 的 mescroll down.auto:有未读消息或投诉直连渠道时,进入页面自动加载历史 - const hasUnread = isLostMessage.value == '1' || isLostMessage.value == '3' - if (hasUnread || store.source === '95460000') { - getChatHistory(true) - } } }) watch( @@ -512,9 +532,6 @@ watch(