This commit is contained in:
tx 2024-12-30 18:02:51 +08:00
parent c2e52df560
commit a9055522cf
9 changed files with 746 additions and 7 deletions

3
package-lock.json generated
View File

@ -13,7 +13,7 @@
"@react-native-camera-roll/camera-roll": "^7.9.0", "@react-native-camera-roll/camera-roll": "^7.9.0",
"@react-native-community/geolocation": "^3.4.0", "@react-native-community/geolocation": "^3.4.0",
"@react-native-community/slider": "^4.5.5", "@react-native-community/slider": "^4.5.5",
"@react-native-picker/picker": "^2.9.0", "@react-native-picker/picker": "^2.10.2",
"@react-navigation/bottom-tabs": "^6.4.0", "@react-navigation/bottom-tabs": "^6.4.0",
"@react-navigation/native": "^6.1.18", "@react-navigation/native": "^6.1.18",
"@react-navigation/stack": "^6.3.8", "@react-navigation/stack": "^6.3.8",
@ -4829,7 +4829,6 @@
"version": "2.10.2", "version": "2.10.2",
"resolved": "https://registry.npmmirror.com/@react-native-picker/picker/-/picker-2.10.2.tgz", "resolved": "https://registry.npmmirror.com/@react-native-picker/picker/-/picker-2.10.2.tgz",
"integrity": "sha512-kr3OvCRwTYjR/OKlb52k4xmQVU7dPRIALqpyiihexdJxEgvc1smnepgqCeM9oXmNSG4YaV5/RSxFlLC5Z/T/Eg==", "integrity": "sha512-kr3OvCRwTYjR/OKlb52k4xmQVU7dPRIALqpyiihexdJxEgvc1smnepgqCeM9oXmNSG4YaV5/RSxFlLC5Z/T/Eg==",
"license": "MIT",
"workspaces": [ "workspaces": [
"example" "example"
], ],

View File

@ -15,7 +15,7 @@
"@react-native-camera-roll/camera-roll": "^7.9.0", "@react-native-camera-roll/camera-roll": "^7.9.0",
"@react-native-community/geolocation": "^3.4.0", "@react-native-community/geolocation": "^3.4.0",
"@react-native-community/slider": "^4.5.5", "@react-native-community/slider": "^4.5.5",
"@react-native-picker/picker": "^2.9.0", "@react-native-picker/picker": "^2.10.2",
"@react-navigation/bottom-tabs": "^6.4.0", "@react-navigation/bottom-tabs": "^6.4.0",
"@react-navigation/native": "^6.1.18", "@react-navigation/native": "^6.1.18",
"@react-navigation/stack": "^6.3.8", "@react-navigation/stack": "^6.3.8",

View File

@ -17,6 +17,7 @@ import { HomeStackParamList } from './types';
import TestBule from '../views/device/test_bule'; import TestBule from '../views/device/test_bule';
import ShareDetailScreen from '../views/device/KeyDetail'; import ShareDetailScreen from '../views/device/KeyDetail';
import ExpiredKeysScreen from '../views/device/ExpiredKeysScreen'; import ExpiredKeysScreen from '../views/device/ExpiredKeysScreen';
import deviceDetailSet from '../views/device/deviceDetailSet';
const Stack = createStackNavigator<HomeStackParamList>(); const Stack = createStackNavigator<HomeStackParamList>();
const createScreenOptions = (title: string): StackNavigationOptions => { const createScreenOptions = (title: string): StackNavigationOptions => {
@ -52,6 +53,13 @@ export default function HomeStackNavigator({ navigation, route }: Props) {
headerShown: false, headerShown: false,
}} }}
/> />
<Stack.Screen
name="deviceDetailSet"
component={deviceDetailSet}
options={{
headerShown: false,
}}
/>
<Stack.Screen <Stack.Screen
name="ShareDetailScreen" name="ShareDetailScreen"
component={ShareDetailScreen} component={ShareDetailScreen}

View File

@ -30,6 +30,7 @@ export type HomeStackParamList = {
TestBule: undefined; TestBule: undefined;
ShareDetailScreen: undefined; ShareDetailScreen: undefined;
ExpiredKeysScreen: undefined; ExpiredKeysScreen: undefined;
deviceDetailSet: undefined;
}; };
// 导航属性类型 // 导航属性类型

