新增已读未读功能对两种视角的处理,等待接口和socket联调

This commit is contained in:
wangyifeng 2025-04-16 17:19:31 +08:00
parent 9e2daf8b2e
commit 676ad46ab5
5 changed files with 6124 additions and 4699 deletions

View File

@ -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",

File diff suppressed because it is too large Load Diff

View File

@ -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,
})
}

View File

@ -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)
}
// 获取聊天面板元素节点

View File

@ -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)
@ -981,15 +1007,16 @@ const onEditorChange = () => {
}
// @
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') {
@ -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,7 +1871,207 @@ 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', //listdetail
// 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()
@ -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()
}
})
//