diff --git a/src/utils/appBridge.js b/src/utils/appBridge.js index 3f50d3a..25b1638 100644 --- a/src/utils/appBridge.js +++ b/src/utils/appBridge.js @@ -97,7 +97,7 @@ function handleAndroidPlatform(renderFun) { // 等待Flutter注入完成 const waitForFlutterInterface = () => { let attempts = 0 - const maxAttempts = 50 // 最多等待5秒 + const maxAttempts = 150 // 最多等待15秒 const checkInterface = () => { attempts++ @@ -118,19 +118,29 @@ function handleAndroidPlatform(renderFun) { errorLog('调用 getAccessOrigin 出错:', error) } } else if (attempts < maxAttempts) { + if ( + (attempts === 1 || attempts % 20 === 0) && + window.FlutterPageControl && + window.FlutterPageControl.postMessage + ) { + window.FlutterPageControl.postMessage('requestAccessOrigin') + } // 没找到,继续查询 setTimeout(checkInterface, 100) } else { // 查询次数用完 errorLog('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) + if (process.env.NODE_ENV === 'development') { + 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) + } else if (window.FlutterPageControl && window.FlutterPageControl.postMessage) { + window.FlutterPageControl.postMessage('requestAccessOrigin') + } } } diff --git a/src/utils/appConnector.js b/src/utils/appConnector.js index dce4f7f..d7b043e 100644 --- a/src/utils/appConnector.js +++ b/src/utils/appConnector.js @@ -15,6 +15,7 @@ import { getUserId, setUserInfo } from '@/utils/userStore.js' import { getUserIdentity, getMemberProfile } from '@/api/wallet.js' import { useRouter } from 'vue-router' const router = useRouter() +const APP_CONNECTION_TIMEOUT = 18000 /** * 默认的连接失败处理函数 @@ -108,7 +109,20 @@ export const connectToApp = async (onConnected, onFailed = defaultOnFailed) => { console.debug('📱 Establishing new APP connection...') const connectionResult = await new Promise((resolve, reject) => { + let connectionTimer = null + const clearConnectionTimer = () => { + if (connectionTimer) { + clearTimeout(connectionTimer) + connectionTimer = null + } + } + const connectionPromise = new Promise((connectResolve, connectReject) => { + connectionTimer = setTimeout(() => { + appConnectionManager.reset() + connectReject(new Error('APP connection timeout')) + }, APP_CONNECTION_TIMEOUT) + connectApplication(async (access) => { try { const result = parseAccessOrigin(access) @@ -142,7 +156,15 @@ export const connectToApp = async (onConnected, onFailed = defaultOnFailed) => { // 设置连接状态 appConnectionManager.setConnecting(connectionPromise) - connectionPromise.then(resolve).catch(reject) + connectionPromise + .then((value) => { + clearConnectionTimer() + resolve(value) + }) + .catch((error) => { + clearConnectionTimer() + reject(error) + }) }) console.debug('✅ Connection completed successfully') diff --git a/src/views/Activities/GulbenFestival/components/barrage.vue b/src/views/Activities/GulbenFestival/components/barrage.vue new file mode 100644 index 0000000..e69de29 diff --git a/src/views/Activities/GulbenFestival/index.vue b/src/views/Activities/GulbenFestival/index.vue index 9385a50..7c07ade 100644 --- a/src/views/Activities/GulbenFestival/index.vue +++ b/src/views/Activities/GulbenFestival/index.vue @@ -1009,10 +1009,12 @@ " > +
{ const isRtlLayout = computed(() => currentLangType.value === 'ar') -const imageUrl = (filename) => getPngUrl(pageConfig.assetPath, filename) +const imageUrl = (filename) => getWebpUrl(pageConfig.assetPath, filename) const webpUrl = (filename) => getWebpUrl(pageConfig.assetPath, filename) // 多语言图片:只在 page.config.js 声明已有阿语图时切换 `_ar`,没有就继续使用原图。 @@ -2563,17 +2565,38 @@ const changeCardDrawResultIndex = (step) => { cardDrawResultIndex.value = (cardDrawResultIndex.value + step + resultCount) % resultCount } +// 弹窗互斥:打开某个弹窗前先关闭其它弹窗,避免多个内容同时挂在同一遮罩层中。 +const closeOtherPopups = (activePopup = '') => { + if (activePopup !== 'rule') { + ruleShow.value = false + } + + if (activePopup !== 'history') { + historyShow.value = false + if (historyObserver) { + historyObserver.disconnect() + } + } + + if (activePopup !== 'result') { + cardDrawResultShow.value = false + cardDrawResultList.value = [] + cardDrawResultIndex.value = 0 + } +} + const showCardDrawAnimationThenResult = (animationUrl, actionMode = 'draw') => { if (cardDrawAnimationTimer) { window.clearTimeout(cardDrawAnimationTimer) } + closeOtherPopups() cardResultActionMode.value = actionMode cardDrawAnimationUrl.value = animationUrl cardDrawAnimationShow.value = Boolean(animationUrl) - cardDrawResultShow.value = false if (!animationUrl) { + closeOtherPopups('result') cardDrawResultShow.value = true return } @@ -2581,6 +2604,7 @@ const showCardDrawAnimationThenResult = (animationUrl, actionMode = 'draw') => { cardDrawAnimationTimer = window.setTimeout(() => { cardDrawAnimationShow.value = false cardDrawAnimationTimer = null + closeOtherPopups('result') cardDrawResultShow.value = true }, 1500) } @@ -3227,6 +3251,7 @@ const handleLotteryDrawRequest = async (drawCount = 1) => { const resultIndex = getLotteryResultIndex(drawResult) await startLotteryAnimation(resultIndex) + closeOtherPopups('result') cardResultActionMode.value = 'rewardDraw' cardDrawResultImageUrl.value = getLotteryRewardResultImageUrl(drawResult) cardDrawResultList.value = getLotteryRewardResultList(drawResult) @@ -3302,6 +3327,7 @@ const changeChildTab = (value) => { // 规则弹窗:参考 LuckyDollars Season5,点击规则入口打开,点击遮罩关闭。 const openRulePopup = () => { + closeOtherPopups('rule') ruleShow.value = true } @@ -3310,6 +3336,7 @@ const closeRulePopup = () => { } const openHistoryPopupRequest = async () => { + closeOtherPopups('history') historyShow.value = true historyPage.value = 1 historyHasMore.value = true diff --git a/src/views/Activities/GulbenFestival/page.config.js b/src/views/Activities/GulbenFestival/page.config.js index f64465c..08b42c5 100644 --- a/src/views/Activities/GulbenFestival/page.config.js +++ b/src/views/Activities/GulbenFestival/page.config.js @@ -2,7 +2,7 @@ export const pageConfig = { name: 'Gulben Festival', routePath: '/activities/gulben-festival', routeName: 'gulben-festival', - assetPath: 'Activities/GulbenFestival/', + assetPath: 'Activities/GulbenFestivalWebp/', activity: { activityId: '1001', diff --git a/src/views/Activities/GulbenFestival/页面创建信息.md b/src/views/Activities/GulbenFestival/页面创建信息.md index a50e9d3..815d601 100644 --- a/src/views/Activities/GulbenFestival/页面创建信息.md +++ b/src/views/Activities/GulbenFestival/页面创建信息.md @@ -18,8 +18,8 @@ ## 3. OSS 图片目录 -- OSS 相对路径:`Activities/GulbenFestival/` -- 本地开发目录:`public/oss/h5/Azizi/Activities/GulbenFestival/` +- OSS 相对路径:`Activities/GulbenFestivalWebp/` +- 本地开发目录:`public/oss/h5/Azizi/Activities/GulbenFestivalWebp/` - 是否需要 Codex 自动创建本地开发目录:是 - 图片是否已放入本地目录:是 @@ -976,7 +976,7 @@ - 补充 Personal Rank / Ranking 预览结构:活动礼物模块使用 `giftBg` 的 3 个预设空位直接展示 giftPhoto / giftCandy,不额外套参考页的 `gift` 单项背景;排行榜布局参考 LuckyDollars Season5,数据改用 `/activity/week-star/ranking` 和 `/activity/week-star/my-rank`;前三名使用 GulbenFestival 私有 `components/topUser.vue` + `top1/top2/top3`,布局为第二名/第一名/第三名左中右排列且第一名稍宽;贡献值参考 WeeklyStar 使用金币图标,前三名贡献值外观在私有组件内参考 GamesKing 的 `topUserDaily.vue` 圆角渐变底;排行榜排名和贡献值文字统一使用白色;第四名以后和我的排名使用 `RankingItem` 承载,名称下方新增 vip 等级图、userLevelBg、wealthLevelBg 等级行,vip 图片根据 `vipLevelName` 按 `VISCOUNT->vip1`、`EARL->vip2`、`MARQUIS->vip3`、`DUKEI->vip4`、`KING->vip5`、`EMPEROR->vip6` 映射展示,没有 `vipLevelName` 时不展示;userLevel 背景图根据 `charmLevel` 按 1-10/11-20/21-30/31-40/41-50 对应 `userLevel1Bg~userLevel5Bg`,wealthLevel 背景图根据 `wealthLevel` 按同样分段对应 `wealthLevel1Bg~wealthLevel5Bg`,内部居中展示对应等级文字,文字白色且 `font-size:1em`;切换到该标签时贴底展示我的排名,并通过底部 `rankingLoadmore` 上拉加载更多 - `Personal Rank / Reward` 和 `Personal Rank / Ranking` 的活动奖励、活动礼物都来自 `/activity/king-queen/effective?templateId=2057676469390872578`;页面内必须通过共享缓存方法读取,避免进入 Reward 和 Ranking 时重复请求同一个 effective 接口。 - 补充 Room Rank / Reward 和 Ranking:Reward 直接 100vw 展示 `ruleActivity`;Ranking 复用 Personal Rank 的排行榜展示和 weekly-star 上拉分页,但不展示活动礼物模块,头像统一 `border-radius:4px`,列表和我的排名只展示名称、ID、金币数量,不展示 `vip1`、`userLevel1Bg`、`wealthLevel1Bg` 三个等级图片 -- 创建本地 OSS 目录 `public/oss/h5/Azizi/Activities/GulbenFestival/` +- 创建本地 OSS 目录 `public/oss/h5/Azizi/Activities/GulbenFestivalWebp/` - 添加路由 `/activities/gulben-festival` - 添加安全白名单和活动页权限片段 @@ -998,7 +998,7 @@ 构建结果:`npm run build` 通过;保留项目既有的 Vite 运行时图片引用和动态导入提示。 -本地图片目录:`public/oss/h5/Azizi/Activities/GulbenFestival/` +本地图片目录:`public/oss/h5/Azizi/Activities/GulbenFestivalWebp/` 路由 path:`/activities/gulben-festival` @@ -1012,7 +1012,7 @@ ## 本次同步:图片扫描与预加载 -- 已重新扫描 `public/oss/h5/Azizi/Activities/GulbenFestival/` 下的图片,并同步 `src/views/Activities/GulbenFestival/page.config.js` 的 `preload`。 +- 已重新扫描 `public/oss/h5/Azizi/Activities/GulbenFestivalWebp/` 下的图片,并同步 `src/views/Activities/GulbenFestival/page.config.js` 的 `preload`。 - `blocking` 保持首屏关键图片:`bg`、`ruleBtBg`、`timeBg`、`tagActiveBg`、`tagBg`、`lotteryBg`、`collectGameBg`。 - `background` 已补齐当前目录中除首屏关键图外的全部 `.png` 图片:`barrageBg`、`cardBackBg`、`draw1Bt`、`draw5BtBg`、`GameDrawBtBg`、`Gameticket`、`gameTitleR`、`gameTitleSR`、`gameTitleSSR`、`giftBg`、`helpBt`、`leftBt`、`lotteryPrizeActiveBg`、`lotteryPrizeBg`、`moduleActiveBg`、`moduleBg`、`RankingItem`、`redeemR2001~redeemR2005`、`redeemSR2011~redeemSR2013`、`redeemSSR2021~redeemSSR2022`、`RewardTop1~RewardTop3`、`ruleActivity`、`ruleInfoBg`、`taskBtGo`、`taskBtReceive`、`taskBtReceived`、`taskItemBg`、`ticketHandleBtBg`、`ticketR`、`ticketSR`、`ticketSSR`、`top1~top3`、`vip1~vip6`、`userLevel1Bg~userLevel5Bg`、`wealthLevel1Bg~wealthLevel5Bg`。 - 已移除 `page.config.js` 预加载中不存在于本地目录的旧素材名:`redeemRGift`、`redeemRGoat`、`redeemRLamp`、`redeemRLight`、`redeemRMoon`、`redeemSRLamb`、`redeemSRScroll`、`redeemSRTurkey`、`redeemSSRGoat`、`redeemSSRLamb`。