|
@@ -0,0 +1,455 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <van-nav-bar fixed @click-left="onClickLeft" @click-right="onClickRight">
|
|
|
+ <template #left>
|
|
|
+ <div>
|
|
|
+ <van-icon name="arrow-left" color="black" size="18" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #title>
|
|
|
+ <div class="text-2xl text-black-3 text-semibold">
|
|
|
+ <div class="inline-block w-220 line-clamp-1">
|
|
|
+ {{ title }}
|
|
|
+ </div>
|
|
|
+ <!-- <span class="ml-7 iconfont icon-close-remind text-black-9" style="font-size: 16px"></span> -->
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #right>
|
|
|
+ <van-icon name="ellipsis" color="black" size="18" />
|
|
|
+ </template>
|
|
|
+ </van-nav-bar>
|
|
|
+
|
|
|
+ <van-pull-refresh v-model="loading" @refresh="onRefresh">
|
|
|
+ <div class="w-full h-[100vh] pt-55">
|
|
|
+ <div ref="messageBoxRef" class="w-full box-border px-12 overflow-y-auto scrollbar">
|
|
|
+ <van-list
|
|
|
+ v-model:loading="loading"
|
|
|
+ :finished="finished"
|
|
|
+ finished-text="没有更多了"
|
|
|
+ @load="onLoad"
|
|
|
+ >
|
|
|
+ <template v-for="(message, index) in receiveGetter" :key="index">
|
|
|
+ <ChatMessage :message="message"></ChatMessage>
|
|
|
+ </template>
|
|
|
+
|
|
|
+<!-- <template v-for="(item, index) in receiveGetter" :key="index">
|
|
|
+ <!– 右侧是自己的消息 –>
|
|
|
+
|
|
|
+ <template v-if="item.sendUserId == user.pass">
|
|
|
+ <!– 右侧消息 图片 –>
|
|
|
+ <template v-if="item.messageType == 1">
|
|
|
+ <div class="w-full text-center text-black/[0.4] mt-24 text-sm">
|
|
|
+ {{ item.createTime }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex justify-end items-start">
|
|
|
+ <div
|
|
|
+ class="mr-10 text-left inline-block min-h-46 max-w-full break-words overflow-auto bg-[#FEF4E6] box-border text-base p-12 text-wrap"
|
|
|
+ >
|
|
|
+ {{ item.messageContent }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="w-40 h-40 rounded-full overflow-hidden shrink-0">
|
|
|
+ <img
|
|
|
+ v-if="user?.headImageUrl"
|
|
|
+ class="w-full h-full object-cover"
|
|
|
+ :src="user?.headImageUrl"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <img
|
|
|
+ v-else
|
|
|
+ class="w-full h-full object-cover"
|
|
|
+ src="~/assets/img/default_avatar.png"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!– 文字消息 –>
|
|
|
+ <template v-if="item.messageType == 0 && item.messageContent.trim()">
|
|
|
+ <div class="w-full text-center text-black/[0.4] mt-24 text-sm">
|
|
|
+ {{ formatTimestamp(item.createTime) }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex justify-end items-start">
|
|
|
+ <div
|
|
|
+ class="mr-10 text-left inline-block min-h-46 max-w-full break-words overflow-auto rounded-b-2xl rounded-tl-2xl bg-white box-border text-base p-12 text-wrap"
|
|
|
+ >
|
|
|
+ {{ messageContentParse(item.messageContent) }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="w-40 h-40 rounded-full overflow-hidden shrink-0">
|
|
|
+ <img
|
|
|
+ v-if="user?.headImageUrl"
|
|
|
+ class="w-full h-full object-cover"
|
|
|
+ :src="user?.headImageUrl"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <img
|
|
|
+ v-else
|
|
|
+ class="w-full h-full object-cover"
|
|
|
+ src="~/assets/img/default_avatar.png"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!– 左侧他人的消息 –>
|
|
|
+ <template v-else>
|
|
|
+ <!– 左侧消息 图片 –>
|
|
|
+ <template v-if="item.messageType == 1">
|
|
|
+ <div class="w-full text-center text-black/[0.4] mt-24 text-sm">
|
|
|
+ {{ item.createTime }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex justify-end items-start">
|
|
|
+ <div
|
|
|
+ class="mr-10 text-left inline-block min-h-46 max-w-full break-words overflow-auto rounded-[4px] bg-white box-border text-base p-12 text-wrap"
|
|
|
+ >
|
|
|
+ <!– {{ item.messageContent }} –>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="w-40 h-40 rounded-full overflow-hidden shrink-0">
|
|
|
+ <img
|
|
|
+ v-if="user?.headImageUrl"
|
|
|
+ class="w-full h-full object-cover"
|
|
|
+ :src="user?.headImageUrl"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <img
|
|
|
+ v-else
|
|
|
+ class="w-full h-full object-cover"
|
|
|
+ src="~/assets/img/default_avatar.png"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!– 左侧文字 –>
|
|
|
+ <!– <template v-if="item.messageType == 0 && item.messageContent.trim()"> –>
|
|
|
+ <div class="w-full text-center text-black/[0.4] mt-24 text-sm">
|
|
|
+ {{ formatTimestamp(item.createTime) }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex justify-start items-start">
|
|
|
+ <div class="w-40 h-40 rounded-full overflow-hidden shrink-0">
|
|
|
+ <img
|
|
|
+ v-if="user?.headImageUrl"
|
|
|
+ class="w-full h-full object-cover"
|
|
|
+ :src="user?.headImageUrl"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <img
|
|
|
+ v-else
|
|
|
+ class="w-full h-full object-cover"
|
|
|
+ src="~/assets/img/default_avatar.png"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="ml-8 inline-block rounded-b-2xl rounded-tr-2xl min-h-46 max-w-full break-words overflow-auto bg-[#F3F3F3] box-border text-base p-12 text-wrap"
|
|
|
+ >
|
|
|
+ {{ messageContentParse(item.messageContent) }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!– </template> –>
|
|
|
+ </template>
|
|
|
+ </template>-->
|
|
|
+
|
|
|
+ <div ref="msgBottomRef"></div>
|
|
|
+ </van-list>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </van-pull-refresh>
|
|
|
+
|
|
|
+ <ProfileNewsChatInput
|
|
|
+ :shareGroup="false"
|
|
|
+ @on-select-Img="selectImg"
|
|
|
+ @on-send-message="sendMessage"
|
|
|
+ ></ProfileNewsChatInput>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<script setup>
|
|
|
+import ChatMessage from './chat-message'
|
|
|
+import { messageContentParse, formatTimestamp } from '~/utils/detalTime'
|
|
|
+import {findHyperlinks} from "~/pages/chat/chat-message/link-message/handle";
|
|
|
+const route = useRoute()
|
|
|
+const router = useRouter()
|
|
|
+
|
|
|
+const uploadUrl = `${import.meta.env.VITE_APP_BASE_URL}/website/tourMessage/upload`
|
|
|
+const chatStore = useChatStore()
|
|
|
+const { ws, curConversiton, receive, receiveGetter, connectSta, onNewMessage } =
|
|
|
+ storeToRefs(chatStore)
|
|
|
+const user = computed(() => chatStore.user)
|
|
|
+
|
|
|
+// 消息接收者的用户id
|
|
|
+const getUserId = computed(() => curConversiton.value.toUserId)
|
|
|
+
|
|
|
+// 消息发送者:当前登录用户的加密id
|
|
|
+const sendUserId = computed(() => user.value.pass)
|
|
|
+
|
|
|
+// 会话id
|
|
|
+const groupId = computed(() => route.query.groupId)
|
|
|
+
|
|
|
+// 用户在群聊中艾特的人
|
|
|
+const specialUserId = ref('')
|
|
|
+
|
|
|
+// 用户输入的文本消息
|
|
|
+const messageContent = ref('')
|
|
|
+
|
|
|
+// 聊天类型 1单聊 2群聊 3系统消息 4关注信息
|
|
|
+const noticeType = computed(() => curConversiton.value.noticeType)
|
|
|
+
|
|
|
+const messageBoxRef = ref(null)
|
|
|
+const msgBottomRef = ref(null)
|
|
|
+const inputBoxRef = ref(null)
|
|
|
+
|
|
|
+// 当前websocket连接状态 0: 未连接 1: 连接中 2: 已连接 3: 已断开
|
|
|
+const wsConnect = computed(() => connectSta.value)
|
|
|
+
|
|
|
+const followStatus = ref(0)
|
|
|
+
|
|
|
+const querySwParams = reactive({
|
|
|
+ getUserId: computed(() => route.query.getUserId ?? ''),
|
|
|
+ groupId: computed(() => route.query.groupId ?? ''),
|
|
|
+ sendUserId: computed(() => route.query.sendUserId ?? '')
|
|
|
+})
|
|
|
+
|
|
|
+// 本地生成一个唯一消息id
|
|
|
+function getLocalId() {
|
|
|
+ const random = Math.floor(Math.random() * 10000)
|
|
|
+ return Date.now() + '' + random
|
|
|
+}
|
|
|
+
|
|
|
+// 刷新次数
|
|
|
+const count = ref(0)
|
|
|
+const loading = ref(false)
|
|
|
+const finished = ref(false)
|
|
|
+const messageList = ref([])
|
|
|
+
|
|
|
+// 单聊的标题
|
|
|
+const title = computed(() => route.query.groupRemark)
|
|
|
+
|
|
|
+// 刷新
|
|
|
+const onRefresh = () => {}
|
|
|
+
|
|
|
+const onClickLeft = () => router.back()
|
|
|
+
|
|
|
+const onClickRight = () => {
|
|
|
+ navigateTo({
|
|
|
+ path: '/chat/set-single',
|
|
|
+ query: {
|
|
|
+ toUserId: route.query.getUserId
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 发送消息的方法
|
|
|
+// 发送文本消息
|
|
|
+function sendMessage(messageParams) {
|
|
|
+ console.log(messageParams, 'messageParams')
|
|
|
+ console.log(getUserId.value, '5555')
|
|
|
+ console.log(noticeType, '5555')
|
|
|
+ if (!messageParams.trim()) return
|
|
|
+ let msg = {
|
|
|
+ // getUserId: getUserId.value,
|
|
|
+ // sendUserId: sendUserId.value,
|
|
|
+ // groupId: groupId.value,
|
|
|
+ ...querySwParams,
|
|
|
+ specialUserId: specialUserId.value,
|
|
|
+ messageContent: messageParams,
|
|
|
+ messageType: 0,
|
|
|
+ noticeType: 1,
|
|
|
+ object: {
|
|
|
+ id: getLocalId()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const isLink = !!findHyperlinks(messageParams)
|
|
|
+ if(isLink) {
|
|
|
+ msg.messageType = 4
|
|
|
+ }
|
|
|
+
|
|
|
+ receive.value.push({
|
|
|
+ ...msg,
|
|
|
+ messageContent: JSON.stringify({ messageContent: msg.messageContent })
|
|
|
+ })
|
|
|
+ messageContent.value = ''
|
|
|
+ messageBoxRef.value.scrollTop = messageBoxRef.value.scrollHeight
|
|
|
+
|
|
|
+ msg = JSON.stringify(msg)
|
|
|
+ ws.value.send(msg)
|
|
|
+}
|
|
|
+
|
|
|
+const pageSize = ref(10)
|
|
|
+
|
|
|
+const messageCount = ref(0)
|
|
|
+
|
|
|
+// 获取聊天记录
|
|
|
+async function getChatHistory(messageId = '') {
|
|
|
+ if (curConversiton.value.isLocal) return
|
|
|
+
|
|
|
+ if (!groupId.value) return
|
|
|
+
|
|
|
+ if (receive.value.length >= messageCount.value && receive.value.length > 0) return
|
|
|
+
|
|
|
+ if (receive.value.length && !messageId) return
|
|
|
+
|
|
|
+ const query = {
|
|
|
+ pageSize: pageSize.value,
|
|
|
+ groupId: groupId.value,
|
|
|
+ messageId
|
|
|
+ }
|
|
|
+
|
|
|
+ const {
|
|
|
+ data: { data = [], count = [] }
|
|
|
+ } = await request('/website/tourMessage/getMessageByGroupId', { query })
|
|
|
+
|
|
|
+ messageCount.value = count || 0
|
|
|
+
|
|
|
+ if (Array.isArray(data)) {
|
|
|
+ receive.value = [...data, ...receive.value]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取到数据后,滚动到底部
|
|
|
+ if (!messageId) {
|
|
|
+ nextTick(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ messageBoxRef.value && messageBoxRef.value.scrollTo({ top: msgBottomRef.value.offsetTop })
|
|
|
+ }, 100)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ console.log('历史记录:', data)
|
|
|
+}
|
|
|
+
|
|
|
+// 监听输入框的enter事件
|
|
|
+function addEventListenerTextarea() {
|
|
|
+ nextTick(() => {
|
|
|
+ if (inputBoxRef.value) {
|
|
|
+ inputBoxRef.value.removeEventListener('keydown', () => {})
|
|
|
+ inputBoxRef.value.addEventListener('keydown', function (event) {
|
|
|
+ if (event.key === 'Enter') {
|
|
|
+ event.preventDefault()
|
|
|
+ sendMessage()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 监听消息列表的滚动事件
|
|
|
+function addEventListenerMessage() {
|
|
|
+ nextTick(() => {
|
|
|
+ if (messageBoxRef.value) {
|
|
|
+ messageBoxRef.value.removeEventListener('scroll', () => {})
|
|
|
+
|
|
|
+ messageBoxRef.value.addEventListener('scroll', (e) => {
|
|
|
+ if (messageBoxRef.value.scrollTop == 0 && receive.value[0]?.id) {
|
|
|
+ console.log('滚动到顶部了')
|
|
|
+ console.log(receive.value[0].id)
|
|
|
+ getChatHistory(receive.value[0].id)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 选择发送图片
|
|
|
+function selectImg(e) {
|
|
|
+ const file = e.target.files[0]
|
|
|
+
|
|
|
+ const { size, type, name } = file
|
|
|
+ const IMIETypes = ['image/jpeg', 'image/png', 'image/gif']
|
|
|
+
|
|
|
+ if (!IMIETypes.includes(type)) {
|
|
|
+ showToast('请上传图片')
|
|
|
+
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const maxSize = 1024 * 1024 * 20
|
|
|
+
|
|
|
+ if (size > maxSize) {
|
|
|
+ showFailToast('图片大小不能超过10M')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const formData = new FormData()
|
|
|
+
|
|
|
+ formData.append('uploadFile', file)
|
|
|
+ formData.append('fieldName', 'messageContent')
|
|
|
+ formData.append('asImage', true)
|
|
|
+
|
|
|
+ request(uploadUrl, { method: 'post', body: formData }).then((res) => {
|
|
|
+ const {
|
|
|
+ data: { fileUrl }
|
|
|
+ } = res
|
|
|
+ if (fileUrl) {
|
|
|
+ const msg = {
|
|
|
+ getUserId: getUserId.value,
|
|
|
+ sendUserId: sendUserId.value,
|
|
|
+ specialUserId: '',
|
|
|
+ groupId: groupId.value,
|
|
|
+ messageContent: fileUrl,
|
|
|
+ messageType: 1,
|
|
|
+ noticeType: noticeType.value,
|
|
|
+ object: {
|
|
|
+ id: getLocalId()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ws.value.send(JSON.stringify(msg))
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 获取我与对方的关注情况
|
|
|
+async function isFollow() {
|
|
|
+ if (noticeType.value !== 1) return //只有单聊中才需要获取关注情况
|
|
|
+
|
|
|
+ const query = {
|
|
|
+ userId: curConversiton.value.toUserId
|
|
|
+ }
|
|
|
+ const { data: status = 0 } = await request('/website/tourGroup/isFollow', { query })
|
|
|
+ followStatus.value = status
|
|
|
+ console.log('关注情况:', status)
|
|
|
+}
|
|
|
+
|
|
|
+watch(groupId, async () => {
|
|
|
+ // 消息置空
|
|
|
+ receive.value = []
|
|
|
+
|
|
|
+ // 监听消息输入框键盘enter事件
|
|
|
+ addEventListenerTextarea()
|
|
|
+
|
|
|
+ // 监听聊天框消息滚动事件
|
|
|
+ addEventListenerMessage()
|
|
|
+
|
|
|
+ // 获取与当前会话用户的关注状态
|
|
|
+ isFollow()
|
|
|
+
|
|
|
+ // 获取前会话用户的聊天记录
|
|
|
+ getChatHistory()
|
|
|
+})
|
|
|
+
|
|
|
+watch(onNewMessage, getUserId, sendUserId, () => {})
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ // 获取前会话用户的聊天记录
|
|
|
+ getChatHistory()
|
|
|
+
|
|
|
+ addEventListenerTextarea()
|
|
|
+ addEventListenerMessage()
|
|
|
+
|
|
|
+ // 获取与当前会话用户的关注状态
|
|
|
+ isFollow()
|
|
|
+})
|
|
|
+
|
|
|
+definePageMeta({
|
|
|
+ layout: false
|
|
|
+})
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped></style>
|