fix(aihr): harden broadcast and mobile assistant flows
This commit is contained in:
@@ -894,10 +894,6 @@ const askTopic = (topic: string) => {
|
||||
uni.switchTab({ url: '/pages/user/sop/index' });
|
||||
};
|
||||
|
||||
const goCases = () => {
|
||||
uni.navigateTo({ url: '/pages/user/cases/index' });
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
const nextAccountKey = currentAccountKey();
|
||||
if (visibleAccountKey && visibleAccountKey !== nextAccountKey) {
|
||||
|
||||
@@ -373,7 +373,7 @@
|
||||
|
||||
<view v-if="result" class="result-actions">
|
||||
<button class="secondary-button" @click="openPracticeHistory">查看训练记录</button>
|
||||
<button class="primary-button" @click="start(selectedScenarioId)">再练一次</button>
|
||||
<button class="primary-button tabbar-safe-action" @click="start(selectedScenarioId)">再练一次</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -1443,6 +1443,7 @@ const finish = async (force = false) => {
|
||||
const nextResult = await finishPractice(sessionId.value, assignmentId);
|
||||
if (!requests.isCurrent(request)) return;
|
||||
result.value = nextResult;
|
||||
resetPageScroll();
|
||||
status.value = 'finished';
|
||||
message.value = '训练完成,主管端待复盘已更新';
|
||||
completedAssignmentId.value = assignmentId;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<view v-if="loggedIn" class="position-badge">
|
||||
<text class="badge-text">{{ positionProfile.value }} · {{ positionProfile.heroTitle }}</text>
|
||||
</view>
|
||||
<text v-if="loggedIn" class="identity-source">岗位由人力系统组织同步</text>
|
||||
<text v-else class="identity-note">登录后查看训练记录和能力画像</text>
|
||||
</view>
|
||||
<button v-if="!loggedIn" class="primary-gradient-btn compact-login-btn" @click="goLogin">去登录</button>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -310,23 +310,6 @@ const primaryScheduleTitle = computed(() => {
|
||||
return '';
|
||||
});
|
||||
|
||||
const assignmentSourceLabel = (source?: string) => {
|
||||
if (source === 'daily') return '每日三题';
|
||||
if (source === 'retry') return '错题再练';
|
||||
if (source === 'camp') return '训练营';
|
||||
return '专项训练';
|
||||
};
|
||||
|
||||
const primaryAssignmentSourceLabel = computed(() =>
|
||||
primaryAssignment.value ? assignmentSourceLabel(primaryAssignment.value.source) : ''
|
||||
);
|
||||
|
||||
const primaryDisplayTitle = computed(() => {
|
||||
const label = primaryAssignmentSourceLabel.value;
|
||||
const title = primaryScheduleTitle.value;
|
||||
return label && title ? `${label}:${title}` : title;
|
||||
});
|
||||
|
||||
const primaryScheduleDesc = computed(() => {
|
||||
const item = primaryAssignment.value;
|
||||
if (item) {
|
||||
|
||||
Reference in New Issue
Block a user