新增已读未读功能对两种视角的处理,等待接口和socket联调
This commit is contained in:
parent
9e2daf8b2e
commit
676ad46ab5
@ -64,7 +64,7 @@
|
||||
"lint-staged": "^15.2.0",
|
||||
"naive-ui": "^2.41.0",
|
||||
"pinia": "2.0.36",
|
||||
"sass": "^1.77.8",
|
||||
"sass": "1.62.1",
|
||||
"simple-git-hooks": "^2.9.0",
|
||||
"typescript": "^5.3.3",
|
||||
"unocss": "^0.58.9",
|
||||
|
10264
pnpm-lock.yaml
10264
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -212,3 +212,12 @@ export const detailGetRecordsContext = (data) => {
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取自己消息已读回执列表
|
||||
export const ServeReadConditionList = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/talk/my-records/read/condition',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
@ -286,12 +286,12 @@ class Talk extends Base {
|
||||
|
||||
if (!this.isCurrSender()) {
|
||||
// 推送已读消息
|
||||
setTimeout(() => {
|
||||
ws.emit('im.message.read', {
|
||||
receiver_id: this.sender_id,
|
||||
msg_ids: [this.resource.msg_id],
|
||||
})
|
||||
}, 1000)
|
||||
// setTimeout(() => {
|
||||
// ws.emit('im.message.read', {
|
||||
// receiver_id: this.sender_id,
|
||||
// msg_ids: [this.resource.msg_id],
|
||||
// })
|
||||
// }, 1000)
|
||||
}
|
||||
|
||||
// 获取聊天面板元素节点
|
||||
|
@ -67,7 +67,17 @@
|
||||
v-for="item in virtualList"
|
||||
:id="`zp-id-${item.msg_id}`"
|
||||
:key="item.zp_index"
|
||||
:data-msgid="item.msg_id"
|
||||
:data-msgtype="item.msg_type"
|
||||
:data-userid="item.user_id"
|
||||
:data-talktype="talkParams.type"
|
||||
:data-receiverid="talkParams.receiver_id"
|
||||
style="transform: scaleY(-1);"
|
||||
:ref="
|
||||
(el) => {
|
||||
if (el) messageRecordElementRefs[item.zp_index] = el
|
||||
}
|
||||
"
|
||||
>
|
||||
<!-- 系统消息 -->
|
||||
<div v-if="item.msg_type >= 1000" class="message-box">
|
||||
@ -568,6 +578,7 @@ import {
|
||||
detailGetRecordsContext,
|
||||
ServeClearTalkUnreadNum,
|
||||
ServeTalkRecords,
|
||||
ServeReadConditionList,
|
||||
} from '@/api/chat'
|
||||
import copy07 from '@/static/image/chatList/copy07@2x.png'
|
||||
import multipleChoices from '@/static/image/chatList/multipleChoices@2x.png'
|
||||
@ -582,11 +593,17 @@ import deepBubble from '@/components/deep-bubble/deep-bubble.vue'
|
||||
import { isRevoke } from './menu'
|
||||
import useConfirm from '@/components/x-confirm/useConfirm.js'
|
||||
import { onLoad as uniOnload, onUnload as uniOnUnload } from '@dcloudio/uni-app'
|
||||
import ws from '@/connect'
|
||||
|
||||
Quill.register('formats/emoji', EmojiBlot)
|
||||
import 'quill-mention'
|
||||
const selectMemberByAlphabetRef = ref(null)
|
||||
|
||||
//观察者
|
||||
let observer
|
||||
//获取聊天消息记录元素
|
||||
const messageRecordElementRefs = ref([])
|
||||
|
||||
const {
|
||||
getDialogueList,
|
||||
updateZpagingRef,
|
||||
@ -652,6 +669,13 @@ const state = ref({
|
||||
lastSelection: 0,
|
||||
canUseQuillEditor: true, //是否可以使用quill编辑器,如果版本不支持,则使用普通输入框
|
||||
textAreaValue: '', //普通输入框的值
|
||||
visibleElements: new Set(), //当前需要发送已读回执的可见元素
|
||||
lastUpdateTime: 0, // 用于节流
|
||||
setMessageReadInterval: null, // 设置别人发出的消息,我已读的定时器
|
||||
tempWaitDoRead: [], //当前需要发送已读回执的元素
|
||||
visibleOutElements: new Set(), //当前需要获取别人发送的已读回执列表的可见元素
|
||||
setOutMessageReadInterval: null, // 设置我发出的消息,别人已读的定时器
|
||||
tempWaitDoCheck: [], //当前获取别人发送已读回执的元素
|
||||
})
|
||||
|
||||
uniOnload(async (options) => {
|
||||
@ -967,7 +991,9 @@ const onEmoticonEvent = (data) => {
|
||||
quill.insertText(index, data.value)
|
||||
}
|
||||
|
||||
if (index) {
|
||||
quill.setSelection(index + 1, 0, 'user')
|
||||
}
|
||||
} else {
|
||||
let fn = emitCall('emoticon_event', data.value, () => {})
|
||||
emit('editor-event', fn)
|
||||
@ -975,21 +1001,22 @@ const onEmoticonEvent = (data) => {
|
||||
}
|
||||
let calcDelta = false
|
||||
const onEditorChange = () => {
|
||||
if(calcDelta){
|
||||
if (calcDelta) {
|
||||
calcDelta = false
|
||||
return
|
||||
}
|
||||
// 以下逻辑是 光标与@之间存在其他内容时 不触发选人弹窗
|
||||
const qtext = getQuill().getText()
|
||||
let selectIdx = getQuill().getSelection().index
|
||||
let selectIdx = getQuill()?.getSelection()?.index
|
||||
const textBeforeCursor = qtext.substring(0, selectIdx)
|
||||
if (textBeforeCursor[0]?.charCodeAt(0) === 10) {
|
||||
const delta = getQuill().getContents()
|
||||
const ops = delta.ops || []
|
||||
if (ops[0].insert === '\n') {
|
||||
ops.splice(0, 1)
|
||||
if (ops.length > 0) {
|
||||
getQuill().setContents(delta)
|
||||
getQuill().setSelection(getQuill().getText().length, 0)
|
||||
}
|
||||
}
|
||||
// for (let i = 0; i < ops.length; i++) {
|
||||
// if (ops[i].insert === '\n') {
|
||||
@ -1483,7 +1510,7 @@ const clearMultiSelect = () => {
|
||||
const initData = async () => {
|
||||
const dialogueList = getDialogueList(talkParams.index_name)
|
||||
let doLocalPaging = false
|
||||
if(dialogueList?.records?.length > 0){
|
||||
if (dialogueList?.records?.length > 0) {
|
||||
doLocalPaging = true
|
||||
}
|
||||
console.error('dialogueList', dialogueList)
|
||||
@ -1497,7 +1524,7 @@ const initData = async () => {
|
||||
no_limit: dialogueList ? 1 : 0,
|
||||
}
|
||||
await onLoad({ ...objT })
|
||||
if(doLocalPaging){
|
||||
if (doLocalPaging) {
|
||||
zpagingRef.value?.setLocalPaging(records.value)
|
||||
}
|
||||
}
|
||||
@ -1818,8 +1845,8 @@ const handleAvatarTouchEnd = () => {
|
||||
onMounted(async () => {
|
||||
if (uni.getSystemInfoSync().osName === 'ios') {
|
||||
let versions = uni.getSystemInfoSync().osVersion.split('.')
|
||||
if (Number(versions[0]) < 17) {
|
||||
console.error('ios版本低于17')
|
||||
if (Number(versions[0]) < 16) {
|
||||
console.error('ios版本低于16')
|
||||
state.value.canUseQuillEditor = false
|
||||
}
|
||||
}
|
||||
@ -1844,8 +1871,208 @@ onMounted(async () => {
|
||||
state.value.selectAreaHeight =
|
||||
rpxToPx(state.value.mentionSelectHeight) - rpxToPx(90) + 'px'
|
||||
})
|
||||
|
||||
//设置观察者前设置定时器
|
||||
if (state.value.setMessageReadInterval) {
|
||||
clearInterval(state.value.setMessageReadInterval)
|
||||
state.value.setMessageReadInterval = null
|
||||
}
|
||||
state.value.setMessageReadInterval = setInterval(() => {
|
||||
checkVisibleElements()
|
||||
}, 3000)
|
||||
|
||||
if (state.value.setOutMessageReadInterval) {
|
||||
clearInterval(state.value.setOutMessageReadInterval)
|
||||
state.value.setOutMessageReadInterval = null
|
||||
}
|
||||
state.value.setOutMessageReadInterval = setInterval(() => {
|
||||
checkVisibleOutElements()
|
||||
}, 3000)
|
||||
|
||||
//初始化设置观察者
|
||||
const options = {
|
||||
root: null, // 使用浏览器窗口作为根容器
|
||||
threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], // 设置多个阈值点
|
||||
rootMargin: '50px 0px', // 增加观察范围
|
||||
}
|
||||
observer = new IntersectionObserver(handleIntersection, options)
|
||||
nextTick(() => {
|
||||
watch(
|
||||
messageRecordElementRefs,
|
||||
(newMessageRecordElementRefs) => {
|
||||
if (Array.isArray(newMessageRecordElementRefs)) {
|
||||
newMessageRecordElementRefs.forEach((el, index) => {
|
||||
observeElement(el, index)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
)
|
||||
if (messageRecordElementRefs.value.length > 0) {
|
||||
messageRecordElementRefs.value.forEach((el, index) =>
|
||||
observeElement(el, index),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
//检查需要发送已读回执的元素
|
||||
const checkVisibleElements = () => {
|
||||
if (state.value.visibleElements.size > 0) {
|
||||
// console.error('当前待发送已读的消息元素:', state.value.visibleElements)
|
||||
let waitDoRead = []
|
||||
state.value.visibleElements.forEach((el) => {
|
||||
// console.log(el)
|
||||
const msgId = el.dataset.msgid
|
||||
const talkType = el.dataset.talktype
|
||||
const receiverId = el.dataset.receiverid
|
||||
// console.log(msgId, talkType, receiverId)
|
||||
if (waitDoRead.length === 0) {
|
||||
waitDoRead.push({
|
||||
msg_ids: [msgId],
|
||||
talk_type: talkType,
|
||||
receiver_id: receiverId,
|
||||
})
|
||||
} else {
|
||||
waitDoRead.forEach((doReadItem) => {
|
||||
if (
|
||||
doReadItem.talk_type === talkType &&
|
||||
doReadItem.receiver_id === receiverId
|
||||
) {
|
||||
doReadItem.msg_ids.push(msgId)
|
||||
} else {
|
||||
waitDoRead.push({
|
||||
msg_ids: [msgId],
|
||||
talk_type: talkType,
|
||||
receiver_id: receiverId,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// console.error(waitDoRead)
|
||||
if (waitDoRead.length > 0) {
|
||||
waitDoRead.forEach((doReadItem) => {
|
||||
// 查找上一次存储的对应项
|
||||
const prevItem = state.value.tempWaitDoRead.find(
|
||||
(prev) =>
|
||||
prev.talk_type === doReadItem.talk_type &&
|
||||
prev.receiver_id === doReadItem.receiver_id,
|
||||
)
|
||||
// 如果找不到上一次的项,或者msg_ids数量不一致,则发送
|
||||
if (
|
||||
!prevItem ||
|
||||
prevItem.msg_ids.length !== doReadItem.msg_ids.length
|
||||
) {
|
||||
console.error('====发送了已读回执=====', doReadItem)
|
||||
//发送已读回执
|
||||
ws.emit('im.message.read', doReadItem)
|
||||
}
|
||||
})
|
||||
}
|
||||
// 更新完成后再保存当前数据
|
||||
state.value.tempWaitDoRead = JSON.parse(JSON.stringify(waitDoRead))
|
||||
}
|
||||
}
|
||||
|
||||
//检查需要获取别人发送的已读回执列表的元素
|
||||
const checkVisibleOutElements = () => {
|
||||
if (state.value.visibleOutElements.size > 0) {
|
||||
// console.error('当前待获取已读回执列表的消息元素:', state.value.visibleOutElements)
|
||||
let waitDoCheck = []
|
||||
state.value.visibleOutElements.forEach((el) => {
|
||||
// console.log(el)
|
||||
const msgId = el.dataset.msgid
|
||||
const talkType = el.dataset.talktype
|
||||
const receiverId = el.dataset.receiverid
|
||||
if (waitDoCheck.length === 0) {
|
||||
waitDoCheck.push({
|
||||
msg_ids: [msgId],
|
||||
talk_type: talkType,
|
||||
receiver_id: receiverId,
|
||||
})
|
||||
} else {
|
||||
waitDoCheck.forEach((doReadItem) => {
|
||||
if (
|
||||
doReadItem.talk_type === talkType &&
|
||||
doReadItem.receiver_id === receiverId
|
||||
) {
|
||||
doReadItem.msg_ids.push(msgId)
|
||||
} else {
|
||||
waitDoCheck.push({
|
||||
msg_ids: [msgId],
|
||||
talk_type: talkType,
|
||||
receiver_id: receiverId,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// console.error(waitDoCheck)
|
||||
if (waitDoCheck.length > 0) {
|
||||
waitDoCheck.forEach((doCheckItem) => {
|
||||
// 查找上一次存储的对应项
|
||||
const prevItem = state.value.tempWaitDoCheck.find(
|
||||
(prev) =>
|
||||
prev.talk_type === doCheckItem.talk_type &&
|
||||
prev.receiver_id === doCheckItem.receiver_id,
|
||||
)
|
||||
// 如果找不到上一次的项,或者msg_ids数量不一致,则发送
|
||||
if (
|
||||
!prevItem ||
|
||||
prevItem.msg_ids.length !== doCheckItem.msg_ids.length
|
||||
) {
|
||||
console.error('====调用了已读回执查询接口=====', doCheckItem)
|
||||
// ServeReadConditionList({
|
||||
// type: 'list', //list是列表,detail是详情
|
||||
// talkType: talkParams.type,
|
||||
// receiverId: talkParams.receiver_id,
|
||||
// msgIds: waitDoCheck,
|
||||
// }).then(({ code, data }) => {
|
||||
// console.log(data)
|
||||
// })
|
||||
}
|
||||
})
|
||||
}
|
||||
// 更新完成后再保存当前数据
|
||||
state.value.tempWaitDoCheck = JSON.parse(JSON.stringify(waitDoCheck))
|
||||
}
|
||||
}
|
||||
|
||||
// 观察元素
|
||||
const observeElement = (el, index) => {
|
||||
if (el && observer) {
|
||||
// 使用 requestAnimationFrame 优化 DOM 操作
|
||||
requestAnimationFrame(() => {
|
||||
observer.unobserve(el) // 先取消观察
|
||||
observer.observe(el) // 再重新观察
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 观察者函数
|
||||
const handleIntersection = (entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
// 元素进入视口
|
||||
let elData = entry.target.dataset
|
||||
const msgType = elData.msgtype
|
||||
const userId = elData.userid
|
||||
if (Number(msgType) < 1000 && Number(userId) !== Number(talkParams.uid)) {
|
||||
//我读别人发的消息,需要发送已读回执
|
||||
state.value.visibleElements.add(entry.target)
|
||||
}
|
||||
if (Number(msgType) < 1000 && Number(userId) === Number(talkParams.uid)) {
|
||||
//我发的消息,需要获取别人发送的已读回执列表
|
||||
state.value.visibleOutElements.add(entry.target)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const pxTorPx = (px) => {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
const rpx = px / (sysInfo.screenWidth / 750)
|
||||
@ -1873,6 +2100,19 @@ onUnmounted(() => {
|
||||
if (uploadsStore.isUploading) {
|
||||
uploadsStore.clearUpload()
|
||||
}
|
||||
if (observer) {
|
||||
observer.disconnect()
|
||||
}
|
||||
if (state.value.setMessageReadInterval) {
|
||||
clearInterval(state.value.setMessageReadInterval)
|
||||
state.value.setMessageReadInterval = null
|
||||
checkVisibleElements()
|
||||
}
|
||||
if (state.value.setOutMessageReadInterval) {
|
||||
clearInterval(state.value.setOutMessageReadInterval)
|
||||
state.value.setOutMessageReadInterval = null
|
||||
checkVisibleOutElements()
|
||||
}
|
||||
})
|
||||
|
||||
// 修改防抖函数的实现
|
||||
|
Loading…
Reference in New Issue
Block a user