This commit is contained in:
tx 2024-11-14 13:56:11 +08:00
parent 0ef962e5ba
commit fc87ad0b3d
19 changed files with 1516 additions and 93 deletions

11
App.tsx
View File

@ -1,4 +1,5 @@
import * as React from 'react';
import { useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { View, StyleSheet, Image, Platform } from 'react-native';
@ -9,7 +10,7 @@ import { enableScreens } from 'react-native-screens';
import HomeStackNavigator from './src/views/HomeStackNavigator';
import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
import { rpx } from './src/utils/rpx';
import { AMapSdk, MapView, Marker, MapType } from 'react-native-amap3d';
enableScreens();
type RootStackParamList = {
@ -21,6 +22,14 @@ type RootStackParamList = {
const Tab = createBottomTabNavigator<RootStackParamList>();
function App() {
useEffect(() => {
AMapSdk.init(
Platform.select({
android: "812efd3a950ba3675f928630302c6463",
})
);
}, []);
const getTabBarVisibility = (route: any) => {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
const showOnScreens = ['Home']; // 只在 Home 页面显示底部导航栏

View File

@ -75,6 +75,9 @@ android {
compileSdk rootProject.ext.compileSdkVersion
namespace "com.bikeapp_demo"
configurations {
all*.exclude group: 'com.amap.api', module: 'location'
}
defaultConfig {
missingDimensionStrategy 'react-native-camera', 'general'
applicationId "com.bikeapp_demo"
@ -111,6 +114,8 @@ dependencies {
implementation "com.google.android.gms:play-services-vision:20.1.3"
implementation "com.google.android.gms:play-services-vision-common:19.1.3"
implementation project(':react-native-camera')
implementation 'com.amap.api:3dmap:latest.integration'
implementation 'com.amap.api:location:latest.integration'
if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
} else {

View File

@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE"/>
<application
android:name=".MainApplication"
android:label="@string/app_name"

View File

@ -10,6 +10,9 @@ buildscript {
repositories {
google()
mavenCentral()
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
}
dependencies {
classpath("com.android.tools.build:gradle")
@ -17,5 +20,16 @@ buildscript {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
}
}
allprojects {
repositories {
// maven
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
//
google()
mavenCentral()
}
}
apply plugin: "com.facebook.react.rootproject"

41
package-lock.json generated
View File

@ -9,13 +9,14 @@
"version": "0.0.1",
"dependencies": {
"@react-native-community/geolocation": "^3.4.0",
"@react-native-community/slider": "^4.5.5",
"@react-native-picker/picker": "^2.9.0",
"@react-navigation/bottom-tabs": "^6.4.0",
"@react-navigation/native": "^6.1.18",
"@react-navigation/stack": "^6.3.8",
"axios": "^1.7.7",
"react": "18.2.0",
"react-native": "0.74.5",
"react-native-amap-geolocation": "^1.2.3",
"react-native-amap3d": "^3.0.7",
"react-native-camera": "^4.2.1",
"react-native-gesture-handler": "^2.20.2",
@ -24,7 +25,8 @@
"react-native-qrcode-scanner": "^1.5.5",
"react-native-reanimated": "^3.16.1",
"react-native-safe-area-context": "^4.14.0",
"react-native-screens": "^3.35.0"
"react-native-screens": "^3.35.0",
"react-native-wheel-picker-android": "^2.0.6"
},
"devDependencies": {
"@babel/core": "^7.20.0",
@ -2799,6 +2801,20 @@
"react-native": "*"
}
},
"node_modules/@react-native-community/slider": {
"version": "4.5.5",
"resolved": "https://registry.npmmirror.com/@react-native-community/slider/-/slider-4.5.5.tgz",
"integrity": "sha512-x2N415pg4ZxIltArOKczPwn7JEYh+1OxQ4+hTnafomnMsqs65HZuEWcX+Ch8c5r8V83DiunuQUf5hWGWlw8hQQ=="
},
"node_modules/@react-native-picker/picker": {
"version": "2.9.0",
"resolved": "https://registry.npmmirror.com/@react-native-picker/picker/-/picker-2.9.0.tgz",
"integrity": "sha512-khEhIW/uhfMqq/+tvg4rEAiPGT8GX+Y6QydlP2TSMSmRHoSJK+ShXvXZXSr4Sii4imkj4BwvLunGywwtQDODqg==",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/@react-native/assets-registry": {
"version": "0.74.87",
"license": "MIT",
@ -8305,6 +8321,14 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.3",
"license": "MIT"
@ -9055,11 +9079,6 @@
}
}
},
"node_modules/react-native-amap-geolocation": {
"version": "1.2.3",
"resolved": "https://registry.npmmirror.com/react-native-amap-geolocation/-/react-native-amap-geolocation-1.2.3.tgz",
"integrity": "sha512-NKQG1eKJGHFnSGAMtsXZYfoKzlDAyK23cuKaIcJaWfa0kNr23pVrxOss3TcNRZTu8Syr9AwRus7I0PLGEcAaNA=="
},
"node_modules/react-native-amap3d": {
"version": "3.2.4",
"license": "MIT",
@ -9179,6 +9198,14 @@
"react-native": "*"
}
},
"node_modules/react-native-wheel-picker-android": {
"version": "2.0.6",
"resolved": "https://registry.npmmirror.com/react-native-wheel-picker-android/-/react-native-wheel-picker-android-2.0.6.tgz",
"integrity": "sha512-gwstWymEZUmSokWKDQufD3t/alSRkYDTSEe3YrtsDtpcKM55t9BeAPdKzYURTVRUGCYeoOy4nyxKBYkMnqbvlQ==",
"dependencies": {
"moment": "^2.22.0"
}
},
"node_modules/react-native/node_modules/@jest/types": {
"version": "26.6.2",
"license": "MIT",

View File

@ -11,13 +11,14 @@
},
"dependencies": {
"@react-native-community/geolocation": "^3.4.0",
"@react-native-community/slider": "^4.5.5",
"@react-native-picker/picker": "^2.9.0",
"@react-navigation/bottom-tabs": "^6.4.0",
"@react-navigation/native": "^6.1.18",
"@react-navigation/stack": "^6.3.8",
"axios": "^1.7.7",
"react": "18.2.0",
"react-native": "0.74.5",
"react-native-amap-geolocation": "^1.2.3",
"react-native-amap3d": "^3.0.7",
"react-native-camera": "^4.2.1",
"react-native-gesture-handler": "^2.20.2",
@ -26,7 +27,8 @@
"react-native-qrcode-scanner": "^1.5.5",
"react-native-reanimated": "^3.16.1",
"react-native-safe-area-context": "^4.14.0",
"react-native-screens": "^3.35.0"
"react-native-screens": "^3.35.0",
"react-native-wheel-picker-android": "^2.0.6"
},
"devDependencies": {
"@babel/core": "^7.20.0",

BIN
src/assets/Image/Bledis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -0,0 +1,40 @@
// WGS-84 转 GCJ-02
export const transformFromWGSToGCJ = (lat: number, lng: number) => {
const PI = 3.14159265358979324;
const ee = 0.00669342162296594323;
const a = 6378245.0;
let dLat = transformLat(lng - 105.0, lat - 35.0);
let dLng = transformLng(lng - 105.0, lat - 35.0);
const radLat = lat / 180.0 * PI;
let magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
const sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);
return {
latitude: lat + dLat,
longitude: lng + dLng,
};
};
function transformLat(x: number, y: number) {
let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
return ret;
}
function transformLng(x: number, y: number) {
let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
return ret;
}
const PI = 3.14159265358979324;

View File

@ -17,13 +17,13 @@ type NavigationProp = StackNavigationProp<RootStackParamList>;
const MiniMap = () => {
const navigation = useNavigation<NavigationProp>();
useEffect(() => {
AMapSdk.init(
Platform.select({
android: "812efd3a950ba3675f928630302c6463",
})
);
}, []);
// useEffect(() => {
// AMapSdk.init(
// Platform.select({
// android: "812efd3a950ba3675f928630302c6463",
// })
// );
// }, []);
const handleMapPress = () => {
navigation.navigate('DeviceMap');

View File

@ -8,7 +8,7 @@ const NoDevice: React.FC = () => {
const handlePress = () => {
navigation.navigate('BindIndex');
navigation.navigate('BindIndex' as never);
// console.log(navigation);
};
return (

View File

@ -23,6 +23,8 @@ type RootStackParamList = {
Home: undefined;
DeviceList: undefined;
DeviceMap: undefined;
DeviceSet: undefined;
DeviceShare: undefined;
// 添加其他页面的路由参数类型
};
@ -39,10 +41,16 @@ const NormaIndex: React.FC = () => {
const handlePress = () => {
navigation.navigate('DeviceList');
};
const toSet = () => {
navigation.navigate('DeviceSet');
};
const toMap = () => {
navigation.navigate('DeviceMap');
};
const toShare = () => {
navigation.navigate('DeviceShare');
};
const panResponder = useRef(
PanResponder.create({
@ -215,7 +223,7 @@ const NormaIndex: React.FC = () => {
</TouchableWithoutFeedback>
<TouchableOpacity onPress={handlePress}>
<TouchableOpacity onPress={toSet}>
<Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/ucYQHQ2Ep4odL8JpbtfT' }}
style={styles.carSet}
@ -223,10 +231,13 @@ const NormaIndex: React.FC = () => {
</TouchableOpacity>
<View style={styles.otherSet}>
<TouchableOpacity onPress={toShare}>
<Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/ulDHhC4MrH3FO0AeTqVg' }}
style={styles.otherImg}
/>
</TouchableOpacity>
<Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/u849NsNxdtzxhUkUJnfW' }}
style={styles.otherImg}

View File

@ -7,6 +7,11 @@ import BindIndex from './bind/bind_index';
import SnBind from './bind/sn_bind';
import ConfirmBind from './bind/ConfirmBind';
import DeviceMap from './device/deviceMap';
import DeviceSet from './device/deviceSet';
import UnlockSetting from './device/UnlockSetting';
import BleDistance from './device/BleDistance';
import DeviceShare from './device/DeviceShare';
import AddShare from './device/AddShare';
// import BleBind from './bind/ble_bind';
import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
@ -17,6 +22,11 @@ type RootStackParamList = {
SnBind: undefined;
ConfirmBind: undefined;
DeviceMap: undefined;
DeviceSet: undefined;
BleDistance: undefined;
UnlockSetting: undefined;
DeviceShare: undefined;
AddShare: undefined;
};
const Stack = createStackNavigator<RootStackParamList>();
@ -44,7 +54,7 @@ type Props = {
export default function HomeStackNavigator({ navigation, route }: Props) {
React.useEffect(() => {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
const hideTabBarRoutes = ['DeviceList', 'BindIndex', 'SnBind', 'BleBind', 'ConfirmBind', 'DeviceMap']; // 添加新的路由名
const hideTabBarRoutes = ['DeviceList', 'BindIndex', 'SnBind', 'BleBind', 'ConfirmBind', 'DeviceMap', 'DeviceSet', 'UnlockSetting', 'BleDistance', 'DeviceShare']; // 添加新的路由名
const shouldHideTabBar = hideTabBarRoutes.includes(routeName);
navigation.getParent()?.setOptions({
@ -86,7 +96,32 @@ export default function HomeStackNavigator({ navigation, route }: Props) {
name="DeviceMap"
component={DeviceMap}
options={createScreenOptions('设备位置')}
/>
/>
<Stack.Screen
name="DeviceSet"
component={DeviceSet}
options={createScreenOptions('车辆设置')}
/>
<Stack.Screen
name="UnlockSetting"
component={UnlockSetting}
options={createScreenOptions('无感解锁')}
/>
<Stack.Screen
name="BleDistance"
component={BleDistance}
options={createScreenOptions('蓝牙距离')}
/>
<Stack.Screen
name="DeviceShare"
component={DeviceShare}
options={createScreenOptions('车辆共享')}
/>
<Stack.Screen
name="AddShare"
component={AddShare}
options={createScreenOptions('添加共享人')}
/>
</Stack.Navigator>
);
}

View File

@ -0,0 +1,354 @@
import React, { useState } from 'react';
import { View, Text, StyleSheet, Image, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity, Modal } from 'react-native';
import { rpx } from '../../utils/rpx';
const AddShare = () => {
const [phone, setPhone] = useState('');
const [name, setName] = useState('');
const [time, setTime] = useState('');
const [showTimeModal, setShowTimeModal] = useState(false);
const [showBleInfo, setShowBleInfo] = useState(false);
const timeOptions = ['1天', '7天', '30天', '永久'];
const [allowBleLocation, setAllowBleLocation] = useState(true);
const handlePress = () => {
setShowTimeModal(true);
}
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<View style={styles.IptBox}>
<TextInput
style={styles.input}
placeholder="共享人手机号"
value={phone}
onChangeText={setPhone}
/>
<Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uMSABuH69gxKXIP0zqtl' }}
style={styles.phoneImg}
/>
</View>
<View style={styles.IptBox}>
<TextInput
style={styles.input}
placeholder="共享人姓名"
value={name}
onChangeText={setName}
/>
</View>
<TouchableOpacity onPress={handlePress} activeOpacity={1}>
<View style={styles.IptBox}>
<TextInput
style={styles.input}
placeholder="共享钥匙有效期"
value={time}
onChangeText={setTime}
editable={false}
/>
<Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uGq4yJlU1ZZRkwiJ8Y74' }}
style={styles.phoneImg}
/>
</View>
</TouchableOpacity>
<TouchableOpacity style={styles.bleInfo} onPress={() => setShowBleInfo(true)}>
<Image source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uvjPwfAKsL9kHD1Tb9aW' }} style={styles.bleImg}></Image>
<View style={styles.bleInfoBox}>
<Text style={styles.tit}></Text>
<Text style={[
styles.txt,
{ color: allowBleLocation ? '#4297F3' : '#666' }
]}>
{allowBleLocation ? '允许' : '不允许'}
</Text>
</View>
</TouchableOpacity>
<Modal visible={showBleInfo} transparent={true} animationType="slide">
<View style={styles.modalContainers}>
<View style={styles.modalContents}>
<Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uwMP7N2hCLFdwwDPhHMm' }}
style={styles.bleLocationImg}
/>
<Text style={styles.bleModalTitle}>
{'\n'}
</Text>
<View style={styles.radioGroup}>
<TouchableOpacity
style={styles.radioItem}
onPress={() => setAllowBleLocation(true)}
>
<Text style={styles.radioText}></Text>
<View style={styles.radioCircle}>
{allowBleLocation && <View style={styles.radioSelected} />}
</View>
</TouchableOpacity>
<View style={styles.line}></View>
<TouchableOpacity
style={styles.radioItem}
onPress={() => setAllowBleLocation(false)}
>
<Text style={styles.radioText}></Text>
<View style={styles.radioCircle}>
{!allowBleLocation && <View style={styles.radioSelected} />}
</View>
</TouchableOpacity>
</View>
<TouchableOpacity
style={styles.confirmButton}
onPress={() => setShowBleInfo(false)}
>
<Text style={styles.confirmButtonText}></Text>
</TouchableOpacity>
</View>
</View>
</Modal>
<Modal
visible={showTimeModal}
transparent={true}
animationType="slide"
>
<View style={styles.modalContainer}>
<View style={styles.modalContent}>
<View style={styles.modalHeader}>
<TouchableOpacity
style={styles.headerButton}
onPress={() => setShowTimeModal(false)}
>
<Text style={styles.cancelText}></Text>
</TouchableOpacity>
<Text style={styles.modalTitle}></Text>
<TouchableOpacity
style={styles.headerButton}
onPress={() => setShowTimeModal(false)}
>
<Text style={styles.confirmText}></Text>
</TouchableOpacity>
</View>
<View style={styles.optionsContainer}>
{timeOptions.map((option, index) => (
<TouchableOpacity
key={index}
style={[
styles.optionItem,
time === option && styles.selectedItemBg
]}
onPress={() => {
setTime(option);
setShowTimeModal(false);
}}
>
<Text style={[
styles.optionText,
time === option && styles.selectedOption
]}>
{option}
</Text>
</TouchableOpacity>
))}
</View>
</View>
</View>
</Modal>
</View>
</TouchableWithoutFeedback>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F3FCFF',
alignItems: 'center',
},
modalContainers: {
flex: 1,
justifyContent: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
paddingHorizontal: rpx(30),
},
modalContents: {
backgroundColor: '#fff',
borderRadius: rpx(20),
alignItems: 'center',
paddingVertical: rpx(40),
padding:rpx(28)
},
bleLocationImg: {
width: rpx(338),
height: rpx(338),
marginBottom: rpx(30),
},
bleModalTitle: {
fontSize: rpx(36),
color: '#333',
textAlign: 'center',
lineHeight: rpx(50),
marginBottom: rpx(40),
},
line: {
borderWidth: rpx(1),
borderColor: '#eee',
marginBottom: rpx(28)
},
radioGroup: {
width: '100%',
paddingHorizontal: rpx(60),
marginBottom: rpx(40),
borderWidth: rpx(2),
borderColor: '#eee',
borderRadius: rpx(30),
paddingTop: rpx(28)
},
radioItem: {
justifyContent: 'space-between',
flexDirection: 'row',
alignItems: 'center',
marginBottom: rpx(30),
},
radioCircle: {
width: rpx(44),
height: rpx(44),
borderRadius: rpx(22),
borderWidth: rpx(2),
borderColor: '#4297F3',
marginRight: rpx(20),
justifyContent: 'center',
alignItems: 'center',
},
radioSelected: {
width: rpx(28),
height: rpx(28),
borderRadius: rpx(14),
backgroundColor: '#4297F3',
},
radioText: {
fontSize: rpx(32),
color: '#333',
},
confirmButton: {
width: rpx(560),
height: rpx(88),
backgroundColor: '#4297F3',
borderRadius: rpx(44),
justifyContent: 'center',
alignItems: 'center',
},
confirmButtonText: {
fontSize: rpx(32),
color: '#fff',
fontWeight: '500',
},
bleImg: {
width: rpx(88),
height: rpx(88),
},
tit: {
fontSize: rpx(36),
color: '#1E1D20',
},
txt: {
fontSize: rpx(36),
color: '#4297F3',
},
bleInfo: {
marginTop: rpx(30),
width: rpx(688),
height: rpx(174),
backgroundColor: '#ffffff',
padding: rpx(44),
flexDirection: 'row',
// alignItems: 'center',
// justifyContent: 'center',
},
bleInfoBox: {
paddingLeft: rpx(20),
},
modalContainer: {
flex: 1,
justifyContent: 'flex-end',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
modalContent: {
backgroundColor: '#fff',
borderTopLeftRadius: rpx(20),
borderTopRightRadius: rpx(20),
paddingBottom: rpx(30),
},
modalHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
height: rpx(100),
borderBottomWidth: 1,
borderBottomColor: '#eee',
paddingHorizontal: rpx(30),
},
headerButton: {
padding: rpx(20),
},
modalTitle: {
fontSize: rpx(32),
fontWeight: '500',
color: '#333',
},
cancelText: {
fontSize: rpx(32),
color: '#666',
},
confirmText: {
fontSize: rpx(32),
color: '#4297F3',
},
optionsContainer: {
paddingHorizontal: rpx(30),
paddingTop: rpx(20),
alignItems: 'center',
},
optionItem: {
height: rpx(100),
width: rpx(300),
justifyContent: 'center',
alignItems: 'center',
// paddingLeft: rpx(30),
borderRadius: rpx(16),
marginBottom: rpx(20),
},
optionText: {
fontSize: rpx(32),
color: '#333',
},
selectedItemBg: {
backgroundColor: '#F0F7FF',
},
selectedOption: {
color: '#4297F3',
},
IptBox: {
marginTop: rpx(30),
paddingHorizontal: rpx(30),
width: rpx(688),
height: rpx(128),
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#ffffff',
borderRadius: rpx(30),
},
input: {
flex: 1,
fontSize: rpx(36),
},
phoneImg: {
width: rpx(54),
height: rpx(54),
}
})
export default AddShare;

View File

@ -0,0 +1,79 @@
import React, { useState } from 'react';
import { View, Text, StyleSheet, Image, TouchableOpacity, ScrollView } from 'react-native';
import { rpx } from '../../utils/rpx';
const BleDistance = () => {
const [isEnabled, setIsEnabled] = useState(false); // 添加状态控制按钮
return (
<View style={styles.container}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.scrollContent}
>
<Image source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/upCrcuiBaoZrd9ZRZayj' }} style={styles.icon} />
<Text style={styles.tit}>1.</Text>
<Text style={styles.txt}></Text>
<Text style={styles.tit}>2.</Text>
<Text style={styles.txt}></Text>
<Text style={styles.tit}>3.</Text>
<Text style={styles.txt}> </Text>
<Text style={styles.tit}>4.</Text>
<Text style={styles.txt}></Text>
<Text style={styles.tit}></Text>
<Text style={styles.txt}></Text>
<TouchableOpacity
style={[styles.btn, { backgroundColor: isEnabled ? '#4297F3' : '#ccc' }]}
disabled={!isEnabled}
>
<Text style={styles.btnTxt}></Text>
</TouchableOpacity>
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F3FCFF',
},
scrollContent: {
alignItems: 'center',
paddingBottom: rpx(40), // 添加底部间距
},
btn: {
marginTop: rpx(50),
width: rpx(614),
height: rpx(92),
backgroundColor: '#4297F3',
borderRadius: rpx(16),
alignItems: 'center',
justifyContent: 'center',
},
btnTxt: {
fontSize: rpx(40),
fontWeight: '500',
color: '#fff',
},
icon: {
marginLeft: rpx(80),
marginTop: rpx(100),
width: rpx(540),
height: rpx(450),
},
txt: {
width: rpx(608),
marginTop: rpx(20),
fontSize: rpx(28),
color: '#808080',
},
tit: {
fontWeight: '500',
width: rpx(608),
marginTop: rpx(20),
fontSize: rpx(32),
color: '#3D3D3D',
},
});
export default BleDistance;

View File

@ -0,0 +1,154 @@
import React from 'react';
import { View, Text, StyleSheet, Image, TouchableOpacity } from 'react-native';
import { rpx } from '../../utils/rpx';
import { useNavigation } from '@react-navigation/native';
const DeviceShare = () => {
const navigation = useNavigation();
return (
<View style={styles.container}>
<View style={styles.shareBox}>
<Text style={styles.shareTitle}>2/3</Text>
<Text style={styles.shareTxt}>使</Text>
<View style={styles.card}>
<View style={styles.cardTop}>
<Image source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uVnIDwcwQP7oo12PeYVJ' }} style={styles.cardTopImg}></Image>
<View style={styles.cardTopTxt}>
<Text style={styles.cardTopName}></Text>
<Text style={styles.cardTopPhone}>13860332568</Text>
</View>
<Text style={styles.cardType}></Text>
</View>
<View style={styles.cardBottom}>
<Text style={styles.lasttime}>2327</Text>
<Image source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uGq4yJlU1ZZRkwiJ8Y74' }} style={styles.lasttimeImg}></Image>
</View>
</View>
<TouchableOpacity onPress={() => {
navigation.navigate('AddShare' as never);
}}>
<View style={styles.addBtn}>
<Image source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uI4CdJFzS1GkY1AzfFyG' }} style={styles.addBtnImg}></Image>
<Text style={styles.addBtnTxt}></Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.shareTip}>
<Text style={styles.shareBtnTxt}></Text>
<Image source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uGq4yJlU1ZZRkwiJ8Y74' }} style={styles.lasttimeImg}></Image>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F3FCFF',
alignItems: 'center',
},
addBtnImg: {
width: rpx(32),
height: rpx(32),
},
shareTip: {
flexDirection: 'row',
alignItems: 'center',
marginTop: rpx(44),
},
shareBtnTxt: {
fontSize: rpx(32),
color: '#808080',
},
addBtn: {
width: rpx(592),
height: rpx(108),
backgroundColor: '#ffffff',
borderRadius: rpx(16),
marginTop: rpx(30),
borderWidth: rpx(2),
borderColor: '#808080',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
},
addBtnTxt: {
marginLeft: rpx(12),
fontSize: rpx(36),
color: '#808080',
},
cardBottom: {
flexDirection: 'row',
alignItems: 'center',
display: 'flex',
flexWrap: 'nowrap',
},
lasttime: {
marginLeft: rpx(124),
fontSize: rpx(24),
color: '#4297F3',
},
lasttimeImg: {
marginLeft: 'auto',
width: rpx(24),
height: rpx(24),
},
cardTop: {
flexDirection: 'row',
alignItems: 'center',
display: 'flex',
flexWrap: 'nowrap',
},
cardType: {
marginLeft: 'auto',
alignSelf: 'flex-start',
fontSize: rpx(32),
color: '#4297F3',
},
cardTopTxt: {
marginLeft: rpx(28),
},
cardTopImg: {
width: rpx(96),
height: rpx(96),
borderRadius: rpx(48),
},
cardTopName: {
fontSize: rpx(44),
color: '#3D3D3D',
},
cardTopPhone: {
fontSize: rpx(36),
color: '#808080',
},
shareBox: {
width: rpx(688),
borderRadius: rpx(30),
backgroundColor: '#fff',
padding: rpx(32),
},
shareTitle: {
fontWeight: 'bold',
fontSize: rpx(44),
color: '#3D3D3D',
},
shareTxt: {
marginTop: rpx(14),
fontWeight: '500',
fontSize: rpx(28),
color: '#999',
},
card: {
width: rpx(592),
padding: rpx(32),
marginTop: rpx(32),
borderRadius: rpx(16),
borderWidth: rpx(2),
borderColor: '#808080 ',
},
});
export default DeviceShare;

View File

@ -0,0 +1,241 @@
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Image, Switch } from 'react-native';
import Slider from '@react-native-community/slider'; // 修改这里的导入
import { rpx } from '../../utils/rpx';
import { useNavigation } from '@react-navigation/native';
const UnlockSetting = () => {
const navigation = useNavigation();
const [isAutoOff, setIsAutoOff] = useState(true);
const [sliderValue, setSliderValue] = useState(1); // 1表示标准
const [isCustomDistance, setIsCustomDistance] = useState(false); // 是否自定义距离
return (
<View style={styles.container}>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uKcmg9OiTA72mIm59GRh' }} style={styles.img} />
<View style={styles.card}>
<View style={styles.cont_li}>
<View style={styles.cont_li_content}>
<View style={styles.cont_li_top}>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/ue1qF3cHAVlV8Fn5CKV6' }} style={styles.icon_set} />
<Text style={styles.text_set}></Text>
<View style={styles.cont_li_right}>
<Switch
onValueChange={() => setIsAutoOff(!isAutoOff)}
value={isAutoOff}
trackColor={{ false: '#767577', true: '#4297F3' }}
thumbColor={'#fff'}
/>
</View>
</View>
<Text style={styles.tipTxt}>1. </Text>
<Text style={styles.tipTxt}>2. </Text>
</View>
</View>
</View>
{isAutoOff && (
<View style={styles.card}>
<View style={styles.cont_li}>
<View style={styles.cont_li_content}>
<View style={styles.cont_li_top}>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uBAdXDKxFsApstvKlEWo' }} style={styles.icon_set} />
<Text style={styles.text_set}></Text>
</View>
<View style={styles.distanceOptions}>
<TouchableOpacity
style={[styles.distanceOption, !isCustomDistance && styles.selectedOption]}
onPress={() => setIsCustomDistance(false)}
>
<View style={[styles.radioButton, !isCustomDistance && styles.selectedRadioButton]}>
<View style={[styles.radioInner, !isCustomDistance && styles.radioInnerSelected]} />
</View>
<Text style={[styles.distanceText, !isCustomDistance && styles.selectedText]}></Text>
</TouchableOpacity>
</View>
<View style={styles.sliderContainer}>
<Slider
style={styles.slider}
minimumValue={0}
maximumValue={2}
value={sliderValue}
onValueChange={setSliderValue}
minimumTrackTintColor="#4297F3"
maximumTrackTintColor="#EBEBEB"
/>
<View style={styles.sliderLabels}>
<Text style={styles.sliderLabel}></Text>
<Text style={styles.sliderLabel}></Text>
<Text style={styles.sliderLabel}></Text>
</View>
</View>
<View style={styles.distanceOptions}>
<TouchableOpacity
style={[styles.distanceOption, isCustomDistance && styles.selectedOption]}
onPress={() => setIsCustomDistance(true)}
>
<View style={[styles.radioButton, isCustomDistance && styles.selectedRadioButton]}>
<View style={[styles.radioInner, isCustomDistance && styles.radioInnerSelected]} />
</View>
<Text style={[styles.distanceText, isCustomDistance && styles.selectedText]}></Text>
</TouchableOpacity>
{isCustomDistance && (
<TouchableOpacity style={styles.distanceBtn} onPress={() => navigation.navigate('BleDistance' as never)}>
<Text style={styles.distanceBtnTxt}></Text>
</TouchableOpacity>
)}
</View>
</View>
</View>
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
backgroundColor: '#F3FCFF',
},
distanceBtn: {
marginLeft: 'auto',
marginRight: rpx(40),
width: rpx(154),
height: rpx(56),
backgroundColor: '#4297F3',
borderRadius: rpx(28),
alignItems: 'center',
justifyContent: 'center',
},
distanceBtnTxt: {
fontWeight: '500',
fontSize: rpx(32),
color: '#fff',
},
selectedOption: {
backgroundColor: '#F5F5F5',
borderRadius: rpx(30),
},
selectedText: {
color: '#4297F3',
},
selectedRadioButton: {
borderColor: '#4297F3',
},
distanceOptions: {
flexDirection: 'row',
marginTop: rpx(20),
marginLeft: rpx(62),
},
distanceOption: {
flexDirection: 'row',
alignItems: 'center',
marginRight: rpx(40),
},
radioButton: {
width: rpx(40),
height: rpx(40),
borderRadius: rpx(20),
borderWidth: 2,
borderColor: '#CCCCCC',
alignItems: 'center',
justifyContent: 'center',
marginRight: rpx(10),
},
radioInner: {
width: rpx(24),
height: rpx(24),
borderRadius: rpx(12),
backgroundColor: 'transparent',
},
radioInnerSelected: {
backgroundColor: '#4297F3',
},
distanceText: {
fontSize: rpx(28),
color: '#333',
},
sliderContainer: {
marginTop: rpx(30),
marginLeft: rpx(62),
width: '85%',
},
slider: {
width: '100%',
height: rpx(40),
},
sliderLabels: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: rpx(10),
},
sliderLabel: {
fontSize: rpx(24),
color: '#999',
},
img: {
width: rpx(688),
height: rpx(600),
},
card: {
marginTop: rpx(20),
width: rpx(688),
backgroundColor: '#fff',
borderRadius: rpx(30),
padding: rpx(30),
paddingTop: rpx(0),
},
icon_set: {
width: rpx(50),
height: rpx(50),
},
cont_li: {
paddingTop: rpx(32),
paddingBottom: rpx(32),
borderBottomWidth: rpx(1),
borderBottomColor: '#EBEBEB',
},
cont_li_right: {
marginLeft: 'auto',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
},
cont_li_content: {
flexDirection: 'column',
},
cont_li_top: {
width: '100%',
flexDirection: 'row',
alignItems: 'center',
marginBottom: rpx(8),
},
text_set: {
fontWeight: '500',
fontSize: rpx(36),
color: '#333',
marginLeft: rpx(10),
},
rightTxt: {
fontWeight: '500',
fontSize: rpx(28),
color: '#4297F3',
marginRight: rpx(8),
},
icon_right: {
width: rpx(32),
height: rpx(32),
},
tipTxt: {
width: '85%',
marginLeft: rpx(62),
fontSize: rpx(28),
color: '#999',
marginTop: rpx(8),
},
});
export default UnlockSetting;

View File

@ -16,7 +16,7 @@ export default function DeviceList() {
const [selectedItem, setSelectedItem] = useState<number | null>(null);
const handlePress = () => {
navigation.navigate('BindIndex');
navigation.navigate('BindIndex' as never);
// console.log(navigation);
};

View File

@ -1,64 +1,128 @@
import React, { useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Image, StatusBar, Linking, Platform } from 'react-native';
import { AMapSdk, MapView, Marker, MapType } from 'react-native-amap3d';
import { init, Geolocation } from 'react-native-amap-geolocation';
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Image, StatusBar, Linking, Platform, PermissionsAndroid } from 'react-native';
import { MapView, Marker, MapType } from 'react-native-amap3d';
import Geolocation from '@react-native-community/geolocation';
import { rpx } from '../../utils/rpx';
import { useNavigation } from '@react-navigation/native';
import { transformFromWGSToGCJ } from '../../utils/coordtransform';
const DeviceMap = () => {
const navigation = useNavigation();
const latitude = 26.95500669;
const longitude = 120.32736769;
const imageUrl = "https://lxnapi.ccttiot.com/bike/img/static/uRx1B8B8acbquF2TO7Ry";
const [userLocation, setUserLocation] = React.useState({
latitude: 0,
longitude: 0,
const [location, setLocation] = useState({
latitude: 26.95500669,
longitude: 120.32736769,
});
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
// 初始化定位
async function initGeolocation() {
await init({
ios: "812efd3a950ba3675f928630302c6463",
android: "812efd3a950ba3675f928630302c6463"
});
Geolocation.getCurrentPosition(
({ coords }) => {
// console.log('定位错误:', coords);
setUserLocation({
latitude: coords.latitude,
longitude: coords.longitude,
});
},
(error) => {
console.log('定位错误:', error);
},
// {
// timeout: 15000,
// maximumAge: 10000,
// distanceFilter: 100,
// }
// 请求 Android 定位权限
const requestAndroidPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: "位置信息权限",
message: "需要获取您的位置信息",
buttonNeutral: "稍后询问",
buttonNegative: "取消",
buttonPositive: "确定"
}
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
} catch (err) {
console.warn(err);
return false;
}
};
initGeolocation();
const getCurrentLocation = async () => {
setIsLoading(true);
try {
// ... 权限检查和配置代码保持不变
// 同时发起高精度和低精度定位请求
const highAccuracyPromise = new Promise((resolve, reject) => {
Geolocation.getCurrentPosition(
resolve,
reject,
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 1000
}
);
});
const lowAccuracyPromise = new Promise((resolve, reject) => {
Geolocation.getCurrentPosition(
resolve,
reject,
{
enableHighAccuracy: false,
timeout: 10000,
maximumAge: 5000
}
);
});
Promise.race([highAccuracyPromise, lowAccuracyPromise])
.then((position: any) => {
console.log('原始定位结果:', position);
// 转换坐标系
const gcjLocation = transformFromWGSToGCJ(
position.coords.latitude,
position.coords.longitude
);
console.log('转换后的坐标:', gcjLocation);
setLocation(gcjLocation);
setIsLoading(false);
})
.catch((error) => {
console.error('定位失败:', error);
setIsLoading(false);
});
} catch (error) {
console.error('获取位置信息失败:', error);
setIsLoading(false);
}
};
// 位置监听也需要转换坐标系
useEffect(() => {
getCurrentLocation();
const watchId = Geolocation.watchPosition(
(position) => {
const gcjLocation = transformFromWGSToGCJ(
position.coords.latitude,
position.coords.longitude
);
setLocation(gcjLocation);
},
(error) => {
console.error('位置监听错误:', error);
},
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 1000,
distanceFilter: 10
}
);
// 组件卸载时清理
return () => {
Geolocation.stop();
Geolocation.clearWatch(watchId);
};
}, []);
// 跳转到高德地图
// 跳转到高德地图
const openAMap = async () => {
const url = Platform.select({
android: `androidamap://navi?sourceApplication=appname&lat=${latitude}&lon=${longitude}&dev=0&style=2`,
ios: `iosamap://navi?sourceApplication=appname&lat=${latitude}&lon=${longitude}&dev=0&style=2`,
android: `androidamap://navi?sourceApplication=appname&lat=${location.latitude}&lon=${location.longitude}&dev=0&style=2`,
ios: `iosamap://navi?sourceApplication=appname&lat=${location.latitude}&lon=${location.longitude}&dev=0&style=2`,
});
const fallbackUrl = `https://uri.amap.com/navigation?to=${longitude},${latitude},目的地&mode=car&coordinate=gaode`;
const imageUrl = "https://lxnapi.ccttiot.com/bike/img/static/uRx1B8B8acbquF2TO7Ry";
const fallbackUrl = `https://uri.amap.com/navigation?to=${location.longitude},${location.latitude},目的地&mode=car&coordinate=gaode`;
try {
// 检查是否安装了高德地图
@ -79,11 +143,6 @@ const DeviceMap = () => {
return (
<View style={styles.container}>
<StatusBar
backgroundColor="#FFFFFF"
barStyle="dark-content"
translucent={false}
/>
<MapView
style={styles.map}
mapType={MapType.Standard}
@ -92,42 +151,38 @@ const DeviceMap = () => {
zoomEnabled={true}
initialCameraPosition={{
target: {
latitude,
longitude,
latitude: location.latitude,
longitude: location.longitude,
},
zoom: 15,
}}
>
{/* 设备位置标记 */}
<Marker
position={{
latitude,
longitude,
latitude: location.latitude,
longitude: location.longitude,
}}
icon={{ uri: imageUrl }}
icon={{ uri: 'imageUrl' }}
/>
{/* 用户位置标记 */}
{userLocation.latitude !== 0 && (
<Marker
position={{
latitude: userLocation.latitude,
longitude: userLocation.longitude,
}}
icon={{ uri: 'imageUrl' }}
/>
)}
</MapView>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uOCaLkinKXhZLxCkTFAQ' }} style={styles.locationIcon} />
<View style={styles.bottomCard}>
<View style={styles.addressInfo}>
<Text style={styles.addressText}>
200
</Text>
<Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/voice' }}
source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/ucBlLZW1SpAaKxSQYkr6' }}
style={styles.voiceIcon}
/>
</View>
<View style={styles.timeBlock}>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uMnpK2e8az06pzJrKms5' }} style={styles.timeClock} />
<Text style={styles.timeText1}>
12:00
</Text>
</View>
<TouchableOpacity
style={styles.navigationButton}
onPress={openAMap}
@ -140,14 +195,46 @@ const DeviceMap = () => {
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#FFFFFF',
},
locationIcon: {
position: 'absolute',
right: rpx(32),
bottom: rpx(400),
width: rpx(90),
height: rpx(90),
},
map: {
flex: 1,
width: '100%',
},
timeBlock: {
display: 'flex',
flexDirection: 'row', // 确保内容水平排列
alignSelf: 'flex-start',
justifyContent:'center',
alignItems:'center',
padding: rpx(8) ,
paddingHorizontal:rpx(18),
backgroundColor: '#EFEFEF',
borderRadius: rpx(29),
flexWrap: 'nowrap', // 防止换行
marginBottom:rpx(40),
},
timeClock:{
marginRight:rpx(14),
width:rpx(26),
height:rpx(26),
},
timeText1:{
fontSize:rpx(24),
color:'#808080',
},
header: {
position: 'absolute',
top: 0,
@ -196,7 +283,7 @@ const styles = StyleSheet.create({
backgroundColor: '#FFFFFF',
borderTopLeftRadius: rpx(24),
borderTopRightRadius: rpx(24),
padding: rpx(32),
padding: rpx(36),
},
addressInfo: {
flexDirection: 'row',
@ -205,18 +292,18 @@ const styles = StyleSheet.create({
},
addressText: {
flex: 1,
fontSize: rpx(28),
fontSize: rpx(36),
color: '#333333',
marginRight: rpx(16),
},
voiceIcon: {
width: rpx(44),
height: rpx(44),
width: rpx(90),
height: rpx(90),
},
navigationButton: {
height: rpx(96),
backgroundColor: '#2089FF',
borderRadius: rpx(48),
borderRadius: rpx(20),
justifyContent: 'center',
alignItems: 'center',
},

View File

@ -0,0 +1,364 @@
import React, { useState } from 'react';
import { View, Text, StyleSheet, Image, TouchableOpacity, Modal } from 'react-native';
import { rpx } from '../../utils/rpx';
import { useNavigation } from '@react-navigation/native';
const DeviceSet = () => {
const navigation = useNavigation();
const [showAutoOffModal, setShowAutoOffModal] = useState(false);
const [showAutoLockModal, setShowAutoLockModal] = useState(false);
const [selectedTime, setSelectedTime] = useState('3分钟');
const [selectedLockTime, setSelectedLockTime] = useState('15秒');
const [showSensitivityModal, setShowSensitivityModal] = useState(false);
const [selectedSensitivity, setSelectedSensitivity] = useState('3级');
// 自动关机选项
const autoOffOptions = ['1分钟', '3分钟', '5分钟', '10分钟'];
// 自动锁车选项
const autoLockOptions = ['15秒', '30秒', '1分钟', '3分钟'];
// 震动灵敏度选项
const sensitivityOptions = ['1级', '2级', '3级', '4级', '5级'];
// 处理无感解锁点击
const handleUnlockPress = () => {
navigation.navigate('UnlockSetting' as never); // 替换成实际的路由名
};
return (
<View style={styles.container}>
<View style={styles.card}>
<TouchableOpacity style={styles.cont_li} onPress={handleUnlockPress}>
<View style={styles.cont_li_content}>
<View style={styles.cont_li_top}>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/ue1qF3cHAVlV8Fn5CKV6' }} style={styles.icon_set} />
<Text style={styles.text_set}></Text>
<View style={styles.cont_li_right}>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uPNzghLYVj0uTnfliS0q' }} style={styles.icon_right} />
</View>
</View>
</View>
</TouchableOpacity>
<TouchableOpacity style={styles.cont_li} onPress={() => setShowAutoOffModal(true)}>
<View style={styles.cont_li_content}>
<View style={styles.cont_li_top}>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uY9EQQuqFMzgRrD92ENX' }} style={styles.icon_set} />
<Text style={styles.text_set}></Text>
<View style={styles.cont_li_right}>
<Text style={styles.rightTxt}>{selectedTime}</Text>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uPNzghLYVj0uTnfliS0q' }} style={styles.icon_right} />
</View>
</View>
<Text style={styles.tipTxt}></Text>
</View>
</TouchableOpacity>
<TouchableOpacity style={styles.cont_li} onPress={() => setShowAutoLockModal(true)}>
<View style={styles.cont_li_content}>
<View style={styles.cont_li_top}>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uZ8H5kYVkMM9jVChkHQS' }} style={styles.icon_set} />
<Text style={styles.text_set}></Text>
<View style={styles.cont_li_right}>
<Text style={styles.rightTxt}>{selectedLockTime}</Text>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uPNzghLYVj0uTnfliS0q' }} style={styles.icon_right} />
</View>
</View>
<Text style={styles.tipTxt}></Text>
</View>
</TouchableOpacity>
<TouchableOpacity style={styles.cont_li} onPress={() => setShowSensitivityModal(true)}>
<View style={[styles.cont_li_content, styles.noBotLine]}>
<View style={styles.cont_li_top}>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/ue1qF3cHAVlV8Fn5CKV6' }} style={styles.icon_set} />
<Text style={styles.text_set}></Text>
<View style={styles.cont_li_right}>
<Image source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uPNzghLYVj0uTnfliS0q' }} style={styles.icon_right} />
</View>
</View>
</View>
</TouchableOpacity>
</View>
<Modal
visible={showAutoOffModal}
transparent={true}
animationType="slide"
>
<View style={styles.modalContainer}>
<View style={styles.modalContent}>
<View style={styles.modalHeader}>
<TouchableOpacity
style={styles.headerButton}
onPress={() => setShowAutoOffModal(false)}
>
<Text style={styles.cancelText}></Text>
</TouchableOpacity>
<Text style={styles.modalTitle}></Text>
<TouchableOpacity
style={styles.headerButton}
onPress={() => setShowAutoOffModal(false)}
>
<Text style={styles.confirmText}></Text>
</TouchableOpacity>
</View>
<View style={styles.optionsContainer}>
{autoOffOptions.map((time, index) => (
<TouchableOpacity
key={index}
style={[
styles.optionItem,
selectedTime === time && styles.selectedItemBg
]}
onPress={() => {
setSelectedTime(time);
setShowAutoOffModal(false);
}}
>
<Text style={[
styles.optionText,
selectedTime === time && styles.selectedOption
]}>
{time}
</Text>
</TouchableOpacity>
))}
</View>
</View>
</View>
</Modal>
{/* 修改自动锁车弹窗 */}
<Modal
visible={showAutoLockModal}
transparent={true}
animationType="slide"
>
<View style={styles.modalContainer}>
<View style={styles.modalContent}>
<View style={styles.modalHeader}>
<TouchableOpacity
style={styles.headerButton}
onPress={() => setShowAutoLockModal(false)}
>
<Text style={styles.cancelText}></Text>
</TouchableOpacity>
<Text style={styles.modalTitle}></Text>
<TouchableOpacity
style={styles.headerButton}
onPress={() => setShowAutoLockModal(false)}
>
<Text style={styles.confirmText}></Text>
</TouchableOpacity>
</View>
<View style={styles.optionsContainer}>
{autoLockOptions.map((time, index) => (
<TouchableOpacity
key={index}
style={[
styles.optionItem,
selectedLockTime === time && styles.selectedItemBg
]}
onPress={() => {
setSelectedLockTime(time);
setShowAutoLockModal(false);
}}
>
<Text style={[
styles.optionText,
selectedLockTime === time && styles.selectedOption
]}>
{time}
</Text>
</TouchableOpacity>
))}
</View>
</View>
</View>
</Modal>
{/* 在最后添加震动报警灵敏度选择弹窗 */}
<Modal
visible={showSensitivityModal}
transparent={true}
animationType="slide"
>
<View style={styles.modalContainer}>
<View style={styles.modalContent}>
<View style={styles.modalHeader}>
<TouchableOpacity
style={styles.headerButton}
onPress={() => setShowSensitivityModal(false)}
>
<Text style={styles.cancelText}></Text>
</TouchableOpacity>
<Text style={styles.modalTitle}></Text>
<TouchableOpacity
style={styles.headerButton}
onPress={() => setShowSensitivityModal(false)}
>
<Text style={styles.confirmText}></Text>
</TouchableOpacity>
</View>
<View style={styles.optionsContainer}>
{sensitivityOptions.map((level, index) => (
<TouchableOpacity
key={index}
style={[
styles.optionItem,
selectedSensitivity === level && styles.selectedItemBg
]}
onPress={() => {
setSelectedSensitivity(level);
setShowSensitivityModal(false);
}}
>
<Text style={[
styles.optionText,
selectedSensitivity === level && styles.selectedOption
]}>
{level}
</Text>
</TouchableOpacity>
))}
</View>
</View>
</View>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
headerButton: {
padding: rpx(8),
minWidth: rpx(80),
alignItems: 'center',
},
confirmText: {
fontSize: rpx(28),
color: '#4297F3',
fontWeight: '500',
},
optionsContainer: {
paddingVertical: rpx(20),
},
optionItem: {
padding: rpx(30),
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: '#EBEBEB',
},
selectedItemBg: {
backgroundColor: '#F5F5F5',
},
optionText: {
fontSize: rpx(30),
color: '#333',
},
selectedOption: {
color: '#4297F3',
fontWeight: '500',
},
modalHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: rpx(16),
borderBottomWidth: 1,
borderBottomColor: '#EBEBEB',
},
container: {
flex: 1,
alignItems: 'center',
backgroundColor: '#F3FCFF',
},
card: {
marginTop: rpx(20),
width: rpx(688),
backgroundColor: '#fff',
borderRadius: rpx(30),
padding: rpx(30),
paddingTop: rpx(0),
},
icon_set: {
width: rpx(50),
height: rpx(50),
},
cont_li_right: {
marginLeft: 'auto',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
},
rightTxt: {
fontWeight: '500',
fontSize: rpx(28),
color: '#4297F3',
marginRight: rpx(8),
},
noBotLine: {
borderBottomWidth: 0,
borderBottomColor: 'transparent',
},
tipTxt: {
width: '85%',
marginLeft: rpx(62),
fontSize: rpx(28),
color: '#999',
marginTop: rpx(8),
},
icon_right: {
width: rpx(32),
height: rpx(32),
},
cont_li_content: {
flexDirection: 'column',
},
cont_li: {
paddingTop: rpx(32),
paddingBottom: rpx(32),
borderBottomWidth: rpx(1),
borderBottomColor: '#EBEBEB',
},
text_set: {
fontWeight: '500',
fontSize: rpx(36),
color: '#333',
marginLeft: rpx(10),
},
cont_li_top: {
width: '100%',
flexDirection: 'row',
alignItems: 'center',
marginBottom: rpx(8),
},
modalContainer: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.5)',
justifyContent: 'flex-end',
},
modalContent: {
backgroundColor: '#fff',
borderTopLeftRadius: rpx(20),
borderTopRightRadius: rpx(20),
paddingBottom: rpx(34),
},
modalTitle: {
fontSize: rpx(32),
fontWeight: '500',
color: '#333',
},
cancelButton: {
marginTop: rpx(16),
padding: rpx(30),
alignItems: 'center',
},
cancelText: {
fontSize: rpx(30),
color: '#666',
},
});
export default DeviceSet;