|
|
|
|
@ -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: '<p class="downwarp-progress"></p><p class="downwarp-tip">没有数据了</p>', |
|
|
|
|
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( |
|
|
|
|
<template> |
|
|
|
|
<div class="chat-new"> |
|
|
|
|
<div class="main-container" ref="mainContainerRef" @scroll.passive="onMainScroll"> |
|
|
|
|
<div class="history-loading" v-if="historyLoading"> |
|
|
|
|
<van-loading type="spinner" size="18rem" /> |
|
|
|
|
</div> |
|
|
|
|
<div class="empty-tips opacity" v-if="nolist">{{ nolistTxt }}</div> |
|
|
|
|
<div class="historicRecords" v-if="historylist.length > 0"> |
|
|
|
|
<chatContent |
|
|
|
|
@ -590,11 +607,6 @@ watch( |
|
|
|
|
overflow-y: auto; |
|
|
|
|
overflow-x: hidden; |
|
|
|
|
|
|
|
|
|
.history-loading { |
|
|
|
|
display: flex; |
|
|
|
|
justify-content: center; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.message-con { |
|
|
|
|
display: flex; |
|
|
|
|
flex-direction: column; |
|
|
|
|
|