From 42cee4913ae42c3b1fbd743d67f0b20cd1f5ef43 Mon Sep 17 00:00:00 2001 From: tianfeng <769204422@qq.com> Date: Fri, 22 Aug 2025 14:41:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/appBridge.js | 24 +- src/utils/pageConfig.js | 82 ++++++ src/utils/permissionManager.js | 22 +- src/utils/usePageInitialization.js | 392 +++++++++++++++++++++++++++++ src/views/AgencyCenterView.vue | 148 ++--------- src/views/CoinSellerView.vue | 46 ++-- src/views/HostCenterView.vue | 258 +------------------ 7 files changed, 553 insertions(+), 419 deletions(-) create mode 100644 src/utils/pageConfig.js create mode 100644 src/utils/usePageInitialization.js diff --git a/src/utils/appBridge.js b/src/utils/appBridge.js index c1cf56f..3bc418b 100644 --- a/src/utils/appBridge.js +++ b/src/utils/appBridge.js @@ -94,15 +94,7 @@ export function connectApplication(renderFun) { window.getIosAccessOriginParam = renderFun try { - 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 if (android()) { + if (android()) { console.debug('Flutter Android WebView 检测') console.debug('window.app 对象:', window.app) @@ -177,7 +169,19 @@ export function connectApplication(renderFun) { } checkApp() } - } else { + } + + /*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') diff --git a/src/utils/pageConfig.js b/src/utils/pageConfig.js new file mode 100644 index 0000000..63c3bdb --- /dev/null +++ b/src/utils/pageConfig.js @@ -0,0 +1,82 @@ +// src/config/pageConfigs.js +/** + * 各页面的初始化配置 + * 统一管理不同页面对 usePageInitialization 的配置需求 + */ +export const PageConfig = { + HOST_CENTER: { + needsBankBalance: true, + needsWorkStatistics: true, + needsRouteGuard: true, + title: 'Host Center', + userTag: '👑 Host' + }, + + COIN_SELLER: { + needsBankBalance: true, + needsWorkStatistics: false, // Coin Seller 不需要工作统计 + needsRouteGuard: true, + title: 'Coin Seller', + userTag: '💰 Coin Seller' + }, + + AGENCY_CENTER: { + needsBankBalance: true, + needsWorkStatistics: true, + needsRouteGuard: true, + title: 'Agency Center', + userTag: '🏢 Agency' + }, + + BD_CENTER: { + needsBankBalance: true, + needsWorkStatistics: false, // BD 可能不需要日常工作统计 + needsRouteGuard: true, + title: 'BD Center', + userTag: '📈 BD' + }, + + APPLY: { + needsBankBalance: false, // 申请页面不需要余额 + needsWorkStatistics: false, // 申请页面不需要工作统计 + needsRouteGuard: false, // 申请页面不需要身份检查 + title: 'Apply', + userTag: null // 申请页面可能没有身份标签 + } +} + +/** + * 获取页面配置的工具函数 + * @param {string} pageType 页面类型 + * @returns {Object} 页面配置对象 + */ +export function getPageConfig(pageType) { + const config = PageConfig[pageType] + if (!config) { + console.warn(`未找到页面配置: ${pageType}`) + return PageConfig.HOST_CENTER // 默认使用 HOST_CENTER 配置 + } + return config +} + +// ========================================== +import { usePageInitialization } from './usePageInitialization.js' + +/** + * 使用预配置的页面初始化函数 + * @param {string} pageType 页面类型 (HOST_CENTER, COIN_SELLER, etc.) + * @param {Object} customOptions 自定义选项,会覆盖默认配置 + * @returns {Object} usePageInitialization 的返回值 + */ +export function usePageInitializationWithConfig(pageType, customOptions = {}) { + const pageConfig = getPageConfig(pageType) + + // 合并默认配置和自定义选项 + const options = { + ...pageConfig, + ...customOptions + } + + return usePageInitialization(options) +} + diff --git a/src/utils/permissionManager.js b/src/utils/permissionManager.js index 213be28..afc534d 100644 --- a/src/utils/permissionManager.js +++ b/src/utils/permissionManager.js @@ -6,7 +6,7 @@ // 用户身份类型 export const USER_ROLES = { FREIGHT_AGENT: 'freightAgent', - AGENT: 'agent', + AGENT: 'agent', BD: 'bd', ANCHOR: 'anchor', GUEST: 'guest' @@ -16,7 +16,7 @@ export const USER_ROLES = { export const PAGES = { COIN_SELLER: '/coin-seller', AGENCY_CENTER: '/agency-center', - BD_CENTER: '/bd-center', + BD_CENTER: '/bd-center', HOST_CENTER: '/host-center', APPLY: '/apply', MESSAGE: '/message', @@ -30,7 +30,7 @@ export const PAGE_PERMISSIONS = { [PAGES.COIN_SELLER]: [USER_ROLES.FREIGHT_AGENT], [PAGES.AGENCY_CENTER]: [USER_ROLES.AGENT], [PAGES.BD_CENTER]: [USER_ROLES.BD], - [PAGES.HOST_CENTER]: [USER_ROLES.AGENT, USER_ROLES.ANCHOR], + [PAGES.HOST_CENTER]: [USER_ROLES.ANCHOR], [PAGES.APPLY]: [USER_ROLES.GUEST], [PAGES.MESSAGE]: [USER_ROLES.AGENT], [PAGES.TRANSFER]: [USER_ROLES.AGENT, USER_ROLES.BD, USER_ROLES.ANCHOR], @@ -76,7 +76,7 @@ class PermissionManager { this.primaryRole = this.calculatePrimaryRole(identity) this.allowedPages = this.calculateAllowedPages(this.primaryRole) this.isIdentityLoaded = true - + console.debug('🔐 User identity updated:', { identity: this.userIdentity, primaryRole: this.primaryRole, @@ -97,7 +97,7 @@ class PermissionManager { if (identity.agent) return USER_ROLES.AGENT if (identity.bd) return USER_ROLES.BD if (identity.anchor) return USER_ROLES.ANCHOR - + return USER_ROLES.GUEST } @@ -108,13 +108,13 @@ class PermissionManager { */ calculateAllowedPages(primaryRole) { const allowedPages = [] - + Object.entries(PAGE_PERMISSIONS).forEach(([page, roles]) => { if (roles.length === 0 || roles.includes(primaryRole)) { allowedPages.push(page) } }) - + return allowedPages } @@ -131,13 +131,13 @@ class PermissionManager { // 检查是否在允许列表中 const hasPermission = this.allowedPages.includes(page) - + console.debug('🔍 Permission check:', { page, primaryRole: this.primaryRole, hasPermission }) - + return hasPermission } @@ -179,13 +179,13 @@ class PermissionManager { */ getAllRoles() { if (!this.userIdentity) return [USER_ROLES.GUEST] - + const roles = [] if (this.userIdentity.freightAgent) roles.push(USER_ROLES.FREIGHT_AGENT) if (this.userIdentity.agent) roles.push(USER_ROLES.AGENT) if (this.userIdentity.bd) roles.push(USER_ROLES.BD) if (this.userIdentity.anchor) roles.push(USER_ROLES.ANCHOR) - + return roles.length > 0 ? roles : [USER_ROLES.GUEST] } diff --git a/src/utils/usePageInitialization.js b/src/utils/usePageInitialization.js new file mode 100644 index 0000000..f905300 --- /dev/null +++ b/src/utils/usePageInitialization.js @@ -0,0 +1,392 @@ +// src/composables/usePageInitialization.js +import { ref, reactive, onMounted } from 'vue' +import { useRouter } from 'vue-router' +import { getBankBalance, getTeamEntry, getTeamMemberWorkStatistics } from '../api/wallet.js' +import { connectApplication, parseAccessOrigin, parseHeader, setHttpHeaders, isInApp } from '../utils/appBridge.js' +import { appConnectionManager, isAppConnected, getAppHeaderInfo } from '../utils/appConnectionManager.js' +import { checkAndRedirect } from '../utils/routeGuard.js' +import { getUserId, setUserInfo } from '../utils/userStore.js' +import { showError } from '../utils/toast.js' + +/** + * 页面初始化公共组合式函数 + * 适用于: HOST_CENTER, COIN_SELLER, AGENCY_CENTER, BD_CENTER, APPLY 等页面 + * + * @param {Object} options 配置选项 + * @param {boolean} options.needsBankBalance 是否需要获取银行余额 (默认: true) + * @param {boolean} options.needsWorkStatistics 是否需要获取工作统计 (默认: true) + * @param {boolean} options.needsRouteGuard 是否需要路由守卫检查 (默认: true) + * @param {Function} options.onDataLoaded 数据加载完成后的回调函数 + * @param {Function} options.onConnectionSuccess APP连接成功后的回调函数 + * @param {Function} options.onConnectionFailed APP连接失败后的回调函数 + */ +export function usePageInitialization(options = {}) { + const { + needsBankBalance = true, + needsWorkStatistics = true, + needsRouteGuard = true, + onDataLoaded, + onConnectionSuccess, + onConnectionFailed + } = options + + const router = useRouter() + + // 响应式状态 + const loading = ref(false) + const appConnected = ref(false) + const headerInfo = ref({}) + const userProfile = ref(null) + + // 用户信息 + const userInfo = reactive({ + name: 'user', + id: '1001', + userAvatar: '', + userNickname: 'user', + account: '1234567890' + }) + + // 收入信息 + const salaryInfo = reactive({ + hostSalary: 0.00, + currentSalary: 0.00 + }) + + // 任务信息 + const taskInfo = reactive({ + anchorType: 'Chat', + minutesProgress: 0, + minutesTotal: 120, + giftTask: 0 + }) + + /** + * 连接APP并获取认证信息(优化版 - 仅专注连接) + */ + const connectToApp = async () => { + console.group('🔗 APP Connection Process') + console.debug('🚀 Starting APP connection...') + + try { + // 检查是否在APP环境中 + if (!isInApp()) { + console.debug('🌐 Browser environment detected') + appConnected.value = true + console.groupEnd() + + // 触发连接成功回调 + if (onConnectionSuccess) { + onConnectionSuccess({ environment: 'browser' }) + } + + return { success: true, environment: 'browser' } + } + + // 检查是否已经连接且未过期 + if (isAppConnected()) { + console.debug('✅ APP already connected and valid') + appConnected.value = true + + // 使用缓存的头部信息 + const cachedHeaderInfo = getAppHeaderInfo() + if (cachedHeaderInfo) { + headerInfo.value = cachedHeaderInfo + console.debug('🔧 Using cached HTTP headers') + } + + console.groupEnd() + + // 触发连接成功回调 + if (onConnectionSuccess) { + onConnectionSuccess({ environment: 'app', fromCache: true }) + } + + return { success: true, environment: 'app', fromCache: true } + } + + // 检查是否有正在进行的连接 + if (appConnectionManager.isConnecting()) { + console.debug('⏳ Connection already in progress, waiting...') + await appConnectionManager.getConnectionPromise() + appConnected.value = true + console.debug('✅ Connected via existing process') + console.groupEnd() + + // 触发连接成功回调 + if (onConnectionSuccess) { + onConnectionSuccess({ environment: 'app', fromCache: true }) + } + + return { success: true, environment: 'app', fromCache: true } + } + + // 建立新的APP连接 + console.debug('📱 Establishing new APP connection...') + + const connectionResult = await new Promise((resolve, reject) => { + const connectionPromise = new Promise((connectResolve, connectReject) => { + connectApplication(async (access) => { + try { + const result = parseAccessOrigin(access) + + if (result.success) { + // 解析头部信息 + headerInfo.value = parseHeader(result.data) + + // 设置HTTP请求头 + console.debug('🔧 Setting HTTP headers...') + await setHttpHeaders(headerInfo.value) + + // 标记连接成功 + appConnected.value = true + appConnectionManager.setConnected(headerInfo.value) + + console.debug('🎉 APP connection established successfully') + connectResolve({ success: true, environment: 'app', fromCache: false }) + } else { + console.error('❌ Failed to parse access origin:', result.error) + appConnectionManager.reset() + connectReject(new Error(result.error)) + } + } catch (error) { + console.error('❌ Connection process failed:', error) + appConnectionManager.reset() + connectReject(error) + } + }) + }) + + // 设置连接状态 + appConnectionManager.setConnecting(connectionPromise) + + connectionPromise + .then(resolve) + .catch(reject) + }) + + console.debug('✅ Connection completed successfully') + console.groupEnd() + + // 触发连接成功回调 + if (onConnectionSuccess) { + onConnectionSuccess(connectionResult) + } + + return connectionResult + + } catch (error) { + console.error('❌ Connection failed:', error) + appConnected.value = false + console.groupEnd() + + // 触发连接失败回调 + if (onConnectionFailed) { + onConnectionFailed(error) + } + + // 连接失败的特殊处理 + if (error.message && error.message.includes('parse access')) { + router.push({ path: '/not_app', query: { message: error.message }}) + } + + return { success: false, error: error.message } + } + } + + /** + * 获取银行余额 + */ + const fetchBankBalance = async () => { + if (!needsBankBalance) return + + loading.value = true + + try { + const response = await getBankBalance() + + if (response.status && typeof response.body === 'number') { + // 格式化余额显示,保留2位小数 + salaryInfo.currentSalary = response.body.toFixed(2) + } else { + salaryInfo.currentSalary = 0.00 + } + } catch (error) { + console.error('Failed to fetch bank balance:', error) + salaryInfo.currentSalary = 0.00 + // 可以显示错误提示 + showError('Failed to load balance. Please try again.') + } finally { + loading.value = false + } + } + + /** + * 获取工作统计数据 + */ + const fetchWorkStatistics = async () => { + if (!needsWorkStatistics) return + + try { + const response = await getTeamMemberWorkStatistics('DAILY', getUserId()) + if (response.status && response.body) { + // 更新任务信息 + taskInfo.anchorType = 'Chat' // 固定值 + taskInfo.minutesProgress = response.body.ownTime + taskInfo.giftTask = response.body.acceptGiftValue || 0 // acceptGiftValue + } + + } catch (error) { + console.error('获取工作统计失败:', error) + } + } + + /** + * 获取团队入口信息 + */ + const fetchTeamEntry = async () => { + try { + const response = await getTeamEntry() + + if (response && response.status && response.body) { + userProfile.value = response.body + + // 缓存用户信息 + if (response.body.memberProfile) { + setUserInfo(response.body.memberProfile, response.body.teamProfile) + + // 更新用户信息显示 + userInfo.userNickname = response.body.memberProfile.userNickname + userInfo.id = response.body.memberProfile.account + userInfo.userAvatar = response.body.memberProfile.userAvatar + } + + return response.body.memberProfile?.id + } + } catch (error) { + console.error('获取团队信息失败:', error) + if (error.message && error.message.includes('401')) { + await router.push({ + path: '/not_app', + query: { message: error.message } + }) + return + } + } + return null + } + + /** + * 数据初始化函数 + */ + const initializePageData = async () => { + console.debug('📊 Starting data initialization...') + + try { + // 获取团队信息 + const teamEntryResult = await fetchTeamEntry() + if (teamEntryResult != null) { + // 并行获取银行余额和工作统计(提高性能) + const dataPromises = [] + + if (needsBankBalance) { + dataPromises.push(fetchBankBalance()) + } + + if (needsWorkStatistics) { + dataPromises.push(fetchWorkStatistics()) + } + + // 等待所有数据加载完成 + await Promise.all(dataPromises) + + // 使用路由守卫进行身份检查和重定向 + if (needsRouteGuard) { + await checkAndRedirect(router, teamEntryResult) + } + + // 触发数据加载完成回调 + if (onDataLoaded) { + onDataLoaded({ + userProfile: userProfile.value, + userInfo: userInfo, + salaryInfo: salaryInfo, + taskInfo: taskInfo + }) + } + + console.debug('✅ Data initialization completed') + } + } catch (error) { + console.error('❌ Data initialization failed:', error) + } + } + + /** + * 处理头像图片加载失败 + */ + const handleImageError = () => { + // 图片加载失败时,清空userAvatar,显示占位符 + userInfo.userAvatar = '' + } + + /** + * 获取头像占位符(用户名首字母) + */ + const getAvatarPlaceholder = () => { + if (userInfo.name && userInfo.name.length > 0) { + return userInfo.name.charAt(0).toUpperCase() + } + return 'U' // 默认显示 'U' + } + + /** + * 页面挂载时的初始化逻辑 + */ + const initializePage = async () => { + console.debug('📄 Page initialization started') + + try { + // 第一步:仅进行APP连接 + const connectionResult = await connectToApp() + + // 第二步:根据连接结果决定是否初始化数据 + if (connectionResult.success) { + console.debug('🔗 Connection successful, initializing data...') + await initializePageData() + } else { + console.warn('⚠️ Connection failed, skipping data initialization') + } + } catch (error) { + console.error('❌ Page initialization failed:', error) + } + } + + // 自动在 onMounted 时初始化 + onMounted(initializePage) + + // 返回所有需要的状态和方法 + return { + // 状态 + loading, + appConnected, + headerInfo, + userProfile, + userInfo, + salaryInfo, + taskInfo, + + // 方法 + connectToApp, + initializePageData, + fetchBankBalance, + fetchWorkStatistics, + fetchTeamEntry, + handleImageError, + getAvatarPlaceholder, + initializePage, + + // 手动初始化方法(如果需要重新初始化) + reinitialize: initializePage + } +} diff --git a/src/views/AgencyCenterView.vue b/src/views/AgencyCenterView.vue index 889c6f0..01795e4 100644 --- a/src/views/AgencyCenterView.vue +++ b/src/views/AgencyCenterView.vue @@ -10,13 +10,13 @@ {{ getAvatarPlaceholder() }}
-

{{ userInfo.name }}

+

{{ userInfo.userNickname }}

ID: {{ userInfo.id }}

@@ -78,7 +78,6 @@ placeholder="Please Enter The Amount Of Gold Coins To Transfer" class="amount-input" /> - 0/50