Browse Source

fix(navigation): 修正双端(端口)切换的角色与入口问题

- 身份选项类型 hr→enterprise,与全局 AppRole 对齐(消除类型不一致)
- 两端 layout 将 setRole 由 useEffect 改为 useFocusEffect,
  根治「从企业端返回求职端」后 role 残留 enterprise、菜单/数据串端的问题
- 企业端「我的」页补「切换到求职端」入口(setRole('candidate') + replace /(tabs)/jobs),
  与求职端「切换到企业端」对称,避免经设置进入企业端后回不去
- 求职端「切换到企业端」卡片 onPress 显式 setRole('enterprise') 兜底
master
doublex001 2 months ago
parent
commit
0d4ee5b87c
  1. 11
      app/(enterprise)/_layout.tsx
  2. 73
      app/(enterprise)/enterprise-overview.tsx
  3. 11
      app/(tabs)/_layout.tsx
  4. 10
      app/(tabs)/profile.tsx
  5. 2
      src/api/api.ts
  6. 4
      src/types/tools.ts

11
app/(enterprise)/_layout.tsx

@ -1,7 +1,8 @@
import { Ionicons } from '@expo/vector-icons';
import { Tabs } from 'expo-router/js-tabs';
import type { BottomTabBarProps } from 'expo-router/js-tabs';
import { useEffect, useRef } from 'react';
import { useFocusEffect } from 'expo-router';
import { useCallback, useEffect, useRef } from 'react';
import { Animated, Easing, Pressable, StyleSheet, View } from 'react-native';
import { colors } from '../../src/theme';
import { useRole } from '../../src/context/RoleContext';
@ -142,9 +143,13 @@ function TabBar({ state, descriptors, navigation, insets }: BottomTabBarProps) {
export default function EnterpriseTabLayout() {
const { setRole } = useRole();
useEffect(() => {
// 用 useFocusEffect:进入/返回该端时随焦点写入角色,
// 避免「切换到企业端」后返回时 layout 未重新挂载导致 role 残留 candidate。
useFocusEffect(
useCallback(() => {
setRole('enterprise');
}, [setRole]);
}, [setRole]),
);
return (
<Tabs

73
app/(enterprise)/enterprise-overview.tsx

@ -11,6 +11,7 @@ import {
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useEnterpriseOverview } from '../../src/hooks';
import { useRole } from '../../src/context/RoleContext';
import { colors, headerGradient } from '../../src/theme';
import type { EnterpriseOverview, EnterpriseQuickAction, EnterpriseStat, JobEvaluationSummary, MenuItem } from '../../src/types';
@ -223,6 +224,36 @@ function MenuList({ items }: { items: MenuItem[] }) {
);
}
/* ===================== 子组件:切换到求职端卡片 ===================== */
/**
* 企业端「我的」页回切求职端,与求职端「切换到企业端」卡片对称,
* 避免经「设置→身份切换」进入企业端后返回栈为空、回不去求职端。
*/
function CandidateEntryCard() {
const router = useRouter();
const { setRole } = useRole();
return (
<Pressable
style={styles.candidateCard}
onPress={() => {
setRole('candidate');
router.replace('/(tabs)/jobs');
}}
hitSlop={4}
>
<View style={styles.candidateIconWrap}>
<Ionicons name="person-outline" size={22} color={colors.primary} />
</View>
<View style={styles.candidateText}>
<Text style={styles.candidateTitle}>切换到求职端</Text>
<Text style={styles.candidateDesc}>返回求职者端,浏览岗位与你的竞争力数据</Text>
</View>
<Text style={styles.candidateAction}>去求职端 →</Text>
</Pressable>
);
}
/* ===================== 页面主组件 ===================== */
export default function EnterpriseOverviewScreen() {
@ -254,6 +285,7 @@ export default function EnterpriseOverviewScreen() {
</SafeAreaView>
</LinearGradient>
<ScrollView style={styles.scroll} contentContainerStyle={styles.scrollContent}>
<CandidateEntryCard />
<StatGrid stats={overview.stats} />
<QuickActions actions={overview.quickActions} />
<JobEvaluationList jobs={overview.jobEvaluations} />
@ -520,4 +552,45 @@ const styles = StyleSheet.create({
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,
},
});

11
app/(tabs)/_layout.tsx

@ -1,7 +1,8 @@
import { Ionicons } from '@expo/vector-icons';
import { Tabs } from 'expo-router/js-tabs';
import type { BottomTabBarProps } from 'expo-router/js-tabs';
import { useEffect, useRef } from 'react';
import { useFocusEffect } from 'expo-router';
import { useCallback, useEffect, useRef } from 'react';
import { Animated, Easing, Pressable, StyleSheet, View } from 'react-native';
import { colors } from '../../src/theme';
import { useRole } from '../../src/context/RoleContext';
@ -164,9 +165,13 @@ function TabBar({ state, descriptors, navigation, insets }: BottomTabBarProps) {
export default function TabLayout() {
const { setRole } = useRole();
useEffect(() => {
// 用 useFocusEffect:进入/返回该端时随焦点写入角色,
// 避免「从企业端返回求职端」时 layout 未重新挂载导致 role 残留 enterprise。
useFocusEffect(
useCallback(() => {
setRole('candidate');
}, [setRole]);
}, [setRole]),
);
return (
<Tabs

10
app/(tabs)/profile.tsx

@ -149,8 +149,16 @@ function StatsRow({ stats }: { stats: UserStats }) {
/** 切换到企业端卡片 */
function EnterpriseEntryCard({ entry }: { entry: EnterpriseEntry }) {
const router = useRouter();
const { setRole } = useRole();
return (
<Pressable style={styles.enterpriseCard} onPress={() => router.push('/enterprise-overview')}>
<Pressable
style={styles.enterpriseCard}
onPress={() => {
setRole('enterprise');
router.push('/enterprise-overview');
}}
hitSlop={4}
>
<View style={styles.enterpriseIconWrap}>
<Ionicons name="business-outline" size={22} color={colors.primary} />
</View>

2
src/api/api.ts

@ -3290,7 +3290,7 @@ const MOCK_IDENTITY_OPTIONS: IdentityOption[] = [
title: '我是企业 / HR',
desc: '发布岗位、筛选人才、管理评价、展示企业形象',
icon: 'briefcase-outline',
role: 'hr',
role: 'enterprise',
},
];

4
src/types/tools.ts

@ -343,6 +343,6 @@ export interface IdentityOption {
desc: string;
/** 图标名(Ionicons) */
icon: string;
/** 角色:求职者 / HR */
role: 'candidate' | 'hr';
/** 角色:求职者 / 企业(HR) */
role: 'candidate' | 'enterprise';
}

Loading…
Cancel
Save