细节修改

This commit is contained in:
tx 2024-12-30 13:47:31 +08:00
parent e54344328f
commit c2e52df560
8 changed files with 145 additions and 173 deletions

View File

@ -58,7 +58,7 @@ const DeviceControl: React.FC<DeviceControlProps> = ({ defaultDevice, onDeviceUp
try { try {
setIsConnecting(true); setIsConnecting(true);
showWithAnimation(); showWithAnimation();
const targetMac = 'FD51BB7A4EE0'; // 暂时使用固定MAC const targetMac = defaultDevice?.mac// 暂时使用固定MAC
console.log(`${isRetry ? '重试' : '开始'}连接设备, 目标MAC:`, targetMac); console.log(`${isRetry ? '重试' : '开始'}连接设备, 目标MAC:`, targetMac);
const initialized = await BluetoothManager.init(); const initialized = await BluetoothManager.init();
@ -165,27 +165,27 @@ const DeviceControl: React.FC<DeviceControlProps> = ({ defaultDevice, onDeviceUp
Alert.alert('提示', '设备数据无效,请确保设备已正确选择'); Alert.alert('提示', '设备数据无效,请确保设备已正确选择');
return; return;
} }
const response = await (status ? const response = await (status ?
apiService.unlocking(defaultDevice.sn) : apiService.unlocking(defaultDevice.sn) :
apiService.lock(defaultDevice.sn)); apiService.lock(defaultDevice.sn));
console.log('开关锁API响应:', { console.log('开关锁API响应:', {
响应数据: response, 响应数据: response,
操作类型: status ? '开锁' : '关锁', 操作类型: status ? '开锁' : '关锁',
时间: new Date().toISOString() 时间: new Date().toISOString()
}); });
if (response.code != 200) { if (response.code != 200) {
retryAttemptRef.current = false; retryAttemptRef.current = false;
const connected = await handleBluetoothConnection(false); const connected = await handleBluetoothConnection(false);
if (connected) { if (connected) {
try { try {
await (status ? await (status ?
BluetoothManager.openDevice() : BluetoothManager.openDevice() :
BluetoothManager.closeDevice()); BluetoothManager.closeDevice());
if (onDeviceUpdate) { if (onDeviceUpdate) {
onDeviceUpdate(); // 蓝牙操作成功后调用更新 onDeviceUpdate(); // 蓝牙操作成功后调用更新
} }
@ -199,7 +199,7 @@ const DeviceControl: React.FC<DeviceControlProps> = ({ defaultDevice, onDeviceUp
} else { } else {
// API 调用成功,直接更新状态 // API 调用成功,直接更新状态
if (onDeviceUpdate) { if (onDeviceUpdate) {
onDeviceUpdate(); onDeviceUpdate();
} }
} }
}; };
@ -213,6 +213,19 @@ const DeviceControl: React.FC<DeviceControlProps> = ({ defaultDevice, onDeviceUp
return 'rgba(255, 69, 58, 0.5)'; return 'rgba(255, 69, 58, 0.5)';
} }
}; };
const calculateTimeRemaining = (expirationTime: string): string => {
const expiration = new Date(expirationTime).getTime();
const now = new Date().getTime();
const diff = expiration - now;
if (diff <= 0) return '已过期';
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
return `${days}${hours}小时${minutes}`;
};
useEffect(() => { useEffect(() => {
const removeListener = BluetoothManager.addConnectionStateListener((state) => { const removeListener = BluetoothManager.addConnectionStateListener((state) => {
@ -253,6 +266,13 @@ const DeviceControl: React.FC<DeviceControlProps> = ({ defaultDevice, onDeviceUp
<View style={styles.yuan}></View> <View style={styles.yuan}></View>
<Text style={styles.txt}> {defaultDevice?.lockStatus == 1 ? '开锁' : '关锁'}</Text> <Text style={styles.txt}> {defaultDevice?.lockStatus == 1 ? '开锁' : '关锁'}</Text>
</View> </View>
{defaultDevice?.type == 2 && defaultDevice?.expirationTime && (
<View style={styles.txtbox1}>
<Text style={styles.txt1}>
{calculateTimeRemaining(defaultDevice.expirationTime)}
</Text>
</View>
)}
</View> </View>
{showBluetoothStatus && ( {showBluetoothStatus && (
<Animated.View <Animated.View
@ -382,6 +402,19 @@ const styles = StyleSheet.create({
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
}, },
txtbox1: {
marginTop: rpx(10),
width: rpx(440),
justifyContent: 'center',
flexDirection: 'row',
alignItems: 'center',
},
txt1: {
marginLeft: rpx(14),
fontSize: rpx(32),
fontWeight: '400',
color: '#3D3D3D',
},
txt: { txt: {
marginLeft: rpx(14), marginLeft: rpx(14),
fontSize: rpx(28), fontSize: rpx(28),

View File

@ -131,17 +131,17 @@ const NormaIndex: React.FC = () => {
}; };
const handleDeviceUpdate = async () => { const handleDeviceUpdate = async () => {
console.log('handleDeviceUpdate'); console.log('handleDeviceUpdate');
const response = await apiService.getDeviceList(); const response = await apiService.getDeviceList();
if (response?.code == 200 && response.data) { if (response?.code == 200 && response.data) {
const defaultDev = response.data.find((device: DeviceType) => device.isDefault == 1); const defaultDev = response.data.find((device: DeviceType) => device.isDefault == 1);
if (defaultDev) { if (defaultDev) {
setDefaultDevice(defaultDev); setDefaultDevice(defaultDev);
// 存储设备 sn 到本地存储 // 存储设备 sn 到本地存储
} }
} }
}; };
@ -149,11 +149,11 @@ const NormaIndex: React.FC = () => {
try { try {
const response = await apiService.getDeviceList(); const response = await apiService.getDeviceList();
console.log(response,'response'); console.log(response, 'response');
if (response?.code === 200 && response.data) { if (response?.code === 200 && response.data) {
const defaultDev = response.data.find((device: DeviceType) => device.isDefault == 1); const defaultDev = response.data.find((device: DeviceType) => device.isDefault == 1);
if (defaultDev) { if (defaultDev) {
console.log(defaultDev.sn,'defaultDev.sn'); console.log(defaultDev.sn, 'defaultDev.sn');
await AsyncStorage.setItem('defaultDeviceSN', defaultDev.sn); await AsyncStorage.setItem('defaultDeviceSN', defaultDev.sn);
// console.log(defaultDev, 'defaultDev'); // console.log(defaultDev, 'defaultDev');
setDefaultDevice(defaultDev); setDefaultDevice(defaultDev);
@ -223,96 +223,39 @@ const NormaIndex: React.FC = () => {
</View> </View>
</View> </View>
</View> </View>
<DeviceControl defaultDevice={defaultDevice} onDeviceUpdate={handleDeviceUpdate}/> <DeviceControl defaultDevice={defaultDevice} onDeviceUpdate={handleDeviceUpdate} />
{/* <View style={styles.infoBox}>
<View style={styles.eleBox}> <TouchableWithoutFeedback>
<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}> <View style={styles.mapWrapper}>
<MiniMap defaultDevice={defaultDevice}/> <MiniMap defaultDevice={defaultDevice} />
</View> </View>
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
{defaultDevice?.type == 1 && (
<>
<TouchableOpacity onPress={toSet}>
<Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/ucYQHQ2Ep4odL8JpbtfT' }}
style={styles.carSet}
/>
</TouchableOpacity>
<View style={styles.otherSet}> <TouchableOpacity onPress={toSet}>
<TouchableOpacity onPress={toShare}> <Image
<Image source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/ucYQHQ2Ep4odL8JpbtfT' }}
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/ulDHhC4MrH3FO0AeTqVg' }} style={styles.carSet}
style={styles.otherImg} />
/> </TouchableOpacity>
</TouchableOpacity>
<Image <View style={styles.otherSet}>
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/u849NsNxdtzxhUkUJnfW' }} <TouchableOpacity onPress={toShare}>
style={styles.otherImg} <Image
/> source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/ulDHhC4MrH3FO0AeTqVg' }}
</View> style={styles.otherImg}
/>
</TouchableOpacity>
<Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/u849NsNxdtzxhUkUJnfW' }}
style={styles.otherImg}
/>
</View>
</>
)}
</View> </View>
</ScrollView> </ScrollView>
</View> </View>

View File

@ -76,7 +76,7 @@ const ProfileScreen = () => {
</View> </View>
<TouchableOpacity <TouchableOpacity
style={styles.headerRight} style={styles.headerRight}
onPress={() => navigation.navigate('QrBind')} onPress={() => navigation.navigate('BindIndex')}
> >
<Image <Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uyb0iFo50FJ0MZg3RKkV' }} source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uyb0iFo50FJ0MZg3RKkV' }}
@ -88,7 +88,7 @@ const ProfileScreen = () => {
{/* 管理与服务区域 */} {/* 管理与服务区域 */}
<Text style={styles.tit}>管理与服务</Text> <Text style={styles.tit}>管理与服务</Text>
<View style={styles.content}> <View style={styles.content}>
<TouchableOpacity {/* <TouchableOpacity
style={styles.item} style={styles.item}
onPress={() => navigation.navigate('OrderList')} onPress={() => navigation.navigate('OrderList')}
> >
@ -97,7 +97,7 @@ const ProfileScreen = () => {
style={styles.itemIcon} style={styles.itemIcon}
/> />
<Text style={styles.itemText}>我的订单</Text> <Text style={styles.itemText}>我的订单</Text>
</TouchableOpacity> </TouchableOpacity> */}
<TouchableOpacity <TouchableOpacity
style={styles.item} style={styles.item}
@ -134,7 +134,7 @@ const ProfileScreen = () => {
</TouchableOpacity> </TouchableOpacity>
)} )}
{userInfo.userType === '02' && ( {/* {userInfo.userType === '02' && (
<TouchableOpacity <TouchableOpacity
style={[styles.item, styles.lastItem]} style={[styles.item, styles.lastItem]}
onPress={() => navigation.navigate('MerchantPortal')} onPress={() => navigation.navigate('MerchantPortal')}
@ -145,7 +145,7 @@ const ProfileScreen = () => {
/> />
<Text style={styles.itemText}>商户端</Text> <Text style={styles.itemText}>商户端</Text>
</TouchableOpacity> </TouchableOpacity>
)} )} */}
</View> </View>
{/* 退出登录按钮 */} {/* 退出登录按钮 */}

View File

@ -19,6 +19,7 @@ const DeviceShare = () => {
const getKeyList = async () => { const getKeyList = async () => {
const response = await apiService.getKeyListByOwnerId(); const response = await apiService.getKeyListByOwnerId();
console.log('response', response);
if (response.code == 200) { if (response.code == 200) {
setKeyList(response.data); setKeyList(response.data);
} }
@ -47,12 +48,13 @@ const DeviceShare = () => {
}; };
const getStatusText = (status: string | number) => { const getStatusText = (status: string | number) => {
console.log('status', status);
switch (status) { switch (status) {
case '0': case '0':
case 0: case 0:
return '待领取'; return '待领取';
case '1': case '2':
case 1: case 2:
return '已领取'; return '已领取';
default: default:
return '未知状态'; return '未知状态';

View File

@ -95,7 +95,7 @@ const KeyDetail = () => {
const handleTimeSelect = async (time: string) => { const handleTimeSelect = async (time: string) => {
setSelectedTime(time); setSelectedTime(time);
if (!keyItem) return; if (!keyItem) return;
let days = 0; let days = 0;
@ -105,14 +105,16 @@ const KeyDetail = () => {
case '30天': days = 30; break; case '30天': days = 30; break;
case '永久': days = 36500; break; case '永久': days = 36500; break;
} }
const baseDate = new Date(keyItem.createTime); // 使用当前时间作为基准
const newExpirationDate = new Date(baseDate.getTime() + days * 24 * 60 * 60 * 1000); const now = new Date();
const newExpirationDate = new Date(now.getTime() + days * 24 * 60 * 60 * 1000);
// 设置为当天的最后一秒
newExpirationDate.setHours(23); newExpirationDate.setHours(23);
newExpirationDate.setMinutes(59); newExpirationDate.setMinutes(59);
newExpirationDate.setSeconds(59); newExpirationDate.setSeconds(59);
const formattedDate = newExpirationDate.getFullYear() + '-' + const formattedDate = newExpirationDate.getFullYear() + '-' +
String(newExpirationDate.getMonth() + 1).padStart(2, '0') + '-' + String(newExpirationDate.getMonth() + 1).padStart(2, '0') + '-' +
String(newExpirationDate.getDate()).padStart(2, '0') + ' ' + String(newExpirationDate.getDate()).padStart(2, '0') + ' ' +
@ -132,7 +134,6 @@ const KeyDetail = () => {
type: 'success', type: 'success',
text1: '更新成功', text1: '更新成功',
}); });
// 更新成功后重新获取钥匙信息
await fetchKeyInfo(); await fetchKeyInfo();
} else { } else {
Toast.show({ Toast.show({
@ -184,6 +185,7 @@ const KeyDetail = () => {
navigation.navigate('ShareQrcode', { QrId: keyId }); navigation.navigate('ShareQrcode', { QrId: keyId });
}; };
return ( return (
<Layout style={styles.container}> <Layout style={styles.container}>
<TopNavigation <TopNavigation
@ -193,36 +195,41 @@ const KeyDetail = () => {
style={styles.topNav} style={styles.topNav}
/> />
<Layout style={styles.header}>
<Avatar
style={styles.avatar}
size="giant"
source={{ uri: keyItem.avatarUrl || 'https://lxnapi.ccttiot.com/bike/img/static/uVnIDwcwQP7oo12PeYVJ' }}
/>
<Text style={styles.name}>{keyItem.shareUserName}</Text>
<Text style={styles.phone}>{keyItem.sharePhone}</Text>
<Text style={styles.expireTime}>{calculateRemainingTime(keyItem.expirationTime)}</Text>
<TouchableOpacity style={styles.eidtBtn} onPress={() => setShowTimeModal(true)}>
<View style={styles.editBtnContent}>
<Text style={styles.editBtnText}></Text>
<Icon name='arrow-ios-forward-outline' style={styles.arrowIcon} />
</View>
</TouchableOpacity>
<TouchableOpacity style={styles.deleteBtn} onPress={showDeleteModal}>
<Text style={styles.deleteText}></Text>
</TouchableOpacity>
</Layout>
<Layout style={styles.actionContainer}> {keyItem && ( // 添加条件渲染检查
<Button <>
appearance='ghost' <Layout style={styles.header}>
style={styles.actionButton} <Avatar
accessoryRight={RightIcon} style={styles.avatar}
onPress={toQrCode} size="giant"
> source={{ uri: keyItem.avatarUrl || 'https://lxnapi.ccttiot.com/bike/img/static/uVnIDwcwQP7oo12PeYVJ' }}
{evaProps => <Text {...evaProps} style={styles.buttonText}></Text>} />
</Button> <Text style={styles.name}>{keyItem.shareUserName}</Text>
</Layout> <Text style={styles.phone}>{keyItem.sharePhone}</Text>
<Text style={styles.expireTime}>{calculateRemainingTime(keyItem.expirationTime)}</Text>
<TouchableOpacity style={styles.eidtBtn} onPress={() => setShowTimeModal(true)}>
<View style={styles.editBtnContent}>
<Text style={styles.editBtnText}></Text>
<Icon name='arrow-ios-forward-outline' style={styles.arrowIcon} />
</View>
</TouchableOpacity>
<TouchableOpacity style={styles.deleteBtn} onPress={showDeleteModal}>
<Text style={styles.deleteText}></Text>
</TouchableOpacity>
</Layout>
<Layout style={styles.actionContainer}>
<Button
appearance='ghost'
style={styles.actionButton}
accessoryRight={RightIcon}
onPress={toQrCode}
>
{evaProps => <Text {...evaProps} style={styles.buttonText}></Text>}
</Button>
</Layout>
</>
)}
{/* 删除确认弹窗 */} {/* 删除确认弹窗 */}
<Modal <Modal

View File

View File

@ -111,7 +111,7 @@ export default function DeviceList() {
</View> </View>
<Text style={styles.model}>{item.model}</Text> <Text style={styles.model}>{item.model}</Text>
<Text style={styles.carNum}>{item.vehicleNum}</Text> <Text style={styles.carNum}>{item.vehicleNum}</Text>
<Text style={styles.status}></Text> <Text style={styles.status}>{item.type == 1 ? '车主' : '临时租赁'}</Text>
</View> </View>
<View style={styles.rightContent}> <View style={styles.rightContent}>
<Image <Image

View File

@ -58,55 +58,40 @@ const DeviceMap = () => {
}; };
const getCurrentLocation = () => { const getCurrentLocation = () => {
// const watchId = Geolocation.watchPosition( // 移除原有的检查,因为 userLocation 可能还未获取到
// (position) => {
// const gcjLocation = transformFromWGSToGCJ(
// position.coords.latitude,
// position.coords.longitude
// );
// setUserLocation(gcjLocation);
// },
// (error) => {
// console.error('位置监听错误:', error);
// },
// {
// enableHighAccuracy: true,
// timeout: 5000,
// maximumAge: 1000,
// distanceFilter: 10
// }
// );
if (!userLocation) {
console.warn('用户位置未获取');
return;
}
try { try {
// 使用 setStatus 方法移动地图 if (mapRef.current && userLocation) {
if (mapRef.current) {
mapRef.current.moveCamera({ mapRef.current.moveCamera({
target: userLocation, target: userLocation,
zoom: 15, zoom: 15,
}, 1000); }, 1000);
} else {
// 如果还没有获取到位置,等待位置信息
console.log('等待获取用户位置...');
} }
} catch (error) { } catch (error) {
console.error('移动地图失败:', error); console.error('移动地图失败:', error);
} }
}; };
useEffect(() => {
getCurrentLocation();
getDeviceLocation();
useEffect(() => {
// 先获取位置,再调用 getCurrentLocation
const watchId = Geolocation.watchPosition( const watchId = Geolocation.watchPosition(
(position) => { (position) => {
const gcjLocation = transformFromWGSToGCJ( const gcjLocation = transformFromWGSToGCJ(
position.coords.latitude, position.coords.latitude,
position.coords.longitude position.coords.longitude
); );
console.log(gcjLocation,'gcjLocation');
setUserLocation(gcjLocation); setUserLocation(gcjLocation);
// 位置获取成功后自动移动地图
if (mapRef.current) {
mapRef.current.moveCamera({
target: gcjLocation,
zoom: 15,
}, 1000);
}
}, },
(error) => { (error) => {
console.error('位置监听错误:', error); console.error('位置监听错误:', error);
@ -118,7 +103,9 @@ const DeviceMap = () => {
distanceFilter: 10 distanceFilter: 10
} }
); );
getDeviceLocation();
return () => { return () => {
Geolocation.clearWatch(watchId); Geolocation.clearWatch(watchId);
}; };