feat(aihr): filter supervisor reviews by org scope

This commit is contained in:
2026-07-09 12:43:16 +08:00
parent 45d06f56cd
commit 9bd567a72f
8 changed files with 281 additions and 40 deletions
+80 -8
View File
@@ -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;