chat-app/src/pages/dialog/index.vue

1145 lines
29 KiB
Vue
Raw Normal View History

2024-11-22 01:06:37 +00:00
<template>
<div class="dialog-page">
<ZPaging
use-chat-record-mode
:refresher-enabled="false"
:show-scrollbar="false"
:loading-more-enabled="false"
:hide-empty-view="true"
height="100%"
ref="zpagingRef"
:use-virtual-list="true"
:preload-page="1"
cell-height-mode="dynamic"
virtual-scroll-fps="80"
:loading-more-custom-style="{ display: 'none', height: '0' }"
@virtualListChange="virtualListChange"
@scrolltolower="onRefreshLoad"
>
<template #top>
<customNavbar :title="talkParams.username">
<template
#subTitle
v-if="talkStore?.findItem(talkParams.index_name)?.group_type === 4"
>
<div class="text-[24rpx] text-[#999999]">公司群</div>
</template>
<template #right>
<div class="mr-[36rpx] toChatSetting_btn">
<tm-icon
color="rgb(51, 51, 51)"
:font-size="36"
name="tmicon-gengduo"
@click="toChatSettingsPage"
></tm-icon>
</div>
</template>
</customNavbar>
</template>
<!-- <template #top>
2024-12-06 08:55:15 +00:00
<div class="load-toolbar pointer">
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
<span v-else-if="loadConfig.status == 1" @click="onRefreshLoad"> 查看更多消息 ... </span>
<span v-else class="no-more"> 没有更多消息了 </span>
</div>
</template> -->
<!-- 数据加载状态栏 -->
<div class="dialog-list">
<div
class="message-item"
v-for="item in virtualList"
:id="`zp-id-${item.zp_index}`"
:key="item.zp_index"
style="transform: scaleY(-1);"
>
<!-- 系统消息 -->
<div v-if="item.msg_type >= 1000" class="message-box">
<component
:is="MessageComponents[item.msg_type] || 'unknown-message'"
:extra="item.extra"
:data="item"
/>
</div>
<!-- 撤回消息 -->
<div v-else-if="item.is_revoke == 1" class="message-box">
<revoke-message
:login_uid="userStore.uid"
:user_id="item.user_id"
:nickname="item.nickname"
:talk_type="item.talk_type"
:datetime="item.created_at"
/>
</div>
<div
v-else
class="message-box record-box"
:class="{
'direction-rt': item.float == 'right',
'multi-select': dialogueStore.isOpenMultiSelect,
'multi-select-check': item.isCheck,
}"
>
<!-- 多选按钮 -->
<aside
v-if="dialogueStore.isOpenMultiSelect"
class="checkbox-column"
>
<!-- <n-checkbox size="small" :checked="item.isCheck" @update:checked="item.isCheck = !item.isCheck" /> -->
<tm-checkbox
:round="10"
:defaultChecked="item.isCheck"
@update:modelValue="item.isCheck = !item.isCheck"
:size="42"
color="#46299D"
></tm-checkbox>
</aside>
<!-- 头像信息 -->
<aside class="avatar-column" @click="toUserDetailPage(item)">
<im-avatar
class="pointer"
:src="item.avatar"
:size="80"
:username="item.nickname"
@click="showUserInfoModal(item.user_id)"
/>
</aside>
<!-- 主体信息 -->
<main class="main-column">
<div class="talk-title">
<span
class="nickname pointer"
v-show="talkParams.type == 2 && item.float == 'left'"
@click="onClickNickname(item)"
>
<span class="at">@</span>
{{ item.nickname }}
</span>
<span>
{{ parseTime(item.created_at, '{m}/{d} {h}:{i}') }}
</span>
</div>
<div
class="talk-content"
:class="{ pointer: dialogueStore.isOpenMultiSelect }"
>
<deepBubble
@clickMenu="(menuType) => onContextMenu(menuType, item)"
:isShowCopy="isShowCopy(item)"
:isShowWithdraw="isRevoke(talkParams.uid, item)"
>
<component
class="component-content"
:key="item.zp_index"
:is="MessageComponents[item.msg_type] || 'unknown-message'"
:extra="item.extra"
:data="item"
:max-width="true"
:source="'panel'"
/>
</deepBubble>
<!-- <div class="talk-tools">
2024-12-06 08:55:15 +00:00
<template v-if="talkParams.type == 1 && item.float == 'right'">
2024-12-19 03:02:47 +00:00
<loading theme="outline" size="19" fill="#000" :sxtrokeWidth="1" class="icon-rotate"
2024-11-26 08:51:36 +00:00
v-show="item.send_status == 1" />
2024-11-22 09:00:03 +00:00
2024-11-26 08:51:36 +00:00
<span v-show="item.send_status == 1"> 正在发送... </span>
2024-12-06 08:55:15 +00:00
<span v-show="item.send_status != 1"> 已送达 </span>
2024-11-26 08:51:36 +00:00
</template>
2024-12-06 08:55:15 +00:00
</div> -->
</div>
2024-11-22 01:06:37 +00:00
<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" /> -->
<span class="ellipsis">
回复 {{ item.extra?.reply?.nickname }}:
{{ item.extra?.reply?.content }}
</span>
</div>
</main>
2024-12-19 03:02:47 +00:00
</div>
</div>
<div class="load-toolbar pointer" style="transform: scaleY(-1);">
<span v-if="loadConfig.status == 0">正在加载数据中 ...</span>
<span v-else-if="loadConfig.status == 1" @click="onRefreshLoad">
查看更多消息 ...
</span>
<span v-else class="no-more">没有更多消息了</span>
2024-11-22 01:06:37 +00:00
</div>
</div>
<template #bottom>
<div class="footBox">
<div v-if="!dialogueStore.isOpenMultiSelect">
<div
class="pt-[16rpx] ml-[32rpx] mr-[32rpx] flex items-center justify-between"
>
<div class="flex-1 quillBox">
<QuillEditor
ref="editor"
id="editor"
:options="editorOption"
@editorChange="onEditorChange"
style="height: 100%; border: none;"
/>
<!-- <tm-input type=textarea autoHeight focusColor="#F9F9F9" color="#F9F9F9" :inputPadding="[12]"
placeholder=""></tm-input> -->
</div>
<tm-image
:margin="[10, 0]"
@click="handleEmojiPanel"
:width="52"
:height="52"
:round="12"
:src="state.isOpenEmojiPanel ? keyboard : smile"
></tm-image>
<tm-image
@click="handleFilePanel"
:margin="[10, 0]"
:width="52"
:height="52"
:round="12"
:src="addCircleGray"
></tm-image>
<tm-button
@click="onSendMessageClick"
:margin="[0, 0]"
:padding="[0, 30]"
color="#46299D"
:fontSize="28"
size="mini"
:shadow="0"
label="发送"
></tm-button>
</div>
<div v-if="state.isOpenEmojiPanel" class="mt-[50rpx]">
<emojiPanel @on-select="onEmoticonEvent" />
</div>
<div v-if="state.isOpenFilePanel" class="mt-[16rpx]">
<filePanel
@selectImg="handleSelectImg"
:talkParams="talkParams"
/>
</div>
2024-12-19 03:02:47 +00:00
</div>
<div v-else class="h-[232rpx]">
<div
class="flex items-center justify-center mt-[12rpx] text-[24rpx] text-[#747474] leading-[44rpx]"
>
<div class="mr-[8rpx]">已选中:</div>
<div>{{ selectedMessage.length }}条消息</div>
</div>
<div
class="flex items-center justify-around pl-[128rpx] pr-[128rpx] mt-[18rpx] text-[20rpx] text-[#737373]"
>
<div
@click="handleMergeForward"
class="flex flex-col items-center justify-center"
>
<tm-image :width="68" :height="68" :src="zu6050"></tm-image>
<div class="mt-[6rpx]">合并转发</div>
</div>
<div
@click="handleSingleForward"
class="flex flex-col items-center justify-center"
>
<tm-image :width="68" :height="68" :src="zu6051"></tm-image>
<div class="mt-[6rpx]">逐条转发</div>
</div>
<div
@click="handleWechatForward"
class="flex flex-col items-center justify-center"
>
<tm-image :width="68" :height="68" :src="zu6052"></tm-image>
<div class="mt-[6rpx]">微信</div>
</div>
<div
@click="handleDelete"
class="flex flex-col items-center justify-center"
>
<tm-image :width="68" :height="68" :src="zu6053"></tm-image>
<div class="mt-[6rpx]">删除</div>
</div>
</div>
2024-12-19 03:02:47 +00:00
</div>
2024-12-06 08:55:15 +00:00
<!--底部安全区-->
<div class="content-placeholder"></div>
<tm-drawer
placement="bottom"
v-model:show="state.showWin"
:hideHeader="true"
:height="416"
:round="6"
>
<div class="w-full h-full flex flex-col items-center">
<div
class="mt-[46rpx] mb-[44rpx] leading-[48rpx] text-[#747474] text-[24rpx]"
>
撤回该条消息
</div>
<div class="divider"></div>
<div
@click="withdrawerConfirm"
class="mt-[32rpx] mb-[32rpx] text-[32rpx] text-[#CF3050] leading-[48rpx]"
>
撤回
</div>
<div class="divider"></div>
<div
@click="state.showWin = false"
class="mt-[32rpx] mb-[32rpx] text-[32rpx] text-[#000000] leading-[48rpx]"
>
取消
</div>
</div>
</tm-drawer>
2024-12-24 08:28:44 +00:00
</div>
</template>
</ZPaging>
2024-11-22 01:06:37 +00:00
</div>
</template>
<script setup>
import {
ref,
reactive,
watch,
computed,
onMounted,
onUnmounted,
nextTick,
} from 'vue'
2024-11-26 08:51:36 +00:00
import { QuillEditor, Quill } from '@vueup/vue-quill'
2024-11-28 08:55:45 +00:00
import EmojiBlot from './formats/emoji'
import { useChatList } from '@/store/chatList/index.js'
import { useAuth } from '@/store/auth'
2024-12-20 08:59:58 +00:00
import {
useUserStore,
useDialogueStore,
useUploadsStore,
useEditorDraftStore,
useTalkStore,
useSettingsStore,
useDialogueListStore,
2024-12-20 08:59:58 +00:00
} from '@/store'
import addCircleGray from '@/static/image/chatList/addCircleGray.png'
2024-11-22 01:06:37 +00:00
import { MessageComponents, ForwardableMessageType } from '@/constant/message'
2024-11-22 09:00:03 +00:00
import { formatTime, parseTime } from '@/utils/datetime'
2024-11-26 08:51:36 +00:00
import { deltaToMessage, deltaToString, isEmptyDelta } from './util'
import smile from '@/static/image/chatList/smile@2x.png'
import keyboard from '@/static/image/chatList/keyboard@2x.png'
2024-11-22 01:06:37 +00:00
import { useInject, useTalkRecord } from '@/hooks'
2024-11-28 08:55:45 +00:00
import { emitCall } from '@/utils/common'
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
2024-11-26 08:51:36 +00:00
import emojiPanel from './components/emojiPanel.vue'
2024-12-06 08:55:15 +00:00
import filePanel from './components/filePanel.vue'
import lodash from 'lodash'
2024-11-28 08:55:45 +00:00
import { ServePublishMessage } from '@/api/chat'
import copy07 from '@/static/image/chatList/copy07@2x.png'
import multipleChoices from '@/static/image/chatList/multipleChoices@2x.png'
import cite from '@/static/image/chatList/cite@2x.png'
import withdraw from '@/static/image/chatList/withdraw@2x.png'
import delete07 from '@/static/image/chatList/delete@2x.png'
import zu6050 from '@/static/image/chatList/zu6050@2x.png'
import zu6051 from '@/static/image/chatList/zu6051@2x.png'
import zu6052 from '@/static/image/chatList/zu6052@2x.png'
import zu6053 from '@/static/image/chatList/zu6053@2x.png'
import deepBubble from '@/components/deep-bubble/deep-bubble.vue'
import { isRevoke } from './menu'
2024-12-24 08:28:44 +00:00
import useConfirm from '@/components/x-confirm/useConfirm.js'
import { onLoad as uniOnload } from '@dcloudio/uni-app'
2024-11-22 01:06:37 +00:00
2024-11-28 08:55:45 +00:00
Quill.register('formats/emoji', EmojiBlot)
const {
getDialogueList,
updateZpagingRef,
virtualList,
} = useDialogueListStore()
2024-11-28 08:55:45 +00:00
const talkStore = useTalkStore()
const { showConfirm } = useConfirm()
2024-11-28 08:55:45 +00:00
const settingsStore = useSettingsStore()
2024-11-22 01:06:37 +00:00
const userStore = useUserStore()
const dialogueStore = useDialogueStore()
2024-11-26 08:51:36 +00:00
const editorDraftStore = useEditorDraftStore()
const editor = ref()
2024-12-10 03:31:36 +00:00
const zpagingRef = ref()
2024-12-06 08:55:15 +00:00
useZPaging(zpagingRef)
2024-11-26 08:51:36 +00:00
const indexName = computed(() => dialogueStore.index_name)
2024-11-22 01:06:37 +00:00
const talkParams = reactive({
uid: computed(() => userStore.uid),
index_name: computed(() => dialogueStore.index_name),
type: computed(() => dialogueStore.talk.talk_type),
receiver_id: computed(() => dialogueStore.talk.receiver_id),
username: computed(() => dialogueStore.talk.username),
online: computed(() => dialogueStore.online),
keyboard: computed(() => dialogueStore.keyboard),
num: computed(() => dialogueStore.members.length),
2024-11-22 01:06:37 +00:00
})
2024-11-26 08:51:36 +00:00
const state = ref({
2024-12-06 08:55:15 +00:00
isOpenEmojiPanel: false,
2024-12-19 03:02:47 +00:00
isOpenFilePanel: false,
2024-12-24 08:28:44 +00:00
showWin: false,
onfocusItem: null,
sessionId: '',
})
uniOnload((options) => {
if (options.sessionId) {
state.sessionId = options.sessionId
}
2024-11-26 08:51:36 +00:00
})
2024-12-06 08:55:15 +00:00
const handleEmojiPanel = () => {
state.value.isOpenFilePanel = false
2024-12-06 08:55:15 +00:00
state.value.isOpenEmojiPanel = !state.value.isOpenEmojiPanel
}
const handleFilePanel = () => {
state.value.isOpenEmojiPanel = false
2024-12-06 08:55:15 +00:00
state.value.isOpenFilePanel = !state.value.isOpenFilePanel
}
2024-12-13 05:09:38 +00:00
const onSendMessage = (data = {}) => {
2024-11-28 08:55:45 +00:00
let message = {
...data,
receiver: {
receiver_id: talkParams.receiver_id,
talk_type: talkParams.type,
},
2024-11-28 08:55:45 +00:00
}
ServePublishMessage(message)
.then(({ code, message }) => {
if (code == 200) {
2024-12-13 05:09:38 +00:00
// callBack(true)
2024-11-28 08:55:45 +00:00
} else {
message.warning(message)
}
})
.catch(() => {
message.warning('网络繁忙,请稍后重试!')
})
}
const onSendMessageClick = () => {
let delta = getQuill().getContents()
let data = deltaToMessage(delta)
if (data.items.length === 0) {
return
}
switch (data.msgType) {
case 1: // 文字消息
if (data.items[0].content.length > 1024) {
return message.info('发送内容超长,请分条发送')
}
2024-12-19 03:02:47 +00:00
onSendTextEvent({
data,
callBack: (ok) => {
2024-12-19 03:02:47 +00:00
if (!ok) return
getQuill().setContents([], Quill.sources.USER)
},
2024-12-19 03:02:47 +00:00
})
2024-11-28 08:55:45 +00:00
break
}
}
// 发送文本消息
const onSendTextEvent = lodash.throttle((value) => {
let { data, callBack } = value
let message = {
type: 'text',
content: data.items[0].content,
quote_id: data.quoteId,
mentions: data.mentionUids,
2024-11-28 08:55:45 +00:00
}
2024-12-13 05:09:38 +00:00
onSendMessage(message)
2024-11-28 08:55:45 +00:00
}, 1000)
// 编辑器输入事件
const onInputEvent = ({ data }) => {
talkStore.updateItem({
index_name: indexName.value,
draft_text: data,
2024-11-28 08:55:45 +00:00
})
// 判断对方是否在线和是否需要推送
// 3秒时间内推送一次
if (settingsStore.isKeyboard && props.online) {
onKeyboardPush()
}
}
// 发送表情消息
2024-12-13 05:09:38 +00:00
const onSendEmoticonEvent = ({ data }) => {
onSendMessage({ type: 'emoticon', emoticon_id: data })
2024-11-28 08:55:45 +00:00
}
// 注册事件
const evnets = {
text_event: onSendTextEvent,
input_event: onInputEvent,
emoticon_event: onSendEmoticonEvent,
history_event: () => {
isShowHistory.value = true
},
}
const {
loadConfig,
records,
onLoad,
onRefreshLoad,
onJumpMessage,
} = useTalkRecord(talkParams.uid)
2024-11-26 08:51:36 +00:00
const getQuill = () => {
return editor.value?.getQuill()
}
2024-12-20 08:59:58 +00:00
const isShowCopy = (item) => {
switch (item.msg_type) {
case 1:
return true
case 3:
return true
case 5:
return true
case 6:
return true
default:
return false
}
}
2024-12-19 03:02:47 +00:00
const selectedMessage = computed(() => {
return virtualList.value.filter((item) => item.isCheck)
2024-12-19 03:02:47 +00:00
})
2024-11-28 08:55:45 +00:00
// 编辑器事件
const onEditorEvent = (msg) => {
evnets[msg.event] && evnets[msg.event](msg)
}
const getQuillSelectionIndex = () => {
let quill = getQuill()
return (quill.getSelection() || {}).index
}
const onEmoticonEvent = (data) => {
if (data.type == 1) {
const quill = getQuill()
let index = getQuillSelectionIndex()
if (index == 1 && quill.getLength() == 1 && quill.getText(0, 1) == '\n') {
quill.deleteText(0, 1)
index = 0
}
if (data.img) {
quill.insertEmbed(index, 'emoji', {
alt: data.value,
src: data.img,
})
} else {
quill.insertText(index, data.value)
}
quill.setSelection(index + 1, 0, 'user')
} else {
let fn = emitCall('emoticon_event', data.value, () => {})
2024-11-28 08:55:45 +00:00
emit('editor-event', fn)
}
}
2024-11-26 08:51:36 +00:00
const onEditorChange = () => {
let delta = getQuill().getContents()
let text = deltaToString(delta)
if (!isEmptyDelta(delta)) {
editorDraftStore.items[indexName.value || ''] = JSON.stringify({
text: text,
ops: delta.ops,
2024-11-26 08:51:36 +00:00
})
} else {
// 删除 editorDraftStore.items 下的元素
delete editorDraftStore.items[indexName.value || '']
}
2024-11-28 08:55:45 +00:00
onEditorEvent(emitCall('input_event', text))
2024-11-26 08:51:36 +00:00
// emit('editor-event', emitCall('input_event', text))
}
2024-11-28 08:55:45 +00:00
const onClipboardMatcher = (node, Delta) => {
const ops = []
Delta.ops.forEach((op) => {
// 如果粘贴了图片,这里会是一个对象,所以可以这样处理
if (op.insert && typeof op.insert === 'string') {
ops.push({
insert: op.insert, // 文字内容
attributes: {}, //文字样式(包括背景色和文字颜色等)
2024-11-28 08:55:45 +00:00
})
} else {
ops.push(op)
}
})
2024-11-26 08:51:36 +00:00
2024-11-28 08:55:45 +00:00
Delta.ops = ops
return Delta
2024-11-26 08:51:36 +00:00
}
const editorOption = {
debug: false,
modules: {
toolbar: false,
2024-11-28 08:55:45 +00:00
clipboard: {
// 粘贴版,处理粘贴时候的自带样式
matchers: [[Node.ELEMENT_NODE, onClipboardMatcher]],
2024-11-28 08:55:45 +00:00
},
2024-11-26 08:51:36 +00:00
keyboard: {
bindings: {
enter: {
key: 13,
handler: onSendMessageClick,
},
},
2024-11-26 08:51:36 +00:00
},
// imageUploader: {
// upload: onEditorUpload
// },
// mention: {
// allowedChars: /^[\u4e00-\u9fa5]*$/,
// mentionDenotationChars: ['@'],
// positioningStrategy: 'fixed',
// renderItem: (data) => {
// const el = document.createElement('div')
// el.className = 'ed-member-item'
// el.innerHTML = `<img src="${data.avatar}" class="avator"/>`
// el.innerHTML += `<span class="nickname">${data.nickname}</span>`
// return el
// },
// source: function (searchTerm, renderList) {
// if (!props.members.length) {
// return renderList([])
// }
// let list = [
// { id: 0, nickname: '所有人', avatar: defAvatar, value: '所有人' },
// ...props.members
// ]
// const items = list.filter(
// (item) => item.nickname.toLowerCase().indexOf(searchTerm) !== -1
// )
// renderList(items)
// },
// mentionContainerClass: 'ql-mention-list-container me-scrollbar me-scrollbar-thumb'
// }
},
placeholder: '',
}
2024-12-06 08:55:15 +00:00
const handleSelectImg = (data) => {
2024-12-13 05:09:38 +00:00
onSendMessage({ ...data })
2024-12-06 08:55:15 +00:00
}
2024-11-26 08:51:36 +00:00
2024-12-06 08:55:15 +00:00
const virtualListChange = (vList) => {
virtualList.value = vList
}
2024-11-26 08:51:36 +00:00
2024-12-20 08:59:58 +00:00
const onContextMenu = (menuType, item) => {
console.log(menuType, item, 'item')
2024-12-20 08:59:58 +00:00
switch (menuType) {
case 'actionCopy':
actionCopy(item)
break
2024-12-20 08:59:58 +00:00
case 'multipleChoose':
multipleChoose(item)
break
2024-12-20 08:59:58 +00:00
case 'actionCite':
actionCite(item)
break
2024-12-20 08:59:58 +00:00
case 'actionWithdraw':
actionWithdraw(item)
break
2024-12-20 08:59:58 +00:00
case 'actionDelete':
actionDelete(item)
break
2024-12-20 08:59:58 +00:00
default:
break
2024-12-20 08:59:58 +00:00
}
2024-12-19 03:02:47 +00:00
}
const actionCopy = (item) => {
console.log('复制')
2024-12-19 03:02:47 +00:00
let content = ''
switch (item.msg_type) {
case 1:
content = item.extra.content
break
2024-12-19 03:02:47 +00:00
case 3:
content = item.extra.url
break
2024-12-19 03:02:47 +00:00
case 5:
content = item.extra.url
break
2024-12-19 03:02:47 +00:00
default:
break
2024-12-19 03:02:47 +00:00
}
uni.setClipboardData({
data: content,
})
2024-12-19 03:02:47 +00:00
}
const multipleChoose = (item) => {
item.isCheck = true
dialogueStore.setMultiSelect(true)
}
2024-12-20 08:59:58 +00:00
const actionCite = (item) => {
console.log('引用')
2024-12-19 03:02:47 +00:00
}
2024-12-20 08:59:58 +00:00
const actionWithdraw = (item) => {
console.log('撤回')
2024-12-24 08:28:44 +00:00
state.value.onfocusItem = item
state.value.showWin = true
2024-12-24 08:28:44 +00:00
}
const withdrawerConfirm = () => {
dialogueStore.ApiRevokeRecord(state.value.onfocusItem.msg_id)
state.value.onfocusItem = null
state.value.showWin = false
2024-12-19 03:02:47 +00:00
}
2024-12-20 08:59:58 +00:00
const actionDelete = (item) => {
console.log('删除')
2024-12-20 08:59:58 +00:00
item.isCheck = true
handleDelete()
2024-12-19 03:02:47 +00:00
}
const handleMergeForward = () => {
if (selectedMessage.value.length == 0) {
return message.warning('未选择消息')
}
console.log('合并转发')
2024-12-19 03:02:47 +00:00
dialogueStore.setForwardType(2)
dialogueStore.setForwardMessages(selectedMessage.value)
uni.navigateTo({
url: '/pages/chooseChat/index',
success: function (res) {
clearMultiSelect()
},
2024-12-19 03:02:47 +00:00
})
}
const handleSingleForward = () => {
if (selectedMessage.value.length == 0) {
return message.warning('未选择消息')
}
console.log('逐条转发')
2024-12-20 08:59:58 +00:00
dialogueStore.setForwardType(1)
dialogueStore.setForwardMessages(selectedMessage.value)
uni.navigateTo({
url: '/pages/chooseChat/index',
success: function (res) {
clearMultiSelect()
},
2024-12-20 08:59:58 +00:00
})
2024-12-19 03:02:47 +00:00
}
const handleWechatForward = () => {
if (selectedMessage.value.length == 0) {
return message.warning('未选择消息')
}
console.log('微信转发')
2024-12-19 03:02:47 +00:00
}
const handleDelete = () => {
if (selectedMessage.value.length == 0) {
return message.warning('未选择消息')
}
console.log('删除')
2024-12-24 08:28:44 +00:00
showConfirm({
content: '确定删除聊天记录',
confirmText: '删除',
confirmColor: '#CF3050',
2024-12-24 08:28:44 +00:00
onConfirm: async () => {
const msgIds = selectedMessage.value.map((item) => item.msg_id)
virtualList.value = virtualList.value.filter(
(item) => !msgIds.includes(item.msg_id),
)
2024-12-24 08:28:44 +00:00
dialogueStore.ApiDeleteRecord(msgIds)
clearMultiSelect()
},
onCancel: () => {},
2024-12-24 08:28:44 +00:00
})
2024-12-19 03:02:47 +00:00
}
watch(
() => zpagingRef.value,
(newValue, oldValue) => {
if (newValue) {
updateZpagingRef(newValue)
}
},
)
2024-11-22 01:06:37 +00:00
2024-12-19 03:02:47 +00:00
const clearMultiSelect = () => {
dialogueStore.setMultiSelect(false)
virtualList.value.forEach((item) => {
2024-12-19 03:02:47 +00:00
item.isCheck = false
})
}
const initData = async () => {
2024-12-10 03:31:36 +00:00
const dialogueList = getDialogueList(talkParams.index_name)
2024-11-22 01:06:37 +00:00
let objT = {
uid: talkParams.uid,
talk_type: talkParams.type,
receiver_id: talkParams.receiver_id,
2024-12-06 08:55:15 +00:00
index_name: talkParams.index_name,
2024-12-19 03:02:47 +00:00
direction: dialogueList ? 'down' : 'up',
no_limit: dialogueList ? 1 : 0,
2024-11-22 01:06:37 +00:00
}
2024-12-10 03:31:36 +00:00
await onLoad({ ...objT })
2024-12-13 05:09:38 +00:00
zpagingRef.value?.complete(records.value)
2024-12-10 03:31:36 +00:00
}
//点击跳转到聊天设置页面
const toChatSettingsPage = () => {
uni.navigateTo({
url:
'/pages/chatSettings/index?groupId=' +
talkParams?.receiver_id +
'&sessionId=' +
state.sessionId,
})
}
//点击跳转到用户详情页面
const toUserDetailPage = (userItem) => {
uni.navigateTo({
url:
'/pages/dialog/dialogDetail/userDetail?erpUserId=' + userItem.erp_user_id,
})
}
2024-12-19 03:02:47 +00:00
onMounted(async () => {
2024-12-10 03:31:36 +00:00
initData()
2024-11-22 01:06:37 +00:00
})
2024-12-06 08:55:15 +00:00
onUnmounted(() => {
dialogueStore.setDialogue({})
2024-12-19 03:02:47 +00:00
clearMultiSelect()
2024-12-06 08:55:15 +00:00
})
2024-11-22 01:06:37 +00:00
</script>
2024-11-22 09:00:03 +00:00
<style scoped lang="less">
.dialog-page {
2024-11-22 01:06:37 +00:00
flex: 1;
background-image: url('@/static/image/clockIn/z3280@3x.png');
2024-11-22 01:06:37 +00:00
background-size: cover;
background-position: bottom center;
background-attachment: fixed;
width: 100%;
2024-11-22 09:00:03 +00:00
.dialog-list {
padding: 20rpx 32rpx;
}
.toChatSetting_btn {
::v-deep .tmicon-gengduo {
line-height: unset !important;
}
}
2024-11-22 01:06:37 +00:00
}
2024-11-22 09:00:03 +00:00
2024-11-22 01:06:37 +00:00
.searchRoot {
background-color: #fff;
padding: 22rpx 18rpx;
}
2024-11-22 09:00:03 +00:00
2024-11-22 01:06:37 +00:00
.contentRoot {
margin-top: 20rpx;
background-color: #fff;
}
2024-11-22 09:00:03 +00:00
2024-11-22 01:06:37 +00:00
.footBox {
2024-11-26 08:51:36 +00:00
min-height: 162rpx;
2024-11-22 01:06:37 +00:00
background-color: #fff;
}
2024-11-22 09:00:03 +00:00
.load-toolbar {
2024-11-26 08:51:36 +00:00
height: 50rpx;
2024-11-22 09:00:03 +00:00
color: #409eff;
text-align: center;
2024-11-26 08:51:36 +00:00
line-height: 50rpx;
font-size: 24rpx;
2024-11-22 09:00:03 +00:00
.no-more {
color: #b9b3b3;
}
}
.message-item {
&.border {
2024-11-26 08:51:36 +00:00
border-radius: 16rpx;
2024-11-22 09:00:03 +00:00
}
}
.message-box {
width: 100%;
2024-11-26 08:51:36 +00:00
min-height: 30rpx;
margin-bottom: 5rpx;
2024-11-22 09:00:03 +00:00
}
.record-box {
display: flex;
flex-direction: row;
align-items: flex-start;
2024-11-26 08:51:36 +00:00
2024-11-22 09:00:03 +00:00
.checkbox-column {
display: flex;
justify-content: center;
2024-12-19 03:02:47 +00:00
width: 42rpx;
2024-11-22 09:00:03 +00:00
order: 1;
user-select: none;
2024-12-19 03:02:47 +00:00
margin-top: 20rpx;
margin-right: 20rpx;
2024-11-22 09:00:03 +00:00
}
.avatar-column {
width: 80rpx;
display: flex;
align-items: center;
order: 2;
user-select: none;
margin-top: 16rpx;
flex-direction: column;
}
.main-column {
flex: 1 auto;
order: 3;
position: relative;
box-sizing: border-box;
padding: 16rpx 20rpx 14rpx 20rpx;
2024-12-19 03:02:47 +00:00
// overflow: hidden;
2024-11-22 09:00:03 +00:00
min-height: 30px;
.talk-title {
display: flex;
align-items: center;
margin-bottom: 6rpx;
font-size: 24rpx;
user-select: none;
color: #bababa;
2024-11-22 09:00:03 +00:00
opacity: 1;
&.show {
opacity: 1;
}
.nickname {
color: var(--im-text-color);
2024-11-26 08:51:36 +00:00
margin-right: 5rpx;
2024-11-22 09:00:03 +00:00
.at {
display: none;
}
&:hover {
color: var(--im-primary-color);
.at {
display: inline-block;
}
}
}
span {
transform: scale(0.88);
transform-origin: left center;
}
}
.talk-content {
display: flex;
justify-content: flex-start;
align-items: flex-end;
box-sizing: border-box;
width: 100%;
.talk-tools {
display: flex;
2024-11-26 08:51:36 +00:00
margin: 0 16rpx;
2024-11-22 09:00:03 +00:00
color: #a79e9e;
2024-11-26 08:51:36 +00:00
font-size: 24rpx;
2024-11-22 09:00:03 +00:00
user-select: none;
align-items: center;
justify-content: space-around;
.more-tools {
visibility: hidden;
2024-11-26 08:51:36 +00:00
margin-left: 5rpx;
2024-11-22 09:00:03 +00:00
}
}
}
.talk-reply {
display: flex;
align-items: flex-start;
align-items: center;
width: fit-content;
2024-11-26 08:51:36 +00:00
padding: 8rpx;
margin-top: 6rpx;
2024-11-22 09:00:03 +00:00
margin-right: auto;
2024-11-26 08:51:36 +00:00
font-size: 24rpx;
2024-11-22 09:00:03 +00:00
color: #8f8f8f;
word-break: break-all;
background-color: var(--im-message-left-bg-color);
2024-11-26 08:51:36 +00:00
border-radius: 10rpx;
max-width: 450rpx;
2024-11-22 09:00:03 +00:00
overflow: hidden;
user-select: none;
.icon-top {
2024-11-26 08:51:36 +00:00
margin-right: 6rpx;
2024-11-22 09:00:03 +00:00
}
.ellipsis {
display: -webkit-inline-box;
text-overflow: ellipsis;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
}
&:hover {
.talk-title {
opacity: 1;
}
.more-tools {
visibility: visible !important;
}
}
}
&.direction-rt {
.avatar-column {
order: 3;
}
.main-column {
order: 2;
.talk-title {
justify-content: flex-end;
span {
transform-origin: right center;
}
}
.talk-content {
flex-direction: row-reverse;
}
.talk-reply {
margin-right: 0;
margin-left: auto;
}
}
}
&.multi-select {
border-radius: 5px;
&:hover,
&.multi-select-check {
background-color: var(--im-active-bg-color);
}
}
}
2024-11-26 08:51:36 +00:00
.content-placeholder {
height: 58rpx;
}
.quillBox {
:deep(.ql-clipboard) {
2024-11-28 08:55:45 +00:00
position: relative;
opacity: 0;
height: 1rpx;
overflow: auto;
2024-11-26 08:51:36 +00:00
}
:deep(.ql-editor) {
padding: 14rpx 22rpx;
background-color: #f9f9f9;
2024-11-26 08:51:36 +00:00
border-radius: 8rpx;
outline: none !important;
2024-11-28 08:55:45 +00:00
max-height: 294rpx;
overflow: auto;
line-height: 44rpx;
font-size: 32rpx;
2024-12-19 03:02:47 +00:00
p {
2024-11-28 08:55:45 +00:00
display: inline-flex;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
white-space: normal;
word-break: break-all;
2024-12-19 03:02:47 +00:00
.ed-emoji {
2024-11-28 08:55:45 +00:00
width: 44rpx;
height: 44rpx;
display: inline-block;
}
}
2024-11-26 08:51:36 +00:00
}
}
2024-12-19 03:02:47 +00:00
:deep(.wd-action-sheet) {
background-color: #8b8b8b !important;
}
:deep(.wd-action-sheet__panel-title) {
color: #fff !important;
}
2024-12-20 08:59:58 +00:00
.component-content {
position: relative;
z-index: 1;
/* 确保 z-index 低于 deepBubble */
}
2024-12-24 08:28:44 +00:00
.divider {
width: 100%;
height: 1rpx;
background-color: #e7e7e7;
2024-12-24 08:28:44 +00:00
}
2024-11-22 01:06:37 +00:00
</style>