@@ -2,6 +2,7 @@ import WxMap from 'common/js/map-wx-jssdk.js';
import Config from '@/common/js/config.js' ;
import util from '@/common/js/util.js' ;
import { classifyHttpError , httpErrorKey } from '@/common/js/http-error.js' ;
import { consumeNeedStartupAd , isColdLaunchPending } from '@/common/js/startup-ad.js' ;
let systemInfo = util . getDeviceInfo ( ) ;
@@ -297,25 +298,84 @@ export default {
data : { } ,
async : false
} ) ;
this . adv = res . value
// 弹框形式,首次弹出 1, 每次弹出 0
// 接口把启动广告配置放在 data 字段(少数环境也可能放在 value), 两者兼容,
// 避免取到 undefined 后模板 v-if 抛错导致 uni-popup 不渲染、真机广告弹不出来
let advData = ( res && ( res . data != null ? res . data : res . value ) ) || { } ;
this . adv = advData ;
// 后台配置: advshow=1 开启启动广告; advtype 决定弹出策略
// advtype == 1: 首次弹出 —— 仅第一次进入小程序弹出(持久化标记, 清缓存后可再弹)
// advtype == 0: 每次弹出 —— 每次"重新进入小程序"都弹出(不含 Tab 切换回首页)
if ( this . adv . advshow == 1 ) {
setTimeout ( ( ) => {
if ( res . value . advtype == 1 ) {
var popwindow _count = uni . getStorageSync ( this . id + this . name + '_popwindow_count' ) ;
if ( ( this . $refs . uniPopupWindow && popwindow _count == '' ) || (
this . $refs . uniPopupWindow && popwindow _count == 1 ) ) {
setTimeout ( async ( ) => {
// 隐私协议未同意时, 先等用户点击隐私协议(同意/拒绝)关闭弹窗,
// 否则广告弹窗会被隐私弹窗(z-index:5000)盖住, 真机上表现为"点了隐私协议后广告不弹"
await this . _waitPrivacyResolved ( ) ;
if ( ! this . $refs . uniPopupWindow ) return ;
this . $refs . uniPopupWindow . open ( ) ;
uni . setStorageSync ( this . id + this . name + '_popwindow_count' , 1 ) ;
// 先判定本次是否需要弹出, 避免无谓的图片预加载
let shouldOpen = false ;
let isFirstType = false ;
if ( this . adv . advtype == 1 ) {
// 首次弹出: 从未展示过才弹
var popwindow _count = uni . getStorageSync ( this . id + this . name + '_popwindow_count' ) ;
if ( popwindow _count == '' || popwindow _count == 0 ) {
shouldOpen = true ;
isFirstType = true ;
}
} else if ( res . value . advtype == 0 ) {
this . $refs . uniPopupWindow . open ( ) ;
uni . setStorageSync ( this . id + this . name + '_popwindow_count' , 0 ) ;
} else {
// 每次弹出: 冷启动首次 或 真正"重新进入小程序"(App 前后台切换)时弹出,
// 由 App.onShow 在重新进入时置位的信号驱动, 切 Tab 不会触发 App.onShow 故不弹
if ( consumeNeedStartupAd ( ) || isColdLaunchPending ( ) ) {
shouldOpen = true ;
}
}
if ( ! shouldOpen ) return ;
// 关键: 广告图片是网络图, 而 uni-popup 用 v-if 在 open() 时才渲染 slot,
// 图片要等弹窗打开后才开始请求/解码, 真机网络慢时就会"一截一截"逐步显示。
// 先把所有广告图片预加载进本地缓存, 全部就绪后再 open, 使弹窗一次性完整出现。
await this . _preloadAdvImages ( this . adv . list ) ;
if ( ! this . $refs . uniPopupWindow ) return ;
this . $refs . uniPopupWindow . open ( ) ;
if ( isFirstType ) {
uni . setStorageSync ( this . id + this . name + '_popwindow_count' , 1 ) ;
}
} , 500 ) ;
}
} ,
/**
* 等待隐私协议流程结束后再弹启动广告
* - 鸿蒙元服务: 隐私由系统托管, 应用层不弹自建弹窗, 直接放行
* - 已同意过(storage privacyAgreed): 无需等待, 直接放行
* - 未同意: 监听 PrivacyManager 广播的 privacy:agreed / privacy:disagreed,
* 用户点击隐私协议(同意/拒绝)关闭弹窗后放行
* - 兜底超时: 隐私协议因规则未弹等情况永不 emit 时也要弹广告, 避免卡死
* @param {number} [maxWait=15000]
* @returns {Promise<void>}
*/
_waitPrivacyResolved ( maxWait = 15000 ) {
// 鸿蒙元服务: 隐私协议由系统托管, 应用层不弹自建隐私弹窗, 无需等待
// #ifdef MP-HARMONY
return Promise . resolve ( ) ;
// #endif
// 已经同意过 → 隐私弹窗不会再出现, 直接放行
if ( uni . getStorageSync ( 'privacyAgreed' ) ) return Promise . resolve ( ) ;
return new Promise ( ( resolve ) => {
let done = false ;
const finish = ( ) => {
if ( done ) return ;
done = true ;
uni . $off ( 'privacy:agreed' , finish ) ;
uni . $off ( 'privacy:disagreed' , finish ) ;
clearTimeout ( timer ) ;
resolve ( ) ;
} ;
uni . $on ( 'privacy:agreed' , finish ) ;
uni . $on ( 'privacy:disagreed' , finish ) ;
// 兜底: 隐私协议因规则未弹 / 一直未处理时, 超时后也要弹广告
const timer = setTimeout ( finish , maxWait ) ;
} ) ;
} ,
// 无法从网络获取 diy 数据时:在页面内渲染错误横幅(各端可靠、一定可见)
// 同时通过 uni.$emit 通知 splash-state 关闭启动屏 (否则 strict 模式永远不关)
@@ -542,9 +602,46 @@ export default {
console . warn ( '[DIY][getDiyInfo] no value in res.data' , JSON . stringify ( diyDataValue ) . slice ( 0 , 200 ) ) ;
}
} ,
/**
* 预加载启动广告图片
* 广告弹窗由 uni-popup 在 open() 时才用 v-if 渲染内容, 图片是网络图,
* 若等到弹窗打开后才开始加载, 真机网络慢时会出现"一截一截"逐步显示。
* 这里提前把图片下载进本地缓存, 等全部加载完成(或超时)后再弹出,
* 使弹窗一次性完整呈现。
* @param {Array} list - adv.list, 元素形如 { imageUrl, link }
* @param {number} [timeout=4000] - 单张兜底超时, 避免个别图片卡住导致广告永不弹出
* @returns {Promise<void>}
*/
_preloadAdvImages ( list , timeout = 4000 ) {
if ( ! list || ! list . length ) return Promise . resolve ( ) ;
const tasks = list . map ( ( item ) => {
return new Promise ( ( resolve ) => {
const url = ( item && item . imageUrl ) ? this . $util . img ( item . imageUrl ) : '' ;
if ( ! url ) return resolve ( ) ;
let done = false ;
const finish = ( ) => {
if ( done ) return ;
done = true ;
clearTimeout ( timer ) ;
resolve ( ) ;
} ;
const timer = setTimeout ( finish , timeout ) ;
try {
// getImageInfo 会下载图片并写入本地缓存, 之后 image 组件可直接命中缓存
uni . getImageInfo ( {
src : url ,
success : finish ,
fail : finish
} ) ;
} catch ( e ) {
finish ( ) ;
}
} ) ;
} ) ;
return Promise . all ( tasks ) . then ( ( ) => { } ) ;
} ,
closePopupWindow ( ) {
this . $refs . uniPopupWindow . close ( ) ;
uni . setStorageSync ( this . id + this . name + '_popwindow_count' , - 1 ) ;
} ,
closeCollectPopupWindow ( ) {
this . $refs . collectPopupWindow . close ( ) ;