Merge branch 'dev' of https://gitea.wangzhiwen.top/0214/ALOps_sys1 into dev
commit
07efe8e04b
@ -0,0 +1,248 @@ |
||||
<template> |
||||
|
||||
<div id="app"> |
||||
<el-card class="customer-service"> |
||||
<!-- 客服头部 --> |
||||
<div class="service-header"> |
||||
<h2>AI智能助手</h2> |
||||
<p>您好,我是智能助手,很高兴为您服务</p> |
||||
</div> |
||||
|
||||
<div class="service-container"> |
||||
<!-- 右侧聊天区域 --> |
||||
<div class="chat-section"> |
||||
<div class="chat-messages" ref="messagesContainer"> |
||||
<div |
||||
v-for="(message, index) in messages" |
||||
:key="index" |
||||
:class="['message', message.sender]" |
||||
> |
||||
<div class="message-content"> |
||||
<div>{{ message.text }}</div> |
||||
<div class="timestamp">{{ message.time }}</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="chat-input"> |
||||
<el-input |
||||
v-model="newMessage" |
||||
placeholder="请输入您的问题..." |
||||
@keyup.enter.native="sendMessage" |
||||
></el-input> |
||||
<el-button |
||||
type="primary" |
||||
@click="sendMessage" |
||||
:disabled="!newMessage.trim()" |
||||
>发送</el-button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</el-card> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from "@/api/inMonitoring/assessment" |
||||
import { getToken } from "@/utils/auth" |
||||
import Treeselect from "@riophae/vue-treeselect" |
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css" |
||||
import { Splitpanes, Pane } from "splitpanes" |
||||
import "splitpanes/dist/splitpanes.css" |
||||
|
||||
export default { |
||||
name: "User", |
||||
dicts: ['sys_normal_disable', 'sys_user_sex'], |
||||
components: { Treeselect, Splitpanes, Pane }, |
||||
data() { |
||||
return { |
||||
newMessage: '', |
||||
messages: [ |
||||
{ |
||||
text: '您好!我是AI智能助手,请问有什么可以帮您?', |
||||
sender: 'service', |
||||
time: this.formatTime(new Date()) |
||||
} |
||||
], |
||||
faqs: [ |
||||
{ question: '如何查询订单状态?', answer: '您可以在"我的订单"页面查看订单的当前状态和物流信息。' }, |
||||
{ question: '如何办理退货?', answer: '请在订单详情页点击"申请退货",填写相关信息后提交申请。' }, |
||||
{ question: '支付方式有哪些?', answer: '我们支持支付宝、微信支付、银联等多种支付方式。' }, |
||||
{ question: '会员有哪些优惠?', answer: '会员可享受购物折扣、专属优惠券、生日特权等多重优惠。' }, |
||||
{ question: '商品何时发货?', answer: '一般情况下,我们会在订单支付成功后24小时内安排发货。' } |
||||
] |
||||
}; |
||||
},mounted() { |
||||
// 初始化时滚动到底部 |
||||
this.scrollToBottom(); |
||||
}, |
||||
methods: { |
||||
sendMessage() { |
||||
if (!this.newMessage.trim()) return; |
||||
|
||||
// 添加用户消息 |
||||
this.messages.push({ |
||||
text: this.newMessage, |
||||
sender: 'customer', |
||||
time: this.formatTime(new Date()) |
||||
}); |
||||
|
||||
// 模拟自动回复 |
||||
setTimeout(() => { |
||||
this.autoReply(this.newMessage); |
||||
}, 1000); |
||||
|
||||
this.newMessage = ''; |
||||
}, |
||||
autoReply(userMessage) { |
||||
let replyText = ''; |
||||
|
||||
// 简单关键词匹配回复 |
||||
if (userMessage.includes('订单') || userMessage.includes('物流')) { |
||||
replyText = '您可以在"我的订单"页面查看订单详情和物流信息。'; |
||||
} else if (userMessage.includes('支付') || userMessage.includes('付款')) { |
||||
replyText = '我们支持支付宝、微信支付、银联等多种支付方式。'; |
||||
} else if (userMessage.includes('退货') || userMessage.includes('退款')) { |
||||
replyText = '如需退货退款,请在订单详情页提交申请,客服会尽快处理。'; |
||||
} else if (userMessage.includes('会员') || userMessage.includes('优惠')) { |
||||
replyText = '会员可享受专属折扣和优惠券,详情请查看会员中心。'; |
||||
} else if (userMessage.includes('发票')) { |
||||
replyText = '您可以在下单时选择开具发票,我们提供电子发票和纸质发票两种方式。'; |
||||
} else { |
||||
replyText = '抱歉,我没有完全理解您的问题。您可以尝试联系人工客服获得更详细的帮助。'; |
||||
} |
||||
|
||||
// 添加客服回复 |
||||
this.messages.push({ |
||||
text: replyText, |
||||
sender: 'service', |
||||
time: this.formatTime(new Date()) |
||||
}); |
||||
|
||||
// 滚动到底部 |
||||
this.scrollToBottom(); |
||||
}, |
||||
selectFaq(faq) { |
||||
// 添加用户选择的常见问题 |
||||
this.messages.push({ |
||||
text: faq.question, |
||||
sender: 'customer', |
||||
time: this.formatTime(new Date()) |
||||
}); |
||||
|
||||
// 添加对应的回答 |
||||
setTimeout(() => { |
||||
this.messages.push({ |
||||
text: faq.answer, |
||||
sender: 'service', |
||||
time: this.formatTime(new Date()) |
||||
}); |
||||
this.scrollToBottom(); |
||||
}, 1000); |
||||
}, |
||||
scrollToBottom() { |
||||
this.$nextTick(() => { |
||||
const container = this.$refs.messagesContainer; |
||||
container.scrollTop = container.scrollHeight; |
||||
}); |
||||
}, |
||||
formatTime(date) { |
||||
const hours = date.getHours().toString().padStart(2, '0'); |
||||
const minutes = date.getMinutes().toString().padStart(2, '0'); |
||||
return `${hours}:${minutes}`; |
||||
} |
||||
}, |
||||
watch: { |
||||
messages: { |
||||
handler() { |
||||
this.scrollToBottom(); |
||||
}, |
||||
deep: true |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
<style scoped> |
||||
|
||||
#app { |
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; |
||||
margin: 20px; |
||||
} |
||||
.customer-service { |
||||
/* max-width: 1000px; */ |
||||
margin: 0 auto; |
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
border-radius: 4px; |
||||
} |
||||
.service-header { |
||||
background-color: #409EFF; |
||||
color: white; |
||||
padding: 15px 20px; |
||||
border-top-left-radius: 4px; |
||||
border-top-right-radius: 4px; |
||||
} |
||||
.service-container { |
||||
display: flex; |
||||
min-height: 400px; |
||||
} |
||||
.faq-section { |
||||
width: 30%; |
||||
border-right: 1px solid #e6e6e6; |
||||
overflow-y: auto; |
||||
padding: 15px; |
||||
} |
||||
.chat-section { |
||||
width: 100%; |
||||
display: flex; |
||||
flex-direction: column; |
||||
} |
||||
.chat-messages { |
||||
flex: 1; |
||||
overflow-y: auto; |
||||
padding: 15px; |
||||
} |
||||
.message { |
||||
margin-bottom: 15px; |
||||
display: flex; |
||||
} |
||||
.message.customer { |
||||
justify-content: flex-end; |
||||
} |
||||
.message-content { |
||||
max-width: 70%; |
||||
padding: 10px 15px; |
||||
border-radius: 4px; |
||||
} |
||||
.message.customer .message-content { |
||||
background-color: #ecf5ff; |
||||
border: 1px solid #d9ecff; |
||||
} |
||||
.message.service .message-content { |
||||
background-color: #f4f4f5; |
||||
border: 1px solid #e9e9eb; |
||||
} |
||||
.chat-input { |
||||
padding: 15px; |
||||
border-top: 1px solid #e6e6e6; |
||||
display: flex; |
||||
} |
||||
.chat-input .el-input { |
||||
margin-right: 10px; |
||||
} |
||||
.faq-item { |
||||
padding: 10px; |
||||
margin-bottom: 10px; |
||||
border-radius: 4px; |
||||
cursor: pointer; |
||||
border: 1px solid #ebeef5; |
||||
} |
||||
.faq-item:hover { |
||||
background-color: #f5f7fa; |
||||
} |
||||
.timestamp { |
||||
font-size: 12px; |
||||
color: #909399; |
||||
margin-top: 5px; |
||||
text-align: right; |
||||
} |
||||
</style> |
||||
@ -1,272 +0,0 @@ |
||||
<template> |
||||
<div class="chat-container"> |
||||
<!-- 消息列表区域 --> |
||||
<el-scrollbar |
||||
ref="messageScroll" |
||||
class="message-area" |
||||
@scroll.native="handleScroll"> |
||||
<div |
||||
v-for="(msg, index) in messages" |
||||
:key="index" |
||||
:class="['message', msg.sender === 'user' ? 'user-msg' : 'customer-msg']"> |
||||
<el-avatar :size="40" :src="msg.avatar"></el-avatar> |
||||
<div class="message-content"> |
||||
<div class="sender">{{ msg.sender === 'user' ? '我' : '客服' }}</div> |
||||
<div class="content">{{ msg.content }}</div> |
||||
<div class="time">{{ formatTime(msg.timestamp) }}</div> |
||||
</div> |
||||
</div> |
||||
<!-- 加载中提示 --> |
||||
<div |
||||
v-if="loading" |
||||
class="loading-indicator"> |
||||
<i class="el-icon-loading"></i> 加载中... |
||||
</div> |
||||
</el-scrollbar> |
||||
|
||||
<!-- 输入区域 --> |
||||
<div class="input-area"> |
||||
<el-input |
||||
v-model="inputMessage" |
||||
type="textarea" |
||||
placeholder="请输入消息..." |
||||
@keyup.enter.native="sendMessage" |
||||
:disabled="!connected"> |
||||
</el-input> |
||||
<div class="action-buttons"> |
||||
<el-button |
||||
type="primary" |
||||
@click="sendMessage" |
||||
:disabled="!connected || !inputMessage.trim()" |
||||
:loading="sending"> |
||||
发送 |
||||
</el-button> |
||||
<el-button @click="toggleConnection"> |
||||
{{ connected ? '断开' : '连接' }} |
||||
</el-button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { Loading } from 'element-ui'; |
||||
|
||||
export default { |
||||
data() { |
||||
return { |
||||
messages: [], |
||||
inputMessage: '', |
||||
connected: false, |
||||
loading: false, |
||||
sending: false, |
||||
page: 1, |
||||
pageSize: 20 |
||||
}; |
||||
}, |
||||
mounted() { |
||||
this.connect(); |
||||
this.scrollToBottom(); |
||||
}, |
||||
methods: { |
||||
// 连接客服系统 |
||||
connect() { |
||||
this.connected = true; |
||||
this.loadMessages(); |
||||
}, |
||||
|
||||
// 断开连接 |
||||
toggleConnection() { |
||||
this.connected = !this.connected; |
||||
if (this.connected) { |
||||
this.loadMessages(); |
||||
} |
||||
}, |
||||
|
||||
// 加载历史消息 |
||||
async loadMessages() { |
||||
this.loading = true; |
||||
try { |
||||
// 模拟API请求 |
||||
const response = await this.mockApiCall(); |
||||
this.messages = response.data; |
||||
this.page++; |
||||
} catch (error) { |
||||
console.error('加载消息失败:', error); |
||||
} finally { |
||||
this.loading = false; |
||||
this.$nextTick(this.scrollToBottom); |
||||
} |
||||
}, |
||||
|
||||
// 发送消息 |
||||
sendMessage() { |
||||
if (!this.inputMessage.trim()) return; |
||||
|
||||
this.sending = true; |
||||
|
||||
// 模拟发送延时 |
||||
setTimeout(() => { |
||||
this.messages.push({ |
||||
sender: 'user', |
||||
content: this.inputMessage, |
||||
timestamp: Date.now(), |
||||
avatar: 'https://via.placeholder.com/40?text=U' |
||||
}); |
||||
|
||||
this.inputMessage = ''; |
||||
this.sending = false; |
||||
this.scrollToBottom(); |
||||
|
||||
// 模拟客服回复 |
||||
this.simulateCustomerReply(); |
||||
}, 500); |
||||
}, |
||||
|
||||
// 模拟客服自动回复 |
||||
simulateCustomerReply() { |
||||
setTimeout(() => { |
||||
this.messages.push({ |
||||
sender: 'customer', |
||||
content: '您好,已收到您的消息,我们会尽快处理', |
||||
timestamp: Date.now(), |
||||
avatar: 'https://via.placeholder.com/40?text=C' |
||||
}); |
||||
this.scrollToBottom(); |
||||
}, 1000); |
||||
}, |
||||
|
||||
// 滚动到底部 |
||||
scrollToBottom() { |
||||
const scrollContainer = this.$refs.messageScroll.$el.querySelector('.el-scrollbar__wrap'); |
||||
if (scrollContainer) { |
||||
scrollContainer.scrollTop = scrollContainer.scrollHeight; |
||||
} |
||||
}, |
||||
|
||||
// 处理滚动事件 |
||||
handleScroll(e) { |
||||
const { scrollTop, scrollHeight, clientHeight } = e.target; |
||||
const scrollBottom = scrollHeight - scrollTop - clientHeight; |
||||
|
||||
// 滚动到底部加载更多 |
||||
if (scrollBottom < 50 && !this.loading) { |
||||
this.loadMoreMessages(); |
||||
} |
||||
}, |
||||
|
||||
// 加载更多消息 |
||||
async loadMoreMessages() { |
||||
this.loading = true; |
||||
try { |
||||
// 模拟API请求更多消息 |
||||
const response = await this.mockApiCall(this.page); |
||||
this.messages = [...response.data, ...this.messages]; |
||||
this.page++; |
||||
} catch (error) { |
||||
console.error('加载更多消息失败:', error); |
||||
} finally { |
||||
this.loading = false; |
||||
} |
||||
}, |
||||
|
||||
// 格式化时间 |
||||
formatTime(timestamp) { |
||||
return new Date(timestamp).toLocaleTimeString(); |
||||
}, |
||||
|
||||
// 模拟API请求 |
||||
mockApiCall(page = 1) { |
||||
return new Promise((resolve) => { |
||||
setTimeout(() => { |
||||
resolve({ |
||||
data: Array(this.pageSize).fill().map((_, i) => ({ |
||||
sender: Math.random() > 0.5 ? 'user' : 'customer', |
||||
content: `消息 ${(page-1)*this.pageSize + i + 1}`, |
||||
timestamp: Date.now() - i * 1000, |
||||
avatar: Math.random() > 0.5 |
||||
? 'https://via.placeholder.com/40?text=U' |
||||
: 'https://via.placeholder.com/40?text=C' |
||||
})) |
||||
}); |
||||
}, 800); |
||||
}); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style> |
||||
.chat-container { |
||||
display: flex; |
||||
flex-direction: column; |
||||
height: 100vh; |
||||
border: 1px solid #ebeef5; |
||||
border-radius: 4px; |
||||
} |
||||
|
||||
.message-area { |
||||
flex: 1; |
||||
padding: 20px; |
||||
overflow-y: auto; |
||||
} |
||||
|
||||
.message { |
||||
display: flex; |
||||
margin-bottom: 20px; |
||||
} |
||||
|
||||
.user-msg { |
||||
flex-direction: row-reverse; |
||||
} |
||||
|
||||
.customer-msg { |
||||
flex-direction: row; |
||||
} |
||||
|
||||
.message-content { |
||||
margin-left: 15px; |
||||
background: #f0f0f0; |
||||
padding: 12px; |
||||
border-radius: 8px; |
||||
max-width: 80%; |
||||
} |
||||
|
||||
.user-msg .message-content { |
||||
background: #e1f0ff; |
||||
} |
||||
|
||||
.sender { |
||||
font-weight: bold; |
||||
margin-bottom: 5px; |
||||
} |
||||
|
||||
.content { |
||||
white-space: pre-wrap; |
||||
} |
||||
|
||||
.time { |
||||
font-size: 0.8em; |
||||
color: #999; |
||||
text-align: right; |
||||
margin-top: 5px; |
||||
} |
||||
|
||||
.input-area { |
||||
padding: 15px; |
||||
border-top: 1px solid #ebeef5; |
||||
background: #fff; |
||||
} |
||||
|
||||
.action-buttons { |
||||
display: flex; |
||||
justify-content: flex-end; |
||||
margin-top: 10px; |
||||
} |
||||
|
||||
.loading-indicator { |
||||
text-align: center; |
||||
padding: 15px; |
||||
color: #999; |
||||
} |
||||
</style> |
||||
Loading…
Reference in new issue