chore: 添加AI聊天对话框

This commit is contained in:
2025-11-03 16:31:53 +08:00
parent c1d887a2e1
commit 2dfb2e0316
7 changed files with 2779 additions and 0 deletions

View File

@@ -0,0 +1,238 @@
# AI智能客服浮动按钮组件
## 组件介绍
AI智能客服浮动按钮组件是一个可以在任何页面中使用的浮动AI客服按钮点击按钮可以弹出完整的AI智能客服聊天界面。该组件基于现有的AI聊天消息组件提供了丰富的交互功能和配置选项。
## 功能特性
-**浮动按钮** - 可自定义位置的浮动AI客服按钮
-**聊天弹窗** - 点击按钮弹出完整的AI聊天界面
-**全屏模式** - 支持全屏/窗口模式切换
-**未读消息** - 显示未读消息数量小红点
-**多种消息类型** - 支持文本、文件、图片、音频、视频、链接等
-**流式对话** - 支持流式AI回复效果
-**响应式设计** - 适配不同屏幕尺寸
-**自定义配置** - 丰富的配置选项
## 安装使用
### 1. 引入组件
在需要使用AI客服浮动按钮的页面中引入组件
```json
// 页面配置文件 pages/xxx/xxx.json
{
"usingComponents": {
"ai-chat-float": "@/components/ai-chat-float/ai-chat-float"
}
}
```
### 2. 在页面中使用
```vue
<!-- 页面模板 pages/xxx/xxx.vue -->
<template>
<view class="page">
<!-- 页面内容 -->
<view class="content">
<!-- 您的页面内容 -->
</view>
<!-- AI客服浮动按钮 -->
<ai-chat-float
ref="aiChatFloat"
:position="position"
:show-float-button="showButton"
:auto-open="autoOpen"
:initial-messages="initialMessages"
@chat-open="onChatOpen"
@chat-close="onChatClose"
@message-sent="onMessageSent"
@ai-response="onAIResponse"
@unread-update="onUnreadUpdate" />
</view>
</template>
<script>
import aiChatFloat from '@/components/ai-chat-float/ai-chat-float.vue'
export default {
components: {
aiChatFloat
},
data() {
return {
position: 'bottom-right',
showButton: true,
autoOpen: false,
initialMessages: []
}
},
methods: {
onChatOpen() {
console.log('AI客服聊天窗口打开')
},
onChatClose() {
console.log('AI客服聊天窗口关闭')
},
onMessageSent(message) {
console.log('用户发送消息:', message)
},
onAIResponse(message) {
console.log('AI回复消息:', message)
},
onUnreadUpdate(count) {
console.log('未读消息数量:', count)
}
}
}
</script>
```
## 组件属性
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| position | String | 'bottom-right' | 按钮位置,可选值:'bottom-right', 'bottom-left', 'top-right', 'top-left' |
| offset | Object | { bottom: '100rpx', right: '40rpx', top: '100rpx', left: '40rpx' } | 按钮距离边缘的距离 |
| userAvatar | String | '/static/images/default-avatar.png' | 用户头像 |
| aiAvatar | String | '/static/images/ai-avatar.png' | AI头像 |
| initialMessages | Array | [] | 初始聊天消息 |
| showFloatButton | Boolean | true | 是否显示浮动按钮 |
| autoOpen | Boolean | false | 是否自动打开聊天窗口 |
## 事件监听
| 事件名 | 参数 | 说明 |
|--------|------|------|
| chat-open | - | 聊天窗口打开事件 |
| chat-close | - | 聊天窗口关闭事件 |
| message-sent | message | 用户发送消息事件 |
| ai-response | message | AI回复消息事件 |
| unread-update | count | 未读消息数量更新事件 |
| history-loaded | messages | 历史消息加载完成事件 |
| file-preview | message | 文件预览事件 |
| audio-play | message | 音频播放事件 |
| video-play | message | 视频播放事件 |
| link-open | message | 链接打开事件 |
| product-view | message | 商品查看事件 |
| action-click | {action, message} | 操作按钮点击事件 |
| input-change | value | 输入内容变化事件 |
## 方法调用
通过组件引用可以调用以下方法:
```javascript
// 获取组件引用
const aiChatFloat = this.$refs.aiChatFloat
// 打开聊天窗口
aiChatFloat.openChat()
// 关闭聊天窗口
aiChatFloat.closeChat()
// 添加消息
aiChatFloat.addMessage({
role: 'ai',
type: 'text',
content: '这是一条新消息',
read: false
})
// 清空聊天记录
aiChatFloat.clearMessages()
// 标记所有消息为已读
aiChatFloat.markAllAsRead()
```
## 配置示例
### 基本配置
```vue
<ai-chat-float
position="bottom-right"
:show-float-button="true"
:auto-open="false" />
```
### 自定义位置
```vue
<ai-chat-float
position="top-left"
:offset="{ top: '200rpx', left: '60rpx' }" />
```
### 自定义头像
```vue
<ai-chat-float
:user-avatar="/static/avatars/user.jpg"
:ai-avatar="/static/avatars/ai.jpg" />
```
### 初始消息
```vue
<ai-chat-float
:initial-messages="[
{
id: 1,
role: 'ai',
type: 'text',
content: '欢迎使用AI客服',
timestamp: Date.now(),
read: false
}
]" />
```
## 样式定制
组件支持通过CSS变量进行样式定制
```css
/* 自定义主题色 */
.ai-float-button {
--primary-color: #ff4544;
--primary-gradient: linear-gradient(135deg, #ff4544, #ff6b6b);
}
/* 自定义尺寸 */
.ai-float-button {
--button-size: 120rpx;
--icon-size: 48rpx;
}
```
## 注意事项
1. **依赖组件**:本组件依赖 `ai-chat-message` 组件,请确保该组件已正确安装
2. **图标字体**:组件使用了 iconfont 图标,请确保项目中已引入相应的图标字体
3. **响应式设计**组件已适配移动端和PC端但在特殊场景下可能需要额外调整
4. **性能优化**:在大量消息时建议启用消息数量限制
## 演示页面
访问 `/pages/ai-chat-float/index` 查看完整的演示效果,包含所有配置选项的实时预览。
## 更新日志
### v1.0.0 (2024-11-03)
- ✨ 初始版本发布
- ✨ 支持浮动按钮和聊天弹窗
- ✨ 支持多种消息类型
- ✨ 支持流式对话
- ✨ 支持全屏模式
- ✨ 支持未读消息提示
## 技术支持
如有问题或建议,请联系开发团队。

