import { Ionicons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import Svg, { Line, Polygon, Text as SvgText } from 'react-native-svg'; import { useInterviewScore } from '../src/hooks'; import { colors } from '../src/theme'; import type { HireSuggestion, InterviewScoreData, InterviewScoreDimension, InterviewScoreTag } from '../src/types'; const COLOR_TOKENS: Record = { primary: colors.primary, success: colors.success, action: colors.action, info: colors.info, danger: colors.danger, }; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack }: { onBack: () => void }) { return ( 面试评分 ); } /* ===================== 子组件:面试信息卡 ===================== */ function InterviewInfoCard({ data }: { data: InterviewScoreData }) { return ( 候选人 {data.candidateName} 面试岗位 {data.positionName} 面试时间 {data.interviewTime} ); } /* ===================== 子组件:综合评分卡 ===================== */ function OverallScoreCard({ score }: { score: number }) { return ( 综合评分 {score} ); } /* ===================== 子组件:雷达图 ===================== */ function RadarChart({ dimensions }: { dimensions: InterviewScoreDimension[] }) { const size = 220; const center = size / 2; const radius = 80; const angleStep = (Math.PI * 2) / dimensions.length; const axisPoints = dimensions.map((_, i) => { const angle = i * angleStep - Math.PI / 2; return { x: center + radius * Math.cos(angle), y: center + radius * Math.sin(angle), label: dimensions[i].name, }; }); const dataPoints = dimensions.map((d, i) => { const angle = i * angleStep - Math.PI / 2; const r = (d.score / d.maxScore) * radius; return { x: center + r * Math.cos(angle), y: center + r * Math.sin(angle), }; }); const polygonPoints = dataPoints.map((p) => `${p.x},${p.y}`).join(' '); return ( 八维能力评分 {[0.2, 0.4, 0.6, 0.8, 1].map((scale) => ( { const px = center + (a.x - center) * scale; const py = center + (a.y - center) * scale; return `${px},${py}`; }).join(' ')} fill="none" stroke={colors.borderLight} strokeWidth={1} /> ))} {axisPoints.map((a, i) => ( ))} {axisPoints.map((a, i) => { const labelX = center + (a.x - center) * 1.18; const labelY = center + (a.y - center) * 1.18; return ( {a.label} ); })} {dimensions.map((d) => ( {d.name} {d.score} ))} ); } /* ===================== 子组件:评价标签 ===================== */ function TagGroup({ tags, onToggle }: { tags: InterviewScoreTag[]; onToggle: (id: string) => void }) { return ( 面试评价标签 {tags.map((tag) => { const color = COLOR_TOKENS[tag.color] ?? colors.primary; return ( onToggle(tag.id)} > {tag.selected && } {tag.label} ); })} ); } /* ===================== 子组件:文字评价 ===================== */ function CommentInput({ value, onChangeText }: { value: string; onChangeText: (text: string) => void }) { return ( 文字评价 {value.length}/500 ); } /* ===================== 子组件:录用建议 ===================== */ function HireSuggestionGroup({ suggestions, onSelect, }: { suggestions: HireSuggestion[]; onSelect: (type: HireSuggestion['type']) => void; }) { const typeColor: Record = { strong: colors.success, consider: colors.compareOrange, reject: colors.danger, }; return ( 录用建议 {suggestions.map((s) => ( onSelect(s.type)} > {s.title} {s.desc} {s.selected && } ))} ); } /* ===================== 子组件:薪资建议 ===================== */ function SalarySuggestionCard({ salary }: { salary: InterviewScoreData['salarySuggestion'] }) { return ( 薪资建议(选填) {salary.range} {salary.level} {salary.note} ); } /* ===================== 页面主组件 ===================== */ export default function InterviewScoreScreen() { const router = useRouter(); const { score, loading, error, toggleTag, updateComment, selectHireSuggestion, submit, submitting, } = useInterviewScore('interview-1'); if (loading) { return ( ); } if (error || !score) { return ( 加载失败,请稍后重试 ); } return (
router.back()} /> 保存草稿 {submitting ? '提交中...' : '提交评分'} ); } /* ===================== 样式 ===================== */ const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: colors.pageBg, }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: colors.pageBg, }, errorText: { color: colors.textSecondary, fontSize: 14, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 12, backgroundColor: colors.white, borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, headerBtn: { width: 36, height: 36, justifyContent: 'center', alignItems: 'center', }, headerTitle: { fontSize: 17, fontWeight: '600', color: colors.textPrimary, }, scroll: { flex: 1, }, scrollContent: { padding: 16, paddingBottom: 100, }, infoCard: { backgroundColor: colors.white, borderRadius: 16, padding: 16, marginBottom: 16, }, infoRow: { flexDirection: 'row', marginBottom: 12, }, infoItem: { flex: 1, }, infoLabel: { fontSize: 12, color: colors.textHint, marginBottom: 4, }, infoValue: { fontSize: 15, fontWeight: '600', color: colors.textPrimary, }, scoreCard: { backgroundColor: colors.primary, borderRadius: 16, padding: 20, marginBottom: 16, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, scoreLabel: { fontSize: 14, color: colors.white, opacity: 0.9, }, scoreValue: { fontSize: 42, fontWeight: '700', color: colors.white, marginTop: 4, }, scoreIcon: { width: 64, height: 64, borderRadius: 32, backgroundColor: 'rgba(255,255,255,0.2)', justifyContent: 'center', alignItems: 'center', }, card: { backgroundColor: colors.white, borderRadius: 16, padding: 16, marginBottom: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.04, shadowRadius: 6, elevation: 2, }, cardTitle: { fontSize: 16, fontWeight: '700', color: colors.textPrimary, marginBottom: 12, }, radarWrap: { alignItems: 'center', }, dimensionList: { marginTop: 16, }, dimensionItem: { flexDirection: 'row', alignItems: 'center', marginBottom: 10, }, dimensionName: { width: 70, fontSize: 13, color: colors.textSecondary, }, dimensionTrack: { flex: 1, height: 8, borderRadius: 4, backgroundColor: colors.borderLight, marginHorizontal: 10, }, dimensionFill: { height: 8, borderRadius: 4, backgroundColor: colors.primary, }, dimensionScore: { width: 28, fontSize: 13, fontWeight: '600', color: colors.textPrimary, textAlign: 'right', }, tagRow: { flexDirection: 'row', flexWrap: 'wrap', marginHorizontal: -4, }, tag: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 12, paddingVertical: 7, borderRadius: 18, borderWidth: 1, borderColor: colors.border, margin: 4, backgroundColor: colors.white, }, tagText: { fontSize: 13, color: colors.textPrimary, marginLeft: 4, }, tagTextActive: { color: colors.white, fontWeight: '500', }, textarea: { height: 120, borderWidth: 1, borderColor: colors.border, borderRadius: 12, padding: 12, fontSize: 14, color: colors.textPrimary, backgroundColor: colors.pageBg, }, charCount: { textAlign: 'right', fontSize: 12, color: colors.textHint, marginTop: 8, }, suggestionRow: { flexDirection: 'row', alignItems: 'center', padding: 12, borderRadius: 12, borderWidth: 1, borderColor: colors.borderLight, marginBottom: 10, }, suggestionDot: { width: 10, height: 10, borderRadius: 5, marginRight: 10, }, suggestionBody: { flex: 1, }, suggestionTitle: { fontSize: 14, fontWeight: '600', color: colors.textPrimary, marginBottom: 2, }, suggestionDesc: { fontSize: 12, color: colors.textSecondary, }, salaryBox: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', backgroundColor: colors.infoLight, borderRadius: 12, padding: 16, }, salaryRange: { fontSize: 24, fontWeight: '700', color: colors.info, }, salaryLevel: { fontSize: 14, color: colors.infoText, marginTop: 4, }, salaryNote: { fontSize: 12, color: colors.textSecondary, marginTop: 10, }, footer: { flexDirection: 'row', padding: 16, paddingBottom: 24, backgroundColor: colors.white, borderTopWidth: 1, borderTopColor: colors.borderLight, }, draftBtn: { flex: 1, height: 46, borderRadius: 24, borderWidth: 1, borderColor: colors.border, justifyContent: 'center', alignItems: 'center', marginRight: 12, }, draftText: { fontSize: 15, fontWeight: '600', color: colors.textPrimary, }, submitBtn: { flex: 2, height: 46, borderRadius: 24, backgroundColor: colors.primaryBtn, justifyContent: 'center', alignItems: 'center', }, submitBtnDisabled: { opacity: 0.6, }, submitText: { fontSize: 15, fontWeight: '600', color: colors.white, }, });