Quellcode durchsuchen

feat (1.63.4 -> 1.64.0): 优化二维码扫描功能,提升残缺二维码识别率并添加图片上传备选方案

- 提高扫描配置参数:fps从10提升到20,qrbox从250px扩大到300x300,新增aspectRatio和disableFlip参数
- 添加图片上传扫描模式,使用html5-qrcode的scanFile方法处理图片
- 优化UI界面,支持摄像头扫描和图片上传两种模式的切换
- 提升残缺二维码的识别成功率,提供更友好的用户体验

改动文件:
- src/components/QRCodeScanner/QRCodeScanModal.tsx
- package.json
- CHANGELOG.md
dengdx vor 6 Tagen
Ursprung
Commit
600a23eae3
3 geänderte Dateien mit 175 neuen und 31 gelöschten Zeilen
  1. 15 0
      CHANGELOG.md
  2. 1 1
      package.json
  3. 159 30
      src/components/QRCodeScanner/QRCodeScanModal.tsx

+ 15 - 0
CHANGELOG.md

@@ -3,6 +3,21 @@
 本项目的所有重要变更都将记录在此文件中.
 
 
+## [1.64.0] - 2026-01-12 14:58
+
+### 新增 (Added)
+
+- **优化二维码扫描功能,提升残缺二维码识别率并添加图片上传备选方案** - 针对轻微污损的二维码优化扫描参数,并新增图片上传扫描功能
+  - 提高扫描配置参数:fps从10提升到20,qrbox从250px扩大到300x300,新增aspectRatio和disableFlip参数
+  - 添加图片上传扫描模式,使用html5-qrcode的scanFile方法处理图片
+  - 优化UI界面,支持摄像头扫描和图片上传两种模式的切换
+  - 提升残缺二维码的识别成功率,提供更友好的用户体验
+
+**改动文件:**
+
+- src/components/QRCodeScanner/QRCodeScanModal.tsx
+
+
 ## [1.63.4] - 2026-01-12 09:50
 
 - 人医第一个,注册时有默认值

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "zsis",
-  "version": "1.63.4",
+  "version": "1.64.0",
   "private": true,
   "description": "医学成像系统",
   "main": "main.js",

+ 159 - 30
src/components/QRCodeScanner/QRCodeScanModal.tsx

@@ -5,7 +5,8 @@
  */
 
 import React, { useEffect, useRef, useState, useMemo } from 'react';
-import { Modal, Button, Alert, Spin, Typography } from 'antd';
+import { Modal, Button, Alert, Spin, Typography, Upload, message } from 'antd';
+import { UploadOutlined } from '@ant-design/icons';
 import { Html5Qrcode } from 'html5-qrcode';
 import { useDispatch, useSelector } from 'react-redux';
 import { FormInstance } from 'antd';
