chore: 添加AI聊天对话框
This commit is contained in:
238
components/ai-chat-float/README.md
Normal file
238
components/ai-chat-float/README.md
Normal 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)
|
||||
- ✨ 初始版本发布
|
||||
- ✨ 支持浮动按钮和聊天弹窗
|
||||
- ✨ 支持多种消息类型
|
||||
- ✨ 支持流式对话
|
||||
- ✨ 支持全屏模式
|
||||
- ✨ 支持未读消息提示
|
||||
|
||||
## 技术支持
|
||||
|
||||
如有问题或建议,请联系开发团队。
|
||||
6
components/ai-chat-float/ai-chat-float.json
Normal file
6
components/ai-chat-float/ai-chat-float.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"ai-chat-message": "../ai-chat-message/ai-chat-message"
|
||||
}
|
||||
}
|
||||
546
components/ai-chat-float/ai-chat-float.vue
Normal file
546
components/ai-chat-float/ai-chat-float.vue
Normal 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>
|
||||
271
components/ai-chat-message/README.md
Normal file
271
components/ai-chat-message/README.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# AI智能客服组件
|
||||
|
||||
一个功能完整的AI智能客服对话组件,支持多种消息类型和交互功能。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- ✅ 支持对话上下文管理
|
||||
- ✅ 支持多种消息类型:文本、Markdown、文件、音频、视频、链接、商品卡片
|
||||
- ✅ 支持语音输入和录音
|
||||
- ✅ 支持图片、文件、位置等附件发送
|
||||
- ✅ 支持消息操作按钮(点赞、踩等)
|
||||
- ✅ 支持历史消息加载
|
||||
- ✅ 响应式设计,适配多端
|
||||
|
||||
## 安装使用
|
||||
|
||||
### 1. 引入组件
|
||||
|
||||
在 `pages.json` 中注册组件:
|
||||
|
||||
```json
|
||||
{
|
||||
"usingComponents": {
|
||||
"ai-chat-message": "/components/ai-chat-message/ai-chat-message"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 在页面中使用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view class="container">
|
||||
<ai-chat-message
|
||||
ref="chat"
|
||||
:initial-messages="messages"
|
||||
@message-sent="onMessageSent"
|
||||
@ai-response="onAIResponse"
|
||||
@action-click="onActionClick" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
id: 1,
|
||||
role: 'ai',
|
||||
type: 'text',
|
||||
content: '您好!我是AI智能客服,有什么可以帮助您的吗?',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onMessageSent(message) {
|
||||
console.log('用户发送消息:', message)
|
||||
},
|
||||
onAIResponse(message) {
|
||||
console.log('AI回复消息:', message)
|
||||
},
|
||||
onActionClick({ action, message }) {
|
||||
console.log('操作点击:', action, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Props 配置
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| initialMessages | Array | [] | 初始消息列表 |
|
||||
| userAvatar | String | '/static/images/default-avatar.png' | 用户头像 |
|
||||
| aiAvatar | String | '/static/images/ai-avatar.png' | AI头像 |
|
||||
| showLoadMore | Boolean | true | 是否显示加载更多 |
|
||||
| maxMessages | Number | 100 | 最大消息数量 |
|
||||
|
||||
## Events 事件
|
||||
|
||||
| 事件名 | 参数 | 说明 |
|
||||
|--------|------|------|
|
||||
| message-sent | message | 用户发送消息 |
|
||||
| ai-response | message | AI回复消息 |
|
||||
| action-click | {action, message} | 操作按钮点击 |
|
||||
| history-loaded | messages | 历史消息加载完成 |
|
||||
| file-preview | message | 文件预览 |
|
||||
| audio-play | message | 音频播放 |
|
||||
| audio-pause | message | 音频暂停 |
|
||||
| video-play | message | 视频播放 |
|
||||
| video-pause | message | 视频暂停 |
|
||||
| link-open | message | 链接打开 |
|
||||
| product-view | message | 商品查看 |
|
||||
| input-change | value | 输入内容变化 |
|
||||
|
||||
## 消息类型格式
|
||||
|
||||
### 文本消息
|
||||
```javascript
|
||||
{
|
||||
id: 1,
|
||||
role: 'user', // 或 'ai'
|
||||
type: 'text',
|
||||
content: '消息内容',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
```
|
||||
|
||||
### Markdown消息
|
||||
```javascript
|
||||
{
|
||||
id: 2,
|
||||
role: 'ai',
|
||||
type: 'markdown',
|
||||
content: '# 标题\n**粗体** *斜体* `代码`',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
```
|
||||
|
||||
### 文件消息
|
||||
```javascript
|
||||
{
|
||||
id: 3,
|
||||
role: 'ai',
|
||||
type: 'file',
|
||||
fileName: '文档.pdf',
|
||||
fileSize: 1024000,
|
||||
url: '文件地址',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
```
|
||||
|
||||
### 音频消息
|
||||
```javascript
|
||||
{
|
||||
id: 4,
|
||||
role: 'ai',
|
||||
type: 'audio',
|
||||
title: '语音消息',
|
||||
duration: 60, // 秒
|
||||
url: '音频地址',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
```
|
||||
|
||||
### 视频消息
|
||||
```javascript
|
||||
{
|
||||
id: 5,
|
||||
role: 'ai',
|
||||
type: 'video',
|
||||
title: '产品介绍',
|
||||
duration: 120, // 秒
|
||||
url: '视频地址',
|
||||
cover: '封面图',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
```
|
||||
|
||||
### 链接消息
|
||||
```javascript
|
||||
{
|
||||
id: 6,
|
||||
role: 'ai',
|
||||
type: 'link',
|
||||
title: '帮助文档',
|
||||
description: '详细的使用说明',
|
||||
url: 'https://example.com',
|
||||
image: '缩略图',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
```
|
||||
|
||||
### 商品卡片
|
||||
```javascript
|
||||
{
|
||||
id: 7,
|
||||
role: 'ai',
|
||||
type: 'product',
|
||||
title: '商品名称',
|
||||
price: 299,
|
||||
description: '商品描述',
|
||||
image: '商品图片',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
```
|
||||
|
||||
## 方法
|
||||
|
||||
通过 ref 调用组件方法:
|
||||
|
||||
```javascript
|
||||
// 添加消息
|
||||
this.$refs.chat.addMessage(message)
|
||||
|
||||
// 清空消息
|
||||
this.$refs.chat.clearMessages()
|
||||
|
||||
// 滚动到底部
|
||||
this.$refs.chat.scrollToBottom()
|
||||
```
|
||||
|
||||
## 样式定制
|
||||
|
||||
组件使用 SCSS 编写,可以通过 CSS 变量进行主题定制:
|
||||
|
||||
```css
|
||||
.ai-chat-container {
|
||||
--primary-color: #ff4544;
|
||||
--bg-color: #f8f8f8;
|
||||
--text-color: #333;
|
||||
}
|
||||
```
|
||||
|
||||
## 方法
|
||||
|
||||
通过 ref 调用组件方法:
|
||||
|
||||
```javascript
|
||||
// 添加消息
|
||||
this.$refs.chat.addMessage(message)
|
||||
|
||||
// 清空消息
|
||||
this.$refs.chat.clearMessages()
|
||||
|
||||
// 滚动到底部
|
||||
this.$refs.chat.scrollToBottom()
|
||||
|
||||
// 开始语音输入
|
||||
this.$refs.chat.startVoiceInput()
|
||||
|
||||
// 停止语音输入
|
||||
this.$refs.chat.stopVoiceInput()
|
||||
```
|
||||
|
||||
## 样式定制
|
||||
|
||||
组件使用 SCSS 编写,可以通过 CSS 变量进行主题定制:
|
||||
|
||||
```css
|
||||
.ai-chat-container {
|
||||
--primary-color: #ff4544;
|
||||
--bg-color: #f8f8f8;
|
||||
--text-color: #333;
|
||||
--border-color: #eeeeee;
|
||||
--user-bg: #e6f7ff;
|
||||
--ai-bg: #f6f6f6;
|
||||
}
|
||||
```
|
||||
|
||||
## 图标字体
|
||||
|
||||
组件使用自定义图标字体,需要在页面中引入:
|
||||
|
||||
```html
|
||||
<style>
|
||||
@import url('/components/ai-chat-message/iconfont.css');
|
||||
</style>
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 组件已适配项目中已有的 `ns-loading` 组件
|
||||
2. 需要配置对应的图标字体文件
|
||||
3. 音频播放功能在 H5 和 APP 端支持较好
|
||||
4. 文件预览功能依赖平台能力
|
||||
5. 语音输入功能需要用户授权麦克风权限
|
||||
6
components/ai-chat-message/ai-chat-message.json
Normal file
6
components/ai-chat-message/ai-chat-message.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"ns-loading": "../ns-loading/ns-loading"
|
||||
}
|
||||
}
|
||||
1424
components/ai-chat-message/ai-chat-message.vue
Normal file
1424
components/ai-chat-message/ai-chat-message.vue
Normal file
File diff suppressed because it is too large
Load Diff
288
components/ai-chat-message/demo.vue
Normal file
288
components/ai-chat-message/demo.vue
Normal file
@@ -0,0 +1,288 @@
|
||||
<template>
|
||||
<view class="demo-container">
|
||||
<view class="demo-header">
|
||||
<text class="demo-title">AI智能客服组件演示</text>
|
||||
</view>
|
||||
|
||||
<ai-chat-message
|
||||
ref="chat"
|
||||
:initial-messages="demoMessages"
|
||||
user-avatar="/static/images/demo-user.png"
|
||||
ai-avatar="/static/images/demo-ai.png"
|
||||
@message-sent="onMessageSent"
|
||||
@ai-response="onAIResponse"
|
||||
@action-click="onActionClick"
|
||||
@file-preview="onFilePreview"
|
||||
@audio-play="onAudioPlay"
|
||||
@video-play="onVideoPlay"
|
||||
@link-open="onLinkOpen"
|
||||
@product-view="onProductView" />
|
||||
|
||||
<view class="demo-controls">
|
||||
<button class="control-btn" @click="addTextMessage">添加文本消息</button>
|
||||
<button class="control-btn" @click="addMarkdownMessage">添加Markdown</button>
|
||||
<button class="control-btn" @click="addFileMessage">添加文件</button>
|
||||
<button class="control-btn" @click="addAudioMessage">添加音频</button>
|
||||
<button class="control-btn" @click="addVideoMessage">添加视频</button>
|
||||
<button class="control-btn" @click="addLinkMessage">添加链接</button>
|
||||
<button class="control-btn" @click="addProductMessage">添加商品</button>
|
||||
<button class="control-btn" @click="clearMessages">清空消息</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
demoMessages: [
|
||||
{
|
||||
id: 1,
|
||||
role: 'ai',
|
||||
type: 'text',
|
||||
content: '您好!我是AI智能客服演示程序,我可以展示多种消息类型。请点击下方的按钮体验不同功能!',
|
||||
timestamp: Date.now() - 300000,
|
||||
actions: [
|
||||
{ id: 1, text: '开始体验', type: 'like' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 添加文本消息
|
||||
addTextMessage() {
|
||||
const message = {
|
||||
id: Date.now(),
|
||||
role: 'ai',
|
||||
type: 'text',
|
||||
content: '这是一个文本消息示例,支持**粗体**、*斜体*和`代码`格式。也可以包含换行符\n这是第二行内容。',
|
||||
timestamp: Date.now(),
|
||||
actions: [
|
||||
{ id: 1, text: '有帮助', type: 'like' },
|
||||
{ id: 2, text: '没帮助', type: 'dislike' }
|
||||
]
|
||||
}
|
||||
this.$refs.chat.messages.push(message)
|
||||
},
|
||||
|
||||
// 添加Markdown消息
|
||||
addMarkdownMessage() {
|
||||
const message = {
|
||||
id: Date.now(),
|
||||
role: 'ai',
|
||||
type: 'markdown',
|
||||
content: `# Markdown文档示例
|
||||
|
||||
## 二级标题
|
||||
|
||||
这是一个支持**粗体**、*斜体*和\`代码\`的Markdown消息。
|
||||
|
||||
### 功能特性
|
||||
- ✅ 支持标题层级
|
||||
- ✅ 支持列表展示
|
||||
- ✅ 支持代码高亮
|
||||
- ✅ 支持链接跳转
|
||||
|
||||
[查看详细文档](https://example.com)\n\n**注意:** 这是一个演示内容,实际使用时可以集成真实的文档系统。`,
|
||||
timestamp: Date.now(),
|
||||
actions: [
|
||||
{ id: 1, text: '查看文档', type: 'like' }
|
||||
]
|
||||
}
|
||||
this.$refs.chat.messages.push(message)
|
||||
},
|
||||
|
||||
// 添加文件消息
|
||||
addFileMessage() {
|
||||
const message = {
|
||||
id: Date.now(),
|
||||
role: 'ai',
|
||||
type: 'file',
|
||||
fileName: '产品介绍文档.pdf',
|
||||
fileSize: 2048000,
|
||||
url: 'https://example.com/document.pdf',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
this.$refs.chat.messages.push(message)
|
||||
},
|
||||
|
||||
// 添加音频消息
|
||||
addAudioMessage() {
|
||||
const message = {
|
||||
id: Date.now(),
|
||||
role: 'ai',
|
||||
type: 'audio',
|
||||
title: '产品功能介绍',
|
||||
duration: 89,
|
||||
url: 'https://example.com/audio.mp3',
|
||||
timestamp: Date.now(),
|
||||
playing: false,
|
||||
currentTime: 0
|
||||
}
|
||||
this.$refs.chat.messages.push(message)
|
||||
},
|
||||
|
||||
// 添加视频消息
|
||||
addVideoMessage() {
|
||||
const message = {
|
||||
id: Date.now(),
|
||||
role: 'ai',
|
||||
type: 'video',
|
||||
title: '产品演示视频',
|
||||
duration: 180,
|
||||
url: 'https://example.com/video.mp4',
|
||||
cover: '/static/images/video-cover.jpg',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
this.$refs.chat.messages.push(message)
|
||||
},
|
||||
|
||||
// 添加链接消息
|
||||
addLinkMessage() {
|
||||
const message = {
|
||||
id: Date.now(),
|
||||
role: 'ai',
|
||||
type: 'link',
|
||||
title: '帮助中心',
|
||||
description: '详细的产品使用说明和常见问题解答',
|
||||
url: 'https://example.com/help',
|
||||
image: '/static/images/link-thumbnail.jpg',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
this.$refs.chat.messages.push(message)
|
||||
},
|
||||
|
||||
// 添加商品消息
|
||||
addProductMessage() {
|
||||
const message = {
|
||||
id: Date.now(),
|
||||
role: 'ai',
|
||||
type: 'product',
|
||||
title: '智能手表 X1',
|
||||
price: 1299,
|
||||
description: '全新一代智能手表,支持心率监测、运动追踪、消息提醒等功能',
|
||||
image: '/static/images/product-demo.jpg',
|
||||
timestamp: Date.now(),
|
||||
actions: [
|
||||
{ id: 1, text: '立即购买', type: 'like' },
|
||||
{ id: 2, text: '查看详情', type: 'dislike' }
|
||||
]
|
||||
}
|
||||
this.$refs.chat.messages.push(message)
|
||||
},
|
||||
|
||||
// 清空消息
|
||||
clearMessages() {
|
||||
this.$refs.chat.messages = [
|
||||
{
|
||||
id: 1,
|
||||
role: 'ai',
|
||||
type: 'text',
|
||||
content: '消息已清空,可以重新开始体验!',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// 事件处理
|
||||
onMessageSent(message) {
|
||||
console.log('用户发送消息:', message)
|
||||
uni.showToast({
|
||||
title: '消息发送成功',
|
||||
icon: 'success'
|
||||
})
|
||||
},
|
||||
|
||||
onAIResponse(message) {
|
||||
console.log('AI回复消息:', message)
|
||||
},
|
||||
|
||||
onActionClick({ action, message }) {
|
||||
console.log('操作点击:', action, message)
|
||||
uni.showToast({
|
||||
title: `点击了: ${action.text}`,
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
|
||||
onFilePreview(message) {
|
||||
console.log('文件预览:', message)
|
||||
uni.showToast({
|
||||
title: '正在打开文件...',
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
|
||||
onAudioPlay(message) {
|
||||
console.log('音频播放:', message)
|
||||
},
|
||||
|
||||
onVideoPlay(message) {
|
||||
console.log('视频播放:', message)
|
||||
},
|
||||
|
||||
onLinkOpen(message) {
|
||||
console.log('链接打开:', message)
|
||||
uni.showToast({
|
||||
title: '正在打开链接...',
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
|
||||
onProductView(message) {
|
||||
console.log('商品查看:', message)
|
||||
uni.showToast({
|
||||
title: '正在查看商品...',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.demo-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
background-color: #ff4544;
|
||||
color: white;
|
||||
padding: 30rpx;
|
||||
text-align: center;
|
||||
|
||||
.demo-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-controls {
|
||||
background-color: white;
|
||||
padding: 20rpx;
|
||||
border-top: 2rpx solid #eeeeee;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10rpx;
|
||||
|
||||
.control-btn {
|
||||
flex: 1;
|
||||
min-width: 150rpx;
|
||||
height: 60rpx;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 30rpx;
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
border: none;
|
||||
|
||||
&:active {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user