QRScanner.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /**
  2. * QR码扫描器
  3. * @邠心vbe on 2026/04/21
  4. */
  5. import React, { useEffect, useState } from 'react';
  6. import { Pressable, StyleSheet, View } from 'react-native';
  7. import TextView from '../../components/TextView';
  8. import { Camera, useCameraDevice, useCodeScanner } from 'react-native-vision-camera';
  9. import { check, PERMISSIONS, RESULTS } from 'react-native-permissions';
  10. import utils from '../../utils/utils';
  11. import app from '../../../app.json';
  12. const checkPermission = (back) => {
  13. if (isIOS) {
  14. back(true);
  15. } else {
  16. check(PERMISSIONS.ANDROID.CAMERA).then(res => {
  17. console.log('checkPermission', res);
  18. switch (res) {
  19. case RESULTS.DENIED://权限未被请求/被拒绝,但可以请求
  20. back(false);
  21. break;
  22. case RESULTS.LIMITED://权限是有限的:有些操作是可能的
  23. back(true);
  24. break;
  25. case RESULTS.GRANTED://许可被授予
  26. back(true);
  27. break;
  28. case RESULTS.BLOCKED://权限被拒绝,不再可请求
  29. back(false);
  30. break;
  31. default:
  32. back(false);
  33. break;
  34. }
  35. }).catch(err => {
  36. back(false);
  37. })
  38. }
  39. }
  40. const QRScanner = ({ onResult, isActive }) => {
  41. const [flashOn, switchFlash] = useState(false);
  42. const [hasPermission, setHasPermission] = useState(false);
  43. const [permissionStr, setPermissionStr] = useState("");
  44. const device = useCameraDevice('back');
  45. useEffect(() => {
  46. console.log("相机设备", device);
  47. checkPermission(result => {
  48. setHasPermission(result);
  49. })
  50. Camera.requestCameraPermission().then(res => {
  51. setPermissionStr(res);
  52. utils.logEventTracking("scan_camera_permission", res)
  53. if (!hasPermission) {
  54. setHasPermission(res == 'granted');
  55. }
  56. }).catch(err => {
  57. console.warn("相机权限请求错误", err);
  58. utils.logEventTracking("scan_camera_permission_error", err)
  59. setPermissionStr("Exception");
  60. setHasPermission(false);
  61. });
  62. }, []);
  63. const codeScanner = useCodeScanner({
  64. codeTypes: ['qr'],
  65. onCodeScanned: (codes) => {
  66. if (codes && codes.length > 0) {
  67. onResult(codes)
  68. }
  69. },
  70. });
  71. if (!device || !hasPermission) {
  72. return (
  73. <View style={styles.tipsScreen}>
  74. <TextView style={styles.tipsText}>
  75. {!hasPermission ? "Camera access has been denied. Please enable it in your device settings.(E0)" : "Can not find camera device.(E1)"}
  76. </TextView>
  77. </View>
  78. )
  79. }
  80. return (
  81. <>
  82. <Camera
  83. style={{width: $width, height: $vh(110)}}
  84. device={device}
  85. isActive={isActive}
  86. codeScanner={codeScanner}
  87. enableZoomGesture={true}
  88. photo={false}
  89. video={false}
  90. audio={false}
  91. resizeMode="cover"
  92. torch={flashOn ? 'on' : 'off'}
  93. />
  94. { isActive &&
  95. <Pressable
  96. style={styles.flashLight}
  97. onPress={() => switchFlash(!flashOn)}>
  98. <MaterialIcons
  99. name={flashOn ? "flashlight-on" : "flashlight-off"}
  100. size={36}
  101. color="#fff"/>
  102. { (!isIOS && !app.product) &&
  103. <TextView style={{color: "#fff", fontSize: 12}}>{permissionStr}</TextView>
  104. }
  105. </Pressable>
  106. }
  107. </>
  108. );
  109. };
  110. export default QRScanner;
  111. const styles = StyleSheet.create({
  112. tipsScreen: {
  113. top: 0,
  114. left: 0,
  115. right: 0,
  116. bottom: 0,
  117. position: 'absolute',
  118. alignItems: "center",
  119. justifyContent: "center",
  120. backgroundColor: '#111',
  121. },
  122. tipsText: {
  123. color: textLight,
  124. fontSize: 14,
  125. textAlign: "center"
  126. },
  127. flashLight: {
  128. bottom: 120,
  129. zIndex: 2,
  130. opacity: 0.7,
  131. padding: 8,
  132. position: 'absolute'
  133. }
  134. })