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 { useAppealForm } from '../src/hooks'; import { colors } from '../src/theme'; import type { AppealReason, AppealType } from '../src/types'; /** 申诉类型选项 */ const APPEAL_TYPES: { id: AppealType; label: string; desc: string }[] = [ { id: 'overall', label: '综合评分申诉', desc: '对整体评分结果有异议' }, { id: 'single', label: '单条评价申诉', desc: '对某一条 HR 评价有异议' }, ]; /** 申诉原因选项 */ const APPEAL_REASONS: { id: AppealReason; label: string }[] = [ { id: 'untrue', label: '评分与事实不符' }, { id: 'malicious', label: '评价存在主观偏见' }, { id: 'privacy', label: '涉及个人隐私信息' }, { id: 'other', label: '其他原因' }, ]; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack }: { onBack: () => void }) { return ( 评分申诉 ); } /* ===================== 子组件:申诉须知 ===================== */ function NoticeCard() { return ( 申诉须知 1. 请在收到评价后 30 天内发起申诉,逾期将无法受理。 2. 请如实描述申诉原因,并提供相关凭证,虚假申诉将影响账号信用。 3. 平台将在 3 个工作日内完成审核,结果将通过站内消息通知你。 ); } /* ===================== 子组件:申诉类型 ===================== */ function TypeSection({ active, onSelect, }: { active: AppealType; onSelect: (t: AppealType) => void; }) { return ( 申诉类型 * {APPEAL_TYPES.map((item) => { const selected = item.id === active; return ( onSelect(item.id)} > {item.label} {item.desc} ); })} ); } /* ===================== 子组件:申诉维度(多选) ===================== */ function DimensionSection({ dimensions, active, onToggle, }: { dimensions: { id: string; name: string }[]; active: string[]; onToggle: (id: string) => void; }) { return ( 申请维度 (可多选) {dimensions.map((dim) => { const selected = active.includes(dim.id); return ( onToggle(dim.id)} > {selected && ( )} {dim.name} ); })} ); } /* ===================== 子组件:申诉原因 ===================== */ function ReasonSection({ active, onSelect, }: { active: AppealReason | null; onSelect: (r: AppealReason) => void; }) { return ( 申诉原因 * {APPEAL_REASONS.map((item) => { const selected = item.id === active; return ( onSelect(item.id)} > {item.label} ); })} ); } /* ===================== 子组件:详细说明 ===================== */ function DescriptionSection({ value, onChange, }: { value: string; onChange: (v: string) => void; }) { return ( 详细说明 * {value.length}/500 ); } /* ===================== 子组件:上传凭证 ===================== */ function EvidenceSection() { return ( 上传凭证 点击上传图片或文件 支持 JPG / PNG / PDF,单个文件不超过 10MB ); } /* ===================== 子组件:联系方式 ===================== */ function ContactSection({ phone, email, onPhoneChange, onEmailChange, }: { phone: string; email: string; onPhoneChange: (v: string) => void; onEmailChange: (v: string) => void; }) { return ( 联系方式 * 手机号 邮箱 ); } /* ===================== 子组件:四态 ===================== */ function Loading() { return ( 正在加载申诉表单… ); } function ErrorState({ onRetry }: { onRetry: () => void }) { return ( 表单加载失败 点击重试 ); } /* ===================== 页面 ===================== */ export default function AppealScreen() { const router = useRouter(); const { form, loading, error, refresh, type, setType, dimensions, toggleDimension, reason, setReason, reasonText, setReasonText, contactPhone, setContactPhone, contactEmail, setContactEmail, submit, submitting, } = useAppealForm(); const handleSubmit = async () => { const result = await submit(); if (result) { // TODO: 提交成功后跳转结果页或关闭弹层 } }; if (loading) { return (
router.back()} /> ); } if (error || !form) { return (
router.back()} /> ); } return (
router.back()} /> {submitting ? ( ) : ( 提交申诉 )} ); } 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 }, 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 }, noticeCard: { backgroundColor: colors.infoLight, borderRadius: 12, padding: 14, marginBottom: 16, borderWidth: 1, borderColor: colors.infoBorder, }, noticeHead: { flexDirection: 'row', alignItems: 'center', marginBottom: 8 }, noticeTitle: { fontSize: 14, fontWeight: '700', color: colors.infoText, marginLeft: 6 }, noticeText: { fontSize: 12, color: colors.infoText, lineHeight: 20, marginBottom: 2 }, section: { marginBottom: 16 }, sectionTitle: { fontSize: 15, fontWeight: '700', color: colors.textPrimary, marginBottom: 10 }, required: { color: colors.danger }, optional: { fontSize: 12, fontWeight: '400', color: colors.textHint }, typeCard: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 12, padding: 14, marginBottom: 10, borderWidth: 1, borderColor: colors.borderLight, }, typeCardActive: { borderColor: colors.primary, backgroundColor: colors.primaryLight }, radioOuter: { width: 18, height: 18, borderRadius: 9, borderWidth: 1, borderColor: colors.borderStrong, }, typeBody: { flex: 1, marginLeft: 12 }, typeLabel: { fontSize: 14, fontWeight: '700', color: colors.textPrimary }, typeLabelActive: { color: colors.primaryText }, typeDesc: { fontSize: 12, color: colors.textSecondary, marginTop: 2 }, dimWrap: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 }, dimTag: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 18, backgroundColor: colors.white, borderWidth: 1, borderColor: colors.border, }, dimTagActive: { backgroundColor: colors.primary, borderColor: colors.primary }, dimTagText: { fontSize: 13, color: colors.textSecondary, marginLeft: 4 }, dimTagTextActive: { color: colors.white, fontWeight: '600' }, reasonRow: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 10, paddingVertical: 12, paddingHorizontal: 14, marginBottom: 8, borderWidth: 1, borderColor: colors.borderLight, }, reasonLabel: { fontSize: 14, color: colors.textPrimary, marginLeft: 10 }, reasonLabelActive: { color: colors.primaryText, fontWeight: '600' }, textArea: { height: 120, backgroundColor: colors.white, borderRadius: 10, borderWidth: 1, borderColor: colors.borderLight, padding: 12, fontSize: 14, color: colors.textPrimary, }, counter: { marginTop: 6, fontSize: 12, color: colors.textHint, textAlign: 'right' }, uploadBox: { backgroundColor: colors.white, borderRadius: 10, borderWidth: 1, borderColor: colors.borderLight, borderStyle: 'dashed', paddingVertical: 24, alignItems: 'center', }, uploadText: { fontSize: 14, color: colors.textPrimary, marginTop: 8 }, uploadHint: { fontSize: 11, color: colors.textHint, marginTop: 4 }, card: { backgroundColor: colors.white, borderRadius: 10, paddingHorizontal: 14, borderWidth: 1, borderColor: colors.borderLight, }, inputRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, inputLabel: { width: 60, fontSize: 14, color: colors.textPrimary }, input: { flex: 1, fontSize: 14, color: colors.textPrimary }, footer: { position: 'absolute', left: 0, right: 0, bottom: 0, paddingHorizontal: 16, paddingVertical: 12, backgroundColor: colors.white, borderTopWidth: 1, borderTopColor: colors.borderLight, }, submitBtn: { backgroundColor: colors.primaryBtn, borderRadius: 24, paddingVertical: 14, alignItems: 'center', }, submitBtnDisabled: { backgroundColor: colors.textDisabled }, submitBtnText: { fontSize: 16, fontWeight: '700', color: colors.white }, });