fix:澄清卡片显示完整选项和中文答案
问题:详情页 chat-bubble 卡片视图显示「已回答:family」等英文 value, 而不是中文 label(如「家人」),且不展示所有可选选项。 修复: - 新增 getAnswerLabel(msg) 函数,通过 card.options 查找 value 对应的中文 label - rebuildResultMessages 增加 selectedValue 字段,与 answer 同时存原始 value - 卡片已回答视图新增 card-options-list,渲染所有选项,已选加 .selected 样式并标 ✓ - submitClarification(实时模式)同步存 selectedValue,保持渲染逻辑一致 影响: - ScriptView.vue(chat-bubble 流程视图) - ScriptDetailView.vue(详情页只读视图) 数据基础未动,后端澄清问答 answers 仍存原始 value(语义化)。 全局约束:rpx 单位、代码注释中文、禁止 mock 数据。
This commit is contained in:
@@ -24,7 +24,24 @@
|
|||||||
<view v-else-if="msg.kind === 'card'" class="chat-bubble system">
|
<view v-else-if="msg.kind === 'card'" class="chat-bubble system">
|
||||||
<view class="card-answered">
|
<view class="card-answered">
|
||||||
<text v-if="msg.card?.question" class="card-question-text">{{ msg.card.question }}</text>
|
<text v-if="msg.card?.question" class="card-question-text">{{ msg.card.question }}</text>
|
||||||
<text class="card-answer-text">已回答:{{ msg.answer || '未回答' }}</text>
|
<!-- 用户回答(中文 label):通过 options 把 value 映射回 label 显示 -->
|
||||||
|
<view class="card-answer-row">
|
||||||
|
<text class="card-answer-prefix">已回答:</text>
|
||||||
|
<text class="card-answer-label">{{ getAnswerLabel(msg) }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 选项列表:展示所有可选选项,已选的高亮显示 -->
|
||||||
|
<view v-if="Array.isArray(msg.card?.options) && msg.card.options.length" class="card-options-list">
|
||||||
|
<view
|
||||||
|
v-for="opt in msg.card.options"
|
||||||
|
:key="opt.value"
|
||||||
|
class="card-option-item"
|
||||||
|
:class="{ selected: opt.value === (msg.selectedValue || msg.answer) }"
|
||||||
|
>
|
||||||
|
<text class="card-option-label">{{ opt.label || opt.value }}</text>
|
||||||
|
<text v-if="opt.description" class="card-option-desc">{{ opt.description }}</text>
|
||||||
|
<text v-if="opt.value === (msg.selectedValue || msg.answer)" class="card-option-mark">✓</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -135,7 +152,9 @@ const rebuildResultMessages = (scriptData, messages) => {
|
|||||||
} else if (m.type === 'clarification_answer') {
|
} else if (m.type === 'clarification_answer') {
|
||||||
const lastCard = [...result].reverse().find(x => x.kind === 'card')
|
const lastCard = [...result].reverse().find(x => x.kind === 'card')
|
||||||
if (lastCard) {
|
if (lastCard) {
|
||||||
lastCard.answer = m.content || '未回答'
|
// answer 存用户提交的 value,渲染时通过 card.options 映射成中文 label
|
||||||
|
lastCard.answer = m.content || ''
|
||||||
|
lastCard.selectedValue = m.content || ''
|
||||||
} else {
|
} else {
|
||||||
// 无对应 question,作为独立 user 消息
|
// 无对应 question,作为独立 user 消息
|
||||||
result.push({ id: m.id, role: 'user', kind: 'text', content: m.content || '' })
|
result.push({ id: m.id, role: 'user', kind: 'text', content: m.content || '' })
|
||||||
@@ -167,6 +186,20 @@ const rebuildResultMessages = (scriptData, messages) => {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把已回答卡片的 value(如 "family")映射为对应的中文 label(如 "家人")
|
||||||
|
* 若选项中找不到匹配的 value,则原样返回 answer(兜底)
|
||||||
|
* 若 msg.card 不存在则返回 "未回答"
|
||||||
|
*/
|
||||||
|
const getAnswerLabel = (msg) => {
|
||||||
|
if (!msg || !msg.card) return '未回答'
|
||||||
|
const raw = msg.selectedValue || msg.answer || ''
|
||||||
|
if (!raw) return '未回答'
|
||||||
|
const options = Array.isArray(msg.card.options) ? msg.card.options : []
|
||||||
|
const match = options.find(opt => opt && opt.value === raw)
|
||||||
|
return match?.label || raw
|
||||||
|
}
|
||||||
|
|
||||||
const continueCurrent = () => {
|
const continueCurrent = () => {
|
||||||
if (!script.value?.id) return
|
if (!script.value?.id) return
|
||||||
uni.setStorageSync('pending_open_script_chat', {
|
uni.setStorageSync('pending_open_script_chat', {
|
||||||
@@ -332,9 +365,62 @@ onUnmounted(() => {
|
|||||||
color: rgba(255, 255, 255, 0.9);
|
color: rgba(255, 255, 255, 0.9);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.card-answer-text {
|
.card-answer-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8rpx;
|
||||||
|
margin-bottom: 4rpx;
|
||||||
|
}
|
||||||
|
.card-answer-prefix {
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: rgba(193, 134, 255, 0.88);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
.card-answer-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: rgba(193, 134, 255, 0.95);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.card-options-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10rpx;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
.card-option-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4rpx;
|
||||||
|
padding: 14rpx 18rpx;
|
||||||
|
border-radius: 14rpx;
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
border: 1rpx solid rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
.card-option-item.selected {
|
||||||
|
background: rgba(168, 85, 247, 0.16);
|
||||||
|
border-color: rgba(168, 85, 247, 0.45);
|
||||||
|
}
|
||||||
|
.card-option-label {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.card-option-item.selected .card-option-label {
|
||||||
|
color: rgba(220, 195, 255, 1);
|
||||||
|
}
|
||||||
|
.card-option-desc {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.55);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.card-option-mark {
|
||||||
|
position: absolute;
|
||||||
|
right: 14rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: rgba(220, 195, 255, 1);
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.outline-title {
|
.outline-title {
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
|
|||||||
@@ -130,7 +130,24 @@
|
|||||||
/>
|
/>
|
||||||
<view v-else class="card-answered">
|
<view v-else class="card-answered">
|
||||||
<text v-if="msg.card?.question" class="card-question-text">{{ msg.card.question }}</text>
|
<text v-if="msg.card?.question" class="card-question-text">{{ msg.card.question }}</text>
|
||||||
<text>已回答:{{ msg.answer }}</text>
|
<!-- 用户回答(中文 label):通过 options 把 value 映射回 label 显示 -->
|
||||||
|
<view class="card-answer-row">
|
||||||
|
<text class="card-answer-prefix">已回答:</text>
|
||||||
|
<text class="card-answer-label">{{ getAnswerLabel(msg) }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 选项列表:展示用户可选的所有选项,已选的高亮显示 -->
|
||||||
|
<view v-if="Array.isArray(msg.card?.options) && msg.card.options.length" class="card-options-list">
|
||||||
|
<view
|
||||||
|
v-for="opt in msg.card.options"
|
||||||
|
:key="opt.value"
|
||||||
|
class="card-option-item"
|
||||||
|
:class="{ selected: opt.value === (msg.selectedValue || msg.answer) }"
|
||||||
|
>
|
||||||
|
<text class="card-option-label">{{ opt.label || opt.value }}</text>
|
||||||
|
<text v-if="opt.description" class="card-option-desc">{{ opt.description }}</text>
|
||||||
|
<text v-if="opt.value === (msg.selectedValue || msg.answer)" class="card-option-mark">✓</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -1189,6 +1206,20 @@ const PENDING_SCRIPT_CHAT_KEY = 'pending_open_script_chat'
|
|||||||
const createMessageId = () => `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
const createMessageId = () => `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
||||||
const createConversationId = () => `conv-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
const createConversationId = () => `conv-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把已回答卡片的 value(如 "family")映射为对应的中文 label(如 "家人")
|
||||||
|
* 若选项中找不到匹配的 value,则原样返回 answer(兜底)
|
||||||
|
* 若 msg.card 不存在则返回 "未回答"
|
||||||
|
*/
|
||||||
|
const getAnswerLabel = (msg) => {
|
||||||
|
if (!msg || !msg.card) return '未回答'
|
||||||
|
const raw = msg.selectedValue || msg.answer || ''
|
||||||
|
if (!raw) return '未回答'
|
||||||
|
const options = Array.isArray(msg.card.options) ? msg.card.options : []
|
||||||
|
const match = options.find(opt => opt && opt.value === raw)
|
||||||
|
return match?.label || raw
|
||||||
|
}
|
||||||
|
|
||||||
const getCurrentScriptId = () => String(currentResult.value?.id || '')
|
const getCurrentScriptId = () => String(currentResult.value?.id || '')
|
||||||
const getCurrentConversationId = () => String(currentConversationId.value || currentResult.value?.conversationId || '')
|
const getCurrentConversationId = () => String(currentConversationId.value || currentResult.value?.conversationId || '')
|
||||||
|
|
||||||
@@ -1475,10 +1506,12 @@ const openScriptChat = async (payload = {}) => {
|
|||||||
confirmed: true
|
confirmed: true
|
||||||
})
|
})
|
||||||
} else if (m.type === 'clarification_answer') {
|
} else if (m.type === 'clarification_answer') {
|
||||||
// 把答案合并到上一条 card 消息的 answer 字段
|
// 把答案合并到上一条 card 消息:answer 存用户原始 value,selectedValue 也存 value 用于渲染时映射 label
|
||||||
const lastCard = [...resultMessages.value].reverse().find(x => x.kind === 'card')
|
const lastCard = [...resultMessages.value].reverse().find(x => x.kind === 'card')
|
||||||
if (lastCard) {
|
if (lastCard) {
|
||||||
lastCard.answer = m.content || '未回答'
|
const rawValue = m.content || ''
|
||||||
|
lastCard.answer = rawValue // 原始 value(用于查找 label)
|
||||||
|
lastCard.selectedValue = rawValue // 选中的 value(渲染时通过 options 映射成 label)
|
||||||
lastCard.submitted = true
|
lastCard.submitted = true
|
||||||
} else {
|
} else {
|
||||||
// 无对应 question,作为独立 user 消息
|
// 无对应 question,作为独立 user 消息
|
||||||
@@ -1841,12 +1874,11 @@ const submitClarification = (answer) => {
|
|||||||
if (answeringClarification.value) return
|
if (answeringClarification.value) return
|
||||||
// 找最后一张未提交的卡片消息(mp-weixin 模板不支持内联箭头函数传 msg,改为自己查找)
|
// 找最后一张未提交的卡片消息(mp-weixin 模板不支持内联箭头函数传 msg,改为自己查找)
|
||||||
const card = [...resultMessages.value].reverse().find(m => m.kind === 'card' && !m.submitted)
|
const card = [...resultMessages.value].reverse().find(m => m.kind === 'card' && !m.submitted)
|
||||||
// 通过 value 查找选项的 label,用于前端显示;后端 payload 仍用 value(语义化)
|
|
||||||
const option = card?.card?.options?.find(opt => opt.value === answer)
|
|
||||||
const label = option?.label || answer
|
|
||||||
if (card) {
|
if (card) {
|
||||||
card.submitted = true
|
card.submitted = true
|
||||||
card.answer = label // 显示用 label(中文);答案已在卡片中显示,无需额外用户气泡
|
// answer 存用户提交的 value,渲染时通过 card.options 映射成中文 label(与历史回放逻辑一致)
|
||||||
|
card.answer = answer
|
||||||
|
card.selectedValue = answer
|
||||||
}
|
}
|
||||||
pendingNextResponse.value = true // 标记等待下一个 assistant 响应
|
pendingNextResponse.value = true // 标记等待下一个 assistant 响应
|
||||||
answeringClarification.value = true
|
answeringClarification.value = true
|
||||||
@@ -3775,6 +3807,63 @@ onUnmounted(() => {
|
|||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
padding: 16rpx 0;
|
padding: 16rpx 0;
|
||||||
}
|
}
|
||||||
|
.card-answer-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8rpx;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
.card-answer-prefix {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
.card-answer-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: rgba(193, 134, 255, 0.95);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.card-options-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10rpx;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
.card-option-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4rpx;
|
||||||
|
padding: 14rpx 18rpx;
|
||||||
|
border-radius: 14rpx;
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
border: 1rpx solid rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
.card-option-item.selected {
|
||||||
|
background: rgba(168, 85, 247, 0.16);
|
||||||
|
border-color: rgba(168, 85, 247, 0.45);
|
||||||
|
}
|
||||||
|
.card-option-label {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.card-option-item.selected .card-option-label {
|
||||||
|
color: rgba(220, 195, 255, 1);
|
||||||
|
}
|
||||||
|
.card-option-desc {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.55);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.card-option-mark {
|
||||||
|
position: absolute;
|
||||||
|
right: 14rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: rgba(220, 195, 255, 1);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.outline-title {
|
.outline-title {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
|||||||
Reference in New Issue
Block a user