|
|
<script setup> // 移除未使用的导入
import { ref, nextTick, watch,onMounted } from 'vue' import { useAppBridge } from '../assets/js/useAppBridge' import { useUserInfo } from '../store/userPermissionCode' const { getQueryVariable } = useUserInfo() const isTokenValid = ref(false) const fnGetToken = () => { localStorage.setItem('localToken', decodeURIComponent(String(getQueryVariable('token')))) console.log(localStorage.getItem('localToken')); } setTimeout(() => { fnGetToken() }, 800)
// 公共参数
const commonParams = { token: localStorage.getItem('localToken'), }
// 验证 token
const validateToken = async () => { const token = localStorage.getItem('localToken') if (!token) { console.error('未找到 token,请重新登录') return false } // const isValid = await validateTokenAPI(token)
// if (!isValid) {
// console.error('Token 无效,请重新登录')
// return false
// }
return true }
// 页面加载时验证 token
validateToken().then((isValid) => { isTokenValid.value = isValid if (!isValid) { // 可以在这里添加跳转到登录页等逻辑
console.error('Token 验证失败,请重新登录') } })
// 定义Props(可配置参数)
const props = defineProps({ apiUrl: { type: String, default: 'http://localhost:5000/ask' }, initialGreeting: { type: String, default: '您好!请问有什么可以帮助您?' } }) // 响应式数据
const messages = ref([ { content: props.initialGreeting, sender: 'bot', timestamp: new Date() } ]) const inputMessage = ref('') const messageContainer = ref(null) const isLoading = ref(false) // 新增:加载状态
// 自动滚动到底部
const scrollToBottom = () => { nextTick(() => { if (messageContainer.value) { messageContainer.value.scrollTop = messageContainer.value.scrollHeight } }) } // 发送消息
const sendMessage = async () => {
if (!isTokenValid.value) { console.error('Token 验证失败,无法发送消息') return }
if (isLoading.value) return; const content = inputMessage.value.trim() if (!content) return // 添加用户消息
messages.value.push({ content, sender: 'user', timestamp: new Date() }) // 清空输入框
inputMessage.value = '' scrollToBottom()
// 显示加载动画
isLoading.value = true messages.value.push({ content: '我正在思考...', sender: 'bot', timestamp: new Date(), isLoading: true }) scrollToBottom()
try { // 调用API获取回复
const response = await fetch(props.apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ question: content }) }) const data = await response.json()
// 移除加载消息
messages.value = messages.value.filter(msg => !msg.isLoading)
// 添加机器人回复
messages.value.push({ content: data.answer, sender: 'bot', timestamp: new Date() }) scrollToBottom() } catch (error) { console.error('API请求失败:', error) // 移除加载消息
messages.value = messages.value.filter(msg => !msg.isLoading)
messages.value.push({ content: '服务暂时不可用,请稍后再试', sender: 'bot', timestamp: new Date() }) scrollToBottom() } finally { // 隐藏加载动画
isLoading.value = false } } // 格式化时间显示
const formatTime = (date) => { return new Date(date).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) } // 自适应输入框高度
const adjustInputHeight = () => { const textarea = document.querySelector('.message-input') textarea.style.height = 'auto' textarea.style.height = `${textarea.scrollHeight}px` } // 监听输入内容变化
watch(inputMessage, adjustInputHeight)
// 在组件挂载后按顺序执行操作
onMounted(async () => { // 先获取 token
fnGetToken() // 再验证 token
const isValid = await validateToken() isTokenValid.value = isValid if (!isValid) { console.error('Token 验证失败,请重新登录') } })
</script> <template> <!-- 聊天容器 --> <div class="chat-container"> <!-- 聊天框头部 --> <div class="chat-header">夺宝奇兵智能客服</div> <!-- 消息展示区域 --> <div class="message-list" ref="messageContainer"> <div v-for="(message, index) in messages" :key="index" class="message-item" :class="[message.sender]"> <!-- 机器人头像 --> <div v-if="message.sender === 'bot'" class="bot-avatar"> <img src="/src/assets/img/avatar/超级云脑按钮.png" alt="Bot Avatar"> </div> <div class="message-bubble"> <div class="message-content"> <!-- 显示加载动画 --> <span v-if="message.isLoading"> {{ message.content }} <el-icon class="is-loading"> <Loading /> </el-icon> </span> <span v-else>{{ message.content }}</span> </div> <div class="message-time">{{ formatTime(message.timestamp) }}</div> </div> <!-- 用户头像 --> <div v-if="message.sender === 'user'" class="user-avatar"> <img src="/src/assets/img/avatar/小柒.png" alt="User Avatar"> </div> </div> </div> <!-- 输入区域 --> <div class="input-area"> <textarea v-model="inputMessage" @keydown.enter.exact.prevent="isLoading ? null : sendMessage()" placeholder="输入您的问题..." rows="1" class="message-input"></textarea> <el-tooltip content="机器人正在思考" :disabled="!isLoading"> <template #content> 机器人正在思考 </template> <button @click="sendMessage" :disabled="!isTokenValid || isLoading" class="send-button"> <!-- 使用ElementPlus的发送图标 --> <span v-if="isLoading"> <el-icon class="is-loading"> <Loading /> </el-icon> </span> <span v-else class="send-button-content"> <el-icon> <Position /> </el-icon> <span> 发送</span> </span> </button> </el-tooltip> </div> <!-- 未登录覆盖层 --> <div v-if="!isTokenValid" class="overlay"> <div class="overlay-content">用户未登录</div> </div> </div> </template>
<style scoped> .chat-container { display: flex; flex-direction: column; height: 90vh; width: 90vw; max-width: 800px; margin: 0; border: 1px solid #e0e0e0; border-radius: 12px; background: #f8f9fa; overflow: hidden; /* 新增样式,实现水平和垂直居中 */ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
/* 聊天框头部样式 */ .chat-header { background-color: #007bff; color: white; padding: 16px; font-size: 1.2rem; text-align: center; }
.message-list { flex: 1; padding: 20px; overflow-y: auto; background: white; }
.message-item { display: flex; margin-bottom: 16px; }
.message-item.user { justify-content: flex-end; }
.bot-avatar { margin-right: 10px; }
.bot-avatar img { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; }
.user-avatar { margin-left: 10px; }
.user-avatar img { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; }
.message-bubble { max-width: 80%; padding: 12px 16px; border-radius: 15px; position: relative; }
.message-content span { display: block; /* 确保元素显示 */ }
.message-item.user .message-bubble { background: #007bff; color: white; border-bottom-right-radius: 4px; }
.message-item.bot .message-bubble { background: #f1f3f5; color: #212529; border-bottom-left-radius: 4px; }
.message-time { font-size: 0.75rem; color: rgba(255, 255, 255, 0.8); margin-top: 4px; text-align: right; }
.message-item.bot .message-time { color: rgba(0, 0, 0, 0.6); }
.input-area { display: flex; align-items: center; gap: 12px; padding: 16px; border-top: 1px solid #e0e0e0; background: white; }
.message-input { flex: 1; padding: 10px 16px; border: 1px solid #e0e0e0; border-radius: 20px; resize: none; max-height: 120px; font-family: inherit; }
.send-button { display: flex; align-items: center; justify-content: center; width: 100px; /* 调整宽度以适应文字 */ height: 40px; border: none; border-radius: 20px; /* 调整圆角 */ background: #007bff; color: white; cursor: pointer; transition: all 0.3s ease; /* 添加过渡效果 */ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); /* 添加阴影 */ font-size: 16px; /* 调整字体大小 */ font-weight: 600; /* 调整字体粗细 */
}
.send-button:hover { background: #0056b3; transform: translateY(-2px); /* 悬停时向上移动 */ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 悬停时增加阴影 */ }
/* 新增加载状态样式 */ .loading-state { background: #ccc; cursor: not-allowed; }
.send-button-content { display: flex; align-items: center; justify-content: center; gap: 8px; /* 调整文字和图标间距 */ }
/* .send-button { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border: none; border-radius: 50%; background: #007bff; color: white; cursor: pointer; transition: background 0.2s; }
.send-button:hover { background: #0056b3; } */
.send-button svg { width: 20px; height: 20px; }
.overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(255, 255, 255, 0.8); /* 透明度 50% 的白色背景 */ display: flex; justify-content: center; align-items: center; z-index: 1; /* 确保覆盖层在聊天框上方 */ }
.overlay-content { font-size: 36px; font-weight: bold; color: #f60707; } </style>
|