@hlw-uni/mp-vue 2.0.4 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/composables/ad/index.d.ts +8 -26
- package/dist/composables/index.d.ts +1 -1
- package/dist/composables/msg/index.d.ts +2 -0
- package/dist/index.js +20 -41
- package/dist/index.mjs +20 -41
- package/package.json +1 -1
- package/src/components/hlw-notice-popup/index.vue +238 -0
- package/src/composables/ad/index.ts +27 -70
- package/src/composables/index.ts +0 -3
- package/src/composables/msg/index.ts +4 -0
|
@@ -37,24 +37,6 @@ export interface AdCloseResult {
|
|
|
37
37
|
/** 用户是否完整观看 */
|
|
38
38
|
isEnded: boolean;
|
|
39
39
|
}
|
|
40
|
-
/** showReward 的 claim 回调返回契约 */
|
|
41
|
-
export interface AdClaimResult {
|
|
42
|
-
/** 本次结果:true=成功 / false=失败 */
|
|
43
|
-
ok: boolean;
|
|
44
|
-
/** 奖励数(用于 toast 显示 +N 积分;无则不 toast) */
|
|
45
|
-
reward?: number;
|
|
46
|
-
/** 失败提示语;不传不弹 toast */
|
|
47
|
-
msg?: string;
|
|
48
|
-
/** 仅 isEnded=false 时常用:true 让 mp-core 重新 show 一次(业务方挽留确认后用) */
|
|
49
|
-
retry?: boolean;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* showReward 的 claim 回调签名 —— 业务方按场景实现(领积分、解锁、抽奖等)。
|
|
53
|
-
* 每次广告关闭后被调用一次(无论是否完整看完),业务方根据 closeRes.isEnded 决定怎么走:
|
|
54
|
-
* - isEnded=true → 调发奖接口 → return { ok: true, reward: N }
|
|
55
|
-
* - isEnded=false → 业务自己决定挽留:return { ok: false, retry: true } 让重新 show
|
|
56
|
-
*/
|
|
57
|
-
export type AdClaimFn = (closeRes: AdCloseResult) => Promise<AdClaimResult>;
|
|
58
40
|
/**
|
|
59
41
|
* 注入业务回调,应用启动时调用一次。
|
|
60
42
|
* 不调用也不会崩,但 loadConfig / showReward 会无效。
|
|
@@ -79,23 +61,23 @@ export declare function useAd(): {
|
|
|
79
61
|
loaded: Ref<boolean, boolean>;
|
|
80
62
|
loadConfig: (force?: boolean) => Promise<void>;
|
|
81
63
|
getUnitId: (type: AdType) => string;
|
|
82
|
-
showReward: (
|
|
64
|
+
showReward: (onClose?: ((res: AdCloseResult) => void | Promise<void>) | undefined) => Promise<void>;
|
|
83
65
|
showPopup: () => Promise<boolean>;
|
|
66
|
+
confirm: typeof confirmModal;
|
|
84
67
|
};
|
|
85
68
|
/**
|
|
86
|
-
* 激励视频中途关闭挽留弹窗 ——
|
|
69
|
+
* 激励视频中途关闭挽留弹窗 —— 通过 useAd().confirm 暴露给业务方:
|
|
87
70
|
*
|
|
88
|
-
* showReward
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* return { ok: false, retry: goon };
|
|
92
|
-
* }
|
|
71
|
+
* const { showReward, confirm } = useAd();
|
|
72
|
+
* showReward(async ({ isEnded }) => {
|
|
73
|
+
* if (!isEnded) return { ok: false, retry: await confirm() };
|
|
93
74
|
* const r = await claimAdReward();
|
|
94
75
|
* return r.code === 1 ? { ok: true, reward: r.data?.reward } : { ok: false, msg: r.info };
|
|
95
76
|
* });
|
|
96
77
|
*
|
|
97
78
|
* @returns true=用户选「继续观看」 / false=放弃
|
|
98
79
|
*/
|
|
99
|
-
|
|
80
|
+
declare function confirmModal(): Promise<boolean>;
|
|
100
81
|
/** 销毁全部广告实例并清空缓存(业务一般不用,hot reload 时调) */
|
|
101
82
|
export declare function destroyAds(): void;
|
|
83
|
+
export {};
|
|
@@ -10,7 +10,7 @@ export { usePageMeta } from './page-meta';
|
|
|
10
10
|
export { useStorage, type StorageInstance } from './storage';
|
|
11
11
|
export { useValidate } from './validate';
|
|
12
12
|
export { useFormat } from './format';
|
|
13
|
-
export { useAd, setConfigAd, destroyAds,
|
|
13
|
+
export { useAd, setConfigAd, destroyAds, type AdType, type AdConfig, type AdError, type AdAdapter, type AdCloseResult, } from './ad';
|
|
14
14
|
export { useShare, useShareConfig, setConfigShare, type ShareConfig, type ShareConfigResolver, type ShareFrom, type ShareAppMessageContent, type ShareTimelineContent, type ShareConfigMap, type ShareConfigAdapter, type PageShareItem, type PageShareFallback, type SharePayload, } from './share';
|
|
15
15
|
export { useContact, setConfigContact, type ContactConfig, type ContactAdapter, type ContactBindProps, } from './contact';
|
|
16
16
|
export { useUtils, type DownloadFileOptions, type DownloadFileResult, type TapEvent } from './utils';
|
package/dist/index.js
CHANGED
|
@@ -352,7 +352,8 @@ var __publicField = (obj, key, value) => {
|
|
|
352
352
|
confirmText = "确定",
|
|
353
353
|
cancelText = "取消",
|
|
354
354
|
confirmColor = "#3b82f6",
|
|
355
|
-
cancelColor = "#999999"
|
|
355
|
+
cancelColor = "#999999",
|
|
356
|
+
showCancel = true
|
|
356
357
|
} = opts;
|
|
357
358
|
uni.showModal({
|
|
358
359
|
title,
|
|
@@ -361,6 +362,7 @@ var __publicField = (obj, key, value) => {
|
|
|
361
362
|
cancelText,
|
|
362
363
|
confirmColor,
|
|
363
364
|
cancelColor,
|
|
365
|
+
showCancel,
|
|
364
366
|
success: (res) => resolve(res.confirm),
|
|
365
367
|
fail: () => resolve(false)
|
|
366
368
|
});
|
|
@@ -672,44 +674,19 @@ var __publicField = (obj, key, value) => {
|
|
|
672
674
|
function getUnitId(type) {
|
|
673
675
|
return store.config[`${type}_unit_id`] || "";
|
|
674
676
|
}
|
|
675
|
-
async function showReward(
|
|
677
|
+
async function showReward(onClose) {
|
|
676
678
|
const unitId = getUnitId("reward");
|
|
677
679
|
if (!unitId) {
|
|
678
680
|
msg().toast("激励广告未配置");
|
|
679
|
-
return
|
|
681
|
+
return;
|
|
680
682
|
}
|
|
681
683
|
if ((adapter$1 == null ? void 0 : adapter$1.isAuth) && !adapter$1.isAuth()) {
|
|
682
684
|
msg().toast("请先登录");
|
|
683
|
-
return
|
|
684
|
-
}
|
|
685
|
-
while (true) {
|
|
686
|
-
const closeRes = await playRewardedOnce(unitId);
|
|
687
|
-
if (!claim) {
|
|
688
|
-
if (closeRes.isEnded)
|
|
689
|
-
return true;
|
|
690
|
-
const goon = await confirmReward();
|
|
691
|
-
if (!goon)
|
|
692
|
-
return false;
|
|
693
|
-
continue;
|
|
694
|
-
}
|
|
695
|
-
try {
|
|
696
|
-
const r = await claim(closeRes);
|
|
697
|
-
if (r.retry)
|
|
698
|
-
continue;
|
|
699
|
-
if (!r.ok) {
|
|
700
|
-
if (r.msg)
|
|
701
|
-
msg().error(r.msg);
|
|
702
|
-
return false;
|
|
703
|
-
}
|
|
704
|
-
if (r.reward && r.reward > 0)
|
|
705
|
-
msg().success(`+${r.reward} 积分`);
|
|
706
|
-
return true;
|
|
707
|
-
} catch (e) {
|
|
708
|
-
console.warn("[useAd] claim failed", e);
|
|
709
|
-
msg().error("领取失败,请稍后再试");
|
|
710
|
-
return false;
|
|
711
|
-
}
|
|
685
|
+
return;
|
|
712
686
|
}
|
|
687
|
+
const closeRes = await playRewardedOnce(unitId);
|
|
688
|
+
if (onClose)
|
|
689
|
+
await onClose(closeRes);
|
|
713
690
|
}
|
|
714
691
|
async function showPopup() {
|
|
715
692
|
const unitId = getUnitId("popup");
|
|
@@ -725,7 +702,8 @@ var __publicField = (obj, key, value) => {
|
|
|
725
702
|
loadConfig: loadConfig2,
|
|
726
703
|
getUnitId,
|
|
727
704
|
showReward,
|
|
728
|
-
showPopup
|
|
705
|
+
showPopup,
|
|
706
|
+
confirm: confirmModal
|
|
729
707
|
};
|
|
730
708
|
}
|
|
731
709
|
async function playRewardedOnce(unitId) {
|
|
@@ -744,7 +722,7 @@ var __publicField = (obj, key, value) => {
|
|
|
744
722
|
hide();
|
|
745
723
|
}
|
|
746
724
|
}
|
|
747
|
-
function
|
|
725
|
+
function confirmModal() {
|
|
748
726
|
return new Promise((resolve) => {
|
|
749
727
|
uni.showModal({
|
|
750
728
|
title: "提示",
|
|
@@ -824,6 +802,11 @@ var __publicField = (obj, key, value) => {
|
|
|
824
802
|
var _a;
|
|
825
803
|
let ad = interstitialCache.get(unitId);
|
|
826
804
|
if (!ad) {
|
|
805
|
+
if (typeof uni.createInterstitialAd !== "function") {
|
|
806
|
+
console.warn("[useAd] 当前基础库不支持插屏广告");
|
|
807
|
+
resolve(false);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
827
810
|
ad = uni.createInterstitialAd({ adUnitId: unitId });
|
|
828
811
|
if (!ad) {
|
|
829
812
|
resolve(false);
|
|
@@ -834,12 +817,9 @@ var __publicField = (obj, key, value) => {
|
|
|
834
817
|
});
|
|
835
818
|
interstitialCache.set(unitId, ad);
|
|
836
819
|
}
|
|
837
|
-
ad.show().then(() => resolve(true)).catch(() => {
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
return;
|
|
841
|
-
}
|
|
842
|
-
ad.load().then(() => ad.show()).then(() => resolve(true)).catch(() => resolve(false));
|
|
820
|
+
ad.show().then(() => resolve(true)).catch((err) => {
|
|
821
|
+
console.warn(`[useAd] popup show error (${unitId})`, err);
|
|
822
|
+
resolve(false);
|
|
843
823
|
});
|
|
844
824
|
});
|
|
845
825
|
}
|
|
@@ -1900,7 +1880,6 @@ var __publicField = (obj, key, value) => {
|
|
|
1900
1880
|
exports2.alistAdapter = alistAdapter;
|
|
1901
1881
|
exports2.buildThemeStyle = buildThemeStyle;
|
|
1902
1882
|
exports2.clearDeviceCache = clearDeviceCache;
|
|
1903
|
-
exports2.confirmReward = confirmReward;
|
|
1904
1883
|
exports2.cosAdapter = cosAdapter;
|
|
1905
1884
|
exports2.destroyAds = destroyAds;
|
|
1906
1885
|
exports2.deviceToQuery = deviceToQuery;
|
package/dist/index.mjs
CHANGED
|
@@ -351,7 +351,8 @@ function useMsg() {
|
|
|
351
351
|
confirmText = "确定",
|
|
352
352
|
cancelText = "取消",
|
|
353
353
|
confirmColor = "#3b82f6",
|
|
354
|
-
cancelColor = "#999999"
|
|
354
|
+
cancelColor = "#999999",
|
|
355
|
+
showCancel = true
|
|
355
356
|
} = opts;
|
|
356
357
|
uni.showModal({
|
|
357
358
|
title,
|
|
@@ -360,6 +361,7 @@ function useMsg() {
|
|
|
360
361
|
cancelText,
|
|
361
362
|
confirmColor,
|
|
362
363
|
cancelColor,
|
|
364
|
+
showCancel,
|
|
363
365
|
success: (res) => resolve(res.confirm),
|
|
364
366
|
fail: () => resolve(false)
|
|
365
367
|
});
|
|
@@ -671,44 +673,19 @@ function useAd() {
|
|
|
671
673
|
function getUnitId(type) {
|
|
672
674
|
return store.config[`${type}_unit_id`] || "";
|
|
673
675
|
}
|
|
674
|
-
async function showReward(
|
|
676
|
+
async function showReward(onClose) {
|
|
675
677
|
const unitId = getUnitId("reward");
|
|
676
678
|
if (!unitId) {
|
|
677
679
|
msg().toast("激励广告未配置");
|
|
678
|
-
return
|
|
680
|
+
return;
|
|
679
681
|
}
|
|
680
682
|
if ((adapter$1 == null ? void 0 : adapter$1.isAuth) && !adapter$1.isAuth()) {
|
|
681
683
|
msg().toast("请先登录");
|
|
682
|
-
return
|
|
683
|
-
}
|
|
684
|
-
while (true) {
|
|
685
|
-
const closeRes = await playRewardedOnce(unitId);
|
|
686
|
-
if (!claim) {
|
|
687
|
-
if (closeRes.isEnded)
|
|
688
|
-
return true;
|
|
689
|
-
const goon = await confirmReward();
|
|
690
|
-
if (!goon)
|
|
691
|
-
return false;
|
|
692
|
-
continue;
|
|
693
|
-
}
|
|
694
|
-
try {
|
|
695
|
-
const r = await claim(closeRes);
|
|
696
|
-
if (r.retry)
|
|
697
|
-
continue;
|
|
698
|
-
if (!r.ok) {
|
|
699
|
-
if (r.msg)
|
|
700
|
-
msg().error(r.msg);
|
|
701
|
-
return false;
|
|
702
|
-
}
|
|
703
|
-
if (r.reward && r.reward > 0)
|
|
704
|
-
msg().success(`+${r.reward} 积分`);
|
|
705
|
-
return true;
|
|
706
|
-
} catch (e) {
|
|
707
|
-
console.warn("[useAd] claim failed", e);
|
|
708
|
-
msg().error("领取失败,请稍后再试");
|
|
709
|
-
return false;
|
|
710
|
-
}
|
|
684
|
+
return;
|
|
711
685
|
}
|
|
686
|
+
const closeRes = await playRewardedOnce(unitId);
|
|
687
|
+
if (onClose)
|
|
688
|
+
await onClose(closeRes);
|
|
712
689
|
}
|
|
713
690
|
async function showPopup() {
|
|
714
691
|
const unitId = getUnitId("popup");
|
|
@@ -724,7 +701,8 @@ function useAd() {
|
|
|
724
701
|
loadConfig: loadConfig2,
|
|
725
702
|
getUnitId,
|
|
726
703
|
showReward,
|
|
727
|
-
showPopup
|
|
704
|
+
showPopup,
|
|
705
|
+
confirm: confirmModal
|
|
728
706
|
};
|
|
729
707
|
}
|
|
730
708
|
async function playRewardedOnce(unitId) {
|
|
@@ -743,7 +721,7 @@ async function playRewardedOnce(unitId) {
|
|
|
743
721
|
hide();
|
|
744
722
|
}
|
|
745
723
|
}
|
|
746
|
-
function
|
|
724
|
+
function confirmModal() {
|
|
747
725
|
return new Promise((resolve) => {
|
|
748
726
|
uni.showModal({
|
|
749
727
|
title: "提示",
|
|
@@ -823,6 +801,11 @@ function showInterstitialAd(unitId) {
|
|
|
823
801
|
var _a;
|
|
824
802
|
let ad = interstitialCache.get(unitId);
|
|
825
803
|
if (!ad) {
|
|
804
|
+
if (typeof uni.createInterstitialAd !== "function") {
|
|
805
|
+
console.warn("[useAd] 当前基础库不支持插屏广告");
|
|
806
|
+
resolve(false);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
826
809
|
ad = uni.createInterstitialAd({ adUnitId: unitId });
|
|
827
810
|
if (!ad) {
|
|
828
811
|
resolve(false);
|
|
@@ -833,12 +816,9 @@ function showInterstitialAd(unitId) {
|
|
|
833
816
|
});
|
|
834
817
|
interstitialCache.set(unitId, ad);
|
|
835
818
|
}
|
|
836
|
-
ad.show().then(() => resolve(true)).catch(() => {
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
ad.load().then(() => ad.show()).then(() => resolve(true)).catch(() => resolve(false));
|
|
819
|
+
ad.show().then(() => resolve(true)).catch((err) => {
|
|
820
|
+
console.warn(`[useAd] popup show error (${unitId})`, err);
|
|
821
|
+
resolve(false);
|
|
842
822
|
});
|
|
843
823
|
});
|
|
844
824
|
}
|
|
@@ -1900,7 +1880,6 @@ export {
|
|
|
1900
1880
|
alistAdapter,
|
|
1901
1881
|
buildThemeStyle,
|
|
1902
1882
|
clearDeviceCache,
|
|
1903
|
-
confirmReward,
|
|
1904
1883
|
cosAdapter,
|
|
1905
1884
|
destroyAds,
|
|
1906
1885
|
deviceToQuery,
|
package/package.json
CHANGED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view v-if="mounted" class="hlw-notice-mask" :class="{ 'hlw-notice-mask--in': visible }" @tap.self>
|
|
3
|
+
<view class="hlw-notice-card" :class="{ 'hlw-notice-card--in': visible }" @tap.stop>
|
|
4
|
+
<view class="hlw-notice-head">
|
|
5
|
+
<view class="hlw-notice-icon" :style="{ background: iconTint }">
|
|
6
|
+
<view class="hlw-notice-icon-i" :class="iconClass" :style="{ color: iconColor }" />
|
|
7
|
+
</view>
|
|
8
|
+
<text class="hlw-notice-title">{{ title }}</text>
|
|
9
|
+
</view>
|
|
10
|
+
|
|
11
|
+
<scroll-view
|
|
12
|
+
class="hlw-notice-body"
|
|
13
|
+
scroll-y
|
|
14
|
+
:show-scrollbar="false"
|
|
15
|
+
:enhanced="true"
|
|
16
|
+
>
|
|
17
|
+
<rich-text class="hlw-notice-text" :nodes="contentNodes" />
|
|
18
|
+
</scroll-view>
|
|
19
|
+
|
|
20
|
+
<view
|
|
21
|
+
class="hlw-notice-btn"
|
|
22
|
+
:style="{ background: iconColor }"
|
|
23
|
+
hover-class="hlw-notice-btn--hover"
|
|
24
|
+
@tap="onConfirm"
|
|
25
|
+
>
|
|
26
|
+
<text class="hlw-notice-btn-text">{{ confirmText }}</text>
|
|
27
|
+
</view>
|
|
28
|
+
|
|
29
|
+
<view
|
|
30
|
+
v-if="showCancel"
|
|
31
|
+
class="hlw-notice-dismiss"
|
|
32
|
+
hover-class="hlw-notice-dismiss--hover"
|
|
33
|
+
@tap="onCancel"
|
|
34
|
+
>
|
|
35
|
+
<text class="hlw-notice-dismiss-text">{{ cancelText }}</text>
|
|
36
|
+
</view>
|
|
37
|
+
</view>
|
|
38
|
+
</view>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script setup lang="ts">
|
|
42
|
+
import { computed, nextTick, ref, watch } from "vue";
|
|
43
|
+
|
|
44
|
+
interface Props {
|
|
45
|
+
show?: boolean;
|
|
46
|
+
title?: string;
|
|
47
|
+
/** 内容:HTML 原样渲染,纯文本自动 \n -> <br/> */
|
|
48
|
+
content?: string;
|
|
49
|
+
/** iconify class,例:i-fa6-solid-bell */
|
|
50
|
+
iconClass?: string;
|
|
51
|
+
/** 图标 + 主按钮颜色 */
|
|
52
|
+
iconColor?: string;
|
|
53
|
+
/** 图标背景色 */
|
|
54
|
+
iconTint?: string;
|
|
55
|
+
confirmText?: string;
|
|
56
|
+
cancelText?: string;
|
|
57
|
+
showCancel?: boolean;
|
|
58
|
+
/** 进出场动画时长(ms),需与 CSS transition 一致 */
|
|
59
|
+
animMs?: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
63
|
+
show: false,
|
|
64
|
+
title: "",
|
|
65
|
+
content: "",
|
|
66
|
+
iconClass: "i-fa6-solid-bell",
|
|
67
|
+
iconColor: "#1C1C1E",
|
|
68
|
+
iconTint: "#F2F2F7",
|
|
69
|
+
confirmText: "我知道了",
|
|
70
|
+
cancelText: "稍后再看",
|
|
71
|
+
showCancel: false,
|
|
72
|
+
animMs: 260,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const emit = defineEmits<{ confirm: []; cancel: [] }>();
|
|
76
|
+
|
|
77
|
+
const mounted = ref(false);
|
|
78
|
+
const visible = ref(false);
|
|
79
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
80
|
+
|
|
81
|
+
const contentNodes = computed(() => {
|
|
82
|
+
const raw = props.content;
|
|
83
|
+
return /<[a-z][\s\S]*>/i.test(raw) ? raw : raw.replace(/\n/g, "<br/>");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
watch(
|
|
87
|
+
() => props.show,
|
|
88
|
+
async (next) => {
|
|
89
|
+
if (timer) {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
timer = null;
|
|
92
|
+
}
|
|
93
|
+
if (next) {
|
|
94
|
+
mounted.value = true;
|
|
95
|
+
await nextTick();
|
|
96
|
+
visible.value = true;
|
|
97
|
+
} else {
|
|
98
|
+
visible.value = false;
|
|
99
|
+
timer = setTimeout(() => {
|
|
100
|
+
mounted.value = false;
|
|
101
|
+
timer = null;
|
|
102
|
+
}, props.animMs);
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{ immediate: true },
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
function onConfirm() {
|
|
109
|
+
emit("confirm");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function onCancel() {
|
|
113
|
+
emit("cancel");
|
|
114
|
+
}
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
<style lang="scss" scoped>
|
|
118
|
+
.hlw-notice-mask {
|
|
119
|
+
position: fixed;
|
|
120
|
+
inset: 0;
|
|
121
|
+
z-index: 1000;
|
|
122
|
+
display: flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
justify-content: center;
|
|
125
|
+
padding: 40rpx;
|
|
126
|
+
background: rgba(0, 0, 0, 0);
|
|
127
|
+
transition: background 0.25s ease-out;
|
|
128
|
+
|
|
129
|
+
&--in {
|
|
130
|
+
background: rgba(0, 0, 0, 0.4);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.hlw-notice-card {
|
|
135
|
+
width: 100%;
|
|
136
|
+
max-width: 540rpx;
|
|
137
|
+
background: var(--surface-card, #ffffff);
|
|
138
|
+
border-radius: var(--radius-xl, 36rpx);
|
|
139
|
+
padding: 40rpx 36rpx 32rpx;
|
|
140
|
+
box-shadow: 0 40rpx 80rpx rgba(0, 0, 0, 0.1);
|
|
141
|
+
transform: scale(0.92);
|
|
142
|
+
opacity: 0;
|
|
143
|
+
transition: transform 0.28s cubic-bezier(0.34, 1.2, 0.64, 1), opacity 0.22s ease-out;
|
|
144
|
+
|
|
145
|
+
&--in {
|
|
146
|
+
transform: scale(1);
|
|
147
|
+
opacity: 1;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.hlw-notice-head {
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
gap: 14rpx;
|
|
156
|
+
margin-bottom: 24rpx;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.hlw-notice-icon {
|
|
160
|
+
width: 48rpx;
|
|
161
|
+
height: 48rpx;
|
|
162
|
+
border-radius: var(--radius-sm, 12rpx);
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
justify-content: center;
|
|
166
|
+
flex-shrink: 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.hlw-notice-icon-i {
|
|
170
|
+
font-size: 24rpx;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.hlw-notice-title {
|
|
174
|
+
font-size: var(--font-md, 30rpx);
|
|
175
|
+
font-weight: 700;
|
|
176
|
+
color: var(--text-primary, #1c1c1e);
|
|
177
|
+
line-height: 1.3;
|
|
178
|
+
word-break: break-word;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.hlw-notice-body {
|
|
182
|
+
width: 100%;
|
|
183
|
+
max-height: 560rpx;
|
|
184
|
+
margin-bottom: 36rpx;
|
|
185
|
+
padding-right: 6rpx;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.hlw-notice-text {
|
|
189
|
+
display: block;
|
|
190
|
+
font-size: var(--font-sm, 26rpx);
|
|
191
|
+
line-height: 1.65;
|
|
192
|
+
color: var(--text-secondary, #5c5c5e);
|
|
193
|
+
text-align: left;
|
|
194
|
+
white-space: pre-wrap;
|
|
195
|
+
word-break: break-word;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.hlw-notice-btn {
|
|
199
|
+
width: 100%;
|
|
200
|
+
padding: 22rpx 0;
|
|
201
|
+
border-radius: 9999rpx;
|
|
202
|
+
display: flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
justify-content: center;
|
|
205
|
+
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
|
|
206
|
+
transition: transform 0.15s ease-out, filter 0.15s ease-out;
|
|
207
|
+
|
|
208
|
+
&--hover {
|
|
209
|
+
transform: scale(0.97);
|
|
210
|
+
filter: brightness(0.92);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.hlw-notice-btn-text {
|
|
215
|
+
color: #ffffff;
|
|
216
|
+
font-size: var(--font-base, 28rpx);
|
|
217
|
+
font-weight: 600;
|
|
218
|
+
letter-spacing: 1rpx;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.hlw-notice-dismiss {
|
|
222
|
+
margin-top: 16rpx;
|
|
223
|
+
padding: 10rpx 0;
|
|
224
|
+
display: flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
justify-content: center;
|
|
227
|
+
|
|
228
|
+
&--hover {
|
|
229
|
+
opacity: 0.5;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.hlw-notice-dismiss-text {
|
|
234
|
+
font-size: var(--font-xs, 24rpx);
|
|
235
|
+
color: var(--text-muted, #8e8e93);
|
|
236
|
+
font-weight: 500;
|
|
237
|
+
}
|
|
238
|
+
</style>
|
|
@@ -88,26 +88,6 @@ export interface AdCloseResult {
|
|
|
88
88
|
isEnded: boolean;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/** showReward 的 claim 回调返回契约 */
|
|
92
|
-
export interface AdClaimResult {
|
|
93
|
-
/** 本次结果:true=成功 / false=失败 */
|
|
94
|
-
ok: boolean;
|
|
95
|
-
/** 奖励数(用于 toast 显示 +N 积分;无则不 toast) */
|
|
96
|
-
reward?: number;
|
|
97
|
-
/** 失败提示语;不传不弹 toast */
|
|
98
|
-
msg?: string;
|
|
99
|
-
/** 仅 isEnded=false 时常用:true 让 mp-core 重新 show 一次(业务方挽留确认后用) */
|
|
100
|
-
retry?: boolean;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* showReward 的 claim 回调签名 —— 业务方按场景实现(领积分、解锁、抽奖等)。
|
|
105
|
-
* 每次广告关闭后被调用一次(无论是否完整看完),业务方根据 closeRes.isEnded 决定怎么走:
|
|
106
|
-
* - isEnded=true → 调发奖接口 → return { ok: true, reward: N }
|
|
107
|
-
* - isEnded=false → 业务自己决定挽留:return { ok: false, retry: true } 让重新 show
|
|
108
|
-
*/
|
|
109
|
-
export type AdClaimFn = (closeRes: AdCloseResult) => Promise<AdClaimResult>;
|
|
110
|
-
|
|
111
91
|
const EMPTY: AdConfig = {
|
|
112
92
|
banner_unit_id: "",
|
|
113
93
|
grid_unit_id: "",
|
|
@@ -180,51 +160,27 @@ export function useAd() {
|
|
|
180
160
|
}
|
|
181
161
|
|
|
182
162
|
/**
|
|
183
|
-
* 显示激励视频
|
|
163
|
+
* 显示激励视频 —— 播一次、关闭后调 onClose(带 isEnded),其它都交给业务。
|
|
184
164
|
*
|
|
185
|
-
*
|
|
165
|
+
* showReward(({ isEnded }) => {
|
|
166
|
+
* if (!isEnded) confirm().then(goon => goon && tapReward());
|
|
167
|
+
* else claimAdReward().then(...);
|
|
168
|
+
* });
|
|
186
169
|
*
|
|
187
|
-
* @param
|
|
188
|
-
* @returns true=最终成功;false=未看完且业务放弃 / 配置缺失 / claim 失败
|
|
170
|
+
* @param onClose 关闭回调;不传则只播。retry / toast / 发奖全在业务侧自己写。
|
|
189
171
|
*/
|
|
190
|
-
async function showReward(
|
|
172
|
+
async function showReward(onClose?: (res: AdCloseResult) => void | Promise<void>): Promise<void> {
|
|
191
173
|
const unitId = getUnitId("reward");
|
|
192
174
|
if (!unitId) {
|
|
193
175
|
msg().toast("激励广告未配置");
|
|
194
|
-
return
|
|
176
|
+
return;
|
|
195
177
|
}
|
|
196
178
|
if (adapter?.isAuth && !adapter.isAuth()) {
|
|
197
179
|
msg().toast("请先登录");
|
|
198
|
-
return
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
while (true) {
|
|
202
|
-
const closeRes = await playRewardedOnce(unitId);
|
|
203
|
-
|
|
204
|
-
// 没传 claim:默认行为 = 看完了 true / 没看完弹挽留 + 重试 / 放弃 false
|
|
205
|
-
if (!claim) {
|
|
206
|
-
if (closeRes.isEnded) return true;
|
|
207
|
-
const goon = await confirmReward();
|
|
208
|
-
if (!goon) return false;
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// 业务方决定怎么走(包括「未看完是否挽留」)
|
|
213
|
-
try {
|
|
214
|
-
const r = await claim(closeRes);
|
|
215
|
-
if (r.retry) continue;
|
|
216
|
-
if (!r.ok) {
|
|
217
|
-
if (r.msg) msg().error(r.msg);
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
if (r.reward && r.reward > 0) msg().success(`+${r.reward} 积分`);
|
|
221
|
-
return true;
|
|
222
|
-
} catch (e) {
|
|
223
|
-
console.warn("[useAd] claim failed", e);
|
|
224
|
-
msg().error("领取失败,请稍后再试");
|
|
225
|
-
return false;
|
|
226
|
-
}
|
|
180
|
+
return;
|
|
227
181
|
}
|
|
182
|
+
const closeRes = await playRewardedOnce(unitId);
|
|
183
|
+
if (onClose) await onClose(closeRes);
|
|
228
184
|
}
|
|
229
185
|
|
|
230
186
|
/**
|
|
@@ -246,6 +202,7 @@ export function useAd() {
|
|
|
246
202
|
getUnitId,
|
|
247
203
|
showReward,
|
|
248
204
|
showPopup,
|
|
205
|
+
confirm: confirmModal,
|
|
249
206
|
};
|
|
250
207
|
}
|
|
251
208
|
|
|
@@ -272,20 +229,18 @@ async function playRewardedOnce(unitId: string): Promise<AdCloseResult> {
|
|
|
272
229
|
}
|
|
273
230
|
|
|
274
231
|
/**
|
|
275
|
-
* 激励视频中途关闭挽留弹窗 ——
|
|
232
|
+
* 激励视频中途关闭挽留弹窗 —— 通过 useAd().confirm 暴露给业务方:
|
|
276
233
|
*
|
|
277
|
-
* showReward
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* return { ok: false, retry: goon };
|
|
281
|
-
* }
|
|
234
|
+
* const { showReward, confirm } = useAd();
|
|
235
|
+
* showReward(async ({ isEnded }) => {
|
|
236
|
+
* if (!isEnded) return { ok: false, retry: await confirm() };
|
|
282
237
|
* const r = await claimAdReward();
|
|
283
238
|
* return r.code === 1 ? { ok: true, reward: r.data?.reward } : { ok: false, msg: r.info };
|
|
284
239
|
* });
|
|
285
240
|
*
|
|
286
241
|
* @returns true=用户选「继续观看」 / false=放弃
|
|
287
242
|
*/
|
|
288
|
-
|
|
243
|
+
function confirmModal(): Promise<boolean> {
|
|
289
244
|
return new Promise((resolve) => {
|
|
290
245
|
uni.showModal({
|
|
291
246
|
title: "提示",
|
|
@@ -361,11 +316,17 @@ function showRewardedAd(unitId: string, hooks?: { onShown?: () => void }): Promi
|
|
|
361
316
|
});
|
|
362
317
|
}
|
|
363
318
|
|
|
364
|
-
/**
|
|
319
|
+
/** 底层插屏广告包装:实例按 unitId 缓存复用,show 失败 console.warn 不重试(频控/网络等失败重试也救不了) */
|
|
365
320
|
function showInterstitialAd(unitId: string): Promise<boolean> {
|
|
366
321
|
return new Promise((resolve) => {
|
|
367
322
|
let ad: any = interstitialCache.get(unitId);
|
|
368
323
|
if (!ad) {
|
|
324
|
+
// 老基础库可能没有这个 API(对应官方 if (wx.createInterstitialAd))
|
|
325
|
+
if (typeof uni.createInterstitialAd !== "function") {
|
|
326
|
+
console.warn("[useAd] 当前基础库不支持插屏广告");
|
|
327
|
+
resolve(false);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
369
330
|
ad = uni.createInterstitialAd({ adUnitId: unitId });
|
|
370
331
|
if (!ad) { resolve(false); return; }
|
|
371
332
|
ad.onError?.((err: AdError) => {
|
|
@@ -373,15 +334,11 @@ function showInterstitialAd(unitId: string): Promise<boolean> {
|
|
|
373
334
|
});
|
|
374
335
|
interstitialCache.set(unitId, ad);
|
|
375
336
|
}
|
|
376
|
-
|
|
377
337
|
ad.show()
|
|
378
338
|
.then(() => resolve(true))
|
|
379
|
-
.catch(() => {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
.then(() => ad.show())
|
|
383
|
-
.then(() => resolve(true))
|
|
384
|
-
.catch(() => resolve(false));
|
|
339
|
+
.catch((err: any) => {
|
|
340
|
+
console.warn(`[useAd] popup show error (${unitId})`, err);
|
|
341
|
+
resolve(false);
|
|
385
342
|
});
|
|
386
343
|
});
|
|
387
344
|
}
|
package/src/composables/index.ts
CHANGED
|
@@ -14,14 +14,11 @@ export {
|
|
|
14
14
|
useAd,
|
|
15
15
|
setConfigAd,
|
|
16
16
|
destroyAds,
|
|
17
|
-
confirmReward,
|
|
18
17
|
type AdType,
|
|
19
18
|
type AdConfig,
|
|
20
19
|
type AdError,
|
|
21
20
|
type AdAdapter,
|
|
22
21
|
type AdCloseResult,
|
|
23
|
-
type AdClaimResult,
|
|
24
|
-
type AdClaimFn,
|
|
25
22
|
} from "./ad";
|
|
26
23
|
export {
|
|
27
24
|
useShare,
|
|
@@ -20,6 +20,8 @@ export interface ModalOptions {
|
|
|
20
20
|
cancelText?: string;
|
|
21
21
|
confirmColor?: string;
|
|
22
22
|
cancelColor?: string;
|
|
23
|
+
/** false=隐藏取消按钮(纯提示弹窗用) */
|
|
24
|
+
showCancel?: boolean;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export interface HlwMsg {
|
|
@@ -87,6 +89,7 @@ export function useMsg(): HlwMsg {
|
|
|
87
89
|
cancelText = "取消",
|
|
88
90
|
confirmColor = "#3b82f6",
|
|
89
91
|
cancelColor = "#999999",
|
|
92
|
+
showCancel = true,
|
|
90
93
|
} = opts;
|
|
91
94
|
uni.showModal({
|
|
92
95
|
title,
|
|
@@ -95,6 +98,7 @@ export function useMsg(): HlwMsg {
|
|
|
95
98
|
cancelText,
|
|
96
99
|
confirmColor,
|
|
97
100
|
cancelColor,
|
|
101
|
+
showCancel,
|
|
98
102
|
success: (res) => resolve(res.confirm),
|
|
99
103
|
fail: () => resolve(false),
|
|
100
104
|
});
|