diff --git a/App.tsx b/App.tsx index f68aea3..1d01c78 100644 --- a/App.tsx +++ b/App.tsx @@ -8,6 +8,7 @@ import 'react-native-gesture-handler'; import { enableScreens } from 'react-native-screens'; import HomeStackNavigator from './src/views/HomeStackNavigator'; import { getFocusedRouteNameFromRoute } from '@react-navigation/native'; +import { rpx } from './src/utils/rpx'; enableScreens(); @@ -35,7 +36,7 @@ function App() { tabBarIcon: ({ color, size }) => ( ), @@ -44,7 +45,7 @@ function App() { } })} /> - ), }} - /> + /> */} =10" } }, + "node_modules/@react-native-community/geolocation": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/@react-native-community/geolocation/-/geolocation-3.4.0.tgz", + "integrity": "sha512-bzZH89/cwmpkPMKKveoC72C4JH0yF4St5Ceg/ZM9pA1SqX9MlRIrIrrOGZ/+yi++xAvFDiYfihtn9TvXWU9/rA==", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/@react-native/assets-registry": { "version": "0.74.87", "license": "MIT", @@ -9041,6 +9055,11 @@ } } }, + "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", diff --git a/package.json b/package.json index ad7d9d0..a1d2fb9 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,14 @@ "test": "jest" }, "dependencies": { + "@react-native-community/geolocation": "^3.4.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", diff --git a/src/views/Home/slider.tsx b/src/components/slider.tsx similarity index 98% rename from src/views/Home/slider.tsx rename to src/components/slider.tsx index d39ebfd..87f93fc 100644 --- a/src/views/Home/slider.tsx +++ b/src/components/slider.tsx @@ -1,6 +1,7 @@ import React, { useRef, useEffect, useState } from 'react'; import { View, Text, StyleSheet, Animated, Image, PanResponder } from 'react-native'; -import { rpx } from '../../utils/rpx'; // 根据需要调整路径 +import { rpx } from '../utils/rpx'; // 根据需要调整路径 + const Slider: React.FC = () => { const translateX = useRef(new Animated.Value(0)).current; diff --git a/src/views/Home/HomeScreen.tsx b/src/views/Home/HomeScreen.tsx index 6f567f0..b68cced 100644 --- a/src/views/Home/HomeScreen.tsx +++ b/src/views/Home/HomeScreen.tsx @@ -8,7 +8,7 @@ import NormaIndex from "./NormaIndex"; const HomeScreen: React.FC = () => { const [count, setCount] = useState(0); const [data, setData] = useState(null); - const [pageIndex, setPageIndex] = useState(1); + const [pageIndex, setPageIndex] = useState(0); useEffect(() => { // 发起 GET 请求 diff --git a/src/views/Home/MiniMap.tsx b/src/views/Home/MiniMap.tsx index fcd58ea..511e343 100644 --- a/src/views/Home/MiniMap.tsx +++ b/src/views/Home/MiniMap.tsx @@ -1,10 +1,22 @@ import React, { useEffect } from 'react'; -import { View, StyleSheet, Platform,Text,Image } from 'react-native'; +import { View, StyleSheet, Platform, Text, Image, TouchableOpacity } from 'react-native'; import { AMapSdk, MapView, Marker, MapType } from 'react-native-amap3d'; import LinearGradient from 'react-native-linear-gradient'; import { rpx } from '../../utils/rpx'; +import { useNavigation } from '@react-navigation/native'; +import { StackNavigationProp } from '@react-navigation/stack'; + +type RootStackParamList = { + Home: undefined; + DeviceList: undefined; + DeviceMap: undefined; +}; + +type NavigationProp = StackNavigationProp; const MiniMap = () => { + const navigation = useNavigation(); + useEffect(() => { AMapSdk.init( Platform.select({ @@ -13,6 +25,10 @@ const MiniMap = () => { ); }, []); + const handleMapPress = () => { + navigation.navigate('DeviceMap'); + }; + const latitude = 26.95500669; const longitude = 120.32736769; const imageUrl = "https://lxnapi.ccttiot.com/bike/img/static/uRx1B8B8acbquF2TO7Ry"; @@ -23,6 +39,8 @@ const MiniMap = () => { style={styles.map} mapType={MapType.Standard} zoomControlsEnabled={false} + scrollEnabled={true} + zoomEnabled={true} initialCameraPosition={{ target: { latitude, @@ -39,19 +57,24 @@ const MiniMap = () => { icon={{ uri: imageUrl }} /> + - + @@ -66,12 +89,12 @@ const MiniMap = () => { - - + ); @@ -109,7 +132,6 @@ const styles = StyleSheet.create({ fontWeight: '400', fontSize: rpx(24), color: '#3D3D3D', - lineHeight: rpx(32), }, cont_right: { width: rpx(48), @@ -121,7 +143,6 @@ const styles = StyleSheet.create({ width: rpx(32), height: rpx(32), }, - container: { marginTop: rpx(44), width: rpx(688), @@ -135,7 +156,7 @@ const styles = StyleSheet.create({ left: rpx(0), top: rpx(0), width: rpx(688), - height: rpx(112), + height: rpx(160), zIndex: 999, elevation: 3, }, diff --git a/src/views/Home/NormaIndex.tsx b/src/views/Home/NormaIndex.tsx index b2003a1..618d70e 100644 --- a/src/views/Home/NormaIndex.tsx +++ b/src/views/Home/NormaIndex.tsx @@ -1,24 +1,49 @@ import React, { useEffect, useState, useRef } from 'react'; -import { View, Text, StyleSheet, Image, PanResponder, Animated, ScrollView, TouchableOpacity } from 'react-native'; -import http from '../../utils/http'; // 调整路径 -import { rpx } from '../../utils/rpx'; // Adjust the path as necessary -import Slider from "./slider"; -import MiniMap from './MiniMap'; +import { + View, + Text, + StyleSheet, + Image, + PanResponder, + Animated, + ScrollView, + TouchableOpacity, + TouchableWithoutFeedback , + StatusBar +} from 'react-native'; import { useNavigation } from '@react-navigation/native'; -const NormaIndex: React.FC = () => { +import { StackNavigationProp } from '@react-navigation/stack'; +import http from '../../utils/http'; +import { rpx } from '../../utils/rpx'; +import Slider from '../../components/slider'; +import MiniMap from './MiniMap'; +// 定义导航参数类型 +type RootStackParamList = { + Home: undefined; + DeviceList: undefined; + DeviceMap: undefined; + // 添加其他页面的路由参数类型 +}; + +// 定义导航类型 +type NavigationProp = StackNavigationProp; + +const NormaIndex: React.FC = () => { const [count, setCount] = useState(0); const [data, setData] = useState(null); const translateX = useRef(new Animated.Value(0)).current; const bgColor = useRef(new Animated.Value(0)).current; - - const navigation = useNavigation(); + const navigation = useNavigation(); const handlePress = () => { - navigation.navigate('DeviceList'); - // console.log(navigation); }; + + const toMap = () => { + navigation.navigate('DeviceMap'); + }; + const panResponder = useRef( PanResponder.create({ onStartShouldSetPanResponder: () => true, @@ -69,10 +94,9 @@ const NormaIndex: React.FC = () => { }); useEffect(() => { - // 发起 GET 请求 - http.get('/app/article/9') // 替换为你的 API endpoint + http.get('/app/article/9') .then(response => { - // setData(response); // Uncomment when API response is needed + // setData(response); }) .catch(error => { console.error('请求错误', error); @@ -80,11 +104,34 @@ const NormaIndex: React.FC = () => { }, []); return ( - + + + - - 朵VFLU-13762 - + + + + 朵VFLU-13762 + + + + + @@ -92,8 +139,10 @@ const NormaIndex: React.FC = () => { km - + 剩余里程 @@ -103,12 +152,15 @@ const NormaIndex: React.FC = () => { km - + 剩余里程 + @@ -116,54 +168,94 @@ const NormaIndex: React.FC = () => { - + 当前车辆状态:锁车 - - + + + - - 鸣笛寻车 + + 鸣笛寻车 - - 警报已开 + + 警报已开 - + + + + + + + + + - + - - + + - + ); }; + const styles = StyleSheet.create({ - scrollContent: { - flexGrow: 1, // This ensures the content expands to fill the available space + pageContainer: { + flex: 1, + backgroundColor: '#F3FCFF', // 设置整个页面的背景色为白色 }, + scrollContent: { + flexGrow: 1, + }, + mapWrapper: { + marginVertical: rpx(20), + }, + stauseText: { + fontSize: rpx(32), + color: '#3D3D3D', + textAlign: 'center', + marginTop: rpx(24) + }, + otherSet: { marginTop: rpx(30), // display:f,\ @@ -271,10 +363,22 @@ const styles = StyleSheet.create({ borderRadius: rpx(16), backgroundColor: 'rgba(89,202,112,0.5)' }, - titBox: { - + titTxts: { + maxWidth: rpx(480), + fontSize: rpx(48), + fontWeight: '500', + color: '#3D3D3D', + }, + titBox: { + // width: rpx(750), + flexDirection: 'row', // 添加这行 + alignItems: 'center', // 添加这行 + // // 移除这些不需要的属性 + display: 'flex', + flexWrap: 'nowrap', + // // flex: 1, + // justifyContent: 'center', }, - KmLi_bot: { marginTop: rpx(4), flexDirection: 'row', // 保持在一行中 diff --git a/src/views/HomeStackNavigator.tsx b/src/views/HomeStackNavigator.tsx index c10d0c0..4583eb9 100644 --- a/src/views/HomeStackNavigator.tsx +++ b/src/views/HomeStackNavigator.tsx @@ -6,6 +6,7 @@ import DeviceList from './device/deviceList'; import BindIndex from './bind/bind_index'; import SnBind from './bind/sn_bind'; import ConfirmBind from './bind/ConfirmBind'; +import DeviceMap from './device/deviceMap'; // import BleBind from './bind/ble_bind'; import { getFocusedRouteNameFromRoute } from '@react-navigation/native'; @@ -15,6 +16,7 @@ type RootStackParamList = { BindIndex: undefined; SnBind: undefined; ConfirmBind: undefined; + DeviceMap: undefined; }; const Stack = createStackNavigator(); @@ -42,7 +44,7 @@ type Props = { export default function HomeStackNavigator({ navigation, route }: Props) { React.useEffect(() => { const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home'; - const hideTabBarRoutes = ['DeviceList', 'BindIndex', 'SnBind', 'BleBind', 'ConfirmBind']; // 添加新的路由名 + const hideTabBarRoutes = ['DeviceList', 'BindIndex', 'SnBind', 'BleBind', 'ConfirmBind', 'DeviceMap']; // 添加新的路由名 const shouldHideTabBar = hideTabBarRoutes.includes(routeName); navigation.getParent()?.setOptions({ @@ -80,6 +82,11 @@ export default function HomeStackNavigator({ navigation, route }: Props) { component={ConfirmBind} options={createScreenOptions('确认绑定')} /> + ); } \ No newline at end of file diff --git a/src/views/ProfileScreen.jsx b/src/views/ProfileScreen.jsx index 2826aa0..a248ab5 100644 --- a/src/views/ProfileScreen.jsx +++ b/src/views/ProfileScreen.jsx @@ -2,10 +2,10 @@ import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; -const ShopScreen = () => { +const ProfileScreen = () => { return ( - Shop Screen + ); }; @@ -15,10 +15,11 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: 'center', alignItems: 'center', + backgroundColor: '#F3FCFF', }, }); -export default ShopScreen; +export default ProfileScreen; diff --git a/src/views/ShopScreen.jsx b/src/views/ShopScreen.jsx index b0f716f..ffbd5e4 100644 --- a/src/views/ShopScreen.jsx +++ b/src/views/ShopScreen.jsx @@ -2,10 +2,10 @@ import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; -const ProfileScreen = () => { +const ShopScreen = () => { return ( - Profile Screen + ShopScreen Screen ); }; @@ -18,4 +18,4 @@ const styles = StyleSheet.create({ }, }); -export default ProfileScreen; +export default ShopScreen; diff --git a/src/views/device/deviceList.tsx b/src/views/device/deviceList.tsx index f45ecb9..cc58302 100644 --- a/src/views/device/deviceList.tsx +++ b/src/views/device/deviceList.tsx @@ -1,15 +1,101 @@ -import React, { useEffect } from 'react'; -import { View, Text, StyleSheet, Button } from 'react-native'; +import React, { useState } from 'react'; +import { View, Text, StyleSheet, ScrollView, Image, TouchableOpacity } from 'react-native'; import { useNavigation } from '@react-navigation/native'; +import { rpx } from '../../utils/rpx'; + +type CarItem = { + id: number; + title: string; + model: string; + status: string; + isShared: boolean; +}; export default function DeviceList() { const navigation = useNavigation(); + const [selectedItem, setSelectedItem] = useState(null); + const handlePress = () => { + + navigation.navigate('BindIndex'); + // console.log(navigation); +}; - +const handleCancel = () => { + navigation.goBack(); +}; + + // 示例数据保持不变 + const carList: CarItem[] = [ + + { + id: 1, + title: '备注1376', + model: '车型:飞过的魔毯不须归于温暖', + status: '车辆登入X小时出租', + isShared: false, + }, + { + id: 2, + title: '备注1376', + model: '车型:飞过的魔毯不须归于温暖', + status: '车辆登入X小时出租', + isShared: true, + }, + ]; + + const toggleSelect = (id: number) => { + setSelectedItem(prev => prev === id ? null : id); + }; + + const renderCarItem = (item: CarItem) => ( + + + {item.title} + {item.model} + {item.status} + + {item.isShared ? '临时共享' : '车主'} + + + + + + 选择车辆 + toggleSelect(item.id)} + style={styles.checkboxContainer} + > + + + + + + ); return ( - + + {carList.map(item => renderCarItem(item))} + + + + + + 添加车辆 + + + 取消 + + ); } @@ -17,7 +103,123 @@ export default function DeviceList() { const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: '#fff', + backgroundColor: '#F3FCFF', }, - -}); + scrollContent: { + marginBottom: rpx(300), + flex: 1, + paddingHorizontal: rpx(20), + // marginBottom: rpx(160), + }, + carItem: { + flexDirection: 'row', + backgroundColor: '#EEF2FD', + borderRadius: rpx(20), + padding: rpx(30), + marginTop: rpx(20), + alignItems: 'center', + }, + marginTop: { + marginTop: rpx(20), + }, + leftContent: { + flex: 1, + justifyContent: 'center', + }, + rightContent: { + alignItems: 'center', + justifyContent: 'center', + marginLeft: rpx(20), + }, + title: { + fontSize: rpx(40), + fontWeight: '500', + color: '#3D3D3D', + marginBottom: rpx(16), + }, + subTitle: { + fontWeight: '500', + fontSize: rpx(24), + color: '#3D3D3D', + marginBottom: rpx(12), + }, + status: { + fontSize: rpx(24), + color: '#666', + marginBottom: rpx(16), + }, + tag: { + alignSelf: 'flex-start', + paddingVertical: rpx(6), + paddingHorizontal: rpx(30), + backgroundColor: '#D2E8FF', + borderRadius: rpx(29), + fontSize: rpx(24), + color: '#4297F3', + marginTop: rpx(10), + fontWeight: '500' + }, + tagShare: { + color: '#FF9500', + backgroundColor: '#FFEEDE' + }, + carImage: { + width: rpx(232), + height: rpx(180), + marginBottom: rpx(16), + }, + checkboxContainer: { + width: rpx(44), + height: rpx(44), + justifyContent: 'center', + alignItems: 'center', + }, + checkboxWrapper: { + flexDirection: 'row', + alignItems: 'center', + }, + checkboxText: { + fontSize: rpx(32), + color: '#3D3D3D', + marginRight: rpx(8), + }, + checkboxImage: { + width: rpx(44), + height: rpx(44), + resizeMode: 'contain', + }, + bottomButtons: { + position: 'absolute', + bottom: rpx(30), + left: 0, + right: 0, + padding: rpx(20), + backgroundColor: '#F3FCFF', + }, + addButton: { + backgroundColor: '#4297F3', + height: rpx(92), + borderRadius: rpx(20), + justifyContent: 'center', + alignItems: 'center', + marginBottom: rpx(20), + }, + addButtonText: { + color: '#FFFFFF', + fontSize: rpx(32), + }, + cancelButton: { + backgroundColor: '#fff', + height: rpx(92), + borderRadius: rpx(20), + borderWidth: rpx(2), + borderColor: '#4297F3', + + justifyContent: 'center', + alignItems: 'center', + }, + cancelButtonText: { + color: '#4297F3', + fontSize: rpx(32), + }, +}); \ No newline at end of file diff --git a/src/views/device/deviceMap.tsx b/src/views/device/deviceMap.tsx new file mode 100644 index 0000000..75a2a59 --- /dev/null +++ b/src/views/device/deviceMap.tsx @@ -0,0 +1,230 @@ +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 { rpx } from '../../utils/rpx'; +import { useNavigation } from '@react-navigation/native'; + +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, + }); + + 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, + // } + ); + } + + initGeolocation(); + + // 组件卸载时清理 + return () => { + Geolocation.stop(); + }; + }, []); + + // 跳转到高德地图 + 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`, + }); + + const fallbackUrl = `https://uri.amap.com/navigation?to=${longitude},${latitude},目的地&mode=car&coordinate=gaode`; + + try { + // 检查是否安装了高德地图 + const supported = await Linking.canOpenURL(url); + + if (supported) { + await Linking.openURL(url); + } else { + // 如果没有安装高德地图,则打开网页版 + await Linking.openURL(fallbackUrl); + } + } catch (error) { + console.error('无法打开高德地图:', error); + // 打开网页版作为后备方案 + await Linking.openURL(fallbackUrl); + } + }; + + return ( + + + + {/* 设备位置标记 */} + + + {/* 用户位置标记 */} + {userLocation.latitude !== 0 && ( + + )} + + + + + 福建省宁德市福鼎市海滨路200号靠近福鼎第四中学 + + + + + 导航到车辆 + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#FFFFFF', + }, + map: { + flex: 1, + width: '100%', + }, + header: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + height: rpx(88), + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: rpx(32), + backgroundColor: 'transparent', + }, + backButton: { + width: rpx(44), + height: rpx(44), + justifyContent: 'center', + alignItems: 'center', + }, + backIcon: { + width: rpx(32), + height: rpx(32), + }, + timeContainer: { + flex: 1, + alignItems: 'center', + }, + timeText: { + fontSize: rpx(28), + color: '#333333', + }, + menuButton: { + width: rpx(44), + height: rpx(44), + justifyContent: 'center', + alignItems: 'center', + }, + menuIcon: { + width: rpx(32), + height: rpx(32), + }, + bottomCard: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + backgroundColor: '#FFFFFF', + borderTopLeftRadius: rpx(24), + borderTopRightRadius: rpx(24), + padding: rpx(32), + }, + addressInfo: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: rpx(32), + }, + addressText: { + flex: 1, + fontSize: rpx(28), + color: '#333333', + marginRight: rpx(16), + }, + voiceIcon: { + width: rpx(44), + height: rpx(44), + }, + navigationButton: { + height: rpx(96), + backgroundColor: '#2089FF', + borderRadius: rpx(48), + justifyContent: 'center', + alignItems: 'center', + }, + buttonText: { + fontSize: rpx(32), + color: '#FFFFFF', + fontWeight: '500', + }, +}); + +export default DeviceMap; \ No newline at end of file