chore:添加欢迎语配套快捷问题功能,支持用户快速发送常见问题;优化数据格式处理与样式
This commit is contained in:
@@ -288,7 +288,22 @@
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- PR-WELCOME-QUICK-CHIPS (2026-09-19): 欢迎语配套快捷问题
|
||||
欢迎语气泡下方显示快捷问题 chips, 点击直接作为用户消息发送
|
||||
一次性,点了之后从 message 上摘除 (避免重复点) -->
|
||||
<view v-if="message.quickQuestions && message.quickQuestions.length > 0" class="quick-questions-row">
|
||||
<view
|
||||
v-for="(q, qi) in message.quickQuestions"
|
||||
:key="qi"
|
||||
class="quick-question-chip"
|
||||
hover-class="quick-question-chip--hover"
|
||||
@click="onQuickQuestionClick(message, q)">
|
||||
<text class="quick-question-icon">💬</text>
|
||||
<text class="quick-question-text">{{ getQuickQuestionText(q) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="message-actions" v-if="message.actions && message.actions.length > 0">
|
||||
<button
|
||||
@@ -1011,13 +1026,16 @@ export default {
|
||||
let welcomeText = ''
|
||||
let isMarkdown = false
|
||||
let welcomeLoaded = false
|
||||
// PR-FIX (2026-09-20): welcomeInfo 原为 if 块内 const,第 3 步拼 quickQuestions
|
||||
// 时在块外引用 → ReferenceError, 提升到函数作用域
|
||||
let welcomeInfo = null
|
||||
|
||||
// 1. 尝试从 API 获取远程欢迎语
|
||||
try {
|
||||
const config = await aiService.getServiceConfig()
|
||||
console.log('[AI Chat] API getServiceConfig 返回:', JSON.stringify(config))
|
||||
if (config) {
|
||||
const welcomeInfo = config.welcome_info
|
||||
welcomeInfo = config.welcome_info
|
||||
console.log('[AI Chat] welcomeInfo:', JSON.stringify(welcomeInfo))
|
||||
const remoteText = welcomeInfo?.welcome || ''
|
||||
const isEnabled = welcomeInfo?.is_enabled
|
||||
@@ -1053,7 +1071,11 @@ export default {
|
||||
type: isMarkdown ? 'markdown' : 'text',
|
||||
content: welcomeText,
|
||||
timestamp: Date.now(),
|
||||
actions: []
|
||||
actions: [],
|
||||
// PR-WELCOME-QUICK-CHIPS (2026-09-19): 欢迎语配套快捷问题 (chips)
|
||||
// 后端可存 string[] 或 {id,text}[] ,前端统一归一化为 string[]
|
||||
// message.quickQuestions 数组非空 → 模板渲染 chips;点击调 onQuickQuestionClick
|
||||
quickQuestions: this.normalizeQuickQuestions(welcomeInfo && welcomeInfo.quick_questions)
|
||||
}
|
||||
this.messages.push(welcomeMsg)
|
||||
this.$nextTick(() => this.scrollToBottom())
|
||||
@@ -1061,6 +1083,46 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// PR-WELCOME-QUICK-CHIPS (2026-09-19): 归一化 quick_questions 数据格式
|
||||
// 后端可能存 string[] ("如何下单?" / "退换货流程")
|
||||
// 也可能存 {id, text}[] ({id:'q1',text:'如何下单?'})
|
||||
// 前端统一抽出 text,空值过滤
|
||||
normalizeQuickQuestions(raw) {
|
||||
if (!Array.isArray(raw)) return []
|
||||
return raw
|
||||
.map(function (q) {
|
||||
if (q && typeof q === 'object') return q.text || q.label || ''
|
||||
return String(q || '')
|
||||
})
|
||||
.filter(function (s) { return s && s.trim() !== '' })
|
||||
},
|
||||
|
||||
// PR-WELCOME-QUICK-CHIPS (2026-09-19): 渲染文本提取
|
||||
getQuickQuestionText(q) {
|
||||
if (q && typeof q === 'object') return q.text || q.label || ''
|
||||
return String(q || '')
|
||||
},
|
||||
|
||||
// PR-WELCOME-QUICK-CHIPS (2026-09-19): 点击 chip
|
||||
// 1. 把文本塞进 inputText,模拟"用户已输入"
|
||||
// 2. 清空 welcome bubble 的 chips (一次性)
|
||||
// 3. 调 sendMessage() 走正常 chat 流 (建 user bubble + AI 回复)
|
||||
async onQuickQuestionClick(message, q) {
|
||||
const text = this.getQuickQuestionText(q)
|
||||
if (!text || !text.trim()) return
|
||||
try {
|
||||
// 一次性:点了就摘掉 chips,避免重复点
|
||||
message.quickQuestions = []
|
||||
// 走现有 sendMessage 流程 (inputText → 校验 → sendStream/Normal)
|
||||
this.inputText = text
|
||||
// 让 DOM 先更新再发,避免用户视觉上"消息凭空出现"
|
||||
await this.$nextTick()
|
||||
await this.sendMessage()
|
||||
} catch (e) {
|
||||
console.warn('[AI Chat] onQuickQuestionClick failed:', e && e.message)
|
||||
}
|
||||
},
|
||||
|
||||
// 生成消息的唯一 key(兼容非 H5 平台)
|
||||
getMessageKey(message, index) {
|
||||
const rawId = message.id || message.timestamp || index;
|
||||
@@ -3827,6 +3889,50 @@ _handleStreamError(msg) {
|
||||
}
|
||||
}
|
||||
|
||||
// PR-WELCOME-QUICK-CHIPS (2026-09-19): 欢迎语配套快捷问题 chip 样式
|
||||
.quick-questions-row {
|
||||
margin-top: 16rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
justify-content: flex-start;
|
||||
max-width: 100%;
|
||||
|
||||
.quick-question-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
background: #ffffff;
|
||||
border: 1rpx solid #1E9FFF;
|
||||
border-radius: 32rpx;
|
||||
font-size: 26rpx;
|
||||
color: #1E9FFF;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
max-width: 100%;
|
||||
line-height: 1.4;
|
||||
|
||||
.quick-question-icon {
|
||||
font-size: 24rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.quick-question-text {
|
||||
flex: 1;
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-question-chip--hover {
|
||||
background: #1E9FFF;
|
||||
color: #ffffff;
|
||||
transform: translateY(-1rpx);
|
||||
box-shadow: 0 4rpx 12rpx rgba(30, 159, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.message-actions {
|
||||
margin-top: 24rpx;
|
||||
display: flex;
|
||||
@@ -3834,7 +3940,7 @@ _handleStreamError(msg) {
|
||||
justify-content: flex-start;
|
||||
flex-wrap: nowrap;
|
||||
width: 100%;
|
||||
|
||||
|
||||
.action-btn {
|
||||
border-radius: $radius-lg;
|
||||
font-size: 22rpx;
|
||||
|
||||
Reference in New Issue
Block a user