import { Ionicons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Switch, Text, TextInput, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useJobPublish } from '../../src/hooks'; import { colors } from '../../src/theme'; import type { JobPublishFormData } from '../../src/types'; const WORK_TYPE_OPTIONS: { label: string; value: JobPublishFormData['workType'] }[] = [ { label: '全职', value: 'full-time' }, { label: '兼职', value: 'part-time' }, { label: '实习', value: 'intern' }, { label: '远程', value: 'remote' }, ]; const EDUCATION_OPTIONS: JobPublishFormData['education'][] = ['不限', '大专', '本科', '硕士', '博士']; const EXPERIENCE_OPTIONS: JobPublishFormData['experience'][] = [ '应届生', '1年以内', '1-3年', '3-5年', '5-10年', '10年以上', ]; const BENEFIT_OPTIONS = ['五险一金', '年终奖金', '股票期权', '带薪年假', '弹性工作', '餐补', '交通补助', '定期体检']; const SKILL_OPTIONS = ['React', 'Vue', 'TypeScript', 'Node.js', 'Webpack', 'Git', 'Python', 'Java', 'Go', 'SQL']; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack, onSave }: { onBack: () => void; onSave: () => void }) { return ( 发布岗位 保存 ); } /* ===================== 子组件:表单项 ===================== */ function Field({ label, required, children }: { label: string; required?: boolean; children: React.ReactNode }) { return ( {label} {required && *} {children} ); } function Input({ value, onChangeText, placeholder, multiline }: { value: string; onChangeText: (text: string) => void; placeholder?: string; multiline?: boolean; }) { return ( ); } function NumberInput({ value, onChangeText, placeholder }: { value: number; onChangeText: (text: string) => void; placeholder?: string; }) { return ( ); } /* ===================== 子组件:单选标签组 ===================== */ function RadioGroup({ options, value, onChange, }: { options: T[] | { label: string; value: T }[]; value: T; onChange: (value: T) => void; }) { return ( {options.map((opt) => { const optionValue = typeof opt === 'string' ? opt : opt.value; const optionLabel = typeof opt === 'string' ? opt : opt.label; const selected = optionValue === value; return ( onChange(optionValue)} > {optionLabel} ); })} ); } /* ===================== 子组件:多选标签组 ===================== */ function CheckboxGroup({ options, values, onToggle }: { options: string[]; values: string[]; onToggle: (v: string) => void }) { return ( {options.map((opt) => { const selected = values.includes(opt); return ( onToggle(opt)} > {selected && } {opt} ); })} ); } /* ===================== 子组件:开关行 ===================== */ function SwitchRow({ label, value, onValueChange }: { label: string; value: boolean; onValueChange: (v: boolean) => void }) { return ( {label} ); } /* ===================== 子组件:质量预估卡 ===================== */ function QualityEstimateCard({ score, level, suggestions, estimating }: { score: number; level: string; suggestions: string[]; estimating: boolean; }) { return ( 岗位质量预估 AI 实时评估岗位信息完整度 {estimating ? ( ) : ( <> {score} {level} )} {suggestions.length > 0 && ( {suggestions.map((s, i) => ( {s} ))} )} ); } /* ===================== 页面主组件 ===================== */ export default function JobPublishScreen() { const router = useRouter(); const { form, loading, error, updateStringField, updateNumberField, updateField, toggleBenefit, toggleSkill, estimate, estimating, runEstimate, publishing, publish, } = useJobPublish(); if (loading) { return ( ); } if (error || !form) { return ( 加载失败,请稍后重试 ); } return (
router.back()} onSave={() => {}} /> {/* 基本信息 */} 基本信息 updateStringField('positionName', v)} placeholder="请输入岗位名称" /> updateStringField('department', v)} placeholder="请输入所属部门" /> updateNumberField('headcount', v)} placeholder="请输入招聘人数" /> updateStringField('city', v)} placeholder="请输入工作城市" /> updateStringField('address', v)} placeholder="请输入详细地址" /> options={WORK_TYPE_OPTIONS} value={form.workType} onChange={(v) => updateField('workType', v)} /> {/* 岗位要求 */} 岗位要求 options={EDUCATION_OPTIONS} value={form.education} onChange={(v) => updateField('education', v)} /> options={EXPERIENCE_OPTIONS} value={form.experience} onChange={(v) => updateField('experience', v)} /> {/* 薪资福利 */} 薪资福利 updateNumberField('salaryMin', v)} placeholder="最低" /> - updateNumberField('salaryMax', v)} placeholder="最高" /> updateNumberField('salaryMonths', v)} placeholder="如 14" /> {/* 岗位描述 */} 岗位描述 updateStringField('responsibilities', v)} placeholder="请输入岗位职责" multiline /> updateStringField('requirements', v)} placeholder="请输入岗位要求" multiline /> {/* 岗位设置 */} 岗位设置 options={[ { label: '全部可见', value: 'all' as const }, { label: '内部推荐', value: 'internal' as const }, { label: '校园招聘', value: 'campus' as const }, ]} value={form.visibility} onChange={(v) => updateField('visibility', v)} /> updateField('urgent', v)} /> updateField('acceptFresh', v)} /> updateStringField('deadline', v)} placeholder="YYYY-MM-DD" /> {/* 质量预估 */} 质量预估 {publishing ? '发布中...' : '发布岗位'} ); } /* ===================== 样式 ===================== */ 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, }, headerSaveBtn: { paddingHorizontal: 12, paddingVertical: 6, backgroundColor: colors.primaryLight, borderRadius: 16, }, headerSaveText: { fontSize: 13, fontWeight: '600', color: colors.primary, }, scroll: { flex: 1, }, scrollContent: { padding: 16, paddingBottom: 100, }, 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: 16, }, field: { marginBottom: 16, }, fieldLabel: { fontSize: 14, fontWeight: '500', color: colors.textPrimary, marginBottom: 8, }, required: { color: colors.danger, }, input: { height: 44, borderWidth: 1, borderColor: colors.border, borderRadius: 10, paddingHorizontal: 12, fontSize: 14, color: colors.textPrimary, backgroundColor: colors.pageBg, }, textarea: { height: 100, paddingTop: 12, paddingBottom: 12, }, tagRow: { flexDirection: 'row', flexWrap: 'wrap', marginHorizontal: -4, }, radioTag: { paddingHorizontal: 14, paddingVertical: 8, borderRadius: 18, borderWidth: 1, borderColor: colors.border, margin: 4, backgroundColor: colors.white, }, radioTagActive: { backgroundColor: colors.primary, borderColor: colors.primary, }, radioText: { fontSize: 13, color: colors.textPrimary, }, radioTextActive: { color: colors.white, fontWeight: '500', }, checkboxTag: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 12, paddingVertical: 7, borderRadius: 18, borderWidth: 1, borderColor: colors.border, margin: 4, backgroundColor: colors.white, }, checkboxTagActive: { backgroundColor: colors.primary, borderColor: colors.primary, }, checkboxText: { fontSize: 13, color: colors.textPrimary, marginLeft: 4, }, checkboxTextActive: { color: colors.white, fontWeight: '500', }, switchRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, switchLabel: { fontSize: 14, color: colors.textPrimary, }, salaryRow: { flexDirection: 'row', alignItems: 'flex-end', }, salarySeparator: { marginHorizontal: 8, marginBottom: 16, fontSize: 16, color: colors.textSecondary, }, qualityCard: { backgroundColor: colors.primaryLight, borderWidth: 1, borderColor: colors.primaryBorder, }, qualityHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, qualitySub: { fontSize: 12, color: colors.textSecondary, marginTop: 4, }, qualityScoreBox: { alignItems: 'center', }, qualityScore: { fontSize: 32, fontWeight: '700', color: colors.primary, }, qualityLevel: { fontSize: 13, color: colors.primaryText, fontWeight: '500', }, suggestionList: { marginTop: 16, paddingTop: 16, borderTopWidth: 1, borderTopColor: colors.primaryBorder, }, suggestionItem: { flexDirection: 'row', alignItems: 'flex-start', marginBottom: 8, }, suggestionText: { flex: 1, fontSize: 13, color: colors.textSecondary, marginLeft: 8, lineHeight: 18, }, 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.primary, justifyContent: 'center', alignItems: 'center', marginRight: 12, }, draftText: { fontSize: 15, fontWeight: '600', color: colors.primary, }, publishBtn: { flex: 2, height: 46, borderRadius: 24, backgroundColor: colors.primaryBtn, justifyContent: 'center', alignItems: 'center', }, publishBtnDisabled: { opacity: 0.6, }, publishText: { fontSize: 15, fontWeight: '600', color: colors.white, }, });