Files
medical-mall/pages/mall/consumer/chat.uvue

593 lines
14 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- pages/mall/consumer/chat.uvue -->
<template>
<view class="chat-page">
<!-- 聊天头部 -->
<view class="chat-header">
<view class="header-back" @click="goBack">
<text class="back-icon"></text>
</view>
<view class="header-info">
<text class="chat-title">在线客服</text>
<text class="chat-status">在线</text>
</view>
<view class="header-actions">
<text class="action-icon" @click="showMoreActions">⋮</text>
</view>
</view>
<!-- 聊天内容 -->
<scroll-view
scroll-y
class="chat-content"
:scroll-into-view="scrollToView"
scroll-with-animation
>
<!-- 聊天消息列表 -->
<view class="chat-messages">
<!-- 系统消息 -->
<view class="message-item system">
<text class="system-text">客服 小美 已接入,请描述您的问题</text>
</view>
<!-- 时间分割线 -->
<view class="time-divider">
<text>今天 14:30</text>
</view>
<!-- 消息项 -->
<view
v-for="message in messages"
:key="message.id"
:class="['message-item', message.type]"
:id="'msg-' + message.id"
>
<!-- 对方消息 -->
<view v-if="message.type === 'received'" class="message-wrapper">
<image
class="avatar"
src="https://picsum.photos/40/40?random=1"
mode="aspectFill"
/>
<view class="message-content-wrapper">
<text class="sender-name">客服小美</text>
<view class="message-bubble">
<text class="message-text">{{ message.content }}</text>
<text class="message-time">{{ message.time }}</text>
</view>
</view>
</view>
<!-- 我的消息 -->
<view v-else class="message-wrapper me">
<view class="message-content-wrapper">
<view class="message-bubble me">
<text class="message-text">{{ message.content }}</text>
<text class="message-time">{{ message.time }}</text>
</view>
</view>
<image
class="avatar me"
src="https://picsum.photos/40/40?random=2"
mode="aspectFill"
/>
</view>
</view>
</view>
</scroll-view>
<!-- 聊天输入区 -->
<view class="chat-input">
<view class="input-tools">
<text class="tool-icon" @click="showEmojiPicker">😊</text>
<text class="tool-icon" @click="showImagePicker">📷</text>
<text class="tool-icon" @click="showMoreTools"></text>
</view>
<view class="input-wrapper">
<input
class="message-input"
v-model="inputMessage"
placeholder="请输入消息..."
:focus="inputFocus"
@confirm="sendMessage"
confirm-type="send"
/>
<button
class="send-button"
:class="{ active: inputMessage.trim() }"
@click="sendMessage"
>
发送
</button>
</view>
</view>
<!-- 表情选择器 -->
<view v-if="showEmoji" class="emoji-picker">
<view class="emoji-category">
<text
v-for="emoji in emojiList"
:key="emoji"
class="emoji-item"
@click="insertEmoji(emoji)"
>
{{ emoji }}
</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, reactive, onMounted, nextTick } from 'vue'
// 响应式数据
const messages = ref<any[]>([])
const inputMessage = ref<string>('')
const inputFocus = ref<boolean>(false)
const showEmoji = ref<boolean>(false)
const scrollToView = ref<string>('')
// 模拟表情列表
const emojiList = ['😊', '😂', '🤣', '😍', '😘', '🥰', '😭', '😡', '👍', '👏', '🙏', '🎉', '❤️', '🔥', '⭐']
// Mock 聊天记录
const mockMessages = [
{
id: 1,
type: 'received',
content: '您好,欢迎咨询!有什么可以帮助您的吗?',
time: '14:30'
},
{
id: 2,
type: 'sent',
content: '你好,我昨天下的订单一直没有发货',
time: '14:31'
},
{
id: 3,
type: 'received',
content: '请问您的订单号是多少?我帮您查询一下',
time: '14:32'
},
{
id: 4,
type: 'sent',
content: '订单号是202311230001',
time: '14:33'
},
{
id: 5,
type: 'received',
content: '正在为您查询订单状态...',
time: '14:34'
}
]
// 生命周期
onMounted(() => {
loadChatHistory()
// 模拟客服自动回复
setTimeout(() => {
addReceivedMessage('查询到您的订单正在打包中,预计今天下午发货')
}, 3000)
})
// 加载聊天记录
const loadChatHistory = () => {
messages.value = [...mockMessages]
// 滚动到底部
setTimeout(() => {
scrollToBottom()
}, 100)
}
// 发送消息
const sendMessage = () => {
const content = inputMessage.value.trim()
if (!content) return
// 添加发送的消息
const newMessage = {
id: Date.now(),
type: 'sent',
content: content,
time: getCurrentTime()
}
messages.value.push(newMessage)
inputMessage.value = ''
// 滚动到底部
scrollToBottom()
// 模拟客服回复2秒后
setTimeout(() => {
simulateCustomerReply()
}, 2000)
}
// 模拟客服回复
const simulateCustomerReply = () => {
const replies = [
'好的,已为您记录',
'这个问题需要进一步核实',
'我明白了,马上为您处理',
'请稍等,正在为您查询',
'感谢您的反馈'
]
const randomReply = replies[Math.floor(Math.random() * replies.length)]
addReceivedMessage(randomReply)
}
// 添加接收的消息
const addReceivedMessage = (content: string) => {
const newMessage = {
id: Date.now(),
type: 'received',
content: content,
time: getCurrentTime()
}
messages.value.push(newMessage)
scrollToBottom()
}
// 滚动到底部
const scrollToBottom = () => {
nextTick(() => {
if (messages.value.length > 0) {
const lastMsgId = messages.value[messages.value.length - 1].id
scrollToView.value = 'msg-' + lastMsgId
}
})
}
// 获取当前时间
const getCurrentTime = (): string => {
const now = new Date()
const hours = now.getHours().toString().padStart(2, '0')
const minutes = now.getMinutes().toString().padStart(2, '0')
return `${hours}:${minutes}`
}
// 插入表情
const insertEmoji = (emoji: string) => {
inputMessage.value += emoji
inputFocus.value = true
}
// 显示表情选择器
const showEmojiPicker = () => {
showEmoji.value = !showEmoji.value
}
// 显示图片选择器
const showImagePicker = () => {
uni.chooseImage({
count: 1,
success: (res) => {
console.log('选择图片:', res.tempFilePaths)
// 这里可以处理图片上传
}
})
}
// 显示更多工具
const showMoreTools = () => {
uni.showActionSheet({
itemList: ['发送位置', '发送文件', '发送语音'],
success: (res) => {
console.log('选择工具:', res.tapIndex)
}
})
}
// 显示更多操作
const showMoreActions = () => {
uni.showActionSheet({
itemList: ['投诉客服', '结束对话', '清除记录'],
success: (res) => {
switch (res.tapIndex) {
case 0:
uni.navigateTo({ url: '/pages/mall/consumer/complaint' })
break
case 1:
uni.showModal({
title: '确认结束',
content: '确定要结束本次对话吗?',
success: (res) => {
if (res.confirm) {
uni.navigateBack()
}
}
})
break
case 2:
uni.showModal({
title: '确认清除',
content: '确定要清除聊天记录吗?',
success: (res) => {
if (res.confirm) {
messages.value = []
}
}
})
break
}
}
})
}
// 返回
const goBack = () => {
uni.navigateBack()
}
</script>
<style>
.chat-page {
width: 100%;
height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
/* 聊天头部 */
.chat-header {
background-color: white;
padding: 10px 15px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #eee;
}
.header-back {
width: 40px;
}
.back-icon {
font-size: 24px;
color: #333;
}
.header-info {
flex: 1;
text-align: center;
}
.chat-title {
font-size: 16px;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 2px;
}
.chat-status {
font-size: 12px;
color: #34c759;
}
.header-actions .action-icon {
font-size: 20px;
color: #333;
width: 40px;
text-align: right;
}
/* 聊天内容区 */
.chat-content {
flex: 1;
padding: 15px;
padding-bottom: 70px; /* 为输入区留出空间 */
}
/* 系统消息 */
.message-item.system {
text-align: center;
margin-bottom: 20px;
}
.system-text {
font-size: 12px;
color: #999;
background-color: #f0f0f0;
padding: 5px 15px;
border-radius: 15px;
display: inline-block;
}
/* 时间分割线 */
.time-divider {
text-align: center;
margin: 20px 0;
}
.time-divider text {
font-size: 12px;
color: #999;
background-color: #f0f0f0;
padding: 3px 10px;
border-radius: 10px;
}
/* 消息项 */
.message-wrapper {
display: flex;
margin-bottom: 15px;
}
.message-wrapper.me {
justify-content: flex-end;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 20px;
margin-right: 10px;
flex-shrink: 0;
}
.avatar.me {
margin-right: 0;
margin-left: 10px;
order: 2;
}
.message-content-wrapper {
max-width: 70%;
}
.sender-name {
font-size: 12px;
color: #999;
margin-bottom: 5px;
display: block;
}
.message-bubble {
background-color: white;
padding: 10px 15px;
border-radius: 18px;
position: relative;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
max-width: 100%;
word-wrap: break-word;
}
.message-bubble.me {
background-color: #95ec69;
border-bottom-right-radius: 4px;
}
.message-bubble:not(.me) {
border-bottom-left-radius: 4px;
}
.message-text {
font-size: 15px;
color: #333;
line-height: 1.4;
display: block;
margin-bottom: 5px;
}
.message-time {
font-size: 11px;
color: #999;
display: block;
text-align: right;
}
/* 聊天输入区 */
.chat-input {
background-color: white;
border-top: 1px solid #eee;
padding: 10px 15px;
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
}
.input-tools {
display: flex;
margin-bottom: 10px;
}
.tool-icon {
font-size: 20px;
margin-right: 15px;
color: #666;
}
.input-wrapper {
display: flex;
align-items: center;
}
.message-input {
flex: 1;
background-color: #f5f5f5;
border-radius: 20px;
padding: 10px 15px;
font-size: 15px;
margin-right: 10px;
min-height: 40px;
max-height: 100px;
}
.send-button {
background-color: #ccc;
color: white;
border: none;
border-radius: 20px;
padding: 8px 20px;
font-size: 14px;
min-width: 60px;
transition: all 0.3s ease;
}
.send-button.active {
background-color: #ff5000;
}
/* 表情选择器 */
.emoji-picker {
background-color: white;
border-top: 1px solid #eee;
padding: 10px;
max-height: 200px;
overflow-y: auto;
position: fixed;
bottom: 60px;
left: 0;
right: 0;
z-index: 99;
}
.emoji-category {
display: flex;
flex-wrap: wrap;
}
.emoji-item {
font-size: 24px;
padding: 8px;
width: 45px;
height: 45px;
display: flex;
align-items: center;
justify-content: center;
}
/* 响应式适配 */
@media screen and (max-width: 320px) {
.message-content-wrapper {
max-width: 65%;
}
.message-text {
font-size: 14px;
}
}
@media screen and (min-width: 415px) {
.message-content-wrapper {
max-width: 75%;
}
.chat-input {
padding: 15px 20px;
}
}
</style>