Compare commits
11 Commits
e4aa6181a8
...
eb1523516c
Author | SHA1 | Date | |
---|---|---|---|
eb1523516c | |||
02999dc782 | |||
dd0ccb9818 | |||
71d97f069f | |||
7be32e930c | |||
885707f328 | |||
f76198b674 | |||
66828a254c | |||
3898c637b4 | |||
29dcfac775 | |||
676ad46ab5 |
@ -212,3 +212,21 @@ export const detailGetRecordsContext = (data) => {
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取自己消息已读回执列表
|
||||
export const ServeReadConditionList = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/talk/my-records/read/condition',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取消息已读未读详情
|
||||
export const ServeMessageReadDetail = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/talk/my-records/read/condition',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
@ -244,8 +244,16 @@ class Talk extends Base {
|
||||
|
||||
//群成员被移出时,需要更新群成员权限
|
||||
if ([1104, 1115].includes(record.msg_type)) {
|
||||
console.error(this.resource.extra.members, 'this.resource.extra.members')
|
||||
if (this.resource?.extra?.members?.length > 0) {
|
||||
const isMeQuit = this.resource.extra.members.find(
|
||||
(item) => item.user_id === this.getAccountId(),
|
||||
)
|
||||
if (isMeQuit) {
|
||||
useDialogueStore().updateQuit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//群禁言变化时,需要更新群禁言状态——即更新群成员列表
|
||||
if ([1107, 1108, 1109, 1110].includes(record.msg_type)) {
|
||||
@ -286,12 +294,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)
|
||||
}
|
||||
|
||||
// 获取聊天面板元素节点
|
||||
|
@ -56,7 +56,7 @@
|
||||
@click="() => allCheck(allCheckStatus)"
|
||||
>
|
||||
<div class="mr-[20rpx]">
|
||||
<checkBox :modelValue="allCheckStatus"></checkBox>
|
||||
<checkBox @change="(val) => allCheck(allCheckStatus)" :modelValue="allCheckStatus"></checkBox>
|
||||
</div>
|
||||
<div class="font-medium text-[28rpx] leading-[40rpx]">
|
||||
{{ $t('radio.btn.selectAll') }}
|
||||
|
@ -106,7 +106,7 @@
|
||||
</div>
|
||||
<template #bottom>
|
||||
<customBtn :isBottom="true" :btnText="$t('pageTitle.create.group')" @click="handleConfirm"
|
||||
:disabled="confirmBtnStatus"></customBtn>
|
||||
:isLoading="isLoading" :disabled="confirmBtnStatus || isLoading"></customBtn>
|
||||
</template>
|
||||
</zPaging>
|
||||
</div>
|
||||
@ -219,13 +219,17 @@
|
||||
url: '/pages/chooseByDeps/index?chooseMode=2',
|
||||
})
|
||||
}
|
||||
const isLoading = ref(false)
|
||||
|
||||
// 点击发起群聊
|
||||
const handleConfirm = async () => {
|
||||
console.log(allChooseMembers.value)
|
||||
if (isLoading.value) return
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
let erp_ids = ''
|
||||
if (allChooseMembers?.value?.length > 0) {
|
||||
allChooseMembers?.value?.forEach((ele) => {
|
||||
allChooseMembers.value.forEach((ele) => {
|
||||
if (!erp_ids) {
|
||||
erp_ids = String(ele.ID)
|
||||
} else {
|
||||
@ -233,44 +237,45 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let res = null
|
||||
|
||||
if (groupActiveIndex.value === 0) {
|
||||
// 普通群
|
||||
let params = {
|
||||
const params = {
|
||||
avatar: '',
|
||||
name: groupName.value,
|
||||
erp_ids: erp_ids,
|
||||
type: 1,
|
||||
profile: '',
|
||||
}
|
||||
console.log(params)
|
||||
const res = await ServeCreateGroup(params)
|
||||
if (res.code === 200) {
|
||||
resetGroupInfo()
|
||||
uni.navigateBack()
|
||||
}
|
||||
console.log('普通群参数:', params)
|
||||
res = await ServeCreateGroup(params)
|
||||
} else if (groupActiveIndex.value === 1) {
|
||||
// 部门群
|
||||
const res = await createDepGroup()
|
||||
if (res.code === 200) {
|
||||
resetGroupInfo()
|
||||
uni.navigateBack()
|
||||
}
|
||||
res = await createDepGroup()
|
||||
} else if (groupActiveIndex.value === 2) {
|
||||
// 项目群
|
||||
let params = {
|
||||
const params = {
|
||||
avatar: '',
|
||||
name: groupName.value,
|
||||
erp_ids: erp_ids,
|
||||
type: 3,
|
||||
profile: '',
|
||||
}
|
||||
console.log(params)
|
||||
const res = await ServeCreateGroup(params)
|
||||
if (res.code === 200) {
|
||||
console.log('项目群参数:', params)
|
||||
res = await ServeCreateGroup(params)
|
||||
}
|
||||
|
||||
if (res?.code === 200) {
|
||||
resetGroupInfo()
|
||||
uni.navigateBack()
|
||||
}
|
||||
} else {}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
//发起群聊按钮可点击状态
|
||||
const confirmBtnStatus = computed(() => {
|
||||
|
@ -67,7 +67,21 @@
|
||||
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) {
|
||||
if (item.zp_index !== undefined) {
|
||||
messageRecordElementRefs[item.zp_index] = el
|
||||
}
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
<!-- 系统消息 -->
|
||||
<div v-if="item.msg_type >= 1000" class="message-box">
|
||||
@ -205,6 +219,17 @@
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="talk-tools have_read_num"
|
||||
v-if="item.user_id === talkParams.uid"
|
||||
@click="toShowMessageReadDetail(item)"
|
||||
>
|
||||
<span v-if="talkParams.type === 1">未读</span>
|
||||
<span v-if="talkParams.type === 2">
|
||||
已读 (0/{{ (Number(talkParams.num)-1) > 0 ? Number(talkParams.num)-1 : 0 }})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="item.extra.reply"
|
||||
class="talk-reply pointer"
|
||||
@ -540,6 +565,102 @@
|
||||
@getMentionSelectLists="getMentionSelectLists"
|
||||
></selectMemberByAlphabet>
|
||||
</tm-drawer>
|
||||
<tm-drawer
|
||||
placement="bottom"
|
||||
v-model:show="state.isShowMessageReadDetail"
|
||||
:hideHeader="true"
|
||||
:round="5"
|
||||
:height="state.mentionSelectHeight"
|
||||
:inContent="true"
|
||||
>
|
||||
<div
|
||||
class="mention-select-drawer flex flex-row flex-1 flex-row flex-row-center-between"
|
||||
>
|
||||
<div
|
||||
class="cancel-btns flex-row flex flex-row-center-start"
|
||||
style="width: 210rpx;"
|
||||
@click="hideMessageReadDetail"
|
||||
>
|
||||
<span
|
||||
style="flex-shrink: 0; display: block;"
|
||||
class="text-[32rpx] font-regular text-[#191919]"
|
||||
>
|
||||
{{ $t('cancel') }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-row-center-center flex-col"
|
||||
style="padding: 6rpx 0;"
|
||||
>
|
||||
<text>{{ $t('chat.msgRead.list') }}</text>
|
||||
</div>
|
||||
<div
|
||||
class="flex-row flex flex-row-center-end"
|
||||
style="width: 210rpx;"
|
||||
></div>
|
||||
</div>
|
||||
<div class="msg-read-detail-tabs">
|
||||
<tm-sheet>
|
||||
<tm-tabs
|
||||
:list="state.msgReadDetailTabs"
|
||||
showTabsLineAni
|
||||
activeFontColor="#7A58DE"
|
||||
tabsLineAniColor="#7A58DE"
|
||||
:item-width="375"
|
||||
:width="750"
|
||||
@change="msgReadDetailTabsChange"
|
||||
:default-name="state.msgReadDetailTabs[0].key"
|
||||
></tm-tabs>
|
||||
<div class="msg-read-detail">
|
||||
<ZPaging
|
||||
ref="zPagingReadDetails"
|
||||
:show-scrollbar="false"
|
||||
:fixed="false"
|
||||
:height="state.readDetailHeight"
|
||||
:safe-area-inset-bottom="true"
|
||||
:inside-more="true"
|
||||
:hide-no-more-inside="true"
|
||||
@scrolltolower="loadMoreReadDetails"
|
||||
>
|
||||
<div
|
||||
class="msg-read-detail-each"
|
||||
v-for="(item, index) in state.msgReadOrNotDetail"
|
||||
:key="index"
|
||||
>
|
||||
<div class="msg-read-detail-each-avatar">
|
||||
<avatarModule
|
||||
:mode="2"
|
||||
:groupType="item?.group_type"
|
||||
:avatar="item?.avatar"
|
||||
:userName="item?.nickName"
|
||||
:customStyle="{ width: '72rpx', height: '72rpx' }"
|
||||
:customTextStyle="{
|
||||
fontSize: '24rpx',
|
||||
fontWeight: 'bold',
|
||||
color: '#fff',
|
||||
lineHeight: '34rpx',
|
||||
}"
|
||||
></avatarModule>
|
||||
</div>
|
||||
<div class="msg-read-detail-each-info">
|
||||
<span>{{ item.nickName }}</span>
|
||||
<span
|
||||
style="
|
||||
font-size: 20rpx;
|
||||
color: #747474;
|
||||
font-weight: 400;
|
||||
line-height: 28rpx;
|
||||
"
|
||||
>
|
||||
{{ item.jobNum }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</ZPaging>
|
||||
</div>
|
||||
</tm-sheet>
|
||||
</div>
|
||||
</tm-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
@ -591,6 +712,8 @@ import {
|
||||
detailGetRecordsContext,
|
||||
ServeClearTalkUnreadNum,
|
||||
ServeTalkRecords,
|
||||
ServeReadConditionList,
|
||||
ServeMessageReadDetail,
|
||||
} from '@/api/chat'
|
||||
import copy07 from '@/static/image/chatList/copy07@2x.png'
|
||||
import multipleChoices from '@/static/image/chatList/multipleChoices@2x.png'
|
||||
@ -604,12 +727,24 @@ import zu6053 from '@/static/image/chatList/zu6053@2x.png'
|
||||
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 {
|
||||
onLoad as uniOnload,
|
||||
onUnload as uniOnUnload,
|
||||
onShow,
|
||||
onHide,
|
||||
} 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 messageRecordElementRefsWithoutIndex = ref([])
|
||||
|
||||
const {
|
||||
getDialogueList,
|
||||
updateZpagingRef,
|
||||
@ -676,8 +811,65 @@ const state = ref({
|
||||
canUseQuillEditor: true, //是否可以使用quill编辑器,如果版本不支持,则使用普通输入框
|
||||
textAreaValue: '', //普通输入框的值
|
||||
isUseSpeech: true, //是否使用语音输入
|
||||
visibleElements: new Set(), //当前需要发送已读回执的可见元素
|
||||
lastUpdateTime: 0, // 用于节流
|
||||
setMessageReadInterval: null, // 设置别人发出的消息,我已读的定时器
|
||||
tempWaitDoRead: [], //当前需要发送已读回执的元素
|
||||
visibleOutElements: new Set(), //当前需要获取别人发送的已读回执列表的可见元素
|
||||
setOutMessageReadInterval: null, // 设置我发出的消息,别人已读的定时器
|
||||
tempWaitDoCheck: [], //当前获取别人发送已读回执的元素
|
||||
isShowMessageReadDetail: false, //是否显示消息已读未读详情
|
||||
msgReadDetailTabs: [
|
||||
{
|
||||
key: '1',
|
||||
title: '未读',
|
||||
},
|
||||
{
|
||||
key: '0',
|
||||
title: '已读',
|
||||
},
|
||||
],
|
||||
msgReadOrNotDetail: [], //消息已读未读详情
|
||||
currentMsgReadOrNotDetail: null, //当前消息已读未读详情
|
||||
readNumPage: 1, //已读未读详情分页
|
||||
hasMoreReadDetail: true, //是否还有更多已读未读详情
|
||||
currentIsUnread: 1, //当前已读未读状态
|
||||
readDetailHeight: 400, //已读未读详情高度
|
||||
isFirstEnter: true, //是否是第一次进入
|
||||
isFromHide: false, //是否从hide后恢复
|
||||
})
|
||||
|
||||
// 创建一个响应式的 Map 来维护已读数量
|
||||
const recordReadsMap = ref(new Map())
|
||||
|
||||
// 监听 Map 的变化,更新 UI
|
||||
watch(
|
||||
recordReadsMap,
|
||||
(newMap) => {
|
||||
requestAnimationFrame(() => {
|
||||
newMap.forEach((readNum, msgId) => {
|
||||
const element = document.getElementById(`zp-id-${msgId}`)
|
||||
if (element) {
|
||||
element.dataset.readNum = readNum
|
||||
const readNumElement = element.querySelector('.have_read_num')
|
||||
if (readNumElement) {
|
||||
if (talkParams.type === 1) {
|
||||
readNumElement.textContent = readNum > 0 ? '已读' : '未读'
|
||||
} else {
|
||||
readNumElement.textContent =
|
||||
'已读 (' + readNum + '/' + (Number(talkParams.num) - 1 > 0 ? Number(talkParams.num) - 1 : 0) + ')'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
|
||||
uniOnload(async (options) => {
|
||||
console.log('onLoad' + JSON.stringify(options))
|
||||
if (options.sessionId) {
|
||||
@ -999,7 +1191,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)
|
||||
@ -1013,15 +1207,19 @@ 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) {
|
||||
nextTick(() => {
|
||||
getQuill().setContents(delta)
|
||||
getQuill().setSelection(getQuill().getText().length, 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
// for (let i = 0; i < ops.length; i++) {
|
||||
// if (ops[i].insert === '\n') {
|
||||
@ -1381,6 +1579,57 @@ watch(
|
||||
state.value.localPageLoadDone = false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理新消息
|
||||
if (oldValue && oldValue.length > 0) {
|
||||
const lastOldMsgId = oldValue[0].msg_id
|
||||
const lastIndex = newValue.findIndex(
|
||||
(msg) => msg.msg_id === lastOldMsgId,
|
||||
)
|
||||
console.error(lastIndex, 'lastIndex')
|
||||
|
||||
if (lastIndex === -1) {
|
||||
// 没找到lastOldMsgId,说明都是新消息
|
||||
newValue.forEach((msg) => {
|
||||
nextTick(() => {
|
||||
const element = document.getElementById(`zp-id-${msg.msg_id}`)
|
||||
if (element) {
|
||||
messageRecordElementRefs.value.unshift(element)
|
||||
}
|
||||
})
|
||||
})
|
||||
} else if (lastIndex > 0) {
|
||||
// 找到了lastOldMsgId,它前面的都是新消息
|
||||
for (let i = 0; i < lastIndex; i++) {
|
||||
const msg = newValue[i]
|
||||
console.error(msg, '新消息msg')
|
||||
nextTick(() => {
|
||||
const element = document.getElementById(`zp-id-${msg.msg_id}`)
|
||||
if (element) {
|
||||
messageRecordElementRefs.value.unshift(element)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (lastIndex === 0) {
|
||||
messageRecordElementRefsWithoutIndex.value = []
|
||||
// 新消息和旧消息的id相同,说明没有比它更新的消息需要处理。但是资源文件是预创建的,需要处理
|
||||
for (let i = 0; i < newValue.length; i++) {
|
||||
if (!newValue[i].zp_index && newValue[i].zp_index !== 0) {
|
||||
if (newValue[i]?.file_num === newValue[i].msg_id) {
|
||||
return
|
||||
}
|
||||
nextTick(() => {
|
||||
const element = document.getElementById(
|
||||
`zp-id-${newValue[i].msg_id}`,
|
||||
)
|
||||
if (element) {
|
||||
messageRecordElementRefsWithoutIndex.value.unshift(element)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -1850,8 +2099,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
|
||||
}
|
||||
}
|
||||
@ -1875,9 +2124,260 @@ onMounted(async () => {
|
||||
|
||||
state.value.selectAreaHeight =
|
||||
rpxToPx(state.value.mentionSelectHeight) - rpxToPx(90) + 'px'
|
||||
|
||||
state.value.readDetailHeight =
|
||||
rpxToPx(state.value.mentionSelectHeight) - rpxToPx(300) + 'px'
|
||||
})
|
||||
|
||||
//设置观察者前设置定时器
|
||||
if (state.value.setMessageReadInterval) {
|
||||
clearInterval(state.value.setMessageReadInterval)
|
||||
state.value.setMessageReadInterval = null
|
||||
}
|
||||
state.value.setMessageReadInterval = setInterval(() => {
|
||||
checkVisibleElements()
|
||||
}, 2000)
|
||||
|
||||
if (state.value.setOutMessageReadInterval) {
|
||||
clearInterval(state.value.setOutMessageReadInterval)
|
||||
state.value.setOutMessageReadInterval = null
|
||||
}
|
||||
state.value.setOutMessageReadInterval = setInterval(() => {
|
||||
checkVisibleOutElements()
|
||||
}, 2000)
|
||||
|
||||
//初始化设置观察者
|
||||
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,
|
||||
},
|
||||
)
|
||||
watch(
|
||||
messageRecordElementRefsWithoutIndex,
|
||||
(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 = Number(el.dataset.talktype)
|
||||
const receiverId = Number(el.dataset.receiverid)
|
||||
// console.log(msgId, talkType, receiverId)
|
||||
if (waitDoRead.length === 0) {
|
||||
waitDoRead.push({
|
||||
msg_ids: [msgId],
|
||||
talk_type: talkType,
|
||||
receiver_id: receiverId,
|
||||
})
|
||||
} else {
|
||||
const existingItem = waitDoRead.find(
|
||||
(item) =>
|
||||
item.talk_type === talkType && item.receiver_id === receiverId,
|
||||
)
|
||||
if (existingItem) {
|
||||
existingItem.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 ||
|
||||
!doReadItem.msg_ids.every((id) => prevItem.msg_ids.includes(id))
|
||||
) {
|
||||
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 = Number(el.dataset.talktype)
|
||||
const receiverId = Number(el.dataset.receiverid)
|
||||
if (waitDoCheck.length === 0) {
|
||||
waitDoCheck.push({
|
||||
msg_ids: [msgId],
|
||||
talk_type: talkType,
|
||||
receiver_id: receiverId,
|
||||
})
|
||||
} else {
|
||||
const existingItem = waitDoCheck.find(
|
||||
(item) =>
|
||||
item.talk_type === talkType && item.receiver_id === receiverId,
|
||||
)
|
||||
if (existingItem) {
|
||||
existingItem.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 ||
|
||||
// !doCheckItem.msg_ids.every((id) => prevItem.msg_ids.includes(id))
|
||||
// ) {
|
||||
console.error('====调用了已读回执查询接口=====', doCheckItem)
|
||||
let params = Object.assign({}, doCheckItem, {
|
||||
talkType: doCheckItem.talk_type, //1私聊2群聊
|
||||
receiverId:
|
||||
doCheckItem.talk_type === 1
|
||||
? talkParams.uid
|
||||
: talkParams.receiver_id, //私聊的时候是对方用户id,群聊的时候是对方群id
|
||||
msgIds: doCheckItem.msg_ids,
|
||||
type: 'list', //list是列表,detail是详情
|
||||
})
|
||||
|
||||
const resp = ServeReadConditionList(params)
|
||||
// console.log(resp)
|
||||
resp
|
||||
.then(({ code, data }) => {
|
||||
// console.log(data)
|
||||
if (code == 200) {
|
||||
// 处理批量更新
|
||||
if (Array.isArray(data.data)) {
|
||||
console.error('处理批量更新', data.data)
|
||||
data.data.forEach((item) => {
|
||||
if (item.msgId && item.readNum !== undefined) {
|
||||
recordReadsMap.value.set(item.msgId, item.readNum)
|
||||
}
|
||||
})
|
||||
} else if (data.data && data.data.readNum !== undefined) {
|
||||
console.error('处理单个更新', data.data)
|
||||
doCheckItem.msg_ids.forEach((msgId) => {
|
||||
recordReadsMap.value.set(msgId, data.data.readNum)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
// }
|
||||
})
|
||||
}
|
||||
// 更新完成后再保存当前数据
|
||||
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 && entry.intersectionRatio >= 0.5) {
|
||||
// 确保元素有足够比例可见
|
||||
// 元素进入视口
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
// 元素离开视口,从集合中移除
|
||||
state.value.visibleElements.delete(entry.target)
|
||||
state.value.visibleOutElements.delete(entry.target)
|
||||
}
|
||||
})
|
||||
if (state.value.isFirstEnter) {
|
||||
state.value.isFirstEnter = false
|
||||
// 立即执行一次
|
||||
// checkVisibleElements()
|
||||
// checkVisibleOutElements()
|
||||
}
|
||||
}
|
||||
|
||||
const pxTorPx = (px) => {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
const rpx = px / (sysInfo.screenWidth / 750)
|
||||
@ -1905,6 +2405,53 @@ 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()
|
||||
}
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
if (state.value.isFromHide) {
|
||||
if (state.value.setMessageReadInterval) {
|
||||
clearInterval(state.value.setMessageReadInterval)
|
||||
state.value.setMessageReadInterval = null
|
||||
}
|
||||
state.value.setMessageReadInterval = setInterval(() => {
|
||||
checkVisibleElements()
|
||||
}, 2000)
|
||||
|
||||
if (state.value.setOutMessageReadInterval) {
|
||||
clearInterval(state.value.setOutMessageReadInterval)
|
||||
state.value.setOutMessageReadInterval = null
|
||||
}
|
||||
state.value.setOutMessageReadInterval = setInterval(() => {
|
||||
checkVisibleOutElements()
|
||||
}, 2000)
|
||||
state.value.isFromHide = false
|
||||
}
|
||||
})
|
||||
onHide(() => {
|
||||
state.value.isFromHide = true
|
||||
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()
|
||||
}
|
||||
})
|
||||
|
||||
// 修改防抖函数的实现
|
||||
@ -1968,6 +2515,82 @@ const onTextAreaMention = (user) => {
|
||||
state.value.isShowMentionSelect = false
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏消息已读未读详情
|
||||
const hideMessageReadDetail = () => {
|
||||
state.value.isShowMessageReadDetail = false
|
||||
}
|
||||
|
||||
// 显示消息已读未读详情
|
||||
const toShowMessageReadDetail = (item) => {
|
||||
console.log(item)
|
||||
if (talkParams.type === 1) {
|
||||
return
|
||||
}
|
||||
state.value.isShowMessageReadDetail = true
|
||||
state.value.currentMsgReadOrNotDetail = item
|
||||
getMessageReadDetail(1)
|
||||
}
|
||||
//切换消息已读/未读详情
|
||||
const msgReadDetailTabsChange = (e) => {
|
||||
// console.log(e)
|
||||
if (Number(e) !== Number(state.value.currentIsUnread)) {
|
||||
state.value.readNumPage = 1
|
||||
}
|
||||
state.value.currentIsUnread = Number(e)
|
||||
getMessageReadDetail(e)
|
||||
}
|
||||
//获取消息已读未读详情
|
||||
const getMessageReadDetail = (isUnread) => {
|
||||
let params = {
|
||||
page: state.value.readNumPage,
|
||||
pageSize: 10,
|
||||
type: 'detail', //list是列表,detail是详情
|
||||
talkType: state.value.currentMsgReadOrNotDetail.talk_type, //1私聊2群聊
|
||||
receiverId: state.value.currentMsgReadOrNotDetail.receiver_id, //私聊的时候是对方用户id,群聊的时候是对方群id
|
||||
msgId: state.value.currentMsgReadOrNotDetail.msg_id,
|
||||
isUnread: Number(isUnread), //不送或者送0代表看已读,送1看未读
|
||||
}
|
||||
const resp = ServeMessageReadDetail(params)
|
||||
// console.log(resp)
|
||||
resp
|
||||
.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
if (Number(isUnread) === 0) {
|
||||
state.value.msgReadDetailTabs[0].title =
|
||||
'未读 (' + (Number(talkParams.num) - 1 - data.count > 0 ? Number(talkParams.num) - 1 - data.count : 0) + ')'
|
||||
state.value.msgReadDetailTabs[1].title = '已读 (' + data.count + ')'
|
||||
} else if (Number(isUnread) === 1) {
|
||||
state.value.msgReadDetailTabs[0].title = '未读 (' + data.count + ')'
|
||||
state.value.msgReadDetailTabs[1].title =
|
||||
'已读 (' + (Number(talkParams.num) - 1 - data.count > 0 ? Number(talkParams.num) - 1 - data.count : 0) + ')'
|
||||
}
|
||||
if (state.value.readNumPage === 1) {
|
||||
state.value.msgReadOrNotDetail = data.data
|
||||
} else {
|
||||
state.value.msgReadOrNotDetail = [
|
||||
...state.value.msgReadOrNotDetail,
|
||||
...data.data,
|
||||
]
|
||||
}
|
||||
if (!data?.data || data?.data?.length < 10) {
|
||||
state.value.hasMoreReadDetail = false
|
||||
} else {
|
||||
state.value.hasMoreReadDetail = true
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
//滚动到底部加载更多已读未读详情
|
||||
const loadMoreReadDetails = () => {
|
||||
if (!state.value.hasMoreReadDetail) {
|
||||
return
|
||||
}
|
||||
state.value.readNumPage += 1
|
||||
getMessageReadDetail(state.value.currentIsUnread)
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.dialog-page {
|
||||
@ -2157,6 +2780,15 @@ const onTextAreaMention = (user) => {
|
||||
}
|
||||
}
|
||||
|
||||
.have_read_num {
|
||||
text-align: right;
|
||||
color: #7a58de;
|
||||
font-size: 22rpx;
|
||||
font-weight: 400;
|
||||
line-height: 34rpx;
|
||||
margin: 5rpx 0 0;
|
||||
}
|
||||
|
||||
.talk-reply {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@ -2364,4 +2996,38 @@ const onTextAreaMention = (user) => {
|
||||
:deep(.mention) {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.msg-read-detail-tabs {
|
||||
padding: 32rpx 0 0;
|
||||
:deep(.noNvueBorder) {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
.msg-read-detail {
|
||||
padding: 28rpx 32rpx;
|
||||
.msg-read-detail-each {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 20rpx 32rpx;
|
||||
border-bottom: 1rpx solid #f8f8f8;
|
||||
.msg-read-detail-each-avatar {
|
||||
margin: 0 20rpx 0 0;
|
||||
}
|
||||
.msg-read-detail-each-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
span {
|
||||
font-size: 28rpx;
|
||||
color: #191919;
|
||||
font-weight: 500;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -156,6 +156,7 @@
|
||||
"popup.title.phone": "电话",
|
||||
"pageTitle.view.deps": "查看部门",
|
||||
"group.dismiss.confirm": "确定解散本群",
|
||||
"chat.msgRead.list": "消息接收人列表",
|
||||
"chat.settings.report": "投诉",
|
||||
"complaint": {
|
||||
"title": "投诉举报",
|
||||
|
Loading…
Reference in New Issue
Block a user