fix(aihr): close Figma UI regression gaps

This commit is contained in:
2026-07-17 04:17:14 +08:00
parent a822b22116
commit e93421ca1e
35 changed files with 1452 additions and 107 deletions
+3
View File
@@ -134,3 +134,6 @@ export const consumeLoginRedirect = () => {
};
export const validatePhone = (phone: string) => phonePattern.test(phone);
export const maskPhone = (phone: string) =>
/^\d{8,}$/.test(phone) ? `${phone.slice(0, 3)}****${phone.slice(-4)}` : phone;
+5 -1
View File
@@ -49,6 +49,8 @@ export const submitAnswerFeedback = (params: {
fragmentIds: number[];
verdict: AnswerFeedbackVerdict;
reviewId?: number | null;
reasonCodes?: string[];
comment?: string;
}) =>
apiRequest<{ saved: number }>({
url: '/api/knowledge/answer-feedback',
@@ -60,7 +62,9 @@ export const submitAnswerFeedback = (params: {
category: 'sop',
position: getSelectedPosition(),
source: 'mobile_uni_sop',
reviewId: params.reviewId ?? null
reviewId: params.reviewId ?? null,
reasonCodes: params.reasonCodes || [],
comment: params.comment || ''
},
timeout: 15000
});
+57 -2
View File
@@ -15,6 +15,12 @@ export interface SupervisorMetric {
primary?: boolean;
}
export interface TrainingTrendPoint {
key: string;
label: string;
value: number;
}
export interface TeamProfile {
key: string;
extPartyId: string;
@@ -98,8 +104,28 @@ export const manualPracticeAssignments = (assignments: PracticeAssignment[]) =>
export const actionablePracticeAlerts = (alerts: PracticeAlert[]) =>
alerts.filter((item) => item.type !== 'stable');
export const buildTrainingTrend = (records: PracticeRecord[]): TrainingTrendPoint[] =>
records
.filter((item) => Number.isFinite(item.score))
.sort((a, b) => practiceTimeValue(b.time) - practiceTimeValue(a.time))
.slice(0, 5)
.reverse()
.map((item, index) => ({
key: `${item.id || item.sessionId || item.time}-${index}`,
label: trainingTrendLabel(item.time, index),
value: Math.max(0, Math.min(100, item.score))
}));
export const isReviewEditable = (status: string | null | undefined) => status === '待复盘';
export type ReviewFilter = 'all' | 'low' | 'pending';
export const filterReviewRecords = (reviews: PracticeRecord[], filter: ReviewFilter) => {
if (filter === 'low') return reviews.filter((item) => item.score < 80);
if (filter === 'pending') return reviews.filter((item) => isReviewEditable(item.status));
return reviews;
};
export const isSupervisorAccessDenied = (error: unknown) => {
const message = error instanceof Error
? error.message
@@ -131,14 +157,14 @@ export const buildSupervisorMetrics = (
return [
{
key: 'pendingReviews',
title: '当前待复盘',
title: '待复盘',
value: pendingReviews,
desc: pendingReviews ? '优先处理低分和待处理记录' : '暂无待复盘记录',
primary: true
},
{
key: 'pendingAssignments',
title: '当前待训练',
title: '待训练',
value: pendingAssignments,
desc: pendingAssignments ? '仍有专项训练待完成' : '暂无未完成专项'
},
@@ -271,12 +297,41 @@ export const buildTeamProfiles = (
|| (a.latestScore ?? 100) - (b.latestScore ?? 100));
};
export const filterTeamProfiles = <T extends Pick<TeamProfile, 'displayName' | 'extPartyId'>>(
profiles: T[],
query: string
) => {
const keyword = query.trim().toLowerCase();
if (!keyword) return profiles;
return profiles.filter((item) =>
item.displayName.toLowerCase().includes(keyword)
|| item.extPartyId.toLowerCase().includes(keyword));
};
export const paginateTeamProfiles = <T>(profiles: T[], requestedPage: number, pageSize: number) => {
const safeSize = Math.max(1, Math.floor(pageSize));
const pageCount = Math.max(1, Math.ceil(profiles.length / safeSize));
const page = Math.min(pageCount, Math.max(1, Math.floor(requestedPage)));
return {
items: profiles.slice((page - 1) * safeSize, page * safeSize),
page,
pageCount
};
};
function identitySuffix(value: string) {
if (/^1[3-9]\d{9}$/.test(value)) return `尾号${value.slice(-4)}`;
if (value.length > 8) return `末尾${value.slice(-6)}`;
return value;
}
function trainingTrendLabel(value: string, index: number) {
const parts = value.match(/\d+/g) || [];
if (parts.length >= 3 && parts[0].length === 4) return `${parts[1].padStart(2, '0')}-${parts[2].padStart(2, '0')}`;
if (parts.length >= 2) return `${parts[0].padStart(2, '0')}-${parts[1].padStart(2, '0')}`;
return `第${index + 1}次`;
}
function practiceTimeValue(value: string) {
const parts = value.match(/\d+/g)?.map(Number) || [];
if (parts.length < 4 || parts.some((part) => !Number.isFinite(part))) {