import React, { useState, useEffect, useCallback } from 'react'; import { View, Text, TouchableOpacity, Modal, FlatList, StyleSheet, ActivityIndicator, Platform, } from 'react-native'; import * as StorageService from '../services/StorageService'; import type { SessionInfo } from '../services/StorageService'; import { useTheme } from '../design/tokens'; import { Radius } from '../design/tokens'; interface Props { selectedSessionId: string; projectId: string; onSelect: (sessionId: string) => void; } export function SessionSelector({ selectedSessionId, projectId, onSelect }: Props) { const theme = useTheme(); const [visible, setVisible] = useState(false); const [sessions, setSessions] = useState([]); const [loading, setLoading] = useState(false); const current = sessions.find((s) => s.sessionId === selectedSessionId); const reload = useCallback(async () => { setLoading(true); const data = await StorageService.listSessionsByProject(projectId); setSessions(data); setLoading(false); }, [projectId]); useEffect(() => { void reload(); }, [reload]); const handleOpen = () => { void reload(); setVisible(true); }; const handleSelect = (sid: string) => { setVisible(false); if (sid !== selectedSessionId) onSelect(sid); }; return ( <> 测线 {selectedSessionId || '--'} {current && ( {current.frameCount} 点 )} ▾ setVisible(false)}> setVisible(false)} /> 选择测线 {loading ? ( ) : sessions.length === 0 ? ( 当前工程无测线 ) : ( s.sessionId} style={{ maxHeight: 400 }} renderItem={({ item }) => { const active = item.sessionId === selectedSessionId; const date = new Date(item.createdAt); const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`; return ( handleSelect(item.sessionId)} activeOpacity={0.7} > {active && } {item.sessionId} {dateStr} · {item.frameCount} 测点 {active && 当前} ); }} /> )} ); } const MONO = { fontFamily: Platform.OS === 'ios' ? 'Courier' : 'monospace' } as const; const S = StyleSheet.create({ trigger: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 12, paddingVertical: 8, borderRadius: Radius.md, borderWidth: 1, gap: 8, }, triggerLeft: { flex: 1, gap: 2 }, label: { fontSize: 9, fontWeight: '600' }, sessionId: { fontSize: 11, fontWeight: '600', ...MONO }, count: { fontSize: 10, ...MONO }, arrow: { fontSize: 12 }, backdrop: { ...StyleSheet.absoluteFill, backgroundColor: 'rgba(0,0,0,0.5)' }, sheet: { position: 'absolute', bottom: 0, left: 0, right: 0, maxHeight: '70%', borderTopLeftRadius: 20, borderTopRightRadius: 20, paddingBottom: 32, }, sheetHandle: { width: 36, height: 4, borderRadius: 2, alignSelf: 'center', marginTop: 10, marginBottom: 10 }, sheetTitle: { fontSize: 14, fontWeight: '700', paddingHorizontal: 16, paddingBottom: 10 }, empty: { fontSize: 12, textAlign: 'center', paddingVertical: 24 }, item: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: StyleSheet.hairlineWidth, gap: 8, }, dot: { width: 6, height: 6, borderRadius: 3 }, itemLeft: { flex: 1, gap: 2 }, itemId: { fontSize: 12, fontWeight: '600', ...MONO }, itemSub: { fontSize: 10 }, activeLabel:{ fontSize: 10, fontWeight: '700' }, });