This commit is contained in:
2026-06-06 22:13:00 +08:00
parent ed8682a1e2
commit 091b358705
27 changed files with 3001 additions and 102 deletions
+235
View File
@@ -0,0 +1,235 @@
import React from 'react';
import { View, Text, TextInput, TouchableOpacity, ScrollView, StyleSheet, Switch } from 'react-native';
import { useDeviceStore } from '../stores/deviceStore';
import {
SEND_FREQ_TABLE,
SAMPLE_FREQ_TABLE,
AMP_RATIO_TABLE,
SOURCE_MODE_TABLE,
} from '../protocol/constants';
interface PickerRowProps {
label: string;
options: readonly { code: number; label: string }[];
value: number;
onChange: (code: number) => void;
}
function PickerRow({ label, options, value, onChange }: PickerRowProps) {
return (
<View style={styles.row}>
<Text style={styles.rowLabel}>{label}</Text>
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.optScroll}>
{options.map((o) => (
<TouchableOpacity
key={o.code}
style={[styles.optChip, o.code === value && styles.optChipActive]}
onPress={() => onChange(o.code)}
>
<Text style={[styles.optText, o.code === value && styles.optTextActive]}>
{o.label}
</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
);
}
interface NumberRowProps {
label: string;
value: number;
onChangeText: (v: string) => void;
suffix?: string;
keyboardType?: 'numeric';
}
function NumberRow({ label, value, onChangeText, suffix, keyboardType = 'numeric' }: NumberRowProps) {
return (
<View style={styles.row}>
<Text style={styles.rowLabel}>{label}</Text>
<View style={styles.inputWrap}>
<TextInput
style={styles.input}
value={String(value)}
onChangeText={onChangeText}
keyboardType={keyboardType}
selectTextOnFocus
placeholderTextColor="#555"
/>
{suffix && <Text style={styles.suffix}>{suffix}</Text>}
</View>
</View>
);
}
interface FormProps {
onAnyChange?: () => void;
}
export function ParamForm({ onAnyChange }: FormProps = {}) {
const { config, updateConfig } = useDeviceStore();
const patch = (partial: Partial<typeof config>) => {
updateConfig(partial);
onAnyChange?.();
};
return (
<View style={styles.container}>
{/* Channel selector */}
<View style={styles.row}>
<Text style={styles.rowLabel}>通道数</Text>
<View style={styles.channelRow}>
{[1, 2, 3, 4, 5, 6].map((n) => (
<TouchableOpacity
key={n}
style={[styles.chChip, config.channelNum === n && styles.chChipActive]}
onPress={() => patch({ channelNum: n })}
>
<Text style={[styles.chText, config.channelNum === n && styles.chTextActive]}>
{n}
</Text>
</TouchableOpacity>
))}
</View>
</View>
<PickerRow
label="发射频率"
options={SEND_FREQ_TABLE}
value={config.sendFreq}
onChange={(v) => patch({ sendFreq: v })}
/>
<PickerRow
label="采样频率"
options={SAMPLE_FREQ_TABLE}
value={config.sampleFreq}
onChange={(v) => patch({ sampleFreq: v })}
/>
<NumberRow
label="采样点数"
value={config.sampleDepth}
onChangeText={(v) => patch({ sampleDepth: parseInt(v, 10) || 0 })}
/>
<NumberRow
label="叠加次数"
value={config.accNum}
onChangeText={(v) => patch({ accNum: parseInt(v, 10) || 0 })}
/>
<PickerRow
label="增益"
options={AMP_RATIO_TABLE}
value={config.ampRatio}
onChange={(v) => patch({ ampRatio: v })}
/>
<PickerRow
label="源模式"
options={SOURCE_MODE_TABLE}
value={config.sourceMode}
onChange={(v) => patch({ sourceMode: v })}
/>
{/* Reverse accumulation flags */}
<View style={styles.row}>
<Text style={styles.rowLabel}>反向叠加</Text>
<View style={styles.switchRow}>
<Text style={styles.switchLabel}>CH1-3</Text>
<Switch
value={config.negAcc123}
onValueChange={(v) => patch({ negAcc123: v })}
thumbColor={config.negAcc123 ? '#4a9eff' : '#888'}
trackColor={{ false: '#333', true: '#1a4a88' }}
/>
<Text style={styles.switchLabel}>CH4-6</Text>
<Switch
value={config.negAcc456}
onValueChange={(v) => patch({ negAcc456: v })}
thumbColor={config.negAcc456 ? '#4a9eff' : '#888'}
trackColor={{ false: '#333', true: '#1a4a88' }}
/>
</View>
</View>
<NumberRow
label="补偿延时"
value={config.compDisableDelay}
onChangeText={(v) => patch({ compDisableDelay: parseInt(v, 10) || 0 })}
suffix="×50μs"
/>
{/* File prefix */}
<View style={styles.row}>
<Text style={styles.rowLabel}>文件前缀</Text>
<View style={styles.inputWrap}>
<TextInput
style={styles.input}
value={config.filePrefix}
onChangeText={(v) => patch({ filePrefix: v.slice(0, 15) })}
maxLength={15}
autoCapitalize="none"
placeholderTextColor="#555"
/>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { backgroundColor: '#111' },
row: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 14,
paddingVertical: 8,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#2a2a2a',
gap: 10,
},
rowLabel: { color: '#aaa', fontSize: 13, width: 68, flexShrink: 0 },
optScroll: { flex: 1 },
optChip: {
borderWidth: 1,
borderColor: '#444',
borderRadius: 8,
paddingHorizontal: 8,
paddingVertical: 3,
marginRight: 6,
},
optChipActive: { borderColor: '#4a9eff', backgroundColor: '#0d2b55' },
optText: { color: '#888', fontSize: 11 },
optTextActive: { color: '#4a9eff', fontWeight: '600' },
inputWrap: { flexDirection: 'row', alignItems: 'center', flex: 1 },
input: {
flex: 1,
backgroundColor: '#1e1e1e',
color: '#eee',
fontSize: 13,
borderRadius: 6,
paddingHorizontal: 8,
paddingVertical: 4,
borderWidth: 1,
borderColor: '#333',
},
suffix: { color: '#666', fontSize: 11, marginLeft: 6 },
channelRow: { flexDirection: 'row', gap: 6, flex: 1 },
chChip: {
width: 28,
height: 28,
borderRadius: 14,
borderWidth: 1,
borderColor: '#444',
alignItems: 'center',
justifyContent: 'center',
},
chChipActive: { borderColor: '#4a9eff', backgroundColor: '#0d2b55' },
chText: { color: '#888', fontSize: 12, fontWeight: '600' },
chTextActive: { color: '#4a9eff' },
switchRow: { flexDirection: 'row', alignItems: 'center', gap: 6, flex: 1 },
switchLabel: { color: '#888', fontSize: 12 },
});