feat(aihr): filter supervisor reviews by org scope
This commit is contained in:
@@ -154,10 +154,17 @@ export type OrgSnapshotResponse = {
|
||||
rows: OrgPersonRow[];
|
||||
};
|
||||
|
||||
export function listPracticeReviews(): Promise<ApiResult<ReviewRecord[]>> {
|
||||
export type ReviewListParams = {
|
||||
projectCode?: string;
|
||||
extPartyId?: string;
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
export function listPracticeReviews(params?: ReviewListParams): Promise<ApiResult<ReviewRecord[]>> {
|
||||
return request({
|
||||
url: '/api/aihr/mobile/practice/reviews',
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,17 @@
|
||||
<p>展示能力画像、初/中/高级证据包、预警、待训任务和学分规则最小集。</p>
|
||||
</div>
|
||||
<div class="head-actions">
|
||||
<el-input v-model="extPartyId" clearable placeholder="员工手机号或 extPartyId" />
|
||||
<el-select v-model="selectedProjectCode" clearable filterable placeholder="项目范围">
|
||||
<el-option v-for="item in orgSnapshot?.projects || []" :key="item.projectCode" :label="item.projectName" :value="item.projectCode" />
|
||||
</el-select>
|
||||
<el-select v-model="extPartyId" clearable filterable allow-create default-first-option placeholder="组织人员或 extPartyId">
|
||||
<el-option
|
||||
v-for="person in orgPeople"
|
||||
:key="person.extPartyId"
|
||||
:label="`${person.personName} / ${person.positionName}`"
|
||||
:value="person.extPartyId"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="danger" :icon="Refresh" :loading="loading" @click="loadAll">刷新</el-button>
|
||||
</div>
|
||||
</section>
|
||||
@@ -123,27 +133,31 @@
|
||||
<script setup name="CompetencyGrowth" lang="ts">
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { listPracticeScenarios } from '@/api/aihr/practice';
|
||||
import {
|
||||
createPracticeAssignment,
|
||||
getCompetencyProfile,
|
||||
getOrgSnapshot,
|
||||
getPromotionEvidence,
|
||||
listPracticeAlerts,
|
||||
listPracticeAssignments,
|
||||
type CompetencyProfile,
|
||||
type OrgSnapshotResponse,
|
||||
type PracticeAlert,
|
||||
type PracticeAssignment,
|
||||
type PromotionEvidence
|
||||
} from '@/api/aihr/mobile-admin';
|
||||
|
||||
const extPartyId = ref('');
|
||||
const selectedProjectCode = ref('');
|
||||
const loading = ref(false);
|
||||
const leaderboardEnabled = ref(false);
|
||||
const profile = ref<CompetencyProfile | null>(null);
|
||||
const evidence = ref<PromotionEvidence | null>(null);
|
||||
const alerts = ref<PracticeAlert[]>([]);
|
||||
const assignments = ref<PracticeAssignment[]>([]);
|
||||
const orgSnapshot = ref<OrgSnapshotResponse | null>(null);
|
||||
|
||||
const rules = [
|
||||
{ source: '训练完成', points: '+5', guard: '只认完成记录,不按点击或打开计分' },
|
||||
@@ -152,6 +166,23 @@ const rules = [
|
||||
{ source: '课程考试通过', points: '+10', guard: '阶段二课程考试上线后启用' }
|
||||
];
|
||||
|
||||
const orgPeople = computed(() => orgSnapshot.value?.rows || []);
|
||||
const selectedPerson = computed(() => orgPeople.value.find((item) => item.extPartyId === extPartyId.value));
|
||||
|
||||
const loadOrgSnapshot = async () => {
|
||||
try {
|
||||
const result = await getOrgSnapshot({
|
||||
status: 'active',
|
||||
projectCode: selectedProjectCode.value,
|
||||
pageNum: 1,
|
||||
pageSize: 500
|
||||
});
|
||||
orgSnapshot.value = result.data;
|
||||
} catch (error) {
|
||||
orgSnapshot.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const loadAll = async () => {
|
||||
loading.value = true;
|
||||
const party = extPartyId.value.trim();
|
||||
@@ -184,7 +215,7 @@ const createManualAssignment = async () => {
|
||||
const scenario = scenarios.find((item) => item.enabled) || scenarios[0];
|
||||
await createPracticeAssignment({
|
||||
extPartyId: party,
|
||||
traineeName: party,
|
||||
traineeName: selectedPerson.value?.personName || party,
|
||||
scenarioId: scenario?.id || 'fee-parking',
|
||||
scenarioName: scenario?.name,
|
||||
source: 'manual',
|
||||
@@ -197,7 +228,19 @@ const createManualAssignment = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(loadAll);
|
||||
watch(selectedProjectCode, async () => {
|
||||
extPartyId.value = '';
|
||||
await loadOrgSnapshot();
|
||||
});
|
||||
|
||||
watch(extPartyId, () => {
|
||||
loadAll();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadOrgSnapshot();
|
||||
loadAll();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -236,7 +279,7 @@ onMounted(loadAll);
|
||||
|
||||
.head-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 260px auto;
|
||||
grid-template-columns: 220px 280px auto;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@@ -357,6 +400,8 @@ onMounted(loadAll);
|
||||
}
|
||||
|
||||
.head-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,22 @@
|
||||
<article class="panel list-panel">
|
||||
<div class="panel-title">
|
||||
<h2>待复盘记录</h2>
|
||||
<el-tag type="danger">{{ reviews.length }} 条</el-tag>
|
||||
<el-tag type="danger">{{ visibleReviews.length }} 条</el-tag>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="reviews" size="small" highlight-current-row @row-click="selectReview">
|
||||
<div class="list-filters">
|
||||
<el-select v-model="selectedProjectCode" clearable filterable placeholder="项目范围">
|
||||
<el-option v-for="item in orgSnapshot?.projects || []" :key="item.projectCode" :label="item.projectName" :value="item.projectCode" />
|
||||
</el-select>
|
||||
<el-select v-model="selectedExtPartyId" clearable filterable placeholder="组织人员">
|
||||
<el-option
|
||||
v-for="person in orgPeople"
|
||||
:key="person.extPartyId"
|
||||
:label="`${person.personName} / ${person.positionName}`"
|
||||
:value="person.extPartyId"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="visibleReviews" size="small" highlight-current-row @row-click="selectReview">
|
||||
<el-table-column prop="trainee" label="学员" width="96" />
|
||||
<el-table-column prop="scene" label="场景" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="score" label="得分" width="70" />
|
||||
@@ -92,15 +105,26 @@
|
||||
<script setup name="TrainReviews" lang="ts">
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { listPracticeScenarios, type PracticeScenarioResponse } from '@/api/aihr/practice';
|
||||
import { getPracticeReview, listPracticeReviews, markPracticeReviewed, type ReviewDetail, type ReviewRecord } from '@/api/aihr/mobile-admin';
|
||||
import {
|
||||
getOrgSnapshot,
|
||||
getPracticeReview,
|
||||
listPracticeReviews,
|
||||
markPracticeReviewed,
|
||||
type OrgSnapshotResponse,
|
||||
type ReviewDetail,
|
||||
type ReviewRecord
|
||||
} from '@/api/aihr/mobile-admin';
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const reviews = ref<ReviewRecord[]>([]);
|
||||
const detail = ref<ReviewDetail | null>(null);
|
||||
const scenarios = ref<PracticeScenarioResponse[]>([]);
|
||||
const orgSnapshot = ref<OrgSnapshotResponse | null>(null);
|
||||
const selectedProjectCode = ref('');
|
||||
const selectedExtPartyId = ref('');
|
||||
|
||||
const reviewForm = reactive({
|
||||
reviewer: '主管',
|
||||
@@ -110,6 +134,9 @@ const reviewForm = reactive({
|
||||
nextReason: ''
|
||||
});
|
||||
|
||||
const orgPeople = computed(() => orgSnapshot.value?.rows || []);
|
||||
const visibleReviews = computed(() => reviews.value);
|
||||
|
||||
const loadScenarios = async () => {
|
||||
try {
|
||||
const result = await listPracticeScenarios();
|
||||
@@ -119,14 +146,30 @@ const loadScenarios = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadOrgSnapshot = async () => {
|
||||
try {
|
||||
const result = await getOrgSnapshot({
|
||||
status: 'active',
|
||||
projectCode: selectedProjectCode.value,
|
||||
pageNum: 1,
|
||||
pageSize: 500
|
||||
});
|
||||
orgSnapshot.value = result.data;
|
||||
} catch (error) {
|
||||
orgSnapshot.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const loadReviews = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await listPracticeReviews();
|
||||
const result = await listPracticeReviews({
|
||||
projectCode: selectedProjectCode.value,
|
||||
extPartyId: selectedExtPartyId.value,
|
||||
limit: 50
|
||||
});
|
||||
reviews.value = result.data || [];
|
||||
if (!detail.value && reviews.value.length > 0) {
|
||||
await selectReview(reviews.value[0]);
|
||||
}
|
||||
await selectFirstVisibleReview();
|
||||
} catch (error) {
|
||||
ElMessage.error('待复盘列表加载失败');
|
||||
} finally {
|
||||
@@ -143,6 +186,16 @@ const selectReview = async (row: ReviewRecord) => {
|
||||
reviewForm.nextReason = '';
|
||||
};
|
||||
|
||||
const selectFirstVisibleReview = async () => {
|
||||
if (visibleReviews.value.some((item) => item.id === detail.value?.id)) return;
|
||||
const first = visibleReviews.value[0];
|
||||
if (!first) {
|
||||
detail.value = null;
|
||||
return;
|
||||
}
|
||||
await selectReview(first);
|
||||
};
|
||||
|
||||
const submitReview = async () => {
|
||||
if (!detail.value) return;
|
||||
saving.value = true;
|
||||
@@ -164,8 +217,19 @@ const submitReview = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
watch(selectedProjectCode, async () => {
|
||||
selectedExtPartyId.value = '';
|
||||
await loadOrgSnapshot();
|
||||
await loadReviews();
|
||||
});
|
||||
|
||||
watch(selectedExtPartyId, () => {
|
||||
loadReviews();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadScenarios();
|
||||
loadOrgSnapshot();
|
||||
loadReviews();
|
||||
});
|
||||
</script>
|
||||
@@ -227,6 +291,13 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.list-filters {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.score-grid,
|
||||
.form-grid {
|
||||
display: grid;
|
||||
@@ -287,6 +358,7 @@ onMounted(() => {
|
||||
|
||||
@media (max-width: 1080px) {
|
||||
.review-layout,
|
||||
.list-filters,
|
||||
.score-grid,
|
||||
.form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
Reference in New Issue
Block a user