@bi-nova/ui-weapp 1.1.3 → 1.2.0-beta.202608171048

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bi-nova/ui-weapp",
3
- "version": "1.1.3",
3
+ "version": "1.2.0-beta.202608171048",
4
4
  "description": "BI Nova SDK WeChat Mini Program Components",
5
5
  "miniprogram": "src",
6
6
  "license": "ISC",
package/src/ad/README.md CHANGED
@@ -73,6 +73,10 @@ yarn add @bi-nova/core @bi-nova/services @bi-nova/ui-weapp
73
73
 
74
74
  广告位配置由我方维护,通常包含实际投放渠道、广告素材、展示开关等信息。业务方一般不需要关心底层配置结构,只需要确保传入的 `ad-id` 与投放场景一致。
75
75
 
76
+ ### 小程序合法域名
77
+
78
+ 请在微信公众平台将 `https://nad.ehuoke.com` 配置为小程序的 `request 合法域名`。如果未配置,广告位配置请求会被微信拦截,组件无法获取可展示的广告内容。
79
+
76
80
  使用 `ad-id` 前,请确认 SDK 已完成以下初始化:
77
81
 
78
82
  1. 通过 `createNova` 创建 SDK 实例,并在微信小程序环境开启 `autoEnv: true`。
@@ -161,6 +165,8 @@ getApp().globalData.biNova.clearToken()
161
165
 
162
166
  开屏广告支持微信原生广告、自定义图片广告、自定义视频广告,并提供倒计时跳过和画中画能力。
163
167
 
168
+ 广告来源、展示顺序和加载失败后的降级均由广告位配置与组件自动处理。业务方不需要判断广告平台或实现兜底逻辑,只需通过 `load`、`error`、`close` 等事件接收最终状态。
169
+
164
170
  建议承载开屏广告的页面在页面配置中设置 `"navigationStyle": "custom"`,隐藏默认导航栏后开屏广告展示更完整,整体视觉效果更好。
165
171
 
