feat(aihr): add supervisor mistake aggregation

This commit is contained in:
2026-07-14 04:27:10 +08:00
parent 37ddb307cf
commit cd2c7fdd45
8 changed files with 185 additions and 7 deletions
+100 -1
View File
@@ -46,6 +46,27 @@
</view>
</view>
<view class="panel mistake-panel">
<view class="section-head">
<text class="panel-title">团队错题本</text>
<text class="muted">按项目范围聚合</text>
</view>
<view v-if="mistakes.length" class="mistake-list">
<view v-for="item in mistakes" :key="`${item.scenarioId}-${item.cause}`" class="mistake-row">
<view class="mistake-main">
<view class="mistake-title-row">
<text class="mistake-title">{{ item.scenarioName }}</text>
<text class="mistake-count">{{ item.count }} 次</text>
</view>
<text class="mistake-cause">{{ item.cause }} · {{ item.affectedPeople }} 名成员 · 平均 {{ item.averageScore }} 分</text>
<text class="mistake-time">最近发生 {{ item.latestTime || '暂无时间' }}</text>
</view>
<button class="secondary-button mistake-action" @click="openReviews">去待复盘</button>
</view>
</view>
<view v-else class="empty compact-empty">暂无团队错题记录</view>
</view>
<view class="panel team-panel">
<view class="section-head">
<text class="panel-title">成员列表</text>
@@ -120,7 +141,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import type { PracticeAssignment, PracticeRecord, PracticeTeamMember } from '@/types/api';
import type { PracticeAssignment, PracticeMistakeAggregate, PracticeRecord, PracticeTeamMember } from '@/types/api';
import { clearAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
import { resetPageScroll } from '@/services/navigation';
import { getPracticeTeamSnapshot } from '@/services/practice';
@@ -136,6 +157,7 @@ const currentPath = '/pages/supervisor/team/index';
const records = ref<PracticeRecord[]>([]);
const assignments = ref<PracticeAssignment[]>([]);
const members = ref<PracticeTeamMember[]>([]);
const mistakes = ref<PracticeMistakeAggregate[]>([]);
const loggedIn = ref(false);
const accessDenied = ref(false);
const loading = ref(false);
@@ -161,6 +183,7 @@ const clearData = () => {
records.value = [];
assignments.value = [];
members.value = [];
mistakes.value = [];
selectedMember.value = null;
};
@@ -201,6 +224,7 @@ const loadPage = async () => {
records.value = snapshot.records;
assignments.value = snapshot.assignments;
members.value = snapshot.members;
mistakes.value = snapshot.mistakes ?? [];
} catch (error) {
handleRequestError(error, '团队画像加载失败');
} finally {
@@ -223,6 +247,10 @@ const selectMember = (member: TeamProfile) => {
selectedMember.value = selectedMember.value?.key === member.key ? null : member;
};
const openReviews = () => {
uni.navigateTo({ url: '/pages/supervisor/reviews/index' });
};
const goLogin = () => {
rememberLoginRedirect(currentPath);
uni.redirectTo({ url: '/pages/auth/login/index' });
@@ -340,6 +368,77 @@ onShow(() => {
color: var(--employee-primary);
}
.mistake-panel {
margin-top: 14px;
}
.mistake-list {
margin-top: 8px;
}
.mistake-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 12px 0;
border-bottom: 1px solid #e5edf7;
}
.mistake-row:last-child {
border-bottom: 0;
}
.mistake-main {
min-width: 0;
flex: 1;
}
.mistake-title-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.mistake-title,
.mistake-cause,
.mistake-time {
display: block;
}
.mistake-title {
overflow: hidden;
color: var(--employee-text);
font-size: 14px;
font-weight: 900;
text-overflow: ellipsis;
white-space: nowrap;
}
.mistake-count {
flex: 0 0 auto;
color: #b91c1c;
font-size: 13px;
font-weight: 900;
}
.mistake-cause,
.mistake-time {
margin-top: 5px;
color: var(--employee-muted);
font-size: 11px;
line-height: 1.4;
}
.mistake-action {
min-width: 64px;
min-height: 34px;
flex: 0 0 auto;
padding: 0 8px;
font-size: 12px;
}
.team-list {
margin-top: 8px;
}
+11
View File
@@ -224,6 +224,16 @@ export interface PracticeMistake {
summary: string;
}
export interface PracticeMistakeAggregate {
scenarioId: string;
scenarioName: string;
cause: string;
count: number;
affectedPeople: number;
averageScore: number;
latestTime: string;
}
export interface PracticeTeamMember {
extPartyId: string;
name: string;
@@ -233,6 +243,7 @@ export interface PracticeTeamSnapshot {
members: PracticeTeamMember[];
records: PracticeRecord[];
assignments: PracticeAssignment[];
mistakes: PracticeMistakeAggregate[];
}
export interface ReviewDialogue {