593 lines
14 KiB
Plaintext
593 lines
14 KiB
Plaintext
<!-- 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> |