完成新版已读回执规则接入,处理相应的socket消息监听、相关已读未读数量统计和详情列表展示
This commit is contained in:
parent
bdfd604fd9
commit
331ca65db6
@ -94,3 +94,8 @@ export const ServeConfirmVoteHandle = (data = {}) => {
|
|||||||
export const ServeEmptyMessage = (data) => {
|
export const ServeEmptyMessage = (data) => {
|
||||||
return post('/api/v1/talk/message/empty', data)
|
return post('/api/v1/talk/message/empty', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//获取消息已读未读详情
|
||||||
|
export const ServeMessageReadDetail = (data) => {
|
||||||
|
return post('/api/v1/talk/my-records/read/condition', data)
|
||||||
|
}
|
||||||
|
@ -327,7 +327,7 @@ import { parseTime } from '@/utils/datetime'
|
|||||||
import { fileFormatSize, fileSuffix } from '@/utils/strings'
|
import { fileFormatSize, fileSuffix } from '@/utils/strings'
|
||||||
import { NImage, NInfiniteScroll, NScrollbar, NIcon, NDatePicker } from 'naive-ui'
|
import { NImage, NInfiniteScroll, NScrollbar, NIcon, NDatePicker } from 'naive-ui'
|
||||||
|
|
||||||
const emits = defineEmits(['clearSearchMemberByAlphabet', 'getDisabledDateArray'])
|
const emits = defineEmits(['clearSearchMemberByAlphabet', 'getDisabledDateArray', 'hideSearchResultModal'])
|
||||||
|
|
||||||
const dialogueStore = useDialogueStore()
|
const dialogueStore = useDialogueStore()
|
||||||
// 当前对话参数
|
// 当前对话参数
|
||||||
@ -736,15 +736,29 @@ const downloadAndOpenFile = (item) => {
|
|||||||
|
|
||||||
//跳转到对应的记录位置
|
//跳转到对应的记录位置
|
||||||
const toDialogueByMember = async (msgInfo) => {
|
const toDialogueByMember = async (msgInfo) => {
|
||||||
const sessionId = await getSessionId(dialogueParams.talk_type, dialogueParams.receiver_id)
|
console.error('====跳转到对应的记录位置====', msgInfo)
|
||||||
uni.navigateTo({
|
// 根据搜索结果, 指定用于查询指定消息上下文的sequence
|
||||||
url:
|
dialogueStore.specifiedMsg = encodeURIComponent(
|
||||||
'/pages/dialog/index?sessionId=' +
|
JSON.stringify({
|
||||||
sessionId +
|
talk_type: msgInfo.talk_type,
|
||||||
'&keepDialogInfo=1' +
|
receiver_id: msgInfo.receiver_id,
|
||||||
'&msgInfo=' +
|
msg_id: msgInfo.msg_id,
|
||||||
encodeURIComponent(JSON.stringify(msgInfo))
|
cursor: msgInfo.sequence - 15 > 0 ? msgInfo.sequence - 15 : 0,
|
||||||
})
|
direction: 'down',
|
||||||
|
sort_sequence: 'asc',
|
||||||
|
create_time: msgInfo.created_at
|
||||||
|
})
|
||||||
|
)
|
||||||
|
emits('hideSearchResultModal')
|
||||||
|
// const sessionId = await getSessionId(dialogueParams.talk_type, dialogueParams.receiver_id)
|
||||||
|
// uni.navigateTo({
|
||||||
|
// url:
|
||||||
|
// '/pages/dialog/index?sessionId=' +
|
||||||
|
// sessionId +
|
||||||
|
// '&keepDialogInfo=1' +
|
||||||
|
// '&msgInfo=' +
|
||||||
|
// encodeURIComponent(JSON.stringify(msgInfo))
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
//重置部分搜索条件
|
//重置部分搜索条件
|
||||||
|
@ -7,6 +7,7 @@ import EventTalk from './event/talk'
|
|||||||
import EventKeyboard from './event/keyboard'
|
import EventKeyboard from './event/keyboard'
|
||||||
import EventLogin from './event/login'
|
import EventLogin from './event/login'
|
||||||
import EventRevoke from './event/revoke'
|
import EventRevoke from './event/revoke'
|
||||||
|
import EventRead from './event/read'
|
||||||
import { getAccessToken, isLoggedIn } from './utils/auth'
|
import { getAccessToken, isLoggedIn } from './utils/auth'
|
||||||
|
|
||||||
const urlCallback = () => {
|
const urlCallback = () => {
|
||||||
@ -85,6 +86,7 @@ class Connect {
|
|||||||
this.onImContactStatus()
|
this.onImContactStatus()
|
||||||
this.onImMessageRevoke()
|
this.onImMessageRevoke()
|
||||||
this.onImMessageKeyboard()
|
this.onImMessageKeyboard()
|
||||||
|
this.onImMessageListenRead()
|
||||||
this.onImMessageListenReadIncr()
|
this.onImMessageListenReadIncr()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,11 +134,14 @@ class Connect {
|
|||||||
this.conn.on('im.message.revoke', (data: any) => new EventRevoke(data))
|
this.conn.on('im.message.revoke', (data: any) => new EventRevoke(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onImMessageListenRead() {
|
||||||
|
// 消息已读回执监听事件(全量)
|
||||||
|
this.conn.on('im.message.listen.read', (data: any) => new EventRead(data, 'total'))
|
||||||
|
}
|
||||||
|
|
||||||
onImMessageListenReadIncr() {
|
onImMessageListenReadIncr() {
|
||||||
// 消息已读回执监听事件
|
// 消息已读回执监听事件(增量)
|
||||||
this.conn.on('im.message.listen.read.incr', (data: any) => {
|
this.conn.on('im.message.listen.read.incr', (data: any) => new EventRead(data, 'incr'))
|
||||||
console.error('====接收到了新版已读回执增量=====', data)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onImContactApply() {
|
onImContactApply() {
|
||||||
|
54
src/event/read.js
Normal file
54
src/event/read.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import Base from './base'
|
||||||
|
import { useTalkStore, useDialogueStore } from '@/store'
|
||||||
|
import ws from '@/connect'
|
||||||
|
import { bus } from '@/utils/event-bus'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已读回执事件
|
||||||
|
*/
|
||||||
|
class Read extends Base {
|
||||||
|
/**
|
||||||
|
* @var resource 资源
|
||||||
|
*/
|
||||||
|
resource
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景类型
|
||||||
|
*/
|
||||||
|
type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化构造方法
|
||||||
|
*
|
||||||
|
* @param {Object} resource Socket消息
|
||||||
|
*/
|
||||||
|
constructor(resource, type) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.resource = resource
|
||||||
|
this.type = type
|
||||||
|
this.handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
handle() {
|
||||||
|
if (this.type == 'total') {
|
||||||
|
console.error('====接收到了新版已读回执全量=====', this.resource)
|
||||||
|
const readList = this.resource.result
|
||||||
|
if (readList.length > 0) {
|
||||||
|
readList.forEach((item) => {
|
||||||
|
useDialogueStore().updateDialogueRecord({
|
||||||
|
msg_id: item.msg_id,
|
||||||
|
read_total_num: item.read_total_num
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (this.type == 'incr') {
|
||||||
|
console.error('====接收到了新版已读回执增量=====', this.resource)
|
||||||
|
// 由于直接使用增量的数值,会导致消息列表的已读回执数量不准确,可能多可能少
|
||||||
|
// 所以收到增量消息后,直接手动触发一次查询全量
|
||||||
|
bus.emit('check-visible-out-elements', 'incr')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Read
|
@ -49,8 +49,8 @@ export interface ITalkRecord {
|
|||||||
send_status: number
|
send_status: number
|
||||||
float: string,
|
float: string,
|
||||||
is_convert_text?:number//语音记录的 是否是在展示转文本状态 1:是 0:否,
|
is_convert_text?:number//语音记录的 是否是在展示转文本状态 1:是 0:否,
|
||||||
erp_user_id:number
|
erp_user_id:number,
|
||||||
|
read_total_num:number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITalkRecordExtraText {
|
export interface ITalkRecordExtraText {
|
||||||
|
@ -481,6 +481,12 @@ const onDatePickShow = (show) => {
|
|||||||
// state.nowDateTime = new Date()
|
// state.nowDateTime = new Date()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 隐藏搜索结果模态框
|
||||||
|
const hideSearchResultModal = () => {
|
||||||
|
handleSearchRecordByConditionModalClose()
|
||||||
|
state.isShowGroupAside = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -506,6 +512,7 @@ const onDatePickShow = (show) => {
|
|||||||
:receiver_id="talkParams.receiver_id"
|
:receiver_id="talkParams.receiver_id"
|
||||||
:index_name="talkParams.index_name"
|
:index_name="talkParams.index_name"
|
||||||
:specifiedMsg="talkParams.specifiedMsg"
|
:specifiedMsg="talkParams.specifiedMsg"
|
||||||
|
:num="talkParams.num"
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@ -706,6 +713,7 @@ const onDatePickShow = (show) => {
|
|||||||
@getDisabledDateArray="getDisabledDateArray"
|
@getDisabledDateArray="getDisabledDateArray"
|
||||||
:selectedDateTime="state.selectedDateTime"
|
:selectedDateTime="state.selectedDateTime"
|
||||||
:nowDateTime="state.nowDateTime"
|
:nowDateTime="state.nowDateTime"
|
||||||
|
@hideSearchResultModal="hideSearchResultModal"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</n-card>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch, onMounted, ref, nextTick, onUnmounted } from 'vue'
|
import { watch, onMounted, ref, nextTick, onUnmounted } from 'vue'
|
||||||
import { NDropdown, NCheckbox } from 'naive-ui'
|
import { NDropdown, NCheckbox, NPopover, NInfiniteScroll } from 'naive-ui'
|
||||||
import { Loading, MoreThree, ToTop } from '@icon-park/vue-next'
|
import { Loading, MoreThree, ToTop } from '@icon-park/vue-next'
|
||||||
import { bus } from '@/utils/event-bus'
|
import { bus } from '@/utils/event-bus'
|
||||||
import { useDialogueStore } from '@/store'
|
import { useDialogueStore } from '@/store'
|
||||||
@ -16,16 +16,17 @@ import { useInject, useTalkRecord, useUtil } from '@/hooks'
|
|||||||
import { ExclamationCircleFilled } from '@ant-design/icons-vue'
|
import { ExclamationCircleFilled } from '@ant-design/icons-vue'
|
||||||
import { useUserStore, useUploadsStore } from '@/store'
|
import { useUserStore, useUploadsStore } from '@/store'
|
||||||
import RevokeMessage from '@/components/talk/message/RevokeMessage.vue'
|
import RevokeMessage from '@/components/talk/message/RevokeMessage.vue'
|
||||||
import { voiceToText } from '@/api/chat.js'
|
import { voiceToText, ServeMessageReadDetail } from '@/api/chat.js'
|
||||||
import { confirmBox } from '@/components/confirm-box/service.js'
|
import { confirmBox } from '@/components/confirm-box/service.js'
|
||||||
import ws from '@/connect'
|
import ws from '@/connect'
|
||||||
|
import avatarModule from '@/components/avatar-module/index.vue'
|
||||||
|
|
||||||
// 定义消息已读状态接口
|
// 定义消息已读状态接口
|
||||||
interface ReadStatus {
|
interface ReadStatus {
|
||||||
msg_ids: string[]
|
msg_ids: string[]
|
||||||
talk_type: number
|
talk_type: number
|
||||||
receiver_id: number,
|
receiver_id: number
|
||||||
user_id: number
|
user_id?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义状态接口
|
// 定义状态接口
|
||||||
@ -40,6 +41,12 @@ interface State {
|
|||||||
isScrolling: boolean
|
isScrolling: boolean
|
||||||
scrollTimer: number | null
|
scrollTimer: number | null
|
||||||
lastTriggerTime: number
|
lastTriggerTime: number
|
||||||
|
talkReadListDetail: any[]
|
||||||
|
readDetailIsUnread: number
|
||||||
|
currentMsgReadDetail: any | null
|
||||||
|
currentReadDetailPage: number
|
||||||
|
hasMoreReadListDetail: boolean
|
||||||
|
loadingReadListDetail: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -135,7 +142,7 @@ const onPanelScroll = (e: any) => {
|
|||||||
checkVisibleOutElements()
|
checkVisibleOutElements()
|
||||||
// 重置节流时间戳,保证下一次变化能立刻触发
|
// 重置节流时间戳,保证下一次变化能立刻触发
|
||||||
lastVisibleOutTriggerTime = Date.now()
|
lastVisibleOutTriggerTime = Date.now()
|
||||||
}, 150) // 150ms 的防抖时间
|
}, 300) // 300ms 的防抖时间
|
||||||
|
|
||||||
// 检测是否到达底部
|
// 检测是否到达底部
|
||||||
if (skipBottom.value == false) {
|
if (skipBottom.value == false) {
|
||||||
@ -387,47 +394,18 @@ const state = ref<State>({
|
|||||||
lastUpdateTime: 0,
|
lastUpdateTime: 0,
|
||||||
isScrolling: false,
|
isScrolling: false,
|
||||||
scrollTimer: null,
|
scrollTimer: null,
|
||||||
lastTriggerTime: 0
|
lastTriggerTime: 0,
|
||||||
|
talkReadListDetail: [],
|
||||||
|
readDetailIsUnread: 1,
|
||||||
|
currentMsgReadDetail: null,
|
||||||
|
currentReadDetailPage: 1,
|
||||||
|
hasMoreReadListDetail: true,
|
||||||
|
loadingReadListDetail: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// 创建一个响应式的 Map 来维护已读数量
|
|
||||||
const recordReadsMap = ref<Map<string, number>>(new Map())
|
|
||||||
|
|
||||||
// 定义观察者变量
|
// 定义观察者变量
|
||||||
let observer: IntersectionObserver | null = null
|
let observer: IntersectionObserver | null = null
|
||||||
|
|
||||||
// 监听 Map 的变化,更新 UI
|
|
||||||
watch(
|
|
||||||
recordReadsMap,
|
|
||||||
(newMap) => {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
newMap.forEach((readNum, msgId) => {
|
|
||||||
const element = document.getElementById(msgId)
|
|
||||||
if (element) {
|
|
||||||
element.dataset.readNum = String(readNum)
|
|
||||||
const readNumElement = element.querySelector('.have_read_num')
|
|
||||||
if (readNumElement) {
|
|
||||||
if (props.talk_type === 1) {
|
|
||||||
readNumElement.textContent = readNum > 0 ? '已读' : '未读'
|
|
||||||
} else {
|
|
||||||
readNumElement.textContent =
|
|
||||||
'已读 (' +
|
|
||||||
readNum +
|
|
||||||
'/' +
|
|
||||||
(Number(props.num) - 1 > 0 ? Number(props.num) - 1 : 0) +
|
|
||||||
')'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 检查需要发送已读回执的元素
|
// 检查需要发送已读回执的元素
|
||||||
const checkVisibleElements = () => {
|
const checkVisibleElements = () => {
|
||||||
if (state.value.visibleElements.size > 0) {
|
if (state.value.visibleElements.size > 0) {
|
||||||
@ -512,36 +490,8 @@ const checkVisibleOutElements = () => {
|
|||||||
})
|
})
|
||||||
if (waitDoCheck.length > 0) {
|
if (waitDoCheck.length > 0) {
|
||||||
waitDoCheck.forEach((doCheckItem) => {
|
waitDoCheck.forEach((doCheckItem) => {
|
||||||
// console.error('====调用了已读回执查询接口=====', doCheckItem)
|
|
||||||
console.error('====组装了新版已读回执参数,需要发送socket=====', doCheckItem)
|
console.error('====组装了新版已读回执参数,需要发送socket=====', doCheckItem)
|
||||||
ws.emit('im.message.listen.read', doCheckItem)
|
ws.emit('im.message.listen.read', doCheckItem)
|
||||||
// let params = Object.assign({}, doCheckItem, {
|
|
||||||
// talkType: doCheckItem.talk_type,
|
|
||||||
// receiverId: doCheckItem.talk_type === 1 ? props.uid : props.receiver_id,
|
|
||||||
// msgIds: doCheckItem.msg_ids,
|
|
||||||
// type: 'list'
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const resp = ServeReadConditionList(params)
|
|
||||||
// resp
|
|
||||||
// .then(({ code, 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))
|
state.value.tempWaitDoCheck = JSON.parse(JSON.stringify(waitDoCheck))
|
||||||
@ -552,17 +502,21 @@ const checkVisibleOutElements = () => {
|
|||||||
let lastVisibleOutTriggerTime = 0
|
let lastVisibleOutTriggerTime = 0
|
||||||
|
|
||||||
//新版采用socket监听已读回执,不轮询接口
|
//新版采用socket监听已读回执,不轮询接口
|
||||||
watch(() => state.value.visibleOutElements, (newVal) => {
|
watch(
|
||||||
const now = Date.now()
|
() => state.value.visibleOutElements,
|
||||||
if (now - lastVisibleOutTriggerTime < 1000) {
|
(newVal) => {
|
||||||
return
|
const now = Date.now()
|
||||||
|
if (now - lastVisibleOutTriggerTime < 1000) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastVisibleOutTriggerTime = now
|
||||||
|
checkVisibleOutElements()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
}
|
}
|
||||||
lastVisibleOutTriggerTime = now
|
)
|
||||||
checkVisibleOutElements()
|
|
||||||
}, {
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
})
|
|
||||||
|
|
||||||
// 观察者函数
|
// 观察者函数
|
||||||
const handleIntersection = (entries) => {
|
const handleIntersection = (entries) => {
|
||||||
@ -617,7 +571,20 @@ watch(
|
|||||||
{ deep: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 事件总线防抖处理
|
||||||
|
let eventBusDebounceTimer: number | null = null
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 事件总线监听
|
||||||
|
bus.subscribe('check-visible-out-elements', (type) => {
|
||||||
|
if (eventBusDebounceTimer) {
|
||||||
|
clearTimeout(eventBusDebounceTimer)
|
||||||
|
}
|
||||||
|
eventBusDebounceTimer = window.setTimeout(() => {
|
||||||
|
checkVisibleOutElements()
|
||||||
|
eventBusDebounceTimer = null
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
//设置观察者前设置定时器
|
//设置观察者前设置定时器
|
||||||
if (state.value.setMessageReadInterval) {
|
if (state.value.setMessageReadInterval) {
|
||||||
clearInterval(state.value.setMessageReadInterval)
|
clearInterval(state.value.setMessageReadInterval)
|
||||||
@ -669,7 +636,77 @@ onUnmounted(() => {
|
|||||||
state.value.setOutMessageReadInterval = null
|
state.value.setOutMessageReadInterval = null
|
||||||
checkVisibleOutElements()
|
checkVisibleOutElements()
|
||||||
}
|
}
|
||||||
|
// 清理事件总线防抖定时器
|
||||||
|
if (eventBusDebounceTimer) {
|
||||||
|
clearTimeout(eventBusDebounceTimer)
|
||||||
|
eventBusDebounceTimer = null
|
||||||
|
}
|
||||||
|
// 事件总线移除监听
|
||||||
|
bus.unsubscribe('check-visible-out-elements', checkVisibleOutElements)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//点击显示对应消息的已读回执详情
|
||||||
|
const toShowMessageReadDetail = async (item?: ITalkRecord) => {
|
||||||
|
if (item) {
|
||||||
|
state.value.currentMsgReadDetail = item
|
||||||
|
onReadTabChange('unread-tab')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
page: state.value.currentReadDetailPage,
|
||||||
|
pageSize: 10,
|
||||||
|
type: 'detail', //list是列表,detail是详情
|
||||||
|
talkType: state?.value?.currentMsgReadDetail?.talk_type, //1私聊2群聊
|
||||||
|
receiverId: state?.value?.currentMsgReadDetail?.receiver_id, //私聊的时候是对方用户id,群聊的时候是对方群id
|
||||||
|
msgId: state?.value?.currentMsgReadDetail?.msg_id,
|
||||||
|
isUnread: state?.value?.readDetailIsUnread //不送或者送0代表看已读,送1看未读
|
||||||
|
}
|
||||||
|
console.log(params)
|
||||||
|
const resp = await ServeMessageReadDetail(params)
|
||||||
|
console.log(resp)
|
||||||
|
if (resp.code === 200) {
|
||||||
|
console.log(resp?.data?.data?.length)
|
||||||
|
if (resp?.data?.data?.length > 0) {
|
||||||
|
state.value.hasMoreReadListDetail = true
|
||||||
|
if (state.value.currentReadDetailPage === 1) {
|
||||||
|
state.value.talkReadListDetail = resp.data.data
|
||||||
|
} else {
|
||||||
|
state.value.talkReadListDetail = [...state.value.talkReadListDetail, ...resp.data.data]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (state.value.currentReadDetailPage === 1) {
|
||||||
|
state.value.talkReadListDetail = []
|
||||||
|
}
|
||||||
|
state.value.hasMoreReadListDetail = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//已读回执tab切换
|
||||||
|
const onReadTabChange = (value) => {
|
||||||
|
if (value === 'unread-tab') {
|
||||||
|
//未读
|
||||||
|
state.value.readDetailIsUnread = 1
|
||||||
|
} else if (value === 'read-tab') {
|
||||||
|
//已读
|
||||||
|
state.value.readDetailIsUnread = 0
|
||||||
|
}
|
||||||
|
state.value.currentReadDetailPage = 1
|
||||||
|
toShowMessageReadDetail()
|
||||||
|
}
|
||||||
|
|
||||||
|
//加载更多已读回执
|
||||||
|
const loadMoreReadListDetail = () => {
|
||||||
|
console.log('loadMoreReadListDetail')
|
||||||
|
if (!state.value.hasMoreReadListDetail || state.value.loadingReadListDetail) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state.value.loadingReadListDetail = true
|
||||||
|
state.value.currentReadDetailPage++
|
||||||
|
toShowMessageReadDetail().finally(() => {
|
||||||
|
state.value.loadingReadListDetail = false
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -821,6 +858,72 @@ onUnmounted(() => {
|
|||||||
{{ item.extra?.reply?.content }}
|
{{ item.extra?.reply?.content }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 已读回执 -->
|
||||||
|
<div class="talk_read_num" v-if="item.user_id === props.uid">
|
||||||
|
<n-popover trigger="click" placement="bottom-end" style="height: 382px; padding: 0;">
|
||||||
|
<template #trigger>
|
||||||
|
<span v-if="props.talk_type === 1">{{
|
||||||
|
item.read_total_num > 0 ? '已读' : '未读'
|
||||||
|
}}</span>
|
||||||
|
<span v-if="props.talk_type === 2" @click="toShowMessageReadDetail(item)">
|
||||||
|
已读 ({{ item?.read_total_num || 0 }}/{{
|
||||||
|
props.num - 1 > 0 ? props.num - 1 : 0
|
||||||
|
}})
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<div class="talk-read-list-detail">
|
||||||
|
<n-tabs
|
||||||
|
type="line"
|
||||||
|
animated
|
||||||
|
justify-content="space-around"
|
||||||
|
@update:value="onReadTabChange"
|
||||||
|
>
|
||||||
|
<n-tab name="unread-tab">
|
||||||
|
{{ `未读(${props.num - 1 - (item.read_total_num || 0) || 0})` }}
|
||||||
|
</n-tab>
|
||||||
|
<n-tab name="read-tab">
|
||||||
|
{{ `已读(${item.read_total_num || 0})` }}
|
||||||
|
</n-tab>
|
||||||
|
</n-tabs>
|
||||||
|
<div class="talk-read-list">
|
||||||
|
<n-infinite-scroll style="height: 340px;" @load="loadMoreReadListDetail">
|
||||||
|
<div
|
||||||
|
class="talk-read-list-item"
|
||||||
|
v-for="(talkReadDetailItem,
|
||||||
|
talkReadDetailIndex) in state.talkReadListDetail"
|
||||||
|
:key="talkReadDetailIndex"
|
||||||
|
>
|
||||||
|
<avatarModule
|
||||||
|
:mode="1"
|
||||||
|
:avatar="talkReadDetailItem.avatar"
|
||||||
|
:userName="talkReadDetailItem.nickName"
|
||||||
|
:groupType="0"
|
||||||
|
:customStyle="{
|
||||||
|
width: '36px',
|
||||||
|
height: '36px'
|
||||||
|
}"
|
||||||
|
:customTextStyle="{
|
||||||
|
fontSize: '12px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#fff',
|
||||||
|
lineHeight: '17px'
|
||||||
|
}"
|
||||||
|
></avatarModule>
|
||||||
|
<div class="talk-read-list-item-info">
|
||||||
|
<span style="font-size: 12px; font-weight: 600; line-height: 17px;">{{
|
||||||
|
talkReadDetailItem.nickName
|
||||||
|
}}</span>
|
||||||
|
<span style="font-size: 12px; color: #999; line-height: 14px;">{{
|
||||||
|
talkReadDetailItem.jobNum
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-infinite-scroll>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-popover>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -1019,6 +1122,18 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.talk_read_num {
|
||||||
|
text-align: right;
|
||||||
|
color: #7a58de;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 17px;
|
||||||
|
margin: 5px 0 0;
|
||||||
|
span {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.talk-title {
|
.talk-title {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -1067,4 +1182,31 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.talk-read-list-detail {
|
||||||
|
width: 341px;
|
||||||
|
padding: 0 14px;
|
||||||
|
|
||||||
|
.talk-read-list {
|
||||||
|
.talk-read-list-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-bottom: 1px solid #f1f1f1;
|
||||||
|
|
||||||
|
.talk-read-list-item-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user