View File

@ -113,5 +113,9 @@ export const apiService = {
getExpiredKeys: () => api.get('/appVerify/getExpiredKeyListByOwnerId'), getExpiredKeys: () => api.get('/appVerify/getExpiredKeyListByOwnerId'),
getKeyInfo: (keyId: string) => api.get('/appVerify/key/'+keyId), getKeyInfo: (keyId: string) => api.get('/appVerify/key/'+keyId),
bindKey: (keyId: string) => api.post('/appVerify/claimKey?keyId='+keyId), bindKey: (keyId: string) => api.post('/appVerify/claimKey?keyId='+keyId),
updateDevice: ( data: any) => api.put('/appVerify/device/edit', data),
untieDevice: (deviceId: string) => api.post('/appVerify/untie/'+deviceId),
getQiniuToken: () => api.get('/common/qiniu/uploadInfo'),
getModelList: () => api.get('/appVerify/modelList'),
// updateKeyExpiration: (keyId: string, expirationTime: string) => api.put('/appVerify/updateKeyExpiration', { keyId, expirationTime }), // updateKeyExpiration: (keyId: string, expirationTime: string) => api.put('/appVerify/updateKeyExpiration', { keyId, expirationTime }),
}; };

183
src/utils/uploadImg.ts Normal file
View File

@ -0,0 +1,183 @@
import { Platform } from 'react-native';
import { request, PERMISSIONS, RESULTS } from 'react-native-permissions';
import { launchImageLibrary } from 'react-native-image-picker';
import { apiService } from './api';
import Toast from 'react-native-toast-message';
interface UploadResponse {
success: boolean;
imageUrl?: string;
error?: string;
}
interface QiniuUploadInfo {
token: string;
domain: string;
}
class UploadImageService {
private static instance: UploadImageService;
private uploadInfo: QiniuUploadInfo | null = null;
private constructor() {}
public static getInstance(): UploadImageService {
if (!UploadImageService.instance) {
UploadImageService.instance = new UploadImageService();
}
return UploadImageService.instance;
}
private showToast(message: string, type: 'success' | 'error' | 'info' = 'info') {
Toast.show({
type,
text1: message,
position: 'top',
topOffset: Platform.OS === 'ios' ? 60 : 20,
visibilityTime: 2000,
});
}
private async checkPermission(): Promise<boolean> {
try {
const permission = Platform.select({
android: PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,
ios: PERMISSIONS.IOS.PHOTO_LIBRARY,
default: PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,
});
const result = await request(permission);
if (result !== RESULTS.GRANTED) {
this.showToast('请在设置中授予相册访问权限', 'error');
return false;
}
return true;
} catch (error) {
console.error('Permission check error:', error);
return false;
}
}
private async getQiniuToken(): Promise<boolean> {
try {
const response = await apiService.getQiniuToken();
if (response.code == 200) {
this.uploadInfo = {
token: response.token,
domain: response.domain
};
return true;
}
this.showToast('获取上传凭证失败', 'error');
return false;
} catch (error) {
console.error('Get qiniu token error:', error);
this.showToast('获取上传凭证失败', 'error');
return false;
}
}
private generateKey(): string {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 15);
return `bike/img/static/${timestamp}_${random}`;
}
private async uploadToQiniu(uri: string, key: string): Promise<UploadResponse> {
if (!this.uploadInfo?.token) {
return { success: false, error: '上传凭证无效' };
}
const formData = new FormData();
formData.append('token', this.uploadInfo.token);
formData.append('key', key);
formData.append('file', {
uri: uri,
type: 'image/jpeg',
name: 'image.jpg',
});
try {
const response = await fetch('https://up-z2.qiniup.com', {
method: 'POST',
body: formData,
headers: {
'Content-Type': 'multipart/form-data',
},
});
const result = await response.json();
if (result.key) {
return {
success: true,
imageUrl: `${this.uploadInfo.domain}/${result.key}`
};
}
return { success: false, error: '上传失败' };
} catch (error) {
console.error('Upload error:', error);
return { success: false, error: '上传过程中出错' };
}
}
public async uploadImage(options = { multiple: false }): Promise<string[]> {
try {
// 检查权限
const hasPermission = await this.checkPermission();
if (!hasPermission) {
return [];
}
// 获取七牛云上传凭证
const hasToken = await this.getQiniuToken();
if (!hasToken) {
return [];
}
// 选择图片
const result = await launchImageLibrary({
mediaType: 'photo',
quality: 1,
selectionLimit: options.multiple ? 0 : 1,
});
if (result.didCancel || !result.assets) {
return [];
}
// 上传所有选中的图片
const uploadPromises = result.assets.map(async (asset) => {
if (!asset.uri) return null;
const key = this.generateKey();
const uploadResult = await this.uploadToQiniu(asset.uri, key);
if (uploadResult.success && uploadResult.imageUrl) {
return uploadResult.imageUrl;
}
return null;
});
const results = await Promise.all(uploadPromises);
const successUrls = results.filter((url): url is string => url !== null);
if (successUrls.length > 0) {
this.showToast('上传成功', 'success');
} else {
this.showToast('上传失败', 'error');
}
return successUrls;
} catch (error) {
console.error('Upload process error:', error);
this.showToast('上传过程中出错', 'error');
return [];
}
}
}
export const uploadImageService = UploadImageService.getInstance();

