二维码保存
This commit is contained in:
parent
e184055bee
commit
a0f361a9ff
17487
package-lock.json
generated
17487
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -1,8 +1,9 @@
|
|||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { View, Text, Image, TouchableOpacity, StyleSheet } from 'react-native';
|
||||
import { useNavigation ,useFocusEffect} from '@react-navigation/native';
|
||||
import { useNavigation, useFocusEffect } from '@react-navigation/native';
|
||||
import { apiService } from '../../utils/api';
|
||||
import { rpx } from '../../utils/rpx';
|
||||
|
||||
interface KeyItem {
|
||||
id: string;
|
||||
avatarUrl: string;
|
||||
|
@ -18,23 +19,20 @@ const DeviceShare = () => {
|
|||
|
||||
const getKeyList = async () => {
|
||||
const response = await apiService.getKeyListByOwnerId();
|
||||
// console.log(response);
|
||||
if (response.code == 200) {
|
||||
setKeyList(response.data);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const calculateRemainingTime = (expirationTime: string): string => {
|
||||
const now = new Date();
|
||||
const expiration = new Date(expirationTime);
|
||||
const diffTime = expiration.getTime() - now.getTime();
|
||||
|
||||
// 如果已过期
|
||||
if (diffTime <= 0) {
|
||||
return '已过期';
|
||||
}
|
||||
|
||||
// 计算剩余天数、小时、分钟
|
||||
const days = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
||||
const hours = Math.floor((diffTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((diffTime % (1000 * 60 * 60)) / (1000 * 60));
|
||||
|
@ -47,6 +45,7 @@ const DeviceShare = () => {
|
|||
return `${minutes}分钟`;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (status: string | number) => {
|
||||
switch (status) {
|
||||
case '0':
|
||||
|
@ -59,6 +58,7 @@ const DeviceShare = () => {
|
|||
return '未知状态';
|
||||
}
|
||||
};
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
getKeyList();
|
||||
|
@ -73,13 +73,12 @@ const DeviceShare = () => {
|
|||
|
||||
{keyList.map((key) => (
|
||||
<TouchableOpacity
|
||||
key={key.id}
|
||||
onPress={() => {
|
||||
navigation.navigate('ShareDetailScreen', { keyItem: key });
|
||||
}}
|
||||
>
|
||||
|
||||
<View key={key.id} style={styles.card}>
|
||||
key={key.id}
|
||||
onPress={() => {
|
||||
navigation.navigate('ShareDetailScreen', { keyItem: key });
|
||||
}}
|
||||
>
|
||||
<View style={styles.card}>
|
||||
<View style={styles.cardTop}>
|
||||
<Image
|
||||
source={{ uri: key.avatarUrl || 'https://lxnapi.ccttiot.com/bike/img/static/uVnIDwcwQP7oo12PeYVJ' }}
|
||||
|
@ -107,8 +106,8 @@ const DeviceShare = () => {
|
|||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
))}
|
||||
|
||||
{keyList.length < 3 && (
|
||||
<TouchableOpacity onPress={() => {
|
||||
navigation.navigate('AddShare' as never);
|
||||
|
@ -124,21 +123,20 @@ const DeviceShare = () => {
|
|||
)}
|
||||
</View>
|
||||
<TouchableOpacity onPress={() => {
|
||||
navigation.navigate('ExpiredKeysScreen' as never );
|
||||
navigation.navigate('ExpiredKeysScreen' as never);
|
||||
}}>
|
||||
<View style={styles.shareTip}>
|
||||
<Text style={styles.shareBtnTxt}>查看已失效共享</Text>
|
||||
<Image
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uGq4yJlU1ZZRkwiJ8Y74' }}
|
||||
style={styles.lasttimeImg}
|
||||
/>
|
||||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uGq4yJlU1ZZRkwiJ8Y74' }}
|
||||
style={styles.lasttimeImg}
|
||||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
|
@ -205,7 +203,6 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
cardTopTxt: {
|
||||
marginLeft: rpx(28),
|
||||
|
||||
},
|
||||
cardTopImg: {
|
||||
width: rpx(96),
|
||||
|
@ -213,7 +210,6 @@ const styles = StyleSheet.create({
|
|||
borderRadius: rpx(48),
|
||||
},
|
||||
cardTopName: {
|
||||
|
||||
fontSize: rpx(44),
|
||||
color: '#3D3D3D',
|
||||
},
|
||||
|
@ -221,7 +217,6 @@ const styles = StyleSheet.create({
|
|||
fontSize: rpx(36),
|
||||
color: '#808080',
|
||||
},
|
||||
|
||||
shareBox: {
|
||||
width: rpx(688),
|
||||
borderRadius: rpx(30),
|
||||
|
@ -245,8 +240,8 @@ const styles = StyleSheet.create({
|
|||
marginTop: rpx(32),
|
||||
borderRadius: rpx(16),
|
||||
borderWidth: rpx(2),
|
||||
borderColor: '#808080 ',
|
||||
|
||||
borderColor: '#808080',
|
||||
},
|
||||
});
|
||||
|
||||
export default DeviceShare;
|
|
@ -1,88 +1,105 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { View, StyleSheet, Image, ImageBackground, TouchableOpacity, Text, Alert, Platform, NativeModules } from 'react-native';
|
||||
import { rpx } from '../../utils/rpx';
|
||||
import { RouteProp, useRoute } from '@react-navigation/native';
|
||||
import RNFS from 'react-native-fs';
|
||||
import { request, PERMISSIONS, RESULTS } from 'react-native-permissions';
|
||||
import { Spinner, Layout } from '@ui-kitten/components';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { captureRef } from 'react-native-view-shot';
|
||||
|
||||
const ShareQrcode = () => {
|
||||
const route = useRoute<RouteProp<RootStackParamList, 'ShareQrcode'>>();
|
||||
const { QrId } = route.params;
|
||||
const [loading, setLoading] = useState(true);
|
||||
const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${rpx(400)}x${rpx(400)}&data=https://testlu.chuangtewl.com/prod-api?keyId=${QrId}`;
|
||||
|
||||
const qrCodeUrl = `https://testlu.chuangtewl.com/prod-api?keyId=${QrId}`;
|
||||
const qrRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 2000);
|
||||
}, 1000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const saveToGallery = async () => {
|
||||
try {
|
||||
const writePermission = await request(PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);
|
||||
const readPermission = await request(PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE);
|
||||
// 修正权限请求
|
||||
const writePermission = await request(
|
||||
Platform.select({
|
||||
android: PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,
|
||||
default: PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,
|
||||
})
|
||||
);
|
||||
|
||||
if (writePermission === RESULTS.GRANTED && readPermission === RESULTS.GRANTED) {
|
||||
try {
|
||||
let directoryPath = `${RNFS.DownloadDirectoryPath}`;
|
||||
const fileName = `qrcode_${Date.now()}.jpg`;
|
||||
let downloadDest = `${directoryPath}/${fileName}`;
|
||||
const readPermission = await request(
|
||||
Platform.select({
|
||||
android: PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,
|
||||
default: PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,
|
||||
})
|
||||
);
|
||||
|
||||
// 检查目录是否存在
|
||||
let exists = await RNFS.exists(directoryPath);
|
||||
if (!exists) {
|
||||
try {
|
||||
await RNFS.mkdir(directoryPath);
|
||||
} catch (mkdirError) {
|
||||
// 使用备用路径
|
||||
directoryPath = `${RNFS.ExternalStorageDirectoryPath}`;
|
||||
downloadDest = `${directoryPath}/${fileName}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
const options = {
|
||||
fromUrl: qrCodeUrl,
|
||||
toFile: downloadDest,
|
||||
};
|
||||
|
||||
const response = await RNFS.downloadFile(options).promise;
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
const fileExists = await RNFS.exists(downloadDest);
|
||||
if (fileExists) {
|
||||
// 刷新媒体库
|
||||
NativeModules.MediaScanner.scanFile(downloadDest, 'image/jpeg', (err) => {
|
||||
if (err) {
|
||||
Alert.alert('错误', '无法刷新媒体库');
|
||||
} else {
|
||||
Alert.alert('成功', '二维码已保存到相册');
|
||||
if (writePermission !== RESULTS.GRANTED || readPermission !== RESULTS.GRANTED) {
|
||||
Alert.alert(
|
||||
'权限不足',
|
||||
'请在设置中授予存储权限',
|
||||
[
|
||||
{ text: '取消' },
|
||||
{
|
||||
text: '去设置',
|
||||
onPress: () => {
|
||||
if (Platform.OS === 'android') {
|
||||
Linking.openSettings();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error('文件未能成功创建');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Alert.alert('错误', '下载失败,请检查网络连接');
|
||||
}
|
||||
} catch (err) {
|
||||
Alert.alert('错误', '保存失败,无法访问存储空间');
|
||||
}
|
||||
} else {
|
||||
Alert.alert('权限不足', '请在设置中授予存储权限');
|
||||
]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用应用专用目录
|
||||
const fileName = `qrcode_${Date.now()}.jpg`;
|
||||
const downloadDest = `${RNFS.ExternalDirectoryPath}/${fileName}`;
|
||||
|
||||
// 截取QR码视图
|
||||
const uri = await captureRef(qrRef, {
|
||||
format: 'jpg',
|
||||
quality: 1,
|
||||
});
|
||||
|
||||
// 复制文件到目标位置
|
||||
await RNFS.copyFile(uri, downloadDest);
|
||||
|
||||
// 使用 MediaScanner 使图片在相册中可见
|
||||
NativeModules.MediaScanner.scanFile(downloadDest, 'image/jpeg', (err) => {
|
||||
if (err) {
|
||||
console.error('MediaScanner error:', err);
|
||||
Alert.alert('错误', '保存失败,请重试');
|
||||
} else {
|
||||
Alert.alert('成功', '二维码已保存到相册');
|
||||
}
|
||||
});
|
||||
|
||||
// 清理临时文件
|
||||
try {
|
||||
await RNFS.unlink(uri);
|
||||
} catch (e) {
|
||||
console.log('清理临时文件失败:', e);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Save error:', error);
|
||||
Alert.alert('错误', '保存失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Layout style={styles.loadingContainer}>
|
||||
<Spinner size='giant' />
|
||||
<Text style={styles.loadingText}>加载中...</Text>
|
||||
<Text style={styles.loadingText}>二维码生成中...</Text>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
@ -97,10 +114,12 @@ const ShareQrcode = () => {
|
|||
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/u8yqVl9y6fcU3fTDDTpK' }}
|
||||
style={styles.qrBack}
|
||||
>
|
||||
<Layout style={styles.qrContainer}>
|
||||
<Image
|
||||
source={{ uri: qrCodeUrl }}
|
||||
style={styles.qrCode}
|
||||
<Layout style={styles.qrContainer} ref={qrRef}>
|
||||
<QRCode
|
||||
value={qrCodeUrl}
|
||||
size={rpx(438)}
|
||||
color="black"
|
||||
backgroundColor="white"
|
||||
/>
|
||||
</Layout>
|
||||
</ImageBackground>
|
||||
|
@ -147,19 +166,20 @@ const styles = StyleSheet.create({
|
|||
width: rpx(614),
|
||||
height: rpx(368),
|
||||
},
|
||||
qrCode:{
|
||||
width: rpx(438),
|
||||
height: rpx(438),
|
||||
},
|
||||
qrContainer: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'white',
|
||||
padding: rpx(20),
|
||||
borderRadius: rpx(10),
|
||||
},
|
||||
qrBack: {
|
||||
marginTop: rpx(-20),
|
||||
width: rpx(614),
|
||||
height: rpx(614),
|
||||
padding: rpx(86),
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
backimg: {
|
||||
width: rpx(688),
|
||||
|
|
Loading…
Reference in New Issue
Block a user