Compare commits

..

5 Commits

Author SHA1 Message Date
b49fa378e7 Merge branch 'temp221' into dev/1.0 2026-10-10 17:34:41 +08:00
1c0444b1ec chore:微信小程序真机广告 正常 2026-10-10 17:31:02 +08:00
09909f0dcf chore:真机上显示广告正常 2026-10-10 16:02:20 +08:00
b14032149e chore:启动广告,后台开启首次弹出,第一次进入小程序后会弹出广告,
后台开启每次弹出,每次重新进入小程序都会弹出广告
2026-10-10 11:35:55 +08:00
447f7cd7b5 chore:启动广告,后台开启首次弹出,每一次重新进入小程序后都会弹出广告 2026-10-10 11:20:36 +08:00
4 changed files with 116 additions and 20 deletions

View File

@@ -257,11 +257,7 @@ const localDevConfig = ({
// })['2811']; // 本地
// })['5g-317']; // 电磁兼容
// })['5g-351']; //
})['2881']; // 本地
})['2843']; // 本地
// })['local-2']; // 本地
// })['local-11']; // 本地
// })['1-test']; // test

View File

@@ -8,6 +8,7 @@
// PR-MULTI-TURN-KEY-BINDING (2026-09-18): AI 客服会话 key 切换
// 登录态恢复后通知 aiService 清旧 user key,避免串会话
import aiService from 'common/js/ai-service.js';
import { markNeedStartupAd } from 'common/js/startup-ad.js';
// 记录 App 是否已至少展示过一次: 首次 onShow = 启动展示(由 onLaunch 完成初始化),
// 之后每一次 onShow(含微信 ... 菜单"重新进入小程序"、从后台切回)都视为"重新进入"。
@@ -512,6 +513,8 @@
// 之后每次 onShow(含 ... 重新进入、从后台切回前台)都视为重新进入,主动失效缓存并重新对齐。
if (_appHasShown) {
try { Lang.onAppReentry(); } catch (e) {}
// 重新进入小程序(从后台切回 / "重新进入小程序"菜单): 标记需要弹出启动广告
try { markNeedStartupAd(); } catch (e) {}
}
_appHasShown = true;

View File

@@ -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();

View File

@@ -53,7 +53,7 @@
<ns-copyright v-show="isShowCopyRight" />
</view>
<template v-if="adv.advshow != -1">
<template v-if="adv && adv.advshow != -1">
<view @touchmove.prevent.stop>
<uni-popup ref="uniPopupWindow" type="center" class="wap-floating" :maskClick="false">
<view class="small-bot">