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.
754 lines
20 KiB
754 lines
20 KiB
import { Ionicons } from '@expo/vector-icons';
|
|
import { useLocalSearchParams, useRouter } from 'expo-router';
|
|
import {
|
|
ActivityIndicator,
|
|
Alert,
|
|
Pressable,
|
|
ScrollView,
|
|
StyleSheet,
|
|
Text,
|
|
View,
|
|
} from 'react-native';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import Svg, { Circle, Rect } from 'react-native-svg';
|
|
import { useRankData } from '../../src/hooks';
|
|
import { colors } from '../../src/theme';
|
|
import { formatThousands } from '../../src/utils';
|
|
import type { DimensionPercentile, RankData, RankEmptyState, TopCandidate } from '../../src/types';
|
|
|
|
/** 分布条刻度(0/25/50/75/100) */
|
|
const DISTRIBUTION_TICKS = [0, 25, 50, 75, 100];
|
|
|
|
/* ===================== 子组件:顶部导航 ===================== */
|
|
|
|
function Header({ onBack }: { onBack: () => void }) {
|
|
return (
|
|
<View style={styles.header}>
|
|
<Pressable style={styles.headerBtn} onPress={onBack} hitSlop={8}>
|
|
<Ionicons name="chevron-back" size={24} color={colors.textPrimary} />
|
|
</Pressable>
|
|
<Text style={styles.headerTitle}>同岗位竞争力排名</Text>
|
|
<View style={styles.headerBtn} />
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:综合排名卡片 ===================== */
|
|
|
|
function RankSummary({ rank }: { rank: RankData }) {
|
|
return (
|
|
<View style={styles.rankCard}>
|
|
<Text style={styles.rankNumber}>第 {rank.rank} 名</Text>
|
|
<Text style={styles.rankPercentile}>超越 {rank.percentile}% 的候选人</Text>
|
|
|
|
<View style={styles.scoreRow}>
|
|
<Text style={styles.scoreLabel}>综合评分</Text>
|
|
<Text style={styles.scoreValue}>{rank.overallScore} 分</Text>
|
|
<View style={styles.levelBadge}>
|
|
<Text style={styles.levelText}>{rank.scoreLevel}</Text>
|
|
</View>
|
|
</View>
|
|
|
|
<Text style={styles.rankPosition}>百分位排名:{rank.rankPosition}</Text>
|
|
|
|
<Pressable
|
|
style={styles.detailLink}
|
|
onPress={() => Alert.alert('详细分析', '详细竞争力分析报告功能开发中,敬请期待')}
|
|
hitSlop={6}
|
|
>
|
|
<Text style={styles.detailLinkText}>查看我的详细分析</Text>
|
|
<Ionicons name="chevron-forward" size={14} color={colors.primary} />
|
|
</Pressable>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:百分位分布图(SVG) ===================== */
|
|
|
|
function DistributionChart({ percentile }: { percentile: number }) {
|
|
const clamped = Math.max(0, Math.min(100, percentile));
|
|
const width = 300;
|
|
const height = 40;
|
|
const padding = 20;
|
|
const trackWidth = width - padding * 2;
|
|
const fillWidth = (trackWidth * clamped) / 100;
|
|
const dotX = padding + fillWidth;
|
|
|
|
return (
|
|
<View>
|
|
<Svg width="100%" height={height} viewBox={`0 0 ${width} ${height}`}>
|
|
{/* 轨道 */}
|
|
<Rect
|
|
x={padding}
|
|
y={height / 2 - 3}
|
|
width={trackWidth}
|
|
height={6}
|
|
rx={3}
|
|
fill={colors.border}
|
|
/>
|
|
{/* 已超越填充 */}
|
|
<Rect
|
|
x={padding}
|
|
y={height / 2 - 3}
|
|
width={fillWidth}
|
|
height={6}
|
|
rx={3}
|
|
fill={colors.primary}
|
|
/>
|
|
{/* 当前所在位置圆点 */}
|
|
<Circle
|
|
cx={dotX}
|
|
cy={height / 2}
|
|
r={8}
|
|
fill={colors.primary}
|
|
stroke={colors.white}
|
|
strokeWidth={2}
|
|
/>
|
|
</Svg>
|
|
<View style={styles.distributionLabels}>
|
|
{DISTRIBUTION_TICKS.map((tick) => (
|
|
<Text key={tick} style={styles.distributionLabel}>
|
|
{tick}%
|
|
</Text>
|
|
))}
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:维度百分位行 ===================== */
|
|
|
|
function DimensionRow({ dim, isLast }: { dim: DimensionPercentile; isLast: boolean }) {
|
|
return (
|
|
<View style={[styles.dimensionItem, isLast && styles.dimensionItemLast]}>
|
|
<Text style={styles.dimensionName}>{dim.name}</Text>
|
|
<View style={styles.dimensionRight}>
|
|
<Text style={styles.dimensionPercentile}>{dim.percentile}</Text>
|
|
<Text style={styles.dimensionScore}>{dim.score} 分</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:TOP 候选人行 ===================== */
|
|
|
|
function CandidateRow({ candidate, rank }: { candidate: TopCandidate; rank: number }) {
|
|
return (
|
|
<View style={styles.candidateItem}>
|
|
<View style={styles.candidateRank}>
|
|
<Text style={styles.candidateRankText}>{rank}</Text>
|
|
</View>
|
|
<View style={styles.candidateInfo}>
|
|
<View style={styles.candidateHeader}>
|
|
<Text style={styles.candidateName}>{candidate.name}</Text>
|
|
<Text style={styles.candidateScore}>{candidate.score}分</Text>
|
|
</View>
|
|
<Text style={styles.candidateTagLine}>{candidate.tags.join(' · ')}</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:空状态 ===================== */
|
|
|
|
function EmptyState({ state, onAction }: { state: RankEmptyState; onAction: () => void }) {
|
|
return (
|
|
<ScrollView
|
|
style={styles.scrollView}
|
|
contentContainerStyle={styles.emptyContent}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
<View style={styles.emptyCard}>
|
|
<View style={styles.emptyIconWrap}>
|
|
<Ionicons name="trophy-outline" size={48} color={colors.primary} />
|
|
</View>
|
|
<Text style={styles.emptyTitle}>{state.title}</Text>
|
|
<Text style={styles.emptySubtitle}>{state.subtitle}</Text>
|
|
<Pressable style={styles.emptyBtn} onPress={onAction} hitSlop={6}>
|
|
<Text style={styles.emptyBtnText}>{state.actionText}</Text>
|
|
</Pressable>
|
|
</View>
|
|
|
|
<View style={styles.progressCard}>
|
|
<Text style={styles.progressTitle}>简历完善进度</Text>
|
|
{state.resumeProgress.map((step, index) => (
|
|
<View key={step.id} style={styles.progressRow}>
|
|
<View style={styles.progressLeft}>
|
|
<View
|
|
style={[
|
|
styles.progressDot,
|
|
step.completed ? styles.progressDotDone : styles.progressDotTodo,
|
|
index === 0 && styles.progressDotFirst,
|
|
]}
|
|
>
|
|
{step.completed ? (
|
|
<Ionicons name="checkmark" size={12} color={colors.white} />
|
|
) : (
|
|
<Text style={styles.progressDotText}>{index + 1}</Text>
|
|
)}
|
|
</View>
|
|
{index < state.resumeProgress.length - 1 && (
|
|
<View
|
|
style={[
|
|
styles.progressLine,
|
|
step.completed && state.resumeProgress[index + 1]?.completed
|
|
? styles.progressLineDone
|
|
: null,
|
|
]}
|
|
/>
|
|
)}
|
|
</View>
|
|
<View style={styles.progressBody}>
|
|
<Text style={styles.progressLabel}>{step.label}</Text>
|
|
{step.tip ? <Text style={styles.progressTip}>{step.tip}</Text> : null}
|
|
</View>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</ScrollView>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:提升建议 ===================== */
|
|
|
|
function TipItem({
|
|
title,
|
|
description,
|
|
type,
|
|
onAction,
|
|
}: {
|
|
title: string;
|
|
description: string;
|
|
type: 'tip' | 'action';
|
|
onAction?: () => void;
|
|
}) {
|
|
if (type === 'action') {
|
|
return (
|
|
<Pressable style={styles.tipAction} onPress={onAction} hitSlop={6}>
|
|
<Text style={styles.tipActionText}>{title}</Text>
|
|
<Ionicons name="chevron-forward" size={16} color={colors.primary} />
|
|
</Pressable>
|
|
);
|
|
}
|
|
return (
|
|
<View style={styles.tipItem}>
|
|
<Text style={styles.tipTitle}>{title}</Text>
|
|
{description ? <Text style={styles.tipDesc}>{description}</Text> : null}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 页面 ===================== */
|
|
|
|
export default function RankScreen() {
|
|
const router = useRouter();
|
|
const { jobId } = useLocalSearchParams<{ jobId: string }>();
|
|
const { rankData, dimensions, candidates, tips, emptyState, hasData, loading, error, refresh } =
|
|
useRankData(jobId || '1');
|
|
|
|
if (loading) {
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top', 'bottom']}>
|
|
<Header onBack={() => router.back()} />
|
|
<View style={styles.centerContainer}>
|
|
<ActivityIndicator size="large" color={colors.primary} />
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top', 'bottom']}>
|
|
<Header onBack={() => router.back()} />
|
|
<View style={styles.centerContainer}>
|
|
<Ionicons name="alert-circle-outline" size={48} color={colors.danger} />
|
|
<Text style={styles.errorText}>加载失败</Text>
|
|
<Pressable style={styles.retryButton} onPress={refresh}>
|
|
<Text style={styles.retryButtonText}>重试</Text>
|
|
</Pressable>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
if (!hasData && emptyState) {
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top', 'bottom']}>
|
|
<Header onBack={() => router.back()} />
|
|
<EmptyState state={emptyState} onAction={() => router.push('/resume')} />
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
if (!rankData) {
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top', 'bottom']}>
|
|
<Header onBack={() => router.back()} />
|
|
<View style={styles.centerContainer}>
|
|
<Ionicons name="alert-circle-outline" size={48} color={colors.danger} />
|
|
<Text style={styles.errorText}>暂无数据</Text>
|
|
<Pressable style={styles.retryButton} onPress={refresh}>
|
|
<Text style={styles.retryButtonText}>重试</Text>
|
|
</Pressable>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top', 'bottom']}>
|
|
<Header onBack={() => router.back()} />
|
|
|
|
<ScrollView
|
|
style={styles.scrollView}
|
|
contentContainerStyle={styles.scrollContent}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
{/* 模块2:岗位信息 */}
|
|
<View style={styles.jobInfoWrap}>
|
|
<Text style={styles.jobTitle}>
|
|
{rankData.jobTitle} · {rankData.company}
|
|
</Text>
|
|
<Text style={styles.candidateCount}>
|
|
共 {formatThousands(rankData.totalCandidates)} 位候选人申请
|
|
</Text>
|
|
</View>
|
|
|
|
{/* 模块3:综合排名卡片 */}
|
|
<RankSummary rank={rankData} />
|
|
|
|
{/* 模块4:竞争力百分位分布图 */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>| 竞争力百分位分布</Text>
|
|
<DistributionChart percentile={rankData.percentile} />
|
|
</View>
|
|
|
|
{/* 模块5:各维度百分位排名 */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>| 各维度百分位排名</Text>
|
|
{dimensions.map((dim, index) => (
|
|
<DimensionRow key={dim.id} dim={dim} isLast={index === dimensions.length - 1} />
|
|
))}
|
|
</View>
|
|
|
|
{/* 模块6:TOP 10 候选人 */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>TOP 10 候选人</Text>
|
|
{candidates.map((candidate, index) => (
|
|
<CandidateRow key={candidate.id} candidate={candidate} rank={index + 1} />
|
|
))}
|
|
</View>
|
|
|
|
{/* 模块7:提升建议 */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>提升建议</Text>
|
|
{tips.map((tip) =>
|
|
tip.type === 'action' ? (
|
|
<TipItem
|
|
key={tip.id}
|
|
title={tip.title}
|
|
description={tip.description}
|
|
type="action"
|
|
onAction={() => Alert.alert('提升建议', '该提升项功能开发中,敬请期待')}
|
|
/>
|
|
) : (
|
|
<TipItem
|
|
key={tip.id}
|
|
title={tip.title}
|
|
description={tip.description}
|
|
type="tip"
|
|
/>
|
|
),
|
|
)}
|
|
</View>
|
|
|
|
<View style={styles.bottomSpacer} />
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
/* ===================== 样式 ===================== */
|
|
|
|
const styles = StyleSheet.create({
|
|
safeArea: {
|
|
flex: 1,
|
|
backgroundColor: colors.pageBg,
|
|
},
|
|
/* ---- 顶部导航 ---- */
|
|
header: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 4,
|
|
},
|
|
headerBtn: {
|
|
width: 40,
|
|
height: 40,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
headerTitle: {
|
|
fontSize: 18,
|
|
fontWeight: '700',
|
|
color: colors.textPrimary,
|
|
},
|
|
/* ---- 状态 ---- */
|
|
centerContainer: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
errorText: {
|
|
marginTop: 12,
|
|
fontSize: 16,
|
|
color: colors.danger,
|
|
},
|
|
retryButton: {
|
|
marginTop: 16,
|
|
backgroundColor: colors.primary,
|
|
paddingHorizontal: 32,
|
|
paddingVertical: 10,
|
|
borderRadius: 24,
|
|
},
|
|
retryButtonText: {
|
|
color: colors.white,
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
},
|
|
/* ---- 滚动内容 ---- */
|
|
scrollView: {
|
|
flex: 1,
|
|
},
|
|
scrollContent: {
|
|
paddingHorizontal: 16,
|
|
paddingBottom: 40,
|
|
},
|
|
bottomSpacer: {
|
|
height: 20,
|
|
},
|
|
/* ---- 岗位信息 ---- */
|
|
jobInfoWrap: {
|
|
paddingVertical: 12,
|
|
alignItems: 'center',
|
|
},
|
|
jobTitle: {
|
|
fontSize: 17,
|
|
fontWeight: '700',
|
|
color: colors.textPrimary,
|
|
},
|
|
candidateCount: {
|
|
marginTop: 4,
|
|
fontSize: 13,
|
|
color: colors.textSecondary,
|
|
},
|
|
/* ---- 综合排名卡片 ---- */
|
|
rankCard: {
|
|
backgroundColor: colors.primaryLight,
|
|
borderRadius: 12,
|
|
padding: 16,
|
|
borderWidth: 1,
|
|
borderColor: colors.primaryBorder,
|
|
},
|
|
rankNumber: {
|
|
fontSize: 28,
|
|
fontWeight: '700',
|
|
color: colors.primary,
|
|
marginBottom: 4,
|
|
},
|
|
rankPercentile: {
|
|
fontSize: 20,
|
|
fontWeight: '600',
|
|
color: colors.textPrimary,
|
|
marginBottom: 8,
|
|
},
|
|
scoreRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginTop: 4,
|
|
},
|
|
scoreLabel: {
|
|
fontSize: 14,
|
|
color: colors.textSecondary,
|
|
},
|
|
scoreValue: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
color: colors.textPrimary,
|
|
marginLeft: 8,
|
|
},
|
|
levelBadge: {
|
|
backgroundColor: colors.successLight,
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 2,
|
|
borderRadius: 4,
|
|
marginLeft: 8,
|
|
},
|
|
levelText: {
|
|
fontSize: 12,
|
|
color: colors.successText,
|
|
fontWeight: '600',
|
|
},
|
|
rankPosition: {
|
|
marginTop: 8,
|
|
fontSize: 13,
|
|
color: colors.textSecondary,
|
|
},
|
|
detailLink: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginTop: 10,
|
|
},
|
|
detailLinkText: {
|
|
fontSize: 14,
|
|
color: colors.primary,
|
|
fontWeight: '500',
|
|
marginRight: 2,
|
|
},
|
|
/* ---- 分区 ---- */
|
|
section: {
|
|
backgroundColor: colors.white,
|
|
borderRadius: 12,
|
|
padding: 16,
|
|
marginTop: 16,
|
|
},
|
|
sectionTitle: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
color: colors.textPrimary,
|
|
marginBottom: 12,
|
|
},
|
|
/* ---- 百分位分布图 ---- */
|
|
distributionLabels: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
marginTop: 4,
|
|
},
|
|
distributionLabel: {
|
|
fontSize: 11,
|
|
color: colors.textSecondary,
|
|
},
|
|
/* ---- 维度行 ---- */
|
|
dimensionItem: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
paddingVertical: 12,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: colors.border,
|
|
},
|
|
dimensionItemLast: {
|
|
borderBottomWidth: 0,
|
|
},
|
|
dimensionName: {
|
|
fontSize: 14,
|
|
color: colors.textPrimary,
|
|
},
|
|
dimensionRight: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
dimensionPercentile: {
|
|
fontSize: 13,
|
|
color: colors.primary,
|
|
fontWeight: '500',
|
|
marginRight: 12,
|
|
},
|
|
dimensionScore: {
|
|
fontSize: 14,
|
|
color: colors.textSecondary,
|
|
width: 42,
|
|
textAlign: 'right',
|
|
},
|
|
/* ---- TOP 候选人 ---- */
|
|
candidateItem: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingVertical: 10,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: colors.border,
|
|
},
|
|
candidateRank: {
|
|
width: 28,
|
|
height: 28,
|
|
borderRadius: 14,
|
|
backgroundColor: colors.pageBg,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginRight: 12,
|
|
},
|
|
candidateRankText: {
|
|
fontSize: 12,
|
|
fontWeight: '600',
|
|
color: colors.textSecondary,
|
|
},
|
|
candidateInfo: {
|
|
flex: 1,
|
|
},
|
|
candidateHeader: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
candidateName: {
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
color: colors.textPrimary,
|
|
},
|
|
candidateScore: {
|
|
fontSize: 14,
|
|
fontWeight: '700',
|
|
color: colors.primary,
|
|
},
|
|
candidateTagLine: {
|
|
marginTop: 4,
|
|
fontSize: 12,
|
|
color: colors.textSecondary,
|
|
},
|
|
/* ---- 空状态 ---- */
|
|
emptyContent: {
|
|
paddingHorizontal: 16,
|
|
paddingTop: 24,
|
|
paddingBottom: 40,
|
|
},
|
|
emptyCard: {
|
|
backgroundColor: colors.white,
|
|
borderRadius: 16,
|
|
padding: 28,
|
|
alignItems: 'center',
|
|
},
|
|
emptyIconWrap: {
|
|
width: 88,
|
|
height: 88,
|
|
borderRadius: 44,
|
|
backgroundColor: colors.primaryLight,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginBottom: 16,
|
|
},
|
|
emptyTitle: {
|
|
fontSize: 18,
|
|
fontWeight: '700',
|
|
color: colors.textPrimary,
|
|
marginBottom: 8,
|
|
},
|
|
emptySubtitle: {
|
|
fontSize: 13,
|
|
color: colors.textSecondary,
|
|
textAlign: 'center',
|
|
lineHeight: 20,
|
|
marginBottom: 20,
|
|
},
|
|
emptyBtn: {
|
|
backgroundColor: colors.primaryBtn,
|
|
paddingHorizontal: 32,
|
|
paddingVertical: 12,
|
|
borderRadius: 24,
|
|
},
|
|
emptyBtnText: {
|
|
color: colors.white,
|
|
fontSize: 15,
|
|
fontWeight: '600',
|
|
},
|
|
progressCard: {
|
|
backgroundColor: colors.white,
|
|
borderRadius: 16,
|
|
padding: 20,
|
|
marginTop: 16,
|
|
},
|
|
progressTitle: {
|
|
fontSize: 16,
|
|
fontWeight: '700',
|
|
color: colors.textPrimary,
|
|
marginBottom: 16,
|
|
},
|
|
progressRow: {
|
|
flexDirection: 'row',
|
|
minHeight: 56,
|
|
},
|
|
progressLeft: {
|
|
width: 24,
|
|
alignItems: 'center',
|
|
},
|
|
progressDot: {
|
|
width: 24,
|
|
height: 24,
|
|
borderRadius: 12,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
borderWidth: 1,
|
|
},
|
|
progressDotDone: {
|
|
backgroundColor: colors.success,
|
|
borderColor: colors.success,
|
|
},
|
|
progressDotTodo: {
|
|
backgroundColor: colors.white,
|
|
borderColor: colors.borderStrong,
|
|
},
|
|
progressDotFirst: {
|
|
borderColor: colors.success,
|
|
},
|
|
progressDotText: {
|
|
fontSize: 12,
|
|
color: colors.textSecondary,
|
|
},
|
|
progressLine: {
|
|
width: 2,
|
|
flex: 1,
|
|
backgroundColor: colors.border,
|
|
marginVertical: 4,
|
|
},
|
|
progressLineDone: {
|
|
backgroundColor: colors.success,
|
|
},
|
|
progressBody: {
|
|
flex: 1,
|
|
marginLeft: 12,
|
|
paddingBottom: 16,
|
|
},
|
|
progressLabel: {
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
color: colors.textPrimary,
|
|
},
|
|
progressTip: {
|
|
fontSize: 12,
|
|
color: colors.textSecondary,
|
|
marginTop: 4,
|
|
lineHeight: 18,
|
|
},
|
|
/* ---- 提升建议 ---- */
|
|
tipItem: {
|
|
paddingVertical: 12,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: colors.border,
|
|
},
|
|
tipTitle: {
|
|
fontSize: 14,
|
|
fontWeight: '500',
|
|
color: colors.textPrimary,
|
|
},
|
|
tipDesc: {
|
|
fontSize: 12,
|
|
color: colors.textSecondary,
|
|
marginTop: 4,
|
|
},
|
|
tipAction: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
backgroundColor: colors.primaryLight,
|
|
borderRadius: 8,
|
|
paddingHorizontal: 14,
|
|
paddingVertical: 12,
|
|
marginTop: 8,
|
|
},
|
|
tipActionText: {
|
|
fontSize: 14,
|
|
color: colors.primary,
|
|
fontWeight: '500',
|
|
},
|
|
});
|