import React, { useEffect, useState } from 'react'; import { Card, Descriptions, Tag, Button, Space, Typography, message, Spin, Row, Col, Divider, Form, InputNumber, Input, Modal, Alert, List, } from 'antd'; import { CheckOutlined, CloseOutlined, ArrowLeftOutlined, } from '@ant-design/icons'; import { useParams, useNavigate } from 'react-router-dom'; import { PerformanceRecord, PerformanceItemDetail } from '../../api/performance'; import http from '../../api/http'; import { useIsMobile } from '../../hooks/useBreakpoint'; import dayjs from 'dayjs'; const { Title, Text } = Typography; const { TextArea } = Input; const STATUS_MAP: Record = { draft: { label: '草稿', color: 'default' }, submitted: { label: '已提交', color: 'processing' }, under_review: { label: '审核中', color: 'warning' }, completed: { label: '已完成', color: 'success' }, rejected: { label: '已驳回', color: 'error' }, }; const LEVEL_MAP: Record = { excellent: { label: '优秀', color: 'gold' }, qualified: { label: '合格', color: 'green' }, need_motivation: { label: '需激励', color: 'orange' }, unqualified: { label: '不合格', color: 'red' }, }; interface ItemScoreForm { managerScore: number; scoreExplanation: string; } interface ReviewFormData { reviewOpinion: string; itemScores: Record; } const PerformanceReview: React.FC = () => { const { perfId } = useParams<{ perfId: string }>(); const navigate = useNavigate(); const isMobile = useIsMobile(); const [form] = Form.useForm(); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); const [record, setRecord] = useState(null); const [rejectModalOpen, setRejectModalOpen] = useState(false); const [rejectReason, setRejectReason] = useState(''); useEffect(() => { loadDetail(); }, [perfId]); const loadDetail = async () => { if (!perfId) return; setLoading(true); try { const { data } = await http.get( `/api/performance/manager/detail/${perfId}` ); const record = data.data || data; setRecord(record); // Initialize form with existing manager scores if available const itemScores: Record = {}; data.performanceItems?.forEach((item: PerformanceItemDetail) => { itemScores[item.itemName] = { managerScore: item.managerScore ?? item.aiScore ?? item.selfScore ?? 0, scoreExplanation: item.managerExplanation ?? '', }; }); form.setFieldsValue({ reviewOpinion: data.reviewOpinion ?? '', itemScores, }); } catch (err: any) { message.error(err?.response?.data?.message || '加载绩效详情失败'); navigate('/manager/subordinates'); } finally { setLoading(false); } }; const handleApprove = async () => { try { const values = await form.validateFields(); setSubmitting(true); const itemScores = Object.entries(values.itemScores).map( ([itemName, score]) => ({ itemName, managerScore: score.managerScore, managerExplanation: score.scoreExplanation || '', }) ); await http.post('/api/performance/manager/review', { perfId: Number(perfId), reviewOpinion: values.reviewOpinion, itemScores, }); message.success('审核通过,绩效已归档'); navigate('/manager/subordinates'); } catch (err: any) { if (err?.errorFields) { message.error('请完成所有必填项'); } else { message.error(err?.response?.data?.message || '审核失败,请重试'); } } finally { setSubmitting(false); } }; const handleReject = async () => { if (!rejectReason.trim()) { message.error('请填写驳回原因'); return; } setSubmitting(true); try { await http.post('/api/performance/manager/reject', { perfId: Number(perfId), reason: rejectReason, }); message.success('绩效已驳回,员工将收到通知'); navigate('/manager/subordinates'); } catch (err: any) { message.error(err?.response?.data?.message || '驳回失败,请重试'); } finally { setSubmitting(false); setRejectModalOpen(false); } }; if (loading) { return (
); } if (!record) { return null; } const status = STATUS_MAP[record.status] || { label: record.status, color: 'default', }; const level = record.level ? LEVEL_MAP[record.level] : null; const isCompleted = record.status === 'completed'; return (
绩效审核 — {record.month} {isCompleted && <Tag color="success" style={{ marginLeft: 8 }}>已完成</Tag>} {/* 基础信息 */} {isMobile ? (
{[ { label: '员工姓名', value: (record as any).userName || '-' }, { label: '部门', value: (record as any).userDepartment || '-' }, { label: '岗位', value: (record as any).userPosition || '-' }, { label: '考核月份', value: record.month }, { label: '状态', value: {status.label} }, { label: '提交时间', value: record.submitTime ? dayjs(record.submitTime).format('YYYY-MM-DD HH:mm') : '-' }, { label: '自评总分', value: record.selfScore != null ? Number(record.selfScore).toFixed(1) : '-' }, { label: 'AI 评分', value: record.aiScore != null ? Number(record.aiScore).toFixed(1) : '-' }, { label: '最终总分', value: {record.totalScore != null ? Number(record.totalScore).toFixed(1) : '-'} }, ].map(({ label, value }) => (
{label}
{value}
))} {level && (
绩效等级
{level.label}
)} {isCompleted && record.reviewOpinion && (
审核意见
{record.reviewOpinion}
)}
) : ( {(record as any).userName || '-'} {(record as any).userDepartment || '-'} {(record as any).userPosition || '-'} {record.month} {status.label} {record.submitTime ? dayjs(record.submitTime).format('YYYY-MM-DD HH:mm') : '-'} {record.selfScore != null ? Number(record.selfScore).toFixed(1) : '-'} {record.aiScore != null ? Number(record.aiScore).toFixed(1) : '-'} {record.totalScore != null ? Number(record.totalScore).toFixed(1) : '-'} {level && {level.label}} {record.rewardPunish && {record.rewardPunish}} {isCompleted && record.reviewOpinion && ( {record.reviewOpinion} )} )}
{/* 工作汇总 */} {record.workSummary && ( {record.workSummary} )} {/* 考勤信息 */} {record.attendance && ( 事假:{record.attendance.leave} 天 迟到:{record.attendance.late} 次 旷工:{record.attendance.absent} 天 缺卡:{record.attendance.lackCard} 次 {record.attendance.attendanceScore != null && ( 考勤得分:{record.attendance.attendanceScore} )} )} {/* AI 评分反馈 */} {record.aiResult && ( {record.aiResult.aiTotalScore != null ? Number(record.aiResult.aiTotalScore).toFixed(1) : '-'} {record.aiResult.aiProblems?.length > 0 && ( <> AI 总结的核心问题 ( {i + 1}. {item} )} /> )} {record.aiResult.aiSuggestions?.length > 0 && ( <> AI 改进建议 ( {i + 1}. {item} )} /> )} )} {/* 考核项评分 */} {!isCompleted && ( )}
{record.performanceItems?.map((item) => ( {item.itemName} {item.weight} 分 {item.itemCategory === 'business' ? '业务素质' : '综合素质'} } > {isMobile ? ( // 移动端简化展示
{item.userContent || '-'} 自评
{item.selfScore ?? '-'} AI评分
{item.aiScore ?? '-'} 管理层
{item.managerScore ?? '-'}
{item.aiExplanation && AI说明:{item.aiExplanation}}
) : ( {item.userContent || '-'} {item.selfScore ?? '-'} {item.aiScore ?? '-'} {item.managerScore ?? '-'} {item.aiExplanation && {item.aiExplanation}} )} {!isCompleted && (
)} {isCompleted && item.managerExplanation && (
管理层评分说明: {item.managerExplanation}
)}
))} {/* 审核意见 */}