Browse Source
- 删除 app/(tabs)/index.tsx,Tab 入口改由 _layout 托管 - 删除迁移后失效的 4 个旧 hooks(useHomeData / useCategoryData / useMatchData / useUserProfile) - 删除随之失效的 src/types/home.tsmaster
6 changed files with 0 additions and 664 deletions
@ -1,528 +0,0 @@ |
|||
import { Ionicons } from '@expo/vector-icons'; |
|||
import type { ComponentProps } from 'react'; |
|||
import { |
|||
ActivityIndicator, |
|||
Pressable, |
|||
ScrollView, |
|||
StyleSheet, |
|||
Text, |
|||
View, |
|||
} from 'react-native'; |
|||
import { SafeAreaView } from 'react-native-safe-area-context'; |
|||
import { useHomeData } from '../../src/hooks'; |
|||
import { colors } from '../../src/theme'; |
|||
import type { AssessmentTool, HomeAITool, HotJob } from '../../src/types'; |
|||
|
|||
type IconName = ComponentProps<typeof Ionicons>['name']; |
|||
|
|||
/* ===================== 配色映射(展示层,全部使用主题令牌) ===================== */ |
|||
|
|||
/** 分类图标配色:按分类 id 映射到语义色家族(浅底 + 图标色) */ |
|||
const CATEGORY_THEME: Record<string, { bg: string; icon: string }> = { |
|||
it: { bg: colors.primaryLight, icon: colors.primary }, |
|||
finance: { bg: colors.actionLight, icon: colors.actionText }, |
|||
edu: { bg: colors.successLight, icon: colors.successText }, |
|||
health: { bg: colors.dangerLight, icon: colors.dangerText }, |
|||
mfg: { bg: colors.infoLight, icon: colors.infoText }, |
|||
house: { bg: colors.primaryLight, icon: colors.primaryDark }, |
|||
retail: { bg: colors.actionLight, icon: colors.action }, |
|||
media: { bg: colors.infoLight, icon: colors.info }, |
|||
}; |
|||
|
|||
/** 工具卡片配色:测评工具用主色,AI 工具用信息色 */ |
|||
const TOOL_THEME = { |
|||
primary: { |
|||
iconBg: colors.primaryLight, |
|||
icon: colors.primary, |
|||
action: colors.primaryText, |
|||
}, |
|||
info: { |
|||
iconBg: colors.infoLight, |
|||
icon: colors.info, |
|||
action: colors.infoText, |
|||
}, |
|||
} as const; |
|||
|
|||
/* ===================== 子组件 ===================== */ |
|||
|
|||
/** 高分优质岗位卡片 */ |
|||
function HotJobCard({ job }: { job: HotJob }) { |
|||
return ( |
|||
<View style={styles.jobCard}> |
|||
<View style={styles.jobCardHeader}> |
|||
<Text style={styles.jobTitle} numberOfLines={1}> |
|||
{job.title} |
|||
</Text> |
|||
<View style={styles.scoreTag}> |
|||
<Text style={styles.scoreText}> |
|||
{job.score}分·{job.scoreLabel} |
|||
</Text> |
|||
</View> |
|||
</View> |
|||
<Text style={styles.jobSalary}>{job.salary}</Text> |
|||
<Text style={styles.jobMeta}> |
|||
{job.company} · {job.location} |
|||
</Text> |
|||
<Text style={styles.jobMeta}>经验{job.experience}</Text> |
|||
<View style={styles.jobTags}> |
|||
{job.benefits.map((benefit) => ( |
|||
<View key={benefit} style={styles.jobTag}> |
|||
<Text style={styles.jobTagText}>{benefit}</Text> |
|||
</View> |
|||
))} |
|||
</View> |
|||
</View> |
|||
); |
|||
} |
|||
|
|||
/** 工具卡片(成长测评 / AI 智能工具共用结构) */ |
|||
function ToolCard({ |
|||
tool, |
|||
variant, |
|||
}: { |
|||
tool: AssessmentTool | HomeAITool; |
|||
variant: keyof typeof TOOL_THEME; |
|||
}) { |
|||
const theme = TOOL_THEME[variant]; |
|||
return ( |
|||
<Pressable |
|||
style={styles.toolCard} |
|||
onPress={() => { |
|||
/* TODO: 打开对应工具 */ |
|||
}} |
|||
> |
|||
<View style={[styles.toolIconBg, { backgroundColor: theme.iconBg }]}> |
|||
<Ionicons name={tool.icon as IconName} size={22} color={theme.icon} /> |
|||
</View> |
|||
<Text style={styles.toolTitle}>{tool.title}</Text> |
|||
<Text style={styles.toolDesc}>{tool.description}</Text> |
|||
<Text style={[styles.toolAction, { color: theme.action }]}>{tool.action}</Text> |
|||
</Pressable> |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* 首页 |
|||
*/ |
|||
export default function HomeScreen() { |
|||
const { |
|||
user, |
|||
categories, |
|||
match, |
|||
hotJobs, |
|||
assessmentTools, |
|||
aiTools, |
|||
loading, |
|||
error, |
|||
refresh, |
|||
} = useHomeData(); |
|||
|
|||
// 错误态
|
|||
if (error) { |
|||
return ( |
|||
<SafeAreaView style={styles.safeArea} edges={['top']}> |
|||
<View style={styles.stateWrap}> |
|||
<Text style={styles.stateText}>加载失败,请重试</Text> |
|||
<Pressable style={styles.retryBtn} onPress={refresh}> |
|||
<Text style={styles.retryText}>重试</Text> |
|||
</Pressable> |
|||
</View> |
|||
</SafeAreaView> |
|||
); |
|||
} |
|||
|
|||
// 加载态
|
|||
if (loading || !user || !match) { |
|||
return ( |
|||
<SafeAreaView style={styles.safeArea} edges={['top']}> |
|||
<View style={styles.stateWrap}> |
|||
<ActivityIndicator size="large" color={colors.primary} /> |
|||
</View> |
|||
</SafeAreaView> |
|||
); |
|||
} |
|||
|
|||
return ( |
|||
<SafeAreaView style={styles.safeArea} edges={['top']}> |
|||
<ScrollView |
|||
style={styles.scroll} |
|||
contentContainerStyle={styles.content} |
|||
showsVerticalScrollIndicator={false} |
|||
> |
|||
{/* 1. 问候区域 + 通知入口 */} |
|||
<View style={styles.header}> |
|||
<View style={styles.headerLeft}> |
|||
<Text style={styles.greeting}>早上好,{user.name}</Text> |
|||
<Text style={styles.subtitle}>{user.slogan}</Text> |
|||
</View> |
|||
<Pressable |
|||
style={styles.notice} |
|||
onPress={() => { |
|||
/* TODO: 打开通知列表 */ |
|||
}} |
|||
> |
|||
<Ionicons name="notifications-outline" size={22} color={colors.textPrimary} /> |
|||
<View style={styles.noticeDot} /> |
|||
</Pressable> |
|||
</View> |
|||
|
|||
{/* 2. 搜索框(点击跳转搜索) */} |
|||
<Pressable |
|||
style={styles.searchBox} |
|||
onPress={() => { |
|||
/* TODO: 跳转搜索页 */ |
|||
}} |
|||
> |
|||
<Ionicons name="search" size={18} color={colors.textSecondary} /> |
|||
<Text style={styles.searchPlaceholder}>搜索岗位、公司、关键词</Text> |
|||
</Pressable> |
|||
|
|||
{/* 3. 分类导航(两行四列网格) */} |
|||
<View style={styles.grid}> |
|||
{categories.map((cat) => { |
|||
const theme = CATEGORY_THEME[cat.id] ?? CATEGORY_THEME.it; |
|||
return ( |
|||
<Pressable |
|||
key={cat.id} |
|||
style={styles.gridItem} |
|||
onPress={() => { |
|||
/* TODO: 跳转到对应分类岗位列表 */ |
|||
}} |
|||
> |
|||
<View style={[styles.gridIconBg, { backgroundColor: theme.bg }]}> |
|||
<Ionicons name={cat.icon as IconName} size={24} color={theme.icon} /> |
|||
</View> |
|||
<Text style={styles.gridLabel}>{cat.name}</Text> |
|||
</Pressable> |
|||
); |
|||
})} |
|||
</View> |
|||
|
|||
{/* 4. 匹配度卡片 */} |
|||
<View style={styles.matchCard}> |
|||
<View style={styles.matchRow}> |
|||
<Text style={styles.matchTitle}>您与{match.position}匹配度</Text> |
|||
<Text style={styles.matchRate}>{match.matchRate}%</Text> |
|||
</View> |
|||
<Pressable |
|||
onPress={() => { |
|||
/* TODO: 立即沟通 */ |
|||
}} |
|||
> |
|||
<Text style={styles.matchAction}>{match.subtitle}</Text> |
|||
</Pressable> |
|||
</View> |
|||
|
|||
{/* 5. 高分优质岗位(横向滑动) */} |
|||
<View style={styles.sectionHeader}> |
|||
<Text style={styles.sectionTitle}>高分优质岗位</Text> |
|||
</View> |
|||
<ScrollView |
|||
horizontal |
|||
showsHorizontalScrollIndicator={false} |
|||
style={styles.hScroll} |
|||
contentContainerStyle={styles.hScrollContent} |
|||
> |
|||
{hotJobs.map((job) => ( |
|||
<HotJobCard key={job.id} job={job} /> |
|||
))} |
|||
</ScrollView> |
|||
|
|||
{/* 6. 成长测评工具(两列并排) */} |
|||
<View style={styles.sectionHeader}> |
|||
<Text style={styles.sectionTitle}>成长测评工具</Text> |
|||
</View> |
|||
<View style={styles.toolsRow}> |
|||
{assessmentTools.map((tool) => ( |
|||
<ToolCard key={tool.id} tool={tool} variant="primary" /> |
|||
))} |
|||
</View> |
|||
|
|||
{/* 7. AI 智能工具(两列并排) */} |
|||
<View style={styles.sectionHeader}> |
|||
<Text style={styles.sectionTitle}>AI智能工具</Text> |
|||
</View> |
|||
<View style={styles.toolsRow}> |
|||
{aiTools.map((tool) => ( |
|||
<ToolCard key={tool.id} tool={tool} variant="info" /> |
|||
))} |
|||
</View> |
|||
</ScrollView> |
|||
</SafeAreaView> |
|||
); |
|||
} |
|||
|
|||
// 跨平台阴影
|
|||
const shadow = { |
|||
shadowColor: colors.textPrimary, |
|||
shadowOffset: { width: 0, height: 2 }, |
|||
shadowOpacity: 0.06, |
|||
shadowRadius: 8, |
|||
elevation: 3, |
|||
}; |
|||
|
|||
const styles = StyleSheet.create({ |
|||
safeArea: { |
|||
flex: 1, |
|||
backgroundColor: colors.pageBg, |
|||
}, |
|||
stateWrap: { |
|||
flex: 1, |
|||
alignItems: 'center', |
|||
justifyContent: 'center', |
|||
}, |
|||
stateText: { |
|||
marginTop: 12, |
|||
fontSize: 14, |
|||
color: colors.textSecondary, |
|||
}, |
|||
retryBtn: { |
|||
marginTop: 16, |
|||
paddingHorizontal: 24, |
|||
paddingVertical: 8, |
|||
borderRadius: 18, |
|||
backgroundColor: colors.primaryBtn, |
|||
}, |
|||
retryText: { |
|||
fontSize: 13, |
|||
fontWeight: '600', |
|||
color: colors.white, |
|||
}, |
|||
scroll: { |
|||
flex: 1, |
|||
}, |
|||
content: { |
|||
paddingHorizontal: 20, |
|||
paddingBottom: 24, |
|||
}, |
|||
/* ---- 问候 + 通知 ---- */ |
|||
header: { |
|||
flexDirection: 'row', |
|||
alignItems: 'flex-start', |
|||
justifyContent: 'space-between', |
|||
paddingTop: 8, |
|||
paddingBottom: 16, |
|||
}, |
|||
headerLeft: { |
|||
flex: 1, |
|||
}, |
|||
greeting: { |
|||
fontSize: 24, |
|||
fontWeight: '700', |
|||
color: colors.textPrimary, |
|||
}, |
|||
subtitle: { |
|||
marginTop: 8, |
|||
fontSize: 12, |
|||
color: colors.textSecondary, |
|||
}, |
|||
notice: { |
|||
width: 40, |
|||
height: 40, |
|||
borderRadius: 20, |
|||
backgroundColor: colors.white, |
|||
alignItems: 'center', |
|||
justifyContent: 'center', |
|||
}, |
|||
noticeDot: { |
|||
position: 'absolute', |
|||
top: 8, |
|||
right: 9, |
|||
width: 8, |
|||
height: 8, |
|||
borderRadius: 4, |
|||
backgroundColor: colors.danger, |
|||
borderWidth: 1.5, |
|||
borderColor: colors.white, |
|||
}, |
|||
/* ---- 搜索框 ---- */ |
|||
searchBox: { |
|||
flexDirection: 'row', |
|||
alignItems: 'center', |
|||
height: 44, |
|||
borderRadius: 12, |
|||
backgroundColor: colors.white, |
|||
borderWidth: 1, |
|||
borderColor: colors.border, |
|||
paddingHorizontal: 12, |
|||
}, |
|||
searchPlaceholder: { |
|||
flex: 1, |
|||
marginLeft: 8, |
|||
fontSize: 14, |
|||
color: colors.textHint, |
|||
}, |
|||
/* ---- 分类网格 ---- */ |
|||
grid: { |
|||
flexDirection: 'row', |
|||
flexWrap: 'wrap', |
|||
marginTop: 20, |
|||
}, |
|||
gridItem: { |
|||
width: '25%', |
|||
alignItems: 'center', |
|||
marginBottom: 20, |
|||
}, |
|||
gridIconBg: { |
|||
width: 52, |
|||
height: 52, |
|||
borderRadius: 26, |
|||
alignItems: 'center', |
|||
justifyContent: 'center', |
|||
}, |
|||
gridLabel: { |
|||
marginTop: 8, |
|||
fontSize: 12, |
|||
color: colors.textPrimary, |
|||
textAlign: 'center', |
|||
}, |
|||
/* ---- 匹配度卡片 ---- */ |
|||
matchCard: { |
|||
marginTop: 4, |
|||
borderRadius: 16, |
|||
padding: 16, |
|||
backgroundColor: colors.primaryLight, |
|||
}, |
|||
matchRow: { |
|||
flexDirection: 'row', |
|||
alignItems: 'center', |
|||
justifyContent: 'space-between', |
|||
}, |
|||
matchTitle: { |
|||
flex: 1, |
|||
marginRight: 12, |
|||
fontSize: 15, |
|||
fontWeight: '600', |
|||
color: colors.textPrimary, |
|||
}, |
|||
matchRate: { |
|||
fontSize: 28, |
|||
fontWeight: '700', |
|||
color: colors.primary, |
|||
}, |
|||
matchAction: { |
|||
marginTop: 12, |
|||
fontSize: 13, |
|||
fontWeight: '600', |
|||
color: colors.primaryText, |
|||
}, |
|||
/* ---- 分区标题 ---- */ |
|||
sectionHeader: { |
|||
flexDirection: 'row', |
|||
alignItems: 'center', |
|||
justifyContent: 'space-between', |
|||
marginTop: 24, |
|||
marginBottom: 12, |
|||
}, |
|||
sectionTitle: { |
|||
fontSize: 18, |
|||
fontWeight: '700', |
|||
color: colors.textPrimary, |
|||
}, |
|||
/* ---- 横向滚动 ---- */ |
|||
hScroll: { |
|||
flexGrow: 0, |
|||
marginHorizontal: -20, |
|||
}, |
|||
hScrollContent: { |
|||
paddingHorizontal: 20, |
|||
paddingBottom: 4, |
|||
gap: 12, |
|||
}, |
|||
/* ---- 高分岗位卡片 ---- */ |
|||
jobCard: { |
|||
width: 240, |
|||
backgroundColor: colors.white, |
|||
borderRadius: 16, |
|||
padding: 16, |
|||
...shadow, |
|||
}, |
|||
jobCardHeader: { |
|||
flexDirection: 'row', |
|||
alignItems: 'center', |
|||
justifyContent: 'space-between', |
|||
}, |
|||
jobTitle: { |
|||
flex: 1, |
|||
marginRight: 8, |
|||
fontSize: 16, |
|||
fontWeight: '700', |
|||
color: colors.textPrimary, |
|||
}, |
|||
scoreTag: { |
|||
backgroundColor: colors.primaryLight, |
|||
paddingHorizontal: 8, |
|||
paddingVertical: 4, |
|||
borderRadius: 6, |
|||
}, |
|||
scoreText: { |
|||
fontSize: 12, |
|||
fontWeight: '600', |
|||
color: colors.primaryDark, |
|||
}, |
|||
jobSalary: { |
|||
marginTop: 12, |
|||
fontSize: 18, |
|||
fontWeight: '700', |
|||
color: colors.primary, |
|||
}, |
|||
jobMeta: { |
|||
marginTop: 8, |
|||
fontSize: 13, |
|||
color: colors.textSecondary, |
|||
}, |
|||
jobTags: { |
|||
flexDirection: 'row', |
|||
flexWrap: 'wrap', |
|||
marginTop: 12, |
|||
}, |
|||
jobTag: { |
|||
backgroundColor: colors.primaryLight, |
|||
borderWidth: 1, |
|||
borderColor: colors.primaryBorder, |
|||
borderRadius: 12, |
|||
paddingHorizontal: 10, |
|||
paddingVertical: 4, |
|||
marginRight: 8, |
|||
marginBottom: 8, |
|||
}, |
|||
jobTagText: { |
|||
fontSize: 12, |
|||
color: colors.primary, |
|||
}, |
|||
/* ---- 工具卡片 ---- */ |
|||
toolsRow: { |
|||
flexDirection: 'row', |
|||
gap: 12, |
|||
}, |
|||
toolCard: { |
|||
flex: 1, |
|||
backgroundColor: colors.white, |
|||
borderRadius: 16, |
|||
padding: 16, |
|||
...shadow, |
|||
}, |
|||
toolIconBg: { |
|||
width: 44, |
|||
height: 44, |
|||
borderRadius: 22, |
|||
alignItems: 'center', |
|||
justifyContent: 'center', |
|||
}, |
|||
toolTitle: { |
|||
marginTop: 12, |
|||
fontSize: 15, |
|||
fontWeight: '700', |
|||
color: colors.textPrimary, |
|||
}, |
|||
toolDesc: { |
|||
marginTop: 6, |
|||
fontSize: 12, |
|||
color: colors.textSecondary, |
|||
}, |
|||
toolAction: { |
|||
marginTop: 12, |
|||
fontSize: 13, |
|||
fontWeight: '600', |
|||
}, |
|||
}); |
|||
@ -1,10 +0,0 @@ |
|||
/** |
|||
* 岗位分类数据 hook |
|||
*/ |
|||
import { fetchCategories } from '../api'; |
|||
import { useAsyncData } from './useAsyncData'; |
|||
|
|||
export function useCategoryData() { |
|||
const { data: categories, loading, error, refresh } = useAsyncData(fetchCategories); |
|||
return { categories, loading, error, refresh }; |
|||
} |
|||
@ -1,60 +0,0 @@ |
|||
/** |
|||
* 首页数据 hook:并行组合用户信息、分类、匹配度、高分岗位、成长测评工具、AI 工具, |
|||
* 统一管理 loading / error / refresh 三态。 |
|||
*/ |
|||
import { useCallback } from 'react'; |
|||
import { |
|||
fetchAssessmentTools, |
|||
fetchCategories, |
|||
fetchHomeAITools, |
|||
fetchHotJobs, |
|||
fetchMatchData, |
|||
fetchUserInfo, |
|||
} from '../api'; |
|||
import type { |
|||
AssessmentTool, |
|||
Category, |
|||
HomeAITool, |
|||
HotJob, |
|||
MatchData, |
|||
UserInfo, |
|||
} from '../types'; |
|||
import { useAsyncData } from './useAsyncData'; |
|||
|
|||
/** 首页聚合数据 */ |
|||
interface HomeData { |
|||
user: UserInfo; |
|||
categories: Category[]; |
|||
match: MatchData; |
|||
hotJobs: HotJob[]; |
|||
assessmentTools: AssessmentTool[]; |
|||
aiTools: HomeAITool[]; |
|||
} |
|||
|
|||
export function useHomeData() { |
|||
const fetcher = useCallback(async (): Promise<HomeData> => { |
|||
const [user, categories, match, hotJobs, assessmentTools, aiTools] = await Promise.all([ |
|||
fetchUserInfo(), |
|||
fetchCategories(), |
|||
fetchMatchData(), |
|||
fetchHotJobs(), |
|||
fetchAssessmentTools(), |
|||
fetchHomeAITools(), |
|||
]); |
|||
return { user, categories, match, hotJobs, assessmentTools, aiTools }; |
|||
}, []); |
|||
|
|||
const { data, loading, error, refresh } = useAsyncData(fetcher); |
|||
|
|||
return { |
|||
user: data?.user ?? null, |
|||
categories: data?.categories ?? [], |
|||
match: data?.match ?? null, |
|||
hotJobs: data?.hotJobs ?? [], |
|||
assessmentTools: data?.assessmentTools ?? [], |
|||
aiTools: data?.aiTools ?? [], |
|||
loading, |
|||
error, |
|||
refresh, |
|||
}; |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
/** |
|||
* 岗位匹配度数据 hook |
|||
*/ |
|||
import { fetchMatchData } from '../api'; |
|||
import { useAsyncData } from './useAsyncData'; |
|||
|
|||
export function useMatchData() { |
|||
const { data: match, loading, error, refresh } = useAsyncData(fetchMatchData); |
|||
return { match, loading, error, refresh }; |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
/** |
|||
* 当前用户信息数据 hook |
|||
*/ |
|||
import { fetchUserInfo } from '../api'; |
|||
import { useAsyncData } from './useAsyncData'; |
|||
|
|||
export function useUserProfile() { |
|||
const { data: user, loading, error, refresh } = useAsyncData(fetchUserInfo); |
|||
return { user, loading, error, refresh }; |
|||
} |
|||
@ -1,46 +0,0 @@ |
|||
/** |
|||
* 首页相关类型定义 |
|||
*/ |
|||
|
|||
/** 热门岗位(首页「高分优质岗位」横向卡片;薪资/评分文案已由接口层格式化) */ |
|||
export interface HotJob { |
|||
id: string; |
|||
/** 岗位名称 */ |
|||
title: string; |
|||
/** 匹配分数(0-100) */ |
|||
score: number; |
|||
/** 评级文案,如 "优秀" */ |
|||
scoreLabel: string; |
|||
/** 已格式化薪资,如 "25-40K·15薪" */ |
|||
salary: string; |
|||
/** 公司名 */ |
|||
company: string; |
|||
/** 城市,如 "北京" */ |
|||
location: string; |
|||
/** 经验要求,如 "3-5年" */ |
|||
experience: string; |
|||
/** 福利标签 */ |
|||
benefits: string[]; |
|||
} |
|||
|
|||
/** 首页工具卡片(成长测评工具 / AI 智能工具共用的卡片结构) */ |
|||
export interface HomeTool { |
|||
id: string; |
|||
/** 工具名称 */ |
|||
title: string; |
|||
/** 工具描述 */ |
|||
description: string; |
|||
/** Ionicons 图标名 */ |
|||
icon: string; |
|||
/** 操作文案,如 "立即测评 →" */ |
|||
action: string; |
|||
} |
|||
|
|||
/** |
|||
* 测评工具(与 HomeTool 结构一致,语义化别名)。 |
|||
* 说明:成长页已存在带 variant 字段的 AITool,首页 AI 工具独立命名为 HomeAITool 以规避重名。 |
|||
*/ |
|||
export type AssessmentTool = HomeTool; |
|||
|
|||
/** AI 智能工具(首页) */ |
|||
export type HomeAITool = HomeTool; |
|||
Loading…
Reference in new issue