2025-12-22 19:16:53 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<!-- #ifdef APP -->
|
|
|
|
|
|
<scroll-view style="flex:1">
|
|
|
|
|
|
<!-- #endif -->
|
|
|
|
|
|
<view class="page">
|
|
|
|
|
|
<!-- 自定义导航栏 -->
|
|
|
|
|
|
<view class="nav" :style="{ paddingTop: headerPaddingTop + 'px', height: headerTotalHeight + 'px' }">
|
|
|
|
|
|
<view class="nav-back" @tap="goBack">
|
2025-12-23 13:27:36 +08:00
|
|
|
|
<view class="nav-back-icon"></view>
|
2025-12-22 19:16:53 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<text class="nav-title">{{ roomName }}</text>
|
2025-12-23 18:57:41 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<!-- 聊天室人员和操作 -->
|
|
|
|
|
|
<view class="room-toolbar" :style="{ top: headerPaddingTop + 44 + 'px' }">
|
|
|
|
|
|
<view class="member-count" @tap="showMembers = !showMembers">
|
|
|
|
|
|
<text class="member-count-text">{{ totalMembers.length > 0 ? totalMembers.length + ' 人在线' : '暂无人员' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="toolbar-right">
|
|
|
|
|
|
<button class="toolbar-btn" @tap="handleWorkcaseAction">
|
|
|
|
|
|
<text class="toolbar-btn-text">{{ workcaseId ? '查看工单' : '创建工单' }}</text>
|
2025-12-23 17:16:28 +08:00
|
|
|
|
</button>
|
2025-12-23 18:57:41 +08:00
|
|
|
|
<button class="toolbar-btn meeting-btn" @tap="startMeeting">
|
|
|
|
|
|
<text class="toolbar-btn-text meeting-text">发起会议</text>
|
2025-12-23 17:16:28 +08:00
|
|
|
|
</button>
|
2025-12-22 19:16:53 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2025-12-23 18:57:41 +08:00
|
|
|
|
<!-- 弹窗显示人员列表和在线情况 -->
|
|
|
|
|
|
<view v-if="showMembers" class="members-popup-mask" @tap="showMembers = false">
|
|
|
|
|
|
<view class="members-popup" :style="{ top: headerPaddingTop + 88 + 'px' }" @tap.stop>
|
|
|
|
|
|
<view class="members-list">
|
|
|
|
|
|
<view v-if="totalMembers.length === 0" class="members-empty">
|
|
|
|
|
|
<text class="members-empty-text">暂无人员在线</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else class="member-item" v-for="member in totalMembers" :key="member.oderId">
|
|
|
|
|
|
<view class="member-avatar">
|
|
|
|
|
|
<text class="member-avatar-text">{{ member.userName?.charAt(0) || '客' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="member-name">{{ member.userName || '未知' }}</text>
|
|
|
|
|
|
<view class="member-status" :class="member.isOnline ? 'online' : 'offline'"></view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2025-12-22 19:16:53 +08:00
|
|
|
|
<!-- 聊天消息区域 -->
|
|
|
|
|
|
<scroll-view class="chat-area" scroll-y="true" :scroll-top="scrollTop"
|
2025-12-23 18:57:41 +08:00
|
|
|
|
:style="{ top: (headerPaddingTop + 88) + 'px' }"
|
|
|
|
|
|
@scrolltoupper="loadMoreMessages"
|
|
|
|
|
|
upper-threshold="50">
|
|
|
|
|
|
<!-- 加载更多提示 -->
|
|
|
|
|
|
<view v-if="loadingMore" class="loading-more">
|
|
|
|
|
|
<text class="loading-more-text">加载中...</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="!hasMore" class="loading-more">
|
|
|
|
|
|
<text class="loading-more-text">没有更多消息了</text>
|
|
|
|
|
|
</view>
|
2025-12-22 19:16:53 +08:00
|
|
|
|
<view class="message-list">
|
2025-12-23 18:57:41 +08:00
|
|
|
|
<view class="message-item" v-for="msg in messages" :key="msg.messageId"
|
2025-12-22 19:16:53 +08:00
|
|
|
|
:class="msg.senderType === 'guest' ? 'self' : 'other'">
|
|
|
|
|
|
<!-- 对方消息(左侧) -->
|
|
|
|
|
|
<view class="message-row other-row" v-if="msg.senderType !== 'guest'">
|
2025-12-23 17:16:28 +08:00
|
|
|
|
<view>
|
|
|
|
|
|
<view class="avatar">
|
|
|
|
|
|
<text class="avatar-text">{{ msg.senderName?.charAt(0) || '客' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="sender-name">{{ msg.senderName || '客服' }}</text>
|
2025-12-22 19:16:53 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view class="message-content">
|
|
|
|
|
|
<view class="bubble other-bubble">
|
|
|
|
|
|
<text class="message-text">{{ msg.content }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="message-time">{{ formatTime(msg.sendTime) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<!-- 自己消息(右侧) -->
|
|
|
|
|
|
<view class="message-row self-row" v-else>
|
|
|
|
|
|
<view class="message-content">
|
|
|
|
|
|
<view class="bubble self-bubble">
|
|
|
|
|
|
<text class="message-text">{{ msg.content }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="message-time">{{ formatTime(msg.sendTime) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="avatar self-avatar">
|
|
|
|
|
|
<text class="avatar-text">我</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 底部输入区 -->
|
|
|
|
|
|
<view class="footer">
|
|
|
|
|
|
<view class="input-row">
|
|
|
|
|
|
<input class="chat-input" v-model="inputText" placeholder="输入消息..."
|
|
|
|
|
|
@confirm="sendMessage" />
|
|
|
|
|
|
<view class="send-btn" @tap="sendMessage">
|
|
|
|
|
|
<text class="send-text">发送</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 工单创建弹窗 -->
|
|
|
|
|
|
<WorkcaseCreator v-if="showWorkcaseCreator" :show="showWorkcaseCreator"
|
|
|
|
|
|
@close="hideCreator" @success="onWorkcaseCreated" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<!-- #ifdef APP -->
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
<!-- #endif -->
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-12-23 18:57:41 +08:00
|
|
|
|
import { ref, reactive, computed, nextTick, onMounted } from 'vue'
|
2025-12-22 19:16:53 +08:00
|
|
|
|
import WorkcaseCreator from '@/components/WorkcaseCreator/WorkcaseCreator.uvue'
|
2025-12-23 18:57:41 +08:00
|
|
|
|
import type { ChatRoomMessageVO, CustomerVO, ChatMemberVO, TbChatRoomMessageDTO } from '@/types/workcase'
|
|
|
|
|
|
import { workcaseChatAPI } from '@/api/workcase'
|
2025-12-22 19:16:53 +08:00
|
|
|
|
|
|
|
|
|
|
// 响应式数据
|
|
|
|
|
|
const headerPaddingTop = ref<number>(44)
|
|
|
|
|
|
const headerTotalHeight = ref<number>(88)
|
|
|
|
|
|
const roomId = ref<string>('')
|
|
|
|
|
|
const workcaseId = ref<string>('')
|
|
|
|
|
|
const roomName = ref<string>('聊天室')
|
|
|
|
|
|
const inputText = ref<string>('')
|
|
|
|
|
|
const scrollTop = ref<number>(0)
|
|
|
|
|
|
const showWorkcaseCreator = ref<boolean>(false)
|
2025-12-23 18:57:41 +08:00
|
|
|
|
const loading = ref<boolean>(false)
|
|
|
|
|
|
const sending = ref<boolean>(false)
|
|
|
|
|
|
const loadingMore = ref<boolean>(false)
|
|
|
|
|
|
const currentPage = ref<number>(1)
|
|
|
|
|
|
const hasMore = ref<boolean>(true)
|
|
|
|
|
|
|
|
|
|
|
|
// 用户信息(从storage获取)
|
|
|
|
|
|
const currentUserId = ref<string>('')
|
|
|
|
|
|
const currentUserName = ref<string>('我')
|
|
|
|
|
|
|
|
|
|
|
|
function loadUserInfo() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const userInfo = uni.getStorageSync('userInfo')
|
|
|
|
|
|
if (userInfo) {
|
|
|
|
|
|
const user = typeof userInfo === 'string' ? JSON.parse(userInfo) : userInfo
|
|
|
|
|
|
currentUserId.value = user.userId || user.id || ''
|
|
|
|
|
|
currentUserName.value = user.username || user.nickName || '我'
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('获取用户信息失败:', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-22 19:16:53 +08:00
|
|
|
|
|
|
|
|
|
|
// 消息列表
|
2025-12-23 18:57:41 +08:00
|
|
|
|
const messages = reactive<ChatRoomMessageVO[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
// 所有默认客服
|
|
|
|
|
|
const defaultWorkers = reactive<CustomerVO[]>([])
|
|
|
|
|
|
async function loadDefaultWorkers() {
|
|
|
|
|
|
const res = await workcaseChatAPI.getAvailableCustomerServices()
|
|
|
|
|
|
if(res.success && res.dataList) {
|
|
|
|
|
|
defaultWorkers.splice(0, defaultWorkers.length, ...res.dataList)
|
2025-12-22 19:16:53 +08:00
|
|
|
|
}
|
2025-12-23 18:57:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 查询进入聊天室的人员, 包含了访客
|
|
|
|
|
|
const chatMembers = reactive<ChatMemberVO[]>([])
|
|
|
|
|
|
async function loadChatMembers() {
|
|
|
|
|
|
const res = await workcaseChatAPI.getChatRoomMemberList(roomId.value)
|
|
|
|
|
|
if(res.success && res.dataList) {
|
|
|
|
|
|
chatMembers.splice(0, chatMembers.length, ...res.dataList)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
const showMembers = ref(false)
|
|
|
|
|
|
|
|
|
|
|
|
// 计算聊天室人数: 默认客服转成ChatMemberVO + 进入聊天室的人员,去重,进入聊天室的人员是在线状态
|
|
|
|
|
|
interface MemberDisplay {
|
|
|
|
|
|
oderId: string
|
|
|
|
|
|
userId: string
|
|
|
|
|
|
userName: string
|
|
|
|
|
|
isOnline: boolean
|
|
|
|
|
|
}
|
|
|
|
|
|
const totalMembers = computed<MemberDisplay[]>(() => {
|
|
|
|
|
|
const memberMap = new Map<string, MemberDisplay>()
|
|
|
|
|
|
|
|
|
|
|
|
// 先添加默认客服(离线状态)
|
|
|
|
|
|
defaultWorkers.forEach((worker, index) => {
|
|
|
|
|
|
memberMap.set(worker.userId || '', {
|
|
|
|
|
|
oderId: `worker-${index}`,
|
|
|
|
|
|
userId: worker.userId || '',
|
|
|
|
|
|
userName: worker.username || '客服',
|
|
|
|
|
|
isOnline: false
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 再添加聊天室成员(在线状态),覆盖同一用户
|
|
|
|
|
|
chatMembers.forEach((member, index) => {
|
|
|
|
|
|
memberMap.set(member.userId || '', {
|
|
|
|
|
|
oderId: member.memberId || `member-${index}`,
|
|
|
|
|
|
userId: member.userId || '',
|
|
|
|
|
|
userName: member.userName || '未知',
|
|
|
|
|
|
isOnline: true
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return Array.from(memberMap.values())
|
|
|
|
|
|
})
|
2025-12-22 19:16:53 +08:00
|
|
|
|
|
|
|
|
|
|
// 生命周期
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
uni.getSystemInfo({
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
// #ifdef MP-WEIXIN
|
|
|
|
|
|
try {
|
|
|
|
|
|
const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
|
|
|
|
|
|
headerPaddingTop.value = menuButtonInfo.top
|
|
|
|
|
|
headerTotalHeight.value = menuButtonInfo.bottom + 8
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
headerPaddingTop.value = res.statusBarHeight || 44
|
|
|
|
|
|
headerTotalHeight.value = (res.statusBarHeight || 44) + 44
|
|
|
|
|
|
}
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
// #ifndef MP-WEIXIN
|
|
|
|
|
|
headerPaddingTop.value = res.statusBarHeight || 44
|
|
|
|
|
|
headerTotalHeight.value = (res.statusBarHeight || 44) + 44
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 获取页面参数
|
|
|
|
|
|
const pages = getCurrentPages()
|
|
|
|
|
|
const currentPage = pages[pages.length - 1] as any
|
|
|
|
|
|
if (currentPage && currentPage.options) {
|
|
|
|
|
|
roomId.value = currentPage.options.roomId || ''
|
|
|
|
|
|
workcaseId.value = currentPage.options.workcaseId || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-23 18:57:41 +08:00
|
|
|
|
loadUserInfo()
|
2025-12-22 19:16:53 +08:00
|
|
|
|
loadChatRoom()
|
2025-12-23 18:57:41 +08:00
|
|
|
|
loadDefaultWorkers()
|
|
|
|
|
|
loadChatMembers()
|
2025-12-22 19:16:53 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 加载聊天室
|
2025-12-23 18:57:41 +08:00
|
|
|
|
const PAGE_SIZE = 5
|
|
|
|
|
|
const messageTotal = ref<number>(0)
|
|
|
|
|
|
|
|
|
|
|
|
async function loadChatRoom() {
|
|
|
|
|
|
if (!roomId.value) return
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取聊天室信息
|
|
|
|
|
|
const roomRes = await workcaseChatAPI.getChatRoomById(roomId.value)
|
|
|
|
|
|
if (roomRes.success && roomRes.data) {
|
|
|
|
|
|
roomName.value = roomRes.data.roomName || '聊天室'
|
|
|
|
|
|
workcaseId.value = roomRes.data.workcaseId || ''
|
|
|
|
|
|
messageTotal.value = roomRes.data.messageCount || 0
|
|
|
|
|
|
}
|
|
|
|
|
|
// 后端是降序查询,page1是最新消息
|
|
|
|
|
|
currentPage.value = 1
|
|
|
|
|
|
hasMore.value = true // 默认有更多,loadMessages中会根据实际情况更新
|
|
|
|
|
|
// 获取消息列表
|
|
|
|
|
|
await loadMessages()
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('加载聊天室失败:', e)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载消息列表(后端降序,page1是最新消息,需要反转显示)
|
|
|
|
|
|
async function loadMessages() {
|
|
|
|
|
|
console.log('[loadMessages] 开始加载, currentPage:', currentPage.value, 'roomId:', roomId.value)
|
|
|
|
|
|
if (!roomId.value) return
|
|
|
|
|
|
try {
|
|
|
|
|
|
const msgRes = await workcaseChatAPI.getChatMessagePage({
|
|
|
|
|
|
filter: { roomId: roomId.value },
|
|
|
|
|
|
pageParam: { page: currentPage.value, pageSize: PAGE_SIZE }
|
|
|
|
|
|
})
|
|
|
|
|
|
console.log('[loadMessages] 响应:', msgRes)
|
|
|
|
|
|
if (msgRes.success && msgRes.dataList) {
|
|
|
|
|
|
const pageInfo = msgRes.pageDomain?.pageParam
|
|
|
|
|
|
const actualTotalPages = pageInfo?.totalPages || 1
|
|
|
|
|
|
hasMore.value = actualTotalPages > currentPage.value
|
|
|
|
|
|
console.log('[loadMessages] pageInfo:', pageInfo, 'actualTotalPages:', actualTotalPages, 'hasMore:', hasMore.value)
|
|
|
|
|
|
|
|
|
|
|
|
// 后端降序返回,需要反转后显示(早的在上,新的在下)
|
|
|
|
|
|
const reversedList = [...msgRes.dataList].reverse()
|
|
|
|
|
|
messages.splice(0, messages.length, ...reversedList)
|
|
|
|
|
|
console.log('[loadMessages] 加载完成, 消息数:', messages.length)
|
|
|
|
|
|
nextTick(() => scrollToBottom())
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('加载消息列表失败:', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载更多历史消息(滚动到顶部触发,加载下一页更早的消息)
|
|
|
|
|
|
async function loadMoreMessages() {
|
|
|
|
|
|
console.log('[loadMoreMessages] 触发, roomId:', roomId.value, 'loadingMore:', loadingMore.value, 'hasMore:', hasMore.value, 'currentPage:', currentPage.value)
|
|
|
|
|
|
if (!roomId.value || loadingMore.value || !hasMore.value) {
|
|
|
|
|
|
console.log('[loadMoreMessages] 跳过加载 - roomId:', !roomId.value, 'loadingMore:', loadingMore.value, '!hasMore:', !hasMore.value)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载下一页(更早的消息)
|
|
|
|
|
|
const nextPage = currentPage.value + 1
|
|
|
|
|
|
console.log('[loadMoreMessages] 准备加载页:', nextPage)
|
|
|
|
|
|
|
|
|
|
|
|
loadingMore.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const msgRes = await workcaseChatAPI.getChatMessagePage({
|
|
|
|
|
|
filter: { roomId: roomId.value },
|
|
|
|
|
|
pageParam: { page: nextPage, pageSize: PAGE_SIZE }
|
|
|
|
|
|
})
|
|
|
|
|
|
console.log('[loadMoreMessages] 响应:', msgRes)
|
|
|
|
|
|
if (msgRes.success && msgRes.dataList && msgRes.dataList.length > 0) {
|
|
|
|
|
|
const pageInfo = msgRes.pageDomain?.pageParam
|
|
|
|
|
|
const actualTotalPages = pageInfo?.totalPages || 1
|
|
|
|
|
|
console.log('[loadMoreMessages] pageInfo:', pageInfo, 'actualTotalPages:', actualTotalPages)
|
|
|
|
|
|
|
|
|
|
|
|
currentPage.value = nextPage
|
|
|
|
|
|
hasMore.value = actualTotalPages > currentPage.value
|
|
|
|
|
|
console.log('[loadMoreMessages] 更新后 currentPage:', currentPage.value, 'hasMore:', hasMore.value)
|
|
|
|
|
|
|
|
|
|
|
|
// 后端降序返回,反转后插入到列表前面
|
|
|
|
|
|
const reversedList = [...msgRes.dataList].reverse()
|
|
|
|
|
|
messages.unshift(...reversedList)
|
|
|
|
|
|
console.log('[loadMoreMessages] 加载完成, 消息数:', messages.length)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log('[loadMoreMessages] 没有更多数据')
|
|
|
|
|
|
hasMore.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('加载更多消息失败:', e)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loadingMore.value = false
|
|
|
|
|
|
}
|
2025-12-22 19:16:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-23 17:16:28 +08:00
|
|
|
|
// 格式化时间(兼容 iOS)
|
2025-12-22 19:16:53 +08:00
|
|
|
|
function formatTime(time?: string): string {
|
|
|
|
|
|
if (!time) return ''
|
2025-12-23 17:16:28 +08:00
|
|
|
|
// iOS 不支持 "yyyy-MM-dd HH:mm:ss" 格式,需要转换为 "yyyy-MM-ddTHH:mm:ss" 或 "yyyy/MM/dd HH:mm:ss"
|
|
|
|
|
|
const iosCompatibleTime = time.replace(' ', 'T')
|
|
|
|
|
|
const date = new Date(iosCompatibleTime)
|
|
|
|
|
|
if (isNaN(date.getTime())) return ''
|
2025-12-22 19:16:53 +08:00
|
|
|
|
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送消息
|
2025-12-23 18:57:41 +08:00
|
|
|
|
async function sendMessage() {
|
2025-12-22 19:16:53 +08:00
|
|
|
|
const text = inputText.value.trim()
|
2025-12-23 18:57:41 +08:00
|
|
|
|
if (!text || sending.value) return
|
2025-12-22 19:16:53 +08:00
|
|
|
|
|
2025-12-23 18:57:41 +08:00
|
|
|
|
sending.value = true
|
|
|
|
|
|
const tempId = Date.now().toString()
|
|
|
|
|
|
|
|
|
|
|
|
// 先添加临时消息到界面
|
|
|
|
|
|
const tempMsg: ChatRoomMessageVO = {
|
|
|
|
|
|
messageId: tempId,
|
2025-12-22 19:16:53 +08:00
|
|
|
|
roomId: roomId.value,
|
2025-12-23 18:57:41 +08:00
|
|
|
|
senderId: currentUserId.value,
|
2025-12-22 19:16:53 +08:00
|
|
|
|
senderType: 'guest',
|
2025-12-23 18:57:41 +08:00
|
|
|
|
senderName: currentUserName.value,
|
2025-12-22 19:16:53 +08:00
|
|
|
|
content: text,
|
2025-12-23 18:57:41 +08:00
|
|
|
|
sendTime: new Date().toISOString(),
|
|
|
|
|
|
status: 'sending'
|
2025-12-22 19:16:53 +08:00
|
|
|
|
}
|
2025-12-23 18:57:41 +08:00
|
|
|
|
messages.push(tempMsg)
|
2025-12-22 19:16:53 +08:00
|
|
|
|
inputText.value = ''
|
2025-12-23 18:57:41 +08:00
|
|
|
|
nextTick(() => scrollToBottom())
|
2025-12-22 19:16:53 +08:00
|
|
|
|
|
2025-12-23 18:57:41 +08:00
|
|
|
|
try {
|
|
|
|
|
|
// 调用API发送消息
|
|
|
|
|
|
const msgDTO: TbChatRoomMessageDTO = {
|
|
|
|
|
|
roomId: roomId.value,
|
|
|
|
|
|
senderId: currentUserId.value,
|
|
|
|
|
|
senderType: 'guest',
|
|
|
|
|
|
senderName: currentUserName.value,
|
|
|
|
|
|
messageType: 'text',
|
|
|
|
|
|
content: text
|
|
|
|
|
|
}
|
|
|
|
|
|
const res = await workcaseChatAPI.sendMessage(msgDTO)
|
|
|
|
|
|
if (res.success && res.data) {
|
|
|
|
|
|
// 更新临时消息为真实消息
|
|
|
|
|
|
const idx = messages.findIndex(m => m.messageId === tempId)
|
|
|
|
|
|
if (idx !== -1) {
|
|
|
|
|
|
messages[idx] = { ...res.data, status: 'sent' }
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 发送失败,标记状态
|
|
|
|
|
|
const idx = messages.findIndex(m => m.messageId === tempId)
|
|
|
|
|
|
if (idx !== -1) {
|
|
|
|
|
|
messages[idx].status = 'failed'
|
|
|
|
|
|
}
|
|
|
|
|
|
uni.showToast({ title: res.message || '发送失败', icon: 'none' })
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('发送消息失败:', e)
|
|
|
|
|
|
const idx = messages.findIndex(m => m.messageId === tempId)
|
|
|
|
|
|
if (idx !== -1) {
|
|
|
|
|
|
messages[idx].status = 'failed'
|
|
|
|
|
|
}
|
|
|
|
|
|
uni.showToast({ title: '发送失败', icon: 'none' })
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
sending.value = false
|
|
|
|
|
|
}
|
2025-12-22 19:16:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 滚动到底部
|
|
|
|
|
|
function scrollToBottom() {
|
|
|
|
|
|
scrollTop.value = 999999
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理工单操作
|
|
|
|
|
|
function handleWorkcaseAction() {
|
|
|
|
|
|
if (workcaseId.value) {
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/workcase/workcaseDetail/workcaseDetail?workcaseId=${workcaseId.value}`
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showWorkcaseCreator.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 隐藏工单创建器
|
|
|
|
|
|
function hideCreator() {
|
|
|
|
|
|
showWorkcaseCreator.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 工单创建成功
|
|
|
|
|
|
function onWorkcaseCreated(data: any) {
|
|
|
|
|
|
hideCreator()
|
|
|
|
|
|
workcaseId.value = data.workcaseId || 'new-workcase'
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '工单创建成功',
|
|
|
|
|
|
icon: 'success'
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发起会议
|
|
|
|
|
|
function startMeeting() {
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/meeting/Meeting/Meeting?roomId=${roomId.value}&workcaseId=${workcaseId.value}`
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 返回上一页
|
|
|
|
|
|
function goBack() {
|
|
|
|
|
|
uni.navigateBack()
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
@import "./chatRoom.scss";
|
|
|
|
|
|
</style>
|