feat(aihr): expose position SOP learning scope
This commit is contained in:
@@ -108,6 +108,13 @@
|
||||
{{ knowledgeLoading ? '检索中' : '重查' }}
|
||||
</button>
|
||||
</view>
|
||||
<view v-if="positionSopSummary" class="position-sop-status">
|
||||
<text class="position-sop-title">
|
||||
{{ positionSopSummary.position }} · {{ positionSopSummary.topics.length }} 个学习主题
|
||||
</text>
|
||||
<text class="position-sop-note">{{ positionSopSummary.message }}</text>
|
||||
<text v-if="!positionSopSummary.formalApplicabilityConfigured" class="position-sop-warning">当前摘要不代表正式上岗资格</text>
|
||||
</view>
|
||||
<view class="chip-row topic-row">
|
||||
<button
|
||||
v-for="topic in topics"
|
||||
@@ -177,12 +184,13 @@ import { computed, ref } from 'vue';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
import type {
|
||||
KnowledgeSearchResponse,
|
||||
PositionSopSummary,
|
||||
PracticeAssignment,
|
||||
PracticeMistake,
|
||||
PracticePrepCard
|
||||
} from '@/types/api';
|
||||
import { getPracticeAssignments, getPracticeMistakes, getPracticePrepCard } from '@/services/practice';
|
||||
import { searchKnowledge } from '@/services/knowledge';
|
||||
import { getPositionSopSummary, searchKnowledge } from '@/services/knowledge';
|
||||
import { getSelectedPositionProfile, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { buildLearningTopics, selectLearningTopic } from '@/services/learning';
|
||||
import { navigateBackToToday, resetPageScroll } from '@/services/navigation';
|
||||
@@ -198,6 +206,7 @@ const assignments = ref<PracticeAssignment[]>([]);
|
||||
const mistakes = ref<PracticeMistake[]>([]);
|
||||
const prepCard = ref<PracticePrepCard | null>(null);
|
||||
const knowledgeResult = ref<KnowledgeSearchResponse | null>(null);
|
||||
const positionSopSummary = ref<PositionSopSummary | null>(null);
|
||||
const activeTopic = ref('岗前 SOP');
|
||||
const assignmentLoading = ref(false);
|
||||
const mistakeLoading = ref(false);
|
||||
@@ -311,6 +320,16 @@ const loadKnowledge = async (topic: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadPositionSopSummary = async () => {
|
||||
if (!loggedIn.value) return;
|
||||
try {
|
||||
positionSopSummary.value = await getPositionSopSummary(positionProfile.value.value);
|
||||
} catch (error) {
|
||||
positionSopSummary.value = null;
|
||||
handleRequestError(error, '岗位学习范围加载失败');
|
||||
}
|
||||
};
|
||||
|
||||
const loadPage = async () => {
|
||||
refreshAuthState();
|
||||
message.value = '';
|
||||
@@ -319,6 +338,7 @@ const loadPage = async () => {
|
||||
mistakes.value = [];
|
||||
prepCard.value = null;
|
||||
knowledgeResult.value = null;
|
||||
positionSopSummary.value = null;
|
||||
return;
|
||||
}
|
||||
if (!(await ensureEmployeePosition('/pages/user/learning/index'))) {
|
||||
@@ -328,6 +348,7 @@ const loadPage = async () => {
|
||||
await Promise.all([loadAssignments(), loadMistakes()]);
|
||||
activeTopic.value = selectLearningTopic(assignments.value, defaultTopic.value);
|
||||
await Promise.all([
|
||||
loadPositionSopSummary(),
|
||||
loadKnowledge(activeTopic.value),
|
||||
loadPrepCard()
|
||||
]);
|
||||
@@ -383,6 +404,33 @@ onShow(() => {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.position-sop-status {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
margin-bottom: 12px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
background: #f3f7ff;
|
||||
}
|
||||
|
||||
.position-sop-title {
|
||||
color: #183b70;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.position-sop-note {
|
||||
color: #60708a;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.position-sop-warning {
|
||||
color: #9a6700;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.mistake-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
|
||||
@@ -2,6 +2,7 @@ import type {
|
||||
AnswerFeedbackVerdict,
|
||||
KnowledgeSearchResponse,
|
||||
KnowledgeSnippet,
|
||||
PositionSopSummary,
|
||||
SummaryCardResponse
|
||||
} from '@/types/api';
|
||||
import { apiRequest } from './api';
|
||||
@@ -36,6 +37,13 @@ export const searchKnowledge = async (queryText: string, position = getSelectedP
|
||||
};
|
||||
};
|
||||
|
||||
export const getPositionSopSummary = (position = getSelectedPosition()) =>
|
||||
apiRequest<PositionSopSummary>({
|
||||
url: `/api/knowledge/position-sop?position=${encodeURIComponent(position)}&projectType=住宅`,
|
||||
method: 'GET',
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
export const submitAnswerFeedback = (params: {
|
||||
queryText: string;
|
||||
fragmentIds: number[];
|
||||
|
||||
@@ -103,6 +103,23 @@ export interface KnowledgeSearchResponse {
|
||||
cautions?: string[];
|
||||
}
|
||||
|
||||
export interface PositionSopTopic {
|
||||
category: string;
|
||||
question: string;
|
||||
reference: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface PositionSopSummary {
|
||||
position: string;
|
||||
projectType: string;
|
||||
routingMode: string;
|
||||
formalApplicabilityConfigured: boolean;
|
||||
qualificationStatus: string;
|
||||
message: string;
|
||||
topics: PositionSopTopic[];
|
||||
}
|
||||
|
||||
export type AnswerFeedbackVerdict = 'up' | 'down';
|
||||
|
||||
export interface SummaryCardStep {
|
||||
|
||||
@@ -148,3 +148,14 @@ test('学习页展示真实错题本并可进入已有再练任务', async () =>
|
||||
assert.match(source, /pendingAssignmentKey/);
|
||||
assert.match(practiceService, /\/api\/aihr\/mobile\/practice\/mistakes/);
|
||||
});
|
||||
|
||||
test('学习页显示岗位 SOP 适配状态而不冒充上岗资格', async () => {
|
||||
const [source, knowledgeService] = await Promise.all([
|
||||
pageSource('../src/pages/user/learning/index.vue'),
|
||||
pageSource('../src/services/knowledge.ts')
|
||||
]);
|
||||
|
||||
assert.match(source, /positionSopSummary/);
|
||||
assert.match(source, /正式上岗资格/);
|
||||
assert.match(knowledgeService, /\/api\/knowledge\/position-sop/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user