import { Ionicons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import { Pressable, ScrollView, StyleSheet, Text, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useEnterpriseOverview } from '../../src/hooks'; import { LoadingState, ErrorState } from '../../src/components/StateView'; import { useRole } from '../../src/context/RoleContext'; import { colors, headerGradient } from '../../src/theme'; import type { EnterpriseOverview, EnterpriseQuickAction, EnterpriseStat, JobEvaluationSummary, MenuItem } from '../../src/types'; const COLOR_TOKENS: Record = { primary: colors.primary, success: colors.success, action: colors.action, info: colors.info, danger: colors.danger, }; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onSettings }: { onSettings: () => void }) { return ( 我的 ); } /* ===================== 子组件:企业信息卡 ===================== */ function CompanyCard({ overview }: { overview: EnterpriseOverview }) { return ( {overview.logo} {overview.name} {overview.industry} · {overview.scale} · {overview.stage} {overview.overallScore} 综合评分 {overview.reviewCount} 评价人数 {overview.stats[0]?.value ?? 0} 在招岗位 ); } /* ===================== 子组件:统计四宫格 ===================== */ function StatGrid({ stats }: { stats: EnterpriseStat[] }) { return ( {stats.map((stat) => { const color = COLOR_TOKENS[stat.color] ?? colors.primary; const up = stat.change >= 0; return ( {stat.value.toLocaleString()} {stat.label} {up ? '↑' : '↓'} {Math.abs(stat.change)} ); })} ); } /* ===================== 子组件:快捷入口 ===================== */ function QuickActions({ actions }: { actions: EnterpriseQuickAction[] }) { const router = useRouter(); return ( 快捷入口 {actions.map((action) => { const color = COLOR_TOKENS[action.color] ?? colors.primary; return ( action.route && router.push(action.route as string)} > {action.label} ); })} ); } /* ===================== 子组件:岗位评价列表 ===================== */ function JobEvaluationList({ jobs }: { jobs: JobEvaluationSummary[] }) { const router = useRouter(); return ( 岗位评价管理 router.push('/job-evaluations')}> 查看全部 {jobs.map((job) => { const up = job.change >= 0; return ( {job.positionName} {job.department} {job.score.toFixed(1)} {up ? '↑' : '↓'} {Math.abs(job.change).toFixed(1)} ); })} ); } /* ===================== 子组件:管理后台 ===================== */ /** * 管理后台入口:运营规则配置 / 数据审计等低频管理操作。 * 单独成区纵向排列,既不挤占快捷入口的横排位,也避免后台术语出现在求职者端。 */ function ManageList({ actions }: { actions: EnterpriseQuickAction[] }) { const router = useRouter(); if (actions.length === 0) return null; return ( 管理后台 {actions.map((action) => { const color = COLOR_TOKENS[action.color] ?? colors.primary; return ( action.route && router.push(action.route as string)} > {action.label} ); })} ); } /* ===================== 子组件:企业功能菜单 ===================== */ /** * 企业端功能菜单(更多服务):发布岗位 / 岗位管理 / 人才推荐等业务功能。 * 与求职者端菜单严格隔离,由 AppRole='enterprise' 下发,避免角色混淆。 */ function MenuList({ items }: { items: MenuItem[] }) { const router = useRouter(); if (items.length === 0) return null; return ( 更多服务 {items.map((item) => ( item.route && router.push(item.route as string)} > {item.title} ))} ); } /* ===================== 子组件:切换到求职端卡片 ===================== */ /** * 企业端「我的」页回切求职端,与求职端「切换到企业端」卡片对称, * 避免经「设置→身份切换」进入企业端后返回栈为空、回不去求职端。 */ function CandidateEntryCard() { const router = useRouter(); const { setRole } = useRole(); return ( { setRole('candidate'); router.replace('/(tabs)/jobs'); }} hitSlop={4} > 切换到求职端 返回求职者端,浏览岗位与你的竞争力数据 去求职端 → ); } /* ===================== 页面主组件 ===================== */ export default function EnterpriseOverviewScreen() { const router = useRouter(); const { overview, loading, error, refresh } = useEnterpriseOverview(); if (loading) { return ( ); } if (error || !overview) { return ( ); } return (
router.push('/settings')} /> ); } /* ===================== 样式 ===================== */ const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: colors.pageBg, }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: colors.pageBg, }, gradientHeader: { paddingBottom: 16, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 12, }, headerBtn: { width: 36, height: 36, justifyContent: 'center', alignItems: 'center', }, headerTitle: { fontSize: 17, fontWeight: '600', color: colors.textInverse, }, companyCard: { marginHorizontal: 16, marginTop: 8, padding: 16, backgroundColor: colors.white, borderRadius: 16, shadowColor: colors.shadow, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.06, shadowRadius: 8, elevation: 3, }, companyHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 16, }, logoBox: { width: 52, height: 52, borderRadius: 12, backgroundColor: colors.primaryLight, justifyContent: 'center', alignItems: 'center', marginRight: 12, }, logoText: { fontSize: 22, fontWeight: '700', color: colors.primary, }, companyMeta: { flex: 1, }, companyName: { fontSize: 18, fontWeight: '700', color: colors.textPrimary, marginBottom: 4, }, companyTags: { fontSize: 13, color: colors.textSecondary, }, scoreRow: { flexDirection: 'row', alignItems: 'center', paddingTop: 16, borderTopWidth: 1, borderTopColor: colors.borderLight, }, scoreBox: { flex: 1, alignItems: 'center', }, scoreValue: { fontSize: 20, fontWeight: '700', color: colors.primary, marginBottom: 2, }, scoreLabel: { fontSize: 12, color: colors.textSecondary, }, divider: { width: 1, height: 32, backgroundColor: colors.borderLight, }, scroll: { flex: 1, }, scrollContent: { padding: 16, paddingBottom: 32, }, card: { backgroundColor: colors.white, borderRadius: 16, padding: 16, marginBottom: 16, shadowColor: colors.shadow, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.04, shadowRadius: 6, elevation: 2, }, cardHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, }, cardTitle: { fontSize: 16, fontWeight: '700', color: colors.textPrimary, }, cardMore: { fontSize: 13, color: colors.primary, }, statGrid: { flexDirection: 'row', flexWrap: 'wrap', marginHorizontal: -6, }, statItem: { width: '25%', paddingHorizontal: 6, alignItems: 'center', }, statIcon: { width: 44, height: 44, borderRadius: 12, justifyContent: 'center', alignItems: 'center', marginBottom: 8, }, statValue: { fontSize: 17, fontWeight: '700', color: colors.textPrimary, marginBottom: 2, }, statLabel: { fontSize: 12, color: colors.textSecondary, marginBottom: 2, }, statChange: { fontSize: 11, fontWeight: '500', }, quickGrid: { flexDirection: 'row', marginHorizontal: -8, }, quickItem: { flex: 1, alignItems: 'center', paddingVertical: 8, }, quickIcon: { width: 48, height: 48, borderRadius: 14, justifyContent: 'center', alignItems: 'center', marginBottom: 8, }, quickLabel: { fontSize: 13, color: colors.textPrimary, }, jobRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, jobInfo: { flex: 1, }, jobName: { fontSize: 15, fontWeight: '600', color: colors.textPrimary, marginBottom: 2, }, jobDepartment: { fontSize: 12, color: colors.textSecondary, }, jobScoreBox: { alignItems: 'flex-end', }, jobScore: { fontSize: 18, fontWeight: '700', color: colors.primary, }, jobChange: { fontSize: 11, fontWeight: '500', marginTop: 2, }, standaloneTitle: { fontSize: 16, fontWeight: '700', color: colors.textPrimary, marginBottom: 8, }, manageRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, manageIcon: { width: 38, height: 38, borderRadius: 10, justifyContent: 'center', alignItems: 'center', marginRight: 12, }, manageLabel: { flex: 1, fontSize: 15, fontWeight: '500', color: colors.textPrimary, }, candidateCard: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 16, padding: 16, marginBottom: 16, shadowColor: colors.shadow, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.04, shadowRadius: 6, elevation: 2, }, candidateIconWrap: { width: 44, height: 44, borderRadius: 12, backgroundColor: colors.primaryLight, justifyContent: 'center', alignItems: 'center', marginRight: 12, }, candidateText: { flex: 1, }, candidateTitle: { fontSize: 15, fontWeight: '600', color: colors.textPrimary, marginBottom: 2, }, candidateDesc: { fontSize: 12, color: colors.textSecondary, }, candidateAction: { fontSize: 13, fontWeight: '600', color: colors.primary, marginLeft: 8, }, });