更新版
This commit is contained in:
parent
aa9c365cdf
commit
11c24e9d0c
271
public/app-bridge-test.html
Normal file
271
public/app-bridge-test.html
Normal file
@ -0,0 +1,271 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>APP Bridge Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.test-container {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.test-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.test-section h3 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
}
|
||||
button {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
button:hover {
|
||||
background: #005bb5;
|
||||
}
|
||||
.result {
|
||||
background: #f0f0f0;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
white-space: pre-wrap;
|
||||
font-family: monospace;
|
||||
}
|
||||
.status {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
margin: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.status.success { background: #d4edda; color: #155724; }
|
||||
.status.error { background: #f8d7da; color: #721c24; }
|
||||
.status.warning { background: #fff3cd; color: #856404; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="test-container">
|
||||
<h1>APP Bridge 通信测试</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>环境检测</h3>
|
||||
<div>
|
||||
<span class="status" id="env-status">检测中...</span>
|
||||
<span id="env-details"></span>
|
||||
</div>
|
||||
<div class="result" id="env-result"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>APP对象检测</h3>
|
||||
<button onclick="checkAppObject()">检测APP对象</button>
|
||||
<div class="result" id="app-object-result"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>模拟APP调用</h3>
|
||||
<button onclick="simulateAppCall()">模拟Android APP</button>
|
||||
<button onclick="simulateIOSCall()">模拟iOS APP</button>
|
||||
<button onclick="simulateWebCall()">模拟Web环境</button>
|
||||
<div class="result" id="simulate-result"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>连接APP测试</h3>
|
||||
<button onclick="testAppConnection()">测试连接</button>
|
||||
<div class="result" id="connection-result"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 检测环境
|
||||
function detectEnvironment() {
|
||||
const userAgent = navigator.userAgent;
|
||||
const isIOS = /iPad|iPhone|iPod/.test(userAgent);
|
||||
const isAndroid = /Android/.test(userAgent);
|
||||
const hasAppObject = !!(window.app);
|
||||
const hasWebkitObject = !!(window.webkit && window.webkit.messageHandlers);
|
||||
|
||||
const envStatus = document.getElementById('env-status');
|
||||
const envDetails = document.getElementById('env-details');
|
||||
const envResult = document.getElementById('env-result');
|
||||
|
||||
let status = 'warning';
|
||||
let details = '未知环境';
|
||||
|
||||
if (isIOS) {
|
||||
details = 'iOS 环境';
|
||||
status = hasWebkitObject ? 'success' : 'error';
|
||||
} else if (isAndroid) {
|
||||
details = 'Android 环境';
|
||||
status = hasAppObject ? 'success' : 'error';
|
||||
} else {
|
||||
details = 'Web/桌面环境';
|
||||
status = 'warning';
|
||||
}
|
||||
|
||||
envStatus.className = `status ${status}`;
|
||||
envStatus.textContent = status === 'success' ? '✓ 支持' : status === 'error' ? '✗ 不支持' : '⚠ 警告';
|
||||
envDetails.textContent = details;
|
||||
|
||||
envResult.textContent = JSON.stringify({
|
||||
userAgent: userAgent,
|
||||
isIOS: isIOS,
|
||||
isAndroid: isAndroid,
|
||||
hasAppObject: hasAppObject,
|
||||
hasWebkitObject: hasWebkitObject
|
||||
}, null, 2);
|
||||
}
|
||||
|
||||
// 检测APP对象
|
||||
function checkAppObject() {
|
||||
const result = document.getElementById('app-object-result');
|
||||
const appInfo = {};
|
||||
|
||||
if (window.app) {
|
||||
appInfo.app = {
|
||||
exists: true,
|
||||
methods: Object.getOwnPropertyNames(window.app)
|
||||
};
|
||||
|
||||
if (typeof window.app.getAccessOrigin === 'function') {
|
||||
try {
|
||||
appInfo.app.getAccessOriginResult = 'Function available';
|
||||
} catch (e) {
|
||||
appInfo.app.getAccessOriginResult = 'Error: ' + e.message;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
appInfo.app = { exists: false };
|
||||
}
|
||||
|
||||
if (window.webkit && window.webkit.messageHandlers) {
|
||||
appInfo.webkit = {
|
||||
exists: true,
|
||||
messageHandlers: Object.keys(window.webkit.messageHandlers)
|
||||
};
|
||||
} else {
|
||||
appInfo.webkit = { exists: false };
|
||||
}
|
||||
|
||||
result.textContent = JSON.stringify(appInfo, null, 2);
|
||||
}
|
||||
|
||||
// 模拟APP调用
|
||||
function simulateAppCall() {
|
||||
const mockData = JSON.stringify({
|
||||
'Authorization': 'Bearer MOCK_TOKEN_12345',
|
||||
'Req-Lang': 'en-US',
|
||||
'Req-App-Intel': 'version=2.1.0;build=100;channel=official;Req-Imei=mock_imei_12345',
|
||||
'Req-Sys-Origin': 'origin=android;child=phone'
|
||||
});
|
||||
|
||||
// 创建模拟APP对象
|
||||
window.app = {
|
||||
getAccessOrigin: function() {
|
||||
return mockData;
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('simulate-result').textContent =
|
||||
'Android APP对象已模拟创建\n' +
|
||||
'Mock数据: ' + mockData;
|
||||
}
|
||||
|
||||
function simulateIOSCall() {
|
||||
const mockData = JSON.stringify({
|
||||
'Authorization': 'Bearer MOCK_IOS_TOKEN_67890',
|
||||
'Req-Lang': 'zh-CN',
|
||||
'Req-App-Intel': 'version=2.1.0;build=100;channel=appstore;Req-Imei=mock_ios_imei',
|
||||
'Req-Sys-Origin': 'origin=ios;child=iphone'
|
||||
});
|
||||
|
||||
// 创建模拟WebKit对象
|
||||
window.webkit = {
|
||||
messageHandlers: {
|
||||
getAccessOrigin: {
|
||||
postMessage: function(message) {
|
||||
// 模拟异步回调
|
||||
setTimeout(() => {
|
||||
if (window.renderData) {
|
||||
window.renderData(mockData);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('simulate-result').textContent =
|
||||
'iOS WebKit对象已模拟创建\n' +
|
||||
'Mock数据: ' + mockData;
|
||||
}
|
||||
|
||||
function simulateWebCall() {
|
||||
// 清除模拟对象
|
||||
delete window.app;
|
||||
delete window.webkit;
|
||||
|
||||
document.getElementById('simulate-result').textContent =
|
||||
'Web环境已模拟(无APP对象)';
|
||||
}
|
||||
|
||||
// 测试APP连接
|
||||
function testAppConnection() {
|
||||
const result = document.getElementById('connection-result');
|
||||
result.textContent = '正在测试连接...\n';
|
||||
|
||||
// 导入并测试APP桥接模块
|
||||
import('/src/utils/appBridge.js').then(bridge => {
|
||||
result.textContent += '✓ APP桥接模块加载成功\n';
|
||||
|
||||
const isInApp = bridge.isInApp();
|
||||
result.textContent += `APP环境检测: ${isInApp}\n`;
|
||||
|
||||
// 测试连接
|
||||
bridge.connectApplication((access) => {
|
||||
result.textContent += '✓ 收到APP回调\n';
|
||||
result.textContent += `原始数据: ${access}\n`;
|
||||
|
||||
const parseResult = bridge.parseAccessOrigin(access);
|
||||
if (parseResult.success) {
|
||||
result.textContent += '✓ 数据解析成功\n';
|
||||
|
||||
const headerInfo = bridge.parseHeader(parseResult.data);
|
||||
result.textContent += '✓ 头部信息解析成功\n';
|
||||
result.textContent += `解析结果: ${JSON.stringify(headerInfo, null, 2)}\n`;
|
||||
|
||||
bridge.setHttpHeaders(headerInfo);
|
||||
result.textContent += '✓ HTTP头部设置完成\n';
|
||||
} else {
|
||||
result.textContent += `✗ 数据解析失败: ${parseResult.error}\n`;
|
||||
}
|
||||
});
|
||||
|
||||
}).catch(error => {
|
||||
result.textContent += `✗ 模块加载失败: ${error.message}\n`;
|
||||
console.error('Error loading app bridge:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// 页面加载时自动检测环境
|
||||
document.addEventListener('DOMContentLoaded', detectEnvironment);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -95,6 +95,11 @@ const router = createRouter({
|
||||
name: 'agency-center',
|
||||
component: () => import('../views/AgencyCenterView.vue'),
|
||||
},
|
||||
{
|
||||
path: '/not_app',
|
||||
name: 'not-app',
|
||||
component: () => import('../views/NotAppView.vue'),
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
162
src/utils/appBridge.js
Normal file
162
src/utils/appBridge.js
Normal file
@ -0,0 +1,162 @@
|
||||
/**
|
||||
* APP桥接工具函数
|
||||
* 用于H5页面与原生APP之间的通信
|
||||
*/
|
||||
|
||||
/**
|
||||
* 检测是否为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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接APP并获取访问参数
|
||||
* @param {Function} renderFun 回调函数,接收APP传递的access参数
|
||||
*/
|
||||
export function connectApplication(renderFun) {
|
||||
window.renderData = renderFun
|
||||
// 兼容历史版本
|
||||
window.getIosAccessOriginParam = renderFun
|
||||
|
||||
try {
|
||||
if (ios()) {
|
||||
// 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 if (android()) {
|
||||
// Android处理
|
||||
if (window.app && window.app.getAccessOrigin) {
|
||||
renderFun(window.app.getAccessOrigin())
|
||||
}
|
||||
} else {
|
||||
// 非APP环境,可能是浏览器调试
|
||||
console.warn('Not in APP environment, using mock data for development')
|
||||
// 开发环境可以提供模拟数据
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const mockAccess = JSON.stringify({
|
||||
'Authorization': 'Bearer mock-token-for-development',
|
||||
'Req-Lang': 'en',
|
||||
'Req-App-Intel': 'build=1.0;version=2.1;channel=official;Req-Imei=mock-imei',
|
||||
'Req-Sys-Origin': 'origin=web;child=browser'
|
||||
})
|
||||
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 function setHttpHeaders(headerInfo) {
|
||||
// 动态导入http.js中的函数来避免循环依赖
|
||||
import('../utils/http.js').then(httpModule => {
|
||||
if (httpModule.setHeadersFromApp) {
|
||||
httpModule.setHeadersFromApp(headerInfo)
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('Failed to update HTTP headers:', error)
|
||||
})
|
||||
|
||||
console.log('🔑 Authorization token set:', headerInfo.authorization)
|
||||
console.log('🌍 Language set:', headerInfo.reqLang)
|
||||
console.log('📱 App info:', {
|
||||
version: headerInfo.appVersion,
|
||||
build: headerInfo.buildVersion,
|
||||
channel: headerInfo.appChannel,
|
||||
system: headerInfo.sysOrigin
|
||||
})
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
// HTTP请求配置
|
||||
const API_BASE_URL = 'https://api.likeichat.com'
|
||||
|
||||
// 公共请求头配置
|
||||
const COMMON_HEADERS = {
|
||||
// 默认公共请求头配置
|
||||
let COMMON_HEADERS = {
|
||||
'authorization': 'Bearer F77126B08E13987C3AD88B6015C5B777.djIlM0ExOTU0MDU5NzkzOTYzMzkzMDI1JTNBTElLRUklM0ExNzU3NzYzODU5NzE1JTNBMTc1NTE3MTg1OTcxNQ==',
|
||||
'req-app-intel': 'version=1.0.0;build=1;model=SM-G9550;sysVersion=9;channel=Google',
|
||||
'req-client': 'Android',
|
||||
@ -13,6 +13,64 @@ const COMMON_HEADERS = {
|
||||
'req-zone': 'Asia/Shanghai',
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新公共请求头
|
||||
* @param {object} headers - 要更新的请求头
|
||||
*/
|
||||
export function updateCommonHeaders(headers) {
|
||||
COMMON_HEADERS = {
|
||||
...COMMON_HEADERS,
|
||||
...headers
|
||||
}
|
||||
console.log('🔄 Updated common headers:', COMMON_HEADERS)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从APP头部信息设置HTTP请求头
|
||||
* @param {object} headerInfo - APP传递的头部信息
|
||||
*/
|
||||
export function setHeadersFromApp(headerInfo) {
|
||||
const updatedHeaders = {}
|
||||
|
||||
// 设置Authorization
|
||||
if (headerInfo.authorization) {
|
||||
updatedHeaders.authorization = headerInfo.authorization
|
||||
}
|
||||
|
||||
// 设置语言
|
||||
if (headerInfo.reqLang) {
|
||||
updatedHeaders['req-lang'] = headerInfo.reqLang
|
||||
}
|
||||
|
||||
// 设置APP信息
|
||||
if (headerInfo.appVersion || headerInfo.buildVersion || headerInfo.appChannel) {
|
||||
const appIntelParts = []
|
||||
if (headerInfo.appVersion) appIntelParts.push(`version=${headerInfo.appVersion}`)
|
||||
if (headerInfo.buildVersion) appIntelParts.push(`build=${headerInfo.buildVersion}`)
|
||||
if (headerInfo.appChannel) appIntelParts.push(`channel=${headerInfo.appChannel}`)
|
||||
|
||||
if (appIntelParts.length > 0) {
|
||||
updatedHeaders['req-app-intel'] = appIntelParts.join(';')
|
||||
}
|
||||
}
|
||||
|
||||
// 设置设备信息
|
||||
if (headerInfo.reqImei) {
|
||||
updatedHeaders['req-imei'] = headerInfo.reqImei
|
||||
}
|
||||
|
||||
// 设置系统来源
|
||||
if (headerInfo.sysOrigin) {
|
||||
const sysOriginParts = [`origin=${headerInfo.sysOrigin}`]
|
||||
if (headerInfo.sysOriginChild) {
|
||||
sysOriginParts.push(`child=${headerInfo.sysOriginChild}`)
|
||||
}
|
||||
updatedHeaders['req-sys-origin'] = sysOriginParts.join(';')
|
||||
}
|
||||
|
||||
updateCommonHeaders(updatedHeaders)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用HTTP请求函数
|
||||
* @param {string} url - 请求URL
|
||||
|
||||
@ -7,6 +7,16 @@
|
||||
<div class="content">
|
||||
<!-- 用户信息卡片 -->
|
||||
<div class="user-card">
|
||||
<!-- APP连接状态指示 -->
|
||||
<div v-if="isInApp() && !appConnected" class="app-status connecting">
|
||||
<div class="status-indicator"></div>
|
||||
<span>正在连接APP...</span>
|
||||
</div>
|
||||
<div v-else-if="isInApp() && appConnected" class="app-status connected">
|
||||
<div class="status-indicator"></div>
|
||||
<span>已连接</span>
|
||||
</div>
|
||||
|
||||
<div class="user-info">
|
||||
<div class="avatar">
|
||||
<span>{{ userInfo.name.charAt(0) }}</span>
|
||||
@ -14,6 +24,11 @@
|
||||
<div class="user-details">
|
||||
<h3>{{ userInfo.name }}</h3>
|
||||
<p>ID: {{ userInfo.id }}</p>
|
||||
<!-- 显示系统信息 -->
|
||||
<p v-if="headerInfo.sysOrigin" class="system-info">
|
||||
{{ headerInfo.sysOrigin }}
|
||||
<span v-if="headerInfo.appVersion"> v{{ headerInfo.appVersion }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<button class="notification-btn" @click="goToNotification">
|
||||
<span>✏️</span>
|
||||
@ -118,12 +133,15 @@ import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import MobileHeader from '../components/MobileHeader.vue'
|
||||
import { getBankBalance, getUserIdentity } from '../api/wallet.js'
|
||||
import { connectApplication, parseAccessOrigin, parseHeader, setHttpHeaders, isInApp } from '../utils/appBridge.js'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 当前身份
|
||||
const currentIdentity = ref('host')
|
||||
const loading = ref(false)
|
||||
const appConnected = ref(false)
|
||||
const headerInfo = ref({})
|
||||
|
||||
// 身份选项(保留显示用,但不再用于切换)
|
||||
const identities = [
|
||||
@ -132,6 +150,43 @@ const identities = [
|
||||
{ label: 'Coin Seller', value: 'coin-seller' }
|
||||
]
|
||||
|
||||
// 连接APP并获取认证信息
|
||||
const connectToApp = () => {
|
||||
if (!isInApp()) {
|
||||
console.warn('Not running in APP environment')
|
||||
// 非APP环境,直接执行后续逻辑
|
||||
appConnected.value = true
|
||||
checkUserIdentity()
|
||||
return
|
||||
}
|
||||
|
||||
connectApplication(access => {
|
||||
console.log('Received access from APP:', access)
|
||||
|
||||
const result = parseAccessOrigin(access)
|
||||
if (result.success) {
|
||||
// 解析头部信息
|
||||
headerInfo.value = parseHeader(result.data)
|
||||
|
||||
// 设置HTTP请求头
|
||||
setHttpHeaders(headerInfo.value)
|
||||
|
||||
// 标记连接成功
|
||||
appConnected.value = true
|
||||
|
||||
console.log('APP connection established:', headerInfo.value)
|
||||
|
||||
// 连接成功后获取用户身份和银行余额
|
||||
checkUserIdentity()
|
||||
fetchBankBalance()
|
||||
} else {
|
||||
console.error('Failed to parse access origin:', result.error)
|
||||
// 可以显示错误提示或跳转到错误页面
|
||||
router.push({ path: '/not_app', query: { message: result.error }})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取银行余额
|
||||
const fetchBankBalance = async () => {
|
||||
loading.value = true
|
||||
@ -232,10 +287,15 @@ const transfer = () => {
|
||||
router.push('/transfer')
|
||||
}
|
||||
|
||||
// 页面加载时获取银行余额和检查用户身份
|
||||
// 页面加载时先连接APP,然后获取数据
|
||||
onMounted(() => {
|
||||
fetchBankBalance()
|
||||
checkUserIdentity()
|
||||
// 先连接APP获取认证信息
|
||||
connectToApp()
|
||||
|
||||
// 如果不是APP环境,直接获取银行余额
|
||||
if (!isInApp()) {
|
||||
fetchBankBalance()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -261,6 +321,48 @@ onMounted(() => {
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* APP连接状态 */
|
||||
.app-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.app-status.connecting {
|
||||
background-color: #fef3c7;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.app-status.connected {
|
||||
background-color: #d1fae5;
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: currentColor;
|
||||
}
|
||||
|
||||
.app-status.connecting .status-indicator {
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -298,6 +400,12 @@ onMounted(() => {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.system-info {
|
||||
font-size: 12px !important;
|
||||
color: #8B5CF6 !important;
|
||||
margin-top: 2px !important;
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
@ -22,35 +22,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="team-info">
|
||||
<div class="team-id-section">
|
||||
<h3>Team ID:</h3>
|
||||
<div class="team-id-display">
|
||||
<span class="team-id">{{ teamId }}</span>
|
||||
<button class="copy-btn" @click="copyTeamId">
|
||||
📋
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="share-section">
|
||||
<h3>Share:</h3>
|
||||
<div class="share-buttons">
|
||||
<button class="share-btn" @click="shareToWeChat">
|
||||
<span class="share-icon">💬</span>
|
||||
WeChat
|
||||
</button>
|
||||
<button class="share-btn" @click="shareLink">
|
||||
<span class="share-icon">🔗</span>
|
||||
Link
|
||||
</button>
|
||||
<button class="share-btn" @click="shareQRCode">
|
||||
<span class="share-icon">📱</span>
|
||||
QR Code
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -88,7 +60,7 @@ const shareToWeChat = () => {
|
||||
// 分享链接
|
||||
const shareLink = () => {
|
||||
const shareText = `Join my team! Team ID: ${teamId.value}\nDownload Likei APP: https://likei.app`
|
||||
|
||||
|
||||
if (navigator.share) {
|
||||
navigator.share({
|
||||
title: 'Join my team on Likei',
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
<div class="user-info">
|
||||
<div class="user-avatar">
|
||||
<img v-if="message.avatar" :src="message.avatar" :alt="message.name" />
|
||||
<span v-else>{{ message.name.charAt(0) }}</span>
|
||||
<span v-else>{{ message.name }}</span>
|
||||
</div>
|
||||
<div class="user-details">
|
||||
<h4>{{ message.name }}</h4>
|
||||
@ -55,18 +55,6 @@ const messages = ref([])
|
||||
|
||||
// 模拟消息数据
|
||||
const mockMessages = [
|
||||
{
|
||||
id: '1234567890',
|
||||
name: 'User1',
|
||||
avatar: '',
|
||||
type: 'team_join_request'
|
||||
},
|
||||
{
|
||||
id: '1234567890',
|
||||
name: 'User1',
|
||||
avatar: '',
|
||||
type: 'team_join_request'
|
||||
},
|
||||
{
|
||||
id: '1234567890',
|
||||
name: 'User1',
|
||||
@ -80,8 +68,7 @@ const fetchMessages = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await getApplyRecord('1954059793963393025', 'WAIT') // 调用真实接口
|
||||
// 假设接口返回的数据结构为 { data: [...] } 或直接是数组
|
||||
messages.value = response.data || response // 根据实际返回结构调整
|
||||
loading.value = false
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
|
||||
99
src/views/NotAppView.vue
Normal file
99
src/views/NotAppView.vue
Normal file
@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="not-app-page">
|
||||
<div class="error-container">
|
||||
<div class="error-icon">⚠️</div>
|
||||
<h2>连接错误</h2>
|
||||
<p class="error-message">{{ errorMessage }}</p>
|
||||
<div class="error-details">
|
||||
<p>请确保您正在使用官方APP访问此页面</p>
|
||||
</div>
|
||||
<button class="retry-btn" @click="retry">重试</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const errorMessage = ref('未能连接到APP')
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query.message) {
|
||||
errorMessage.value = route.query.message
|
||||
}
|
||||
})
|
||||
|
||||
const retry = () => {
|
||||
router.go(-1) // 返回上一页
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.not-app-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f1f2f3;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.error-container {
|
||||
background-color: white;
|
||||
padding: 40px 20px;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.error-details {
|
||||
background-color: #f9fafb;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.error-details p {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
background-color: #8B5CF6;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.retry-btn:hover {
|
||||
background-color: #7C3AED;
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user