feat(ai-chat): 添加聊天记录搜索与远程欢迎语功能
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import Config from './config.js'
|
import Config from './config.js'
|
||||||
import http from './http.js'
|
import http from './http.js'
|
||||||
import store from '@/store/index.js'
|
import store from '@/store/index.js'
|
||||||
|
import lang from './lang.js'
|
||||||
|
|
||||||
let currentConversationId = null;
|
let currentConversationId = null;
|
||||||
export const CONVERSATION_KEY = 'ai_conversation_id';
|
export const CONVERSATION_KEY = 'ai_conversation_id';
|
||||||
@@ -524,6 +525,34 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务配置信息
|
||||||
|
*/
|
||||||
|
async getServiceConfig() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
url: '/api/kefu/info',
|
||||||
|
data: {
|
||||||
|
uniacid: this.getUniacid(),
|
||||||
|
token: store.state.token || 'test_token',
|
||||||
|
user_id: getUserId(),
|
||||||
|
member_id: getUserId(),
|
||||||
|
locale: lang.getCurrentLocale()
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const response = await http.sendRequest({
|
||||||
|
...params,
|
||||||
|
async: false,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除会话历史(调用后端接口)
|
* 清除会话历史(调用后端接口)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="ai-chat-container" :style="aiChatContainerStyle">
|
<view class="ai-chat-container" :style="aiChatContainerStyle">
|
||||||
|
<!-- 搜索入口按钮(浮动在消息区右上角) -->
|
||||||
|
<view class="search-entrance" v-if="!showSearchBar" @click="toggleSearch">
|
||||||
|
<text class="search-entrance-icon">🔍</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 聊天消息列表 - 统一使用 scroll-into-view -->
|
<!-- 聊天消息列表 - 统一使用 scroll-into-view -->
|
||||||
<scroll-view
|
<scroll-view
|
||||||
class="chat-messages"
|
class="chat-messages"
|
||||||
@@ -45,7 +50,8 @@
|
|||||||
@touchmove="onBubbleTouchMove"
|
@touchmove="onBubbleTouchMove"
|
||||||
@touchend="onBubbleTouchEnd"
|
@touchend="onBubbleTouchEnd"
|
||||||
@touchcancel="onBubbleTouchEnd">
|
@touchcancel="onBubbleTouchEnd">
|
||||||
<text class="message-text" selectable>{{ message.content }}</text>
|
<rich-text v-if="searchKeyword" :nodes="highlightContent(message.content)" class="message-rich-text" />
|
||||||
|
<text v-else class="message-text" selectable>{{ message.content }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -76,17 +82,17 @@
|
|||||||
@touchcancel="onBubbleTouchEnd">
|
@touchcancel="onBubbleTouchEnd">
|
||||||
<!-- 文本消息 -->
|
<!-- 文本消息 -->
|
||||||
<view v-if="message.type === 'text'" class="text-content">
|
<view v-if="message.type === 'text'" class="text-content">
|
||||||
<rich-text :nodes="parseMarkdown(message.content)" />
|
<rich-text :nodes="getAIDisplayContent(message.content)" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Markdown文档 -->
|
<!-- Markdown文档 -->
|
||||||
<view v-else-if="message.type === 'markdown'" class="markdown-content">
|
<view v-else-if="message.type === 'markdown'" class="markdown-content">
|
||||||
<view class="markdown-header">
|
<view class="markdown-header">
|
||||||
<text class="iconfont icon-markdown"></text>
|
<text class="iconfont icon-markdown"></text>
|
||||||
<text class="markdown-title">文档内容</text>
|
<text class="markdown-title"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="markdown-body">
|
<view class="markdown-body">
|
||||||
<rich-text :nodes="parseMarkdown(message.content)" />
|
<rich-text :nodes="getAIDisplayContent(message.content)" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -206,6 +212,27 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 搜索浮条(消息区和输入框之间,靠近操作位置) -->
|
||||||
|
<view class="search-bar" v-if="showSearchBar">
|
||||||
|
<text class="search-bar-close" @click="closeSearch">✕</text>
|
||||||
|
<view class="search-bar-input-wrap">
|
||||||
|
<text class="search-bar-icon">🔍</text>
|
||||||
|
<input
|
||||||
|
class="search-bar-input"
|
||||||
|
v-model="searchKeyword"
|
||||||
|
placeholder="搜索聊天记录"
|
||||||
|
:focus="true"
|
||||||
|
@input="onSearchInput"
|
||||||
|
@confirm="doSearch" />
|
||||||
|
</view>
|
||||||
|
<view class="search-bar-actions" v-if="searchResults.length > 0">
|
||||||
|
<text class="search-bar-count">{{ searchResultIndex + 1 }}/{{ searchResults.length }}</text>
|
||||||
|
<text class="search-bar-nav" @click="scrollToPrevResult">↑</text>
|
||||||
|
<text class="search-bar-nav" @click="scrollToNextResult">↓</text>
|
||||||
|
</view>
|
||||||
|
<text class="search-bar-clear" v-if="searchKeyword" @click.stop="clearSearch">清空</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 输入区域 -->
|
<!-- 输入区域 -->
|
||||||
<view class="input-area" :style="inputAreaStyle">
|
<view class="input-area" :style="inputAreaStyle">
|
||||||
<view class="input-tools">
|
<view class="input-tools">
|
||||||
@@ -476,6 +503,11 @@ export default {
|
|||||||
streamSpeed: {
|
streamSpeed: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 20
|
default: 20
|
||||||
|
},
|
||||||
|
// API 获取欢迎语失败时的降级欢迎语
|
||||||
|
fallbackWelcome: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data(){
|
data(){
|
||||||
@@ -526,7 +558,13 @@ export default {
|
|||||||
|
|
||||||
// 手动长按检测相关(touch 计时器)
|
// 手动长按检测相关(touch 计时器)
|
||||||
_longPressTimer: null,
|
_longPressTimer: null,
|
||||||
_longPressMessage: null
|
_longPressMessage: null,
|
||||||
|
|
||||||
|
// 聊天记录搜索
|
||||||
|
showSearchBar: false,
|
||||||
|
searchKeyword: '',
|
||||||
|
searchResults: [],
|
||||||
|
searchResultIndex: -1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -540,7 +578,7 @@ export default {
|
|||||||
return this.$util.img(IMAGE_CONFIG.aiAvatar);
|
return this.$util.img(IMAGE_CONFIG.aiAvatar);
|
||||||
},
|
},
|
||||||
chatMessagesStyle() {
|
chatMessagesStyle() {
|
||||||
// 键盘弹出时增加底部padding,避免消息内容被键盘遮挡
|
// 键盘弹出时增加底部padding
|
||||||
return {
|
return {
|
||||||
'padding-bottom': this.keyboardHeight > 0 ? this.keyboardHeight + 'px' : '0px'
|
'padding-bottom': this.keyboardHeight > 0 ? this.keyboardHeight + 'px' : '0px'
|
||||||
};
|
};
|
||||||
@@ -579,8 +617,11 @@ export default {
|
|||||||
this.initUserNicknameData()
|
this.initUserNicknameData()
|
||||||
this.currentConversationId = aiService.getConversationId();
|
this.currentConversationId = aiService.getConversationId();
|
||||||
console.log('[AI Chat] created, currentConversationId:', this.currentConversationId, 'messages.length:', this.messages.length);
|
console.log('[AI Chat] created, currentConversationId:', this.currentConversationId, 'messages.length:', this.messages.length);
|
||||||
|
|
||||||
|
// 异步获取服务配置(含欢迎语)
|
||||||
|
this._loadWelcomeMessage()
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// 初始加载时滚底(仅一次)
|
// 初始加载时滚底(仅一次)
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.scrollToBottom()
|
this.scrollToBottom()
|
||||||
@@ -627,6 +668,65 @@ export default {
|
|||||||
console.log('[AI Chat] → 分支4: 跳过加载,保留当前消息, messages.length:', this.messages.length);
|
console.log('[AI Chat] → 分支4: 跳过加载,保留当前消息, messages.length:', this.messages.length);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 加载欢迎语:优先 API,API 失败或禁用时用父页面传入的 fallbackWelcome
|
||||||
|
async _loadWelcomeMessage() {
|
||||||
|
// 仅全新会话才需要欢迎语
|
||||||
|
if (this.currentConversationId) {
|
||||||
|
console.log('[AI Chat] 已有会话,跳过欢迎语')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let welcomeText = ''
|
||||||
|
let isMarkdown = false
|
||||||
|
let welcomeLoaded = false
|
||||||
|
|
||||||
|
// 1. 尝试从 API 获取远程欢迎语
|
||||||
|
try {
|
||||||
|
const config = await aiService.getServiceConfig()
|
||||||
|
if (config) {
|
||||||
|
const welcomeInfo = config.welcome
|
||||||
|
const remoteText = welcomeInfo?.welcome || ''
|
||||||
|
const isEnabled = welcomeInfo?.is_enabled
|
||||||
|
if (remoteText && isEnabled !== 0 && isEnabled !== false) {
|
||||||
|
welcomeText = remoteText
|
||||||
|
isMarkdown = true
|
||||||
|
welcomeLoaded = true
|
||||||
|
console.log('[AI Chat] 使用远程欢迎语:', welcomeText.substring(0, 50))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[AI Chat] 获取服务配置失败:', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. API 未返回有效欢迎语时,使用降级方案
|
||||||
|
if (!welcomeLoaded) {
|
||||||
|
welcomeText = this.fallbackWelcome
|
||||||
|
isMarkdown = false
|
||||||
|
if (welcomeText) {
|
||||||
|
console.log('[AI Chat] 使用降级欢迎语')
|
||||||
|
} else {
|
||||||
|
console.log('[AI Chat] 无可用欢迎语')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 插入欢迎语(再次检查是否仍是全新会话)
|
||||||
|
if (!this.currentConversationId && this.messages.length === 0) {
|
||||||
|
const welcomeMsg = {
|
||||||
|
id: ++this.messageId,
|
||||||
|
role: 'ai',
|
||||||
|
type: isMarkdown ? 'markdown' : 'text',
|
||||||
|
content: welcomeText,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
actions: []
|
||||||
|
}
|
||||||
|
this.messages.push(welcomeMsg)
|
||||||
|
this.$nextTick(() => this.scrollToBottom())
|
||||||
|
this.$emit('ai-response', welcomeMsg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 生成消息的唯一 key(兼容非 H5 平台)
|
// 生成消息的唯一 key(兼容非 H5 平台)
|
||||||
getMessageKey(message, index) {
|
getMessageKey(message, index) {
|
||||||
const id = message.id || message.timestamp || index;
|
const id = message.id || message.timestamp || index;
|
||||||
@@ -1889,6 +1989,154 @@ _handleStreamError(msg) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// 聊天记录搜索
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
// 切换搜索条
|
||||||
|
toggleSearch() {
|
||||||
|
this.showSearchBar = !this.showSearchBar
|
||||||
|
if (!this.showSearchBar) {
|
||||||
|
this.clearSearch()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 关闭搜索
|
||||||
|
closeSearch() {
|
||||||
|
this.showSearchBar = false
|
||||||
|
this.clearSearch()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 清空搜索状态
|
||||||
|
clearSearch() {
|
||||||
|
this.searchKeyword = ''
|
||||||
|
this.searchResults = []
|
||||||
|
this.searchResultIndex = -1
|
||||||
|
},
|
||||||
|
|
||||||
|
// 输入时实时搜索
|
||||||
|
onSearchInput(e) {
|
||||||
|
const val = e.detail ? e.detail.value : e
|
||||||
|
if (val) {
|
||||||
|
this.searchKeyword = val
|
||||||
|
this.doSearch()
|
||||||
|
} else {
|
||||||
|
this.clearSearch()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 执行搜索
|
||||||
|
doSearch() {
|
||||||
|
const keyword = this.searchKeyword.trim().toLowerCase()
|
||||||
|
if (!keyword) {
|
||||||
|
this.searchResults = []
|
||||||
|
this.searchResultIndex = -1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = []
|
||||||
|
this.messages.forEach((msg, index) => {
|
||||||
|
if (msg.type === 'loading') return
|
||||||
|
const content = (msg.content || '').toLowerCase()
|
||||||
|
if (content.includes(keyword)) {
|
||||||
|
results.push(index)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.searchResults = results
|
||||||
|
this.searchResultIndex = results.length > 0 ? 0 : -1
|
||||||
|
|
||||||
|
if (results.length > 0) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scrollToSearchResult(0)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '未找到匹配结果', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 滚动到指定搜索结果
|
||||||
|
scrollToSearchResult(index) {
|
||||||
|
if (index < 0 || index >= this.searchResults.length) return
|
||||||
|
const msgIdx = this.searchResults[index]
|
||||||
|
const message = this.messages[msgIdx]
|
||||||
|
if (message) {
|
||||||
|
this.scrollIntoViewId = this.getMessageKey(message, msgIdx)
|
||||||
|
this.searchResultIndex = index
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 上一个结果
|
||||||
|
scrollToPrevResult() {
|
||||||
|
const idx = this.searchResultIndex - 1
|
||||||
|
if (idx >= 0) {
|
||||||
|
this.scrollToSearchResult(idx)
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '已到顶部', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 下一个结果
|
||||||
|
scrollToNextResult() {
|
||||||
|
const idx = this.searchResultIndex + 1
|
||||||
|
if (idx < this.searchResults.length) {
|
||||||
|
this.scrollToSearchResult(idx)
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '已到底部', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 用户消息关键词高亮
|
||||||
|
highlightContent(content) {
|
||||||
|
if (!this.searchKeyword || !content) {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
return this._wrapHighlight(content, this.searchKeyword)
|
||||||
|
},
|
||||||
|
|
||||||
|
// AI 消息渲染(含关键词高亮)
|
||||||
|
getAIDisplayContent(content) {
|
||||||
|
if (!this.searchKeyword || !content) {
|
||||||
|
return this.parseMarkdown(content)
|
||||||
|
}
|
||||||
|
// 先做 markdown 渲染,再做关键词高亮
|
||||||
|
const mdHtml = this.parseMarkdown(content)
|
||||||
|
return this._wrapHighlightInHtml(mdHtml, this.searchKeyword)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 在纯文本中高亮关键词(用于用户消息)
|
||||||
|
_wrapHighlight(text, keyword) {
|
||||||
|
if (!keyword || !text) return text
|
||||||
|
const parts = text.split(new RegExp('(' + this._escapeRegExp(keyword) + ')', 'gi'))
|
||||||
|
return parts.map(part => {
|
||||||
|
if (part.toLowerCase() === keyword.toLowerCase()) {
|
||||||
|
return `<span style="background:#ffe066;color:#333;padding:0 2rpx;border-radius:4rpx;">${part}</span>`
|
||||||
|
}
|
||||||
|
return part
|
||||||
|
}).join('')
|
||||||
|
},
|
||||||
|
|
||||||
|
// 在 HTML 中高亮关键词(用于 AI 消息的 markdown 结果)
|
||||||
|
_wrapHighlightInHtml(html, keyword) {
|
||||||
|
if (!keyword || !html) return html
|
||||||
|
// 只高亮文本节点,不破坏 HTML 标签
|
||||||
|
const escaped = this._escapeRegExp(keyword)
|
||||||
|
// 匹配 <xxx> 之外的文本
|
||||||
|
return html.replace(/(>|^)([^<]+)(?=<|$)/g, (match, prefix, text) => {
|
||||||
|
if (!text) return match
|
||||||
|
const highlighted = text.replace(
|
||||||
|
new RegExp('(' + escaped + ')', 'gi'),
|
||||||
|
'<span style="background:#ffe066;color:#333;padding:0 2rpx;border-radius:4rpx;">$1</span>'
|
||||||
|
)
|
||||||
|
return prefix + highlighted
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 转义正则特殊字符
|
||||||
|
_escapeRegExp(str) {
|
||||||
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||||
|
},
|
||||||
|
|
||||||
// 重试消息
|
// 重试消息
|
||||||
async retryMessage(message) {
|
async retryMessage(message) {
|
||||||
// 找到对应的用户消息
|
// 找到对应的用户消息
|
||||||
@@ -4206,5 +4454,121 @@ $radius-lg: 36rpx;
|
|||||||
user-select: text;
|
user-select: text;
|
||||||
-webkit-user-select: text;
|
-webkit-user-select: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 搜索入口浮动按钮
|
||||||
|
.search-entrance {
|
||||||
|
position: absolute;
|
||||||
|
top: 12rpx;
|
||||||
|
right: 16rpx;
|
||||||
|
z-index: 10;
|
||||||
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.search-entrance-icon {
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索浮条(位于消息区和输入框之间)
|
||||||
|
.search-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10rpx 24rpx;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-top: 1rpx solid #e8e8e8;
|
||||||
|
gap: 10rpx;
|
||||||
|
|
||||||
|
.search-bar-close {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #888;
|
||||||
|
flex-shrink: 0;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar-input-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 0 14rpx;
|
||||||
|
height: 58rpx;
|
||||||
|
border: 1rpx solid #ddd;
|
||||||
|
|
||||||
|
.search-bar-icon {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar-input {
|
||||||
|
flex: 1;
|
||||||
|
height: 58rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.search-bar-count {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
min-width: 44rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar-nav {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #666;
|
||||||
|
padding: 6rpx 10rpx;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar-clear {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #07c160;
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户消息 rich-text 高亮(搜索时替代 <text>)
|
||||||
|
.message-rich-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
line-height: 1.7;
|
||||||
|
letter-spacing: 0.8rpx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<ai-chat-message
|
<ai-chat-message
|
||||||
ref="chat"
|
ref="chat"
|
||||||
:initial-messages="initialMessages"
|
:initial-messages="initialMessages"
|
||||||
|
:fallback-welcome="fallbackWelcome"
|
||||||
:user-avatar="userAvatar"
|
:user-avatar="userAvatar"
|
||||||
:ai-avatar="aiAvatar"
|
:ai-avatar="aiAvatar"
|
||||||
@message-sent="onMessageSent"
|
@message-sent="onMessageSent"
|
||||||
@@ -84,15 +85,8 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
initialMessages: [
|
initialMessages: [],
|
||||||
{
|
fallbackWelcome: '您好!我是AI智能客服,很高兴为您服务!\n\n我可以帮您:\n• 解答产品相关问题\n• 处理订单问题\n• 提供技术支持\n• 推荐相关商品\n\n请告诉我您需要什么帮助?',
|
||||||
id: 1,
|
|
||||||
role: 'ai',
|
|
||||||
type: 'text',
|
|
||||||
content: '您好!我是AI智能客服,很高兴为您服务!\n\n我可以帮您:\n• 解答产品相关问题\n• 处理订单问题\n• 提供技术支持\n• 推荐相关商品\n\n请告诉我您需要什么帮助?',
|
|
||||||
timestamp: Date.now() - 60000,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// --- 系统导航栏/状态栏相关 ---
|
// --- 系统导航栏/状态栏相关 ---
|
||||||
navBarHeight: 44, // uni-page-head 系统导航栏,默认高度为44px
|
navBarHeight: 44, // uni-page-head 系统导航栏,默认高度为44px
|
||||||
|
|||||||
Reference in New Issue
Block a user