From 7e57e8720b8879494a1844292fc32c633d4d7049 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Feb 2026 19:21:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E9=A3=9E=E4=B9=A6=20?= =?UTF-8?q?JSBridge=20SDK=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: - 移动端飞书 WebView 中 tt 对象没有被注入 - 导致免登功能无法使用 修复: 1. index.html - 引入飞书 H5 JSBridge SDK 2. approval/detail.tsx - 添加对多种飞书 API 的支持 3. debug-feishu.tsx - 更新调试页面,检测所有可能的 API 对象 支持的飞书 API: - window.tt (标准 H5 应用) - window.h5sdk (JSBridge) - window.bridge (JSBridge) - window.ttjsBridge (旧版) 相关文件: - frontend/index.html - frontend/src/pages/approval/detail.tsx - frontend/src/pages/debug-feishu.tsx Co-Authored-By: Claude Opus 4.6 --- frontend/index.html | 2 + frontend/src/pages/approval/detail.tsx | 90 ++++++++++++------ frontend/src/pages/debug-feishu.tsx | 127 +++++++++++++++++++------ 3 files changed, 160 insertions(+), 59 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 95e2a1f..95b416f 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -7,6 +7,8 @@ frontend + +
diff --git a/frontend/src/pages/approval/detail.tsx b/frontend/src/pages/approval/detail.tsx index 04c6c7e..1fd4052 100644 --- a/frontend/src/pages/approval/detail.tsx +++ b/frontend/src/pages/approval/detail.tsx @@ -126,41 +126,75 @@ export default function ApprovalDetail() { console.log('🔍 tryFeishuLogin 开始') console.log('📱 isInFeishu:', isInFeishu) console.log('🔧 window.tt:', window.tt) + console.log('🔧 window.h5sdk:', (window as any).h5sdk) + console.log('🔧 window.bridge:', (window as any).bridge) - if (!isInFeishu || !window.tt) { - console.log('❌ 不在飞书内或 tt 对象不可用,跳过免登') + if (!isInFeishu) { + console.log('❌ 不在飞书内,跳过免登') resolve(false) return } - console.log('🚀 开始调用 tt.requestAuthCode') const appId = 'cli_a8033d607cba500d' - - window.tt.requestAuthCode({ - appId: appId, - success: async (res: any) => { - console.log('✅ tt.requestAuthCode 成功:', res) - console.log('🔑 authCode:', res.code?.substring(0, 30) + '...') - try { - console.log('📡 调用后端登录API...') - const result = await authApi.feishuLogin(res.code) - console.log('✅ 后端登录成功:', result.user?.name) - login(result.user, result.token) - Toast.show('登录成功') - resolve(true) - } catch (error) { - console.error('❌ 飞书登录API失败:', error) - Toast.show('登录失败: ' + (error.message || '未知错误')) + const requestAuthCode = (apiObj: any, source: string) => { + console.log(`🚀 使用 ${source} 调用 requestAuthCode`) + apiObj.requestAuthCode({ + appId: appId, + success: async (res: any) => { + console.log(`✅ ${source}.requestAuthCode 成功:`, res) + console.log('🔑 authCode:', res.code?.substring(0, 30) + '...') + try { + console.log('📡 调用后端登录API...') + const result = await authApi.feishuLogin(res.code) + console.log('✅ 后端登录成功:', result.user?.name) + login(result.user, result.token) + Toast.show('登录成功') + resolve(true) + } catch (error) { + console.error('❌ 飞书登录API失败:', error) + Toast.show('登录失败: ' + (error.message || '未知错误')) + resolve(false) + } + }, + fail: (error: any) => { + console.error(`❌ ${source}.requestAuthCode 失败:`, error) + console.error('❌ 错误详情:', JSON.stringify(error)) + Toast.show('免登失败: ' + (error.errorMessage || error.errMsg || '未知错误')) resolve(false) - } - }, - fail: (error: any) => { - console.error('❌ tt.requestAuthCode 失败:', error) - console.error('❌ 错误详情:', JSON.stringify(error)) - Toast.show('免登失败: ' + (error.errorMessage || error.errMsg || '未知错误')) - resolve(false) - }, - }) + }, + }) + } + + // 优先使用 tt 对象 + if (window.tt && typeof window.tt.requestAuthCode === 'function') { + requestAuthCode(window.tt, 'tt') + return + } + + // 尝试使用 h5sdk + const h5sdk = (window as any).h5sdk + if (h5sdk && typeof h5sdk.requestAuthCode === 'function') { + requestAuthCode(h5sdk, 'h5sdk') + return + } + + // 尝试使用 bridge + const bridge = (window as any).bridge + if (bridge && typeof bridge.requestAuthCode === 'function') { + requestAuthCode(bridge, 'bridge') + return + } + + // 尝试使用 ttjsBridge + if (window.ttjsBridge && typeof window.ttjsBridge.requestAuthCode === 'function') { + requestAuthCode(window.ttjsBridge, 'ttjsBridge') + return + } + + // 所有方法都不可用 + console.log('❌ 所有飞书 API 都不可用') + Toast.show('飞书API不可用,请使用其他方式登录') + resolve(false) }) } diff --git a/frontend/src/pages/debug-feishu.tsx b/frontend/src/pages/debug-feishu.tsx index eeab2a3..1083a5a 100644 --- a/frontend/src/pages/debug-feishu.tsx +++ b/frontend/src/pages/debug-feishu.tsx @@ -26,47 +26,112 @@ export default function DebugFeishuPage() { const [envInfo, setEnvInfo] = useState(null) useEffect(() => { - const ua = navigator.userAgent - const isInFeishu = ua.includes('Lark') || ua.includes('feishu') || ua.includes('lark') + // 延迟检测,等待 JSBridge 注入 + setTimeout(() => { + const ua = navigator.userAgent + const isInFeishu = ua.includes('Lark') || ua.includes('feishu') || ua.includes('lark') - const ttMethods = window.tt ? Object.keys(window.tt) : [] + const ttMethods = window.tt ? Object.keys(window.tt) : [] - setEnvInfo({ - ua, - isInFeishu, - hasTT: !!window.tt, - hasTTJSBridge: !!window.ttjsBridge, - hasH5SDK: !!(window as any).h5sdk, - hasBridge: !!(window as any).bridge, - ttMethods, - }) + setEnvInfo({ + ua, + isInFeishu, + hasTT: !!window.tt, + hasTTJSBridge: !!window.ttjsBridge, + hasH5SDK: !!(window as any).h5sdk, + hasBridge: !!(window as any).bridge, + ttMethods, + }) - console.log('=== 飞书环境调试 ===') - console.log('User Agent:', ua) - console.log('在飞书中:', isInFeishu) - console.log('window.tt:', window.tt) - console.log('tt 对象方法:', ttMethods) + console.log('=== 飞书环境调试 ===') + console.log('User Agent:', ua) + console.log('在飞书中:', isInFeishu) + console.log('window.tt:', window.tt) + console.log('window.h5sdk:', (window as any).h5sdk) + console.log('window.bridge:', (window as any).bridge) + console.log('window.ttjsBridge:', window.ttjsBridge) + console.log('tt 对象方法:', ttMethods) + + // 检查所有全局对象 + console.log('所有全局对象:', Object.keys(window).filter(k => k.includes('tt') || k.includes('h5') || k.includes('bridge') || k.includes('lark') || k.includes('feishu'))) + }, 500) }, []) const testFeishuLogin = async () => { - if (!window.tt) { - alert('tt 对象不存在,无法测试') + const appId = 'cli_a8033d607cba500d' + + // 优先使用 tt 对象 + if (window.tt && typeof window.tt.requestAuthCode === 'function') { + console.log('🔧 使用 tt.requestAuthCode') + window.tt.requestAuthCode({ + appId: appId, + success: (res: any) => { + alert('✅ 获取授权码成功: ' + res.code?.substring(0, 30)) + console.log('授权码:', res) + }, + fail: (error: any) => { + alert('❌ 获取授权码失败: ' + JSON.stringify(error)) + console.error('错误:', error) + }, + }) return } - const appId = 'cli_a8033d607cba500d' + // 尝试使用 h5sdk + const h5sdk = (window as any).h5sdk + if (h5sdk) { + console.log('🔧 尝试使用 h5sdk') + try { + if (typeof h5sdk.requestAuthCode === 'function') { + h5sdk.requestAuthCode({ + appId: appId, + success: (res: any) => { + alert('✅ h5sdk 获取授权码成功: ' + res.code?.substring(0, 30)) + console.log('授权码:', res) + }, + fail: (error: any) => { + alert('❌ h5sdk 获取授权码失败: ' + JSON.stringify(error)) + console.error('错误:', error) + }, + }) + return + } + } catch (e) { + console.error('h5sdk 调用失败:', e) + } + } - window.tt.requestAuthCode({ - appId: appId, - success: (res: any) => { - alert('✅ 获取授权码成功: ' + res.code?.substring(0, 30)) - console.log('授权码:', res) - }, - fail: (error: any) => { - alert('❌ 获取授权码失败: ' + JSON.stringify(error)) - console.error('错误:', error) - }, - }) + // 尝试使用 bridge + const bridge = (window as any).bridge + if (bridge) { + console.log('🔧 尝试使用 bridge') + try { + if (typeof bridge.requestAuthCode === 'function') { + bridge.requestAuthCode({ + appId: appId, + success: (res: any) => { + alert('✅ bridge 获取授权码成功: ' + res.code?.substring(0, 30)) + console.log('授权码:', res) + }, + fail: (error: any) => { + alert('❌ bridge 获取授权码失败: ' + JSON.stringify(error)) + console.error('错误:', error) + }, + }) + return + } + } catch (e) { + console.error('bridge 调用失败:', e) + } + } + + // 如果都没有,显示详细错误 + alert('❌ 未找到可用的飞书 API\n\n' + + 'window.tt: ' + !!window.tt + '\n' + + 'window.h5sdk: ' + !!(window as any).h5sdk + '\n' + + 'window.bridge: ' + !!(window as any).bridge + '\n' + + 'window.ttjsBridge: ' + !!window.ttjsBridge + ) } return (