fix(aihr): harden broadcast and mobile assistant flows

This commit is contained in:
2026-07-23 03:35:36 +08:00
parent 62c8b41672
commit 4b31848e3d
30 changed files with 1127 additions and 208 deletions
+121 -2
View File
@@ -160,7 +160,7 @@
class="bubble-action"
:class="{ negative: msg.feedbackVerdict === 'down' }"
:disabled="msg.feedbackSubmitting || Boolean(msg.feedbackVerdict)"
@click="feedback('down', msg)"
@click="openNegativeFeedback(msg)"
>
没解决
</button>
@@ -168,6 +168,34 @@
{{ msg.feedbackVerdict === 'up' ? '已记录' : '已进入复核' }}
</text>
</view>
<view v-if="msg.feedbackPanelOpen && !msg.feedbackVerdict" class="negative-feedback-panel">
<text class="negative-feedback-title">哪里还没解决?(可多选)</text>
<view class="negative-feedback-options">
<button
v-for="option in feedbackReasonOptions"
:key="option.code"
class="negative-feedback-option"
:class="{ active: msg.feedbackReasonCodes.includes(option.code) }"
:disabled="msg.feedbackSubmitting"
@click="toggleNegativeFeedbackReason(msg, option.code)"
>{{ option.label }}</button>
</view>
<textarea
v-model="msg.feedbackComment"
class="textarea negative-feedback-comment"
maxlength="200"
:disabled="msg.feedbackSubmitting"
placeholder="可补充具体问题,方便复核"
/>
<text class="negative-feedback-hint">请至少选择一项原因后提交</text>
<button
class="bubble-action primary"
:disabled="msg.feedbackSubmitting || !msg.feedbackReasonCodes.length"
@click="submitNegativeFeedback(msg)"
>
{{ msg.feedbackSubmitting ? '提交中' : '提交复核' }}
</button>
</view>
<view v-if="msg.citationsOpen && msg.result.snippets?.length" class="bubble-citations">
<view v-for="(snippet, index) in msg.result.snippets" :key="`${snippet.docTitle}-${index}`" class="citation">
@@ -442,6 +470,9 @@ interface MasterMsg {
toolCode: ToolCode;
feedbackVerdict: AnswerFeedbackVerdict | '';
feedbackSubmitting: boolean;
feedbackPanelOpen: boolean;
feedbackReasonCodes: string[];
feedbackComment: string;
citationsOpen: boolean;
voiceStatus: 'preparing' | 'ready' | 'unavailable';
voiceDuration: number | null;
@@ -933,6 +964,9 @@ const sendQuestion = async (rawText: string, options: { voice?: boolean } = {})
toolCode: '',
feedbackVerdict: '',
feedbackSubmitting: false,
feedbackPanelOpen: false,
feedbackReasonCodes: [],
feedbackComment: '',
citationsOpen: false,
voiceStatus: spoken ? 'preparing' : 'unavailable',
voiceDuration: null,
@@ -1003,6 +1037,9 @@ const runDataTool = async (toolCode: 'MY_PRACTICE_SUMMARY' | 'TEAM_PRACTICE_SUMM
toolCode: effectiveToolCode,
feedbackVerdict: '',
feedbackSubmitting: false,
feedbackPanelOpen: false,
feedbackReasonCodes: [],
feedbackComment: '',
citationsOpen: false,
voiceStatus: 'unavailable',
voiceDuration: null,
@@ -1205,6 +1242,33 @@ const serviceMemoryStatusLabel = (status?: string | null) => ({
const displayCitationDate = (value?: string | null) => value ? value.replace('T', ' ').slice(0, 16) : '未记录';
const feedbackReasonOptions = [
{ code: 'NO_ANSWER', label: '缺少制度或SOP依据' },
{ code: 'NOT_SPECIFIC', label: '处理步骤不够具体' },
{ code: 'WRONG_REFERENCE', label: '答案或引用不正确' },
{ code: 'NOT_APPLICABLE', label: '不适用于当前现场' }
];
const openNegativeFeedback = (msg: MasterMsg) => {
if (msg.feedbackSubmitting || msg.feedbackVerdict) return;
msg.feedbackPanelOpen = !msg.feedbackPanelOpen;
};
const toggleNegativeFeedbackReason = (msg: MasterMsg, code: string) => {
if (msg.feedbackSubmitting) return;
msg.feedbackReasonCodes = msg.feedbackReasonCodes.includes(code)
? msg.feedbackReasonCodes.filter((item) => item !== code)
: [...msg.feedbackReasonCodes, code];
};
const submitNegativeFeedback = async (msg: MasterMsg) => {
if (!msg.feedbackReasonCodes.length) {
uni.showToast({ title: '请至少选择一项未解决原因', icon: 'none' });
return;
}
await feedback('down', msg);
};
const feedback = async (verdict: AnswerFeedbackVerdict, msg: MasterMsg) => {
if (msg.feedbackSubmitting || msg.feedbackVerdict) return;
const fragmentIds = Array.from(
@@ -1224,9 +1288,12 @@ const feedback = async (verdict: AnswerFeedbackVerdict, msg: MasterMsg) => {
queryText: msg.result.queryText || '',
fragmentIds,
verdict,
reviewId: msg.result.reviewId
reviewId: msg.result.reviewId,
reasonCodes: verdict === 'down' ? msg.feedbackReasonCodes : undefined,
comment: verdict === 'down' ? msg.feedbackComment.trim() : undefined
});
msg.feedbackVerdict = verdict;
msg.feedbackPanelOpen = false;
uni.showToast({ title: verdict === 'down' ? '已进入复核' : '已记录', icon: 'none' });
} catch (error) {
uni.showToast({ title: requestErrorText(error, '反馈失败'), icon: 'none' });
@@ -2918,6 +2985,58 @@ onUnload(() => {
font-size: 12px;
}
.negative-feedback-panel {
display: grid;
gap: 8px;
margin-top: 10px;
padding: 10px;
border: 1px solid #f1c5ca;
border-radius: 12px;
background: #fff8f8;
}
.negative-feedback-title {
color: #5b3136;
font-size: 12px;
font-weight: 700;
}
.negative-feedback-options {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.negative-feedback-option {
min-height: 28px;
margin: 0;
padding: 0 9px;
border: 1px solid #f0d6d9;
border-radius: 999px;
background: #ffffff;
color: #6b7280;
font-size: 12px;
}
.negative-feedback-option.active {
border-color: #e70014;
background: #fff0f1;
color: #bd0010;
}
.negative-feedback-comment {
min-height: 58px;
padding: 8px;
border-color: #f0d6d9;
background: #ffffff;
font-size: 13px;
}
.negative-feedback-hint {
color: #8a5960;
font-size: 12px;
}
.bubble-citations {
display: flex;
flex-direction: column;