diff --git a/frontend/src/pages/login/QRCodeLogin.tsx b/frontend/src/pages/login/QRCodeLogin.tsx index 3e6da7f..f9a1223 100644 --- a/frontend/src/pages/login/QRCodeLogin.tsx +++ b/frontend/src/pages/login/QRCodeLogin.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useRef } from 'react' import { Dialog, Toast } from 'antd-mobile' +import { QRCodeSVG } from 'qrcode.react' import { useNavigate } from 'react-router-dom' import styles from './QRCodeLogin.module.css' @@ -11,7 +12,6 @@ declare global { goto: string width?: string height?: string - style?: string }) => { matchOrigin: (origin: string) => boolean matchData: (data: any) => boolean @@ -25,26 +25,47 @@ interface QRCodeLoginProps { } // 动态加载飞书 QR SDK -const loadFeishuQRSDK = (): Promise => { - return new Promise((resolve, reject) => { +const loadFeishuQRSDK = (): Promise => { + return new Promise((resolve) => { + // 检查是否已经加载 if (window.QR) { - resolve() + console.log('✅ Feishu QR SDK already loaded') + resolve(true) return } - // 检查是否已经加载过 - const existingScript = document.getElementById('feishu-qr-sdk') - if (existingScript) { - existingScript.addEventListener('load', () => resolve()) - existingScript.addEventListener('error', () => reject(new Error('Failed to load Feishu QR SDK'))) - return - } + console.log('📦 Loading Feishu QR SDK...') const script = document.createElement('script') script.id = 'feishu-qr-sdk' script.src = 'https://lf-package-cn.feishucdn.com/obj/feishu-static/lark/passport/qrcode/LarkSSOSDKWebQRCode-1.0.3.js' - script.onload = () => resolve() - script.onerror = () => reject(new Error('Failed to load Feishu QR SDK')) + script.async = true + script.timeout = 5000 // 5秒超时 + + const timeoutId = setTimeout(() => { + console.warn('⚠️ Feishu QR SDK load timeout, using fallback') + resolve(false) + }, 5000) + + script.onload = () => { + clearTimeout(timeoutId) + setTimeout(() => { + if (window.QR) { + console.log('✅ Feishu QR SDK loaded successfully') + resolve(true) + } else { + console.warn('⚠️ QR SDK loaded but window.QR not found, using fallback') + resolve(false) + } + }, 100) + } + + script.onerror = () => { + clearTimeout(timeoutId) + console.warn('⚠️ Failed to load Feishu QR SDK, using fallback') + resolve(false) // 不 reject,而是返回 false 使用降级方案 + } + document.head.appendChild(script) }) } @@ -52,6 +73,8 @@ const loadFeishuQRSDK = (): Promise => { export default function QRCodeLogin({ visible, onClose }: QRCodeLoginProps) { const navigate = useNavigate() const [status, setStatus] = useState<'loading' | 'pending' | 'scanning' | 'success'>('loading') + const [useFallback, setUseFallback] = useState(false) + const [qrcodeUrl, setQrcodeUrl] = useState('') const qrContainerRef = useRef(null) const qrObjRef = useRef(null) const pollTimerRef = useRef(null) @@ -70,18 +93,20 @@ export default function QRCodeLogin({ visible, onClose }: QRCodeLoginProps) { if (pollTimerRef.current) { clearTimeout(pollTimerRef.current) } + // 重置状态 + setUseFallback(false) + setQrcodeUrl('') } }, [visible]) const initQRCode = async () => { setStatus('loading') - try { - // 加载飞书 QR SDK - await loadFeishuQRSDK() + setUseFallback(false) + setQrcodeUrl('') - if (!window.QR) { - throw new Error('QR SDK not loaded') - } + try { + // 尝试加载飞书 QR SDK + const sdkLoaded = await loadFeishuQRSDK() // 生成登录会话 const response = await fetch('/api/v1/auth/qrcode-login', { @@ -98,22 +123,29 @@ export default function QRCodeLogin({ visible, onClose }: QRCodeLoginProps) { const authUrl = result.data.auth_url sessionIdRef.current = result.data.session_id - // 清空容器 - if (qrContainerRef.current) { - qrContainerRef.current.innerHTML = '' + if (sdkLoaded && window.QR) { + // 使用飞书官方 QR SDK + if (qrContainerRef.current) { + qrContainerRef.current.innerHTML = '' + } + + qrObjRef.current = window.QR({ + id: 'feishu-qrcode-container', + goto: authUrl, + width: '200', + height: '200', + }) + + setStatus('pending') + startPolling(result.data.session_id) + } else { + // SDK 加载失败,使用普通二维码 + console.log('📱 Using fallback QR code') + setQrcodeUrl(authUrl) + setUseFallback(true) + setStatus('pending') + startPolling(result.data.session_id) } - - // 使用飞书官方 QR SDK 创建二维码 - qrObjRef.current = window.QR({ - id: 'feishu-qrcode-container', - goto: authUrl, - width: '200', - height: '200', - }) - - setStatus('pending') - // 开始轮询登录状态 - startPolling(result.data.session_id) } else { Toast.show(result.message || '生成二维码失败') } @@ -194,20 +226,54 @@ export default function QRCodeLogin({ visible, onClose }: QRCodeLoginProps) { } } + const handleRefresh = () => { + if (pollTimerRef.current) { + clearTimeout(pollTimerRef.current) + } + initQRCode() + } + return ( -
+ {status === 'loading' &&
正在生成二维码...
} -
- {getStatusText()} -
+ {useFallback && qrcodeUrl && ( + <> +
+ +
-

- 💡 打开飞书 APP,扫一扫登录 -

+
+ {getStatusText()} +
+ +

+ 💡 使用飞书扫码登录 +

+ + )} + + {!useFallback && status !== 'loading' && ( + <> +
+ +
+ {getStatusText()} +
+ +

+ 💡 打开飞书 APP,扫一扫登录 +

+ + )} } onClose={onClose}