297 lines
8.3 KiB
JavaScript
297 lines
8.3 KiB
JavaScript
/**
|
||
* APP桥接工具函数
|
||
* 用于H5页面与原生APP之间的通信
|
||
*/
|
||
|
||
import {COMMON_HEADERS} from "@/utils/http.js";
|
||
|
||
/**
|
||
* 检测是否为iOS系统
|
||
* @returns {boolean}
|
||
*/
|
||
export function ios() {
|
||
return /iPad|iPhone|iPod/.test(navigator.userAgent)
|
||
}
|
||
|
||
/**
|
||
* 检测是否为Android系统
|
||
* @returns {boolean}
|
||
*/
|
||
export function android() {
|
||
return /Android/.test(navigator.userAgent)
|
||
}
|
||
|
||
/**
|
||
* 检测是否在APP环境中
|
||
* @returns {boolean}
|
||
*/
|
||
export function isInApp() {
|
||
return !!(window.app || window.webkit || window.FlutterPageControl)
|
||
}
|
||
|
||
/**
|
||
* 关闭当前页面(返回到Flutter上一页)
|
||
*/
|
||
export function closePage() {
|
||
try {
|
||
if (window.app && window.app.closePage) {
|
||
// 使用注入的方法
|
||
window.app.closePage()
|
||
} else if (window.FlutterPageControl) {
|
||
// 直接使用Flutter通道
|
||
window.FlutterPageControl.postMessage('close_page')
|
||
} else {
|
||
// 降级处理:浏览器环境
|
||
// if (window.history.length > 1) {
|
||
// window.history.back()
|
||
// } else {
|
||
// window.close()
|
||
// }
|
||
}
|
||
} catch (error) {
|
||
console.error('关闭页面失败:', error)
|
||
// 降级处理
|
||
window.history.back()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 退出APP
|
||
*/
|
||
export function exitApp() {
|
||
try {
|
||
if (window.app && window.app.exitApp) {
|
||
window.app.exitApp()
|
||
} else if (window.FlutterPageControl) {
|
||
window.FlutterPageControl.postMessage('exit_app')
|
||
}
|
||
} catch (error) {
|
||
console.error('退出APP失败:', error)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理返回按钮逻辑
|
||
* @param {boolean} isHomePage 是否为首页
|
||
*/
|
||
export function handleBackButton(isHomePage = false) {
|
||
if (isHomePage) {
|
||
// 首页返回:关闭页面
|
||
closePage()
|
||
} else {
|
||
// 非首页:正常返回
|
||
window.history.back()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 连接APP并获取访问参数
|
||
* @param {Function} renderFun 回调函数,接收APP传递的access参数
|
||
*/
|
||
export function connectApplication(renderFun) {
|
||
window.renderData = renderFun
|
||
// 兼容历史版本
|
||
window.getIosAccessOriginParam = renderFun
|
||
|
||
try {
|
||
if (android()) {
|
||
console.debug('Flutter Android WebView 检测')
|
||
console.debug('window.app 对象:', window.app)
|
||
|
||
// 检查Flutter注入的接口
|
||
if (window.app) {
|
||
console.debug('window.app 的所有方法:')
|
||
console.debug(Object.keys(window.app))
|
||
|
||
// 检查具体的方法
|
||
console.debug('getAccessOrigin 方法存在?', typeof window.app.getAccessOrigin)
|
||
console.debug('getAccessOrigin 是函数?', typeof window.app.getAccessOrigin === 'function')
|
||
}
|
||
|
||
// 等待Flutter注入完成
|
||
const waitForFlutterInterface = () => {
|
||
let attempts = 0;
|
||
const maxAttempts = 50; // 最多等待5秒
|
||
|
||
const checkInterface = () => {
|
||
attempts++;
|
||
console.debug(`尝试第 ${attempts} 次检查Flutter接口...`)
|
||
|
||
if (window.app && window.app.getAccessOrigin && typeof window.app.getAccessOrigin === 'function') {
|
||
console.debug('Flutter接口检测成功,调用 getAccessOrigin')
|
||
try {
|
||
const result = window.app.getAccessOrigin()
|
||
console.debug('getAccessOrigin 返回结果:', result)
|
||
renderFun(result)
|
||
} catch (error) {
|
||
console.error('调用 getAccessOrigin 出错:', error)
|
||
}
|
||
} else if (attempts < maxAttempts) {
|
||
// 继续等待
|
||
setTimeout(checkInterface, 100)
|
||
} else {
|
||
console.error('Flutter接口注入超时,使用默认数据')
|
||
// 使用默认的开发数据
|
||
const mockAccess = JSON.stringify({
|
||
'Authorization': 'Bearer FLUTTER_MOCK_TOKEN',
|
||
'Req-Lang': 'en',
|
||
'Req-App-Intel': 'build=1.0;version=2.1;channel=flutter;Req-Imei=flutter-device',
|
||
'Req-Sys-Origin': 'origin=LIKEI;originChild=LIKEI'
|
||
})
|
||
renderFun(mockAccess)
|
||
}
|
||
}
|
||
|
||
checkInterface()
|
||
}
|
||
|
||
// 如果window.app已存在,直接检查,否则等待
|
||
if (window.app) {
|
||
waitForFlutterInterface()
|
||
} else {
|
||
console.debug('等待Flutter注入window.app...')
|
||
// 监听window.app的创建
|
||
let checkCount = 0
|
||
const checkApp = () => {
|
||
checkCount++
|
||
if (window.app) {
|
||
console.debug('检测到window.app,开始连接')
|
||
waitForFlutterInterface()
|
||
} else if (checkCount < 50) {
|
||
setTimeout(checkApp, 100)
|
||
} else {
|
||
console.error('等待window.app超时')
|
||
// 手动通知Flutter注入接口
|
||
if (window.FlutterApp && window.FlutterApp.postMessage) {
|
||
window.FlutterApp.postMessage('requestAccessOrigin')
|
||
}
|
||
}
|
||
}
|
||
checkApp()
|
||
}
|
||
}
|
||
|
||
/*else if (ios()) {
|
||
console.debug('ios access detected')
|
||
// iOS可能需要特殊处理,这里先使用通用方法
|
||
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.getAccessOrigin) {
|
||
window.webkit.messageHandlers.getAccessOrigin.postMessage({})
|
||
} else if (window.app && window.app.getAccessOrigin) {
|
||
renderFun(window.app.getAccessOrigin())
|
||
}
|
||
}*/
|
||
|
||
else {
|
||
console.debug('非移动设备环境')
|
||
// 非APP环境,可能是浏览器调试
|
||
console.warn('Not in APP environment, using mock data for development')
|
||
// 开发环境可以提供模拟数据
|
||
if (process.env.NODE_ENV === 'development') {
|
||
const mockAccess = JSON.stringify({
|
||
'Authorization': COMMON_HEADERS.authorization,
|
||
'Req-Lang': COMMON_HEADERS['req-lang'],
|
||
'Req-App-Intel': COMMON_HEADERS['req-app-intel'],
|
||
'Req-Sys-Origin': COMMON_HEADERS['req-sys-origin']
|
||
})
|
||
setTimeout(() => renderFun(mockAccess), 100)
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to connect to APP:', error)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 解析APP传递的访问参数
|
||
* @param {string} access JSON字符串格式的访问参数
|
||
* @returns {Object} 解析后的参数对象
|
||
*/
|
||
export function parseAccessOrigin(access) {
|
||
try {
|
||
const accessParam = JSON.parse(access)
|
||
return {
|
||
success: true,
|
||
data: accessParam
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to parse access origin:', error)
|
||
return {
|
||
success: false,
|
||
error: 'accessOrigin Error.',
|
||
data: null
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 解析请求头信息
|
||
* @param {Object} head 请求头对象
|
||
* @returns {Object} 解析后的头信息
|
||
*/
|
||
export function parseHeader(head) {
|
||
if (!head) {
|
||
return {}
|
||
}
|
||
|
||
/**
|
||
* 解析键值对字符串 (如: "key1=value1;key2=value2")
|
||
* @param {string} values
|
||
* @returns {Object}
|
||
*/
|
||
function getKeyValObj(values) {
|
||
const keyValBody = {}
|
||
if (!values) return keyValBody
|
||
|
||
const valueArray = values.split(';')
|
||
for (let index = 0; index < valueArray.length; index++) {
|
||
const keyVal = valueArray[index]
|
||
if (keyVal && keyVal.includes('=')) {
|
||
const keyValArray = keyVal.split('=')
|
||
keyValBody[keyValArray[0]] = keyValArray[1]
|
||
}
|
||
}
|
||
return keyValBody
|
||
}
|
||
|
||
const reqAppIntel = head['Req-App-Intel'] || ''
|
||
const reqSysOrigin = head['Req-Sys-Origin'] || ''
|
||
const reqAppIntelObj = getKeyValObj(reqAppIntel)
|
||
const reqSysOriginObj = getKeyValObj(reqSysOrigin)
|
||
|
||
return {
|
||
reqLang: head['Req-Lang'],
|
||
authorization: head['Authorization'],
|
||
buildVersion: reqAppIntelObj['build'],
|
||
appVersion: reqAppIntelObj['version'],
|
||
appChannel: reqAppIntelObj['channel'],
|
||
reqImei: reqAppIntelObj['Req-Imei'],
|
||
sysOrigin: reqSysOriginObj['origin'],
|
||
sysOriginChild: reqSysOriginObj['child']
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置HTTP请求的默认头部信息
|
||
* @param {Object} headerInfo 解析后的头部信息
|
||
*/
|
||
export async function setHttpHeaders(headerInfo) {
|
||
|
||
try {
|
||
const httpModule = await import('../utils/http.js')
|
||
if (httpModule.setHeadersFromApp) {
|
||
httpModule.setHeadersFromApp(headerInfo)
|
||
}
|
||
} catch(error) {
|
||
console.error('Failed to update HTTP headers:', error)
|
||
}
|
||
|
||
console.debug('🔑 Authorization token set:', headerInfo.authorization)
|
||
console.debug('🌍 Language set:', headerInfo.reqLang)
|
||
console.debug('📱 App info:', {
|
||
version: headerInfo.appVersion,
|
||
build: headerInfo.buildVersion,
|
||
channel: headerInfo.appChannel,
|
||
system: headerInfo.sysOrigin
|
||
})
|
||
}
|