import { Ionicons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Text, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import Svg, { Circle, Line, Path, Text as SvgText } from 'react-native-svg'; import { useHREvaluation } from '../src/hooks'; import { colors, levelLabelTextColors } from '../src/theme'; import type { EvaluationDimension, EvaluationReview, EvaluationTrendPoint } from '../src/types'; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack, onAppeal }: { onBack: () => void; onAppeal: () => void }) { return ( HR评价详情 申诉 ); } /* ===================== 子组件:综合评分卡 ===================== */ function ScoreCard({ score, percentile, change, }: { score: number; percentile: number; change: number; }) { return ( 综合评分 {score} 分 良好 超过 {percentile}% 的求职者 = 0 ? styles.changeUp : styles.changeDown}> {' '} 较上期 {change >= 0 ? '+' : ''} {change} ); } /* ===================== 子组件:评分趋势折线图 ===================== */ function TrendChart({ trend }: { trend: EvaluationTrendPoint[] }) { const width = 320; const height = 150; const padding = { top: 20, right: 20, bottom: 28, left: 32 }; const chartW = width - padding.left - padding.right; const chartH = height - padding.top - padding.bottom; const max = 100; const min = 75; const points = trend.map((t, i) => ({ x: padding.left + (i / (trend.length - 1)) * chartW, y: padding.top + (1 - (t.score - min) / (max - min)) * chartH, score: t.score, month: t.month, })); const d = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' '); const last = points[points.length - 1]; return ( 评分趋势 {[80, 85, 90, 95, 100].map((v) => { const y = padding.top + (1 - (v - min) / (max - min)) * chartH; return ( ); })} {[80, 90, 100].map((v) => { const y = padding.top + (1 - (v - min) / (max - min)) * chartH; return ( {v} ); })} {points.map((p, i) => ( ))} {points.map((p) => ( {p.month} ))} ); } /* ===================== 子组件:六维能力评分 ===================== */ function DimensionCard({ dimensions }: { dimensions: EvaluationDimension[] }) { return ( 六维能力评分 {dimensions.map((dim) => ( {dim.name} {dim.score} {dim.level} {dim.change !== undefined && ( = 0 ? styles.changeUp : styles.changeDown}> {dim.change >= 0 ? '+' : ''} {dim.change} )} ))} ); } /* ===================== 子组件:HR 评价列表 ===================== */ function ReviewCard({ review }: { review: EvaluationReview }) { return ( {review.company} {review.rating}.0 {review.date} {review.content} {review.tags.map((tag) => ( {tag} ))} ); } /* ===================== 子组件:评价说明 ===================== */ function RulesCard({ rules }: { rules: string[] }) { return ( 评价说明 {rules.map((rule, i) => ( {rule} ))} ); } /* ===================== 子组件:四态 ===================== */ function Loading() { return ( 正在加载评价… ); } function ErrorState({ onRetry }: { onRetry: () => void }) { return ( 评价加载失败 点击重试 ); } /* ===================== 页面 ===================== */ export default function HREvaluationScreen() { const router = useRouter(); const { evaluation, loading, error, refresh } = useHREvaluation(); if (loading) { return (
router.back()} onAppeal={() => router.push('/appeal')} /> ); } if (error || !evaluation) { return (
router.back()} onAppeal={() => router.push('/appeal')} /> ); } return (
router.back()} onAppeal={() => router.push('/appeal')} /> HR评价({evaluation.reviews.length}) {/* 标题里带着数量,为 0 时下方不能是一片空白 */} {evaluation.reviews.length === 0 ? ( 暂时还没有 HR 留下评价 ) : ( evaluation.reviews.map((review) => ) )} 对评分有疑问? router.push('/appeal')}> 发起申诉 ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: colors.pageBg }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 12, paddingVertical: 12, backgroundColor: colors.white, }, headerBtn: { width: 40, alignItems: 'center' }, headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary }, headerAppealBtn: { paddingHorizontal: 12, paddingVertical: 5, borderRadius: 14, backgroundColor: colors.primaryLight, }, headerAppealText: { fontSize: 13, fontWeight: '600', color: colors.primaryText }, body: { paddingHorizontal: 16, paddingBottom: 100, paddingTop: 12 }, center: { flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 40, gap: 12 }, tip: { fontSize: 14, color: colors.textSecondary }, retryBtn: { paddingHorizontal: 18, paddingVertical: 8, borderRadius: 16, backgroundColor: colors.primaryLight, }, retryText: { fontSize: 13, fontWeight: '600', color: colors.primaryText }, scoreCard: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', backgroundColor: colors.white, borderRadius: 8, padding: 18, marginBottom: 12, borderWidth: 1, borderColor: colors.borderLight, }, scoreLeft: { flex: 1 }, scoreLabel: { fontSize: 13, color: colors.textSecondary }, scoreValueRow: { flexDirection: 'row', alignItems: 'center', marginTop: 8 }, scoreValue: { fontSize: 44, fontWeight: '800', color: colors.textPrimary }, scoreUnit: { fontSize: 14, color: colors.textSecondary, marginTop: 16, marginLeft: 4 }, levelBadge: { marginLeft: 10, backgroundColor: colors.primaryLight, borderRadius: 10, paddingHorizontal: 10, paddingVertical: 4, }, levelText: { fontSize: 12, fontWeight: '700', color: colors.primaryDark }, scoreMeta: { fontSize: 13, color: colors.successText, marginTop: 10 }, changeUp: { color: colors.successText }, changeDown: { color: colors.danger }, scoreIcon: { alignItems: 'center', justifyContent: 'center' }, card: { backgroundColor: colors.white, borderRadius: 8, padding: 16, marginBottom: 12, borderWidth: 1, borderColor: colors.borderLight, }, chartCard: { backgroundColor: colors.white, borderRadius: 8, padding: 16, marginBottom: 12, borderWidth: 1, borderColor: colors.borderLight, }, cardTitle: { fontSize: 15, fontWeight: '700', color: colors.textPrimary, marginBottom: 12 }, dimRow: { marginBottom: 14 }, dimHead: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6, }, dimName: { fontSize: 14, color: colors.textPrimary }, dimRight: { flexDirection: 'row', alignItems: 'center' }, dimScore: { fontSize: 14, fontWeight: '700', color: colors.textPrimary, marginRight: 8 }, dimTag: { backgroundColor: colors.pageBg, borderRadius: 8, paddingHorizontal: 8, paddingVertical: 2, marginRight: 8, }, dimTagText: { fontSize: 11, fontWeight: '600' }, dimTrack: { height: 8, borderRadius: 4, backgroundColor: colors.borderLight, overflow: 'hidden', }, dimFill: { height: 8, borderRadius: 4, backgroundColor: colors.primary }, sectionHeader: { marginBottom: 10, marginTop: 4 }, emptyBox: { backgroundColor: colors.surface, borderRadius: 8, paddingVertical: 28, paddingHorizontal: 16, alignItems: 'center', gap: 8, }, emptyText: { fontSize: 13, color: colors.textSecondary, textAlign: 'center' }, sectionTitle: { fontSize: 16, fontWeight: '700', color: colors.textPrimary }, reviewCard: { backgroundColor: colors.white, borderRadius: 8, padding: 14, marginBottom: 10, borderWidth: 1, borderColor: colors.borderLight, }, reviewHead: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, reviewCompany: { fontSize: 15, fontWeight: '700', color: colors.textPrimary }, reviewRating: { flexDirection: 'row', alignItems: 'center' }, reviewRatingText: { fontSize: 13, fontWeight: '700', color: colors.actionText, marginLeft: 4, }, reviewDate: { fontSize: 12, color: colors.textHint, marginTop: 2 }, reviewContent: { fontSize: 13, color: colors.textSecondary, marginTop: 10, lineHeight: 20 }, reviewTags: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 10 }, reviewTag: { backgroundColor: colors.primaryLight, borderRadius: 8, paddingHorizontal: 8, paddingVertical: 2, }, reviewTagText: { fontSize: 11, color: colors.primaryText }, ruleRow: { flexDirection: 'row', alignItems: 'flex-start', marginBottom: 10 }, ruleDot: { width: 5, height: 5, borderRadius: 3, backgroundColor: colors.primary, marginTop: 7, marginRight: 8, }, ruleText: { flex: 1, fontSize: 13, color: colors.textSecondary, lineHeight: 20 }, footer: { position: 'absolute', left: 0, right: 0, bottom: 0, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 12, backgroundColor: colors.white, borderTopWidth: 1, borderTopColor: colors.borderLight, }, footerQuestion: { fontSize: 14, color: colors.textPrimary }, footerBtn: { backgroundColor: colors.primaryBtn, borderRadius: 18, paddingHorizontal: 20, paddingVertical: 9, }, footerBtnText: { fontSize: 14, fontWeight: '700', color: colors.white }, });