Compare commits

..

No commits in common. "f511244d5996ebac51d5c0f83374367ffa5a2212" and "a5a70391a4b5c439b93c69fe5ea4b9498a832fd1" have entirely different histories.

17 changed files with 108 additions and 659 deletions

View File

@ -94,8 +94,3 @@ export const ServeConfirmVoteHandle = (data = {}) => {
export const ServeEmptyMessage = (data) => {
return post('/api/v1/talk/message/empty', data)
}
//获取消息已读未读详情
export const ServeMessageReadDetail = (data) => {
return post('/api/v1/talk/my-records/read/condition', data)
}

View File

@ -5,16 +5,6 @@ export const ServeSeachQueryAll = (data = {}) => {
return post('/api/v1/elasticsearch/query-all', data)
}
// ES搜索用户数据
export const ServeQueryUser = (data) => {
return post('/api/v1/elasticsearch/query-user', data)
}
// ES搜索群组数据
export const ServeQueryGroup = (data) => {
return post('/api/v1/elasticsearch/query-group', data)
}
//ES搜索聊天记录-主页搜索什么都有、聊天记录
export const ServeQueryTalkRecord = (data = {}) => {
return post('/api/v1/elasticsearch/query-talk-record', data)

View File

@ -11,14 +11,13 @@ import {
ServeSecedeGroup,
ServeUpdateGroupCard,
ServeGetGroupNotices,
ServeEditGroup,
ServeDismissGroup
ServeEditGroup
} from '@/api/group'
import { useInject } from '@/hooks'
import customModal from '@/components/common/customModal.vue'
import avatarModule from '@/components/avatar-module/index.vue'
import UserCardModal from '@/components/user/UserCardModal.vue'
import { ServeEmptyMessage, ServeTopTalkList, ServeSetNotDisturb } from '@/api/chat'
import { ServeEmptyMessage } from '@/api/chat'
import { parseTime } from '@/utils/datetime'
const userStore = useUserStore()
@ -49,12 +48,6 @@ const props = defineProps({
}
})
const talkParams = reactive({
isTop: computed(() => talkStore.findItem(props.talkType + '_' + props.gid)?.is_top),
isDisturb: computed(() => talkStore.findItem(props.talkType + '_' + props.gid)?.is_disturb),
sessionId: computed(() => talkStore.findItem(props.talkType + '_' + props.gid)?.id)
})
watch(props, () => {
if (props.talkType === 2) {
loadDetail()
@ -105,8 +98,7 @@ const state = reactive({
}, //
editGroupName: false, //
editGroupNameValue: '', //
chatSettingOperateType: '', //
isLastAdmin: false //
chatSettingOperateType: '' //
})
const members = ref<any[]>([])
@ -185,36 +177,17 @@ const onClose = () => {
emit('close')
}
const onSignOut = (closeLoading) => {
const onSignOut = () => {
ServeSecedeGroup({
group_id: props.gid
}).then((res) => {
if (res.code == 200) {
window['$message'].success('已退出群聊')
onClose()
} else {
window['$message'].error(res.message)
}
})
.then((res) => {
closeLoading()
if (res.code == 200) {
window['$message'].success('已退出群聊')
state.isShowModal = false
onClose()
} else {
window['$message'].error(res.message || res.msg)
}
})
.catch((err) => {
closeLoading()
window['$message'].error(err.message)
})
}
const onDismiss = async (closeLoading) => {
const { code, message } = await ServeDismissGroup({ group_id: props.gid })
closeLoading()
if (code === 200) {
onClose()
state.isShowModal = false
window['$message'].success('群聊已解散')
} else {
window['$message'].info(message)
}
}
const onChangeRemark = () => {
@ -263,18 +236,8 @@ const handleModalConfirm = (closeLoading) => {
closeLoading()
window['$message'].error(err.message)
})
} else if (state.chatSettingOperateType == 'quit') {
} else if (state.chatSettingOperateType == 'quit'){
//退
if (state.isLastAdmin) {
//退
onDismiss(closeLoading)
} else {
//退
onSignOut(closeLoading)
}
} else if (state.chatSettingOperateType == 'dismiss') {
//
onDismiss(closeLoading)
}
}
@ -293,15 +256,12 @@ const showChatSettingOperateModal = (type: string) => {
break
case 'quit':
state.chatSettingOperateHint = '确定退出群聊'
const findOtherAdmin = groupMemberList.value.find(
(item) => (item.leader === 2 || item.leader === 1) && item.user_id !== userStore.uid
)
if (findOtherAdmin) {
state.isLastAdmin = false
state.chatSettingOperateSubHint = '退出后,聊天记录将被清空'
} else {
state.isLastAdmin = true
const findAdmin = groupMemberList.value.find((item) => item.leader === 2 || item.leader === 1)
const isLastAdmin = findAdmin && findAdmin.user_id === userStore.uid
if (isLastAdmin) {
state.chatSettingOperateSubHint = '退出后,本群将被解散'
} else {
state.chatSettingOperateSubHint = '退出后,聊天记录将被清空'
}
break
}
@ -420,42 +380,6 @@ const handleEditGroupNameConfirm = () => {
}
})
}
//
const onTopChange = (value: boolean) => {
ServeTopTalkList({
list_id: talkParams.sessionId,
type: value ? 1 : 2
}).then(({ code, message }) => {
if (code == 200) {
talkStore.updateItem({
index_name: props.talkType + '_' + props.gid,
is_top: talkParams.isTop == 0 ? 1 : 0
})
} else {
window['$message'].error(message)
}
})
}
//
const onDisturbChange = (value: boolean) => {
ServeSetNotDisturb({
talk_type: props.talkType,
receiver_id: props.gid,
is_disturb: value ? 1 : 0
}).then(({ code, message }) => {
if (code == 200) {
window['$message'].success('设置成功!')
talkStore.updateItem({
index_name: props.talkType + '_' + props.gid,
is_disturb: value ? 1 : 0
})
} else {
window['$message'].error(message)
}
})
}
</script>
<template>
<section class="el-container is-vertical section">
@ -678,14 +602,14 @@ const onDisturbChange = (value: boolean) => {
<div class="b-box" style="margin: 16px 0 32px;">
<div class="block">
<div class="title">置顶会话</div>
<n-switch :value="talkParams.isTop === 1" @update:value="onTopChange" />
<n-switch />
</div>
</div>
<div class="b-box" style="margin: 32px 0 20px;">
<div class="block">
<div class="title">消息免打扰</div>
<n-switch :value="talkParams.isDisturb === 1" @update:value="onDisturbChange" />
<n-switch />
</div>
</div>
</div>
@ -809,11 +733,9 @@ const onDisturbChange = (value: boolean) => {
<template #content>
<div class="custom-modal-content">
<text>{{ state.chatSettingOperateHint }}</text>
<text
style="font-size: 16px; color: #999999; margin: 0; line-height: 22px;"
:style="{ color: state.isLastAdmin ? '#CF3050' : '' }"
>{{ state.chatSettingOperateSubHint }}</text
>
<text style="font-size: 16px; color: #999999; margin: 0; line-height: 22px;">{{
state.chatSettingOperateSubHint
}}</text>
</div>
</template>
</customModal>

View File

@ -327,7 +327,7 @@ import { parseTime } from '@/utils/datetime'
import { fileFormatSize, fileSuffix } from '@/utils/strings'
import { NImage, NInfiniteScroll, NScrollbar, NIcon, NDatePicker } from 'naive-ui'
const emits = defineEmits(['clearSearchMemberByAlphabet', 'getDisabledDateArray', 'hideSearchResultModal'])
const emits = defineEmits(['clearSearchMemberByAlphabet', 'getDisabledDateArray'])
const dialogueStore = useDialogueStore()
//
@ -736,29 +736,15 @@ const downloadAndOpenFile = (item) => {
//
const toDialogueByMember = async (msgInfo) => {
console.error('====跳转到对应的记录位置====', msgInfo)
// , sequence
dialogueStore.specifiedMsg = encodeURIComponent(
JSON.stringify({
talk_type: msgInfo.talk_type,
receiver_id: msgInfo.receiver_id,
msg_id: msgInfo.msg_id,
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))
// })
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))
})
}
//

