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.
752 lines
20 KiB
752 lines
20 KiB
import { Ionicons } from '@expo/vector-icons';
|
|
import { useRouter } from 'expo-router';
|
|
import { useState } from 'react';
|
|
import {
|
|
ActivityIndicator,
|
|
Pressable,
|
|
ScrollView,
|
|
StyleSheet,
|
|
Text,
|
|
View,
|
|
} from 'react-native';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import Svg, { Line, Path, Polygon, Text as SvgText } from 'react-native-svg';
|
|
import { useJobEvaluationEmptyState, useJobEvaluations } from '../src/hooks';
|
|
import { colors } from '../src/theme';
|
|
import type {
|
|
JobEvaluationDimension,
|
|
JobEvaluationEmptyState,
|
|
JobEvaluationEmptyTab,
|
|
JobEvaluationSummary,
|
|
JobReview,
|
|
} from '../src/types';
|
|
|
|
/* ===================== 子组件:顶部导航 ===================== */
|
|
|
|
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>
|
|
<Pressable style={styles.headerBtn} hitSlop={8}>
|
|
<Ionicons name="filter-outline" size={22} color={colors.textPrimary} />
|
|
</Pressable>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:综合评分卡 ===================== */
|
|
|
|
function OverallScoreCard({
|
|
score,
|
|
change,
|
|
reviewCount,
|
|
positiveRate,
|
|
replyRate,
|
|
complaintRate,
|
|
}: {
|
|
score: number;
|
|
change: number;
|
|
reviewCount: number;
|
|
positiveRate: number;
|
|
replyRate: number;
|
|
complaintRate: number;
|
|
}) {
|
|
const up = change >= 0;
|
|
return (
|
|
<View style={styles.scoreCard}>
|
|
<View style={styles.scoreMain}>
|
|
<View>
|
|
<Text style={styles.scoreValue}>{score.toFixed(1)}</Text>
|
|
<Text style={styles.scoreLabel}>企业综合评分</Text>
|
|
</View>
|
|
<View style={styles.scoreMeta}>
|
|
<Text style={[styles.scoreChange, { color: up ? colors.success : colors.danger }]}>
|
|
较上期 {up ? '+' : ''}{change.toFixed(1)}
|
|
</Text>
|
|
<Text style={styles.scoreCount}>{reviewCount} 条评价</Text>
|
|
</View>
|
|
</View>
|
|
<View style={styles.rateRow}>
|
|
<View style={styles.rateItem}>
|
|
<Text style={styles.rateValue}>{positiveRate}%</Text>
|
|
<Text style={styles.rateLabel}>好评率</Text>
|
|
</View>
|
|
<View style={styles.rateItem}>
|
|
<Text style={styles.rateValue}>{replyRate}%</Text>
|
|
<Text style={styles.rateLabel}>回复率</Text>
|
|
</View>
|
|
<View style={styles.rateItem}>
|
|
<Text style={styles.rateValue}>{complaintRate}%</Text>
|
|
<Text style={styles.rateLabel}>投诉率</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:趋势折线图 ===================== */
|
|
|
|
function TrendChart({ trend }: { trend: { month: string; score: number }[] }) {
|
|
const width = 320;
|
|
const height = 120;
|
|
const padding = { top: 16, right: 24, bottom: 24, left: 32 };
|
|
const chartW = width - padding.left - padding.right;
|
|
const chartH = height - padding.top - padding.bottom;
|
|
const minScore = 4.0;
|
|
const maxScore = 5.0;
|
|
|
|
const points = trend.map((t, i) => {
|
|
const x = padding.left + (i / (trend.length - 1)) * chartW;
|
|
const y = padding.top + chartH - ((t.score - minScore) / (maxScore - minScore)) * chartH;
|
|
return { x, y, score: t.score, month: t.month };
|
|
});
|
|
|
|
const pathD = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ');
|
|
|
|
return (
|
|
<View style={styles.chartCard}>
|
|
<Text style={styles.cardTitle}>评分趋势</Text>
|
|
<Svg width={width} height={height}>
|
|
{/* 网格线 */}
|
|
{[4.2, 4.4, 4.6, 4.8].map((s) => {
|
|
const y = padding.top + chartH - ((s - minScore) / (maxScore - minScore)) * chartH;
|
|
return <Line key={s} x1={padding.left} y1={y} x2={width - padding.right} y2={y} stroke={colors.borderLight} strokeWidth={1} />;
|
|
})}
|
|
{/* 折线 */}
|
|
<Path d={pathD} stroke={colors.primary} strokeWidth={2} fill="none" />
|
|
{/* 数据点 */}
|
|
{points.map((p, i) => (
|
|
<Polygon
|
|
key={i}
|
|
points={`${p.x - 4},${p.y - 4} ${p.x + 4},${p.y - 4} ${p.x},${p.y + 4}`}
|
|
fill={colors.primary}
|
|
/>
|
|
))}
|
|
{/* X 轴标签 */}
|
|
{points.map((p, i) => (
|
|
<SvgText key={i} x={p.x} y={height - 6} fill={colors.textSecondary} fontSize={10} textAnchor="middle">
|
|
{p.month}
|
|
</SvgText>
|
|
))}
|
|
</Svg>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:雷达图 ===================== */
|
|
|
|
function RadarChart({ dimensions }: { dimensions: JobEvaluationDimension[] }) {
|
|
const size = 200;
|
|
const center = size / 2;
|
|
const radius = 70;
|
|
const angleStep = (Math.PI * 2) / dimensions.length;
|
|
|
|
const points = dimensions.map((d, i) => {
|
|
const angle = i * angleStep - Math.PI / 2;
|
|
const r = (d.score / d.maxScore) * radius;
|
|
const x = center + r * Math.cos(angle);
|
|
const y = center + r * Math.sin(angle);
|
|
return { x, y, label: d.name };
|
|
});
|
|
|
|
const axisPoints = dimensions.map((_, i) => {
|
|
const angle = i * angleStep - Math.PI / 2;
|
|
return {
|
|
x: center + radius * Math.cos(angle),
|
|
y: center + radius * Math.sin(angle),
|
|
label: dimensions[i].name,
|
|
};
|
|
});
|
|
|
|
const polygonPoints = points.map((p) => `${p.x},${p.y}`).join(' ');
|
|
|
|
return (
|
|
<View style={styles.radarCard}>
|
|
<Text style={styles.cardTitle}>六维评分</Text>
|
|
<Svg width={size} height={size}>
|
|
{/* 网格圆 */}
|
|
{[0.2, 0.4, 0.6, 0.8, 1].map((scale) => (
|
|
<Polygon
|
|
key={scale}
|
|
points={axisPoints.map((a) => {
|
|
const px = center + (a.x - center) * scale;
|
|
const py = center + (a.y - center) * scale;
|
|
return `${px},${py}`;
|
|
}).join(' ')}
|
|
fill="none"
|
|
stroke={colors.borderLight}
|
|
strokeWidth={1}
|
|
/>
|
|
))}
|
|
{/* 轴线 */}
|
|
{axisPoints.map((a, i) => (
|
|
<Line key={i} x1={center} y1={center} x2={a.x} y2={a.y} stroke={colors.borderLight} strokeWidth={1} />
|
|
))}
|
|
{/* 数据区域 */}
|
|
<Polygon points={polygonPoints} fill={colors.primary + '30'} stroke={colors.primary} strokeWidth={2} />
|
|
{/* 标签 */}
|
|
{axisPoints.map((a, i) => {
|
|
const labelX = center + (a.x - center) * 1.22;
|
|
const labelY = center + (a.y - center) * 1.22;
|
|
return (
|
|
<SvgText key={i} x={labelX} y={labelY} fill={colors.textSecondary} fontSize={10} textAnchor="middle" alignmentBaseline="middle">
|
|
{a.label}
|
|
</SvgText>
|
|
);
|
|
})}
|
|
</Svg>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:岗位评分列表 ===================== */
|
|
|
|
function JobScoreList({ jobs }: { jobs: JobEvaluationSummary[] }) {
|
|
return (
|
|
<View style={styles.card}>
|
|
<Text style={styles.cardTitle}>各岗位评分</Text>
|
|
{jobs.map((job) => {
|
|
const up = job.change >= 0;
|
|
return (
|
|
<View key={job.id} style={styles.jobRow}>
|
|
<View style={styles.jobInfo}>
|
|
<Text style={styles.jobName}>{job.positionName}</Text>
|
|
<Text style={styles.jobDepartment}>{job.department} · {job.reviewCount} 条评价</Text>
|
|
</View>
|
|
<View style={styles.jobScoreBox}>
|
|
<Text style={styles.jobScore}>{job.score.toFixed(1)}</Text>
|
|
<Text style={[styles.jobChange, { color: up ? colors.success : colors.danger }]}>
|
|
{up ? '↑' : '↓'} {Math.abs(job.change).toFixed(1)}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:空状态 Tab 栏 ===================== */
|
|
|
|
function EmptyTabBar({
|
|
tabs,
|
|
active,
|
|
onChange,
|
|
}: {
|
|
tabs: JobEvaluationEmptyTab[];
|
|
active: string;
|
|
onChange: (key: string) => void;
|
|
}) {
|
|
return (
|
|
<ScrollView
|
|
horizontal
|
|
showsHorizontalScrollIndicator={false}
|
|
contentContainerStyle={styles.emptyTabContent}
|
|
>
|
|
{tabs.map((tab) => {
|
|
const isActive = tab.key === active;
|
|
return (
|
|
<Pressable
|
|
key={tab.key}
|
|
style={[styles.emptyTabItem, isActive && styles.emptyTabItemActive]}
|
|
onPress={() => onChange(tab.key)}
|
|
>
|
|
<Text style={[styles.emptyTabLabel, isActive && styles.emptyTabLabelActive]}>
|
|
{tab.label}
|
|
</Text>
|
|
<Text style={[styles.emptyTabCount, isActive && styles.emptyTabCountActive]}>
|
|
({tab.count})
|
|
</Text>
|
|
</Pressable>
|
|
);
|
|
})}
|
|
</ScrollView>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:空状态 ===================== */
|
|
|
|
function EmptyState({ state }: { state: JobEvaluationEmptyState }) {
|
|
const [activeTab, setActiveTab] = useState('all');
|
|
return (
|
|
<View style={styles.emptyWrapper}>
|
|
<EmptyTabBar tabs={state.tabs} active={activeTab} onChange={setActiveTab} />
|
|
<View style={styles.emptyCard}>
|
|
<View style={styles.emptyIconWrap}>
|
|
<Ionicons name="star-outline" size={48} color={colors.primary} />
|
|
</View>
|
|
<Text style={styles.emptyMainTitle}>暂无评分数据</Text>
|
|
<Text style={styles.emptyTitle}>{state.emptyTitle}</Text>
|
|
<Text style={styles.emptySubtitle}>{state.emptySubtitle}</Text>
|
|
|
|
<View style={styles.tipCard}>
|
|
<View style={styles.tipHeader}>
|
|
<Ionicons name="bulb-outline" size={18} color={colors.compareOrange} />
|
|
<Text style={styles.tipHeaderTitle}>如何获得更多好评?</Text>
|
|
</View>
|
|
{state.tips.map((tip, index) => (
|
|
<View key={index} style={styles.tipRow}>
|
|
<View style={styles.tipDot} />
|
|
<Text style={styles.tipText}>{tip}</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:评价列表 ===================== */
|
|
|
|
function ReviewList({ reviews }: { reviews: JobReview[] }) {
|
|
return (
|
|
<View style={styles.card}>
|
|
<Text style={styles.cardTitle}>最新评价</Text>
|
|
{reviews.map((review) => (
|
|
<View key={review.id} style={styles.reviewItem}>
|
|
<View style={styles.reviewHeader}>
|
|
<View style={styles.reviewUser}>
|
|
<Text style={styles.reviewName}>{review.userName}</Text>
|
|
<Text style={styles.reviewRole}>{review.userRole}</Text>
|
|
</View>
|
|
<View style={styles.reviewRating}>
|
|
<Text style={styles.reviewScore}>{review.rating}.0</Text>
|
|
<Ionicons name="star" size={12} color={colors.compareOrange} />
|
|
</View>
|
|
</View>
|
|
<Text style={styles.reviewContent}>{review.content}</Text>
|
|
<View style={styles.tagRow}>
|
|
{review.tags.map((tag) => (
|
|
<View key={tag} style={styles.reviewTag}>
|
|
<Text style={styles.reviewTagText}>{tag}</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
<View style={styles.reviewFooter}>
|
|
<Text style={styles.reviewDate}>{review.date}</Text>
|
|
<Pressable style={styles.replyBtn}>
|
|
<Text style={styles.replyText}>{review.replied ? '已回复' : '回复'}</Text>
|
|
</Pressable>
|
|
</View>
|
|
</View>
|
|
))}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 页面主组件 ===================== */
|
|
|
|
export default function JobEvaluationsScreen() {
|
|
const router = useRouter();
|
|
const { management, loading: dataLoading, error: dataError } = useJobEvaluations();
|
|
const { state: emptyState, loading: emptyLoading, error: emptyError } = useJobEvaluationEmptyState();
|
|
|
|
const loading = dataLoading || emptyLoading;
|
|
const error = dataError || emptyError;
|
|
|
|
if (loading) {
|
|
return (
|
|
<SafeAreaView style={styles.loadingContainer} edges={['top']}>
|
|
<ActivityIndicator size="large" color={colors.primary} />
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<SafeAreaView style={styles.loadingContainer} edges={['top']}>
|
|
<Text style={styles.errorText}>加载失败,请稍后重试</Text>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const showEmpty = emptyState?.hasData === false;
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<SafeAreaView edges={['top']}>
|
|
<Header onBack={() => router.back()} />
|
|
</SafeAreaView>
|
|
<ScrollView style={styles.scroll} contentContainerStyle={styles.scrollContent}>
|
|
{showEmpty && emptyState ? (
|
|
<EmptyState state={emptyState} />
|
|
) : management ? (
|
|
<>
|
|
<OverallScoreCard
|
|
score={management.overallScore}
|
|
change={management.change}
|
|
reviewCount={management.reviewCount}
|
|
positiveRate={management.positiveRate}
|
|
replyRate={management.replyRate}
|
|
complaintRate={management.complaintRate}
|
|
/>
|
|
<TrendChart trend={management.trend} />
|
|
<RadarChart dimensions={management.dimensions} />
|
|
<JobScoreList jobs={management.jobScores} />
|
|
<ReviewList reviews={management.reviews} />
|
|
</>
|
|
) : null}
|
|
</ScrollView>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 样式 ===================== */
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: colors.pageBg,
|
|
},
|
|
loadingContainer: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
backgroundColor: colors.pageBg,
|
|
},
|
|
errorText: {
|
|
color: colors.textSecondary,
|
|
fontSize: 14,
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 12,
|
|
backgroundColor: colors.white,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: colors.borderLight,
|
|
},
|
|
headerBtn: {
|
|
width: 36,
|
|
height: 36,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
headerTitle: {
|
|
fontSize: 17,
|
|
fontWeight: '600',
|
|
color: colors.textPrimary,
|
|
},
|
|
scroll: {
|
|
flex: 1,
|
|
},
|
|
scrollContent: {
|
|
padding: 16,
|
|
paddingBottom: 32,
|
|
},
|
|
scoreCard: {
|
|
backgroundColor: colors.primary,
|
|
borderRadius: 16,
|
|
padding: 20,
|
|
marginBottom: 16,
|
|
},
|
|
scoreMain: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
marginBottom: 20,
|
|
},
|
|
scoreValue: {
|
|
fontSize: 42,
|
|
fontWeight: '700',
|
|
color: colors.white,
|
|
},
|
|
scoreLabel: {
|
|
fontSize: 14,
|
|
color: colors.white,
|
|
opacity: 0.9,
|
|
},
|
|
scoreMeta: {
|
|
alignItems: 'flex-end',
|
|
},
|
|
scoreChange: {
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
marginBottom: 4,
|
|
},
|
|
scoreCount: {
|
|
fontSize: 13,
|
|
color: colors.white,
|
|
opacity: 0.9,
|
|
},
|
|
rateRow: {
|
|
flexDirection: 'row',
|
|
borderTopWidth: 1,
|
|
borderTopColor: 'rgba(255,255,255,0.2)',
|
|
paddingTop: 16,
|
|
},
|
|
rateItem: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
},
|
|
rateValue: {
|
|
fontSize: 18,
|
|
fontWeight: '700',
|
|
color: colors.white,
|
|
marginBottom: 2,
|
|
},
|
|
rateLabel: {
|
|
fontSize: 12,
|
|
color: colors.white,
|
|
opacity: 0.9,
|
|
},
|
|
card: {
|
|
backgroundColor: colors.white,
|
|
borderRadius: 16,
|
|
padding: 16,
|
|
marginBottom: 16,
|
|
shadowColor: '#000',
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.04,
|
|
shadowRadius: 6,
|
|
elevation: 2,
|
|
},
|
|
cardTitle: {
|
|
fontSize: 16,
|
|
fontWeight: '700',
|
|
color: colors.textPrimary,
|
|
marginBottom: 12,
|
|
},
|
|
chartCard: {
|
|
backgroundColor: colors.white,
|
|
borderRadius: 16,
|
|
padding: 16,
|
|
marginBottom: 16,
|
|
alignItems: 'center',
|
|
},
|
|
radarCard: {
|
|
backgroundColor: colors.white,
|
|
borderRadius: 16,
|
|
padding: 16,
|
|
marginBottom: 16,
|
|
alignItems: 'center',
|
|
},
|
|
jobRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingVertical: 12,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: colors.borderLight,
|
|
},
|
|
jobInfo: {
|
|
flex: 1,
|
|
},
|
|
jobName: {
|
|
fontSize: 15,
|
|
fontWeight: '600',
|
|
color: colors.textPrimary,
|
|
marginBottom: 2,
|
|
},
|
|
jobDepartment: {
|
|
fontSize: 12,
|
|
color: colors.textSecondary,
|
|
},
|
|
jobScoreBox: {
|
|
alignItems: 'flex-end',
|
|
},
|
|
jobScore: {
|
|
fontSize: 18,
|
|
fontWeight: '700',
|
|
color: colors.primary,
|
|
},
|
|
jobChange: {
|
|
fontSize: 11,
|
|
fontWeight: '500',
|
|
marginTop: 2,
|
|
},
|
|
reviewItem: {
|
|
paddingVertical: 14,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: colors.borderLight,
|
|
},
|
|
reviewHeader: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
marginBottom: 8,
|
|
},
|
|
reviewUser: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
reviewName: {
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
color: colors.textPrimary,
|
|
marginRight: 8,
|
|
},
|
|
reviewRole: {
|
|
fontSize: 12,
|
|
color: colors.textSecondary,
|
|
},
|
|
reviewRating: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
reviewScore: {
|
|
fontSize: 14,
|
|
fontWeight: '700',
|
|
color: colors.compareOrange,
|
|
marginRight: 4,
|
|
},
|
|
reviewContent: {
|
|
fontSize: 14,
|
|
color: colors.textPrimary,
|
|
lineHeight: 20,
|
|
marginBottom: 10,
|
|
},
|
|
tagRow: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
marginBottom: 10,
|
|
},
|
|
reviewTag: {
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 4,
|
|
borderRadius: 12,
|
|
backgroundColor: colors.primaryLight,
|
|
marginRight: 8,
|
|
marginBottom: 6,
|
|
},
|
|
reviewTagText: {
|
|
fontSize: 11,
|
|
color: colors.primaryText,
|
|
},
|
|
reviewFooter: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
reviewDate: {
|
|
fontSize: 12,
|
|
color: colors.textHint,
|
|
},
|
|
replyBtn: {
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 5,
|
|
borderRadius: 12,
|
|
backgroundColor: colors.primaryLight,
|
|
},
|
|
replyText: {
|
|
fontSize: 12,
|
|
color: colors.primary,
|
|
fontWeight: '500',
|
|
},
|
|
/* ---- 空状态 ---- */
|
|
emptyWrapper: {
|
|
marginBottom: 16,
|
|
},
|
|
emptyTabContent: {
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 12,
|
|
gap: 8,
|
|
},
|
|
emptyTabItem: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 6,
|
|
borderRadius: 16,
|
|
backgroundColor: colors.pageBg,
|
|
borderWidth: 1,
|
|
borderColor: colors.border,
|
|
},
|
|
emptyTabItemActive: {
|
|
backgroundColor: colors.primaryLight,
|
|
borderColor: colors.primary,
|
|
},
|
|
emptyTabLabel: {
|
|
fontSize: 13,
|
|
color: colors.textSecondary,
|
|
},
|
|
emptyTabLabelActive: {
|
|
color: colors.primary,
|
|
fontWeight: '600',
|
|
},
|
|
emptyTabCount: {
|
|
fontSize: 12,
|
|
color: colors.textHint,
|
|
marginLeft: 2,
|
|
},
|
|
emptyTabCountActive: {
|
|
color: colors.primary,
|
|
},
|
|
emptyCard: {
|
|
backgroundColor: colors.white,
|
|
borderRadius: 16,
|
|
padding: 28,
|
|
alignItems: 'center',
|
|
marginTop: 8,
|
|
},
|
|
emptyIconWrap: {
|
|
width: 88,
|
|
height: 88,
|
|
borderRadius: 44,
|
|
backgroundColor: colors.primaryLight,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginBottom: 16,
|
|
},
|
|
emptyMainTitle: {
|
|
fontSize: 18,
|
|
fontWeight: '700',
|
|
color: colors.textPrimary,
|
|
marginBottom: 8,
|
|
},
|
|
emptyTitle: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
color: colors.textPrimary,
|
|
marginBottom: 6,
|
|
},
|
|
emptySubtitle: {
|
|
fontSize: 13,
|
|
color: colors.textSecondary,
|
|
textAlign: 'center',
|
|
lineHeight: 20,
|
|
marginBottom: 20,
|
|
},
|
|
tipCard: {
|
|
width: '100%',
|
|
backgroundColor: colors.actionLight,
|
|
borderRadius: 12,
|
|
padding: 16,
|
|
alignItems: 'flex-start',
|
|
},
|
|
tipHeader: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginBottom: 12,
|
|
},
|
|
tipHeaderTitle: {
|
|
fontSize: 14,
|
|
fontWeight: '700',
|
|
color: colors.actionText,
|
|
marginLeft: 8,
|
|
},
|
|
tipRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'flex-start',
|
|
marginBottom: 10,
|
|
},
|
|
tipDot: {
|
|
width: 6,
|
|
height: 6,
|
|
borderRadius: 3,
|
|
backgroundColor: colors.action,
|
|
marginTop: 6,
|
|
marginRight: 10,
|
|
},
|
|
tipText: {
|
|
flex: 1,
|
|
fontSize: 13,
|
|
color: colors.textPrimary,
|
|
lineHeight: 18,
|
|
},
|
|
});
|
|
|