Compare commits
No commits in common. "409af72039a847b3b5df9da9e484424da44363ca" and "ec18d85546a678a68a93083404b0874af1c192bc" have entirely different histories.
409af72039
...
ec18d85546
1
env/.env.test
vendored
1
env/.env.test
vendored
@ -8,5 +8,4 @@ VUE_APP_PREVIEW=false
|
|||||||
VITE_BASE_API=http://114.218.158.24:8503
|
VITE_BASE_API=http://114.218.158.24:8503
|
||||||
VITE_SOCKET_API=ws://114.218.158.24:8504
|
VITE_SOCKET_API=ws://114.218.158.24:8504
|
||||||
VITE_EPR_BASEURL=http://114.218.158.24:9020
|
VITE_EPR_BASEURL=http://114.218.158.24:9020
|
||||||
VITE_PAGE_URL=http://172.16.100.93:9032
|
|
||||||
VUE_APP_WEBSITE_NAME=""
|
VUE_APP_WEBSITE_NAME=""
|
@ -363,9 +363,22 @@ async function onUploadFile(e: any) {
|
|||||||
console.log("文件类型"+file.type)
|
console.log("文件类型"+file.type)
|
||||||
if (file.type.indexOf('image/') === 0) {
|
if (file.type.indexOf('image/') === 0) {
|
||||||
console.log("进入图片")
|
console.log("进入图片")
|
||||||
// 处理图片文件 - 立即显示临时消息,然后上传
|
// 处理图片文件
|
||||||
let fn = emitCall('image_event', file, () => {})
|
const quill = getQuill()
|
||||||
emit('editor-event', fn)
|
let index = getQuillSelectionIndex()
|
||||||
|
|
||||||
|
// 删除编辑器中多余的换行符
|
||||||
|
if (index == 1 && quill.getLength() == 1 && quill.getText(0, 1) == '\n') {
|
||||||
|
quill.deleteText(0, 1)
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传图片并插入到编辑器中
|
||||||
|
let src = await onUploadImage(file)
|
||||||
|
if (src) {
|
||||||
|
quill.insertEmbed(index, 'image', src)
|
||||||
|
quill.setSelection(index + 1)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -424,7 +437,8 @@ function onClipboardMatcher(node: any, Delta) {
|
|||||||
function onSendMessage() {
|
function onSendMessage() {
|
||||||
var delta = getQuill().getContents()
|
var delta = getQuill().getContents()
|
||||||
let data = deltaToMessage(delta) // 转换Delta为消息格式
|
let data = deltaToMessage(delta) // 转换Delta为消息格式
|
||||||
if (data.items.length === 0||!data.items[0].content.trim()) {
|
|
||||||
|
if (data.items.length === 0) {
|
||||||
return // 没有内容不发送
|
return // 没有内容不发送
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ const PPT_EXTENSIONS = ['PPT', 'PPTX', 'PPS', 'PPSX']
|
|||||||
|
|
||||||
// 获取文件类型信息
|
// 获取文件类型信息
|
||||||
const fileInfo = computed(() => {
|
const fileInfo = computed(() => {
|
||||||
const extension = getFileExtension(props.extra.path)
|
const extension = getFileExtension(props.extra.name)
|
||||||
if (EXCEL_EXTENSIONS.includes(extension)) {
|
if (EXCEL_EXTENSIONS.includes(extension)) {
|
||||||
return fileTypes.EXCEL
|
return fileTypes.EXCEL
|
||||||
}
|
}
|
||||||
@ -63,8 +63,8 @@ const fileInfo = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 获取文件扩展名
|
// 获取文件扩展名
|
||||||
function getFileExtension(filepath) {
|
function getFileExtension(filename) {
|
||||||
const parts = filepath.split('.')
|
const parts = filename.split('.')
|
||||||
return parts.length > 1 ? parts.pop().toUpperCase() : ''
|
return parts.length > 1 ? parts.pop().toUpperCase() : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ const strokeDashoffset = computed(() =>
|
|||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if(!props.extra.is_uploading){
|
if(!props.extra.is_uploading){
|
||||||
window.open(
|
window.open(
|
||||||
`${import.meta.env.VITE_PAGE_URL}/office?url=${props.extra.path}`,
|
`${window.location.origin}/office?url=${props.extra.path}`,
|
||||||
'_blank',
|
'_blank',
|
||||||
'width=1200,height=900,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no'
|
'width=1200,height=900,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no'
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { NImage, NSpin } from 'naive-ui'
|
import { NImage } from 'naive-ui'
|
||||||
import { getImageInfo } from '@/utils/functions'
|
import { getImageInfo } from '@/utils/functions'
|
||||||
import { ITalkRecordExtraImage, ITalkRecord } from '@/types/chat'
|
import { ITalkRecordExtraImage, ITalkRecord } from '@/types/chat'
|
||||||
|
|
||||||
const props = defineProps<{
|
defineProps<{
|
||||||
extra: ITalkRecordExtraImage
|
extra: ITalkRecordExtraImage
|
||||||
data: ITalkRecord
|
data: ITalkRecord
|
||||||
maxWidth?: Boolean
|
maxWidth?: Boolean
|
||||||
@ -35,13 +35,7 @@ const img = (src: string, width = 200) => {
|
|||||||
:class="{ left: data.float === 'left' }"
|
:class="{ left: data.float === 'left' }"
|
||||||
:style="img(extra.url, 350)"
|
:style="img(extra.url, 350)"
|
||||||
>
|
>
|
||||||
<div class="image-container">
|
|
||||||
<n-image class="h-149px" :src="extra.url" />
|
<n-image class="h-149px" :src="extra.url" />
|
||||||
<!-- 上传中的loading蒙版 -->
|
|
||||||
<div v-if="props.extra.is_uploading" class="loading-overlay">
|
|
||||||
<n-spin size="large" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@ -50,32 +44,11 @@ const img = (src: string, width = 200) => {
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background: var(--im-message-left-bg-color);
|
background: var(--im-message-left-bg-color);
|
||||||
height:149px;
|
height:149px
|
||||||
|
|
||||||
&.left {
|
&.left {
|
||||||
background: var(--im-message-right-bg-color);
|
background: var(--im-message-right-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-container {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-overlay {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-radius: 5px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.n-image img) {
|
:deep(.n-image img) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -17,7 +17,7 @@ let textContent = props.extra?.content || ''
|
|||||||
textContent = textReplaceLink(textContent)
|
textContent = textReplaceLink(textContent)
|
||||||
|
|
||||||
if (props.data.talk_type == 2) {
|
if (props.data.talk_type == 2) {
|
||||||
textContent = textReplaceMention(textContent, float==='right'?'#fff':'#462AA0')
|
textContent = textReplaceMention(textContent, '#462AA0')
|
||||||
}
|
}
|
||||||
|
|
||||||
textContent = textReplaceEmoji(textContent)
|
textContent = textReplaceEmoji(textContent)
|
||||||
|
@ -21,7 +21,7 @@ export function useFriendsMenu() {
|
|||||||
dropdown.item = Object.assign({}, item)
|
dropdown.item = Object.assign({}, item)
|
||||||
|
|
||||||
dropdown.options = []
|
dropdown.options = []
|
||||||
// dropdown.options.push({ label: '删除好友', key: 'delete' })
|
dropdown.options.push({ label: '删除好友', key: 'delete' })
|
||||||
|
|
||||||
dropdown.x = e.clientX
|
dropdown.x = e.clientX
|
||||||
dropdown.y = e.clientY
|
dropdown.y = e.clientY
|
||||||
|
@ -71,11 +71,11 @@ export function useSessionMenu() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (item.talk_type == 1) {
|
if (item.talk_type == 1) {
|
||||||
// options.push({
|
options.push({
|
||||||
|
|
||||||
// label: '删除好友',
|
label: '删除好友',
|
||||||
// key: 'delete_contact'
|
key: 'delete_contact'
|
||||||
// })
|
})
|
||||||
} else {
|
} else {
|
||||||
options.push({
|
options.push({
|
||||||
|
|
||||||
@ -107,7 +107,9 @@ export function useSessionMenu() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onUserInfo = (item: ISession) => {
|
const onUserInfo = (item: ISession) => {
|
||||||
user(item.erp_user_id)
|
console.error('item',item)
|
||||||
|
|
||||||
|
user(item.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除会话
|
// 移除会话
|
||||||
|
@ -27,7 +27,6 @@ export interface ISession {
|
|||||||
content?: string // 消息内容
|
content?: string // 消息内容
|
||||||
last_message?: ISessionLastMessage
|
last_message?: ISessionLastMessage
|
||||||
draft_text?: string // 草稿文本
|
draft_text?: string // 草稿文本
|
||||||
erp_user_id:number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 消息记录
|
// 消息记录
|
||||||
|
@ -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'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22726726265e9af0db370a54ea5ee002b43662d571b84c8468ac15330f79503a5cd5e72282d8bee92749b1a3c1b7fd87ae70b64b90e437e84c1b558c64a35e181b2ecf5db3007680c3607eac1edee7f59d'
|
return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22a02b52442e2e6a79ca35b2fa54a4bb215abd3b9cc8ad2ef018b380a88e93a2fc55fb3e36695ae76a0ea52c973374861c70339249f89f36d787b0dfa7fc99339470dbc626eb5ff42232137ef97447e79f'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch, onMounted, ref, nextTick } 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'
|
||||||
@ -14,10 +14,42 @@ 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 ,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 avatarModule from '@/components/avatar-module/index.vue'
|
||||||
|
|
||||||
|
// 定义消息已读状态接口
|
||||||
|
interface ReadStatus {
|
||||||
|
msg_ids: string[]
|
||||||
|
talk_type: number
|
||||||
|
receiver_id: number
|
||||||
|
user_id?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义状态接口
|
||||||
|
interface State {
|
||||||
|
visibleElements: Set<HTMLElement>
|
||||||
|
visibleOutElements: Set<HTMLElement>
|
||||||
|
tempWaitDoRead: ReadStatus[]
|
||||||
|
tempWaitDoCheck: ReadStatus[]
|
||||||
|
setMessageReadInterval: number | null
|
||||||
|
setOutMessageReadInterval: number | null
|
||||||
|
lastUpdateTime: number
|
||||||
|
isScrolling: boolean
|
||||||
|
scrollTimer: number | null
|
||||||
|
lastTriggerTime: number
|
||||||
|
talkReadListDetail: any[]
|
||||||
|
readDetailIsUnread: number
|
||||||
|
currentMsgReadDetail: any | null
|
||||||
|
currentReadDetailPage: number
|
||||||
|
hasMoreReadListDetail: boolean
|
||||||
|
loadingReadListDetail: boolean
|
||||||
|
lastScrollTop: number
|
||||||
|
}
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
uid: {
|
uid: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -38,10 +70,14 @@ const props = defineProps({
|
|||||||
specifiedMsg: {
|
specifiedMsg: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
},
|
||||||
|
num: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { loadConfig, records, onLoad, onRefreshLoad, onJumpMessage } = useTalkRecord(props.uid)
|
const { loadConfig, records, onLoad, onRefreshLoad, onJumpMessage, onLoadMoreDown } = useTalkRecord(props.uid)
|
||||||
const uploadsStore = useUploadsStore()
|
const uploadsStore = useUploadsStore()
|
||||||
const { useMessage } = useUtil()
|
const { useMessage } = useUtil()
|
||||||
const { dropdown, showDropdownMenu, closeDropdownMenu } = useMenu()
|
const { dropdown, showDropdownMenu, closeDropdownMenu } = useMenu()
|
||||||
@ -90,11 +126,33 @@ const onPanelScroll = (e: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const height = e.target.scrollTop + e.target.clientHeight
|
const height = e.target.scrollTop + e.target.clientHeight
|
||||||
|
const scrollHeight = e.target.scrollHeight
|
||||||
|
const isScrollingDown = e.target.scrollTop > state.value.lastScrollTop
|
||||||
|
state.value.lastScrollTop = e.target.scrollTop
|
||||||
|
|
||||||
skipBottom.value = height < e.target.scrollHeight - 200
|
// 触底且必须是向下滚动
|
||||||
|
if (height === scrollHeight && isScrollingDown) {
|
||||||
|
onLoadMoreDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
skipBottom.value = height < scrollHeight - 200
|
||||||
if (!skipBottom.value && dialogueStore.unreadBubble) {
|
if (!skipBottom.value && dialogueStore.unreadBubble) {
|
||||||
dialogueStore.setUnreadBubble(0)
|
dialogueStore.setUnreadBubble(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置滚动状态
|
||||||
|
state.value.isScrolling = true
|
||||||
|
if (state.value.scrollTimer) {
|
||||||
|
clearTimeout(state.value.scrollTimer)
|
||||||
|
}
|
||||||
|
state.value.scrollTimer = window.setTimeout(() => {
|
||||||
|
state.value.isScrolling = false
|
||||||
|
// 滚动停止,强制触发一次
|
||||||
|
checkVisibleOutElements()
|
||||||
|
// 重置节流时间戳,保证下一次变化能立刻触发
|
||||||
|
lastVisibleOutTriggerTime = Date.now()
|
||||||
|
}, 300) // 300ms 的防抖时间
|
||||||
|
|
||||||
// 检测是否到达底部
|
// 检测是否到达底部
|
||||||
if (skipBottom.value == false) {
|
if (skipBottom.value == false) {
|
||||||
let len = dialogueStore.records.length
|
let len = dialogueStore.records.length
|
||||||
@ -231,7 +289,6 @@ const onClickNickname = (data: ITalkRecord) => {
|
|||||||
|
|
||||||
// 会话列表右键显示菜单
|
// 会话列表右键显示菜单
|
||||||
const onContextMenu = (e: any, item: ITalkRecord) => {
|
const onContextMenu = (e: any, item: ITalkRecord) => {
|
||||||
|
|
||||||
if (!dialogueStore.isShowEditor || dialogueStore.isOpenMultiSelect) {
|
if (!dialogueStore.isShowEditor || dialogueStore.isOpenMultiSelect) {
|
||||||
return e.preventDefault()
|
return e.preventDefault()
|
||||||
}
|
}
|
||||||
@ -294,7 +351,10 @@ watch(
|
|||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(decodeURIComponent(newProps.specifiedMsg))
|
const parsed = JSON.parse(decodeURIComponent(newProps.specifiedMsg))
|
||||||
// 只有会话id和参数都匹配才进入特殊模式
|
// 只有会话id和参数都匹配才进入特殊模式
|
||||||
if (parsed.talk_type === newProps.talk_type && parsed.receiver_id === newProps.receiver_id) {
|
if (
|
||||||
|
parsed.talk_type === newProps.talk_type &&
|
||||||
|
parsed.receiver_id === newProps.receiver_id
|
||||||
|
) {
|
||||||
specialParams = parsed
|
specialParams = parsed
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
@ -312,29 +372,350 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// onMounted(() => {
|
// onMounted(() => {
|
||||||
// onLoad({ ...props, limit: 30 })
|
// onLoad({ ...props, limit: 30 })
|
||||||
// })
|
// })
|
||||||
const retry=(item:any)=>{
|
const retry = (item: any) => {
|
||||||
confirmBox({
|
confirmBox({
|
||||||
content:'确定重发吗'
|
content: '确定重发吗'
|
||||||
}).then(()=>{
|
}).then(() => {
|
||||||
uploadsStore.retryCommonUpload(item.extra.upload_id)
|
uploadsStore.retryCommonUpload(item.extra.upload_id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onContextMenuAvatar=(e:any,item:any)=>{
|
const onContextMenuAvatar = (e: any, item: any) => {
|
||||||
if(item.talk_type!==1){
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if(item.float!=='right'){
|
if (item.float !== 'right') {
|
||||||
bus.emit(EditorConst.Mention, {
|
bus.emit(EditorConst.Mention, {
|
||||||
id: item.user_id,
|
id: item.user_id,
|
||||||
value: item.nickname
|
value: item.nickname
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const state = ref<State>({
|
||||||
|
visibleElements: new Set(),
|
||||||
|
visibleOutElements: new Set(),
|
||||||
|
tempWaitDoRead: [],
|
||||||
|
tempWaitDoCheck: [],
|
||||||
|
setMessageReadInterval: null,
|
||||||
|
setOutMessageReadInterval: null,
|
||||||
|
lastUpdateTime: 0,
|
||||||
|
isScrolling: false,
|
||||||
|
scrollTimer: null,
|
||||||
|
lastTriggerTime: 0,
|
||||||
|
talkReadListDetail: [],
|
||||||
|
readDetailIsUnread: 1,
|
||||||
|
currentMsgReadDetail: null,
|
||||||
|
currentReadDetailPage: 1,
|
||||||
|
hasMoreReadListDetail: true,
|
||||||
|
loadingReadListDetail: false,
|
||||||
|
lastScrollTop: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义观察者变量
|
||||||
|
let observer: IntersectionObserver | null = null
|
||||||
|
|
||||||
|
// 检查需要发送已读回执的元素
|
||||||
|
const checkVisibleElements = () => {
|
||||||
|
if (state.value.visibleElements.size > 0) {
|
||||||
|
let waitDoRead: ReadStatus[] = []
|
||||||
|
state.value.visibleElements.forEach((el: HTMLElement) => {
|
||||||
|
const msgId = el.dataset.msgid
|
||||||
|
const talkType = Number(el.dataset.talktype)
|
||||||
|
const receiverId = Number(el.dataset.receiverid)
|
||||||
|
|
||||||
|
if (!msgId) return
|
||||||
|
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
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
|
||||||
|
)
|
||||||
|
if (!prevItem || !doReadItem.msg_ids.every((id) => prevItem.msg_ids.includes(id))) {
|
||||||
|
console.error('====发送了新版已读回执=====', doReadItem)
|
||||||
|
ws.emit('im.message.new.read', doReadItem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
state.value.tempWaitDoRead = JSON.parse(JSON.stringify(waitDoRead))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查需要获取别人发送的已读回执列表的元素
|
||||||
|
const checkVisibleOutElements = () => {
|
||||||
|
if (state.value.visibleOutElements.size > 0) {
|
||||||
|
let waitDoCheck: ReadStatus[] = []
|
||||||
|
state.value.visibleOutElements.forEach((el: HTMLElement) => {
|
||||||
|
const msgId = el.dataset.msgid
|
||||||
|
const talkType = Number(el.dataset.talktype)
|
||||||
|
const receiverId = Number(el.dataset.receiverid)
|
||||||
|
|
||||||
|
if (!msgId) return
|
||||||
|
|
||||||
|
if (waitDoCheck.length === 0) {
|
||||||
|
waitDoCheck.push({
|
||||||
|
msg_ids: [msgId],
|
||||||
|
talk_type: talkType,
|
||||||
|
receiver_id: receiverId,
|
||||||
|
user_id: props.uid
|
||||||
|
})
|
||||||
|
} 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,
|
||||||
|
user_id: props.uid
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (waitDoCheck.length > 0) {
|
||||||
|
waitDoCheck.forEach((doCheckItem) => {
|
||||||
|
console.error('====组装了新版已读回执参数,需要发送socket=====', doCheckItem)
|
||||||
|
ws.emit('im.message.listen.read', doCheckItem)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
state.value.tempWaitDoCheck = JSON.parse(JSON.stringify(waitDoCheck))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义节流时间戳
|
||||||
|
let lastVisibleOutTriggerTime = 0
|
||||||
|
|
||||||
|
//新版采用socket监听已读回执,不轮询接口
|
||||||
|
watch(
|
||||||
|
() => state.value.visibleOutElements,
|
||||||
|
(newVal) => {
|
||||||
|
const now = Date.now()
|
||||||
|
if (now - lastVisibleOutTriggerTime < 1000) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastVisibleOutTriggerTime = now
|
||||||
|
checkVisibleOutElements()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 观察者函数
|
||||||
|
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(props.uid)) {
|
||||||
|
//我读别人发的消息,需要发送已读回执
|
||||||
|
state.value.visibleElements.add(entry.target)
|
||||||
|
}
|
||||||
|
if (Number(msgType) < 1000 && Number(userId) === Number(props.uid)) {
|
||||||
|
//我发的消息,需要获取别人发送的已读回执列表
|
||||||
|
state.value.visibleOutElements.add(entry.target)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 元素离开视口,从集合中移除
|
||||||
|
state.value.visibleElements.delete(entry.target)
|
||||||
|
state.value.visibleOutElements.delete(entry.target)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听消息列表变化
|
||||||
|
watch(
|
||||||
|
() => records.value,
|
||||||
|
() => {
|
||||||
|
nextTick(() => {
|
||||||
|
// 断开旧的观察者
|
||||||
|
if (observer) {
|
||||||
|
observer.disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重新初始化观察者
|
||||||
|
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)
|
||||||
|
|
||||||
|
// 重新观察所有消息元素
|
||||||
|
const messageElements = document.querySelectorAll('.message-item')
|
||||||
|
messageElements.forEach((el) => {
|
||||||
|
if (observer) {
|
||||||
|
observer.observe(el)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 事件总线防抖处理
|
||||||
|
let eventBusDebounceTimer: number | null = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 事件总线监听
|
||||||
|
bus.subscribe('check-visible-out-elements', (type) => {
|
||||||
|
if (eventBusDebounceTimer) {
|
||||||
|
clearTimeout(eventBusDebounceTimer)
|
||||||
|
}
|
||||||
|
eventBusDebounceTimer = window.setTimeout(() => {
|
||||||
|
checkVisibleOutElements()
|
||||||
|
eventBusDebounceTimer = null
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
//设置观察者前设置定时器
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// 旧版采用定时器来轮询已读回执,新版采用socket监听已读回执
|
||||||
|
// 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(() => {
|
||||||
|
const messageElements = document.querySelectorAll('.message-item')
|
||||||
|
messageElements.forEach((el) => {
|
||||||
|
if (observer) {
|
||||||
|
observer.observe(el)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
// 清理事件总线防抖定时器
|
||||||
|
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>
|
||||||
@ -356,6 +737,11 @@ const onContextMenuAvatar=(e:any,item:any)=>{
|
|||||||
v-for="(item, index) in records"
|
v-for="(item, index) in records"
|
||||||
:key="item.msg_id"
|
:key="item.msg_id"
|
||||||
:id="item.msg_id"
|
:id="item.msg_id"
|
||||||
|
:data-msgid="item.msg_id"
|
||||||
|
:data-msgtype="item.msg_type"
|
||||||
|
:data-userid="item.user_id"
|
||||||
|
:data-talktype="props?.talk_type"
|
||||||
|
:data-receiverid="props?.receiver_id"
|
||||||
>
|
>
|
||||||
<!-- 系统消息 -->
|
<!-- 系统消息 -->
|
||||||
<div v-if="item.msg_type >= 1000" class="message-box">
|
<div v-if="item.msg_type >= 1000" class="message-box">
|
||||||
@ -389,7 +775,11 @@ const onContextMenuAvatar=(e:any,item:any)=>{
|
|||||||
>
|
>
|
||||||
<!-- 多选按钮 -->
|
<!-- 多选按钮 -->
|
||||||
<aside v-if="dialogueStore.isOpenMultiSelect" class="checkbox-column shrink-0">
|
<aside v-if="dialogueStore.isOpenMultiSelect" class="checkbox-column shrink-0">
|
||||||
<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>
|
||||||
<!-- 头像信息 -->
|
<!-- 头像信息 -->
|
||||||
|
|
||||||
@ -417,9 +807,7 @@ const onContextMenuAvatar=(e:any,item:any)=>{
|
|||||||
<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-title">
|
<div class="talk-title">
|
||||||
<span class="mr-7px"
|
<span class="mr-7px" v-show="talk_type == 2 && item.float == 'left'"
|
||||||
v-show="talk_type == 2 && item.float == 'left'"
|
|
||||||
|
|
||||||
>{{ item.nickname }}
|
>{{ 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>
|
||||||
@ -479,6 +867,72 @@ const onContextMenuAvatar=(e:any,item:any)=>{
|
|||||||
{{ item.extra?.reply?.content }}
|
{{ item.extra?.reply?.content }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 已读回执 -->
|
||||||
|
<div class="talk_read_num" v-if="item.user_id === props.uid">
|
||||||
|
<span v-if="props.talk_type === 1">{{
|
||||||
|
item.read_total_num > 0 ? '已读' : '未读'
|
||||||
|
}}</span>
|
||||||
|
<n-popover trigger="click" placement="bottom-end" style="height: 382px; padding: 0;" v-if="props.talk_type === 2">
|
||||||
|
<template #trigger>
|
||||||
|
<span v-if="props.talk_type === 2" @click="toShowMessageReadDetail(item)" style="cursor: pointer;">
|
||||||
|
已读 ({{ 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>
|
||||||
|
|
||||||
@ -677,6 +1131,17 @@ const onContextMenuAvatar=(e:any,item:any)=>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.talk_read_num {
|
||||||
|
text-align: right;
|
||||||
|
color: #7a58de;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 17px;
|
||||||
|
margin: 5px 0 0;
|
||||||
|
span {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.talk-title {
|
.talk-title {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -725,4 +1190,31 @@ const onContextMenuAvatar=(e:any,item:any)=>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.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>
|
||||||
|
@ -94,60 +94,8 @@ const onSendTextEvent = throttle((value: any) => {
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
// 发送图片消息
|
// 发送图片消息
|
||||||
const onSendImageEvent = ({ data }) => {
|
const onSendImageEvent = ({ data, callBack }) => {
|
||||||
console.log('onSendImageEvent')
|
onSendMessage({ type: 'image', ...data }, callBack)
|
||||||
|
|
||||||
// 先创建一个带有上传ID的临时消息对象,用于显示进度
|
|
||||||
const uploadId = `image-${Date.now()}-${Math.floor(Math.random() * 1000)}`
|
|
||||||
|
|
||||||
// 创建本地预览URL
|
|
||||||
const previewUrl = URL.createObjectURL(data)
|
|
||||||
|
|
||||||
// 创建临时消息记录
|
|
||||||
const tempMessage = {
|
|
||||||
msg_id: uploadId,
|
|
||||||
sequence: Date.now(),
|
|
||||||
talk_type: props.talk_type,
|
|
||||||
msg_type: 3, // 图片消息类型
|
|
||||||
user_id: props.uid,
|
|
||||||
receiver_id: props.receiver_id,
|
|
||||||
is_revoke: 0,
|
|
||||||
is_mark: 0,
|
|
||||||
is_read: 1,
|
|
||||||
content: '',
|
|
||||||
created_at: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}'),
|
|
||||||
extra: {
|
|
||||||
url: previewUrl, // 使用本地预览URL
|
|
||||||
size: data.size,
|
|
||||||
is_uploading: true,
|
|
||||||
upload_id: uploadId,
|
|
||||||
percentage: 0
|
|
||||||
},
|
|
||||||
isCheck: false,
|
|
||||||
send_status: 1,
|
|
||||||
float: 'right' // 我发送的消息显示在右侧
|
|
||||||
}
|
|
||||||
|
|
||||||
// 直接添加到对话记录中
|
|
||||||
dialogueStore.addDialogueRecord(tempMessage)
|
|
||||||
nextTick(()=>{
|
|
||||||
scrollToBottom()
|
|
||||||
})
|
|
||||||
uploadsStore.initUploadFile(
|
|
||||||
data,
|
|
||||||
props.talk_type,
|
|
||||||
props.receiver_id,
|
|
||||||
uploadId,
|
|
||||||
async (percentage) => {
|
|
||||||
dialogueStore.updateUploadProgress(uploadId, percentage)
|
|
||||||
},
|
|
||||||
async () => {
|
|
||||||
// 清理本地预览URL
|
|
||||||
URL.revokeObjectURL(previewUrl)
|
|
||||||
dialogueStore.batchDelDialogueRecord([uploadId])
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送视频消息
|
// 发送视频消息
|
||||||
|
@ -46,9 +46,9 @@ export default defineConfig(({ mode }) => {
|
|||||||
vueJsx({}),
|
vueJsx({}),
|
||||||
compressPlugin(),
|
compressPlugin(),
|
||||||
UnoCSS(),
|
UnoCSS(),
|
||||||
vueDevTools({
|
// vueDevTools({
|
||||||
launchEditor: 'trae',
|
// launchEditor: 'trae',
|
||||||
})
|
// })
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
__APP_ENV__: env.APP_ENV
|
__APP_ENV__: env.APP_ENV
|
||||||
|
Loading…
Reference in New Issue
Block a user