feat: 长按下载图片
This commit is contained in:
parent
106700618d
commit
857a22dd6a
@ -3,8 +3,8 @@ import LogoSvg from "@/assets/icons/svg/logo.svg";
|
|||||||
import UserinfoTotalSvg from "@/assets/icons/svg/userinfoTotal.svg";
|
import UserinfoTotalSvg from "@/assets/icons/svg/userinfoTotal.svg";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Linking, Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
import { Linking, Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import QRCode from 'react-native-qrcode-svg';
|
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
import QRDownloadScreen from "./qrCode";
|
||||||
|
|
||||||
interface AppDownloadProps {
|
interface AppDownloadProps {
|
||||||
IOS_APP_STORE_URL: string,
|
IOS_APP_STORE_URL: string,
|
||||||
@ -39,7 +39,8 @@ export const AppDownload = (props: AppDownloadProps) => {
|
|||||||
<UserinfoTotalSvg style={{ marginBottom: -20, zIndex: 1 }} />
|
<UserinfoTotalSvg style={{ marginBottom: -20, zIndex: 1 }} />
|
||||||
<HandlersSvg style={{ marginBottom: -4, zIndex: 3 }} />
|
<HandlersSvg style={{ marginBottom: -4, zIndex: 3 }} />
|
||||||
<View style={styles.qrCode}>
|
<View style={styles.qrCode}>
|
||||||
<QRCode
|
<QRDownloadScreen url={platform == "ios" ? IOS_APP_STORE_URL : ANDROID_APK_URL} />
|
||||||
|
{/* <QRCode
|
||||||
value={platform == "ios" ? IOS_APP_STORE_URL : ANDROID_APK_URL}
|
value={platform == "ios" ? IOS_APP_STORE_URL : ANDROID_APK_URL}
|
||||||
size={200}
|
size={200}
|
||||||
color="black"
|
color="black"
|
||||||
@ -47,7 +48,7 @@ export const AppDownload = (props: AppDownloadProps) => {
|
|||||||
logoSize={50}
|
logoSize={50}
|
||||||
logoBorderRadius={10}
|
logoBorderRadius={10}
|
||||||
logoBackgroundColor="#f0f0f0"
|
logoBackgroundColor="#f0f0f0"
|
||||||
/>
|
/> */}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
|
|||||||
90
components/download/qrCode.tsx
Normal file
90
components/download/qrCode.tsx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import * as Haptics from 'expo-haptics';
|
||||||
|
import * as MediaLibrary from 'expo-media-library';
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
import { Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
import QRCode from 'react-native-qrcode-svg';
|
||||||
|
import { captureRef } from 'react-native-view-shot';
|
||||||
|
|
||||||
|
export default function QRDownloadScreen(prop: { url: string }) {
|
||||||
|
const qrViewRef = useRef<View>(null); // 用于截图的引用
|
||||||
|
const [qrValue] = React.useState(prop.url); // 二维码内容
|
||||||
|
|
||||||
|
const saveQRToGallery = async () => {
|
||||||
|
try {
|
||||||
|
// 触发轻震,提升交互感
|
||||||
|
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
|
|
||||||
|
// 请求相册写入权限
|
||||||
|
const { status } = await MediaLibrary.requestPermissionsAsync();
|
||||||
|
if (status !== 'granted') {
|
||||||
|
Alert.alert('权限被拒绝', '需要保存图片到相册的权限');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qrViewRef.current) return;
|
||||||
|
|
||||||
|
// 截取二维码视图
|
||||||
|
const uri = await captureRef(qrViewRef, {
|
||||||
|
format: 'png',
|
||||||
|
quality: 1,
|
||||||
|
result: 'tmpfile', // 返回临时文件路径
|
||||||
|
});
|
||||||
|
|
||||||
|
// 保存到相册
|
||||||
|
await MediaLibrary.saveToLibraryAsync(uri);
|
||||||
|
|
||||||
|
Alert.alert('✅ 成功', '二维码已保存到相册!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存失败:', error);
|
||||||
|
Alert.alert('❌ 失败', '无法保存图片,请重试');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>长按保存二维码</Text>
|
||||||
|
|
||||||
|
{/* 可截图的容器 */}
|
||||||
|
<TouchableOpacity onLongPress={saveQRToGallery} activeOpacity={0.8}>
|
||||||
|
<View ref={qrViewRef} style={styles.qrContainer}>
|
||||||
|
<QRCode value={qrValue} size={200} />
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<Text style={styles.tip}>👉 长按二维码即可保存</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 20,
|
||||||
|
backgroundColor: '#f8f8f8',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: 20,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
qrContainer: {
|
||||||
|
padding: 16,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 12,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#ddd',
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 2 },
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 4,
|
||||||
|
elevation: 3,
|
||||||
|
},
|
||||||
|
tip: {
|
||||||
|
marginTop: 20,
|
||||||
|
color: '#666',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
});
|
||||||
63
package-lock.json
generated
63
package-lock.json
generated
@ -69,6 +69,7 @@
|
|||||||
"react-native-svg": "^15.11.2",
|
"react-native-svg": "^15.11.2",
|
||||||
"react-native-toast-message": "^2.3.0",
|
"react-native-toast-message": "^2.3.0",
|
||||||
"react-native-uuid": "^2.0.3",
|
"react-native-uuid": "^2.0.3",
|
||||||
|
"react-native-view-shot": "4.0.3",
|
||||||
"react-native-web": "~0.20.0",
|
"react-native-web": "~0.20.0",
|
||||||
"react-native-webview": "13.13.5",
|
"react-native-webview": "13.13.5",
|
||||||
"react-redux": "^9.2.0"
|
"react-redux": "^9.2.0"
|
||||||
@ -6150,6 +6151,15 @@
|
|||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/base64-arraybuffer": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@ -7066,6 +7076,15 @@
|
|||||||
"hyphenate-style-name": "^1.0.3"
|
"hyphenate-style-name": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-line-break": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"utrie": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/css-select": {
|
"node_modules/css-select": {
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
|
||||||
@ -10002,6 +10021,19 @@
|
|||||||
"void-elements": "3.1.0"
|
"void-elements": "3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html2canvas": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"css-line-break": "^2.1.0",
|
||||||
|
"text-segmentation": "^1.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/htmlparser2": {
|
"node_modules/htmlparser2": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
|
||||||
@ -15166,6 +15198,19 @@
|
|||||||
"npm": ">=6.0.0"
|
"npm": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-view-shot": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-USNjYmED7C0me02c1DxKA0074Hw+y/nxo+xJKlffMvfUWWzL5ELh/TJA/pTnVqFurIrzthZDPtDM7aBFJuhrHQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"html2canvas": "^1.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-native-web": {
|
"node_modules/react-native-web": {
|
||||||
"version": "0.20.0",
|
"version": "0.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.20.0.tgz",
|
||||||
@ -17009,6 +17054,15 @@
|
|||||||
"deprecated": "no longer maintained",
|
"deprecated": "no longer maintained",
|
||||||
"license": "(Unlicense OR Apache-2.0)"
|
"license": "(Unlicense OR Apache-2.0)"
|
||||||
},
|
},
|
||||||
|
"node_modules/text-segmentation": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"utrie": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/thenify": {
|
"node_modules/thenify": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||||
@ -17683,6 +17737,15 @@
|
|||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/utrie": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-arraybuffer": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/uuid": {
|
"node_modules/uuid": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
|
||||||
|
|||||||
@ -77,7 +77,8 @@
|
|||||||
"react-native-uuid": "^2.0.3",
|
"react-native-uuid": "^2.0.3",
|
||||||
"react-native-web": "~0.20.0",
|
"react-native-web": "~0.20.0",
|
||||||
"react-native-webview": "13.13.5",
|
"react-native-webview": "13.13.5",
|
||||||
"react-redux": "^9.2.0"
|
"react-redux": "^9.2.0",
|
||||||
|
"react-native-view-shot": "4.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user