Files
jjx-web/components/ai-chat/ai-chat.vue
T

226 lines
5.7 KiB
Vue
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.
<template>
<view v-if="visible" class="ai-chat-panel">
<!-- 头部 -->
<view class="chat-header">
<text>🤖 金久信 AI 智能客服</text>
<text class="close" @click="visible = false">×</text>
</view>
<!-- 消息列表 -->
<scroll-view scroll-y class="msg-list" :scroll-into-view="scrollId" scroll-with-animation>
<!-- 欢迎消息(仅在没有对话时显示) -->
<view v-if="messages.length === 0" class="msg-item assistant" id="msg-welcome">
<view class="bubble">
您好!我是金久信AI智能客服,可以帮您解答 平台注册、相关认证、平台操作、企业推广等问题。请问有什么可以帮您?
<view class="quick-btns">
<view class="quick-btn" @click="quickSend('使用说明书')">使用说明书</view>
<view class="quick-btn" @click="quickSend('开通金久信公证')">开通金久信公证</view>
<view class="quick-btn" @click="quickSend('如何下询价单')">如何下询价单</view>
<view class="quick-btn" @click="quickSend('如何报价')">如何报价</view>
</view>
</view>
</view>
<!-- 对话消息 -->
<view v-for="(msg, i) in messages" :key="i" :id="'msg' + i" :class="['msg-item', msg.role]">
<view class="bubble" v-html="formatContent(msg.content)"></view>
</view>
<view v-if="loading" class="msg-item assistant" id="msg-loading">
<view class="bubble typing">思考中...</view>
</view>
</scroll-view>
<!-- 输入框 -->
<view class="input-bar">
<input v-model="inputText" @confirm="send" placeholder="输入您的问题..." />
<button @click="send">发送</button>
</view>
</view>
</template>
<script>
export default {
name: "ai-chat",
data() {
return {
visible: false,
inputText: '',
messages: [],
loading: false,
conversationId: null,
scrollId: ''
};
},
methods: {
formatContent(text) {
if (!text) return '';
let html = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
html = html.replace(/^[\-\•]\s+(.+)$/gm, '<div style="padding-left:12px;">• $1</div>');
html = html.replace(/^(\d+)\.\s+(.+)$/gm, '<div style="padding-left:12px;">$1. $2</div>');
html = html.replace(/\n/g, '<br>');
return html;
},
toggle() {
this.visible = !this.visible;
if (this.visible) {
this.$nextTick(() => this.scrollToBottom());
}
},
quickSend(text) {
this.inputText = text;
this.send();
},
async send() {
const msg = this.inputText.trim();
if (!msg || this.loading) return;
this.inputText = '';
this.messages.push({
role: 'user',
content: msg
});
this.loading = true;
this.scrollToBottom();
try {
const res = await this.vk.callFunction({
url: 'client/ai/pub/chat',
data: { message: msg, bot_id: '7680345813062680618' }
});
if (res.data.conversation_id) this.conversationId = res.data.conversation_id;
this.messages.push({
role: 'assistant',
content: res.data.answer
});
} catch (e) {
this.messages.push({
role: 'assistant',
content: '网络异常,请稍后重试'
});
}
this.loading = false;
this.scrollToBottom();
},
scrollToBottom() {
this.$nextTick(() => {
setTimeout(() => {
if (this.messages.length === 0) {
this.scrollId = 'msg-welcome';
} else if (this.loading) {
this.scrollId = 'msg-loading';
} else {
this.scrollId = 'msg' + (this.messages.length - 1);
}
}, 50);
});
}
}
}
</script>
<style lang="scss">
.ai-chat-panel {
position: fixed;
bottom: 90px;
right: 50px;
width: 380px;
height: 520px;
background: #fff;
border-radius: 16px;
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.2);
z-index: 99999;
display: flex;
flex-direction: column;
overflow: hidden;
}
.chat-header {
padding: 12px 16px;
background: linear-gradient(135deg, #a92924, #d4574e);
color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
font-weight: 600;
}
.chat-header .close {
cursor: pointer;
font-size: 22px;
opacity: 0.8;
}
.chat-header .close:hover { opacity: 1; }
.msg-list {
flex: 1;
padding: 12px;
background: #f7f8fa;
overflow-y: auto;
}
.msg-item { margin-bottom: 10px; display: flex; }
.msg-item.user { justify-content: flex-end; }
.msg-item.assistant { justify-content: flex-start; }
.msg-item .bubble {
max-width: 75%;
padding: 10px 14px;
border-radius: 12px;
font-size: 13px;
line-height: 1.6;
word-break: break-word;
}
.msg-item.user .bubble {
background: #a92924;
color: #fff;
border-top-right-radius: 4px;
}
.msg-item.assistant .bubble {
background: #fff;
color: #333;
border: 1px solid #eee;
border-top-left-radius: 4px;
}
.msg-item.assistant .bubble.typing { color: #999; }
.quick-btns {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 10px;
}
.quick-btn {
padding: 5px 12px;
border-radius: 14px;
font-size: 12px;
cursor: pointer;
border: 1px solid #a92924;
color: #a92924;
background: #fff;
transition: all 0.2s;
}
.quick-btn:hover {
background: #a92924;
color: #fff;
}
.input-bar {
padding: 10px 12px;
border-top: 1px solid #eee;
display: flex;
gap: 8px;
background: #fff;
}
.input-bar input {
flex: 1;
height: 36px;
border: 1px solid #ddd;
border-radius: 8px;
padding: 0 10px;
font-size: 13px;
}
.input-bar input:focus { border-color: #a92924; outline: none; }
.input-bar button {
padding: 0 16px;
height: 36px;
background: #a92924;
color: #fff;
border: none;
border-radius: 8px;
font-size: 13px;
cursor: pointer;
}
.input-bar button:hover { background: #982520; }
</style>