Files
x-server-mobile/frontend/src/pages/debug-feishu.tsx
T
2026-02-27 18:01:32 +08:00

261 lines
8.2 KiB
TypeScript

import { useEffect, useState } from 'react'
import { Card, List, NoticeBar } from 'antd-mobile'
import { useNavigate } from 'react-router-dom'
declare global {
interface Window {
tt?: any
ttjsBridge?: any
h5sdk?: any
bridge?: any
}
}
interface EnvInfo {
ua: string
isInFeishu: boolean
hasTT: boolean
hasTTJSBridge: boolean
hasH5SDK: boolean
hasBridge: boolean
ttMethods: string[]
}
export default function DebugFeishuPage() {
const navigate = useNavigate()
const [envInfo, setEnvInfo] = useState<EnvInfo | null>(null)
const [checking, setChecking] = useState(true)
useEffect(() => {
// 多次检测,等待 JSBridge 注入
const checkEnvironment = (attempt: number) => {
console.log(`🔍 第 ${attempt} 次环境检测`)
const ua = navigator.userAgent
const isInFeishu = ua.includes('Lark') || ua.includes('feishu') || ua.includes('lark')
const ttMethods = window.tt ? Object.keys(window.tt) : []
const h5sdk = (window as any).h5sdk
const bridge = (window as any).bridge
const ttjsBridge = window.ttjsBridge
const info = {
ua,
isInFeishu,
hasTT: !!window.tt,
hasTTJSBridge: !!ttjsBridge,
hasH5SDK: !!h5sdk,
hasBridge: !!bridge,
ttMethods,
}
console.log('=== 飞书环境检测 ===')
console.log('尝试次数:', attempt)
console.log('User Agent:', ua)
console.log('在飞书中:', isInFeishu)
console.log('window.tt:', window.tt)
console.log('window.h5sdk:', h5sdk)
console.log('window.bridge:', bridge)
console.log('window.ttjsBridge:', ttjsBridge)
// 检查所有可能的全局对象
const allKeys = Object.keys(window)
const feishuKeys = allKeys.filter(k =>
k.toLowerCase().includes('tt') ||
k.toLowerCase().includes('h5') ||
k.toLowerCase().includes('bridge') ||
k.toLowerCase().includes('lark') ||
k.toLowerCase().includes('feishu') ||
k.toLowerCase().includes('jsbridge')
)
console.log('飞书相关的全局对象:', feishuKeys)
setEnvInfo(info)
setChecking(false)
// 如果检测到任何 API 对象,停止重试
if (window.tt || h5sdk || bridge || ttjsBridge) {
console.log('✅ 检测到飞书 API 对象')
return
}
// 继续重试
if (attempt < 5) {
setTimeout(() => checkEnvironment(attempt + 1), 500)
} else {
console.log('⚠️ 5次检测后仍未发现飞书 API 对象')
}
}
checkEnvironment(1)
}, [])
const testFeishuLogin = async () => {
// 从 URL 获取 company 参数
const urlParams = new URLSearchParams(window.location.search)
const companyParam = urlParams.get('company')
console.log('🔍 URL 中的 company 参数:', companyParam)
// 优先使用 tt 对象
if (window.tt && typeof window.tt.requestAuthCode === 'function') {
console.log('🔧 使用 tt.requestAuthCode')
// 不传 appId,让飞书自动选择(后端会根据 company 参数处理)
window.tt.requestAuthCode({
success: (res: any) => {
alert('✅ 获取授权码成功: ' + res.code?.substring(0, 30))
console.log('授权码:', res)
},
fail: (error: any) => {
alert('❌ 获取授权码失败: ' + JSON.stringify(error))
console.error('错误:', error)
},
})
return
}
// 尝试使用 h5sdk
const h5sdk = (window as any).h5sdk
if (h5sdk) {
console.log('🔧 尝试使用 h5sdk')
try {
if (typeof h5sdk.requestAuthCode === 'function') {
h5sdk.requestAuthCode({
// 不传 appId,让飞书自动选择(后端会根据 company 参数处理)
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)
}
}
// 尝试使用 bridge
const bridge = (window as any).bridge
if (bridge) {
console.log('🔧 尝试使用 bridge')
try {
if (typeof bridge.requestAuthCode === 'function') {
bridge.requestAuthCode({
// 不传 appId,让飞书自动选择(后端会根据 company 参数处理)
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 (
<div style={{ padding: '16px', background: '#f5f5f5', minHeight: '100vh' }}>
<h2>飞书环境调试页面</h2>
{checking && (
<NoticeBar content="正在检测飞书环境..." color="warning" />
)}
{envInfo && !checking && (
<>
<List header="环境信息">
<List.Item extra={envInfo.isInFeishu ? '✅ 是' : '❌ 否'}>
在飞书客户端内
</List.Item>
<List.Item extra={envInfo.hasTT ? '✅ 存在' : '❌ 不存在'}>
window.tt 对象
</List.Item>
<List.Item extra={envInfo.hasTTJSBridge ? '✅ 存在' : '❌ 不存在'}>
window.ttjsBridge 对象
</List.Item>
<List.Item extra={envInfo.hasH5SDK ? '✅ 存在' : '❌ 不存在'}>
window.h5sdk 对象
</List.Item>
<List.Item extra={envInfo.hasBridge ? '✅ 存在' : '❌ 不存在'}>
window.bridge 对象
</List.Item>
<List.Item
extraStyle={{ maxWidth: '200px', fontSize: '12px', wordBreak: 'break-all' }}
>
User Agent
<div style={{ fontSize: '10px', color: '#999' }}>
{envInfo.ua}
</div>
</List.Item>
<List.Item>
tt 对象方法 ({envInfo.ttMethods.length} 个)
<div style={{ fontSize: '10px', color: '#999', textAlign: 'right' }}>
{envInfo.ttMethods.length > 0 ? envInfo.ttMethods.join(', ') : '(无)'}
</div>
</List.Item>
</List>
{!envInfo.hasTT && !envInfo.hasH5SDK && !envInfo.hasBridge && !envInfo.hasTTJSBridge && (
<NoticeBar
content="⚠️ 未检测到飞书 API 对象,可能原因:1. SDK 未加载 2. 需要在飞书应用中打开 3. 移动端 WebView 限制"
color="alert"
/>
)}
</>
)}
<List header="操作">
<List.Item
onClick={testFeishuLogin}
clickable
arrow
>
测试飞书免登
</List.Item>
<List.Item
onClick={() => {
console.log('🔄 重新检测环境')
window.location.reload()
}}
clickable
arrow
>
重新检测
</List.Item>
<List.Item
onClick={() => navigate(-1)}
clickable
arrow
>
返回
</List.Item>
</List>
<Card title="替代方案">
<p>如果飞书免登不可用,请使用以下方式登录:</p>
<p>1. 点击飞书消息中的"查看详情"按钮</p>
<p>2. 系统会跳转到登录页面</p>
<p>3. 使用邮箱/密码登录或扫码登录</p>
<p>4. 登录后返回即可进行审批操作</p>
</Card>
</div>
)
}