feat(aihr): harden M0-M2 mobile training flows
This commit is contained in:
@@ -233,8 +233,7 @@ const login = async () => {
|
||||
method: 'POST',
|
||||
data: {
|
||||
phonenumber: value,
|
||||
smsCode: formData.smsCode.trim(),
|
||||
tenantId: '000000'
|
||||
smsCode: formData.smsCode.trim()
|
||||
},
|
||||
auth: false
|
||||
});
|
||||
|
||||
@@ -126,6 +126,6 @@ onLoad((options) => {
|
||||
.message-time { color: var(--muted); font-size: 12px; }
|
||||
.message-content { overflow-wrap: anywhere; color: var(--ink); font-size: 15px; line-height: 1.8; white-space: pre-wrap; }
|
||||
.detail-actions { display: grid; gap: 8px; margin-top: 8px; padding-top: 16px; border-top: 1px solid var(--employee-line, #ebedf0); }
|
||||
.ask-message-button { width: 100%; min-height: 46px; border-radius: 14px; background: var(--employee-primary, #e60012); color: #fff; font-size: 15px; font-weight: 800; }
|
||||
.ask-message-button { display: flex; width: 100%; min-height: 46px; align-items: center; justify-content: center; padding: 0; border-radius: 14px; background: var(--employee-primary, #e60012); color: #fff; font-size: 15px; font-weight: 800; line-height: 1; }
|
||||
.ask-message-note { color: var(--muted); font-size: 12px; line-height: 1.5; text-align: center; }
|
||||
</style>
|
||||
|
||||
@@ -194,7 +194,7 @@ onUnload(() => {
|
||||
.empty-title { font-size: 17px; font-weight: 900; }
|
||||
.message-list { display: grid; gap: 12px; margin-top: 18px; }
|
||||
.scope-tabs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: 16px; padding: 4px; border-radius: 14px; background: #f3f4f6; }
|
||||
.scope-tab { min-height: 36px; border-radius: 10px; background: transparent; color: var(--muted); font-size: 13px; font-weight: 800; }
|
||||
.scope-tab { display: flex; min-height: 36px; align-items: center; justify-content: center; padding: 0; border-radius: 10px; background: transparent; color: var(--muted); font-size: 13px; font-weight: 800; line-height: 1; }
|
||||
.scope-tab.active { background: #fff; color: var(--brand); box-shadow: 0 1px 4px rgba(20, 28, 42, .12); }
|
||||
.message-card { display: grid; width: 100%; gap: 10px; padding: 16px; color: var(--ink); text-align: left; }
|
||||
.message-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
||||
@@ -206,7 +206,7 @@ onUnload(() => {
|
||||
.message-tag.related { background: #fff7e8; color: #a76500; }
|
||||
.message-time { color: var(--muted); font-size: 12px; }
|
||||
.message-title { overflow-wrap: anywhere; font-size: 17px; font-weight: 900; line-height: 1.5; }
|
||||
.load-more { width: 100%; min-height: 42px; border: 1px solid #f0a6ac; border-radius: 999px; background: #fff; color: var(--brand); font-size: 14px; font-weight: 800; }
|
||||
.load-more { display: flex; width: 100%; min-height: 42px; align-items: center; justify-content: center; padding: 0; border: 1px solid #f0a6ac; border-radius: 999px; background: #fff; color: var(--brand); font-size: 14px; font-weight: 800; line-height: 1; }
|
||||
.load-more::after { border: 0; }
|
||||
.list-end { display: block; padding: 8px; color: var(--muted); font-size: 12px; text-align: center; }
|
||||
</style>
|
||||
|
||||
@@ -392,7 +392,7 @@ import type {
|
||||
PracticeTurn,
|
||||
PracticeRecord
|
||||
} from '@/types/api';
|
||||
import { answerDailyDrill, finishPractice, getPracticeAssignments, getPracticeHistory, getPracticePrepCard, startPractice, submitPracticeQuestionFeedback, submitPracticeSatisfaction, submitPracticeTurn } from '@/services/practice';
|
||||
import { answerDailyDrill, finishPractice, getPracticeAssignments, getPracticeHistory, getPracticePrepCard, recordPracticeHelp, startPractice, submitPracticeQuestionFeedback, submitPracticeSatisfaction, submitPracticeTurn } from '@/services/practice';
|
||||
import { getSelectedPosition, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { searchKnowledge } from '@/services/knowledge';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
@@ -1289,11 +1289,17 @@ const start = async (scenarioId = defaultScenarioId, assignmentId?: number) => {
|
||||
const loadHelp = async () => {
|
||||
if (helpLoading.value || busy.value) return;
|
||||
if (!requireLogin()) return;
|
||||
if (!sessionId.value) {
|
||||
message.value = '请先开始训练后再求助';
|
||||
return;
|
||||
}
|
||||
const request = requests.begin('help');
|
||||
helpLoading.value = true;
|
||||
message.value = '';
|
||||
try {
|
||||
const data = await searchKnowledge(`${activeScenarioName.value} SOP 处理要点`);
|
||||
await recordPracticeHelp(sessionId.value, roundIndex.value);
|
||||
if (!requests.isCurrent(request)) return;
|
||||
const data = await searchKnowledge(`${activeScenarioName.value} SOP 处理要点`, positionLabel.value, undefined, undefined, 'practice_help');
|
||||
if (!requests.isCurrent(request)) return;
|
||||
if (!data.answer) {
|
||||
throw new Error('未检索到可用 SOP 提示');
|
||||
|
||||
@@ -628,6 +628,17 @@ const clearBroadcastConsultation = () => {
|
||||
clearBroadcastQuestionContext();
|
||||
};
|
||||
|
||||
// A message consultation is re-authorized on every show. When the page is
|
||||
// hidden while that request (or its confirmation dialog) is still pending,
|
||||
// invalidate only the in-flight work. Keep the account-bound pending message
|
||||
// ID so the next onShow can restart the authorization instead of leaving the
|
||||
// composer locked in a stale loading state.
|
||||
const cancelPendingBroadcastConsultation = () => {
|
||||
broadcastContextGeneration += 1;
|
||||
broadcastContextLoading.value = false;
|
||||
broadcastContextPrompting.value = false;
|
||||
};
|
||||
|
||||
const preserveCurrentBroadcastConsultation = () => {
|
||||
if (!broadcastContext.value) return;
|
||||
saveBroadcastQuestionContext(broadcastContext.value.messageId, currentAccountKey());
|
||||
@@ -693,12 +704,15 @@ const restoreBroadcastConsultation = async (pageGeneration: number, accountKey:
|
||||
return;
|
||||
}
|
||||
|
||||
const start = () => void activateBroadcastConsultation(pending, accountKey, pageGeneration);
|
||||
if (!conversationStarted.value) {
|
||||
start();
|
||||
// Do not start project selection until the message context has been
|
||||
// resolved. Otherwise the asynchronous handoff can briefly look like an
|
||||
// ordinary empty conversation and incorrectly open the project picker.
|
||||
await activateBroadcastConsultation(pending, accountKey, pageGeneration);
|
||||
return;
|
||||
}
|
||||
|
||||
const start = () => void activateBroadcastConsultation(pending, accountKey, pageGeneration);
|
||||
broadcastContextPrompting.value = true;
|
||||
uni.showModal({
|
||||
title: '开始新的消息咨询?',
|
||||
@@ -847,7 +861,8 @@ const loadProjectContext = async () => {
|
||||
const identity = await fetchMobileIdentity();
|
||||
availableProjects.value = identity.projects || [];
|
||||
currentProject.value = reconcileCurrentProject(availableProjects.value);
|
||||
if (!currentProject.value && availableProjects.value.length > 1 && !broadcastContext.value && !broadcastContextPrompting.value) {
|
||||
if (!currentProject.value && availableProjects.value.length > 1
|
||||
&& !broadcastContext.value && !broadcastContextLoading.value && !broadcastContextPrompting.value) {
|
||||
projectPickerOpen.value = true;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1477,6 +1492,7 @@ onShow(() => {
|
||||
onHide(() => {
|
||||
showGeneration += 1;
|
||||
candidateRefreshGeneration += 1;
|
||||
cancelPendingBroadcastConsultation();
|
||||
speechPlayback.stop();
|
||||
composerRef.value?.teardown();
|
||||
cancelVoiceTranscription();
|
||||
@@ -1485,6 +1501,7 @@ onHide(() => {
|
||||
|
||||
onUnload(() => {
|
||||
showGeneration += 1;
|
||||
cancelPendingBroadcastConsultation();
|
||||
releaseMessageResources();
|
||||
speechPlayback.destroy();
|
||||
composerRef.value?.teardown();
|
||||
|
||||
Reference in New Issue
Block a user