You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
11 KiB
306 lines
11 KiB
/**
|
|
* 岗位对比页:并排对比 2-3 个岗位的九大维度评分、基本信息与福利标签。
|
|
* 数据通过 useCompareData 获取,页面只负责渲染与交互。
|
|
*/
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { useRouter } from 'expo-router';
|
|
import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import { useCompareData } from '../../src/hooks';
|
|
import {colors, spacing} from '../../src/theme';
|
|
|
|
function Header({ onBack }: { onBack: () => void }) {
|
|
return (
|
|
<View style={styles.header}>
|
|
<Pressable onPress={onBack} hitSlop={8} style={styles.headerBtn}>
|
|
<Ionicons name="chevron-back" size={24} color={colors.textPrimary} />
|
|
</Pressable>
|
|
<Text style={styles.headerTitle}>岗位对比</Text>
|
|
<View style={styles.headerBtn} />
|
|
</View>
|
|
);
|
|
}
|
|
|
|
export default function CompareScreen() {
|
|
const router = useRouter();
|
|
const { jobs, dimensionScores, basicInfo, benefits, loading, error, refresh } =
|
|
useCompareData();
|
|
|
|
// 错误态
|
|
if (error) {
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top']}>
|
|
<Header onBack={() => router.back()} />
|
|
<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) {
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top']}>
|
|
<Header onBack={() => router.back()} />
|
|
<View style={styles.stateWrap}>
|
|
<Text style={styles.stateText}>加载中…</Text>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
// 空态
|
|
if (jobs.length === 0) {
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top']}>
|
|
<Header onBack={() => router.back()} />
|
|
<View style={styles.stateWrap}>
|
|
<View style={styles.emptyIconWrap}>
|
|
<Ionicons name="git-compare-outline" size={32} color={colors.primaryBtn} />
|
|
</View>
|
|
<Text style={styles.emptyTitle}>暂无对比岗位</Text>
|
|
<Text style={styles.emptyDesc}>在岗位详情页点击「加入对比」即可开始对比</Text>
|
|
<Pressable style={styles.emptyBtn} onPress={() => router.push('/(tabs)/jobs')}>
|
|
<Text style={styles.emptyBtnText}>去挑选岗位</Text>
|
|
</Pressable>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top']}>
|
|
<Header onBack={() => router.back()} />
|
|
<ScrollView contentContainerStyle={styles.content} showsVerticalScrollIndicator={false}>
|
|
<View style={styles.jobRow}>
|
|
{jobs.map((job) => (
|
|
<View key={job.id} style={styles.jobCard}>
|
|
<View style={[styles.jobColorBar, { backgroundColor: job.color }]} />
|
|
<Text style={styles.jobTitle} numberOfLines={1}>
|
|
{job.title}
|
|
</Text>
|
|
<Text style={styles.jobCompany} numberOfLines={1}>
|
|
{job.company}
|
|
</Text>
|
|
<Text style={styles.jobSalary}>{job.salary}</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
|
|
<View style={styles.sectionHeader}>
|
|
<View style={styles.sectionBar} />
|
|
<Text style={styles.sectionTitle}>九大维度评分</Text>
|
|
</View>
|
|
<View style={styles.card}>
|
|
{dimensionScores.map((row, index) => {
|
|
const isLast = index === dimensionScores.length - 1;
|
|
return (
|
|
<View key={row.dimension} style={[styles.scoreRow, isLast && styles.scoreRowLast]}>
|
|
<Text style={styles.scoreDimension}>{row.dimension}</Text>
|
|
<View style={styles.scoreBars}>
|
|
{row.scores.map((score, i) => (
|
|
<View key={jobs[i]?.id ?? i} style={styles.scoreBarItem}>
|
|
<Text style={styles.scoreValue}>{score}</Text>
|
|
<View style={styles.scoreTrack}>
|
|
<View
|
|
style={[
|
|
styles.scoreFill,
|
|
{
|
|
width: `${score}%`,
|
|
backgroundColor: jobs[i]?.color ?? colors.primaryBtn,
|
|
},
|
|
]}
|
|
/>
|
|
</View>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
|
|
<View style={styles.sectionHeader}>
|
|
<View style={styles.sectionBar} />
|
|
<Text style={styles.sectionTitle}>基本信息</Text>
|
|
</View>
|
|
<View style={styles.card}>
|
|
{basicInfo.fields.map((field, index) => {
|
|
const isLast = index === basicInfo.fields.length - 1;
|
|
return (
|
|
<View key={field} style={[styles.infoRow, isLast && styles.infoRowLast]}>
|
|
<Text style={styles.infoField}>{field}</Text>
|
|
<View style={styles.infoValues}>
|
|
{jobs.map((job, i) => (
|
|
<Text key={job.id} style={styles.infoValue}>
|
|
{basicInfo.values[index]?.[i] ?? '-'}
|
|
</Text>
|
|
))}
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
|
|
<View style={styles.sectionHeader}>
|
|
<View style={styles.sectionBar} />
|
|
<Text style={styles.sectionTitle}>福利标签</Text>
|
|
</View>
|
|
<View style={styles.card}>
|
|
{jobs.map((job, index) => {
|
|
const isLast = index === jobs.length - 1;
|
|
const tags = benefits[index] ?? [];
|
|
return (
|
|
<View key={job.id} style={[styles.benefitRow, isLast && styles.benefitRowLast]}>
|
|
<Text style={styles.benefitJob} numberOfLines={1}>
|
|
{job.title}
|
|
</Text>
|
|
<View style={styles.benefitTags}>
|
|
{tags.length === 0 ? (
|
|
<Text style={styles.benefitEmpty}>暂无</Text>
|
|
) : (
|
|
tags.map((tag) => (
|
|
<View key={tag} style={styles.benefitTag}>
|
|
<Text style={styles.benefitTagText}>{tag}</Text>
|
|
</View>
|
|
))
|
|
)}
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
safeArea: { flex: 1, backgroundColor: colors.pageBg },
|
|
header: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingHorizontal: spacing.framework,
|
|
paddingVertical: 12,
|
|
backgroundColor: colors.surface,
|
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
borderBottomColor: colors.borderLight,
|
|
},
|
|
headerBtn: { width: 32, alignItems: 'center' },
|
|
headerTitle: { fontSize: 17, fontWeight: '500', color: colors.textPrimary },
|
|
content: { padding: spacing.card, paddingBottom: 32 },
|
|
stateWrap: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: 12,
|
|
paddingHorizontal: 32,
|
|
},
|
|
stateText: { fontSize: 14, color: colors.textSecondary },
|
|
retryBtn: {
|
|
paddingHorizontal: 20,
|
|
paddingVertical: spacing.card,
|
|
borderRadius: spacing.card,
|
|
backgroundColor: colors.primaryLight,
|
|
},
|
|
retryText: { fontSize: 14, color: colors.primaryText },
|
|
emptyIconWrap: {
|
|
width: 64,
|
|
height: 64,
|
|
borderRadius: 32,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
backgroundColor: colors.primaryLight,
|
|
marginBottom: 4,
|
|
},
|
|
emptyTitle: { fontSize: 15, fontWeight: '500', color: colors.textPrimary },
|
|
emptyDesc: { fontSize: 13, color: colors.textSecondary, textAlign: 'center' },
|
|
emptyBtn: {
|
|
marginTop: 8,
|
|
paddingHorizontal: 24,
|
|
paddingVertical: 10,
|
|
borderRadius: 10,
|
|
backgroundColor: colors.primaryBtn,
|
|
},
|
|
emptyBtnText: { fontSize: 14, fontWeight: '500', color: colors.white },
|
|
jobRow: { flexDirection: 'row', gap: 8 },
|
|
jobCard: {
|
|
flex: 1,
|
|
padding: spacing.card,
|
|
borderRadius: spacing.card,
|
|
backgroundColor: colors.surface,
|
|
borderWidth: StyleSheet.hairlineWidth,
|
|
borderColor: colors.borderLight,
|
|
overflow: 'hidden',
|
|
},
|
|
jobColorBar: { height: 3, borderRadius: 2, marginBottom: 8 },
|
|
jobTitle: { fontSize: 14, fontWeight: '500', color: colors.textPrimary, marginBottom: 2 },
|
|
jobCompany: { fontSize: 12, color: colors.textSecondary, marginBottom: 6 },
|
|
jobSalary: { fontSize: 13, fontWeight: '500', color: colors.actionText },
|
|
sectionHeader: { flexDirection: 'row', alignItems: 'center', marginTop: 20, marginBottom: 12 },
|
|
sectionBar: {
|
|
width: 3,
|
|
height: 14,
|
|
borderRadius: 2,
|
|
backgroundColor: colors.primaryBtn,
|
|
marginRight: 8,
|
|
},
|
|
sectionTitle: { fontSize: 15, fontWeight: '500', color: colors.textPrimary },
|
|
card: {
|
|
borderRadius: spacing.card,
|
|
backgroundColor: colors.surface,
|
|
borderWidth: StyleSheet.hairlineWidth,
|
|
borderColor: colors.borderLight,
|
|
paddingHorizontal: spacing.card,
|
|
},
|
|
scoreRow: {
|
|
paddingVertical: 12,
|
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
borderBottomColor: colors.borderLight,
|
|
},
|
|
scoreRowLast: { borderBottomWidth: 0 },
|
|
scoreDimension: { fontSize: 13, color: colors.textPrimary, marginBottom: 8 },
|
|
scoreBars: { gap: 6 },
|
|
scoreBarItem: { flexDirection: 'row', alignItems: 'center', gap: 8 },
|
|
scoreValue: { width: 24, fontSize: 12, color: colors.textSecondary },
|
|
scoreTrack: {
|
|
flex: 1,
|
|
height: 6,
|
|
borderRadius: 3,
|
|
backgroundColor: colors.borderLight,
|
|
overflow: 'hidden',
|
|
},
|
|
scoreFill: { height: '100%', borderRadius: 3 },
|
|
infoRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingVertical: 12,
|
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
borderBottomColor: colors.borderLight,
|
|
},
|
|
infoRowLast: { borderBottomWidth: 0 },
|
|
infoField: { width: 80, fontSize: 13, color: colors.textSecondary },
|
|
infoValues: { flex: 1, flexDirection: 'row' },
|
|
infoValue: { flex: 1, fontSize: 13, color: colors.textPrimary },
|
|
benefitRow: {
|
|
paddingVertical: 12,
|
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
borderBottomColor: colors.borderLight,
|
|
},
|
|
benefitRowLast: { borderBottomWidth: 0 },
|
|
benefitJob: { fontSize: 13, color: colors.textPrimary, marginBottom: 8 },
|
|
benefitTags: { flexDirection: 'row', flexWrap: 'wrap', gap: 6 },
|
|
benefitTag: {
|
|
paddingHorizontal: spacing.card,
|
|
paddingVertical: 3,
|
|
borderRadius: 4,
|
|
backgroundColor: colors.primaryLight,
|
|
},
|
|
benefitTagText: { fontSize: 11, color: colors.primaryText },
|
|
benefitEmpty: { fontSize: 12, color: colors.textHint },
|
|
});
|
|
|