diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 8b95c52a..ec509ab7 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -815,7 +815,17 @@ "WebFetch(domain:oneuptime.com)", "Bash(gradlew.bat assembleDebug:*)", "Bash(cmd /c \"gradlew.bat assembleDebug --no-daemon\")", - "Bash(./build-install-debug.bat)" + "Bash(./build-install-debug.bat)", + "Bash(dir /s /b \"backend\\\\mpc-system\\\\services\\\\service-party-android\\\\*.kt\")", + "Bash(set DATABASE_URL=postgresql://postgres:password@localhost:5432/trading_db?schema=public)", + "Bash(ssh -o ProxyJump=ceshi@103.39.231.231 -o StrictHostKeyChecking=no ceshi@192.168.1.111 \"curl -s ''http://localhost:3000/api/v2/trading/asset/account/D25122700015'' | jq .\")", + "Bash(ssh -o ProxyJump=ceshi@103.39.231.231 -o StrictHostKeyChecking=no ceshi@192.168.1.111 \"curl -s ''http://localhost:3000/api/v2/trading/trading/orders?accountSequence=D25122700015'' | jq .\")", + "Bash(docker stop:*)", + "Bash(ssh-add:*)", + "Bash(ls -la \"c:\\\\Users\\\\dong\\\\Desktop\\\\rwadurian\\\\backend\\\\services\\\\auth-service\\\\src\"\" 2>/dev/null || echo \"Source directory structure: \")", + "Bash($env:DATABASE_URL=\"postgresql://postgres:postgres@localhost:5432/rwa_auth\")", + "Bash(DATABASE_URL=\"postgresql://postgres:postgres@localhost:5432/rwa_auth\" npx prisma migrate dev:*)", + "Bash(ssh -J ceshi@103.39.231.231 ceshi@192.168.1.111:*)" ], "deny": [], "ask": [] diff --git a/frontend/mining-app/lib/presentation/widgets/qr_scanner_sheet.dart b/frontend/mining-app/lib/presentation/widgets/qr_scanner_sheet.dart index 5eb4bc4b..215d87c4 100644 --- a/frontend/mining-app/lib/presentation/widgets/qr_scanner_sheet.dart +++ b/frontend/mining-app/lib/presentation/widgets/qr_scanner_sheet.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; /// 二维码扫描底部弹窗 @@ -31,6 +32,7 @@ class _QrScannerSheetState extends State { ); bool _hasScanned = false; + bool _isPickingImage = false; @override void dispose() { @@ -51,6 +53,53 @@ class _QrScannerSheetState extends State { Navigator.of(context).pop(code); } + /// 从相册选择图片识别二维码 + Future _pickImageAndScan() async { + if (_isPickingImage || _hasScanned) return; + setState(() => _isPickingImage = true); + + try { + final picker = ImagePicker(); + final image = await picker.pickImage(source: ImageSource.gallery); + if (image == null) { + setState(() => _isPickingImage = false); + return; + } + + final BarcodeCapture? capture = await _controller.analyzeImage(image.path); + if (capture != null && capture.barcodes.isNotEmpty) { + final code = capture.barcodes.first.rawValue; + if (code != null && code.isNotEmpty && !_hasScanned) { + _hasScanned = true; + if (mounted) Navigator.of(context).pop(code); + return; + } + } + + // 未识别到二维码 + if (mounted) { + setState(() => _isPickingImage = false); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('未在图片中识别到二维码'), + backgroundColor: Colors.red, + ), + ); + } + } catch (e) { + debugPrint('[QR_SCAN] pickImage error: $e'); + if (mounted) { + setState(() => _isPickingImage = false); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('图片识别失败,请重试'), + backgroundColor: Colors.red, + ), + ); + } + } + } + @override Widget build(BuildContext context) { final screenHeight = MediaQuery.of(context).size.height; @@ -200,15 +249,42 @@ class _QrScannerSheetState extends State { ), ), - // 提示文字 + // 提示文字 + 相册按钮 Padding( - padding: const EdgeInsets.all(24), - child: Text( - '将二维码放入框内,即可自动扫描', - style: TextStyle( - fontSize: 14, - color: Colors.grey[600], - ), + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Column( + children: [ + Text( + '将二维码放入框内,即可自动扫描', + style: TextStyle( + fontSize: 14, + color: Colors.grey[600], + ), + ), + const SizedBox(height: 12), + GestureDetector( + onTap: _isPickingImage ? null : _pickImageAndScan, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.photo_library_outlined, + size: 18, + color: _isPickingImage ? Colors.grey : _orange, + ), + const SizedBox(width: 6), + Text( + _isPickingImage ? '识别中...' : '从相册选择', + style: TextStyle( + fontSize: 14, + color: _isPickingImage ? Colors.grey : _orange, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ], ), ), ], diff --git a/frontend/mining-app/pubspec.yaml b/frontend/mining-app/pubspec.yaml index 8f25c7b4..5f19fd81 100644 --- a/frontend/mining-app/pubspec.yaml +++ b/frontend/mining-app/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: shimmer: ^3.0.0 qr_flutter: ^4.1.0 mobile_scanner: ^5.1.1 + image_picker: ^1.0.4 # 图表 fl_chart: ^0.64.0