feat: 优化飞书免登失败后的用户体验
问题: - 移动端飞书 WebView 中 JSBridge SDK 未注入 - 导致免登功能不可用 修复: 1. debug-feishu.tsx - 多次检测环境,添加重试和重新检测功能 2. login/index.tsx - 支持 redirect 参数,登录成功后返回原页面 3. QRCodeLogin.tsx - 支持 redirect 参数 用户体验改进: - 免登失败时,友好提示用户使用其他登录方式 - 登录成功后自动返回到原页面(审批详情页) - 用户完成登录后可直接进行审批操作 替代方案: 1. 点击飞书消息中的"查看详情"按钮 2. 跳转到登录页面 3. 使用邮箱/密码或扫码登录 4. 登录成功后自动返回审批详情页 5. 直接进行审批操作 相关文件: - frontend/src/pages/debug-feishu.tsx - frontend/src/pages/login/index.tsx - frontend/src/pages/login/QRCodeLogin.tsx Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,37 +24,70 @@ interface EnvInfo {
|
||||
export default function DebugFeishuPage() {
|
||||
const navigate = useNavigate()
|
||||
const [envInfo, setEnvInfo] = useState<EnvInfo | null>(null)
|
||||
const [checking, setChecking] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
// 延迟检测,等待 JSBridge 注入
|
||||
setTimeout(() => {
|
||||
// 多次检测,等待 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
|
||||
|
||||
setEnvInfo({
|
||||
const info = {
|
||||
ua,
|
||||
isInFeishu,
|
||||
hasTT: !!window.tt,
|
||||
hasTTJSBridge: !!window.ttjsBridge,
|
||||
hasH5SDK: !!(window as any).h5sdk,
|
||||
hasBridge: !!(window as any).bridge,
|
||||
hasTTJSBridge: !!ttjsBridge,
|
||||
hasH5SDK: !!h5sdk,
|
||||
hasBridge: !!bridge,
|
||||
ttMethods,
|
||||
})
|
||||
}
|
||||
|
||||
console.log('=== 飞书环境调试 ===')
|
||||
console.log('=== 飞书环境检测 ===')
|
||||
console.log('尝试次数:', attempt)
|
||||
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('window.h5sdk:', h5sdk)
|
||||
console.log('window.bridge:', bridge)
|
||||
console.log('window.ttjsBridge:', ttjsBridge)
|
||||
|
||||
// 检查所有全局对象
|
||||
console.log('所有全局对象:', Object.keys(window).filter(k => k.includes('tt') || k.includes('h5') || k.includes('bridge') || k.includes('lark') || k.includes('feishu')))
|
||||
}, 500)
|
||||
// 检查所有可能的全局对象
|
||||
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 () => {
|
||||
@@ -138,32 +171,51 @@ export default function DebugFeishuPage() {
|
||||
<div style={{ padding: '16px', background: '#f5f5f5', minHeight: '100vh' }}>
|
||||
<h2>飞书环境调试页面</h2>
|
||||
|
||||
{envInfo && (
|
||||
<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
|
||||
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>
|
||||
{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="操作">
|
||||
@@ -174,6 +226,16 @@ export default function DebugFeishuPage() {
|
||||
>
|
||||
测试飞书免登
|
||||
</List.Item>
|
||||
<List.Item
|
||||
onClick={() => {
|
||||
console.log('🔄 重新检测环境')
|
||||
window.location.reload()
|
||||
}}
|
||||
clickable
|
||||
arrow
|
||||
>
|
||||
重新检测
|
||||
</List.Item>
|
||||
<List.Item
|
||||
onClick={() => navigate(-1)}
|
||||
clickable
|
||||
@@ -183,13 +245,12 @@ export default function DebugFeishuPage() {
|
||||
</List.Item>
|
||||
</List>
|
||||
|
||||
<NoticeBar content="请在飞书客户端中打开此页面进行测试" color="info" />
|
||||
|
||||
<Card title="使用说明">
|
||||
<p>1. 在飞书客户端中发送此链接给自己</p>
|
||||
<p>2. 点击链接打开此调试页面</p>
|
||||
<p>3. 查看环境信息</p>
|
||||
<p>4. 点击"测试飞书免登"按钮</p>
|
||||
<Card title="替代方案">
|
||||
<p>如果飞书免登不可用,请使用以下方式登录:</p>
|
||||
<p>1. 点击飞书消息中的"查看详情"按钮</p>
|
||||
<p>2. 系统会跳转到登录页面</p>
|
||||
<p>3. 使用邮箱/密码登录或扫码登录</p>
|
||||
<p>4. 登录后返回即可进行审批操作</p>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -234,9 +234,18 @@ export default function QRCodeLogin({ visible, onClose }: QRCodeLoginProps) {
|
||||
|
||||
Toast.show('登录成功')
|
||||
setTimeout(() => {
|
||||
console.log('🚀 Navigating to dashboard...')
|
||||
console.log('🚀 Navigating...')
|
||||
onClose()
|
||||
navigate('/dashboard')
|
||||
|
||||
// 检查是否有 redirect 参数
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const redirect = urlParams.get('redirect')
|
||||
if (redirect) {
|
||||
console.log('🔗 登录成功,跳转到:', redirect)
|
||||
window.location.href = redirect
|
||||
} else {
|
||||
navigate('/dashboard')
|
||||
}
|
||||
}, 500)
|
||||
} else if (result.data.status === 'scanning') {
|
||||
console.log('📱 Status: scanning')
|
||||
|
||||
@@ -218,7 +218,16 @@ export default function Login() {
|
||||
if (result.code === 0) {
|
||||
login(result.data.user, result.data.token)
|
||||
Toast.show('登录成功')
|
||||
navigate('/dashboard')
|
||||
|
||||
// 检查是否有 redirect 参数
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const redirect = urlParams.get('redirect')
|
||||
if (redirect) {
|
||||
console.log('🔗 登录成功,跳转到:', redirect)
|
||||
window.location.href = redirect
|
||||
} else {
|
||||
navigate('/dashboard')
|
||||
}
|
||||
} else {
|
||||
Toast.show(result.message || '登录失败')
|
||||
setLoading(false)
|
||||
|
||||
Reference in New Issue
Block a user