166
172
  ```json
@@ -171,54 +177,6 @@ getApp().globalData.biNova.clearToken()
171
177
 
172
178
  `ad-id` 需要使用开屏类型广告位,具体广告位 ID 请向我方获取。
173
179
 
174
- ## 广告素材能力
175
-
176
- 以下字段由广告位配置生成,业务方通常不需要在页面中直接传入。这里仅用于说明广告素材支持的配置能力,便于和我方确认广告位需求。
177
-
178
- ### 通用字段
179
-
180
- | 字段 | 类型 | 说明 |
181
- | --- | --- | --- |
182
- | `image` / `imageUrl` | `string` | 图片素材地址。Banner 广告和开屏图片广告使用。 |
183
- | `title` | `string` | 广告标题。 |
184
- | `description` | `string` | 广告描述。 |
185
- | `buttonText` | `string` | 操作按钮文案。 |
186
- | `badgeText` / `adLabel` | `string` | 广告角标文案,默认 `广告`。 |
187
- | `loadTimeout` / `timeout` | `number` | 自定义素材加载超时时间,单位毫秒,最小值为 `1000`,默认 `5000`。 |
188
-
189
- ### 开屏扩展字段
190
-
191
- | 字段 | 类型 | 说明 |
192
- | --- | --- | --- |
193
- | `video` / `videoUrl` | `string` | 视频素材地址。存在时开屏广告按视频广告渲染。 |
194
- | `poster` | `string` | 视频封面地址。未传时优先使用 `image`。 |
195
- | `muted` | `boolean` | 视频是否静音。默认静音,传 `false` 可开启声音。 |
196
- | `jumpSeconds` | `number` | 当前广告独立倒计时秒数,优先级高于组件 `jump-seconds`。 |
197
- | `adSpec` / `spec` | `1 \| 2` | 广告规格,`1` 为横版,`2` 为竖版。 |
198
- | `width` / `videoWidth` | `number` | 素材宽度,用于推断广告规格。 |
199
- | `height` / `videoHeight` | `number` | 素材高度,用于推断广告规格。 |
200
-
201
- ### 跳转字段
202
-
203
- | 跳转类型 | 字段 | 说明 |
204
- | --- | --- | --- |
205
- | 跳转其他小程序 | `appId` / `miniProgramAppId` / `jumpAppId` | 目标小程序 appId。 |
206
- | 跳转小程序内部页面 | `path` / `pagePath` / `jumpPath` / `targetUrl` | 当前小程序页面路径。 |
207
- | 打开 WebView | `url` / `webUrl` / `h5Url` / `jumpUrl` | H5 地址。组件不会直接打开 WebView,会触发 `openWebview` 事件交给业务处理。 |
208
-
209
- 跳转优先级为:其他小程序 > 小程序内部页面 > WebView。
210
-
211
- ### 示例
212
-
213
- ```js
214
- Page({
215
- data: {
216
- bannerAdId: 'ad_123456',
217
- splashAdId: 'launch_splash'
218
- }
219
- })
220
- ```
221
-
222
180
  ## 事件
223
181
 
224
182
  ### 通用事件
@@ -226,9 +184,10 @@ Page({
226
184
  | 事件名 | 说明 | 事件详情 |
227
185
  | --- |---------------------------------| --- |
228
186
  | `load` | 广告加载完成;微信系统开屏广告承接时额外返回 `nativeSplash: true` | `{ provider, type, adId?, nativeSplash? }`。仅当 `nativeSplash === true` 时表示微信系统开屏广告已承接;普通微信 `ad-custom` 广告同样返回 `provider: 'wechat'`,但不包含该标识 |
229
- | `error` | 接口请求失败或返回失败码;SDK 未初始化;广告素材加载失败;广告跳转失败 | `{ provider?, type, adId?, errCode, errMsg }` |
187
+ | `error` | 接口请求失败或返回失败码;SDK 未初始化;广告素材加载失败;广告跳转失败 | `{ provider?, type, adId?, errCode, errMsg, errorType?, mediaType?, rawErrCode?, rawErrMsg? }` |
230
188
  | `close` | 广告关闭;远程配置成功但无可展示广告时也会触发 | `{ provider, type, adId?, reason? }`,无可展示广告时 `reason` 为 `noAd` |
231
189
  | `action` | 自定义广告点击 | `{ provider, type, adId? }` |
190
+ | `feedback` | 自研 Banner 反馈操作 | 不感兴趣:`{ provider, type, adId?, action: 'dislike', reason, reasonText }`;反馈问题:`{ provider, type, adId?, action: 'report' }` |
232
191
  | `openWebview` | 自定义广告命中 WebView 跳转 | `{ provider, type, adId?, url }` |
233
192
 
234
193
  ### 统一广告组件扩展事件
@@ -268,6 +227,7 @@ Page({
268
227
  | `DISPLAY_AD_REMOTE_CONFIG_FAIL` | 广告位配置接口返回失败。 |
269
228
  | `DISPLAY_AD_REMOTE_CONFIG_EXCEPTION` | 广告位配置请求异常。 |
270
229
  | `UNSUPPORTED_DISPLAY_AD_TYPE` | `type` 不是 `banner` 或 `splash`。 |
230
+ | `BI_BANNER_WECHAT_AD_LOAD_FAIL` | 微信原生 Banner 加载失败;存在自研 Banner 时组件会自动降级。 |
271
231
  | `BI_BANNER_CUSTOM_MEDIA_LOAD_TIMEOUT` | Banner 自定义图片加载超时。 |
272
232
  | `BI_BANNER_CUSTOM_MEDIA_LOAD_FAIL` | Banner 自定义图片加载失败。 |
273
233
  | `BI_BANNER_NAVIGATE_MINI_PROGRAM_FAIL` | Banner 自定义广告跳转其他小程序失败。 |
@@ -278,6 +238,17 @@ Page({
278
238
  | `BI_SPLASH_NAVIGATE_MINI_PROGRAM_FAIL` | 开屏自定义广告跳转其他小程序失败。 |
279
239
  | `BI_SPLASH_NAVIGATE_FAIL` | 开屏自定义广告跳转内部页面失败。 |
280
240
 
241
+ 自研素材加载失败或超时时,Banner 与开屏广告会额外返回以下统一字段:
242
+
243
+ | 字段 | 说明 |
244
+ | --- | --- |
245
+ | `errorType` | 统一错误分类。加载失败为 `CUSTOM_MEDIA_LOAD_FAIL`,加载超时为 `CUSTOM_MEDIA_LOAD_TIMEOUT`。 |
246
+ | `mediaType` | 素材类型,取值为 `image` 或 `video`。 |
247
+ | `rawErrCode` | 微信图片或视频组件返回的原始错误码;运行时未提供时为 `undefined`。 |
248
+ | `rawErrMsg` | 微信图片或视频组件返回的原始错误信息;运行时未提供时为 `undefined`。 |
249
+
250
+ 自研广告事件还会尽可能携带 `configId`、`placementId`、`configName`、`advertPlatform` 等广告配置上下文。`rawErrCode` 和 `rawErrMsg` 仅用于问题排查,业务逻辑应优先使用稳定的 `errorType` 和 `errCode`;微信运行时不保证原始错误中包含 HTTP 状态码。
251
+
281
252
  ## 注意事项
282
253
 
283
254
  1. `ad-id` 由我方统一分配,请确保广告位类型与组件 `type` 保持一致。
@@ -285,3 +256,4 @@ Page({
285
256
  3. WebView 地址不会由组件直接打开,必须由业务页面监听 `openWebview` 后处理。
286
257
  4. 使用 `ad-id` 前,需要先把业务 SDK 实例挂载到 `app.biNova`。
287
258
  5. 开屏广告页面推荐设置 `"navigationStyle": "custom"`,避免默认导航栏占用展示空间,提升开屏展示效果。
259
+ 6. 请将 `https://nad.ehuoke.com` 配置为小程序的 `request 合法域名`,否则无法获取广告位配置。
@@ -3,7 +3,7 @@
3
3
  * @Author wutao