View File

@ -40,8 +40,11 @@ type DeviceType = {
remainingMileage: number; remainingMileage: number;
isDefault: number; isDefault: number;
lockStatus: number; lockStatus: number;
type: number;
expirationTime: string;
mac: string;
remark: string;
}; };
// 定义导航类型 // 定义导航类型
type NavigationProp = StackNavigationProp<RootStackParamList>; type NavigationProp = StackNavigationProp<RootStackParamList>;
@ -56,7 +59,7 @@ const NormaIndex: React.FC = () => {
navigation.navigate('DeviceList'); navigation.navigate('DeviceList');
}; };
const toSet = () => { const toSet = () => {
navigation.navigate('DeviceSet'); navigation.navigate('deviceDetailSet');
}; };
const toMap = () => { const toMap = () => {
@ -186,7 +189,7 @@ const NormaIndex: React.FC = () => {
numberOfLines={2} numberOfLines={2}
ellipsizeMode="tail" ellipsizeMode="tail"
> >
{defaultDevice?.model || '未选择车辆'} {defaultDevice?.remark || defaultDevice?.model}
</Text> </Text>
<Image <Image
source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uJRIitv0Yn5K7CEVe9qd' }} source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uJRIitv0Yn5K7CEVe9qd' }}

View File

@ -0,0 +1,541 @@
import React, { useState, useEffect } from 'react';
import { View, StyleSheet, ImageBackground, Text, TouchableOpacity, Modal, TextInput, ActivityIndicator, Button, Image, ScrollView } from 'react-native';
import { TopNavigation, TopNavigationAction, Icon, IconElement, Select, SelectItem, IndexPath } from '@ui-kitten/components';
import { useNavigation } from '@react-navigation/native';
import { rpx } from '../../utils/rpx';
import { apiService } from '../../utils/api';
import AsyncStorage from '@react-native-async-storage/async-storage';
import Toast from 'react-native-toast-message';
import { uploadImageService } from '../../utils/uploadImg';
const BackIcon = (props): IconElement => (
<Icon
{...props}
name='arrow-back'
fill='#000000'
/>
);
interface EditModalProps {
visible: boolean;
onClose: () => void;
title: string;
value: string;
onSubmit: (value: string) => void;
placeholder?: string;
}
const EditModal: React.FC<EditModalProps> = ({
visible,
onClose,
title,
value,
onSubmit,
placeholder
}) => {
const [inputValue, setInputValue] = useState(value);
useEffect(() => {
setInputValue(value);
}, [value]);
const handleSubmit = () => {
onSubmit(inputValue);
};
return (
<Modal
visible={visible}
transparent={true}
animationType="fade"
onRequestClose={onClose}
>
<View style={styles.modalOverlay}>
<View style={styles.modalContent}>
<Text style={styles.modalTitle}>{title}</Text>
<TextInput
style={styles.modalInput}
value={inputValue}
onChangeText={setInputValue}
placeholder={placeholder}
placeholderTextColor="#999999"
/>
<View style={styles.modalButtons}>
<TouchableOpacity style={styles.modalButton} onPress={onClose}>
<Text style={styles.modalButtonText}></Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.modalButton, styles.modalButtonPrimary]} onPress={handleSubmit}>
<Text style={[styles.modalButtonText, styles.modalButtonTextPrimary]}></Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
);
};
const deviceDetailSet = () => {
const navigation = useNavigation();
const [modalVisible, setModalVisible] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [deviceInfo, setDeviceInfo] = useState<any>(null);
const [currentEdit, setCurrentEdit] = useState<{
title: string;
value: string;
type: 'remark' | 'fullVoltage' | 'vehicleNum' | 'lowVoltage' | 'fullEndurance';
} | null>(null);
const [modelData, setModelData] = useState([]);
const [selectedBrandIndex, setSelectedBrandIndex] = useState<IndexPath | null>(null);
const [selectedModelIndex, setSelectedModelIndex] = useState<IndexPath | null>(null);
const [brandList, setBrandList] = useState([]);
const [modelList, setModelList] = useState([]);
const [modelModalVisible, setModelModalVisible] = useState(false);
const [previewImage, setPreviewImage] = useState<string | null>(null);
const upload = async () => {
try {
const result = await uploadImageService.uploadImage();
if (result) {
const response = await apiService.updateDevice({
sn: deviceInfo.sn,
pricture: result
});
if (response.code === 200) {
setDeviceInfo(prevInfo => ({
...prevInfo,
pricture: result
}));
Toast.show({
type: 'success',
text1: '图片上传成功',
});
fetchDeviceInfo();
} else {
Toast.show({
type: 'error',
text1: response.msg || '图片上传失败',
});
}
}
} catch (error) {
console.error('Upload failed:', error);
Toast.show({
type: 'error',
text1: '上传失败',
text2: '请检查网络连接',
});
}
};
const fetchDeviceInfo = async () => {
setIsLoading(true);
try {
const sn = await AsyncStorage.getItem('defaultDeviceSN');
if (!sn) {
Toast.show({
type: 'error',
text1: '未找到设备信息',
});
return;
}
const response = await apiService.getDeviceInfo(sn);
if (response.code === 200 && response.data) {
setDeviceInfo(response.data);
} else {
Toast.show({
type: 'error',
text1: response.msg || '获取设备信息失败',
});
}
} catch (error) {
console.error('获取设备信息错误:', error);
Toast.show({
type: 'error',
text1: '获取设备信息失败',
text2: '请检查网络连接',
});
} finally {
setIsLoading(false);
}
};
const fetchModelList = async () => {
const response = await apiService.getModelList();
if (response.code === 200) {
setModelData(response.data);
const brands = Array.from(new Set(response.data.map(item => item.brandName)));
setBrandList(brands);
setSelectedBrandIndex(new IndexPath(0));
filterModels(brands[0]);
}
};
const filterModels = (brand) => {
const models = modelData.filter(item => item.brandName === brand).map(item => item.model);
setModelList(models);
setSelectedModelIndex(new IndexPath(0));
const initialModel = models[0];
const initialImageUrl = getImageUrl(brand, initialModel);
setPreviewImage(initialImageUrl);
};
const getImageUrl = (brand, model) => {
const selectedModel = modelData.find(item => item.brandName === brand && item.model === model);
return selectedModel ? selectedModel.picture : null;
};
const handleBrandChange = (index) => {
setSelectedBrandIndex(index);
const brand = brandList[index.row];
filterModels(brand);
};
const handleModelChange = (index) => {
setSelectedModelIndex(index);
const model = modelList[index.row];
const brand = brandList[selectedBrandIndex.row];
const imageUrl = getImageUrl(brand, model);
setPreviewImage(imageUrl);
};
const handleModelSelection = async () => {
if (!selectedBrandIndex || !selectedModelIndex) return;
const brand = brandList[selectedBrandIndex.row];
const model = modelList[selectedModelIndex.row];
const imageUrl = getImageUrl(brand, model);
try {
const response = await apiService.updateDevice({
sn: deviceInfo.sn,
pricture: imageUrl
});
if (response.code === 200) {
setDeviceInfo(prevInfo => ({
...prevInfo,
pricture: imageUrl
}));
Toast.show({
type: 'success',
text1: '车辆信息更新成功',
});
fetchDeviceInfo();
} else {
Toast.show({
type: 'error',
text1: response.msg || '车辆信息更新失败',
});
}
} catch (error) {
console.error('Update failed:', error);
Toast.show({
type: 'error',
text1: '更新失败',
text2: '请检查网络连接',
});
}
setModelModalVisible(false);
};
useEffect(() => {
fetchDeviceInfo();
fetchModelList();
}, []);
const navigateBack = () => {
navigation.goBack();
};
const BackAction = () => (
<TopNavigationAction
icon={BackIcon}
onPress={navigateBack}
/>
);
return (
<View style={styles.container}>
<TopNavigation
title='设备设置'
alignment='center'
accessoryLeft={BackAction}
style={styles.header}
/>
<ScrollView
contentContainerStyle={styles.scrollContent}
scrollEventThrottle={16}
nestedScrollEnabled={true}
>
<ImageBackground
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uYRs7Cv2Pbp95w3KjGO3' }}
style={styles.imageBackground}
>
<ImageBackground
source={{ uri: 'https://lxnapi.ccttiot.com/FqAu65KgcylSRv1pD4008a_Ctpz-' }}
style={styles.carInfo}
>
<Image source={{ uri: deviceInfo?.pricture || 'https://lxnapi.ccttiot.com/bike/img/static/uVnIDwcwQP7oo12PeYVJ' }} style={{ width: rpx(440), height: rpx(340) }} />
<View style={styles.carInfoButtons}>
<TouchableOpacity style={styles.carInfoButton} onPress={upload} >
<Text style={styles.carInfoButtonText}></Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.carInfoButton, styles.carInfoButtonPrimary]} onPress={() => setModelModalVisible(true)}>
<Text style={[styles.carInfoButtonText, styles.carInfoButtonTextPrimary]}></Text>
</TouchableOpacity>
</View>
</ImageBackground>
<View style={styles.infoContainer}>
{/* ... existing info items ... */}
</View>
<TouchableOpacity style={styles.unbindButton} onPress={handleUnbind}>
<Text style={styles.unbindText}></Text>
</TouchableOpacity>
{currentEdit && (
<EditModal
visible={modalVisible}
onClose={() => setModalVisible(false)}
title={currentEdit.title}
value={currentEdit.value}
onSubmit={handleSubmit}
placeholder={`请输入${currentEdit.title.slice(2)}`}
/>
)}
<Modal
visible={modelModalVisible}
transparent={true}
animationType="slide"
onRequestClose={() => setModelModalVisible(false)}
>
<View style={styles.modalOverlay}>
<View style={styles.modalContent}>
<Text style={styles.modalTitle}></Text>
<Image
source={{ uri: previewImage || 'https://lxnapi.ccttiot.com/bike/img/static/uVnIDwcwQP7oo12PeYVJ' }}
style={styles.modalImage}
/>
<Select
selectedIndex={selectedBrandIndex}
onSelect={handleBrandChange}
style={styles.select}
value={brandList[selectedBrandIndex?.row]}
>
{brandList.map((brand, index) => (
<SelectItem key={index} title={brand} />
))}
</Select>
<Select
selectedIndex={selectedModelIndex}
onSelect={handleModelChange}
style={styles.select}
value={modelList[selectedModelIndex?.row]}
>
{modelList.map((model, index) => (
<SelectItem key={index} title={model} />
))}
</Select>
<View style={styles.modalButtons}>
<TouchableOpacity style={styles.modalButton} onPress={handleModelSelection}>
<Text style={styles.modalButtonText}></Text>
</TouchableOpacity>
<TouchableOpacity style={styles.modalButton} onPress={() => setModelModalVisible(false)}>
<Text style={styles.modalButtonText}></Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
</ImageBackground>
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
scrollContent: {
flexGrow: 1,
},
container: {
flex: 1,
},
imageBackground: {
paddingTop: rpx(40),
paddingBottom: rpx(60),
flex: 1,
width: '100%',
height: '100%',
},
carInfo: {
paddingTop: rpx(180),
width: '100%',
height: rpx(828),
alignItems: 'center',
},
carInfoButtons: {
marginTop: rpx(160),
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
width: '80%',
},
carInfoButton: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: rpx(250),
height: rpx(80),
backgroundColor: '#ffffff',
borderRadius: rpx(20),
borderWidth: rpx(2),
borderColor: '#808080',
},
carInfoButtonPrimary: {
borderRadius: rpx(20),
backgroundColor: '#333333',
},
carInfoButtonText: {
color: '#333333',
fontSize: rpx(32),
fontWeight: '500',
},
carInfoButtonTextPrimary: {
color: '#FFFFFF',
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFFFFF',
},
header: {
backgroundColor: 'transparent',
elevation: 0,
shadowOpacity: 0,
},
titleStyle: {
color: '#000000',
fontSize: rpx(36),
fontWeight: 'bold',
},
infoContainer: {
marginTop: rpx(40),
marginHorizontal: rpx(32),
backgroundColor: '#FFFFFF',
borderRadius: rpx(20),
padding: rpx(20),
},
infoItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: rpx(24),
borderBottomWidth: 1,
borderBottomColor: '#F5F5F5',
},
label: {
fontSize: rpx(28),
color: '#333333',
},
valueContainer: {
flexDirection: 'row',
alignItems: 'center',
},
value: {
fontSize: rpx(28),
color: '#999999',
marginRight: rpx(8),
},
arrow: {
width: rpx(32),
height: rpx(32),
},
unbindButton: {
marginHorizontal: rpx(32),
marginTop: rpx(40),
height: rpx(88),
backgroundColor: '#333333',
borderRadius: rpx(20),
justifyContent: 'center',
alignItems: 'center',
},
unbindText: {
color: '#FFFFFF',
fontSize: rpx(32),
fontWeight: '500',
},
modalOverlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
},
modalContent: {
width: rpx(650),
backgroundColor: '#FFFFFF',
borderRadius: rpx(20),
padding: rpx(40),
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
modalTitle: {
fontSize: rpx(36),
color: '#333333',
fontWeight: 'bold',
marginBottom: rpx(40),
textAlign: 'center',
},
modalInput: {
height: rpx(88),
borderWidth: 1,
borderColor: '#E5E5E5',
borderRadius: rpx(12),
paddingHorizontal: rpx(24),
fontSize: rpx(28),
color: '#333333',
marginBottom: rpx(32),
},
modalButtons: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: rpx(20),
},
modalButton: {
flex: 1,
height: rpx(88),
justifyContent: 'center',
alignItems: 'center',
borderRadius: rpx(12),
marginHorizontal: rpx(8),
backgroundColor: '#F5F5F5',
},
modalButtonPrimary: {
backgroundColor: '#333333',
},
modalButtonText: {
fontSize: rpx(28),
color: '#333333',
},
modalButtonTextPrimary: {
color: '#FFFFFF',
},
select: {
marginBottom: rpx(20),
},
modalImage: {
width: rpx(440),
height: rpx(340),
marginBottom: rpx(20),
},
});
export default deviceDetailSet;

