feat: 添加飞书环境调试页面和详细日志
修改内容: 1. 新增 debug-feishu.tsx - 飞书环境调试页面 2. approval/detail.tsx - 添加详细的调试日志 3. router/index.tsx - 添加调试页面路由 4. business-trip.ts - 修复 import 错误 调试页面功能: - 检测是否在飞书客户端内 - 显示 window.tt 等对象状态 - 测试飞书免登功能 使用方法: 1. 在飞书客户端中打开: https://xsm.dev.hnxinyouhe.com/debug-feishu 2. 查看环境信息 3. 点击"测试飞书免登"按钮 相关文件: - frontend/src/pages/debug-feishu.tsx - frontend/src/pages/approval/detail.tsx - frontend/src/router/index.tsx - frontend/src/services/business-trip.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -123,31 +123,41 @@ export default function ApprovalDetail() {
|
|||||||
const ua = navigator.userAgent
|
const ua = navigator.userAgent
|
||||||
const isInFeishu = ua.includes('Lark') || ua.includes('feishu') || ua.includes('lark')
|
const isInFeishu = ua.includes('Lark') || ua.includes('feishu') || ua.includes('lark')
|
||||||
|
|
||||||
|
console.log('🔍 tryFeishuLogin 开始')
|
||||||
|
console.log('📱 isInFeishu:', isInFeishu)
|
||||||
|
console.log('🔧 window.tt:', window.tt)
|
||||||
|
|
||||||
if (!isInFeishu || !window.tt) {
|
if (!isInFeishu || !window.tt) {
|
||||||
console.log('❌ 不在飞书内或 tt 对象不可用')
|
console.log('❌ 不在飞书内或 tt 对象不可用,跳过免登')
|
||||||
resolve(false)
|
resolve(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🚀 尝试飞书免登')
|
console.log('🚀 开始调用 tt.requestAuthCode')
|
||||||
const appId = 'cli_a8033d607cba500d'
|
const appId = 'cli_a8033d607cba500d'
|
||||||
|
|
||||||
window.tt.requestAuthCode({
|
window.tt.requestAuthCode({
|
||||||
appId: appId,
|
appId: appId,
|
||||||
success: async (res: any) => {
|
success: async (res: any) => {
|
||||||
console.log('✅ 飞书免登成功:', res.code?.substring(0, 20))
|
console.log('✅ tt.requestAuthCode 成功:', res)
|
||||||
|
console.log('🔑 authCode:', res.code?.substring(0, 30) + '...')
|
||||||
try {
|
try {
|
||||||
|
console.log('📡 调用后端登录API...')
|
||||||
const result = await authApi.feishuLogin(res.code)
|
const result = await authApi.feishuLogin(res.code)
|
||||||
|
console.log('✅ 后端登录成功:', result.user?.name)
|
||||||
login(result.user, result.token)
|
login(result.user, result.token)
|
||||||
Toast.show('登录成功')
|
Toast.show('登录成功')
|
||||||
resolve(true)
|
resolve(true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ 飞书登录API失败:', error)
|
console.error('❌ 飞书登录API失败:', error)
|
||||||
|
Toast.show('登录失败: ' + (error.message || '未知错误'))
|
||||||
resolve(false)
|
resolve(false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (error: any) => {
|
fail: (error: any) => {
|
||||||
console.error('❌ 飞书免登失败:', error)
|
console.error('❌ tt.requestAuthCode 失败:', error)
|
||||||
|
console.error('❌ 错误详情:', JSON.stringify(error))
|
||||||
|
Toast.show('免登失败: ' + (error.errorMessage || error.errMsg || '未知错误'))
|
||||||
resolve(false)
|
resolve(false)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -158,35 +168,53 @@ export default function ApprovalDetail() {
|
|||||||
const loadDetail = async () => {
|
const loadDetail = async () => {
|
||||||
if (!id) return
|
if (!id) return
|
||||||
|
|
||||||
// 检查登录状态,如果未登录且在飞书内,先尝试飞书免登
|
console.log('📋 loadDetail 开始, id:', id)
|
||||||
|
|
||||||
|
// 检查登录状态
|
||||||
const token = localStorage.getItem('token')
|
const token = localStorage.getItem('token')
|
||||||
const isLoggedIn = !!user
|
const isLoggedIn = !!user
|
||||||
|
console.log('🔐 登录状态检查:', { isLoggedIn: !!isLoggedIn, hasToken: !!token, userId: user?.id })
|
||||||
|
|
||||||
if (!isLoggedIn && !token) {
|
if (!isLoggedIn && !token) {
|
||||||
|
console.log('⚠️ 用户未登录,检查飞书环境')
|
||||||
|
|
||||||
const ua = navigator.userAgent
|
const ua = navigator.userAgent
|
||||||
const isInFeishu = ua.includes('Lark') || ua.includes('feishu') || ua.includes('lark')
|
const isInFeishu = ua.includes('Lark') || ua.includes('feishu') || ua.includes('lark')
|
||||||
|
console.log('📱 飞书环境检测:', { isInFeishu, ua: ua.substring(0, 100) })
|
||||||
|
console.log('🔧 tt 对象:', window.tt)
|
||||||
|
|
||||||
if (isInFeishu && window.tt && !tryingFeishuLogin) {
|
if (isInFeishu && window.tt && !tryingFeishuLogin) {
|
||||||
|
console.log('🚀 开始飞书免登...')
|
||||||
setTryingFeishuLogin(true)
|
setTryingFeishuLogin(true)
|
||||||
const success = await tryFeishuLogin()
|
const success = await tryFeishuLogin()
|
||||||
setTryingFeishuLogin(false)
|
setTryingFeishuLogin(false)
|
||||||
|
console.log('✅ 免登结果:', success)
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
// 免登失败,跳转到登录页面
|
console.log('❌ 免登失败,跳转登录页')
|
||||||
|
Toast.show('免登失败,请使用其他方式登录')
|
||||||
window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search)
|
window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
console.log('✅ 免登成功,继续加载详情')
|
||||||
} else {
|
} else {
|
||||||
// 不在飞书内或免登不可用,跳转到登录页面
|
console.log('⚠️ 不在飞书内或 tt 对象不可用')
|
||||||
|
Toast.show('请先登录')
|
||||||
window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search)
|
window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
console.log('✅ 用户已登录,直接加载详情')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('🔄 开始加载审批详情...')
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const data = await approvalApi.getDetail(Number(id))
|
const data = await approvalApi.getDetail(Number(id))
|
||||||
|
console.log('✅ 详情加载成功:', data)
|
||||||
setDetail(data)
|
setDetail(data)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
console.error('❌ 详情加载失败:', error)
|
||||||
Toast.show(error.message || '加载失败')
|
Toast.show(error.message || '加载失败')
|
||||||
navigate(-1)
|
navigate(-1)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -195,6 +223,17 @@ export default function ApprovalDetail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.log('🔍 ApprovalDetail mount, id:', id)
|
||||||
|
console.log('🔍 User:', user)
|
||||||
|
console.log('🔍 Token:', localStorage.getItem('token')?.substring(0, 20) + '...')
|
||||||
|
|
||||||
|
// 检查飞书环境
|
||||||
|
const ua = navigator.userAgent
|
||||||
|
const isInFeishu = ua.includes('Lark') || ua.includes('feishu') || ua.includes('lark')
|
||||||
|
console.log('🔍 User Agent:', ua)
|
||||||
|
console.log('🔍 在飞书中:', isInFeishu)
|
||||||
|
console.log('🔍 window.tt:', window.tt)
|
||||||
|
|
||||||
loadDetail()
|
loadDetail()
|
||||||
}, [id])
|
}, [id])
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const ua = navigator.userAgent
|
||||||
|
const isInFeishu = ua.includes('Lark') || ua.includes('feishu') || ua.includes('lark')
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('=== 飞书环境调试 ===')
|
||||||
|
console.log('User Agent:', ua)
|
||||||
|
console.log('在飞书中:', isInFeishu)
|
||||||
|
console.log('window.tt:', window.tt)
|
||||||
|
console.log('tt 对象方法:', ttMethods)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const testFeishuLogin = async () => {
|
||||||
|
if (!window.tt) {
|
||||||
|
alert('tt 对象不存在,无法测试')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const appId = 'cli_a8033d607cba500d'
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<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>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<List header="操作">
|
||||||
|
<List.Item
|
||||||
|
onClick={testFeishuLogin}
|
||||||
|
clickable
|
||||||
|
arrow
|
||||||
|
>
|
||||||
|
测试飞书免登
|
||||||
|
</List.Item>
|
||||||
|
<List.Item
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
clickable
|
||||||
|
arrow
|
||||||
|
>
|
||||||
|
返回
|
||||||
|
</List.Item>
|
||||||
|
</List>
|
||||||
|
|
||||||
|
<NoticeBar content="请在飞书客户端中打开此页面进行测试" color="info" />
|
||||||
|
|
||||||
|
<Card title="使用说明">
|
||||||
|
<p>1. 在飞书客户端中发送此链接给自己</p>
|
||||||
|
<p>2. 点击链接打开此调试页面</p>
|
||||||
|
<p>3. 查看环境信息</p>
|
||||||
|
<p>4. 点击"测试飞书免登"按钮</p>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import ApprovalList from '@/pages/approval/list'
|
|||||||
import ApprovalDetail from '@/pages/approval/detail'
|
import ApprovalDetail from '@/pages/approval/detail'
|
||||||
import Login from '@/pages/login'
|
import Login from '@/pages/login'
|
||||||
import SimpleLogin from '@/pages/login-simple'
|
import SimpleLogin from '@/pages/login-simple'
|
||||||
|
import DebugFeishu from '@/pages/debug-feishu'
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@@ -35,6 +36,7 @@ const router = createBrowserRouter([
|
|||||||
},
|
},
|
||||||
{ path: '/login', element: <Login /> },
|
{ path: '/login', element: <Login /> },
|
||||||
{ path: '/login-simple', element: <SimpleLogin /> },
|
{ path: '/login-simple', element: <SimpleLogin /> },
|
||||||
|
{ path: '/debug-feishu', element: <DebugFeishu /> },
|
||||||
{ path: '*', element: <Navigate to='/login' replace /> },
|
{ path: '*', element: <Navigate to='/login' replace /> },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { api } from './api'
|
import api from './api'
|
||||||
|
|
||||||
// 出差申请接口类型定义
|
// 出差申请接口类型定义
|
||||||
export interface BusinessTripItinerary {
|
export interface BusinessTripItinerary {
|
||||||
|
|||||||
Reference in New Issue
Block a user