fix(aihr): expose enabled scenarios to supervisors
This commit is contained in:
@@ -122,7 +122,7 @@ import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import type { PracticeAlert, PracticeAssignment } from '@/types/api';
|
||||
import { clearAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { createPracticeAssignment, getPracticeAlerts, getPracticeTeamSnapshot } from '@/services/practice';
|
||||
import { createPracticeAssignment, getPracticeAlerts, getPracticeScenarios, getPracticeTeamSnapshot } from '@/services/practice';
|
||||
import SupervisorTabBar from '@/components/SupervisorTabBar.vue';
|
||||
import {
|
||||
createAsyncGenerationGuard,
|
||||
@@ -130,6 +130,7 @@ import {
|
||||
isSupervisorAccessDenied,
|
||||
manualPracticeAssignments,
|
||||
supervisorScenarioOptions,
|
||||
type SupervisorScenarioOption,
|
||||
type SupervisorScenarioId
|
||||
} from '@/services/supervisor-view';
|
||||
|
||||
@@ -137,6 +138,7 @@ const basePath = '/pages/supervisor/assign/index';
|
||||
const targetExtPartyId = ref('');
|
||||
const targetName = ref('');
|
||||
const assignmentScenarioId = ref<SupervisorScenarioId>('fee-parking');
|
||||
const scenarioOptions = ref<SupervisorScenarioOption[]>([...supervisorScenarioOptions]);
|
||||
const assignmentReason = ref('');
|
||||
const assignments = ref<PracticeAssignment[]>([]);
|
||||
const alerts = ref<PracticeAlert[]>([]);
|
||||
@@ -174,7 +176,7 @@ const queryText = (value: unknown) => {
|
||||
};
|
||||
|
||||
const isScenarioId = (value: string): value is SupervisorScenarioId =>
|
||||
supervisorScenarioOptions.some((item) => item.id === value);
|
||||
scenarioOptions.value.some((item) => item.id === value);
|
||||
|
||||
const refreshAuthState = () => {
|
||||
loggedIn.value = isLoggedIn();
|
||||
@@ -240,6 +242,31 @@ const loadAssignments = async (force = false) => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadScenarioOptions = async () => {
|
||||
try {
|
||||
const rows = await getPracticeScenarios('生活顾问');
|
||||
const nextOptions = rows
|
||||
.filter((item) => item.enabled !== false && item.id && item.name)
|
||||
.map((item) => ({ id: item.id, name: item.name }));
|
||||
if (nextOptions.length) {
|
||||
scenarioOptions.value = nextOptions;
|
||||
if (!isScenarioId(assignmentScenarioId.value)) {
|
||||
assignmentScenarioId.value = nextOptions[0].id;
|
||||
}
|
||||
return;
|
||||
}
|
||||
scenarioOptions.value = [...supervisorScenarioOptions];
|
||||
if (!isScenarioId(assignmentScenarioId.value)) {
|
||||
assignmentScenarioId.value = supervisorScenarioOptions[0].id;
|
||||
}
|
||||
} catch {
|
||||
scenarioOptions.value = [...supervisorScenarioOptions];
|
||||
if (!isScenarioId(assignmentScenarioId.value)) {
|
||||
assignmentScenarioId.value = supervisorScenarioOptions[0].id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const loadPage = () => {
|
||||
refreshAuthState();
|
||||
message.value = '';
|
||||
@@ -247,7 +274,7 @@ const loadPage = () => {
|
||||
clearPrivateState();
|
||||
return;
|
||||
}
|
||||
void loadAssignments();
|
||||
void Promise.all([loadAssignments(), loadScenarioOptions()]);
|
||||
};
|
||||
|
||||
const assignPractice = async () => {
|
||||
@@ -261,7 +288,7 @@ const assignPractice = async () => {
|
||||
assigning.value = true;
|
||||
message.value = '';
|
||||
try {
|
||||
const scenario = supervisorScenarioOptions.find((item) => item.id === assignmentScenarioId.value);
|
||||
const scenario = scenarioOptions.value.find((item) => item.id === assignmentScenarioId.value);
|
||||
await createPracticeAssignment({
|
||||
extPartyId,
|
||||
traineeName: targetName.value.trim() || `手机用户${extPartyId.slice(-4)}`,
|
||||
@@ -322,9 +349,7 @@ onLoad((query) => {
|
||||
targetName.value = queryText(query?.name);
|
||||
assignmentReason.value = queryText(query?.reason);
|
||||
const requestedScenario = queryText(query?.scenarioId);
|
||||
assignmentScenarioId.value = isScenarioId(requestedScenario)
|
||||
? requestedScenario
|
||||
: inferSupervisorScenarioId(assignmentReason.value);
|
||||
assignmentScenarioId.value = requestedScenario || inferSupervisorScenarioId(assignmentReason.value);
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
暂不派发
|
||||
</button>
|
||||
<button
|
||||
v-for="item in supervisorScenarioOptions"
|
||||
v-for="item in scenarioOptions"
|
||||
:key="item.id"
|
||||
class="chip scenario-chip"
|
||||
:class="{ active: nextScenarioId === item.id }"
|
||||
@@ -165,7 +165,7 @@ import type { ReviewDetail, ReviewDialogue } from '@/types/api';
|
||||
import { authenticatedFileUrl, releaseAuthenticatedFileUrl } from '@/services/api';
|
||||
import { clearAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { getPracticeReviewDetail, markPracticeReviewed } from '@/services/practice';
|
||||
import { getPracticeReviewDetail, getPracticeScenarios, markPracticeReviewed } from '@/services/practice';
|
||||
import SupervisorTabBar from '@/components/SupervisorTabBar.vue';
|
||||
import {
|
||||
createAsyncGenerationGuard,
|
||||
@@ -173,6 +173,7 @@ import {
|
||||
isSupervisorAccessDenied,
|
||||
reviewCompletionAction,
|
||||
supervisorScenarioOptions,
|
||||
type SupervisorScenarioOption,
|
||||
type SupervisorScenarioId
|
||||
} from '@/services/supervisor-view';
|
||||
|
||||
@@ -182,6 +183,7 @@ const detail = ref<ReviewDetail | null>(null);
|
||||
const advice = ref('');
|
||||
const incentivePoint = ref('');
|
||||
const nextScenarioId = ref<SupervisorScenarioId | ''>('');
|
||||
const scenarioOptions = ref<SupervisorScenarioOption[]>([...supervisorScenarioOptions]);
|
||||
const nextReason = ref('');
|
||||
const loggedIn = ref(false);
|
||||
const accessDenied = ref(false);
|
||||
@@ -309,9 +311,23 @@ const loadDetail = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadScenarioOptions = async () => {
|
||||
try {
|
||||
const rows = await getPracticeScenarios('生活顾问');
|
||||
const nextOptions = rows
|
||||
.filter((item) => item.enabled !== false && item.id && item.name)
|
||||
.map((item) => ({ id: item.id, name: item.name }));
|
||||
if (nextOptions.length) {
|
||||
scenarioOptions.value = nextOptions;
|
||||
}
|
||||
} catch {
|
||||
scenarioOptions.value = [...supervisorScenarioOptions];
|
||||
}
|
||||
};
|
||||
|
||||
const selectNextScenario = (scenarioId: SupervisorScenarioId) => {
|
||||
nextScenarioId.value = scenarioId;
|
||||
const scenario = supervisorScenarioOptions.find((item) => item.id === scenarioId);
|
||||
const scenario = scenarioOptions.value.find((item) => item.id === scenarioId);
|
||||
nextReason.value = `复盘后建议补练${scenario?.name || '专项训练'}`;
|
||||
};
|
||||
|
||||
@@ -402,6 +418,7 @@ onShow(() => {
|
||||
clearDetail();
|
||||
return;
|
||||
}
|
||||
void loadScenarioOptions();
|
||||
if (!detail.value && !loading.value) void loadDetail();
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ import type {
|
||||
PracticeAssignmentRequest,
|
||||
DailyDrillAnswerResponse,
|
||||
PracticeAlert,
|
||||
PromotionEvidence
|
||||
PromotionEvidence,
|
||||
PracticeScenarioOption
|
||||
} from '@/types/api';
|
||||
import { apiRequest } from './api';
|
||||
import { getAuth, getSelectedPosition } from './auth';
|
||||
@@ -35,6 +36,13 @@ export const getPracticePrepCard = (scenarioId = defaultScenarioId) =>
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
export const getPracticeScenarios = (position = getSelectedPosition()) =>
|
||||
apiRequest<PracticeScenarioOption[]>({
|
||||
url: `/api/train/practice/scenarios?position=${encodeURIComponent(position || '生活顾问')}`,
|
||||
method: 'GET',
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
export const startPractice = (scenarioId = defaultScenarioId, assignmentId?: number) =>
|
||||
apiRequest<PracticeStartResponse>({
|
||||
url: '/api/train/practice/start',
|
||||
|
||||
@@ -30,13 +30,18 @@ export interface TeamProfile {
|
||||
risk: 'high' | 'medium' | 'low';
|
||||
}
|
||||
|
||||
export const supervisorScenarioOptions = [
|
||||
export interface SupervisorScenarioOption {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const supervisorScenarioOptions: SupervisorScenarioOption[] = [
|
||||
{ id: 'fee-parking', name: '催费沟通' },
|
||||
{ id: 'complaint-water', name: '投诉接待' },
|
||||
{ id: 'repair-elevator-delay', name: '电梯故障' }
|
||||
] as const;
|
||||
];
|
||||
|
||||
export type SupervisorScenarioId = typeof supervisorScenarioOptions[number]['id'];
|
||||
export type SupervisorScenarioId = string;
|
||||
|
||||
export interface AsyncGenerationGuard {
|
||||
capture: () => number;
|
||||
|
||||
@@ -252,6 +252,14 @@ export interface PracticeTeamSnapshot {
|
||||
mistakes: PracticeMistakeAggregate[];
|
||||
}
|
||||
|
||||
export interface PracticeScenarioOption {
|
||||
id: string;
|
||||
name: string;
|
||||
enabled?: boolean;
|
||||
position?: string;
|
||||
scenarioType?: string;
|
||||
}
|
||||
|
||||
export interface ReviewDialogue {
|
||||
role: string;
|
||||
label: string;
|
||||
|
||||
Reference in New Issue
Block a user