feat(aihr): add pilot satisfaction evidence loop

This commit is contained in:
2026-07-14 04:13:02 +08:00
parent 22bb2cab58
commit d3552cc22d
10 changed files with 201 additions and 14 deletions
+102 -2
View File
@@ -193,6 +193,40 @@
<text>{{ result.mentorRewrite }}</text>
</view>
</view>
<view v-if="result" class="panel satisfaction-card employee-card">
<view class="satisfaction-head">
<text class="panel-title">本次训练体验如何?</text>
<text class="muted">帮助我们改进训练内容</text>
</view>
<view v-if="!satisfactionSubmitted" class="satisfaction-options">
<button
v-for="score in satisfactionOptions"
:key="score"
class="satisfaction-option"
:class="{ selected: satisfactionScore === score }"
@click="satisfactionScore = score"
>
{{ score }}
</button>
</view>
<textarea
v-if="!satisfactionSubmitted"
v-model="satisfactionComment"
class="textarea satisfaction-textarea"
maxlength="500"
placeholder="可选:写下你希望改进的地方"
/>
<button
v-if="!satisfactionSubmitted"
class="primary-button small-button"
:disabled="satisfactionBusy || satisfactionScore === 0"
@click="sendSatisfaction"
>
{{ satisfactionBusy ? '提交中' : '提交体验反馈' }}
</button>
<text v-else class="satisfaction-thanks">感谢反馈,已记录本次训练体验。</text>
</view>
</view>
</template>
@@ -207,7 +241,7 @@ import type {
PracticeTurn,
PracticeRecord
} from '@/types/api';
import { answerDailyDrill, finishPractice, getPracticeAssignments, getPracticeHistory, getPracticePrepCard, startPractice, submitPracticeTurn } from '@/services/practice';
import { answerDailyDrill, finishPractice, getPracticeAssignments, getPracticeHistory, getPracticePrepCard, startPractice, submitPracticeSatisfaction, submitPracticeTurn } from '@/services/practice';
import { isLoggedIn, rememberLoginRedirect } from '@/services/auth';
import { searchKnowledge } from '@/services/knowledge';
import { resetPageScroll } from '@/services/navigation';
@@ -234,6 +268,11 @@ const draft = ref('');
const message = ref('');
const turns = ref<PracticeTurn[]>([]);
const result = ref<PracticeFinishResponse | null>(null);
const satisfactionOptions = [1, 2, 3, 4, 5];
const satisfactionScore = ref(0);
const satisfactionComment = ref('');
const satisfactionBusy = ref(false);
const satisfactionSubmitted = ref(false);
const selectedScenarioId = ref(defaultScenarioId);
const prepCard = ref<PracticePrepCard | null>(null);
const prepLoading = ref(false);
@@ -324,6 +363,10 @@ const clearPracticeData = () => {
prepLoading.value = false;
turns.value = [];
result.value = null;
satisfactionScore.value = 0;
satisfactionComment.value = '';
satisfactionBusy.value = false;
satisfactionSubmitted.value = false;
sessionId.value = '';
roundIndex.value = 0;
draft.value = '';
@@ -591,6 +634,9 @@ const start = async (scenarioId = defaultScenarioId, assignmentId?: number) => {
status.value = 'starting';
message.value = '';
result.value = null;
satisfactionScore.value = 0;
satisfactionComment.value = '';
satisfactionSubmitted.value = false;
turns.value = [];
draft.value = '';
clearDraftAudio();
@@ -756,6 +802,20 @@ const finish = async (force = false) => {
}
};
const sendSatisfaction = async () => {
if (!sessionId.value || satisfactionScore.value < 1 || satisfactionScore.value > 5 || satisfactionBusy.value) return;
satisfactionBusy.value = true;
message.value = '';
try {
await submitPracticeSatisfaction(sessionId.value, satisfactionScore.value, satisfactionComment.value.trim());
satisfactionSubmitted.value = true;
} catch (error) {
handleError(error, '体验反馈提交失败');
} finally {
satisfactionBusy.value = false;
}
};
const handleShow = async () => {
resetPageScroll();
refreshAuthState();
@@ -852,7 +912,8 @@ onShow(() => {
.dialogue-card,
.reply-card,
.help-card,
.result-card {
.result-card,
.satisfaction-card {
display: flex;
flex-direction: column;
gap: 14px;
@@ -1285,6 +1346,45 @@ onShow(() => {
font-weight: 900;
}
.satisfaction-head {
display: flex;
flex-direction: column;
gap: 5px;
}
.satisfaction-options {
display: flex;
gap: 8px;
}
.satisfaction-option {
flex: 1;
min-height: 40px;
padding: 0;
border: 1px solid #d8e1ef;
border-radius: 8px;
background: #ffffff;
color: #5a6270;
font-size: 15px;
font-weight: 800;
}
.satisfaction-option.selected {
border-color: #2f6bff;
background: #eef4ff;
color: #2f6bff;
}
.satisfaction-textarea {
min-height: 76px;
}
.satisfaction-thanks {
color: #168243;
font-size: 14px;
font-weight: 800;
}
/* 练页入口态:与员工端蓝白轻卡片体系保持一致 */
.practice-page {
padding: 0 16px 108px;
+8
View File
@@ -73,6 +73,14 @@ export const finishPractice = (sessionId: string, assignmentId?: number) =>
timeout: 45000
});
export const submitPracticeSatisfaction = (sessionId: string, score: number, comment = '') =>
apiRequest<{ sessionId: string; score: number; comment: string; submittedAt: string }>({
url: '/api/aihr/mobile/practice/satisfaction',
method: 'POST',
data: { sessionId, score, comment },
timeout: 15000
});
export const getPracticeHistory = () => {
const extPartyId = encodeURIComponent(currentExtPartyId());
return apiRequest<PracticeRecord[]>({