处理指定聊天记录的定位跳转,查找上下文【不稳定】
This commit is contained in:
parent
0ab2ce814a
commit
6517c082d5
@ -200,10 +200,10 @@
|
|||||||
>
|
>
|
||||||
<div class="attachment-avatar">
|
<div class="attachment-avatar">
|
||||||
<img :src="item?.extra?.file_avatar" v-if="state.condition === 'file'" />
|
<img :src="item?.extra?.file_avatar" v-if="state.condition === 'file'" />
|
||||||
<img
|
<!-- <img
|
||||||
src="@/static/image/search/result-link-icon.png"
|
src="@/static/image/search/result-link-icon.png"
|
||||||
v-if="state.condition === 'link'"
|
v-if="state.condition === 'link'"
|
||||||
/>
|
/> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="attachment-info">
|
<div class="attachment-info">
|
||||||
<div class="attachment-info-title">
|
<div class="attachment-info-title">
|
||||||
|
@ -11,6 +11,17 @@ interface Params {
|
|||||||
limit: number
|
limit: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SpecialParams extends Params {
|
||||||
|
msg_id?: string
|
||||||
|
cursor?: number
|
||||||
|
direction?: 'up' | 'down'
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LoadOptions {
|
||||||
|
specifiedMsg?: SpecialParams
|
||||||
|
middleMsgCreatedAt?: string
|
||||||
|
}
|
||||||
|
|
||||||
export const useTalkRecord = (uid: number) => {
|
export const useTalkRecord = (uid: number) => {
|
||||||
const dialogueStore = useDialogueStore()
|
const dialogueStore = useDialogueStore()
|
||||||
|
|
||||||
@ -25,9 +36,19 @@ export const useTalkRecord = (uid: number) => {
|
|||||||
receiver_id: 0,
|
receiver_id: 0,
|
||||||
talk_type: 0,
|
talk_type: 0,
|
||||||
status: 0,
|
status: 0,
|
||||||
cursor: 0
|
cursor: 0,
|
||||||
|
specialParams: undefined as SpecialParams | undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 重置 loadConfig
|
||||||
|
const resetLoadConfig = () => {
|
||||||
|
loadConfig.receiver_id = 0
|
||||||
|
loadConfig.talk_type = 0
|
||||||
|
loadConfig.status = 0
|
||||||
|
loadConfig.cursor = 0
|
||||||
|
loadConfig.specialParams = undefined
|
||||||
|
}
|
||||||
|
|
||||||
const onJumpMessage = (msgid: string) => {
|
const onJumpMessage = (msgid: string) => {
|
||||||
const element = document.getElementById(msgid)
|
const element = document.getElementById(msgid)
|
||||||
if (!element) {
|
if (!element) {
|
||||||
@ -131,8 +152,160 @@ export const useTalkRecord = (uid: number) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取当前消息的最小 sequence
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
// 获取当前消息的最大 sequence
|
||||||
|
const getMaxSequence = () => {
|
||||||
|
if (!records.value.length) return 0
|
||||||
|
return Math.max(...records.value.map(item => item.sequence))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载数据主入口,支持指定消息定位模式
|
||||||
|
* @param params 原有参数
|
||||||
|
* @param options 可选,{ specifiedMsg } 指定消息对象
|
||||||
|
*/
|
||||||
|
const onLoad = (params: Params, options?: LoadOptions) => {
|
||||||
|
// 如果会话切换,重置所有状态
|
||||||
|
if (params.talk_type !== loadConfig.talk_type || params.receiver_id !== loadConfig.receiver_id) {
|
||||||
|
resetLoadConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
loadConfig.cursor = 0
|
||||||
|
loadConfig.receiver_id = params.receiver_id
|
||||||
|
loadConfig.talk_type = params.talk_type
|
||||||
|
|
||||||
|
console.error('onLoad', params, options)
|
||||||
|
|
||||||
|
// 新增:支持指定消息定位模式,参数以传入为准合并
|
||||||
|
if (options?.specifiedMsg?.cursor !== undefined) {
|
||||||
|
loadConfig.specialParams = { ...options.specifiedMsg } // 记录特殊参数,供分页加载用
|
||||||
|
console.error('options', options)
|
||||||
|
loadConfig.status = 0 // 复用主流程 loading 状态
|
||||||
|
// 以 params 为基础,合并 specifiedMsg 的所有字段(只要有就覆盖)
|
||||||
|
const contextParams = {
|
||||||
|
...params,
|
||||||
|
...options.specifiedMsg
|
||||||
|
}
|
||||||
|
ServeTalkRecords(contextParams).then(({ data, code }) => {
|
||||||
|
if (code !== 200) {
|
||||||
|
loadConfig.status = 2
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dialogueStore.clearDialogueRecord()
|
||||||
|
const items = (data.items || []).map((item: ITalkRecord) => formatTalkRecord(uid, item))
|
||||||
|
dialogueStore.unshiftDialogueRecord(items.reverse())
|
||||||
|
loadConfig.status = items.length >= contextParams.limit ? 1 : 2
|
||||||
|
loadConfig.cursor = data.cursor
|
||||||
|
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
|
||||||
|
// 居中
|
||||||
|
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) {
|
||||||
|
el.scrollTop = el.scrollHeight
|
||||||
|
}
|
||||||
|
}, 50)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loadConfig.specialParams = undefined // 普通模式清空
|
||||||
|
// 原有逻辑
|
||||||
|
load(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向上加载更多(兼容特殊参数模式)
|
||||||
const onRefreshLoad = () => {
|
const onRefreshLoad = () => {
|
||||||
|
console.error('loadConfig.status', loadConfig.status)
|
||||||
if (loadConfig.status == 1) {
|
if (loadConfig.status == 1) {
|
||||||
|
console.log('specialParams', loadConfig.specialParams)
|
||||||
|
// 判断是否是特殊参数模式
|
||||||
|
if (loadConfig.specialParams && typeof loadConfig.specialParams === 'object') {
|
||||||
|
// 检查特殊参数是否与当前会话匹配
|
||||||
|
if (loadConfig.specialParams.talk_type === loadConfig.talk_type &&
|
||||||
|
loadConfig.specialParams.receiver_id === loadConfig.receiver_id) {
|
||||||
|
// 特殊参数模式下,direction: 'up',cursor: 当前最小 sequence
|
||||||
|
onLoad(
|
||||||
|
{
|
||||||
|
receiver_id: loadConfig.receiver_id,
|
||||||
|
talk_type: loadConfig.talk_type,
|
||||||
|
limit: 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
specifiedMsg: {
|
||||||
|
...loadConfig.specialParams,
|
||||||
|
direction: 'up',
|
||||||
|
cursor: getMinSequence()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向下加载更多(兼容特殊参数模式)
|
||||||
|
const onLoadMoreDown = () => {
|
||||||
|
// 判断是否是特殊参数模式
|
||||||
|
if (loadConfig.specialParams && typeof loadConfig.specialParams === 'object') {
|
||||||
|
// 检查特殊参数是否与当前会话匹配
|
||||||
|
if (loadConfig.specialParams.talk_type === loadConfig.talk_type &&
|
||||||
|
loadConfig.specialParams.receiver_id === loadConfig.receiver_id) {
|
||||||
|
onLoad(
|
||||||
|
{
|
||||||
|
receiver_id: loadConfig.receiver_id,
|
||||||
|
talk_type: loadConfig.talk_type,
|
||||||
|
limit: 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
specifiedMsg: {
|
||||||
|
...loadConfig.specialParams,
|
||||||
|
direction: 'down',
|
||||||
|
cursor: getMaxSequence()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// 如果不匹配,重置为普通模式
|
||||||
|
resetLoadConfig()
|
||||||
|
load({
|
||||||
|
receiver_id: loadConfig.receiver_id,
|
||||||
|
talk_type: loadConfig.talk_type,
|
||||||
|
limit: 30
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
load({
|
load({
|
||||||
receiver_id: loadConfig.receiver_id,
|
receiver_id: loadConfig.receiver_id,
|
||||||
talk_type: loadConfig.talk_type,
|
talk_type: loadConfig.talk_type,
|
||||||
@ -141,13 +314,5 @@ export const useTalkRecord = (uid: number) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onLoad = (params: Params) => {
|
return { loadConfig, records, onLoad, onRefreshLoad, onLoadMoreDown, onJumpMessage, resetLoadConfig }
|
||||||
loadConfig.cursor = 0
|
|
||||||
loadConfig.receiver_id = params.receiver_id
|
|
||||||
loadConfig.talk_type = params.talk_type
|
|
||||||
|
|
||||||
load(params)
|
|
||||||
}
|
|
||||||
|
|
||||||
return { loadConfig, records, onLoad, onRefreshLoad, onJumpMessage }
|
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,12 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
// 聊天记录
|
// 聊天记录
|
||||||
records: [],
|
records: [],
|
||||||
|
|
||||||
|
// 查询指定消息上下文的消息信息
|
||||||
|
specifiedMsg: '',
|
||||||
|
|
||||||
|
// 是否是手动切换会话
|
||||||
|
isManualSwitch: false,
|
||||||
|
|
||||||
// 新消息提示
|
// 新消息提示
|
||||||
unreadBubble: 0,
|
unreadBubble: 0,
|
||||||
|
|
||||||
@ -76,6 +82,7 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
// 更新对话信息
|
// 更新对话信息
|
||||||
setDialogue(data = {}) {
|
setDialogue(data = {}) {
|
||||||
console.log('data', data)
|
console.log('data', data)
|
||||||
|
|
||||||
this.online = data.is_online == 1
|
this.online = data.is_online == 1
|
||||||
this.talk = {
|
this.talk = {
|
||||||
username: data.remark || data.name,
|
username: data.remark || data.name,
|
||||||
@ -90,6 +97,12 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
this.unreadBubble = 0
|
this.unreadBubble = 0
|
||||||
this.isShowEditor = data?.is_robot === 0
|
this.isShowEditor = data?.is_robot === 0
|
||||||
|
|
||||||
|
// 只在手动切换会话时清空 specifiedMsg
|
||||||
|
// if (this.isManualSwitch) {
|
||||||
|
// this.specifiedMsg = ''
|
||||||
|
// this.isManualSwitch = false
|
||||||
|
// }
|
||||||
|
|
||||||
this.members = []
|
this.members = []
|
||||||
if (data.talk_type == 2) {
|
if (data.talk_type == 2) {
|
||||||
this.updateGroupMembers()
|
this.updateGroupMembers()
|
||||||
|
@ -18,7 +18,7 @@ export function isLoggedIn() {
|
|||||||
*/
|
*/
|
||||||
export function getAccessToken() {
|
export function getAccessToken() {
|
||||||
// return storage.get(AccessToken) || ''
|
// return storage.get(AccessToken) || ''
|
||||||
return JSON.parse(localStorage.getItem('token'))||'79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b89855de0ffae3adff28137845adce8c6b2c8160f779573f4e6553e86ac85dc8e689aa8970c6a4de122173f4db7bf7cdfa3ca7796d181a43b3fdf8ec549a8f33c82fbab7f0175efce75aca5cf6188ccf2a8'
|
return JSON.parse(localStorage.getItem('token'))||'79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b89a6a1266874decb435c4ea86321f35d90bdc30204aed39f91f4d34d074462099ce788933c4b4c4c10e31272d7bb0f3b1d8086a4695de3f9cb4cd702f79b98dc4efd5f2b753acec3bad2d1d3a4b6c85972'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,8 @@ const talkParams = reactive({
|
|||||||
online: computed(() => dialogueStore.online),
|
online: computed(() => dialogueStore.online),
|
||||||
keyboard: computed(() => dialogueStore.keyboard),
|
keyboard: computed(() => dialogueStore.keyboard),
|
||||||
num: computed(() => dialogueStore.members.length),
|
num: computed(() => dialogueStore.members.length),
|
||||||
avatar:computed(() => dialogueStore.talk.avatar)
|
avatar:computed(() => dialogueStore.talk.avatar),
|
||||||
|
specifiedMsg: computed(() => dialogueStore.specifiedMsg)
|
||||||
})
|
})
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
@ -394,6 +395,7 @@ const handleGroupNoticeModalShow = (isAdmin) => {
|
|||||||
:talk_type="talkParams.type"
|
:talk_type="talkParams.type"
|
||||||
:receiver_id="talkParams.receiver_id"
|
:receiver_id="talkParams.receiver_id"
|
||||||
:index_name="talkParams.index_name"
|
:index_name="talkParams.index_name"
|
||||||
|
:specifiedMsg="talkParams.specifiedMsg"
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
@ -66,21 +66,10 @@ const renderChatAppSearch = () => {
|
|||||||
return h(
|
return h(
|
||||||
chatAppSearchList,
|
chatAppSearchList,
|
||||||
{
|
{
|
||||||
// searchResultKey: 'user_infos',
|
|
||||||
// searchItem: {
|
|
||||||
// avatar:
|
|
||||||
// 'https://e-cdn.fontree.cn/fonchain-main/prod/image/18248/avatar/a0b2bee7-947f-465a-986e-10a1b2b87032.png',
|
|
||||||
// created_at: '2025-03-27 14:44:23',
|
|
||||||
// erp_user_id: 18248,
|
|
||||||
// id: 44,
|
|
||||||
// mobile: '18994430450',
|
|
||||||
// nickname: '周俊耀'
|
|
||||||
// },
|
|
||||||
// searchText: '周'
|
|
||||||
searchResultPageSize: 3,
|
searchResultPageSize: 3,
|
||||||
listLimit: true,
|
listLimit: true,
|
||||||
apiRequest: ServeSeachQueryAll,
|
apiRequest: ServeSeachQueryAll,
|
||||||
searchText: '王',
|
searchText: searchKeyword.value,
|
||||||
onClickSearchItem: (searchText, searchResultKey, talk_type, receiver_id, res) => {
|
onClickSearchItem: (searchText, searchResultKey, talk_type, receiver_id, res) => {
|
||||||
console.log(searchText, searchResultKey, talk_type, receiver_id)
|
console.log(searchText, searchResultKey, talk_type, receiver_id)
|
||||||
const result = JSON.parse(decodeURIComponent(res))
|
const result = JSON.parse(decodeURIComponent(res))
|
||||||
@ -377,6 +366,8 @@ const onTabTalk = (item: ISession, follow = false) => {
|
|||||||
|
|
||||||
searchKeyword.value = ''
|
searchKeyword.value = ''
|
||||||
|
|
||||||
|
dialogueStore.isManualSwitch = true
|
||||||
|
|
||||||
// 更新编辑信息
|
// 更新编辑信息
|
||||||
dialogueStore.setDialogue(item)
|
dialogueStore.setDialogue(item)
|
||||||
|
|
||||||
@ -619,6 +610,25 @@ const handleClickSearchItem = (searchText, searchResultKey, talk_type, receiver_
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//处理点击搜索结果item
|
||||||
|
const handleClickSearchResultItem = (searchText, searchResultKey, talk_type, receiver_id, res) => {
|
||||||
|
const result = JSON.parse(decodeURIComponent(res))
|
||||||
|
console.error(result, 'result')
|
||||||
|
// 根据搜索结果, 指定用于查询指定消息上下文的sequence
|
||||||
|
dialogueStore.specifiedMsg = encodeURIComponent(
|
||||||
|
JSON.stringify({
|
||||||
|
talk_type,
|
||||||
|
receiver_id,
|
||||||
|
msg_id: result.msg_id,
|
||||||
|
cursor: result.sequence - 15 > 0 ? result.sequence - 15 : 0,
|
||||||
|
direction: 'down',
|
||||||
|
sort_sequence: 'asc',
|
||||||
|
create_time: result.created_at
|
||||||
|
})
|
||||||
|
)
|
||||||
|
console.error(dialogueStore.specifiedMsg, 'dialogueStore.specifiedMsg')
|
||||||
|
talkStore.toTalk(talk_type, receiver_id, router)
|
||||||
|
}
|
||||||
//处理点击停留item变化
|
//处理点击停留item变化
|
||||||
const handleClickStayItemChange = (item) => {
|
const handleClickStayItemChange = (item) => {
|
||||||
if (item) {
|
if (item) {
|
||||||
@ -682,6 +692,15 @@ const handleCloseSearchRecordModal = () => {
|
|||||||
const getResultTotalCount = (total) => {
|
const getResultTotalCount = (total) => {
|
||||||
state.searchDetailList.total = total
|
state.searchDetailList.total = total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 进入搜索结果聊天
|
||||||
|
const handleEnterSearchResultChat = () => {
|
||||||
|
const searchResult = JSON.parse(decodeURIComponent(state.searchDetailList.apiParams))
|
||||||
|
talkStore.toTalk(searchResult.talk_type, searchResult.receiver_id, router)
|
||||||
|
state.isShowSearchRecordModal = false
|
||||||
|
state.searchRecordText = ''
|
||||||
|
searchKeyword.value = ''
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -945,7 +964,7 @@ const getResultTotalCount = (total) => {
|
|||||||
"
|
"
|
||||||
:searchText="state.searchRecordText"
|
:searchText="state.searchRecordText"
|
||||||
/>
|
/>
|
||||||
<div class="search-record-detail-header-btn">
|
<div class="search-record-detail-header-btn" @click="handleEnterSearchResultChat">
|
||||||
<span>进入聊天</span>
|
<span>进入聊天</span>
|
||||||
<n-icon :component="Right" color="#46299D" size="14px" />
|
<n-icon :component="Right" color="#46299D" size="14px" />
|
||||||
</div>
|
</div>
|
||||||
@ -963,6 +982,7 @@ const getResultTotalCount = (total) => {
|
|||||||
@lastIdChange="handleSearchDetailListLastIdChange"
|
@lastIdChange="handleSearchDetailListLastIdChange"
|
||||||
:searchResultMaxHeight="'469px'"
|
:searchResultMaxHeight="'469px'"
|
||||||
@resultTotalCount="getResultTotalCount"
|
@resultTotalCount="getResultTotalCount"
|
||||||
|
@clickSearchItem="handleClickSearchResultItem"
|
||||||
></chatAppSearchList>
|
></chatAppSearchList>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch, onMounted, ref } from 'vue'
|
import { watch, onMounted, ref, nextTick } from 'vue'
|
||||||
import { NDropdown, NCheckbox } from 'naive-ui'
|
import { NDropdown, NCheckbox } 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'
|
||||||
@ -13,7 +13,7 @@ import SkipBottom from './SkipBottom.vue'
|
|||||||
import { ITalkRecord } from '@/types/chat'
|
import { ITalkRecord } from '@/types/chat'
|
||||||
import { EditorConst } from '@/constant/event-bus'
|
import { EditorConst } from '@/constant/event-bus'
|
||||||
import { useInject, useTalkRecord, useUtil } from '@/hooks'
|
import { useInject, useTalkRecord, useUtil } from '@/hooks'
|
||||||
import { ExclamationCircleFilled } from '@ant-design/icons-vue';
|
import { ExclamationCircleFilled } from '@ant-design/icons-vue'
|
||||||
import { useUserStore } from '@/store'
|
import { useUserStore } 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 } from '@/api/chat.js'
|
||||||
@ -33,6 +33,10 @@ const props = defineProps({
|
|||||||
index_name: {
|
index_name: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
},
|
||||||
|
specifiedMsg: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -276,18 +280,48 @@ const onRowClick = (item: ITalkRecord) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(props, () => {
|
const lastParams = ref('')
|
||||||
onLoad({ ...props, limit: 30 })
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
// 监听整个 props 对象的变化
|
||||||
onLoad({ ...props, limit: 30 })
|
watch(
|
||||||
})
|
() => props,
|
||||||
|
async (newProps) => {
|
||||||
|
await nextTick()
|
||||||
|
let specialParams = undefined
|
||||||
|
console.error(newProps, 'newProps')
|
||||||
|
if (newProps.specifiedMsg) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(decodeURIComponent(newProps.specifiedMsg))
|
||||||
|
// 只有会话id和参数都匹配才进入特殊模式
|
||||||
|
if (parsed.talk_type === newProps.talk_type && parsed.receiver_id === newProps.receiver_id) {
|
||||||
|
specialParams = parsed
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
onLoad(
|
||||||
|
{
|
||||||
|
receiver_id: newProps.receiver_id,
|
||||||
|
talk_type: newProps.talk_type,
|
||||||
|
limit: 30
|
||||||
|
},
|
||||||
|
specialParams ? { specifiedMsg: specialParams } : undefined
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// onMounted(() => {
|
||||||
|
// onLoad({ ...props, limit: 30 })
|
||||||
|
// })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div id="imChatPanel" class="me-scrollbar me-scrollbar-thumb talk-container" @scroll="onPanelScroll($event)">
|
<div
|
||||||
|
id="imChatPanel"
|
||||||
|
class="me-scrollbar me-scrollbar-thumb talk-container"
|
||||||
|
@scroll="onPanelScroll($event)"
|
||||||
|
>
|
||||||
<!-- 数据加载状态栏 -->
|
<!-- 数据加载状态栏 -->
|
||||||
<div class="load-toolbar pointer">
|
<div class="load-toolbar pointer">
|
||||||
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
|
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
|
||||||
@ -295,53 +329,100 @@ onMounted(() => {
|
|||||||
<span v-else class="no-more"> 没有更多消息了 </span>
|
<span v-else class="no-more"> 没有更多消息了 </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="message-item" v-for="(item, index) in records" :key="item.msg_id" :id="item.msg_id">
|
<div
|
||||||
|
class="message-item"
|
||||||
|
v-for="(item, index) in records"
|
||||||
|
:key="item.msg_id"
|
||||||
|
:id="item.msg_id"
|
||||||
|
>
|
||||||
<!-- 系统消息 -->
|
<!-- 系统消息 -->
|
||||||
<div v-if="item.msg_type >= 1000" class="message-box">
|
<div v-if="item.msg_type >= 1000" class="message-box">
|
||||||
<component :is="MessageComponents[item.msg_type] || 'unknown-message'" :extra="item.extra" :data="item" />
|
<component
|
||||||
|
:is="MessageComponents[item.msg_type] || 'unknown-message'"
|
||||||
|
:extra="item.extra"
|
||||||
|
:data="item"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 撤回消息 -->
|
<!-- 撤回消息 -->
|
||||||
<div v-else-if="item.is_revoke == 1" class="message-box">
|
<div v-else-if="item.is_revoke == 1" class="message-box">
|
||||||
<revoke-message :login_uid="uid" :data="item" :user_id="item.user_id" :nickname="item.nickname" :talk_type="item.talk_type"
|
<revoke-message
|
||||||
:datetime="item.created_at" />
|
:login_uid="uid"
|
||||||
|
:data="item"
|
||||||
|
:user_id="item.user_id"
|
||||||
|
:nickname="item.nickname"
|
||||||
|
:talk_type="item.talk_type"
|
||||||
|
:datetime="item.created_at"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="message-box record-box" :class="{
|
<div
|
||||||
|
v-else
|
||||||
|
class="message-box record-box"
|
||||||
|
:class="{
|
||||||
'direction-rt': item.float == 'right',
|
'direction-rt': item.float == 'right',
|
||||||
'multi-select': dialogueStore.isOpenMultiSelect,
|
'multi-select': dialogueStore.isOpenMultiSelect,
|
||||||
'multi-select-check': item.isCheck
|
'multi-select-check': item.isCheck
|
||||||
}">
|
}"
|
||||||
|
>
|
||||||
<!-- 多选按钮 -->
|
<!-- 多选按钮 -->
|
||||||
<aside v-if="dialogueStore.isOpenMultiSelect" class="checkbox-column">
|
<aside v-if="dialogueStore.isOpenMultiSelect" class="checkbox-column">
|
||||||
<n-checkbox size="small" :checked="item.isCheck" @update:checked="item.isCheck = !item.isCheck" />
|
<n-checkbox
|
||||||
|
size="small"
|
||||||
|
:checked="item.isCheck"
|
||||||
|
@update:checked="item.isCheck = !item.isCheck"
|
||||||
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
<!-- 头像信息 -->
|
<!-- 头像信息 -->
|
||||||
|
|
||||||
<aside class="avatar-column">
|
<aside class="avatar-column">
|
||||||
<im-avatar class="pointer" :src="item.avatar" :size="42" :username="item.nickname"
|
<im-avatar
|
||||||
@click="showUserInfoModal(item.erp_user_id,item.user_id)" />
|
class="pointer"
|
||||||
|
:src="item.avatar"
|
||||||
|
:size="42"
|
||||||
|
:username="item.nickname"
|
||||||
|
@click="showUserInfoModal(item.erp_user_id, item.user_id)"
|
||||||
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- 主体信息 -->
|
<!-- 主体信息 -->
|
||||||
<main class="main-column">
|
<main class="main-column">
|
||||||
<div class="talk-title">
|
<div class="talk-title">
|
||||||
<span class="nickname pointer" v-show="talk_type == 2 && item.float == 'left'"
|
<span
|
||||||
@click="onClickNickname(item)">
|
class="nickname pointer"
|
||||||
|
v-show="talk_type == 2 && item.float == 'left'"
|
||||||
|
@click="onClickNickname(item)"
|
||||||
|
>
|
||||||
<span class="at">@</span>{{ item.nickname }}
|
<span class="at">@</span>{{ item.nickname }}
|
||||||
</span>
|
</span>
|
||||||
<span>{{ parseTime(item.created_at, '{y}/{m}/{d} {h}:{i}') }}</span>
|
<span>{{ parseTime(item.created_at, '{y}/{m}/{d} {h}:{i}') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="talk-content" :class="{ pointer: dialogueStore.isOpenMultiSelect }" @click="onRowClick(item)">
|
<div
|
||||||
|
class="talk-content"
|
||||||
<component :is="MessageComponents[item.msg_type] || 'unknown-message'" :extra="item.extra" :data="item"
|
:class="{ pointer: dialogueStore.isOpenMultiSelect }"
|
||||||
:max-width="true" :source="'panel'" @contextmenu.prevent="onContextMenu($event, item)" />
|
@click="onRowClick(item)"
|
||||||
<div v-if="item.float==='right'&&item.extra.percentage===-1&&item.extra.is_uploading" class="mr-10px"> <n-button text style="font-size: 20px">
|
>
|
||||||
|
<component
|
||||||
|
:is="MessageComponents[item.msg_type] || 'unknown-message'"
|
||||||
|
:extra="item.extra"
|
||||||
|
:data="item"
|
||||||
|
:max-width="true"
|
||||||
|
:source="'panel'"
|
||||||
|
@contextmenu.prevent="onContextMenu($event, item)"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
item.float === 'right' && item.extra.percentage === -1 && item.extra.is_uploading
|
||||||
|
"
|
||||||
|
class="mr-10px"
|
||||||
|
>
|
||||||
|
<n-button text style="font-size: 20px;">
|
||||||
<n-icon color="#CF3050">
|
<n-icon color="#CF3050">
|
||||||
<ExclamationCircleFilled />
|
<ExclamationCircleFilled />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</n-button></div>
|
</n-button>
|
||||||
|
</div>
|
||||||
<!-- <div class="talk-tools">
|
<!-- <div class="talk-tools">
|
||||||
<template v-if="talk_type == 1 && item.float == 'right'">
|
<template v-if="talk_type == 1 && item.float == 'right'">
|
||||||
<loading
|
<loading
|
||||||
@ -361,7 +442,11 @@ onMounted(() => {
|
|||||||
</div> -->
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="item.extra.reply" class="talk-reply pointer" @click="onJumpMessage(item.extra?.reply?.msg_id)">
|
<div
|
||||||
|
v-if="item.extra.reply"
|
||||||
|
class="talk-reply pointer"
|
||||||
|
@click="onJumpMessage(item.extra?.reply?.msg_id)"
|
||||||
|
>
|
||||||
<n-icon :component="ToTop" size="14" class="icon-top" />
|
<n-icon :component="ToTop" size="14" class="icon-top" />
|
||||||
<span class="ellipsis">
|
<span class="ellipsis">
|
||||||
回复 {{ item.extra?.reply?.nickname }}:
|
回复 {{ item.extra?.reply?.nickname }}:
|
||||||
@ -382,8 +467,15 @@ onMounted(() => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- 右键菜单 -->
|
<!-- 右键菜单 -->
|
||||||
<n-dropdown :show="dropdown.show" :x="dropdown.x" :y="dropdown.y" style="width: 142px;" :options="dropdown.options"
|
<n-dropdown
|
||||||
@select="onContextMenuHandle" @clickoutside="closeDropdownMenu" />
|
:show="dropdown.show"
|
||||||
|
:x="dropdown.x"
|
||||||
|
:y="dropdown.y"
|
||||||
|
style="width: 142px;"
|
||||||
|
:options="dropdown.options"
|
||||||
|
@select="onContextMenuHandle"
|
||||||
|
@clickoutside="closeDropdownMenu"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
Loading…
Reference in New Issue
Block a user