View File

@ -2119,7 +2119,7 @@
resolved "https://registry.npmmirror.com/@react-native-community/slider/-/slider-4.5.5.tgz" resolved "https://registry.npmmirror.com/@react-native-community/slider/-/slider-4.5.5.tgz"
integrity sha512-x2N415pg4ZxIltArOKczPwn7JEYh+1OxQ4+hTnafomnMsqs65HZuEWcX+Ch8c5r8V83DiunuQUf5hWGWlw8hQQ== integrity sha512-x2N415pg4ZxIltArOKczPwn7JEYh+1OxQ4+hTnafomnMsqs65HZuEWcX+Ch8c5r8V83DiunuQUf5hWGWlw8hQQ==
"@react-native-picker/picker@^2.9.0": "@react-native-picker/picker@^2.10.2":
version "2.10.2" version "2.10.2"
resolved "https://registry.npmmirror.com/@react-native-picker/picker/-/picker-2.10.2.tgz" resolved "https://registry.npmmirror.com/@react-native-picker/picker/-/picker-2.10.2.tgz"
integrity sha512-kr3OvCRwTYjR/OKlb52k4xmQVU7dPRIALqpyiihexdJxEgvc1smnepgqCeM9oXmNSG4YaV5/RSxFlLC5Z/T/Eg== integrity sha512-kr3OvCRwTYjR/OKlb52k4xmQVU7dPRIALqpyiihexdJxEgvc1smnepgqCeM9oXmNSG4YaV5/RSxFlLC5Z/T/Eg==