diff --git a/app/(enterprise)/enterprise-overview.tsx b/app/(enterprise)/enterprise-overview.tsx index 6df86b5..66ccf90 100644 --- a/app/(enterprise)/enterprise-overview.tsx +++ b/app/(enterprise)/enterprise-overview.tsx @@ -12,7 +12,7 @@ import { import { SafeAreaView } from 'react-native-safe-area-context'; import { useEnterpriseOverview } from '../../src/hooks'; import { colors, headerGradient } from '../../src/theme'; -import type { EnterpriseOverview, EnterpriseQuickAction, EnterpriseStat, JobEvaluationSummary } from '../../src/types'; +import type { EnterpriseOverview, EnterpriseQuickAction, EnterpriseStat, JobEvaluationSummary, MenuItem } from '../../src/types'; const COLOR_TOKENS: Record = { primary: colors.primary, @@ -194,6 +194,35 @@ function ManageList({ actions }: { actions: EnterpriseQuickAction[] }) { ); } +/* ===================== 子组件:企业功能菜单 ===================== */ + +/** + * 企业端功能菜单(更多服务):发布岗位 / 岗位管理 / 人才推荐等业务功能。 + * 与求职者端菜单严格隔离,由 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 any)} + > + + + + {item.title} + + + ))} + + ); +} + /* ===================== 页面主组件 ===================== */ export default function EnterpriseOverviewScreen() { @@ -229,6 +258,7 @@ export default function EnterpriseOverviewScreen() { + ); diff --git a/app/(tabs)/profile.tsx b/app/(tabs)/profile.tsx index 628663b..5d87a7e 100644 --- a/app/(tabs)/profile.tsx +++ b/app/(tabs)/profile.tsx @@ -11,6 +11,7 @@ import { } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useProfileData } from '../../src/hooks'; +import { useRole } from '../../src/context/RoleContext'; import { colors, headerGradientSolid } from '../../src/theme'; import type { Badge, DimensionScore, EnterpriseEntry, JobPreference, MenuItem, UserStats } from '../../src/types'; @@ -166,6 +167,7 @@ function EnterpriseEntryCard({ entry }: { entry: EnterpriseEntry }) { * 我的页 */ export default function ProfileScreen() { + const { role } = useRole(); const { profile, userStats, @@ -178,7 +180,7 @@ export default function ProfileScreen() { loading, error, refresh, - } = useProfileData(); + } = useProfileData(role ?? 'candidate'); const router = useRouter(); // 错误态 diff --git a/src/api/api.ts b/src/api/api.ts index f88d3d3..db69f84 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -60,6 +60,7 @@ import type { OnboardingChecklist, JobProgress, IdentityOption, + AppRole, AIInterviewData, InterviewSession, HREvaluation, @@ -609,8 +610,12 @@ const MOCK_USER_STATS: UserStats = { offers: 3, }; -/** 功能菜单列表(设置类入口已移至设置页,这里只保留业务功能) */ -const MOCK_MENU_ITEMS: MenuItem[] = [ +/** + * 求职者端功能菜单 + * 只含求职者相关功能;「企业入驻」作为切换到企业端的入口保留在此。 + * 企业侧功能(发布岗位 / 岗位管理等)一律不在此下发,避免角色混淆。 + */ +const MOCK_CANDIDATE_MENU_ITEMS: MenuItem[] = [ { id: 'resume', title: '我的简历', icon: 'document-text-outline', route: '/resume' }, { id: 'favorites', title: '我的收藏', icon: 'heart-outline', route: '/favorites' }, { id: 'history', title: '浏览历史', icon: 'time-outline', route: '/history' }, @@ -627,14 +632,22 @@ const MOCK_MENU_ITEMS: MenuItem[] = [ { id: 'game', title: '通关小游戏', icon: 'trophy-outline', route: '/game' }, { id: 'legal', title: '阿布阿诺法律服务', icon: 'shield-checkmark-outline', route: '/legal' }, { id: 'enterprise-certification', title: '企业入驻', icon: 'business-outline', route: '/enterprise-certification' }, +]; + +/** + * 企业端功能菜单 + * 只含企业 / HR 相关功能,在企业端「我的 - 更多服务」下发。 + * 不设「候选人详情」菜单入口:该页需要候选人 id,独立入口会落到兜底数据, + * 正确路径是「人才推荐 → 列表点击 → 带 id 进详情」。 + * 运营规则配置 / 数据审计等后台项见 MOCK_ENTERPRISE_MANAGE_ACTIONS。 + */ +const MOCK_ENTERPRISE_MENU_ITEMS: MenuItem[] = [ { id: 'job-publish', title: '发布岗位', icon: 'add-circle-outline', route: '/job-publish' }, - { id: 'job-evaluations', title: '岗位评价管理', icon: 'chatbubbles-outline', route: '/job-evaluations' }, - { id: 'candidate-detail', title: '候选人详情', icon: 'person-outline', route: '/candidate-detail' }, - { id: 'interview-score', title: '面试评分', icon: 'create-outline', route: '/interview-score' }, { id: 'job-management', title: '岗位管理', icon: 'business-outline', route: '/job-management' }, + { id: 'job-evaluations', title: '岗位评价管理', icon: 'chatbubbles-outline', route: '/job-evaluations' }, { id: 'talent-recommendation', title: '人才推荐', icon: 'people-outline', route: '/talent-recommendation' }, + { id: 'interview-score', title: '面试评分', icon: 'create-outline', route: '/interview-score' }, { id: 'enterprise-chat', title: '企业沟通', icon: 'chatbubble-outline', route: '/enterprise-chat' }, - // 注:运营规则配置 / 数据审计报告属后台管理术语,已迁至企业端「我的-管理后台」,不在此暴露给求职者 ]; /** 企业端入口 */ @@ -1093,11 +1106,13 @@ export async function fetchUserStats(): Promise { * 获取个人中心功能菜单列表 * @method GET * @url /api/user/menus + * @param role { AppRole } 当前角色(端),按角色下发不同菜单,避免求职者端出现企业功能、反之亦然 * @returns Promise 菜单数组 */ -export async function fetchMenuItems(): Promise { - // TODO: const res = await fetch(`${API_BASE_URL}/api/user/menus`, { method: 'GET' }); return res.json(); - return mockRequest(MOCK_MENU_ITEMS); +export async function fetchMenuItems(role: AppRole = 'candidate'): Promise { + // TODO: const res = await fetch(`${API_BASE_URL}/api/user/menus?role=${role}`, { method: 'GET' }); return res.json(); + const items = role === 'enterprise' ? MOCK_ENTERPRISE_MENU_ITEMS : MOCK_CANDIDATE_MENU_ITEMS; + return mockRequest(items); } /** @@ -2396,6 +2411,7 @@ const MOCK_ENTERPRISE_OVERVIEW: EnterpriseOverview = { jobEvaluations: MOCK_JOB_EVALUATION_SUMMARIES, quickActions: MOCK_ENTERPRISE_QUICK_ACTIONS, manageActions: MOCK_ENTERPRISE_MANAGE_ACTIONS, + menuItems: MOCK_ENTERPRISE_MENU_ITEMS, }; /** diff --git a/src/context/RoleContext.tsx b/src/context/RoleContext.tsx index 8725fed..651d678 100644 --- a/src/context/RoleContext.tsx +++ b/src/context/RoleContext.tsx @@ -6,6 +6,7 @@ import { type ReactNode, } from 'react'; import { storageGet, storageSet, storageRemove } from '../lib/storage'; +import type { AppRole } from '../types'; /* ===================== 角色(端)上下文 ===================== * 区分「求职端 candidate」与「企业端 enterprise」两套管路。 @@ -17,7 +18,8 @@ import { storageGet, storageSet, storageRemove } from '../lib/storage'; * persistRole / loadRole 替换为真实读写,即可跨重启记住当前端。 */ -export type AppRole = 'candidate' | 'enterprise'; +/** 角色类型统一由 types 层定义,此处 re-export 便于页面从 context 一并导入 */ +export type { AppRole }; interface RoleContextValue { role: AppRole | null; diff --git a/src/hooks/useProfileData.ts b/src/hooks/useProfileData.ts index f1fcfd6..21870df 100644 --- a/src/hooks/useProfileData.ts +++ b/src/hooks/useProfileData.ts @@ -14,6 +14,7 @@ import { fetchUserStats, } from '../api'; import type { + AppRole, Badge, DimensionScore, EnterpriseEntry, @@ -37,7 +38,7 @@ interface ProfileData { enterpriseEntry: EnterpriseEntry; } -export function useProfileData() { +export function useProfileData(role: AppRole = 'candidate') { const fetcher = useCallback(async (): Promise => { const [profile, userStats, overallScore, dimensions, preference, badges, menuItems, enterpriseEntry] = await Promise.all([ fetchUserProfile(), @@ -46,11 +47,11 @@ export function useProfileData() { fetchDimensionScores(), fetchJobPreference(), fetchBadges(), - fetchMenuItems(), + fetchMenuItems(role), fetchEnterpriseEntry(), ]); return { profile, userStats, overallScore, dimensions, preference, badges, menuItems, enterpriseEntry }; - }, []); + }, [role]); const { data, loading, error, refresh } = useAsyncData(fetcher); diff --git a/src/types/enterprise.ts b/src/types/enterprise.ts index ddab8ca..cff5406 100644 --- a/src/types/enterprise.ts +++ b/src/types/enterprise.ts @@ -8,6 +8,7 @@ * - 候选人详情 * - 面试评分 */ +import type { MenuItem } from './user'; /* ===================== 企业数据总览 ===================== */ @@ -70,6 +71,12 @@ export interface EnterpriseOverview { * 仅企业端「我的」展示,不在求职者端菜单暴露,避免后台术语干扰 C 端用户 */ manageActions: EnterpriseQuickAction[]; + /** + * 企业端功能菜单(发布岗位、岗位管理、人才推荐等业务功能) + * 由 AppRole='enterprise' 下发,仅在企业端「我的 - 更多服务」展示, + * 与求职者端菜单严格隔离,避免角色混淆 + */ + menuItems: MenuItem[]; } /** 企业总览快捷入口 */ diff --git a/src/types/index.ts b/src/types/index.ts index 63e43de..014d3e8 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -117,6 +117,7 @@ export type { TopCandidate, } from './rank'; export type { + AppRole, Badge, DimensionScore, EnterpriseEntry, diff --git a/src/types/user.ts b/src/types/user.ts index 4f519f7..19d7edd 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -25,6 +25,14 @@ export interface UserStats { offers: number; } +/** + * 应用角色 + * - candidate:求职者 + * - enterprise:企业 / HR + * 用于按角色下发不同的功能菜单,避免求职者端出现企业功能、反之亦然 + */ +export type AppRole = 'candidate' | 'enterprise'; + /** 功能菜单项 */ export interface MenuItem { id: string;