4
4
  * @Date 2026-04-10
5
5
  * @LastEditors wutao
6
- * @LastEditTime 2026-04-17
6
+ * @LastEditTime 2026-08-14 16:23:04
7
7
  */
8
8
  const {
9
9
  navigateCustomAd
@@ -13,26 +13,34 @@ Component({
13
13
  properties: {
14
14
  unitId: { type: String, value: '' },
15
15
  customAd: { type: Object, value: null },
16
+ priority: { type: String, value: 'wechat-first' },
16
17
  adIntervals: { type: Number, value: 30 }
17
18
  },
18
19
  data: {
19
20
  isWechatAd: false,
21
+ visible: false,
22
+ hasCustomMedia: false,
20
23
  customLoaded: false,
21
- customFailed: false
24
+ customFailed: false,
25
+ feedbackVisible: false,
26
+ feedbackStage: 'primary',
27
+ dislikeReasons: [
28
+ { label: '与我无关', value: 'irrelevant' },
29
+ { label: '重复收到多次', value: 'repeated' },
30
+ { label: '内容太差', value: 'poorContent' }
31
+ ]
22
32
  },
23
33
  lifetimes: {
24
34
  attached() {
25
- this.syncProvider(this.properties.unitId)
26
- this.resetCustomLoadState()
35
+ this.syncAdSource(this.properties.unitId, this.properties.customAd, this.properties.priority)
27
36
  },
28
37
  detached() {
29
38
  this.clearCustomLoadTimeout()
30
39
  }
31
40
  },
32
41
  observers: {
33
- 'unitId, customAd'(nextUnitId) {
34
- this.syncProvider(nextUnitId)
35
- this.resetCustomLoadState()
42
+ 'unitId, customAd, priority'(nextUnitId, nextCustomAd, nextPriority) {
43
+ this.syncAdSource(nextUnitId, nextCustomAd, nextPriority)
36
44
  }
37
45
  },
38
46
  methods: {
@@ -42,9 +50,17 @@ Component({
42
50
  * @returns {{provider: 'wechat' | 'custom', type: 'banner'}}
43
51
  */
44
52
  createEventDetail(provider) {
53
+ const customAd = provider === 'custom' && this.properties.customAd || null
54
+
45
55
  return {
46
56
  provider,
47
- type: 'banner'
57
+ type: 'banner',
58
+ ...(customAd ? {
59
+ configId: customAd.configId,
60
+ placementId: customAd.placementId,
61
+ configName: customAd.configName,
62
+ advertPlatform: customAd.advertPlatform
63
+ } : {})
48
64
  }
49
65
  },
50
66
 
@@ -81,12 +97,128 @@ Component({
81
97
  },
82
98
 
83
99
  /**
84
- * 根据 `unitId` 同步广告来源
100
+ * 同步 Banner 广告来源与展示状态
101
+ * @param {string} unitId 微信广告位 ID
102
+ * @param {Record<string, any> | null} customAd 自研广告配置
103
+ * @param {string} priority 广告展示优先级
104
+ * @returns {void}
105
+ */
106
+ syncAdSource(unitId, customAd, priority) {
107
+ const hasCustomMedia = Boolean(customAd && customAd.image)
108
+ const isWechatAd = this.shouldStartWechatAd(unitId, hasCustomMedia, priority)
109
+
110
+ this.clearCustomLoadTimeout()
111
+ this.resetProviderFailures()
112
+ this.setData({
113
+ isWechatAd,
114
+ visible: Boolean(unitId || hasCustomMedia),
115
+ hasCustomMedia,
116
+ customLoaded: false,
117
+ customFailed: false,
118
+ feedbackVisible: false,
119
+ feedbackStage: 'primary'
120
+ }, () => {
121
+ if (!isWechatAd && hasCustomMedia) {
122
+ this.startCustomLoadTimeout()
123
+ }
124
+ })
125
+ },
126
+
127
+ /**
128
+ * 根据广告配置和优先级判断是否首发微信广告
85
129
  * @param {string} unitId 微信广告位 ID
130
+ * @param {boolean} hasCustomMedia 是否存在自研 Banner 素材
131
+ * @param {string} priority 广告展示优先级
132
+ * @returns {boolean}
133
+ */
134
+ shouldStartWechatAd(unitId, hasCustomMedia, priority) {
135
+ if (!unitId) return false
136
+ if (!hasCustomMedia) return true
137
+ return !['custom-first', 'custom-only'].includes(priority)
138
+ },
139
+
140
+ /**
141
+ * 重置当前展示周期的广告来源失败记录
142
+ * @returns {void}
143
+ */
144
+ resetProviderFailures() {
145
+ this.failedProviders = {
146
+ wechat: false,
147
+ custom: false
148
+ }
149
+ },
150
+
151
+ /**
152
+ * 记录指定广告来源加载失败
153
+ * @param {'wechat' | 'custom'} provider 广告来源
86
154
  * @returns {void}
87
155
  */
88
- syncProvider(unitId) {
89
- this.setData({ isWechatAd: Boolean(unitId) })
156
+ markProviderFailed(provider) {
157
+ if (!this.failedProviders) this.resetProviderFailures()
158
+ this.failedProviders[provider] = true
159
+ },
160
+
161
+ /**
162
+ * 判断指定广告来源是否已经失败
163
+ * @param {'wechat' | 'custom'} provider 广告来源
164
+ * @returns {boolean}
165
+ */
166
+ hasProviderFailed(provider) {
167
+ return Boolean(this.failedProviders && this.failedProviders[provider])
168
+ },
169
+
170
+ /**
171
+ * 切换当前展示的广告来源
172
+ * @param {'wechat' | 'custom'} provider 目标广告来源
173
+ * @returns {void}
174
+ */
175
+ switchProvider(provider) {
176
+ const isWechatAd = provider === 'wechat'
177
+
178
+ this.clearCustomLoadTimeout()
179
+ this.setData({
180
+ isWechatAd,
181
+ visible: true,
182
+ customLoaded: false,
183
+ customFailed: false,
184
+ feedbackVisible: false,
185
+ feedbackStage: 'primary'
186
+ }, () => {
187
+ if (!isWechatAd) {
188
+ this.startCustomLoadTimeout()
189
+ }
190
+ })
191
+ },
192
+
193
+ /**
194
+ * 所有可用广告来源均失败后结束当前 Banner 展示流程
195
+ * @param {'wechat' | 'custom'} provider 最后失败的广告来源
196
+ * @param {Record<string, any>} errorDetail 错误详情
197
+ * @returns {void}
198
+ */
199
+ finishProviderFailure(provider, errorDetail) {
200
+ const hasWechatAd = Boolean(this.properties.unitId)
201
+ const hasCustomAd = Boolean(this.data.hasCustomMedia)
202
+ const reason = hasWechatAd && hasCustomAd
203
+ ? 'allProvidersFailed'
204
+ : provider === 'wechat'
205
+ ? 'wechatAdLoadFail'
206
+ : 'customMediaLoadFail'
207
+
208
+ this.clearCustomLoadTimeout()
209
+ this.setData({
210
+ visible: false,
211
+ customLoaded: false,
212
+ customFailed: provider === 'custom',
213
+ feedbackVisible: false,
214
+ feedbackStage: 'primary'
215
+ })
216
+ this.triggerEvent('close', {
217
+ provider,
218
+ type: 'banner',
219
+ reason,
220
+ error: errorDetail
221
+ })
90
222
  },
91
223
 
92
224
  /**
@@ -103,18 +235,11 @@ Component({
103
235
  },
104
236
 
105
237
  /**
106
- * 重置自定义广告加载状态
238
+ * 启动自研 Banner 素材加载超时检测
107
239
  * @returns {void}
108
240
  */
109
- resetCustomLoadState() {
241
+ startCustomLoadTimeout() {
110
242
  this.clearCustomLoadTimeout()
111
- this.setData({
112
- customLoaded: false,
113
- customFailed: false
114
- })
115
-
116
- if (this.properties.unitId || !this.properties.customAd || !this.properties.customAd.image) return
117
-
118
243
  this.customLoadTimer = setTimeout(() => {
119
244
  this.handleCustomLoadTimeout()
120
245
  }, this.getCustomLoadTimeout())
@@ -137,6 +262,8 @@ Component({
137
262
  * @returns {void}
138
263
  */
139
264
  handleLoad(event) {
265
+ if (!this.data.isWechatAd) return
266
+
140
267
  this.triggerEvent('load', this.resolveEventDetail('wechat', event))
141
268
  },
142
269
 
@@ -146,7 +273,25 @@ Component({
146
273
  * @returns {void}
147
274
  */
148
275
  handleError(event) {
149
- this.triggerEvent('error', this.resolveEventDetail('wechat', event))
276
+ if (!this.data.isWechatAd) return
277
+
278
+ const rawDetail = event && event.detail && typeof event.detail === 'object' ? event.detail : {}
279
+ const errorDetail = this.resolveEventDetail('wechat', event, {
280
+ errCode: 'BI_BANNER_WECHAT_AD_LOAD_FAIL',
281
+ errMsg: rawDetail.errMsg || '微信 Banner 广告加载失败',
282
+ rawErrCode: rawDetail.errCode,
283
+ rawErrMsg: rawDetail.errMsg
284
+ })
285
+
286
+ this.triggerEvent('error', errorDetail)
287
+ this.markProviderFailed('wechat')
288
+
289
+ if (this.data.hasCustomMedia && !this.hasProviderFailed('custom')) {
290
+ this.switchProvider('custom')
291
+ return
292
+ }
293
+
294
+ this.finishProviderFailure('wechat', errorDetail)
150
295
  },
151
296
 
152
297
  /**
@@ -155,6 +300,8 @@ Component({
155
300
  * @returns {void}
156
301
  */
157
302
  handleClose(event) {
303
+ if (!this.data.isWechatAd) return
304
+
158
305
  this.triggerEvent('close', this.resolveEventDetail('wechat', event))
159
306
  },
160
307
 
@@ -163,7 +310,7 @@ Component({
163
310
  * @returns {void}
164
311
  */
165
312
  handleCustomLoad() {
166
- if (this.data.customLoaded) return
313
+ if (this.data.isWechatAd || this.data.customLoaded) return
167
314
 
168
315
  this.clearCustomLoadTimeout()
169
316
  this.setData({ customLoaded: true })
@@ -175,13 +322,40 @@ Component({
175
322
  * @returns {void}
176
323
  */
177
324
  handleCustomLoadTimeout() {
178
- if (this.data.customLoaded || this.data.customFailed) return
325
+ if (this.data.isWechatAd || this.data.customLoaded || this.data.customFailed) return
179
326
 
180
- this.setData({ customFailed: true })
181
- this.triggerEvent('error', this.resolveEventDetail('custom', null, {
327
+ this.handleCustomMediaFail(null, {
328
+ errorType: 'CUSTOM_MEDIA_LOAD_TIMEOUT',
182
329
  errCode: 'BI_BANNER_CUSTOM_MEDIA_LOAD_TIMEOUT',
183
- errMsg: 'Banner 广告素材加载超时'
184
- }))
330
+ errMsg: 'Banner 自定义广告素材加载超时',
331
+ mediaType: 'image',
332
+ rawErrCode: undefined,
333
+ rawErrMsg: undefined
334
+ })
335
+ },
336
+
337
+ /**
338
+ * 统一处理自研 Banner 素材加载失败
339
+ * @param {Record<string, any> | null} event 素材错误事件
340
+ * @param {Record<string, any>} detail 错误详情
341
+ * @returns {void}
342
+ */
343
+ handleCustomMediaFail(event, detail) {
344
+ if (this.data.isWechatAd || this.data.customFailed) return
345
+
346
+ this.clearCustomLoadTimeout()
347
+ const errorDetail = this.resolveEventDetail('custom', event, detail)
348
+
349
+ this.setData({ customFailed: true })
350
+ this.triggerEvent('error', errorDetail)
351
+ this.markProviderFailed('custom')
352
+
353
+ if (this.properties.unitId && !this.hasProviderFailed('wechat')) {
354
+ this.switchProvider('wechat')
355
+ return
356
+ }
357
+
358
+ this.finishProviderFailure('custom', errorDetail)
185
359
  },
186
360
 
187
361
  /**
@@ -190,14 +364,21 @@ Component({
190
364
  * @returns {void}
191
365
  */
192
366
  handleCustomMediaError(event) {
193
- if (this.data.customFailed) return
367
+ /** @type {Record<string, any>} 微信图片组件返回的原始错误信息 */
368
+ const rawDetail = event && event.detail && typeof event.detail === 'object'
369
+ ? event.detail
370
+ : event && typeof event === 'object'
371
+ ? event
372
+ : {}
194
373
 
195
- this.clearCustomLoadTimeout()
196
- this.setData({ customFailed: true })
197
- this.triggerEvent('error', this.resolveEventDetail('custom', event, {
374
+ this.handleCustomMediaFail(event, {
375
+ errorType: 'CUSTOM_MEDIA_LOAD_FAIL',
198
376
  errCode: 'BI_BANNER_CUSTOM_MEDIA_LOAD_FAIL',
199
- errMsg: 'Banner 广告素材加载失败'
200
- }))
377
+ errMsg: 'Banner 自定义广告素材加载失败',
378
+ mediaType: 'image',
379
+ rawErrCode: rawDetail.errCode,
380
+ rawErrMsg: rawDetail.errMsg
381
+ })
201
382
  },
202
383
 
203
384
  /**
@@ -205,6 +386,11 @@ Component({
205
386
  * @returns {void}
206
387
  */
207
388
  handleCustomAction() {
389
+ if (this.data.feedbackVisible) {
390
+ this.closeFeedbackPanel()
391
+ return
392
+ }
393
+
208
394
  const actionDetail = this.createEventDetail('custom')
209
395
 
210
396
  this.triggerEvent('action', actionDetail)
@@ -215,8 +401,100 @@ Component({
215
401
  * 处理自定义广告关闭事件
216
402
  * @returns {void}
217
403
  */
218
- handleCustomClose() {
219
- this.triggerEvent('close', this.createEventDetail('custom'))
404
+ handleCustomClose(extraDetail) {
405
+ this.clearCustomLoadTimeout()
406
+ this.setData({
407
+ visible: false,
408
+ feedbackVisible: false,
409
+ feedbackStage: 'primary'
410
+ })
411
+ this.triggerEvent('close', {
412
+ ...this.createEventDetail('custom'),
413
+ ...extraDetail
414
+ })
415
+ },
416
+
417
+ /**
418
+ * 处理广告标识点击,切换反馈菜单显隐
419
+ * @returns {void}
420
+ */
421
+ handleBadgeTap() {
422
+ const feedbackVisible = !this.data.feedbackVisible
423
+
424
+ this.setData({
425
+ feedbackVisible,
426
+ feedbackStage: 'primary'
427
+ })
428
+ },
429
+
430
+ /**
431
+ * 拦截反馈浮层内部点击,避免触发 Banner 整体跳转
432
+ * @returns {void}
433
+ */
434
+ handleFeedbackPanelTap() {},
435
+
436
+ /**
437
+ * 关闭反馈菜单并重置一级菜单
438
+ * @returns {void}
439
+ */
440
+ closeFeedbackPanel() {
441
+ this.setData({
442
+ feedbackVisible: false,
443
+ feedbackStage: 'primary'
444
+ })
445
+ },
446
+
447
+ /**
448
+ * 处理一级反馈选项
449
+ * @param {Record<string, any>} event 点击事件
450
+ * @returns {void}
451
+ */
452
+ handleFeedbackOptionTap(event) {
453
+ const action = event && event.currentTarget && event.currentTarget.dataset && event.currentTarget.dataset.action
454
+
455
+ if (action === 'dislike') {
456
+ this.setData({ feedbackStage: 'dislike' })
457
+ return
458
+ }
459
+
460
+ if (action === 'report') {
461
+ this.closeFeedbackPanel()
462
+ this.triggerEvent('feedback', {
463
+ ...this.createEventDetail('custom'),
464
+ action: 'report'
465
+ })
466
+
467
+ if (typeof wx !== 'undefined' && typeof wx.showToast === 'function') {
468
+ wx.showToast({
469
+ title: '已收到您的反馈,我们会尽快处理~',
470
+ icon: 'none',
471
+ duration: 2000
472
+ })
473
+ }
474
+ }
475
+ },
476
+
477
+ /**
478
+ * 处理不感兴趣原因并关闭当前 Banner
479
+ * @param {Record<string, any>} event 点击事件
480
+ * @returns {void}
481
+ */
482
+ handleDislikeReasonTap(event) {
483
+ const dataset = event && event.currentTarget && event.currentTarget.dataset || {}
484
+ const reason = dataset.reason || ''
485
+ const reasonText = dataset.label || ''
486
+
487
+ this.triggerEvent('feedback', {
488
+ ...this.createEventDetail('custom'),
489
+ action: 'dislike',
490
+ reason,
491
+ reasonText
492
+ })
493
+ this.handleCustomClose({
494
+ reason: 'notInterested',
495
+ feedbackReason: reason,
496
+ feedbackReasonText: reasonText
497
+ })
220
498
  },
221
499
 
222
500
  /**
@@ -1,5 +1,5 @@
1
1
  <ad-custom
2
- wx:if="{{isWechatAd && unitId}}"
2
+ wx:if="{{visible && isWechatAd && unitId}}"
3
3
  unit-id="{{unitId}}"
4
4
  ad-intervals="{{adIntervals}}"
5
5
  bind:load="handleLoad"
@@ -8,31 +8,50 @@
8
8
  />
9
9
 
10
10
  <view
11
- wx:elif="{{customAd && customAd.image && !customFailed}}"
11
+ wx:elif="{{visible && !isWechatAd && customAd && customAd.image && !customFailed}}"
12
12
  class="bi-banner-ad"
13
13
  >
14
14
  <view class="bi-banner-ad__surface" bindtap="handleCustomAction">
15
- <view class="bi-banner-ad__media">
16
- <text class="bi-banner-ad__badge bi-banner-ad__badge--overlay">{{customAd.badgeText || '广告'}}</text>
17
- <image
18
- class="bi-banner-ad__image"
19
- src="{{customAd.image}}"
20
- mode="aspectFill"
21
- bindload="handleCustomLoad"
22
- binderror="handleCustomMediaError"
23
- />
24
- </view>
25
- <view class="bi-banner-ad__content">
26
- <view class="bi-banner-ad__body">
27
- <text wx:if="{{customAd.title}}" class="bi-banner-ad__title">{{customAd.title}}</text>
15
+ <image
16
+ class="bi-banner-ad__image"
17
+ src="{{customAd.image}}"
18
+ mode="widthFix"
19
+ bindload="handleCustomLoad"
20
+ binderror="handleCustomMediaError"
21
+ />
22
+ </view>
23
+
24
+ <view class="bi-banner-ad__badge" catchtap="handleBadgeTap">
25
+ <text>{{customAd.badgeText || '广告'}}</text>
26
+ <view class="bi-banner-ad__badge-arrow"></view>
27
+ </view>
28
+
29
+ <view
30
+ wx:if="{{feedbackVisible}}"
31
+ class="bi-banner-ad__feedback-panel"
32
+ catchtap="handleFeedbackPanelTap"
33
+ >
34
+ <block wx:if="{{feedbackStage === 'primary'}}">
35
+ <view class="bi-banner-ad__feedback-source">由赞助商提供的内容</view>
36
+ <view class="bi-banner-ad__feedback-item" data-action="dislike" catchtap="handleFeedbackOptionTap">
37
+ 不感兴趣
38
+ </view>
39
+ <view class="bi-banner-ad__feedback-item" data-action="report" catchtap="handleFeedbackOptionTap">
40
+ 反馈问题
28
41
  </view>
29
- <view class="bi-banner-ad__footer">
30
- <text wx:if="{{customAd.description}}" class="bi-banner-ad__description">{{customAd.description}}</text>
31
- <button class="bi-banner-ad__button" catchtap="handleCustomAction">
32
- {{customAd.buttonText || '立即查看'}}
33
- </button>
42
+ </block>
43
+
44
+ <block wx:else>
45
+ <view
46
+ wx:for="{{dislikeReasons}}"
47
+ wx:key="value"
48
+ class="bi-banner-ad__feedback-item"
49
+ data-reason="{{item.value}}"
50
+ data-label="{{item.label}}"
51
+ catchtap="handleDislikeReasonTap"
52
+ >
53
+ {{item.label}}
34
54
  </view>
35
- </view>
55
+ </block>
36
56
  </view>
37
- <button class="bi-banner-ad__close" catchtap="handleCustomClose">×</button>
38
57
  </view>