H5-contact/node_modules/@dcloudio/uni-components/lib-x-vapor/ad/ad.mixin.mp-weixin.js

731 lines
19 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const adPlugin = requirePlugin("uni-ad");
import {
canShowHalfScreenModal,
isHalfScreenModalShowing,
markHalfScreenModalConfirmed,
markHalfScreenModalDismissed,
markHalfScreenModalShowing,
queueHalfScreenModalOpenOptions,
resetHalfScreenModalSession,
resolveHalfScreenModalContent,
takePendingHalfScreenModalOpenOptions,
unlockHalfScreenModalShowing
} from '../../lib-x/ad/half-screen-modal.js'
const EventType = {
Load: 'load',
Close: 'close',
Error: 'error'
}
const AdType = {
Banner: "banner",
RewardedVideo: "rewardedVideo",
Interstitial: "interstitial"
}
const ProviderType = {
WeChat: 10018,
UserWeChat: 10017,
ShanHu: 10020,
HalfScreen: 10032
}
/** 微信广告无填充错误码 */
const WxAdErrorCode = {
NoFill: 1004
}
/** 供 uniad 壳组件显式声明,消除 Vue3 编译期 mixin 不可见告警 */
export const adComponentProps = {
options: {
type: [Object, Array],
default () {
return {}
}
},
adpid: {
type: [Number, String],
default: ''
},
unitId: {
type: [Number, String],
default: ''
},
preload: {
type: [Boolean, String],
default: true
},
loadnext: {
type: [Boolean, String],
default: false
},
urlCallback: {
type: Object,
default () {
return {}
}
}
}
/** 供 uniad 壳组件显式声明的 data */
export function adComponentData () {
return {
loading: false,
userwx: false,
userUnitId: '',
wxchannel: false,
errorMessage: null,
isHalfScreen: false
}
}
/** 供 uniad 壳组件显式声明的 emits */
export const adComponentEmits = ['load', 'close', 'error', 'adcreated']
export default {
emits: adComponentEmits,
props: adComponentProps,
data: adComponentData,
created () {
resetHalfScreenModalSession()
this._ad = null
this._loading = false
this._wxRewardedAd = null
this._wxInterstitialAd = null
this._providerType = ProviderType.ShanHu
this._halfScreenOpening = false
this._halfScreenOpeningTimer = null
if (this.preload && this._canCreateAd()) {
this.load()
}
},
methods: {
load () {
if (this.loading) {
return
}
this._startLoading()
if (this._providerType == ProviderType.ShanHu) {
} else if (this._providerType == ProviderType.WeChat) {
this.selectComponent('.uniad-plugin-wx').load()
} else if (this._providerType == ProviderType.UserWeChat) {
this._loadWxAd()
}
},
show (e) {
this.errorMessage = null
const plugin = this.selectComponent('.uniad-plugin')
if (this._isHalfScreenAd(plugin)) {
return
}
if (this._providerType == ProviderType.ShanHu) {
this._showAdInPlugin(this.selectComponent('.uniad-plugin'))
} else if (this._providerType == ProviderType.WeChat) {
this._showAdInPlugin(this.selectComponent('.uniad-plugin-wx'))
} else if (this._providerType == ProviderType.UserWeChat) {
this._showWxAd(e)
}
},
_onclick () {
if (this.isHalfScreen) {
return
}
this.show()
},
_startLoading () {
this.loading = true
this.errorMessage = null
},
_canCreateAd () {
let result = false
if (typeof this.adpid === 'string' && this.adpid.length > 0) {
result = true
} else if (typeof this.adpid === 'number') {
result = true
}
return result
},
_hasCallback () {
return (typeof this.urlCallback === 'object' && Object.keys(this.urlCallback).length > 0)
},
/**
* 按 provider 从 acsv4 配置列表查找广告项
* @param {Array} list 配置数组
* @param {number} provider 渠道 provider
*/
_findAdItemByProvider (list, provider) {
if (!Array.isArray(list)) {
return null
}
return list.find(item => Number(item.provider) === Number(provider)) || null
},
/**
* 是否为微信广告无填充 errCode 1004
* @param {Object} err 错误对象
*/
_isWxAdNoFillError (err) {
if (!err) {
return false
}
const detail = err.detail || err
return Number(detail.errCode) === WxAdErrorCode.NoFill
},
/**
* 代运营无填充时尝试半屏兜底
* @param {Object} plugin uniad-plugin 实例
* @param {Object} err 错误对象
*/
_tryHalfScreenFallback (plugin, err) {
if (!plugin || !plugin.tryHalfScreenFallback) {
return false
}
const detail = err && (err.detail || err)
if (!this._isWxAdNoFillError(detail)) {
return false
}
return plugin.tryHalfScreenFallback(detail)
},
/**
* 处理代运营微信广告错误,无填充时走半屏兜底
* @param {Object} err 错误对象
*/
_handleUserWxAdError (err) {
this.loading = false
const plugin = this.selectComponent('.uniad-plugin')
if (this._tryHalfScreenFallback(plugin, err)) {
return true
}
this.errorMessage = JSON.stringify(err)
this._dispatchEvent(EventType.Error, err)
return false
},
/**
* 切换为半屏广告模式
*/
_setHalfScreenMode () {
this.isHalfScreen = true
this._providerType = ProviderType.HalfScreen
this.userwx = false
this.wxchannel = false
this.loading = false
},
/**
* 是否半屏广告
* @param {Object} plugin uniad-plugin 实例
*/
_isHalfScreenAd (plugin) {
return this._providerType === ProviderType.HalfScreen || !!(
plugin &&
plugin.getHalfScreenConfig &&
plugin.getHalfScreenConfig()
)
},
/**
* 用户点击半屏占位(插件 halfScreenTap 事件)
* @param {Object} e 事件对象
*/
_onHalfScreenTap (e) {
const openOptions = e.detail && e.detail.openOptions
if (!openOptions) {
return
}
this._openTargetMiniProgramInHost(openOptions)
},
/**
* 开始打开小程序防抖锁
*/
_beginMiniProgramOpening () {
this._halfScreenOpening = true
this._halfScreenOpeningTimer = setTimeout(() => {
this._resetHalfScreenOpening()
}, 3000)
},
/**
* 用户手动触发:优先半屏打开,失败则 navigateToMiniProgram 兜底
* @param {Object} openOptions 打开参数
*/
_openTargetMiniProgramInHost (openOptions) {
if (this._halfScreenOpening) {
return
}
if (!openOptions || !openOptions.appId) {
this._dispatchEvent(EventType.Error, {
errMsg: 'mini program openOptions unavailable'
})
return
}
const plugin = this.selectComponent('.uniad-plugin')
this._openHalfScreenEmbeddedInHost(openOptions, plugin)
},
/**
* 半屏嵌入打开 wx.openEmbeddedMiniProgram
* @param {Object} openOptions 打开参数
* @param {Object} plugin uniad-plugin 实例
*/
_openHalfScreenEmbeddedInHost (openOptions, plugin) {
if (typeof wx.openEmbeddedMiniProgram !== 'function') {
this._openMiniProgramDirectInHost(openOptions, plugin)
return
}
this._beginMiniProgramOpening()
wx.openEmbeddedMiniProgram({
appId: openOptions.appId,
path: openOptions.path,
extraData: openOptions.extraData || {},
success: (res) => {
this._resetHalfScreenOpening()
if (plugin && plugin.notifyHalfScreenOpenSuccess) {
plugin.notifyHalfScreenOpenSuccess(res)
}
},
fail: (err) => {
if (this._isMiniProgramOpenCancel(err)) {
this._resetHalfScreenOpening()
this._notifyHalfScreenOpenResult(plugin, err)
return
}
this._openMiniProgramDirectInHost(openOptions, plugin, err)
}
})
},
/**
* 直接打开小程序 wx.navigateToMiniProgram半屏未过审等场景兜底
* @param {Object} openOptions 打开参数
* @param {Object} plugin uniad-plugin 实例
* @param {Object} fromErr 半屏打开失败时的错误
*/
_openMiniProgramDirectInHost (openOptions, plugin, fromErr) {
if (typeof wx.navigateToMiniProgram !== 'function') {
this._resetHalfScreenOpening()
this._notifyHalfScreenOpenResult(plugin, fromErr || {
errMsg: 'navigateToMiniProgram not supported'
})
return
}
if (!this._halfScreenOpening) {
this._beginMiniProgramOpening()
}
wx.navigateToMiniProgram({
appId: openOptions.appId,
path: openOptions.path,
extraData: openOptions.extraData || {},
success: (res) => {
this._resetHalfScreenOpening()
if (plugin && plugin.notifyHalfScreenOpenSuccess) {
plugin.notifyHalfScreenOpenSuccess(res, { isDirect: true })
}
},
fail: (err) => {
this._resetHalfScreenOpening()
this._notifyHalfScreenOpenResult(plugin, err)
}
})
},
/**
* 清除半屏打开锁定时器
*/
_clearHalfScreenOpeningTimer () {
if (this._halfScreenOpeningTimer) {
clearTimeout(this._halfScreenOpeningTimer)
this._halfScreenOpeningTimer = null
}
},
/**
* 重置半屏打开锁
*/
_resetHalfScreenOpening () {
this._halfScreenOpening = false
this._clearHalfScreenOpeningTimer()
},
/**
* 是否为打开/跳转小程序时用户点击取消fail cancel
* @param {Object} err 失败回调对象
*/
_isMiniProgramOpenCancel (err) {
const msg = (err && err.errMsg) || ''
return typeof msg === 'string' && msg.indexOf('fail cancel') !== -1
},
/**
* 通知插件半屏打开失败或用户取消
* @param {Object} plugin uniad-plugin 实例
* @param {Object} err 失败回调对象
*/
_notifyHalfScreenOpenResult (plugin, err) {
if (this._isMiniProgramOpenCancel(err)) {
if (plugin && plugin.notifyHalfScreenOpenCancel) {
plugin.notifyHalfScreenOpenCancel(err)
} else if (plugin && plugin.notifyHalfScreenOpenFail) {
plugin.notifyHalfScreenOpenFail(err)
} else {
this._dispatchEvent(EventType.Close, { type: 'userCancel', detail: err })
}
return
}
if (plugin && plugin.notifyHalfScreenOpenFail) {
plugin.notifyHalfScreenOpenFail(err)
} else {
this._dispatchEvent(EventType.Error, err)
}
},
_onHalfScreenReady () {
this._setHalfScreenMode()
},
/**
* 插件 all_show 双广告ad 配置就绪后并行弹窗
* @param {Object} e 事件对象
*/
_onHalfScreenModal (e) {
const detail = e.detail || e
const openOptions = detail.openOptions
if (openOptions) {
this._tryShowHalfScreenModal(openOptions)
}
},
/**
* 尝试展示排队中的半屏弹窗
*/
_processPendingHalfScreenModal () {
const pending = takePendingHalfScreenModalOpenOptions()
if (pending) {
setTimeout(() => {
this._tryShowHalfScreenModal(pending)
}, 50)
}
},
/**
* 宿主层统一调用 uni.showModal 引导打开半屏
* @param {Object} openOptions 打开半屏/直达小程序参数
* @param {number} retryCount showModal 失败重试次数
*/
_tryShowHalfScreenModal (openOptions, retryCount = 0) {
if (!openOptions || !openOptions.appId) {
return
}
const appId = openOptions.appId
if (!canShowHalfScreenModal(appId)) {
return
}
if (isHalfScreenModalShowing()) {
queueHalfScreenModalOpenOptions(openOptions)
return
}
markHalfScreenModalShowing(appId)
/* eslint-disable no-undef */
uni.showModal({
title: '提示',
content: resolveHalfScreenModalContent(openOptions.msg),
confirmText: '继续',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
markHalfScreenModalConfirmed()
this._openTargetMiniProgramInHost(openOptions)
} else {
markHalfScreenModalDismissed(appId)
}
},
fail: () => {
unlockHalfScreenModalShowing(appId)
if (retryCount < 1) {
setTimeout(() => {
this._tryShowHalfScreenModal(openOptions, retryCount + 1)
}, 300)
} else {
this._processPendingHalfScreenModal()
}
}
})
},
_onmpload (e) {
this.loading = false
const plugin = this.selectComponent('.uniad-plugin')
if (plugin && plugin.getHalfScreenConfig && plugin.getHalfScreenConfig()) {
this._setHalfScreenMode()
}
this._dispatchEvent(EventType.Load, {})
},
_onmpclose (e) {
this._dispatchEvent(EventType.Close, e.detail)
if (e.detail.adsdata) {
const adv = e.detail.adv
const adsdata = e.detail.adsdata
const version = e.detail.version
/* eslint-disable no-undef */
uniCloud.callFunction({
name: 'uniAdCallback',
data: {
adv: adv,
adsdata: adsdata,
version: version
},
secretType: 'both',
success: (res) => {
},
fail: (err) => {
this._dispatchEvent(EventType.Error, err)
}
})
delete e.detail.adv
delete e.detail.adsdata
delete e.detail.version
}
},
_onmperror (e) {
const detail = e.detail || e
if (this._handleUserWxAdError(detail)) {
return
}
if (this._isMiniProgramOpenCancel(detail)) {
return
}
this._dispatchEvent(EventType.Error, detail)
},
_onnextchannel (e) {
const adData = this._findAdItemByProvider(e.detail, ProviderType.UserWeChat)
if (!adData) {
return
}
this.wxchannel = true
this.$nextTick(() => {
if (adData.provider == 10017) {
this._providerType = ProviderType.UserWeChat
switch(adData._nt_) {
case 4:
this.wxAdType = AdType.Banner
this.userwx = true
this.userUnitId = adData.posid
break;
case 9:
this.wxAdType = AdType.RewardedVideo
this._createRewardedAd(adData.posid)
break;
case 15:
this.wxAdType = AdType.Interstitial
this._createInterstitialAd(adData.posid)
break;
}
} else if (adData.provider == 10018) {
this._providerType = ProviderType.WeChat
this.selectComponent('.uniad-plugin-wx').setConfig(adData)
}
})
},
_onwxchannelerror(e) {
this.wxchannel = false
this.$nextTick(() => {
this._providerType = ProviderType.ShanHu
this.selectComponent('.uniad-plugin').shanhuChannel()
})
},
_dispatchEvent (type, data) {
this.$emit(type, {
detail: data
})
},
_showAdInPlugin(adComponent) {
if (this._hasCallback()) {
const userCryptoManager = wx.getUserCryptoManager()
userCryptoManager.getLatestUserKey({
success: ({
encryptKey,
iv,
version,
expireTime
}) => {
adComponent.show({
userId: this.urlCallback.userId || '',
extra: this.urlCallback.extra || '',
encryptKey,
iv,
version,
expireTime
})
},
fail: (err) => {
this._dispatchEvent(EventType.Error, err)
}
})
} else {
adComponent.show({
userId: this.urlCallback.userId || '',
extra: this.urlCallback.extra || ''
})
}
},
_loadWxAd () {
switch (this.wxAdType) {
case AdType.RewardedVideo:
if (this._wxRewardedAd) {
this._wxRewardedAd.load();
}
break;
case AdType.Interstitial:
if (this._wxInterstitialAd) {
this._wxInterstitialAd.load();
}
break;
}
},
// 加载/显示广告
_showWxAd (options) {
this._urlCallback = options || this.urlCallback;
if (this.loading == true) {
this._userInvokeShowFlag = true
return
}
switch (this.wxAdType) {
case AdType.RewardedVideo:
if (!this._wxRewardedAd) {
return;
}
this._wxRewardedAd.show().catch((err) => {
if (this._handleUserWxAdError(err)) {
return
}
this._wxRewardedAd.load().then(() => {
this._wxRewardedAd.show();
}).catch((retryErr) => {
this._handleUserWxAdError(retryErr)
});
});
break;
case AdType.Interstitial:
if (!this._wxInterstitialAd) {
return;
}
this._wxInterstitialAd.show().catch((err) => {
if (this._handleUserWxAdError(err)) {
return
}
this._wxInterstitialAd.load().then(() => {
this._wxInterstitialAd.show();
}).catch((retryErr) => {
this._handleUserWxAdError(retryErr)
});
});
break;
}
},
// 微信激励视频
_createRewardedAd(adUnitId) {
if (this._wxRewardedAd) {
return;
}
this._wxRewardedAd = wx.createRewardedVideoAd({ adUnitId: adUnitId, multiton: true });
this._wxRewardedAd.onLoad(() => {
this.loading = false
this._dispatchEvent(EventType.Load, {})
if (this._userInvokeShowFlag) {
this._userInvokeShowFlag = false;
this._wxRewardedAd.show();
}
});
this._wxRewardedAd.onError(err => {
this._handleUserWxAdError(err)
});
this._wxRewardedAd.onClose(res => {
if (res.isEnded) {
this._callServer()
} else {
this._dispatchEvent(EventType.Close, res);
}
});
this.loading = true
},
// 微信插屏
_createInterstitialAd(adUnitId) {
if (this._wxInterstitialAd) {
return;
}
this._wxInterstitialAd = wx.createInterstitialAd({ adUnitId: adUnitId });
this._wxInterstitialAd.onLoad(() => {
this.loading = false
this._dispatchEvent(EventType.Load, {})
if (this._userInvokeShowFlag) {
this._userInvokeShowFlag = false;
this._wxInterstitialAd.show();
}
});
this._wxInterstitialAd.onError(err => {
this._handleUserWxAdError(err)
});
this._wxInterstitialAd.onClose(res => {
this._dispatchEvent(EventType.Close, res);
});
this.loading = true
},
_callServer() {
const userCryptoManager = wx.getUserCryptoManager()
userCryptoManager.getLatestUserKey({
success: (encryptConfig) => {
const callbackData = adPlugin.buildCallbackData(this.adpid, this.urlCallback, {}, encryptConfig)
uniCloud.callFunction({
name: 'uniAdCallback',
data: callbackData,
secretType: 'both',
success: (res) => {
this._dispatchEvent(EventType.Close, res);
},
fail: (err) => {
this._dispatchEvent(EventType.Error, err);
}
})
},
fail: (err) => {
this._dispatchEvent(EventType.Error, err);
}
})
}
}
}