View File

@@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"ai-chat-message": "../ai-chat-message/ai-chat-message"
}
}

View File

@@ -0,0 +1,546 @@
<template>
<view>
<!-- 浮动AI客服按钮 -->
<view
v-if="showFloatButton"
class="ai-float-button"
:class="{ 'ai-float-button-mini': isChatOpen }"
:style="buttonStyle"
@click="toggleChat">
<!-- 按钮图标 -->
<view class="ai-float-icon">
<text class="iconfont" :class="isChatOpen ? 'icon-close' : 'icon-ai'"></text>
</view>
<!-- 未读消息小红点 -->
<view v-if="unreadCount > 0" class="ai-float-badge">
{{ unreadCount > 99 ? '99+' : unreadCount }}
</view>
<!-- 按钮文字 -->
<view v-if="!isChatOpen" class="ai-float-text">AI客服</view>
</view>
<!-- AI聊天弹窗 -->
<view
v-if="isChatOpen"
class="ai-chat-modal"
:class="{ 'ai-chat-modal-fullscreen': isFullscreen }">
<!-- 遮罩层 -->
<view class="ai-chat-mask" @click="closeChat"></view>
<!-- 聊天窗口 -->
<view class="ai-chat-window">
<!-- 聊天窗口头部 -->
<view class="ai-chat-header">
<view class="ai-chat-title">
<text class="iconfont icon-ai"></text>
<text>AI智能客服</text>
</view>
<view class="ai-chat-actions">
<!-- 全屏/最小化按钮 -->
<button class="action-btn" @click="toggleFullscreen">
<text class="iconfont" :class="isFullscreen ? 'icon-minimize' : 'icon-fullscreen'"></text>
</button>
<!-- 关闭按钮 -->
<button class="action-btn" @click="closeChat">
<text class="iconfont icon-close"></text>
</button>
</view>
</view>
<!-- 聊天内容区域 -->
<view class="ai-chat-content">
<ai-chat-message
ref="chatMessage"
:initial-messages="chatMessages"
:user-avatar="userAvatar"
:ai-avatar="aiAvatar"
:enable-streaming="true"
@message-sent="onMessageSent"
@ai-response="onAIResponse"
@history-loaded="onHistoryLoaded"
@file-preview="onFilePreview"
@audio-play="onAudioPlay"
@video-play="onVideoPlay"
@link-open="onLinkOpen"
@product-view="onProductView"
@action-click="onActionClick"
@input-change="onInputChange" />
</view>
</view>
</view>
</view>
</template>
<script>
import aiChatMessage from '../ai-chat-message/ai-chat-message.vue'
export default {
name: 'ai-chat-float',
components: {
aiChatMessage
},
props: {
// 浮动按钮位置
position: {
type: String,
default: 'bottom-right', // bottom-right, bottom-left, top-right, top-left
validator: (value) => {
return ['bottom-right', 'bottom-left', 'top-right', 'top-left'].includes(value)
}
},
// 按钮距离边缘的距离
offset: {
type: Object,
default: () => ({
bottom: '100rpx',
right: '40rpx',
top: '100rpx',
left: '40rpx'
})
},
// 用户头像
userAvatar: {
type: String,
default: '/static/images/default-avatar.png'
},
// AI头像
aiAvatar: {
type: String,
default: '/static/images/ai-avatar.png'
},
// 初始聊天消息
initialMessages: {
type: Array,
default: () => []
},
// 是否显示浮动按钮
showFloatButton: {
type: Boolean,
default: true
},
// 是否自动打开聊天窗口
autoOpen: {
type: Boolean,
default: false
}
},
data() {
return {
isChatOpen: false, // 聊天窗口是否打开
isFullscreen: false, // 是否全屏
unreadCount: 0, // 未读消息数量
chatMessages: [], // 聊天消息列表
messageId: 0 // 消息ID计数器
}
},
computed: {
// 按钮样式
buttonStyle() {
const style = {}
// 根据位置设置样式
if (this.position.includes('bottom')) {
style.bottom = this.offset.bottom
} else {
style.top = this.offset.top
}
if (this.position.includes('right')) {
style.right = this.offset.right
} else {
style.left = this.offset.left
}
return style
}
},
created() {
// 初始化聊天消息
this.chatMessages = [...this.initialMessages]
this.messageId = this.chatMessages.length
// 如果有初始消息,计算未读数量
this.updateUnreadCount()
// 如果设置了自动打开,则打开聊天窗口
if (this.autoOpen) {
this.openChat()
}
},
methods: {
// 切换聊天窗口
toggleChat() {
if (this.isChatOpen) {
this.closeChat()
} else {
this.openChat()
}
},
// 打开聊天窗口
openChat() {
this.isChatOpen = true
this.unreadCount = 0 // 打开后清空未读消息
this.$emit('chat-open')
},
// 关闭聊天窗口
closeChat() {
this.isChatOpen = false
this.isFullscreen = false
this.$emit('chat-close')
},
// 切换全屏模式
toggleFullscreen() {
this.isFullscreen = !this.isFullscreen
this.$emit('fullscreen-toggle', this.isFullscreen)
},
// 更新未读消息数量
updateUnreadCount() {
// 计算AI发送的未读消息数量
this.unreadCount = this.chatMessages.filter(msg =>
msg.role === 'ai' && !msg.read
).length
this.$emit('unread-update', this.unreadCount)
},
// 发送欢迎消息
sendWelcomeMessage() {
const welcomeMessage = {
id: ++this.messageId,
role: 'ai',
type: 'text',
content: '您好我是AI智能客服很高兴为您服务有什么可以帮助您的吗',
timestamp: Date.now(),
read: false
}
this.chatMessages.push(welcomeMessage)
this.updateUnreadCount()
},
// 消息发送事件
onMessageSent(message) {
this.$emit('message-sent', message)
},
// AI回复事件
onAIResponse(message) {
// 标记为未读
message.read = false
this.updateUnreadCount()
this.$emit('ai-response', message)
},
// 历史消息加载事件
onHistoryLoaded(messages) {
this.$emit('history-loaded', messages)
},
// 文件预览事件
onFilePreview(message) {
this.$emit('file-preview', message)
},
// 音频播放事件
onAudioPlay(message) {
this.$emit('audio-play', message)
},
// 视频播放事件
onVideoPlay(message) {
this.$emit('video-play', message)
},
// 链接打开事件
onLinkOpen(message) {
this.$emit('link-open', message)
},
// 商品查看事件
onProductView(message) {
this.$emit('product-view', message)
},
// 操作点击事件
onActionClick(data) {
this.$emit('action-click', data)
},
// 输入变化事件
onInputChange(value) {
this.$emit('input-change', value)
},
// 添加消息到聊天
addMessage(message) {
this.chatMessages.push({
...message,
id: ++this.messageId,
timestamp: message.timestamp || Date.now()
})
// 如果是AI消息增加未读计数
if (message.role === 'ai' && !message.read) {
this.updateUnreadCount()
}
},
// 清空聊天记录
clearMessages() {
this.chatMessages = []
this.messageId = 0
this.unreadCount = 0
this.$emit('messages-clear')
},
// 标记所有消息为已读
markAllAsRead() {
this.chatMessages.forEach(msg => {
if (msg.role === 'ai') {
msg.read = true
}
})
this.updateUnreadCount()
this.$emit('messages-read')
}
}
}
</script>
<style lang="scss" scoped>
/* 浮动按钮样式 */
.ai-float-button {
position: fixed;
z-index: 9998;
display: flex;
align-items: center;
background: linear-gradient(135deg, #ff4544, #ff6b6b);
border-radius: 50rpx;
padding: 20rpx 30rpx;
box-shadow: 0 8rpx 30rpx rgba(255, 69, 68, 0.3);
transition: all 0.3s ease;
cursor: pointer;
&.ai-float-button-mini {
padding: 20rpx;
border-radius: 50%;
.ai-float-text {
display: none;
}
}
&:active {
transform: scale(0.95);
box-shadow: 0 4rpx 15rpx rgba(255, 69, 68, 0.5);
}
}
.ai-float-icon {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15rpx;
.iconfont {
font-size: 40rpx;
color: white;
}
}
.ai-float-badge {
position: absolute;
top: -10rpx;
right: -10rpx;
background-color: #ff4544;
color: white;
border-radius: 30rpx;
padding: 4rpx 12rpx;
font-size: 20rpx;
font-weight: bold;
min-width: 30rpx;
text-align: center;
border: 2rpx solid white;
}
.ai-float-text {
color: white;
font-size: 28rpx;
font-weight: bold;
white-space: nowrap;
}
/* 聊天弹窗样式 */
.ai-chat-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
&.ai-chat-modal-fullscreen {
.ai-chat-window {
width: 100vw;
height: 100vh;
border-radius: 0;
max-width: none;
max-height: none;
}
}
}
.ai-chat-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
animation: fadeIn 0.3s ease;
}
.ai-chat-window {
position: absolute;
bottom: 150rpx;
right: 40rpx;
width: 600rpx;
height: 800rpx;
max-width: 90vw;
max-height: 80vh;
background-color: white;
border-radius: 20rpx;
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
overflow: hidden;
animation: slideUp 0.3s ease;
/* 响应式调整 */
@media (max-width: 750rpx) {
width: 90vw;
height: 70vh;
bottom: 100rpx;
right: 5vw;
}
}
/* 聊天窗口头部 */
.ai-chat-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
background: linear-gradient(135deg, #ff4544, #ff6b6b);
color: white;
.ai-chat-title {
display: flex;
align-items: center;
font-size: 32rpx;
font-weight: bold;
.iconfont {
font-size: 36rpx;
margin-right: 15rpx;
}
}
.ai-chat-actions {
display: flex;
gap: 15rpx;
.action-btn {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
border: none;
.iconfont {
font-size: 28rpx;
color: white;
}
&:active {
background-color: rgba(255, 255, 255, 0.3);
}
}
}
}
/* 聊天内容区域 */
.ai-chat-content {
flex: 1;
overflow: hidden;
/* 确保聊天组件充满整个区域 */
::v-deep .ai-chat-container {
height: 100%;
}
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideUp {
from {
transform: translateY(100rpx) scale(0.9);
opacity: 0;
}
to {
transform: translateY(0) scale(1);
opacity: 1;
}
}
/* 响应式设计 */
@media (max-width: 750rpx) {
.ai-float-button {
padding: 25rpx;
&.ai-float-button-mini {
padding: 25rpx;
}
.ai-float-text {
font-size: 24rpx;
}
}
.ai-chat-window {
border-radius: 15rpx;
}
.ai-chat-header {
padding: 25rpx;
.ai-chat-title {
font-size: 28rpx;
}
}
}
</style>