import { Ionicons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { useMemo, useState } from 'react'; import { ActivityIndicator, Alert, Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useJobManagement } from '../../src/hooks'; import { colors } from '../../src/theme'; import type { JobManagement, JobManagementJob, JobManagementTab } from '../../src/types'; /* ===================== 子组件:顶部导航 ===================== */ function Header({ companyName, onBack }: { companyName: string; onBack: () => void }) { const router = useRouter(); return ( {companyName} 岗位管理 router.push('/job-publish')} hitSlop={6} > 发布 ); } /* ===================== 子组件:Tab 栏 ===================== */ function TabBar({ tabs, active, onChange, }: { tabs: JobManagementTab[]; active: string; onChange: (key: string) => void; }) { return ( {tabs.map((tab) => { const isActive = tab.key === active; return ( onChange(tab.key)}> {tab.label} ({tab.count}) ); })} ); } /* ===================== 子组件:数据概览 ===================== */ function StatsOverview({ stats }: { stats: JobManagement['stats'] }) { return ( {stats.map((stat) => { const token = stat.color === 'success' ? colors.success : stat.color === 'action' ? colors.action : stat.color === 'info' ? colors.info : colors.primary; const isUp = stat.change.startsWith('+'); const isDown = stat.change.startsWith('-'); const changeColor = isUp ? colors.success : isDown ? colors.action : colors.textSecondary; return ( {stat.value} {stat.label} {stat.change} ); })} ); } /* ===================== 子组件:搜索与筛选 ===================== */ function SearchFilterBar() { return ( Alert.alert('筛选', '岗位筛选功能开发中,敬请期待')}> 筛选 Alert.alert('排序', '岗位排序功能开发中,敬请期待')}> 最新发布 ); } /* ===================== 子组件:岗位卡片 ===================== */ function JobCard({ job }: { job: JobManagementJob }) { return ( {job.title} {job.status === 'active' ? : null} {job.salary} · {job.salaryMonths}薪 {job.city} · {job.experience} · {job.education} 综合评分 {job.overallScore} {job.rankText} {job.tags.map((tag) => ( {tag} ))} {job.applicants} 投递 {job.views >= 1000 ? `${(job.views / 1000).toFixed(1)}k` : job.views} 查看 {job.matches} 匹配 Alert.alert('提示', '功能开发中,敬请期待')}> 编辑 Alert.alert('提示', '功能开发中,敬请期待')}> 刷新 Alert.alert('提示', '功能开发中,敬请期待')}> 下架 ); } /* ===================== 子组件:空状态 ===================== */ function EmptyState({ benefits, onPublish }: { benefits: string[]; onPublish: () => void }) { return ( 还没有发布岗位 发布你的第一个岗位,开始招聘优秀人才 发布岗位 发布岗位后你可以 {benefits.map((benefit, index) => ( {benefit} ))} ); } /* ===================== 页面 ===================== */ export default function JobManagementScreen() { const router = useRouter(); const { management, loading, error, refresh } = useJobManagement(); const [activeTab, setActiveTab] = useState('all'); const filteredJobs = useMemo(() => { if (!management?.hasData) return []; if (activeTab === 'all') return management.jobs; return management.jobs.filter((job) => { if (activeTab === 'active') return job.status === 'active'; if (activeTab === 'offline') return job.status === 'offline'; if (activeTab === 'draft') return job.status === 'draft'; return true; }); }, [management, activeTab]); if (loading) { return (
router.back()} /> ); } if (error || !management) { return (
router.back()} /> 加载失败 重试 ); } return (
router.back()} /> {management.hasData ? ( {filteredJobs.map((job) => ( ))} ) : ( router.push('/job-publish')} /> )} ); } 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' }, headerCenter: { alignItems: 'center' }, headerCompany: { fontSize: 12, color: colors.textSecondary }, headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary, marginTop: 2 }, publishBtn: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.primaryBtn, paddingHorizontal: 12, paddingVertical: 6, borderRadius: 16, }, publishBtnText: { color: colors.white, fontSize: 13, fontWeight: '600', marginLeft: 2, }, tabBar: { flexDirection: 'row', backgroundColor: colors.white, borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, tabItem: { flex: 1, alignItems: 'center', paddingTop: 8, }, tabLabel: { fontSize: 14, color: colors.textSecondary, paddingBottom: 8, }, tabLabelActive: { color: colors.textPrimary, fontWeight: '600', }, tabCount: { fontSize: 12, color: colors.textHint, marginLeft: 2, }, tabCountActive: { color: colors.primary, }, tabIndicator: { height: 2, width: 24, borderRadius: 1, backgroundColor: 'transparent', marginBottom: -1, }, tabIndicatorActive: { backgroundColor: colors.primary, }, body: { padding: 16, paddingBottom: 24, }, center: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 12, }, tip: { fontSize: 14, color: colors.textSecondary }, retryBtn: { marginTop: 8, paddingHorizontal: 24, paddingVertical: 8, borderRadius: 18, backgroundColor: colors.primaryBtn, }, retryText: { color: colors.white, fontSize: 13, fontWeight: '600' }, statsGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 12, marginBottom: 12, }, statCard: { width: '48%', flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 12, padding: 12, }, statIconWrap: { width: 40, height: 40, borderRadius: 10, alignItems: 'center', justifyContent: 'center', marginRight: 10, }, statBody: { flex: 1 }, statValue: { fontSize: 18, fontWeight: '800', color: colors.textPrimary, }, statLabel: { fontSize: 12, color: colors.textSecondary, marginTop: 2, }, statChangeWrap: { alignSelf: 'flex-start', }, statChange: { fontSize: 12, fontWeight: '600', }, searchBar: { flexDirection: 'row', alignItems: 'center', marginBottom: 12, gap: 8, }, searchInputWrap: { flex: 1, flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 8, paddingHorizontal: 10, paddingVertical: 8, }, searchInput: { flex: 1, marginLeft: 8, fontSize: 14, color: colors.textPrimary, }, filterBtn: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 8, paddingHorizontal: 10, paddingVertical: 8, }, filterBtnText: { fontSize: 13, color: colors.textPrimary, marginLeft: 4, }, sortBtn: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 8, paddingHorizontal: 10, paddingVertical: 8, }, sortBtnText: { fontSize: 13, color: colors.textPrimary, marginRight: 2, }, jobCard: { backgroundColor: colors.white, borderRadius: 12, padding: 16, marginBottom: 12, }, jobHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, jobTitleWrap: { flexDirection: 'row', alignItems: 'center', }, jobTitle: { fontSize: 16, fontWeight: '700', color: colors.textPrimary, }, activeDot: { width: 8, height: 8, borderRadius: 4, backgroundColor: colors.success, marginLeft: 8, }, jobSalary: { fontSize: 15, fontWeight: '700', color: colors.action, marginTop: 6, }, jobMeta: { fontSize: 13, color: colors.textSecondary, marginTop: 4, }, jobScoreRow: { flexDirection: 'row', alignItems: 'center', marginTop: 10, }, jobScoreLabel: { fontSize: 13, color: colors.textSecondary, }, jobScoreValue: { fontSize: 14, fontWeight: '800', color: colors.primary, marginLeft: 6, }, rankTag: { backgroundColor: colors.primaryLight, borderRadius: 8, paddingHorizontal: 8, paddingVertical: 2, marginLeft: 8, }, rankTagText: { fontSize: 11, color: colors.primaryText, fontWeight: '600', }, jobTags: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 10, }, jobTag: { backgroundColor: colors.pageBg, borderRadius: 6, paddingHorizontal: 8, paddingVertical: 3, }, jobTagText: { fontSize: 11, color: colors.textSecondary, }, jobStatsRow: { flexDirection: 'row', marginTop: 14, paddingTop: 14, borderTopWidth: 1, borderTopColor: colors.borderLight, }, jobStat: { flex: 1, alignItems: 'center', }, jobStatValue: { fontSize: 16, fontWeight: '700', color: colors.textPrimary, }, jobStatLabel: { fontSize: 11, color: colors.textSecondary, marginTop: 2, }, jobActions: { flexDirection: 'row', justifyContent: 'flex-end', marginTop: 12, gap: 12, }, jobActionBtn: { flexDirection: 'row', alignItems: 'center', }, jobActionText: { fontSize: 13, color: colors.primary, marginLeft: 4, }, emptyCard: { backgroundColor: colors.white, borderRadius: 16, padding: 28, alignItems: 'center', }, emptyIconWrap: { width: 88, height: 88, borderRadius: 44, backgroundColor: colors.primaryLight, alignItems: 'center', justifyContent: 'center', marginBottom: 16, }, emptyTitle: { fontSize: 18, fontWeight: '700', color: colors.textPrimary, marginBottom: 8, }, emptySubtitle: { fontSize: 13, color: colors.textSecondary, textAlign: 'center', marginBottom: 20, }, emptyBtn: { backgroundColor: colors.primaryBtn, paddingHorizontal: 32, paddingVertical: 12, borderRadius: 24, marginBottom: 24, }, emptyBtnText: { color: colors.white, fontSize: 15, fontWeight: '600', }, benefitCard: { width: '100%', backgroundColor: colors.primaryLight, borderRadius: 12, padding: 16, alignItems: 'flex-start', }, benefitTitle: { fontSize: 14, fontWeight: '700', color: colors.primaryText, marginBottom: 12, }, benefitRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 10, }, benefitText: { fontSize: 13, color: colors.textPrimary, marginLeft: 10, }, });