11
This commit is contained in:
parent
a868525bb4
commit
17c5a983e6
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -2302,8 +2302,7 @@
|
|||
"node_modules/@eva-design/eva": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@eva-design/eva/-/eva-2.2.0.tgz",
|
||||
"integrity": "sha512-Wh98ex5cCK+YYSQNpthX1bT4CA3zDRR1WnJv0YlyvULAkmjaEvqtoGMCXzu5DH8v1fGIggu/OpAokLS7UVPe+A==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-Wh98ex5cCK+YYSQNpthX1bT4CA3zDRR1WnJv0YlyvULAkmjaEvqtoGMCXzu5DH8v1fGIggu/OpAokLS7UVPe+A=="
|
||||
},
|
||||
"node_modules/@eva-design/processor": {
|
||||
"version": "2.2.0",
|
||||
|
@ -4226,7 +4225,6 @@
|
|||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/@ui-kitten/components/-/components-5.3.1.tgz",
|
||||
"integrity": "sha512-Oj1WePUQtpNfH7ftXGdkkFVmJI+JcR3cBryPJV0E+JAUdH2dbJ0oG/VA+UAgk27/u0K0OZSUkdMFuGnkDAVuYA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eva-design/dss": "^2.2.0",
|
||||
"@eva-design/processor": "^2.2.0",
|
||||
|
|
|
@ -1,39 +1,69 @@
|
|||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { View, Text, StyleSheet, Animated, Image, PanResponder, Vibration } from 'react-native';
|
||||
import { rpx } from '../utils/rpx';
|
||||
import { View, Text, StyleSheet, Animated, Image, PanResponder, Vibration, Dimensions } from 'react-native';
|
||||
|
||||
const Slider: React.FC = () => {
|
||||
const translateX = useRef(new Animated.Value(0)).current;
|
||||
interface SliderProps {
|
||||
lockStatus?: number;
|
||||
onStatusChange?: (status: boolean) => void;
|
||||
}
|
||||
|
||||
// 添加 rpx 计算函数
|
||||
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
||||
const rpx = (px: number) => {
|
||||
return (SCREEN_WIDTH / 750) * px;
|
||||
};
|
||||
|
||||
const Slider: React.FC<SliderProps> = ({ lockStatus = 1, onStatusChange }) => {
|
||||
const translateX = useRef(new Animated.Value(lockStatus === 0 ? rpx(180) : 0)).current;
|
||||
const maxWidth = rpx(180);
|
||||
const buttonWidth = rpx(86);
|
||||
const [iconOpacity, setIconOpacity] = useState(1);
|
||||
const [isRight, setIsRight] = useState(false);
|
||||
const [isRight, setIsRight] = useState(lockStatus === 0);
|
||||
const hasVibratedRef = useRef(false);
|
||||
const offsetRef = useRef(0);
|
||||
|
||||
// 添加监听器
|
||||
useEffect(() => {
|
||||
const id = translateX.addListener(({ value }) => {
|
||||
if (value >= maxWidth * 0.95) {
|
||||
setIsRight(true);
|
||||
} else if (value <= maxWidth * 0.05) {
|
||||
setIsRight(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => translateX.removeListener(id);
|
||||
}, []);
|
||||
|
||||
const iconOpacity = translateX.interpolate({
|
||||
inputRange: [maxWidth * 0.9, maxWidth],
|
||||
outputRange: [1, 0],
|
||||
extrapolate: 'clamp'
|
||||
});
|
||||
|
||||
const backgroundWidth = translateX.interpolate({
|
||||
inputRange: [0, maxWidth],
|
||||
outputRange: [buttonWidth - rpx(2), maxWidth + buttonWidth - rpx(2)],
|
||||
extrapolate: 'clamp'
|
||||
});
|
||||
|
||||
const panResponder = useRef(
|
||||
PanResponder.create({
|
||||
onMoveShouldSetPanResponder: (_, gestureState) => {
|
||||
return Math.abs(gestureState.dx) > 10;
|
||||
},
|
||||
onStartShouldSetPanResponder: () => true,
|
||||
onMoveShouldSetPanResponder: () => true,
|
||||
onPanResponderGrant: () => {
|
||||
hasVibratedRef.current = false;
|
||||
offsetRef.current = translateX._value;
|
||||
},
|
||||
onPanResponderMove: (_, gestureState) => {
|
||||
const currentValue = translateX.__getValue();
|
||||
let newValue;
|
||||
|
||||
if (isRight) {
|
||||
newValue = maxWidth + gestureState.dx;
|
||||
newValue = offsetRef.current + gestureState.dx;
|
||||
} else {
|
||||
newValue = gestureState.dx;
|
||||
newValue = offsetRef.current + gestureState.dx;
|
||||
}
|
||||
|
||||
newValue = Math.max(0, Math.min(maxWidth, newValue));
|
||||
translateX.setValue(newValue);
|
||||
|
||||
// 根据滑动位置更新状态
|
||||
setIsRight(newValue > maxWidth / 2);
|
||||
|
||||
if (!hasVibratedRef.current) {
|
||||
if (newValue >= maxWidth * 0.95 || newValue <= maxWidth * 0.05) {
|
||||
Vibration.vibrate(20);
|
||||
|
@ -42,98 +72,86 @@ const Slider: React.FC = () => {
|
|||
}
|
||||
},
|
||||
onPanResponderRelease: (_, gestureState) => {
|
||||
const currentValue = translateX.__getValue();
|
||||
const currentValue = translateX._value;
|
||||
let toValue;
|
||||
let finalIsRight;
|
||||
|
||||
if (isRight) {
|
||||
if (gestureState.dx < -maxWidth / 4) {
|
||||
toValue = 0;
|
||||
setIsRight(false);
|
||||
finalIsRight = false;
|
||||
} else {
|
||||
toValue = maxWidth;
|
||||
setIsRight(true);
|
||||
finalIsRight = true;
|
||||
}
|
||||
} else {
|
||||
if (gestureState.dx > maxWidth / 4) {
|
||||
toValue = maxWidth;
|
||||
setIsRight(true);
|
||||
finalIsRight = true;
|
||||
} else {
|
||||
toValue = 0;
|
||||
setIsRight(false);
|
||||
finalIsRight = false;
|
||||
}
|
||||
}
|
||||
|
||||
Animated.spring(translateX, {
|
||||
Animated.timing(translateX, {
|
||||
toValue,
|
||||
tension: 50,
|
||||
friction: 7,
|
||||
duration: 100,
|
||||
useNativeDriver: false,
|
||||
}).start(() => {
|
||||
hasVibratedRef.current = false;
|
||||
onStatusChange?.(finalIsRight);
|
||||
});
|
||||
},
|
||||
})
|
||||
).current;
|
||||
|
||||
const backgroundColorWidth = translateX.interpolate({
|
||||
inputRange: [0, maxWidth],
|
||||
outputRange: [0, maxWidth + buttonWidth],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const listenerId = translateX.addListener(({ value }) => {
|
||||
const newOpacity = value < maxWidth ? (1 - value / maxWidth) : 0;
|
||||
setIconOpacity(newOpacity);
|
||||
});
|
||||
|
||||
return () => {
|
||||
translateX.removeListener(listenerId);
|
||||
};
|
||||
}, [translateX]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.car_Opne_box}>
|
||||
{/* 默认背景 */}
|
||||
<View style={styles.defaultBackground} />
|
||||
|
||||
{/* 蓝色滑动背景 */}
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.background,
|
||||
{
|
||||
width: backgroundColorWidth,
|
||||
backgroundColor: '#4297F3',
|
||||
zIndex: 1,
|
||||
width: backgroundWidth,
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<View style={[styles.defaultBackground]} />
|
||||
|
||||
{/* 滑块 */}
|
||||
<Animated.View
|
||||
{...panResponder.panHandlers}
|
||||
style={[
|
||||
styles.imageContainer,
|
||||
{
|
||||
transform: [{
|
||||
translateX
|
||||
}],
|
||||
zIndex: 2,
|
||||
transform: [{ translateX }]
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uY9tYXXZztuE1VTLDl5y' }}
|
||||
style={styles.imageContent}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</Animated.View>
|
||||
|
||||
{/* 箭头图标 */}
|
||||
<Animated.Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uEJob4XbADaL9ohOTVTL' }}
|
||||
style={[
|
||||
styles.icon,
|
||||
{ opacity: iconOpacity, zIndex: 2 }
|
||||
{ opacity: iconOpacity }
|
||||
]}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.text}>{isRight ? '左滑关闭' : '右滑启动'}</Text>
|
||||
<Text style={styles.text}>
|
||||
{isRight ? '左滑关闭' : '右滑启动'}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
@ -153,17 +171,23 @@ const styles = StyleSheet.create({
|
|||
overflow: 'hidden',
|
||||
},
|
||||
background: {
|
||||
height: '100%',
|
||||
borderRadius: rpx(45),
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
height: '100%',
|
||||
backgroundColor: '#4297F3',
|
||||
borderRadius: rpx(45),
|
||||
zIndex: 1,
|
||||
},
|
||||
defaultBackground: {
|
||||
width: rpx(268),
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '#EBEBEB',
|
||||
borderRadius: rpx(45),
|
||||
zIndex: 0,
|
||||
},
|
||||
imageContainer: {
|
||||
width: rpx(86),
|
||||
|
@ -195,4 +219,4 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
});
|
||||
|
||||
export default Slider;
|
||||
export default React.memo(Slider);
|
|
@ -14,7 +14,7 @@ import AddShare from '../views/device/AddShare';
|
|||
import ShareQrcode from '../views/device/shareQrcode';
|
||||
import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
|
||||
import { HomeStackParamList } from './types';
|
||||
|
||||
import TestBule from '../views/device/test_bule';
|
||||
const Stack = createStackNavigator<HomeStackParamList>();
|
||||
|
||||
const createScreenOptions = (title: string): StackNavigationOptions => {
|
||||
|
@ -50,10 +50,19 @@ export default function HomeStackNavigator({ navigation, route }: Props) {
|
|||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="TestBule"
|
||||
component={TestBule}
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="DeviceList"
|
||||
component={DeviceList}
|
||||
options={createScreenOptions('设备列表')}
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="BindIndex"
|
||||
|
|
|
@ -7,7 +7,6 @@ import { BottomNavigation, BottomNavigationTab, Icon } from '@ui-kitten/componen
|
|||
import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
|
||||
|
||||
// 导入页面组件
|
||||
import LoginScreen from '../views/Login/Login';
|
||||
import ResetPasswordScreen from '../views/Login/ResetPassword';
|
||||
|
@ -19,8 +18,8 @@ import { RootStackParamList, MainTabParamList } from './types';
|
|||
const Stack = createStackNavigator<RootStackParamList>();
|
||||
const Tab = createBottomTabNavigator<MainTabParamList>();
|
||||
|
||||
// 定义需要显示绑定底部栏的路由名称
|
||||
const bindNavBarRoutes = ['BindIndex', 'SnBind', 'ConfirmBind'];
|
||||
// 定义需要显示底部导航栏的路由名称
|
||||
const showTabBarRoutes = ['爱车', '个人中心'];
|
||||
|
||||
const MainNavigator = () => {
|
||||
return (
|
||||
|
@ -30,8 +29,8 @@ const MainNavigator = () => {
|
|||
tabBarHideOnKeyboard: true,
|
||||
}}
|
||||
tabBar={props => {
|
||||
const routeName = getFocusedRouteNameFromRoute(props.state.routes[props.state.index]) ?? 'Home';
|
||||
if (bindNavBarRoutes.includes(routeName)) {
|
||||
const routeName = getFocusedRouteNameFromRoute(props.state.routes[props.state.index]) ?? '爱车';
|
||||
if (!showTabBarRoutes.includes(routeName)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
|
|
|
@ -27,6 +27,7 @@ export type HomeStackParamList = {
|
|||
DeviceShare: undefined;
|
||||
AddShare: undefined;
|
||||
ShareQrcode: undefined;
|
||||
TestBule: undefined;
|
||||
};
|
||||
|
||||
// 导航属性类型
|
||||
|
|
|
@ -26,7 +26,7 @@ const api = axios.create({
|
|||
api.interceptors.request.use(
|
||||
async (config) => {
|
||||
if (__DEV__) {
|
||||
console.log('[Request]:', config.url, config.data);
|
||||
// console.log('[Request]:', config.url, config.data);
|
||||
}
|
||||
|
||||
const token = await auth.getToken();
|
||||
|
@ -42,7 +42,7 @@ api.interceptors.request.use(
|
|||
api.interceptors.response.use(
|
||||
response => {
|
||||
if (__DEV__) {
|
||||
console.log('[Response]:', response.config.url, response.data);
|
||||
// console.log('[Response]:', response.config.url, response.data);
|
||||
}
|
||||
|
||||
const { code, msg, data } = response.data;
|
||||
|
@ -100,5 +100,7 @@ export const apiService = {
|
|||
bindSn: (sn: string) =>
|
||||
api.post('/appVerify/userBandDevice?sn=' + sn),
|
||||
|
||||
getDeviceList: () => api.get('/appVerify/getDeviceListByToken'),
|
||||
getDeviceList: () => api.get('/appVerify/getDeviceListByMerchantToken'),
|
||||
|
||||
toggleDefault: (sn: string) => api.put(`/appVerify/toggleDefault?sn=${sn}`),
|
||||
};
|
|
@ -37,7 +37,8 @@ const HomeScreen = () => {
|
|||
const fetchDeviceList = async () => {
|
||||
try {
|
||||
const response = await apiService.getDeviceList();
|
||||
if (response && response.data && response.data.length > 1) {
|
||||
console.log(response.data.length,'response');
|
||||
if (response && response.data && response.data.length > 0) {
|
||||
setHasMultipleDevices(true);
|
||||
} else {
|
||||
setHasMultipleDevices(false);
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
Animated,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
TouchableWithoutFeedback ,
|
||||
TouchableWithoutFeedback,
|
||||
StatusBar
|
||||
} from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
@ -17,15 +17,26 @@ import http from '../../utils/http';
|
|||
import { rpx } from '../../utils/rpx';
|
||||
import Slider from '../../components/slider';
|
||||
import MiniMap from './MiniMap';
|
||||
|
||||
import { apiService } from '../../utils/api';
|
||||
// 定义导航参数类型
|
||||
type RootStackParamList = {
|
||||
Home: undefined;
|
||||
DeviceList: undefined;
|
||||
DeviceMap: undefined;
|
||||
DeviceSet: undefined;
|
||||
DeviceShare: undefined;
|
||||
// 添加其他页面的路由参数类型
|
||||
Home: undefined;
|
||||
DeviceList: undefined;
|
||||
DeviceMap: undefined;
|
||||
DeviceSet: undefined;
|
||||
DeviceShare: undefined;
|
||||
TestBule: undefined;
|
||||
// 添加其他页面的路由参数类型
|
||||
};
|
||||
type DeviceType = {
|
||||
id: number;
|
||||
sn: string;
|
||||
vehicleNum: string;
|
||||
model: string;
|
||||
remainingPower: number;
|
||||
remainingMileage: number;
|
||||
isDefault: number;
|
||||
lockStatus: number;
|
||||
};
|
||||
|
||||
// 定义导航类型
|
||||
|
@ -37,7 +48,7 @@ const NormaIndex: React.FC = () => {
|
|||
const translateX = useRef(new Animated.Value(0)).current;
|
||||
const bgColor = useRef(new Animated.Value(0)).current;
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
|
||||
const [defaultDevice, setDefaultDevice] = useState<DeviceType | null>(null);
|
||||
const handlePress = () => {
|
||||
navigation.navigate('DeviceList');
|
||||
};
|
||||
|
@ -51,7 +62,9 @@ const NormaIndex: React.FC = () => {
|
|||
const toShare = () => {
|
||||
navigation.navigate('DeviceShare');
|
||||
};
|
||||
|
||||
const toTestBule = () => {
|
||||
navigation.navigate('TestBule');
|
||||
};
|
||||
const panResponder = useRef(
|
||||
PanResponder.create({
|
||||
onStartShouldSetPanResponder: () => true,
|
||||
|
@ -102,154 +115,185 @@ const NormaIndex: React.FC = () => {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
http.get('/app/article/9')
|
||||
.then(response => {
|
||||
// setData(response);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('请求错误', error);
|
||||
});
|
||||
fetchDeviceList();
|
||||
}, []);
|
||||
const getPowerColor = (power: number): string => {
|
||||
if (power >= 60) {
|
||||
return 'rgba(89, 202, 112, 0.5)'; // 绿色
|
||||
} else if (power >= 20) {
|
||||
return 'rgba(255, 149, 0, 0.5)'; // 橙色
|
||||
} else {
|
||||
return 'rgba(255, 69, 58, 0.5)'; // 红色
|
||||
}
|
||||
};
|
||||
const fetchDeviceList = async () => {
|
||||
try {
|
||||
|
||||
const response = await apiService.getDeviceList();
|
||||
|
||||
if (response?.code === 200 && response.data) {
|
||||
const defaultDev = response.data.find((device: DeviceType) => device.isDefault == 1);
|
||||
if (defaultDev) {
|
||||
console.log(defaultDev, 'defaultDev');
|
||||
setDefaultDevice(defaultDev);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取设备列表失败:', error);
|
||||
} finally {
|
||||
|
||||
}
|
||||
};
|
||||
return (
|
||||
<View style={styles.pageContainer}>
|
||||
<StatusBar
|
||||
backgroundColor="#F3FCFF" // 设置为白色
|
||||
barStyle="dark-content" // 状态栏文字为深色
|
||||
translucent={false} // 不透明
|
||||
/>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
scrollEventThrottle={16}
|
||||
nestedScrollEnabled={true}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity onPress={handlePress}>
|
||||
<View style={styles.titBox}>
|
||||
<Text
|
||||
style={styles.titTxts}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
朵VFLU-13762
|
||||
</Text>
|
||||
<Image
|
||||
source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uJRIitv0Yn5K7CEVe9qd' }}
|
||||
style={{ width: rpx(24), height: rpx(14), marginLeft: rpx(8) }}
|
||||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.KmBox}>
|
||||
<View style={styles.KmLi}>
|
||||
<View style={styles.KmLi_top}>
|
||||
<Text style={styles.titTxt}>110</Text>
|
||||
<Text style={styles.KmLi_tits}>km</Text>
|
||||
</View>
|
||||
<View style={styles.KmLi_bot}>
|
||||
<StatusBar
|
||||
backgroundColor="#F3FCFF" // 设置为白色
|
||||
barStyle="dark-content" // 状态栏文字为深色
|
||||
translucent={false} // 不透明
|
||||
/>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
scrollEventThrottle={16}
|
||||
nestedScrollEnabled={true}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity onPress={handlePress}>
|
||||
<View style={styles.titBox}>
|
||||
<Text
|
||||
style={styles.titTxts}
|
||||
numberOfLines={2}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
{defaultDevice?.model || '未选择车辆'}
|
||||
</Text>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uDg93bCzlPFiFBtS71Al' }}
|
||||
style={{ width: rpx(32), height: rpx(32) }}
|
||||
source={{ uri: 'https://api.ccttiot.com/smartmeter/img/static/uJRIitv0Yn5K7CEVe9qd' }}
|
||||
style={styles.arrowIcon}
|
||||
/>
|
||||
<Text style={styles.KmLi_tits1}>剩余里程</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.KmLi}>
|
||||
<View style={styles.KmLi_top}>
|
||||
<Text style={styles.titTxt}>110</Text>
|
||||
<Text style={styles.KmLi_tits}>km</Text>
|
||||
</View>
|
||||
<View style={styles.KmLi_bot}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uDg93bCzlPFiFBtS71Al' }}
|
||||
style={{ width: rpx(32), height: rpx(32) }}
|
||||
/>
|
||||
<Text style={styles.KmLi_tits1}>剩余里程</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.infoBox}>
|
||||
<View style={styles.eleBox}>
|
||||
<View style={styles.eleType}>
|
||||
<Text style={styles.eleTypeTxt}>90%</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.carBox}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uVnIDwcwQP7oo12PeYVJ' }}
|
||||
style={{ width: rpx(440), height: rpx(340) }}
|
||||
/>
|
||||
<View style={styles.txtbox}>
|
||||
<View style={styles.yuan}></View>
|
||||
<Text style={styles.txt}>当前车辆状态:锁车</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.Bind_type}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uQdjlI2DLfXmABfynycn' }}
|
||||
style={{ width: rpx(32), height: rpx(32) }}
|
||||
/>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uLizPj6UxdjBsiqhxZB8' }}
|
||||
style={{ width: rpx(60), height: rpx(60), marginLeft: 'auto' }}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.car_stause_box}>
|
||||
<View style={styles.car_stause_li}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uro1vIU1WydjNWgi7PUg' }}
|
||||
style={{ width: rpx(90), height: rpx(90), marginLeft: rpx(18) }}
|
||||
/>
|
||||
<Text style={styles.stauseText}>鸣笛寻车</Text>
|
||||
</View>
|
||||
<Slider
|
||||
onComplete={() => {
|
||||
console.log('滑动完成');
|
||||
// 处理滑动完成后的逻辑
|
||||
}}
|
||||
/>
|
||||
<View style={styles.car_stause_li}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uVpJNxwWXlyXt4IdHQoe' }}
|
||||
style={{ width: rpx(90), height: rpx(90), marginLeft: rpx(18) }}
|
||||
/>
|
||||
<Text style={styles.stauseText}>警报已开</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<TouchableWithoutFeedback >
|
||||
|
||||
<View style={styles.mapWrapper}>
|
||||
<MiniMap />
|
||||
</View>
|
||||
|
||||
</TouchableWithoutFeedback>
|
||||
|
||||
<TouchableOpacity onPress={toSet}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/ucYQHQ2Ep4odL8JpbtfT' }}
|
||||
style={styles.carSet}
|
||||
/>
|
||||
</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 style={styles.KmBox}>
|
||||
<View style={styles.KmLi}>
|
||||
<View style={styles.KmLi_top}>
|
||||
<Text style={styles.titTxt}>{defaultDevice?.remainingMileage}</Text>
|
||||
<Text style={styles.KmLi_tits}>km</Text>
|
||||
</View>
|
||||
<View style={styles.KmLi_bot}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uDg93bCzlPFiFBtS71Al' }}
|
||||
style={{ width: rpx(32), height: rpx(32) }}
|
||||
/>
|
||||
<Text style={styles.KmLi_tits1}>剩余里程</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.KmLi}>
|
||||
<View style={styles.KmLi_top}>
|
||||
<Text style={styles.titTxt}>110</Text>
|
||||
<Text style={styles.KmLi_tits}>km</Text>
|
||||
</View>
|
||||
<View style={styles.KmLi_bot}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uDg93bCzlPFiFBtS71Al' }}
|
||||
style={{ width: rpx(32), height: rpx(32) }}
|
||||
/>
|
||||
<Text style={styles.KmLi_tits1}>剩余里程</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.infoBox}>
|
||||
<View style={styles.eleBox}>
|
||||
<View style={[
|
||||
styles.eleType,
|
||||
{
|
||||
height: `${defaultDevice?.remainingPower || 0}%`,
|
||||
backgroundColor: getPowerColor(defaultDevice?.remainingPower || 0)
|
||||
}
|
||||
]}>
|
||||
<Text style={styles.eleTypeTxt}>{defaultDevice?.remainingPower || 0}%</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.carBox}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uVnIDwcwQP7oo12PeYVJ' }}
|
||||
style={{ width: rpx(440), height: rpx(340) }}
|
||||
/>
|
||||
<View style={styles.txtbox}>
|
||||
<View style={styles.yuan}></View>
|
||||
<Text style={styles.txt}>当前车辆状态: {defaultDevice?.lockStatus == 1 ? '开锁' : '关锁'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableOpacity onPress={toTestBule}>
|
||||
<View style={styles.Bind_type}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uQdjlI2DLfXmABfynycn' }}
|
||||
style={{ width: rpx(32), height: rpx(32) }}
|
||||
/>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uLizPj6UxdjBsiqhxZB8' }}
|
||||
style={{ width: rpx(60), height: rpx(60), marginLeft: 'auto' }}
|
||||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
</View>
|
||||
|
||||
<View style={styles.car_stause_box}>
|
||||
<View style={styles.car_stause_li}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uro1vIU1WydjNWgi7PUg' }}
|
||||
style={{ width: rpx(90), height: rpx(90), marginLeft: rpx(18) }}
|
||||
/>
|
||||
<Text style={styles.stauseText}>鸣笛寻车</Text>
|
||||
</View>
|
||||
<Slider
|
||||
lockStatus={defaultDevice?.lockStatus}
|
||||
onStatusChange={(status) => {
|
||||
// 处理状态改变
|
||||
console.log('Lock status changed:', status);
|
||||
}}
|
||||
/>
|
||||
<View style={styles.car_stause_li}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uVpJNxwWXlyXt4IdHQoe' }}
|
||||
style={{ width: rpx(90), height: rpx(90), marginLeft: rpx(18) }}
|
||||
/>
|
||||
<Text style={styles.stauseText}>警报已开</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<TouchableWithoutFeedback >
|
||||
|
||||
<View style={styles.mapWrapper}>
|
||||
<MiniMap />
|
||||
</View>
|
||||
|
||||
</TouchableWithoutFeedback>
|
||||
|
||||
<TouchableOpacity onPress={toSet}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/ucYQHQ2Ep4odL8JpbtfT' }}
|
||||
style={styles.carSet}
|
||||
/>
|
||||
</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>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
@ -355,14 +399,6 @@ const styles = StyleSheet.create({
|
|||
borderRadius: rpx(11),
|
||||
backgroundColor: 'rgba(255, 130, 130, 1)',
|
||||
},
|
||||
eleTypeTxt: {
|
||||
position: 'absolute',
|
||||
top: rpx(16),
|
||||
left: rpx(6),
|
||||
fontSize: rpx(32),
|
||||
color: '#3D3D3D',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
eleBox: {
|
||||
paddingTop: rpx(14),
|
||||
paddingRight: rpx(6),
|
||||
|
@ -371,29 +407,59 @@ const styles = StyleSheet.create({
|
|||
width: rpx(86),
|
||||
height: rpx(166),
|
||||
borderRadius: rpx(16),
|
||||
backgroundColor: '#fff'
|
||||
backgroundColor: '#FFFFFF',
|
||||
justifyContent: 'flex-end',
|
||||
// 添加阴影效果
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 4, // Android 阴影
|
||||
// 添加边框使其更加醒目
|
||||
borderWidth: 1,
|
||||
borderColor: '#E0E0E0',
|
||||
},
|
||||
eleType: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
minHeight: rpx(30),
|
||||
borderRadius: rpx(16),
|
||||
backgroundColor: 'rgba(89,202,112,0.5)'
|
||||
position: 'relative',
|
||||
|
||||
},
|
||||
eleTypeTxt: {
|
||||
position: 'absolute',
|
||||
top: rpx(-40),
|
||||
width: '100%',
|
||||
textAlign: 'center',
|
||||
fontSize: rpx(32),
|
||||
color: '#3D3D3D',
|
||||
// fontWeight: 'bold',
|
||||
// 为文字添加阴影效果使其更清晰
|
||||
textShadowColor: 'rgba(255, 255, 255, 0.5)',
|
||||
textShadowOffset: { width: 0, height: 1 },
|
||||
textShadowRadius: 2
|
||||
},
|
||||
titBox: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexWrap: 'nowrap',
|
||||
maxWidth: rpx(600), // 增加最大宽度
|
||||
},
|
||||
titTxts: {
|
||||
maxWidth: rpx(480),
|
||||
width: rpx(250),
|
||||
fontSize: rpx(48),
|
||||
fontWeight: '500',
|
||||
color: '#3D3D3D',
|
||||
// flex: 2, // 添加 flex 属性
|
||||
},
|
||||
titBox: {
|
||||
// width: rpx(750),
|
||||
flexDirection: 'row', // 添加这行
|
||||
alignItems: 'center', // 添加这行
|
||||
// // 移除这些不需要的属性
|
||||
display: 'flex',
|
||||
flexWrap: 'nowrap',
|
||||
// // flex: 1,
|
||||
// justifyContent: 'center',
|
||||
arrowIcon: {
|
||||
width: rpx(24),
|
||||
height: rpx(14),
|
||||
marginLeft: rpx(8)
|
||||
},
|
||||
KmLi_bot: {
|
||||
marginTop: rpx(4),
|
||||
|
|
|
@ -1,63 +1,237 @@
|
|||
import React from 'react';
|
||||
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
||||
import { auth } from '../utils/auth';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
Image,
|
||||
View,
|
||||
TouchableOpacity,
|
||||
ImageBackground
|
||||
} from 'react-native';
|
||||
import { Text } from '@ui-kitten/components';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useAuth } from '../context/AuthContext'; // 添加这行
|
||||
import axios from 'axios';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { rpx } from '../utils/rpx';
|
||||
import { apiService } from '../utils/api';
|
||||
|
||||
const ProfileScreen = () => {
|
||||
const navigation = useNavigation();
|
||||
const { setIsLoggedIn } = useAuth(); // 添加这行
|
||||
const { setIsLoggedIn } = useAuth();
|
||||
const [userInfo, setUserInfo] = useState({});
|
||||
|
||||
const handleLogout = async () => {
|
||||
useEffect(() => {
|
||||
getUserInfo();
|
||||
}, []);
|
||||
|
||||
const formatName = (name) => {
|
||||
if (!name) return '';
|
||||
return name.charAt(0) + '*'.repeat(name.length - 1);
|
||||
};
|
||||
|
||||
const getUserInfo = async () => {
|
||||
try {
|
||||
await auth.removeToken(); // 清除本地存储的 token
|
||||
setIsLoggedIn(false); // 更新登录状态
|
||||
// 不需要手动导航,因为 AppNavigator 会根据 isLoggedIn 自动处理导航
|
||||
const response = await apiService.getUserInfo();
|
||||
if (response.code === 200) {
|
||||
setUserInfo(response.user);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('退出登录失败:', error);
|
||||
console.error('获取用户信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>个人中心</Text>
|
||||
<ImageBackground
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uYRs7Cv2Pbp95w3KjGO3' }}
|
||||
style={styles.background}
|
||||
>
|
||||
<ScrollView style={styles.scrollView}>
|
||||
<View style={styles.page}>
|
||||
{/* 头部区域 */}
|
||||
<View style={styles.header}>
|
||||
<View style={styles.headerLeft}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/u3giTY4VkWYpnGWRuFHF' }}
|
||||
style={styles.avatar}
|
||||
/>
|
||||
<View style={styles.userinfo}>
|
||||
<Text style={styles.username}>
|
||||
{formatName(userInfo.realName)}
|
||||
</Text>
|
||||
<Text style={styles.userphone}>
|
||||
{userInfo.phonenumber}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={styles.headerRight}
|
||||
onPress={() => navigation.navigate('QrBind')}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uyb0iFo50FJ0MZg3RKkV' }}
|
||||
style={styles.code}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{/* <Text style={styles.tit}>我的设备</Text> */}
|
||||
{/* 管理与服务区域 */}
|
||||
<Text style={styles.tit}>管理与服务</Text>
|
||||
<View style={styles.content}>
|
||||
<TouchableOpacity
|
||||
style={styles.item}
|
||||
onPress={() => navigation.navigate('OrderList')}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uVEbrRDbZXvELwK73KAi' }}
|
||||
style={styles.itemIcon}
|
||||
/>
|
||||
<Text style={styles.itemText}>我的订单</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.logoutButton}
|
||||
onPress={handleLogout}
|
||||
>
|
||||
<Text style={styles.logoutText}>退出登录</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={styles.item}
|
||||
onPress={() => navigation.navigate('HelpCenter')}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uO1ju1OpuA5jjMR7bLYh' }}
|
||||
style={styles.itemIcon}
|
||||
/>
|
||||
<Text style={styles.itemText}>帮助和客服</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.item}
|
||||
onPress={() => navigation.navigate('Feedback')}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uHNMNwTRw89NhtgZUNXK' }}
|
||||
style={styles.itemIcon}
|
||||
/>
|
||||
<Text style={styles.itemText}>意见和反馈</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
{userInfo.isAuthentication === false && (
|
||||
<TouchableOpacity
|
||||
style={styles.item}
|
||||
onPress={() => navigation.navigate('IdVerification')}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/unrltNyYYRXUutaqtuJY' }}
|
||||
style={styles.itemIcon}
|
||||
/>
|
||||
<Text style={styles.itemText}>实名认证</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
{userInfo.userType === '02' && (
|
||||
<TouchableOpacity
|
||||
style={[styles.item, styles.lastItem]}
|
||||
onPress={() => navigation.navigate('MerchantPortal')}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/u1SsqJYSQ8jTK9PkhFtF' }}
|
||||
style={styles.itemIcon}
|
||||
/>
|
||||
<Text style={styles.itemText}>商户端</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* TabBar */}
|
||||
{/* <View style={styles.tabBarContainer}>
|
||||
<TabBar indexs={1} />
|
||||
</View> */}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</ImageBackground>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
background: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F3FCFF',
|
||||
width: '100%',
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
marginTop: 20,
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
},
|
||||
logoutButton: {
|
||||
position: 'absolute',
|
||||
bottom: 40,
|
||||
width: '90%',
|
||||
height: 44,
|
||||
backgroundColor: '#FF4D4F',
|
||||
borderRadius: 22,
|
||||
justifyContent: 'center',
|
||||
page: {
|
||||
flex: 1,
|
||||
paddingTop: rpx(200),
|
||||
paddingHorizontal: rpx(32),
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
logoutText: {
|
||||
color: '#FFFFFF',
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
headerLeft: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
avatar: {
|
||||
width: rpx(108),
|
||||
height: rpx(108),
|
||||
borderRadius: rpx(54),
|
||||
},
|
||||
userinfo: {
|
||||
marginLeft: rpx(40),
|
||||
},
|
||||
username: {
|
||||
fontWeight: '700',
|
||||
fontSize: rpx(36),
|
||||
color: '#3D3D3D',
|
||||
},
|
||||
userphone: {
|
||||
fontWeight: '400',
|
||||
fontSize: rpx(28),
|
||||
color: '#3D3D3D',
|
||||
},
|
||||
headerRight: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
code: {
|
||||
width: rpx(40),
|
||||
height: rpx(40),
|
||||
},
|
||||
tit: {
|
||||
marginTop: rpx(40),
|
||||
fontWeight: '700',
|
||||
fontSize: rpx(36),
|
||||
color: '#3D3D3D',
|
||||
},
|
||||
content: {
|
||||
marginTop: rpx(24),
|
||||
width: rpx(688),
|
||||
padding: rpx(42),
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: rpx(30),
|
||||
},
|
||||
item: {
|
||||
width: '100%',
|
||||
paddingVertical: rpx(34),
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#D8D8D8',
|
||||
},
|
||||
lastItem: {
|
||||
borderBottomWidth: 0,
|
||||
},
|
||||
itemIcon: {
|
||||
width: rpx(38),
|
||||
height: rpx(38),
|
||||
marginTop: rpx(4),
|
||||
marginRight: rpx(34),
|
||||
},
|
||||
itemText: {
|
||||
fontWeight: '400',
|
||||
fontSize: rpx(32),
|
||||
color: '#3D3D3D',
|
||||
},
|
||||
tabBarContainer: {
|
||||
marginLeft: rpx(-32),
|
||||
}
|
||||
});
|
||||
|
||||
export default ProfileScreen;
|
|
@ -1,225 +1,311 @@
|
|||
import React, { useState } from 'react';
|
||||
import { View, Text, StyleSheet, ScrollView, Image, TouchableOpacity } from 'react-native';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { View, StyleSheet, ScrollView, Image, TouchableOpacity, SafeAreaView } from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import {
|
||||
TopNavigation,
|
||||
TopNavigationAction,
|
||||
Icon,
|
||||
Text,
|
||||
Layout,
|
||||
Spinner
|
||||
} from '@ui-kitten/components';
|
||||
import { rpx } from '../../utils/rpx';
|
||||
import { apiService } from '../../utils/api';
|
||||
|
||||
type CarItem = {
|
||||
id: number;
|
||||
title: string;
|
||||
sn: string;
|
||||
vehicleNum: string;
|
||||
model: string;
|
||||
status: string;
|
||||
isShared: boolean;
|
||||
remainingPower: number;
|
||||
remainingMileage: number;
|
||||
isDefault: number;
|
||||
};
|
||||
|
||||
const BackIcon = (props: any) => (
|
||||
<Icon {...props} name='arrow-back-outline'/>
|
||||
);
|
||||
|
||||
export default function DeviceList() {
|
||||
const navigation = useNavigation();
|
||||
const [selectedItem, setSelectedItem] = useState<number | null>(null);
|
||||
const handlePress = () => {
|
||||
const [devices, setDevices] = useState<CarItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
navigation.navigate('BindIndex' as never);
|
||||
// console.log(navigation);
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchDeviceList();
|
||||
}, []);
|
||||
|
||||
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 fetchDeviceList = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await apiService.getDeviceList();
|
||||
if (response?.code === 200 && response.data) {
|
||||
setDevices(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取设备列表失败:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const renderCarItem = (item: CarItem) => (
|
||||
<View key={item.id} style={[styles.carItem, item.id !== 1 && styles.marginTop]}>
|
||||
const handlePress = () => {
|
||||
navigation.navigate('BindIndex' as never);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
navigation.goBack();
|
||||
};
|
||||
|
||||
const selectCar = async (item: CarItem) => {
|
||||
try {
|
||||
const response = await apiService.toggleDefault(item.sn);
|
||||
if (response.code === 200) {
|
||||
console.log(response,'response');
|
||||
fetchDeviceList();
|
||||
// TODO: 添加成功提示
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('切换默认车辆失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const renderBackAction = () => (
|
||||
<TopNavigationAction icon={BackIcon} onPress={handleCancel}/>
|
||||
);
|
||||
|
||||
const renderPowerIcons = (power: number) => {
|
||||
const icons = [];
|
||||
const fullPowerIcon = 'https://lxnapi.ccttiot.com/bike/img/static/uhxmJlps8lrRRTmBIFpl';
|
||||
const emptyPowerIcon = 'https://lxnapi.ccttiot.com/bike/img/static/u1CcbtQydd107cOUEZ1l';
|
||||
const powerPerGrid = power / 10;
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
icons.push(
|
||||
<Image
|
||||
key={`power-${i}`}
|
||||
source={{ uri: i < Math.floor(powerPerGrid) ? fullPowerIcon : emptyPowerIcon }}
|
||||
style={styles.powerIcon}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return icons;
|
||||
};
|
||||
|
||||
const renderCarItem = (item: CarItem, index: number) => (
|
||||
<TouchableOpacity
|
||||
key={`device-${item.sn}-${index}`}
|
||||
style={[styles.carItem, index !== 0 && styles.marginTop]}
|
||||
onPress={() => selectCar(item)}
|
||||
>
|
||||
<View style={styles.leftContent}>
|
||||
<Text style={styles.title}>{item.title}</Text>
|
||||
<Text style={styles.subTitle}>{item.model}</Text>
|
||||
<Text style={styles.status}>{item.status}</Text>
|
||||
<Text style={[styles.tag, item.isShared && styles.tagShare]}>
|
||||
{item.isShared ? '临时共享' : '车主'}
|
||||
</Text>
|
||||
<View style={styles.carInfo}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uUIunSd0CSU3ovogLJHk' }}
|
||||
style={styles.titleIcon}
|
||||
/>
|
||||
<View style={styles.power}>
|
||||
{renderPowerIcons(item.remainingPower)}
|
||||
</View>
|
||||
<Text style={styles.mileage}>{item.remainingMileage}KM</Text>
|
||||
</View>
|
||||
<Text style={styles.model}>车型:{item.model}</Text>
|
||||
<Text style={styles.carNum}>{item.vehicleNum}</Text>
|
||||
<Text style={styles.status}>租赁中</Text>
|
||||
</View>
|
||||
<View style={styles.rightContent}>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uVnIDwcwQP7oo12PeYVJ' }}
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uB1F5aibooguILH8uB4F' }}
|
||||
style={styles.carImage}
|
||||
/>
|
||||
<View style={styles.checkboxWrapper}>
|
||||
<Text style={styles.checkboxText}>选择车辆</Text>
|
||||
<TouchableOpacity
|
||||
onPress={() => toggleSelect(item.id)}
|
||||
style={styles.checkboxContainer}
|
||||
>
|
||||
<Image
|
||||
source={{
|
||||
uri: selectedItem === item.id
|
||||
? 'https://api.ccttiot.com/smartmeter/img/static/u4alnzo5240dlVnSQK0r'
|
||||
: 'https://api.ccttiot.com/smartmeter/img/static/uj2puOsyrcZY4ygZL6GX'
|
||||
}}
|
||||
style={styles.checkboxImage}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<Image
|
||||
source={{
|
||||
uri: item.isDefault == 1
|
||||
? 'https://lxnapi.ccttiot.com/bike/img/static/uj7bE7GMcCm6g2igeo7k'
|
||||
: 'https://lxnapi.ccttiot.com/bike/img/static/uN3QtzNuM2CMTLUm3joR'
|
||||
}}
|
||||
style={styles.checkboxImage}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView style={styles.scrollContent}>
|
||||
{carList.map(item => renderCarItem(item))}
|
||||
</ScrollView>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<TopNavigation
|
||||
title="选择车辆"
|
||||
alignment="center"
|
||||
accessoryLeft={renderBackAction}
|
||||
style={styles.topNav}
|
||||
/>
|
||||
|
||||
<View style={styles.bottomButtons}>
|
||||
<TouchableOpacity onPress={handlePress} style={styles.addButton}>
|
||||
<Text style={styles.addButtonText}>+ 添加车辆</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={handleCancel} style={styles.cancelButton}>
|
||||
<Text style={styles.cancelButtonText}>取消</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
{loading ? (
|
||||
<View style={styles.loadingContainer}>
|
||||
<Spinner size="large"/>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<ScrollView style={styles.scrollContent}>
|
||||
{devices.length > 0 ? (
|
||||
devices.map((device, index) => renderCarItem(device, index))
|
||||
) : (
|
||||
<View style={styles.noDataContainer}>
|
||||
<Text category="s1">暂无设备数据</Text>
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
|
||||
<View style={styles.bottomButtons}>
|
||||
<TouchableOpacity onPress={handlePress} style={styles.addButton}>
|
||||
<Text style={styles.addButtonText}>+ 添加车辆</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={handleCancel} style={styles.cancelButton}>
|
||||
<Text style={styles.cancelButtonText}>取消</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#F3FCFF',
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
topNav: {
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
loadingContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
noDataContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingTop: rpx(100),
|
||||
},
|
||||
scrollContent: {
|
||||
marginBottom: rpx(300),
|
||||
flex: 1,
|
||||
paddingHorizontal: rpx(20),
|
||||
// marginBottom: rpx(160),
|
||||
paddingHorizontal: rpx(40),
|
||||
},
|
||||
carItem: {
|
||||
flexDirection: 'row',
|
||||
backgroundColor: '#EEF2FD',
|
||||
borderRadius: rpx(20),
|
||||
padding: rpx(30),
|
||||
borderRadius: rpx(28),
|
||||
padding: rpx(48),
|
||||
marginTop: rpx(20),
|
||||
alignItems: 'center',
|
||||
},
|
||||
marginTop: {
|
||||
marginTop: rpx(20),
|
||||
},
|
||||
leftContent: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
rightContent: {
|
||||
carInfo: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginLeft: rpx(20),
|
||||
},
|
||||
title: {
|
||||
fontSize: rpx(40),
|
||||
fontWeight: '500',
|
||||
color: '#3D3D3D',
|
||||
marginBottom: rpx(16),
|
||||
titleIcon: {
|
||||
width: rpx(30),
|
||||
height: rpx(30),
|
||||
marginRight: rpx(12),
|
||||
marginBottom: rpx(8),
|
||||
},
|
||||
subTitle: {
|
||||
fontWeight: '500',
|
||||
fontSize: rpx(24),
|
||||
power: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginRight: rpx(12),
|
||||
},
|
||||
powerIcon: {
|
||||
width: rpx(18),
|
||||
height: rpx(36),
|
||||
},
|
||||
mileage: {
|
||||
fontSize: rpx(36),
|
||||
fontWeight: '700',
|
||||
color: '#3D3D3D',
|
||||
},
|
||||
model: {
|
||||
marginTop: rpx(15),
|
||||
fontSize: rpx(32),
|
||||
fontWeight: '700',
|
||||
color: '#3D3D3D',
|
||||
},
|
||||
carNum: {
|
||||
marginTop: rpx(15),
|
||||
fontSize: rpx(32),
|
||||
fontWeight: '700',
|
||||
color: '#3D3D3D',
|
||||
marginBottom: rpx(12),
|
||||
},
|
||||
status: {
|
||||
fontSize: rpx(24),
|
||||
color: '#666',
|
||||
marginBottom: rpx(16),
|
||||
},
|
||||
tag: {
|
||||
alignSelf: 'flex-start',
|
||||
paddingVertical: rpx(6),
|
||||
paddingHorizontal: rpx(30),
|
||||
marginTop: rpx(20),
|
||||
paddingVertical: rpx(8),
|
||||
paddingHorizontal: rpx(19),
|
||||
backgroundColor: '#D2E8FF',
|
||||
borderRadius: rpx(29),
|
||||
fontSize: rpx(24),
|
||||
fontSize: rpx(28),
|
||||
color: '#4297F3',
|
||||
marginTop: rpx(10),
|
||||
fontWeight: '500'
|
||||
alignSelf: 'flex-start',
|
||||
fontWeight: '500',
|
||||
},
|
||||
tagShare: {
|
||||
color: '#FF9500',
|
||||
backgroundColor: '#FFEEDE'
|
||||
rightContent: {
|
||||
marginLeft: 'auto',
|
||||
width: rpx(200),
|
||||
alignItems: 'center',
|
||||
},
|
||||
carImage: {
|
||||
width: rpx(232),
|
||||
height: rpx(180),
|
||||
marginBottom: rpx(16),
|
||||
},
|
||||
checkboxContainer: {
|
||||
width: rpx(44),
|
||||
height: rpx(44),
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: rpx(212),
|
||||
height: rpx(164),
|
||||
},
|
||||
checkboxWrapper: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: rpx(22),
|
||||
},
|
||||
checkboxText: {
|
||||
fontSize: rpx(32),
|
||||
checkboxText: {
|
||||
fontSize: rpx(30),
|
||||
color: '#3D3D3D',
|
||||
marginRight: rpx(8),
|
||||
marginRight: rpx(12),
|
||||
},
|
||||
checkboxImage: {
|
||||
width: rpx(44),
|
||||
height: rpx(44),
|
||||
resizeMode: 'contain',
|
||||
width: rpx(29),
|
||||
height: rpx(29),
|
||||
},
|
||||
bottomButtons: {
|
||||
position: 'absolute',
|
||||
bottom: rpx(30),
|
||||
left: 0,
|
||||
right: 0,
|
||||
padding: rpx(20),
|
||||
backgroundColor: '#F3FCFF',
|
||||
padding: rpx(38),
|
||||
},
|
||||
addButton: {
|
||||
backgroundColor: '#4297F3',
|
||||
height: rpx(92),
|
||||
borderRadius: rpx(20),
|
||||
backgroundColor: '#4297F3',
|
||||
borderRadius: rpx(16),
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: rpx(20),
|
||||
},
|
||||
addButtonText: {
|
||||
fontSize: rpx(40),
|
||||
fontWeight: '500',
|
||||
color: '#FFFFFF',
|
||||
fontSize: rpx(32),
|
||||
},
|
||||
cancelButton: {
|
||||
backgroundColor: '#fff',
|
||||
height: rpx(92),
|
||||
borderRadius: rpx(20),
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: rpx(16),
|
||||
borderWidth: rpx(2),
|
||||
borderColor: '#4297F3',
|
||||
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
cancelButtonText: {
|
||||
fontSize: rpx(40),
|
||||
fontWeight: '500',
|
||||
color: '#4297F3',
|
||||
fontSize: rpx(32),
|
||||
},
|
||||
});
|
46
src/views/device/test_bule.tsx
Normal file
46
src/views/device/test_bule.tsx
Normal file
|
@ -0,0 +1,46 @@
|
|||
import React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { TopNavigation, TopNavigationAction, Icon, Text } from '@ui-kitten/components';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
const BackIcon = (props) => (
|
||||
<Icon {...props} name='arrow-back' />
|
||||
);
|
||||
|
||||
const TestBule = () => {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const navigateBack = () => {
|
||||
navigation.goBack();
|
||||
};
|
||||
|
||||
const BackAction = () => (
|
||||
<TopNavigationAction icon={BackIcon} onPress={navigateBack} />
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TopNavigation
|
||||
title="蓝牙测试"
|
||||
alignment="center"
|
||||
accessoryLeft={BackAction}
|
||||
/>
|
||||
<View style={styles.content}>
|
||||
<Text>TestBule</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default TestBule;
|
Loading…
Reference in New Issue
Block a user