View File

@ -41,13 +41,7 @@
>
<searchItem
@click="clickSearchItem(searchResultKey, item)"
v-if="(
searchResultKey === 'user_infos'
? (state.userInfosShowAll || (props.listLimit && index < 3))
: searchResultKey === 'combinedGroup'
? (state.groupInfosShowAll || (props.listLimit && index < 3))
: (props.listLimit && index < 3)
) || !props.listLimit"
v-if="(props.listLimit && index < 3) || !props.listLimit"
:searchResultKey="searchResultKey"
:searchItem="item"
:searchText="state.searchText"
@ -62,14 +56,8 @@
</div>
<div
class="result-has-more"
v-if="
getHasMoreResult(searchResultKey) &&
!(
(searchResultKey === 'user_infos' && state.userInfosExpand) ||
(searchResultKey === 'combinedGroup' && state.groupInfosExpand)
)
"
@click="onMoreResultClick(searchResultKey)"
v-if="getHasMoreResult(searchResultKey)"
@click="toMoreResultPage(searchResultKey)"
>
<span class="text-[14px] font-regular">
{{ getHasMoreResult(searchResultKey) }}
@ -159,7 +147,6 @@
import { NInfiniteScroll } from 'naive-ui'
import searchItem from './searchItem.vue'
import { ref, reactive, defineEmits, defineProps, onMounted, watch } from 'vue'
import { ServeQueryUser, ServeQueryGroup } from '@/api/search'
const emits = defineEmits([
'toMoreResultPage',
@ -177,16 +164,7 @@ const state = reactive({
uid: 12303, //id
clickStayItem: '', //item
hasMore: true, //
loading: false, //
userInfosExpand: false, //
userInfosLoading: false, //
userInfosLastId: undefined, // last_id
userInfosShowAll: false, // "" true
groupInfosExpand: false, //
groupInfosLoading: false, //
groupInfosLastGroupId: 0, // last_group_id
groupInfosLastMemberId: 0, // last_member_id
groupInfosShowAll: false // "" true
loading: false //
})
const props = defineProps({
@ -236,11 +214,7 @@ const props = defineProps({
useCustomTitle: {
type: Boolean,
default: false
}, //使
selectItemInList: {
type: String,
default: ''
} //
} //使
})
onMounted(() => {
@ -273,13 +247,6 @@ watch(
emits('clickStayItemChange', state.clickStayItem)
//
emits('lastIdChange', 0, 0, 0, '', '')
state.userInfosExpand = false
state.userInfosShowAll = false
state.userInfosLastId = undefined
state.groupInfosExpand = false
state.groupInfosShowAll = false
state.groupInfosLastGroupId = 0
state.groupInfosLastMemberId = 0
queryAllSearch()
}
)
@ -417,12 +384,6 @@ const queryAllSearch = (doClearSearchResult) => {
}
}
state.pageNum = state.pageNum + 1
// userInfosLastId
if (typeof data.last_id !== 'undefined') {
state.userInfosLastId = data.last_id
} else {
state.userInfosLastId = undefined
}
} else {
if (state.pageNum === 1) {
//
@ -577,13 +538,6 @@ const clickSearchItem = (searchResultKey, searchItem) => {
//
const doLoadMore = (doClearSearchResult) => {
if (
state.userInfosLoading ||
state.userInfosShowAll ||
state.groupInfosShowAll // queryAllSearch
) {
return
}
if (!state.hasMore || state.loading) {
return
}
@ -592,134 +546,6 @@ const doLoadMore = (doClearSearchResult) => {
state.loading = false
})
}
watch(
() => props.selectItemInList,
(newVal, oldVal) => {
if (newVal) {
const selectedItem = JSON.parse(decodeURIComponent(newVal))
clickSearchItem('general_infos', selectedItem)
}
},
{
deep: true,
immediate: true
}
)
// last_id
async function loadMoreUserInfos() {
if (state.userInfosLoading) return
state.userInfosLoading = true
try {
let params = {
key: state.searchText,
last_id: state.userInfosLastId,
size: 10
}
const resp = await ServeQueryUser(params)
if (resp.code === 200 && Array.isArray(resp.data.user_infos)) {
if (!state.userInfosLastId) {
//
state.searchResult = {
...state.searchResult,
user_infos: resp.data.user_infos
}
} else {
//
state.searchResult = {
...state.searchResult,
user_infos: (state.searchResult.user_infos || []).concat(resp.data.user_infos)
}
}
state.userInfosLastId = resp.data.last_id
//
if (
!resp.data.last_id ||
(Array.isArray(resp.data.user_infos) && resp.data.user_infos.length < 10)
) {
state.userInfosExpand = true
}
}
} finally {
state.userInfosLoading = false
}
}
// "" ""
function onMoreResultClick(searchResultKey) {
if (searchResultKey === 'user_infos') {
state.userInfosShowAll = true
loadMoreUserInfos()
} else if (searchResultKey === 'combinedGroup') {
state.groupInfosShowAll = true
loadMoreGroupInfos()
} else {
emits('toMoreResultPage', searchResultKey, state.searchText)
}
}
// last_id
async function loadMoreGroupInfos() {
if (state.groupInfosLoading) return
state.groupInfosLoading = true
try {
let params = {
key: state.searchText,
last_group_id: state.groupInfosLastGroupId,
last_member_id: state.groupInfosLastMemberId,
size: 10
}
const resp = await ServeQueryGroup(params)
if (resp.code === 200) {
const groupInfos = Array.isArray(resp.data.group_infos) ? resp.data.group_infos : []
const groupMemberInfos = Array.isArray(resp.data.group_member_infos) ? resp.data.group_member_infos : []
// groupTempType
groupInfos.forEach(item => {
item.groupTempType = 'group_infos'
item.group_type = item.type //
})
groupMemberInfos.forEach(item => {
item.groupTempType = 'group_member_infos'
})
const isFirstLoad = (!state.groupInfosLastGroupId && !state.groupInfosLastMemberId) ||
(state.groupInfosLastGroupId === 0 && state.groupInfosLastMemberId === 0)
if (isFirstLoad) {
//
state.searchResult = {
...state.searchResult,
group_infos: groupInfos,
group_member_infos: groupMemberInfos,
combinedGroup: groupInfos.concat(groupMemberInfos)
}
} else {
//
const allGroupInfos = (state.searchResult.group_infos || []).concat(groupInfos)
const allGroupMemberInfos = (state.searchResult.group_member_infos || []).concat(groupMemberInfos)
state.searchResult = {
...state.searchResult,
group_infos: allGroupInfos,
group_member_infos: allGroupMemberInfos,
combinedGroup: allGroupInfos.concat(allGroupMemberInfos)
}
}
state.groupInfosLastGroupId = resp.data.last_group_id
state.groupInfosLastMemberId = resp.data.last_member_id
//
const noMoreData = (
(!groupInfos.length && !groupMemberInfos.length) ||
(resp.data.last_group_id === 0 && resp.data.last_member_id === 0)
)
if (noMoreData) {
state.groupInfosExpand = true
}
}
} finally {
state.groupInfosLoading = false
}
}
</script>
<style lang="scss" scoped>
.search-list {

View File

@ -24,7 +24,7 @@ const { showUserInfoModal } = useInject()
<em v-show="index < extra.members.length - 1"></em>
</template>
<span>出群聊</span>
<span>出群聊</span>
</div>
</div>
</template>

View File

@ -7,7 +7,6 @@ import EventTalk from './event/talk'
import EventKeyboard from './event/keyboard'
import EventLogin from './event/login'
import EventRevoke from './event/revoke'
import EventRead from './event/read'
import { getAccessToken, isLoggedIn } from './utils/auth'
const urlCallback = () => {
@ -86,8 +85,6 @@ class Connect {
this.onImContactStatus()
this.onImMessageRevoke()
this.onImMessageKeyboard()
this.onImMessageListenRead()
this.onImMessageListenReadIncr()
}
onPing() {
@ -134,16 +131,6 @@ class Connect {
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() {
// 消息已读回执监听事件(增量)
this.conn.on('im.message.listen.read.incr', (data: any) => new EventRead(data, 'incr'))
}
onImContactApply() {
// 好友申请事件
this.conn.on('im.contact.apply', (data: any) => {

View File

@ -1,54 +0,0 @@
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

View File

@ -94,7 +94,7 @@ class Talk extends Base {
useSettingsStore().isPromptTone && palyMusic()
}
async handle() {
handle() {
// 不是自己发送的消息则需要播放提示音
if (!this.isCurrSender()) {
this.play()
@ -102,18 +102,7 @@ class Talk extends Base {
// 判断会话列表是否存在,不存在则创建
if (useTalkStore().findTalkIndex(this.getIndexName()) == -1) {
if (this.resource.msg_type == 1102) {
//被邀请进入群聊时,需要热更新会话列表
await useTalkStore().loadTalkList()
} else if (this.resource.msg_type == 1106) {
//群解散时,需要热更新会话列表
await useTalkStore().loadTalkList()
} else if (this.resource.msg_type == 1104 || this.resource.msg_type == 1115) {
//群成员被移出时,需要热更新会话列表
await useTalkStore().loadTalkList()
} else {
return this.addTalkItem()
}
return this.addTalkItem()
}
// 判断当前是否正在和好友对话
@ -180,42 +169,10 @@ class Talk extends Base {
let record = this.resource
// 群成员变化的消息,需要更新群成员列表
if ([1102, 1103, 1104, 1115].includes(record.msg_type)) {
if ([1102, 1103, 1104].includes(record.msg_type)) {
useDialogueStore().updateGroupMembers()
}
//群解散时,需要更新群成员权限
if ([1106].includes(record.msg_type)) {
useDialogueStore().updateDismiss(true)
}
//群成员被移出时,需要更新群成员权限
if ([1104, 1115].includes(record.msg_type)) {
if (this.resource?.extra?.members?.length > 0) {
const isMeQuit = this.resource.extra.members.find(
(item) => item.user_id === this.getAccountId()
)
if (isMeQuit) {
useDialogueStore().updateQuit(true)
}
}
}
// 群信息变更时,需要更新群信息
if ([1116].includes(record.msg_type)) {
// 更新会话信息
useDialogueStore().updateDialogueTalk({
username: record.extra.group_name,
avatar: record.extra.group_avatar
})
//更新会话列表中的会话信息
useTalkStore().updateItem({
index_name: this.getIndexName(),
name: record.extra.group_name,
avatar: record.extra.group_avatar
})
}
useDialogueStore().addDialogueRecord(formatTalkRecord(this.getAccountId(), this.resource))
if (!this.isCurrSender()) {
@ -264,21 +221,6 @@ class Talk extends Base {
msg_text: this.getTalkText(),
updated_at: parseTime(new Date())
})
//收到新消息时,同时判断是否有人@我
if (this.resource.msg_type === 1 && this.resource?.extra?.mentions?.length > 0) {
const findMention = this.resource?.extra?.mentions?.find(
(mention) => mention === this.getAccountId()
)
//有人@我或者@所有人,则更新会话列表
if (findMention || this.resource?.extra?.mentions?.includes(0)) {
// useTalkStore().loadTalkList()
useTalkStore().updateItem({
index_name: this.getIndexName(),
msg_text: this.getTalkText(),
atsign_num: 1
})
}
}
}
}

View File

@ -14,9 +14,8 @@ interface Params {
interface SpecialParams extends Params {
msg_id?: string
cursor?: number
direction?: 'up' | 'down'
direction?: 'up' | 'down',
sort_sequence?: string
type?: 'loadMore'
}
interface LoadOptions {
@ -39,11 +38,7 @@ export const useTalkRecord = (uid: number) => {
talk_type: 0,
status: 0,
cursor: 0,
specialParams: undefined as SpecialParams | undefined,
isLocatingMessage: false,
isLoadingMore: false,
lastLoadMoreTime: 0,
targetMessagePosition: 0
specialParams: undefined as SpecialParams | undefined
})
// 重置 loadConfig
@ -142,11 +137,6 @@ export const useTalkRecord = (uid: number) => {
if (el) {
if (request.cursor == 0) {
// el.scrollTop = el.scrollHeight
// setTimeout(() => {
// el.scrollTop = el.scrollHeight + 1000
// }, 500)
scrollToBottom()
} else {
el.scrollTop = el.scrollHeight - scrollHeight
@ -163,13 +153,13 @@ export const useTalkRecord = (uid: number) => {
const getMinSequence = () => {
console.error('records.value', records.value)
if (!records.value.length) return 0
console.error(Math.min(...records.value.map((item) => item.sequence)))
return Math.min(...records.value.map((item) => item.sequence))
console.error(Math.min(...records.value.map(item => item.sequence)))
return Math.min(...records.value.map(item => item.sequence))
}
// 获取当前消息的最大 sequence
const getMaxSequence = () => {
if (!records.value.length) return 0
return Math.max(...records.value.map((item) => item.sequence))
return Math.max(...records.value.map(item => item.sequence))
}
/**
@ -179,10 +169,7 @@ export const useTalkRecord = (uid: number) => {
*/
const onLoad = (params: Params, options?: LoadOptions) => {
// 如果会话切换,重置所有状态
if (
params.talk_type !== loadConfig.talk_type ||
params.receiver_id !== loadConfig.receiver_id
) {
if (params.talk_type !== loadConfig.talk_type || params.receiver_id !== loadConfig.receiver_id) {
resetLoadConfig()
}
@ -202,68 +189,34 @@ export const useTalkRecord = (uid: number) => {
...params,
...options.specifiedMsg
}
//msg_id是用来做定位的不做参数所以这里清空
contextParams.msg_id = ''
ServeTalkRecords(contextParams).then(({ data, code }) => {
if (code !== 200) {
loadConfig.status = 2
return
}
// 记录当前滚动高度
const el = document.getElementById('imChatPanel')
const scrollHeight = el?.scrollHeight || 0
if (contextParams.direction === 'down' && !contextParams.type) {
dialogueStore.clearDialogueRecord()
}
// dialogueStore.clearDialogueRecord()
const items = (data.items || []).map((item: ITalkRecord) => formatTalkRecord(uid, item))
if (contextParams.type && contextParams.type === 'loadMore') {
dialogueStore.addDialogueRecordForLoadMore(items)
} else {
dialogueStore.unshiftDialogueRecord(
contextParams.direction === 'down' ? items : items.reverse()
)
}
if (
contextParams.direction === 'up' ||
(contextParams.direction === 'down' && !contextParams.type)
) {
loadConfig.status = items[0].sequence == 1 || data.length === 0 ? 2 : 1
}
dialogueStore.unshiftDialogueRecord(contextParams.direction === 'down' ? items : items.reverse())
loadConfig.status = items.length >= contextParams.limit ? 1 : 2
loadConfig.cursor = data.cursor
// 使用 requestAnimationFrame 来确保在下一帧渲染前设置滚动位置
requestAnimationFrame(() => {
const el = document.getElementById('imChatPanel')
const target = document.getElementById(options.specifiedMsg?.msg_id || '')
if (el && target) {
// 如果是向上加载更多,保持原有内容位置
if (contextParams.direction === 'up') {
el.scrollTop = el.scrollHeight - scrollHeight
} else if (contextParams.type && contextParams.type === 'loadMore') {
// 如果是向下加载更多,保持目标消息在可视区域底部
// 使用可视区域高度来调整,而不是新内容的总高度
nextTick(() => {
if (el) {
el.scrollTop = scrollHeight - el.clientHeight
}
})
} else {
// 如果是定位到特定消息,计算并滚动到目标位置
nextTick(() => {
setTimeout(() => {
const el = document.getElementById('imChatPanel')
const target = document.getElementById(options.specifiedMsg?.msg_id || '')
if (el && target) {
const containerRect = el.getBoundingClientRect()
const targetRect = target.getBoundingClientRect()
const offset = targetRect.top - containerRect.top
loadConfig.isLocatingMessage = true
// 居中
const scrollTo = el.scrollTop + offset - el.clientHeight / 2 + target.clientHeight / 2
el.scrollTo({ top: scrollTo, behavior: 'smooth' })
addClass(target, 'border')
setTimeout(() => removeClass(target, 'border'), 3000)
} else if (el) {
scrollToBottom()
}
} else if (el) {
// el.scrollTop = el.scrollHeight
scrollToBottom()
}
}, 50)
})
})
return
@ -282,10 +235,8 @@ export const useTalkRecord = (uid: number) => {
// 判断是否是特殊参数模式
if (loadConfig.specialParams && typeof loadConfig.specialParams === 'object') {
// 检查特殊参数是否与当前会话匹配
if (
loadConfig.specialParams.talk_type === loadConfig.talk_type &&
loadConfig.specialParams.receiver_id === loadConfig.receiver_id
) {
if (loadConfig.specialParams.talk_type === loadConfig.talk_type &&
loadConfig.specialParams.receiver_id === loadConfig.receiver_id) {
// 特殊参数模式下direction: 'up'cursor: 当前最小 sequence
onLoad(
{
@ -298,11 +249,7 @@ export const useTalkRecord = (uid: number) => {
...loadConfig.specialParams,
direction: 'up',
sort_sequence: '',
cursor: getMinSequence(),
msg_id:
records.value.find((item) =>
item.sequence === getMinSequence() ? item.msg_id : ''
)?.msg_id || ''
cursor: getMinSequence()
}
}
)
@ -326,33 +273,13 @@ export const useTalkRecord = (uid: number) => {
}
}
// 向下加载更多(特殊参数模式才生效,普通模式无效,因为普通模式的数据就是从最新开始加载历史的,所以不需要加载更新的数据
// 向下加载更多(兼容特殊参数模式)
const onLoadMoreDown = () => {
// 判断是否是特殊参数模式
if (loadConfig.specialParams && typeof loadConfig.specialParams === 'object') {
if (loadConfig.isLocatingMessage) {
loadConfig.isLocatingMessage = false
return
}
// 添加时间间隔限制,至少间隔 500ms 才能触发下一次加载
const now = Date.now()
if (now - loadConfig.lastLoadMoreTime < 500) {
return
}
loadConfig.lastLoadMoreTime = now
// 记录当前目标消息的位置
const el = document.getElementById('imChatPanel')
if (el) {
loadConfig.targetMessagePosition = el.scrollHeight - el.scrollTop
}
console.log('onLoadMoreDown 特殊模式触底了')
// 检查特殊参数是否与当前会话匹配
if (
loadConfig.specialParams.talk_type === loadConfig.talk_type &&
loadConfig.specialParams.receiver_id === loadConfig.receiver_id
) {
if (loadConfig.specialParams.talk_type === loadConfig.talk_type &&
loadConfig.specialParams.receiver_id === loadConfig.receiver_id) {
onLoad(
{
receiver_id: loadConfig.receiver_id,
@ -363,23 +290,27 @@ export const useTalkRecord = (uid: number) => {
specifiedMsg: {
...loadConfig.specialParams,
direction: 'down',
sort_sequence: 'asc',
cursor: getMaxSequence(),
type: 'loadMore'
cursor: getMaxSequence()
}
}
)
} else {
// 如果不匹配,重置为普通模式
resetLoadConfig()
load({
receiver_id: loadConfig.receiver_id,
talk_type: loadConfig.talk_type,
limit: 30
})
}
} else {
load({
receiver_id: loadConfig.receiver_id,
talk_type: loadConfig.talk_type,
limit: 30
})
}
}
return {
loadConfig,
records,
onLoad,
onRefreshLoad,
onLoadMoreDown,
onJumpMessage,
resetLoadConfig
}
return { loadConfig, records, onLoad, onRefreshLoad, onLoadMoreDown, onJumpMessage, resetLoadConfig }
}

View File

@ -49,12 +49,6 @@ export const useDialogueStore = defineStore('dialogue', {
// 是否显示编辑器
isShowEditor: false,
//是否已被解散
isDismiss: false,
//是否退群/移出群
isQuit: false,
// 是否显示会话列表
isShowSessionList: true,
groupInfo: {} ,
@ -86,16 +80,6 @@ export const useDialogueStore = defineStore('dialogue', {
this.online = status
},
// 更新群解散状态
updateDismiss(isDismiss) {
this.isDismiss = isDismiss
},
// 更新群成员退出状态
updateQuit(isQuit) {
this.isQuit = isQuit
},
// 更新对话信息
setDialogue(data = {}) {
this.online = data.is_online == 1
@ -111,9 +95,6 @@ export const useDialogueStore = defineStore('dialogue', {
this.records = []
this.unreadBubble = 0
this.isShowEditor = data?.is_robot === 0
this.isDismiss = data?.is_dismiss === 1 ? true : false
this.isQuit = data?.is_quit === 1 ? true : false
// 只在手动切换会话时清空 specifiedMsg
// if (this.isManualSwitch) {
@ -170,10 +151,6 @@ export const useDialogueStore = defineStore('dialogue', {
unshiftDialogueRecord(records) {
this.records.unshift(...records)
},
//数组尾部加入更多对话记录
addDialogueRecordForLoadMore(records){
this.records.push(...records)
},
async getGroupInfo(){
const { code, data } = await ServeGroupDetail({
group_id: this.talk.receiver_id
@ -311,11 +288,6 @@ export const useDialogueStore = defineStore('dialogue', {
record.extra.url = videoInfo.url
// record.extra.cover = videoInfo.cover
}
},
// 更新会话信息
updateDialogueTalk(params){
Object.assign(this.talk, params)
}
}
})

View File

@ -50,8 +50,8 @@ export interface ITalkRecord {
send_status: number
float: string,
is_convert_text?:number//语音记录的 是否是在展示转文本状态 1是 0否,
erp_user_id:number,
read_total_num:number
erp_user_id:number
}
export interface ITalkRecordExtraText {

View File

@ -39,8 +39,6 @@ export function formatTalkItem(params) {
is_top: 0,
is_online: 0,
is_robot: 0,
is_dismiss: 0,
is_quit: 0,
unread_num: 0,
content: '......',
draft_text: '',

View File

@ -481,12 +481,6 @@ const onDatePickShow = (show) => {
// state.nowDateTime = new Date()
}
}
//
const hideSearchResultModal = () => {
handleSearchRecordByConditionModalClose()
state.isShowGroupAside = false
}
</script>
<template>
@ -512,7 +506,6 @@ const hideSearchResultModal = () => {
:receiver_id="talkParams.receiver_id"
:index_name="talkParams.index_name"
:specifiedMsg="talkParams.specifiedMsg"
:num="talkParams.num"
/>
</main>
@ -713,7 +706,6 @@ const hideSearchResultModal = () => {
@getDisabledDateArray="getDisabledDateArray"
:selectedDateTime="state.selectedDateTime"
:nowDateTime="state.nowDateTime"
@hideSearchResultModal="hideSearchResultModal"
/>
</div>
</n-card>

View File

@ -74,24 +74,6 @@ const renderChatAppSearch = () => {
console.log(searchText, searchResultKey, talk_type, receiver_id)
const result = JSON.parse(decodeURIComponent(res))
console.log(result)
if (searchResultKey === 'general_infos') {
state.ServeQueryTalkRecordParams = encodeURIComponent(
JSON.stringify({
talk_type: 0, //12
receiver_id: 0, //
last_group_id: 0, //id
last_member_id: 0, //id
last_receiver_user_name: '', //
last_receiver_group_name: '' //
})
)
state.isShowSearchRecordModal = true
state.searchRecordText = searchText
state.selectItemInList = res
} else {
talkStore.toTalk(talk_type, receiver_id, router)
}
state.showSearchDropdown = false
},
onToMoreResultPage: (searchResultKey, searchText) => {
if (searchResultKey === 'general_infos') {
@ -106,10 +88,8 @@ const renderChatAppSearch = () => {
})
)
state.isShowSearchRecordModal = true
state.searchRecordText = searchText
}
console.log(searchResultKey, searchText)
state.showSearchDropdown = false
}
},
{
@ -297,9 +277,7 @@ const state = reactive({
apiParams: '',
lastId: undefined as any,
total: 0
},
showSearchDropdown: false, //
selectItemInList: '' //
}
})
const items = computed((): ISession[] => {
@ -314,9 +292,9 @@ const items = computed((): ISession[] => {
//
const topItems = filtered
.filter((item) => item.is_top === 1)
.filter(item => item.is_top === 1)
.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
const normalItems = filtered.filter((item) => item.is_top !== 1)
const normalItems = filtered.filter(item => item.is_top !== 1)
return [...topItems, ...normalItems]
})
@ -401,8 +379,7 @@ const onTabTalk = (item: ISession, follow = false) => {
}).then(() => {
talkStore.updateItem({
index_name: item.index_name,
unread_num: 0,
atsign_num: 0
unread_num: 0
})
})
}
@ -612,8 +589,8 @@ const handleGroupChatListPaginationSize = (value) => {
//
const handleClickSearchItem = (searchText, searchResultKey, talk_type, receiver_id, res) => {
console.log(searchText, searchResultKey, talk_type, receiver_id)
// const result = JSON.parse(decodeURIComponent(res))
// console.log(result)
const result = JSON.parse(decodeURIComponent(res))
console.log(result)
if (searchResultKey === 'general_infos') {
//
state.isShowSearchRecordDetailInfo = false
@ -651,9 +628,6 @@ const handleClickSearchResultItem = (searchText, searchResultKey, talk_type, rec
)
console.error(dialogueStore.specifiedMsg, 'dialogueStore.specifiedMsg')
talkStore.toTalk(talk_type, receiver_id, router)
state.isShowSearchRecordModal = false
state.searchRecordText = ''
searchKeyword.value = ''
}
//item
const handleClickStayItemChange = (item) => {
@ -748,26 +722,24 @@ const handleEnterSearchResultChat = () => {
trigger="click"
:options="state.chatSearchOptions"
style="width: 248px; height: 677px;"
:show="state.showSearchDropdown"
@clickoutside="state.showSearchDropdown = false"
>
<n-input
placeholder="搜索好友 / 群聊"
v-model:value.trim="searchKeyword"
round
clearable
style="width: 78%;"
@click="state.showSearchDropdown = true"
>
<!-- <template #prefix>
<template #prefix>
<n-icon :component="Search" />
</template> -->
</template>
</n-input>
</n-dropdown>
<!-- <n-button circle @click="isShowGroup = true">
<n-button circle @click="isShowGroup = true">
<template #icon>
<n-icon :component="Plus" />
</template>
</n-button> -->
</n-button>
<img
style="width: 19px; height: 20px; cursor: pointer;"
src="@/assets/image/chatList/addressBook.png"
@ -978,7 +950,6 @@ const handleEnterSearchResultChat = () => {
@clickStayItemChange="handleClickStayItemChange"
@lastIdChange="handleSearchListLastIdChange"
:searchResultMaxHeight="'517px'"
:selectItemInList="state.selectItemInList"
></chatAppSearchList>
</div>
<div class="search-record-detail">

View File

@ -51,7 +51,6 @@ const labelColor=[
<span class="detail" v-html="data.draft_text" />
</template>
<template v-else>
<span class="draft" v-show="data.talk_type == 2 && data.atsign_num"> [有人@] </span>
<span class="online" v-show="data.talk_type == 1 && data.is_online == 1"> [在线] </span>
<span class="detail" v-html="data.msg_text" />
</template>

View File

@ -19,7 +19,7 @@ defineProps({
type: Boolean,
default: false
},
avatar: {
avatar:{
type: String,
default: ''
},
@ -46,22 +46,19 @@ const onSetMenu = () => {
/>
</div> -->
<div class="flex items-center">
<avatarModule
class="mr-10px"
:mode="dialogueStore.talk.talk_type"
:avatar="avatar"
:groupType="dialogueStore.talk?.group_type"
:userName="username"
:customStyle="{ width: '42px', height: '42px' }"
></avatarModule>
<avatarModule class="mr-10px" :mode="dialogueStore.talk.talk_type"
:avatar="avatar"
:groupType="dialogueStore.talk?.group_type"
:userName="username" :customStyle="{width:'42px',height:'42px'}"></avatarModule>
<div class="module left-module">
<!-- <span class="tag" :class="{ red: type == 1 }">
<!-- <span class="tag" :class="{ red: type == 1 }">
{{ type == 1 ? '好友' : '群聊' }}
</span> -->
<span class="nickname">{{ username }}</span>
<span class="num" v-show="type == 2 && num">({{ num }})</span>
</div>
<span class="nickname">{{ username }}</span>
<span class="num" v-show="type == 2 && num">({{ num }})</span>
</div>
</div>
<div class="module center-module" v-if="type == 1">
<p class="online">
@ -83,13 +80,8 @@ const onSetMenu = () => {
:size="18"
class="icon"
@click="emit('evnet', 'group')"
v-show="!dialogueStore.isDismiss && !dialogueStore.isQuit"
>
<img
style="width: 20px; height: 20px;"
src="@/assets/image/chatList/chat-settings.png"
alt=""
/>
<img style="width: 20px; height: 20px;" src="@/assets/image/chatList/chat-settings.png" alt="" />
</n-icon>
</div>
</header>
@ -164,7 +156,7 @@ const onSetMenu = () => {
text-align: center;
&.color {
color: #462aa0;
color: #462AA0;
}
.online-status {
@ -176,7 +168,7 @@ const onSetMenu = () => {
vertical-align: middle;
border-radius: 50%;
position: relative;
background-color: #462aa0;
background-color: #462AA0;
margin-right: 5px;
&:after {
@ -185,7 +177,7 @@ const onSetMenu = () => {
left: -1px;
width: 100%;
height: 100%;
border: 1px solid #462aa0;
border: 1px solid #462AA0;
border-radius: 50%;
-webkit-animation: antStatusProcessing 1.2s ease-in-out infinite;
animation: antStatusProcessing 1.2s ease-in-out infinite;