import { useEffect, useRef } from 'react'; import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native'; import { ScrollView, StyleSheet, Text, View } from 'react-native'; import { colors, spacing } from '../../../src/theme'; import { CURRENT_YEAR } from '../../../src/constants'; const ITEM_H = 44; function Column({ items, value, onValueChange, width, restingIndex, }: { items: string[]; value: string; onValueChange: (v: string) => void; width: number; /** 未选中时滚轮的停留位置(只影响视觉停留,不等于已选中) */ restingIndex: number; }) { const scrollRef = useRef(null); // 未选中('')与用户手滑到第 0 项是两回事:这里不再把空值钳到索引 0, // 而是保留「无选中项」状态,只在视觉上停在 restingIndex。 const idx = items.indexOf(value); const initialIndex = idx >= 0 ? idx : restingIndex; // contentOffset 只在 iOS 生效,Android 上不定位会让滚轮停在第一项、 // 与高亮框里的文字对不上,故挂载时用 scrollTo 补一次(两端通用)。 useEffect(() => { scrollRef.current?.scrollTo({ y: initialIndex * ITEM_H, animated: false }); // 仅挂载时定位:后续滚动由用户驱动,避免每次选值把滚轮拽回去 // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const handleEnd = (e: NativeSyntheticEvent) => { const y = e.nativeEvent.contentOffset.y; const i = Math.min(items.length - 1, Math.max(0, Math.round(y / ITEM_H))); const v = items[i]; if (v !== value) onValueChange(v); }; return ( {items.map((it) => ( {it} ))} ); } interface DualProps { year: string; month: string; onYearChange: (v: string) => void; onMonthChange: (v: string) => void; /** * 未选择年份时滚轮的停留项。默认停在当年(适合「入职时间」这类近期场景); * 「出生年月」应传一个合理的起始年份,否则用户要空滑二十多行。 */ restingYear?: string; /** * 可选年份跨度:往过去几年、往未来几年。 * 原实现固定 40 项(当年往前 39 年),只能覆盖 0–39 岁 —— * 46 岁的求职者选不了自己的出生年;而「入职时间」也选不到明年。 */ pastYears?: number; futureYears?: number; } /** 年月双滚轮:用于出生年月 / 首次参加工作 / 入职时间 */ export function YearMonthWheel({ year, month, onYearChange, onMonthChange, restingYear = `${CURRENT_YEAR}年`, pastYears = 60, futureYears = 0, }: DualProps) { const years = Array.from( { length: pastYears + futureYears + 1 }, (_, i) => `${CURRENT_YEAR + futureYears - i}年`, ); const months = Array.from({ length: 12 }, (_, i) => `${i + 1}月`); const unselected = !year || !month; const restingYearIndex = Math.max(0, years.indexOf(restingYear)); return ( {/* 未选中时滚轮无高亮项,需要文字提示「要滑动才有值」,否则用户会以为已经选好了 */} {unselected ? 请滑动选择 : null} ); } const styles = StyleSheet.create({ col: { height: ITEM_H * 3, position: 'relative' }, scroll: { flex: 1 }, highlight: { position: 'absolute', top: ITEM_H, left: 0, right: 0, height: ITEM_H, borderTopWidth: 1, borderBottomWidth: 1, borderColor: colors.border, backgroundColor: colors.primaryLight, opacity: 0.45, }, // 未选中:高亮框退成中性描边,不再用主色底暗示「已经选好了」 highlightIdle: { backgroundColor: colors.white, opacity: 0.6 }, item: { height: ITEM_H, alignItems: 'center', justifyContent: 'center' }, itemText: { fontSize: 18, color: colors.textHint }, itemTextActive: { fontSize: 20, fontWeight: '700', color: colors.textPrimary }, dual: { flexDirection: 'row', justifyContent: 'center', gap: spacing.block }, hint: { fontSize: 13, color: colors.textHint, textAlign: 'center', marginTop: spacing.sm }, });