@@ -32,6 +33,7 @@ const QRCodeScanModal: React.FC<QRCodeScanModalProps> = ({ visible, onCancel, fo
   const dispatch = useDispatch();
   const { isScanning, isProcessing, error } = useSelector((state: RootState) => state.qrCodeScan);
   const [scannedText, setScannedText] = useState<string>('');
+  const [uploadMode, setUploadMode] = useState<boolean>(false);
   const scannerRef = useRef<Html5Qrcode | null>(null);
   const readerRef = useRef<HTMLDivElement>(null);
 
@@ -52,7 +54,12 @@ const QRCodeScanModal: React.FC<QRCodeScanModalProps> = ({ visible, onCancel, fo
       try {
         await qr.start(
           { facingMode: 'environment' },
-          { fps: 10, qrbox: 250 },
+          {
+            fps: 20, // 提高帧率,增加扫描频率
+            qrbox: { width: 300, height: 300 }, // 更大的扫描框
+            aspectRatio: 1.0, // 正方形扫描框
+            disableFlip: false, // 允许镜像扫描,提高识别率
+          },
           onScanSuccess,
           onScanError
         );
@@ -150,7 +157,12 @@ const QRCodeScanModal: React.FC<QRCodeScanModalProps> = ({ visible, onCancel, fo
       scannerRef.current = qr;
       await scannerRef.current.start(
         { facingMode: 'environment' },
-        { fps: 10, qrbox: 250 },
+        {
+          fps: 20,
+          qrbox: { width: 300, height: 300 },
+          aspectRatio: 1.0,
+          disableFlip: false,
+        },
         onScanSuccess,
         onScanError
       );
@@ -164,15 +176,93 @@ const QRCodeScanModal: React.FC<QRCodeScanModalProps> = ({ visible, onCancel, fo
   };
 
   const handleCancel = () => {
+    setUploadMode(false);
     onCancel();
   };
 
+  // 📤 处理图片上传扫描
+  const handleFileUpload = async (file: File) => {
+    try {
+      dispatch(startProcessing());
+      
+      // 创建临时的 Html5Qrcode 实例用于文件扫描
+      const tempScanner = new Html5Qrcode(qrReaderId);
+      
+      const decodedText = await tempScanner.scanFile(file, false);
+      
+      console.log('[QRCodeScan] 图片扫描成功:', decodedText);
+      setScannedText(decodedText);
+      setUploadMode(true);
+      
+      // 停止摄像头扫描
+      if (scannerRef.current && scannerRef.current.isScanning) {
+        await scannerRef.current.stop().catch(() => {});
+      }
+      
+      dispatch(stopProcessing());
+      message.success('图片扫描成功');
+    } catch (error) {
+      console.error('[QRCodeScan] 图片扫描失败:', error);
+      const errorMsg = error instanceof Error ? error.message : '图片扫描失败,请确保图片包含有效的二维码';
+      dispatch(setError(errorMsg));
+      dispatch(stopProcessing());
+      message.error(errorMsg);
+    }
+    
+    return false; // 阻止自动上传
+  };
+
+  // 切换到上传模式
+  const handleSwitchToUpload = () => {
+    setUploadMode(true);
+  };
+
+  // 切换回摄像头模式
+  const handleSwitchToCamera = async () => {
+    setUploadMode(false);
+    setScannedText('');
+    dispatch(clearError());
+    
+    // 重新启动摄像头扫描
+    if (scannerRef.current) {
+      try {
+        if (scannerRef.current.isScanning) {
+          await scannerRef.current.stop();
+          await scannerRef.current.clear();
+        }
+        const qr = new Html5Qrcode(qrReaderId);
+        scannerRef.current = qr;
+        await qr.start(
+          { facingMode: 'environment' },
+          {
+            fps: 20,
+            qrbox: { width: 300, height: 300 },
+            aspectRatio: 1.0,
+            disableFlip: false,
+          },
+          onScanSuccess,
+          onScanError
+        );
+      } catch (error) {
+        console.error('重新启动摄像头失败:', error);
+        const errorMsg = error instanceof Error ? error.message : '重新启动摄像头失败';
+        dispatch(setError(errorMsg));
+      }
+    }
+  };
+
   const getFooterButtons = () => {
     if (scannedText) {
       return [
-        <Button key="rescan" onClick={handleRescan}>
-          重新扫描
-        </Button>,
+        uploadMode ? (
+          <Button key="camera" onClick={handleSwitchToCamera}>
+            切换摄像头
+          </Button>
+        ) : (
+          <Button key="rescan" onClick={handleRescan}>
+            重新扫描
+          </Button>
+        ),
         <Button key="confirm" type="primary" onClick={handleConfirmScan} loading={isProcessing}>
           确认使用
         </Button>,
@@ -182,7 +272,12 @@ const QRCodeScanModal: React.FC<QRCodeScanModalProps> = ({ visible, onCancel, fo
         <Button key="cancel" onClick={handleCancel}>
           取消
         </Button>,
-      ];
+        !uploadMode && (
+          <Button key="upload" icon={<UploadOutlined />} onClick={handleSwitchToUpload}>
+            上传图片
+          </Button>
+        ),
+      ].filter(Boolean);
     }
   };
 
@@ -215,29 +310,63 @@ const QRCodeScanModal: React.FC<QRCodeScanModalProps> = ({ visible, onCancel, fo
         )}
 
         {/* 扫描区域 */}
-
-        <>
-          <div
-            id={qrReaderId}
-            ref={readerRef}
-            style={{
-              width: '100%',
-              maxWidth: 500,
-              margin: '0 auto',
-              border: '2px solid #1890ff',
-              borderRadius: 8,
-              padding: 0,
-              minHeight: 300, // 给扫描区域固定高度
-              display: scannedText ? 'none' : 'block',
-            }}
-          />
-          <div style={{ marginTop: 16, color: '#666' }}>
-            <p>请将二维码对准扫描框</p>
-            <p style={{ fontSize: 12 }}>
-              提示:确保光线充足,二维码清晰可见
-            </p>
-          </div>
-        </>
+        {!uploadMode ? (
+          <>
+            <div
+              id={qrReaderId}
+              ref={readerRef}
+              style={{
+                width: '100%',
+                maxWidth: 500,
+                margin: '0 auto',
+                border: '2px solid #1890ff',
+                borderRadius: 8,
+                padding: 0,
+                minHeight: 300,
+                display: scannedText ? 'none' : 'block',
+              }}
+            />
+            {!scannedText && (
+              <div style={{ marginTop: 16, color: '#666' }}>
+                <p>请将二维码对准扫描框</p>
+                <p style={{ fontSize: 12 }}>
+                  提示:确保光线充足,二维码清晰可见
+                </p>
+                <p style={{ fontSize: 12, marginTop: 8 }}>
+                  如果摄像头扫描困难,可以点击下方"上传图片"按钮
+                </p>
+              </div>
+            )}
+          </>
+        ) : (
+          !scannedText && (
+            <div style={{ padding: '40px 0', textAlign: 'center' }}>
+              <Upload
+                accept="image/*"
+                beforeUpload={handleFileUpload}
+                showUploadList={false}
+                maxCount={1}
+              >
+                <Button icon={<UploadOutlined />} size="large" type="primary">
+                  选择二维码图片
+                </Button>
+              </Upload>
+              <div style={{ marginTop: 16, color: '#666' }}>
+                <p>支持 JPG、PNG 等常见图片格式</p>
+                <p style={{ fontSize: 12 }}>
+                  请确保图片清晰,二维码完整可见
+                </p>
+              </div>
+              <Button 
+                type="link" 
+                onClick={handleSwitchToCamera}
+                style={{ marginTop: 16 }}
+              >
+                返回摄像头扫描
+              </Button>
+            </div>
+          )
+        )}
 
 
         {/* 扫描结果显示 */}