2025-05-23 16:00:15 +08:00

252 lines
8.3 KiB
Vue
Raw Permalink 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.

<template>
<!-- 悬浮按钮 -->
<button
v-if="!showChat"
class="fixed z-50 bottom-6 right-6 bg-white dark:bg-gray-800 text-blue-600 rounded-full shadow-lg w-14 h-14 flex items-center justify-center text-2xl hover:bg-blue-600 hover:text-white transition"
@click="showChat = true"
aria-label="在线咨询"
>
<img src="../public/img/customerservice.svg" alt="logo" class="w-8 h-8 rounded-full" />
</button>
<!-- Chat 弹窗 -->
<transition name="fade">
<div
v-if="showChat"
class="fixed z-50 bottom-6 right-6"
style="max-width: 400px; width: 90vw;"
>
<div class="relative">
<button
class="absolute -top-3 -right-3 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-700 rounded-full w-7 h-7 flex items-center justify-center shadow hover:bg-gray-100 dark:hover:bg-gray-600"
@click="showChat = false"
aria-label="关闭"
>✕</button>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden">
<!-- 标题栏 -->
<div class="flex items-center px-6 py-4 border-b">
<img src="../public/img/sitelogo.svg" alt="logo" class="w-8 h-8 rounded-full bg-blue-100 mr-2" />
<span class="font-bold text-lg text-primary dark:text-[#4da6ff]">PinnovateCloud</span>
</div>
<!-- 聊天内容 -->
<div id="content" class="px-6 py-4 h-[432px] min-h-[432px] max-h-[432px] overflow-y-auto bg-gray-50 dark:bg-gray-700">
<div v-for="(item, index) in info" :key="index">
<!-- 机器人消息 -->
<div v-if="item.type === 'leftinfo'" class="flex items-start mb-4">
<img src="/img/sitelogo.svg" alt="logo" class="w-8 h-8 rounded-full object-cover mr-2" />
<div>
<div class="bg-gray-200 dark:bg-gray-600 rounded-lg px-4 py-2 text-gray-800 dark:text-white shadow">
{{ item.content }}
<div v-for="(item2, idx) in item.question" :key="idx">
<div class="text-blue-600 cursor-pointer hover:underline mt-1" @click="clickRobot(item2.content, item2.id)">
{{ item2.index }}. {{ item2.content }}
</div>
</div>
</div>
<div class="text-xs text-gray-400 dark:text-gray-300 mt-1">{{ item.time }}</div>
</div>
</div>
<!-- 用户消息 -->
<div v-else-if="item.type === 'rightinfo'" class="flex items-end justify-end mb-4">
<div>
<div class="bg-blue-600 text-white rounded-lg px-4 py-2 shadow text-right">
{{ item.content }}
</div>
<div class="text-xs text-gray-400 text-right mt-1">{{ item.time }}</div>
</div>
<div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center ml-2">
<span class="text-blue-500">😎</span>
</div>
</div>
</div>
</div>
<!-- 输入区 -->
<div class="flex items-center px-6 py-4 border-t bg-white dark:bg-gray-700">
<textarea
v-model="customerText"
placeholder="请输入您的问题..."
class="flex-1 resize-none border border-gray-200 dark:border-gray-700 rounded-lg px-3 py-2 mr-2 focus:outline-none focus:border-blue-400"
rows="2"
@keyup.enter="sentMsg"
></textarea>
<button
@click="sentMsg"
class="bg-blue-600 text-white px-6 py-2 rounded-lg shadow hover:bg-blue-700 transition"
>
发送
</button>
</div>
</div>
</div>
</div>
</transition>
</template>
<script setup>
import { ref, nextTick, onMounted } from 'vue'
const customerText = ref('')
const info = ref([
{
type: 'leftinfo',
time: getTodayTime(),
name: 'robot',
content: '您好欢迎使用PinnovateCloud在线客服我们是AWS云服务专家提供云服务器、对象存储、数据库、AI等全线AWS产品与解决方案。如需帮助请随时咨询。',
question: [
{ id: 1, content: '如何选购AWS云服务器', index: 1 },
{ id: 2, content: 'AWS对象存储S3有哪些优势', index: 2 },
{ id: 3, content: '如何获取AWS产品报价', index: 3 },
{ id: 4, content: '企业上云迁移流程是怎样的?', index: 4 },
{ id: 5, content: '如何联系技术支持?', index: 5 },
],
},
{
type: 'leftinfo',
time: getTodayTime(),
name: 'robot',
content: '网站信息PinnovateCloud 新加坡AWS授权合作伙伴。联系方式support@pinnovatecloud.com电话+86 15656988217',
question: [],
},
])
const robotQuestion = [
{ id: 1, content: '如何选购AWS云服务器', index: 1 },
{ id: 2, content: 'AWS对象存储S3有哪些优势', index: 2 },
{ id: 3, content: '如何获取AWS产品报价', index: 3 },
{ id: 4, content: '企业上云迁移流程是怎样的?', index: 4 },
{ id: 5, content: '如何联系技术支持?', index: 5 },
]
const robotAnswer = [
{ id: 1, content: '您可以根据业务需求选择EC2实例类型我们可为您提供专业选型建议和报价。', index: 1 },
{ id: 2, content: 'S3具备高可用、高持久性、弹性扩展、按需付费等优势适合备份、归档、数据湖等多场景。', index: 2 },
{ id: 3, content: '请提供您的需求我们会有专属顾问为您定制AWS产品报价方案。', index: 3 },
{ id: 4, content: '企业上云迁移包括评估、方案设计、数据迁移、系统上线等环节,我们可全程协助。', index: 4 },
{ id: 5, content: '您可通过在线客服、邮箱support@pinnovatecloud.com或电话+86 15656988217联系我们的技术支持团队。', index: 5 },
]
let timer = null
const showChat = ref(false)
function sentMsg() {
clearTimeout(timer)
showTimer()
const text = customerText.value.trim()
if (text !== '') {
info.value.push({
type: 'rightinfo',
time: getTodayTime(),
content: text,
})
appendRobotMsg(text)
customerText.value = ''
nextTick(scrollToBottom)
}
}
function appendRobotMsg(text) {
clearTimeout(timer)
showTimer()
text = text.trim()
let answerText = ''
let flag = false
for (let i = 0; i < robotAnswer.length; i++) {
if (robotAnswer[i].content.indexOf(text) !== -1) {
flag = true
answerText = robotAnswer[i].content
break
}
}
if (flag) {
info.value.push({
type: 'leftinfo',
time: getTodayTime(),
name: 'robot',
content: answerText,
question: [],
})
} else {
info.value.push({
type: 'leftinfo',
time: getTodayTime(),
name: 'robot',
content: '您可能想问:',
question: robotQuestion,
})
}
nextTick(scrollToBottom)
}
function sentMsgById(val, id) {
clearTimeout(timer)
showTimer()
const robotById = robotAnswer.filter((item) => item.id === id)
info.value.push({
type: 'rightinfo',
time: getTodayTime(),
content: val,
})
info.value.push({
type: 'leftinfo',
time: getTodayTime(),
name: 'robot',
content: robotById[0].content,
question: [],
})
nextTick(scrollToBottom)
}
function clickRobot(val, id) {
sentMsgById(val, id)
}
function endMsg() {
info.value.push({
type: 'leftinfo',
time: getTodayTime(),
content: '欢迎下次咨询',
question: [],
})
nextTick(scrollToBottom)
}
function showTimer() {
timer = setTimeout(endMsg, 1000 * 60)
}
function getTodayTime() {
const day = new Date()
const pad = (n) => (n < 10 ? '0' + n : n)
return (
day.getFullYear() +
'-' +
pad(day.getMonth() + 1) +
'-' +
pad(day.getDate()) +
' ' +
pad(day.getHours()) +
':' +
pad(day.getMinutes()) +
':' +
pad(day.getSeconds())
)
}
function scrollToBottom() {
const contentHeight = document.getElementById('content')
if (contentHeight) {
contentHeight.scrollTop = contentHeight.scrollHeight
}
}
onMounted(() => {
showTimer()
})
</script>
<style scoped>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>