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

229 lines
5.9 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 '';
// 处理 markdown 加粗 **text**
let html = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
// 处理列表项 - xxx 或 • xxx
html = html.replace(/^[\-\•]\s+(.+)$/gm, '<div style="padding-left:12px;">• $1</div>');
// 处理数字列表 1. xxx
html = html.replace(/^(\d+)\.\s+(.+)$/gm, '<div style="padding-left:12px;">$1. $2</div>');
// 换行转 <br>
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: '7683509749475819554' }
});
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>