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 17:16:28 +08:00
|
|
|
|
<view class="nav-right">
|
|
|
|
|
|
<button class="nav-btn" @tap="handleWorkcaseAction">
|
|
|
|
|
|
<text class="nav-btn-text">{{ workcaseId ? '查看工单' : '创建工单' }}</text>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button class="nav-btn meeting-btn" @tap="startMeeting">
|
|
|
|
|
|
<text class="nav-btn-text meeting-text">发起会议</text>
|
|
|
|
|
|
</button>
|
2025-12-22 19:16:53 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 聊天消息区域 -->
|
|
|
|
|
|
<scroll-view class="chat-area" scroll-y="true" :scroll-top="scrollTop"
|
|
|
|
|
|
:style="{ marginTop: headerTotalHeight + 'px' }">
|
|
|
|
|
|
<view class="message-list">
|
|
|
|
|
|
<view class="message-item" v-for="(msg, index) in messages" :key="index"
|
|
|
|
|
|
: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">
|
|
|
|
|
|
import { ref, nextTick, onMounted } from 'vue'
|
|
|
|
|
|
import WorkcaseCreator from '@/components/WorkcaseCreator/WorkcaseCreator.uvue'
|
|
|
|
|
|
import type { ChatRoomMessageVO } from '@/types/workcase'
|
|
|
|
|
|
|
|
|
|
|
|
// 响应式数据
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
// 消息列表
|
|
|
|
|
|
const messages = ref<ChatRoomMessageVO[]>([
|
|
|
|
|
|
{
|
|
|
|
|
|
messageId: '1',
|
|
|
|
|
|
roomId: 'room001',
|
|
|
|
|
|
senderId: 'agent001',
|
|
|
|
|
|
senderType: 'agent',
|
|
|
|
|
|
senderName: '客服小张',
|
|
|
|
|
|
content: '您好,我是客服小张,请问有什么可以帮助您的?',
|
|
|
|
|
|
sendTime: '2024-12-17 16:00:00'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
messageId: '2',
|
|
|
|
|
|
roomId: 'room001',
|
|
|
|
|
|
senderId: 'guest001',
|
|
|
|
|
|
senderType: 'guest',
|
|
|
|
|
|
senderName: '李经理',
|
|
|
|
|
|
content: '我们的设备出现了控制系统故障,无法正常启动',
|
|
|
|
|
|
sendTime: '2024-12-17 16:02:00'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
messageId: '3',
|
|
|
|
|
|
roomId: 'room001',
|
|
|
|
|
|
senderId: 'agent001',
|
|
|
|
|
|
senderType: 'agent',
|
|
|
|
|
|
senderName: '客服小张',
|
|
|
|
|
|
content: '好的,请问是哪个型号的设备?能否提供一下设备序列号?',
|
|
|
|
|
|
sendTime: '2024-12-17 16:03:00'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
messageId: '4',
|
|
|
|
|
|
roomId: 'room001',
|
|
|
|
|
|
senderId: 'guest001',
|
|
|
|
|
|
senderType: 'guest',
|
|
|
|
|
|
senderName: '李经理',
|
|
|
|
|
|
content: '型号是TH-500GF,序列号是TH20230501001',
|
|
|
|
|
|
sendTime: '2024-12-17 16:05:00'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
messageId: '5',
|
|
|
|
|
|
roomId: 'room001',
|
|
|
|
|
|
senderId: 'agent001',
|
|
|
|
|
|
senderType: 'agent',
|
|
|
|
|
|
senderName: '客服小张',
|
|
|
|
|
|
content: '好的,我已经记录了您的问题。建议您创建一个工单,我们会安排工程师尽快上门处理。',
|
|
|
|
|
|
sendTime: '2024-12-17 16: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 || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
loadChatRoom()
|
|
|
|
|
|
scrollToBottom()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 加载聊天室
|
|
|
|
|
|
function loadChatRoom() {
|
|
|
|
|
|
console.log('加载聊天室:', roomId.value)
|
|
|
|
|
|
// TODO: 调用 workcaseChatAPI.getChatRoomById() 获取聊天室信息
|
|
|
|
|
|
// TODO: 调用 workcaseChatAPI.getChatMessagePage() 获取消息列表
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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')}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送消息
|
|
|
|
|
|
function sendMessage() {
|
|
|
|
|
|
const text = inputText.value.trim()
|
|
|
|
|
|
if (!text) return
|
|
|
|
|
|
|
|
|
|
|
|
const newMsg: ChatRoomMessageVO = {
|
|
|
|
|
|
messageId: Date.now().toString(),
|
|
|
|
|
|
roomId: roomId.value,
|
|
|
|
|
|
senderId: 'guest001',
|
|
|
|
|
|
senderType: 'guest',
|
|
|
|
|
|
senderName: '我',
|
|
|
|
|
|
content: text,
|
|
|
|
|
|
sendTime: new Date().toISOString()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
messages.value.push(newMsg)
|
|
|
|
|
|
inputText.value = ''
|
|
|
|
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
scrollToBottom()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: 调用 workcaseChatAPI.sendMessage() 发送消息
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 滚动到底部
|
|
|
|
|
|
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>
|