@yaoxiu/marketing-dsl 1.4.1 → 1.5.1

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/README.md CHANGED
@@ -68,7 +68,7 @@ import { createRuntime } from '@yaoxiu/marketing-dsl';
68
68
  const runtime = createRuntime(dsl, {
69
69
  user: { shopName: '麦爆了旗舰店', version: '专业版' },
70
70
  sources: { listRenewTiers: params => api.getTiers(params) },
71
- handlers: { setNeverRemind: () => localStorage.setItem('never', '1') },
71
+ handlers: { openRecharge: () => showRechargeModal() },
72
72
  emit: (event, payload) => console.log(event, payload),
73
73
  });
74
74
 
@@ -135,6 +135,34 @@ unsubscribe();
135
135
  | 不能打任意接口 | 数据源用注册名引用,配置里写不了 URL |
136
136
  | 不能调任意方法 | `call` 只能调宿主注册在 `handlers` 里的方法 |
137
137
 
138
+ ## `call` 的适用边界
139
+
140
+ `call` 是「**让宿主去做一件事**」,不是「记一笔账」。
141
+
142
+ | ✅ 该用 `call` | ❌ 不该用 `call` |
143
+ | ---------------------------------------------------------- | --------------------------------------------- |
144
+ | 唤起充值弹窗、打开客服会话、复制到剪贴板、跳转应用内特定页 | 记录用户意图、写本地存储(`localStorage` 等) |
145
+
146
+ 判断标准只有一条:**这件事后端需不需要知道**。
147
+
148
+ - 需要 → 走**上报**:`close` 的 `reason`,或 `track` 动作。
149
+ - 纯粹是宿主的本地动作、后端不关心 → 才用 `call`。
150
+
151
+ 典型反例是「不再提醒」。曾经的写法是 `close` 之外再配一条 `call` 让宿主写 `localStorage`,这是错的:
152
+
153
+ 1. **频控职责分裂**。「这条还要不要出」由后端算,前端存一份频控依据后端看不见,两边依据不一致,规则就是假的。
154
+ 2. **换设备、清缓存就失效**。这是用户的长期意图,该跟着账号走,不该跟着浏览器走。
155
+ 3. **每个宿主各写一份**。`handlers` 由宿主注册,前台一份、调试台一份、将来 Vue3 宿主再一份,行为迟早分叉。
156
+
157
+ 正确写法不需要任何新增代码,`close` 带上 `reason` 即可:
158
+
159
+ ```jsonc
160
+ { "type": "close", "reason": "user-dismiss" }
161
+ ```
162
+
163
+ 上报出去就是 `{ "id": "...", "event": "close", "reason": "user-dismiss" }`,后端看到这个 `reason`
164
+ 就永久压制这条物料。注意这个信息走 **`close` 的 `reason`**,不走 `extra`——`extra` 只属于 `click`。
165
+
138
166
  ## 完整 DSL 语法
139
167
 
140
168
  见调试台的「开发文档」页,或从那里导出 markdown / AI 提示词。
@@ -158,3 +186,45 @@ buildAiPrompt(); // 生成喂给 AI 的提示词
158
186
  章节正文是**数据**(`DocBlock` 数组)不是字符串:调试台按块渲染成页面,`toMarkdown` 导出成 markdown,
159
187
  同一份内容两边永远一致。节点类型 / 动作 / 样式白名单 / 埋点事件名正则等清单一律从解释器常量取,
160
188
  并经 `reconcileRows` 对账——解释器加了东西而文档没跟上,会在文档页面上直接标出来。
189
+
190
+ ---
191
+
192
+ ## 上报契约(`/report` 子路径)
193
+
194
+ 物料的曝光 / 点击 / 关闭要如实上报给后端。**「运行时事件 → 上报载荷」这一步的映射是纯函数,
195
+ 放在这个包的 `/report` 子路径里**,前台(`move-front`)与后台调试台(`moveback-front`)共用同一份:
196
+
197
+ ```ts
198
+ import {
199
+ mapRuntimeEventToReport,
200
+ buildExposureReport,
201
+ } from '@yaoxiu/marketing-dsl/report';
202
+ import type { MarketingReportParams } from '@yaoxiu/marketing-dsl/report';
203
+
204
+ // 宿主把物料画到页面上时主动发(曝光不由运行时事件触发,解释器不知道自己被画出来了)
205
+ buildExposureReport(materialId); // → { id, event: 'exposure' }
206
+
207
+ // runtime 的 emit 事件直接喂进来,返回 null 表示这个事件不产生上报
208
+ mapRuntimeEventToReport(materialId, 'track', {
209
+ event: 'banner_renew_click',
210
+ params: { pos: 1 },
211
+ });
212
+ // → { id, event: 'click', extra: { trigger: 'track', name: 'banner_renew_click', params: { pos: 1 } } }
213
+ mapRuntimeEventToReport(materialId, 'navigate', { url: 'https://a.com' });
214
+ // → { id, event: 'click', extra: { trigger: 'navigate', url: 'https://a.com' } }
215
+ mapRuntimeEventToReport(materialId, 'close', { reason: 'mask' });
216
+ // → { id, event: 'close', reason: 'mask' }
217
+ mapRuntimeEventToReport(materialId, 'call', payload); // → null(call / error / state-change / view-change / ready / open 都不上报)
218
+ ```
219
+
220
+ 上报载荷 `MarketingReportParams` 是**可辨识联合**:`reason` 只属于 `close`、`extra` 只属于 `click`,
221
+ 写错编译不过;不该出现的字段声明成 `?: never` 而非省略,消费方仍可直接读,不必先窄化。
222
+
223
+ **给谁用**:接了后端上报的宿主(前台坑位组件),以及调试台的「上报预览」——
224
+ 预览要展示的就是真实会发给后端的 JSON,映射再写第二份,前台改了口径而调试台没跟上,预览就会骗人。
225
+
226
+ **为什么不在主入口**:主入口的身份是「DSL 解释器」,上报是**可选的附加约定**,
227
+ 只有接了上报的宿主才需要。主入口不 re-export,边界才清楚,只用解释器的消费方也不背这份代码。
228
+ 包根同样有一个物理 `report/` 转发垫片,理由与 `docs/` 一致(webpack 4 不认 `exports`)。
229
+
230
+ 契约字段的完整说明(后端视角)见 `move-front` 仓库的 `docs/marketing/README.md`。
@@ -144,7 +144,11 @@ var actionRows = [
144
144
  ["closeAll", "\u2014", "\u4E0D\u7BA1\u51E0\u5C42\uFF0C\u5168\u90E8\u5173\u6389"],
145
145
  ["setState", "`key`\u3001`value`", "\u6539\u5C40\u90E8\u72B6\u6001\uFF0C\u7528\u6765\u505A\u9009\u4E2D\u5207\u6362"],
146
146
  ["track", "`event`\u3001`params`", "\u57CB\u70B9\u4E0A\u62A5\u3002`event` \u6709\u547D\u540D\u89C4\u8303\uFF0C\u89C1\u4E0B"],
147
- ["call", "`name`\u3001`params`", "\u8C03\u7528\u5BBF\u4E3B\u6CE8\u518C\u7684\u65B9\u6CD5\uFF08\u5982\u9886\u5238\uFF09\uFF0C`name` \u662F\u767D\u540D\u5355"],
147
+ [
148
+ "call",
149
+ "`name`\u3001`params`",
150
+ "\u8BA9\u5BBF\u4E3B\u53BB\u505A\u4E00\u4EF6\u4E8B\uFF08\u5982\u5524\u8D77\u5145\u503C\uFF09\uFF0C`name` \u662F\u767D\u540D\u5355\uFF0C\u8FB9\u754C\u89C1\u4E0B"
151
+ ],
148
152
  ["sequence", "`actions`", "\u4F9D\u6B21\u6267\u884C\u591A\u4E2A\u52A8\u4F5C"]
149
153
  ];
150
154
  var sections = [
@@ -257,12 +261,45 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6`
257
261
  {
258
262
  t: "note",
259
263
  tone: "info",
260
- text: '`close` \u7684 `reason` \u4F1A\u4E00\u8D77\u4F20\u7ED9\u5BBF\u4E3B\uFF0C\u5BBF\u4E3B\u636E\u6B64\u51B3\u5B9A\u540E\u7EED\u884C\u4E3A\u2014\u2014\u6BD4\u5982 `reason: "user-dismiss"` \u65F6\u8BB0\u4F4F\u300C\u4ECA\u5929\u4E0D\u518D\u663E\u793A\u300D\u3002\u4E0D\u5199 `reason` \u9ED8\u8BA4\u662F `action`\u3002'
264
+ text: "`close` \u7684 `reason` \u4F1A\u4E00\u8D77\u4F20\u7ED9\u5BBF\u4E3B\uFF0C\u5BBF\u4E3B\u636E\u6B64\u51B3\u5B9A\u540E\u7EED\u884C\u4E3A\uFF0C\u5E76\u539F\u6837\u4E0A\u62A5\u7ED9\u540E\u7AEF\u3002\u4E0D\u5199 `reason` \u9ED8\u8BA4\u662F `action`\u3002"
265
+ },
266
+ { t: "h3", text: "\u300C\u4E0D\u518D\u63D0\u9192\u300D\u5C31\u5199 close \u7684 reason\uFF0C\u4E0D\u8981\u914D call" },
267
+ {
268
+ t: "p",
269
+ text: "\u60F3\u8BA9\u7269\u6599\u300C\u5173\u4E86\u5C31\u522B\u518D\u51FA\u73B0\u300D\uFF0C\u4E00\u6761 `close` \u5C31\u591F\u4E86\uFF0C\u628A\u7528\u6237\u7684\u610F\u56FE\u5199\u8FDB `reason`\uFF1A"
270
+ },
271
+ {
272
+ t: "code",
273
+ code: `{ "type": "close", "reason": "user-dismiss" }`
274
+ },
275
+ {
276
+ t: "p",
277
+ text: '\u4E0A\u62A5\u51FA\u53BB\u5C31\u662F `{ "id": "...", "event": "close", "reason": "user-dismiss" }`\uFF0C\u540E\u7AEF\u770B\u5230\u8FD9\u4E2A `reason` \u5C31\u6C38\u4E45\u538B\u5236\u8FD9\u6761\u7269\u6599\u3002\u8FD9\u4E2A\u4FE1\u606F\u8D70 `close` \u7684 `reason`\uFF0C\u4E0D\u8D70 `extra`\u2014\u2014`extra` \u53EA\u5C5E\u4E8E `click`\u3002'
261
278
  },
262
279
  {
263
280
  t: "note",
264
281
  tone: "warn",
265
- text: "\u60F3\u8BA9\u5F39\u7A97\u300C\u5173\u4E86\u5C31\u522B\u518D\u51FA\u73B0\u300D\uFF0C\u5149\u9760 `close` \u4E0D\u591F\u2014\u2014\u5B83\u53EA\u7BA1\u8FD9\u4E00\u6B21\u3002\u8981\u914D\u4E00\u6761 `call` \u8BA9\u5BBF\u4E3B\u8BB0\u4E0B\u6765\uFF08\u89C1\u793A\u4F8B \u2461 \u548C \u2464 \u7684\u300C\u4E0D\u518D\u63D0\u9192\u300D\uFF09\u3002"
282
+ text: "**\u4E0D\u8981**\u518D\u914D\u4E00\u6761 `call` \u8BA9\u5BBF\u4E3B\u5199 `localStorage` \u8BB0\u4F4F\u3002\u9891\u63A7\u7531\u540E\u7AEF\u7B97\uFF0C\u524D\u7AEF\u5B58\u4E00\u4EFD\u540E\u7AEF\u770B\u4E0D\u89C1\u7684\u4F9D\u636E\uFF0C\u4E24\u8FB9\u5C31\u5BF9\u4E0D\u4E0A\uFF1B\u800C\u4E14\u6362\u8BBE\u5907\u3001\u6E05\u7F13\u5B58\u5C31\u5931\u6548\u2014\u2014\u7528\u6237\u7684\u957F\u671F\u610F\u56FE\u8BE5\u8DDF\u7740\u8D26\u53F7\u8D70\uFF0C\u4E0D\u8BE5\u8DDF\u7740\u6D4F\u89C8\u5668\u8D70\uFF1B`call` \u7684\u65B9\u6CD5\u8FD8\u662F\u5404\u4E2A\u5BBF\u4E3B\u5404\u6CE8\u518C\u4E00\u4EFD\u7684\uFF0C\u884C\u4E3A\u8FDF\u65E9\u5206\u53C9\u3002"
283
+ },
284
+ { t: "h3", text: "call \u7684\u9002\u7528\u8FB9\u754C" },
285
+ {
286
+ t: "p",
287
+ text: "`call` \u662F\u300C**\u8BA9\u5BBF\u4E3B\u53BB\u505A\u4E00\u4EF6\u4E8B**\u300D\uFF0C\u4E0D\u662F\u300C\u8BB0\u4E00\u7B14\u8D26\u300D\u3002"
288
+ },
289
+ {
290
+ t: "table",
291
+ head: ["\u2705 \u8BE5\u7528 call", "\u274C \u4E0D\u8BE5\u7528 call"],
292
+ rows: [
293
+ [
294
+ "\u5524\u8D77\u5145\u503C\u5F39\u7A97\u3001\u6253\u5F00\u5BA2\u670D\u4F1A\u8BDD\u3001\u590D\u5236\u5230\u526A\u8D34\u677F\u3001\u8DF3\u8F6C\u5E94\u7528\u5185\u7279\u5B9A\u9875\u9762",
295
+ "\u8BB0\u5F55\u7528\u6237\u610F\u56FE\u3001\u5199\u672C\u5730\u5B58\u50A8"
296
+ ]
297
+ ]
298
+ },
299
+ {
300
+ t: "note",
301
+ tone: "info",
302
+ text: "\u5224\u65AD\u6807\u51C6\u53EA\u6709\u4E00\u6761\uFF1A**\u8FD9\u4EF6\u4E8B\u540E\u7AEF\u9700\u4E0D\u9700\u8981\u77E5\u9053**\u3002\u9700\u8981 \u2192 \u8D70\u4E0A\u62A5\uFF08`close` \u7684 `reason`\uFF0C\u6216 `track`\uFF09\uFF1B\u7EAF\u7CB9\u662F\u5BBF\u4E3B\u7684\u672C\u5730\u52A8\u4F5C\u3001\u540E\u7AEF\u4E0D\u5173\u5FC3 \u2192 \u624D\u7528 `call`\u3002"
266
303
  },
267
304
  { t: "h3", text: "\u66DD\u5149\u548C\u5173\u95ED\u4E5F\u8981\u57CB\u70B9\uFF1Astage.onShow / stage.onClose" },
268
305
  {
@@ -289,7 +326,7 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6`
289
326
  ["`close-button`", "\u70B9\u4E86\u5173\u95ED\u6309\u94AE"],
290
327
  ["`mask`", "\u70B9\u4E86\u906E\u7F69\uFF08\u9700\u8981 `maskClosable: true`\uFF09"],
291
328
  ["`replace`", "\u88AB `open` \u7684 `replace` \u6A21\u5F0F\u9876\u6389"],
292
- ["\u81EA\u5B9A\u4E49\u503C", '`close` \u52A8\u4F5C\u91CC\u81EA\u5DF1\u5199\u7684 `reason`\uFF0C\u5982 `"never-remind"`'],
329
+ ["\u81EA\u5B9A\u4E49\u503C", '`close` \u52A8\u4F5C\u91CC\u81EA\u5DF1\u5199\u7684 `reason`\uFF0C\u5982 `"user-dismiss"`'],
293
330
  ["`action`", "`close` \u52A8\u4F5C\u6CA1\u5199 `reason` \u65F6\u7684\u9ED8\u8BA4\u503C"]
294
331
  ]
295
332
  },
@@ -310,14 +347,13 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6`
310
347
  "textAlign": "center", "lineHeight": 22, "cursor": "pointer" },
311
348
  "action": { "type": "sequence", "actions": [
312
349
  { "type": "track", "event": "notice_close" },
313
- { "type": "call", "name": "setNeverRemind" }, // \u8BB0\u4F4F\u522B\u518D\u51FA
314
- { "type": "close", "reason": "user-dismiss" }
350
+ { "type": "close", "reason": "user-dismiss" } // reason \u4E0A\u62A5\u7ED9\u540E\u7AEF\uFF0C\u636E\u6B64\u4E0D\u518D\u4E0B\u53D1
315
351
  ] } }`
316
352
  },
317
353
  {
318
354
  t: "note",
319
355
  tone: "info",
320
- text: "`close` \u53EA\u662F\u901A\u77E5\u5BBF\u4E3B\u300C\u7528\u6237\u5173\u6389\u4E86\u300D\uFF0C**\u5173\u6389\u4E4B\u540E\u8981\u4E0D\u8981\u518D\u51FA\u3001\u9694\u591A\u4E45\u518D\u51FA\u7531\u5BBF\u4E3B\u51B3\u5B9A**\u3002\u60F3\u8BA9\u5B83\u522B\u518D\u51FA\u73B0\uFF0C\u914D\u4E00\u6761 `call` \u8BA9\u5BBF\u4E3B\u8BB0\u4E0B\u6765\u3002"
356
+ text: '`close` \u53EA\u662F\u901A\u77E5\u5BBF\u4E3B\u300C\u7528\u6237\u5173\u6389\u4E86\u300D\uFF0C**\u8FD9\u4E00\u6B21\u5173\u4E0D\u5173\u7531\u5BBF\u4E3B\u51B3\u5B9A**\uFF1B\u800C\u300C\u4EE5\u540E\u8FD8\u51FA\u4E0D\u51FA\u300D\u7531\u540E\u7AEF\u6839\u636E\u4E0A\u62A5\u7684 `reason` \u51B3\u5B9A\uFF0C\u6240\u4EE5\u60F3\u8BA9\u5B83\u522B\u518D\u51FA\u73B0\uFF0C\u5199 `reason: "user-dismiss"` \u5373\u53EF\uFF0C\u4E0D\u9700\u8981\u518D\u914D `call`\u3002'
321
357
  },
322
358
  {
323
359
  t: "note",
@@ -2301,13 +2337,7 @@ var noticeToPopup = {
2301
2337
  fontSize: 13,
2302
2338
  cursor: "pointer"
2303
2339
  },
2304
- action: {
2305
- type: "sequence",
2306
- actions: [
2307
- { type: "call", name: "setNeverRemind" },
2308
- { type: "close", reason: "user-dismiss" }
2309
- ]
2310
- }
2340
+ action: { type: "close", reason: "user-dismiss" }
2311
2341
  }
2312
2342
  ]
2313
2343
  }
@@ -2673,13 +2703,13 @@ var confirmAndSwitch = {
2673
2703
  background: ORANGE3,
2674
2704
  cursor: "pointer"
2675
2705
  },
2676
- // 确认后整个关掉,两层一起消失
2706
+ // 确认后整个关掉,两层一起消失。
2707
+ // 「不再提醒」的意图靠 reason 上报给后端,不配 call 写本地存储
2677
2708
  action: {
2678
2709
  type: "sequence",
2679
2710
  actions: [
2680
2711
  { type: "track", event: "popup_never_remind_confirm" },
2681
- { type: "call", name: "setNeverRemind" },
2682
- { type: "closeAll" }
2712
+ { type: "closeAll", reason: "user-dismiss" }
2683
2713
  ]
2684
2714
  },
2685
2715
  children: [
@@ -2993,13 +3023,8 @@ var imageBanner = {
2993
3023
  fontSize: 13,
2994
3024
  cursor: "pointer"
2995
3025
  },
2996
- action: {
2997
- type: "sequence",
2998
- actions: [
2999
- { type: "call", name: "setNeverRemind" },
3000
- { type: "close", reason: "user-dismiss" }
3001
- ]
3002
- }
3026
+ // 「不再提醒」写在 close 的 reason 上,上报给后端即可,不配 call 写本地存储
3027
+ action: { type: "close", reason: "user-dismiss" }
3003
3028
  }
3004
3029
  ]
3005
3030
  }
@@ -3130,16 +3155,11 @@ var imageHotspot = {
3130
3155
  {
3131
3156
  id: "hotspot-never",
3132
3157
  type: "box",
3133
- // 最底部「不再提醒」,动作是关弹窗而不是跳转
3158
+ // 最底部「不再提醒」,动作是关弹窗而不是跳转。
3159
+ // reason 上报给后端,后端据此不再下发这条物料——不需要写本地存储
3134
3160
  rect: ["30%", 478, "40%", 26],
3135
3161
  style: { radius: 13, background: HOTSPOT_BG, cursor: "pointer" },
3136
- action: {
3137
- type: "sequence",
3138
- actions: [
3139
- { type: "call", name: "setNeverRemind" },
3140
- { type: "close", reason: "never-remind" }
3141
- ]
3142
- }
3162
+ action: { type: "close", reason: "user-dismiss" }
3143
3163
  }
3144
3164
  ]
3145
3165
  };
@@ -3217,7 +3237,6 @@ var noticeBar = {
3217
3237
  type: "sequence",
3218
3238
  actions: [
3219
3239
  { type: "track", event: "notice_close", params: { notice: "guide" } },
3220
- { type: "call", name: "setNeverRemind" },
3221
3240
  { type: "close", reason: "user-dismiss" }
3222
3241
  ]
3223
3242
  }
@@ -3417,6 +3436,7 @@ function checklistSection() {
3417
3436
  \u6709\u4E00\u4E2A\u4E0D\u5408\u89C4\uFF0C\u6574\u4EFD\u914D\u7F6E\u5C31\u5B58\u4E0D\u4E0A
3418
3437
  - [ ] \u6BCF\u4E2A\u89C6\u56FE\u7684 \`stage\` \u90FD\u6709 \`onShow\` \u66DD\u5149\u57CB\u70B9\u548C \`onClose\` \u5173\u95ED\u57CB\u70B9\uFF0C\`onClose\` \u7684 params \u91CC\u5E26\u4E0A \`"reason": "{{ closeReason }}"\`
3419
3438
  - [ ] \u5173\u95ED\u6309\u94AE\u6CA1\u6709\u591A\u4F59\u7684 \`icon\`\uFF08\u9ED8\u8BA4\u7684\u5185\u7F6E\u77E2\u91CF\u56FE\u6807\u5C31\u662F\u5BF9\u7684\uFF09
3439
+ - [ ] \u6CA1\u6709\u7528 \`call\` \u53BB\u8BB0\u5F55\u7528\u6237\u610F\u56FE\u6216\u5199\u672C\u5730\u5B58\u50A8\uFF08\`call\` \u53EA\u7528\u6765\u8BA9\u5BBF\u4E3B\u505A\u4E00\u4EF6\u4E8B\uFF0C\u5982\u5524\u8D77\u5145\u503C / \u6253\u5F00\u5BA2\u670D / \u590D\u5236\u5230\u526A\u8D34\u677F\uFF09\u3002\u300C\u4E0D\u518D\u63D0\u9192\u300D\u5199\u6210 \`{ "type": "close", "reason": "user-dismiss" }\`\uFF0C\u4E0D\u914D \`call\`
3420
3440
  - [ ] \u8F93\u51FA\u7684\u662F\u5408\u6CD5 JSON\uFF08\u6CA1\u6709\u591A\u4F59\u9017\u53F7\u3001\u6CA1\u6709\u6CE8\u91CA\uFF09`;
3421
3441
  }
3422
3442
  function requirementSection() {
@@ -3454,7 +3474,8 @@ function actionSection() {
3454
3474
  - \`setState\`\uFF1A\`{ "type": "setState", "key": "period", "value": "{{ index }}" }\` \u505A\u9009\u4E2D\u5207\u6362
3455
3475
  - \`close\`\uFF1A\u5173\u6389\u5F53\u524D\u8FD9\u4E00\u5C42\uFF08\u53EA\u6709\u4E00\u5C42\u65F6\u5C31\u662F\u6574\u4F53\u5173\u95ED\uFF09\u3002\u6CE8\u610F\u5B83\u53EA\u662F**\u901A\u77E5\u5BBF\u4E3B\u9875\u9762**\u300C\u7528\u6237\u60F3\u5173\u4E86\u300D\uFF0C\u7531\u5BBF\u4E3B\u51B3\u5B9A\u5173\u4E0D\u5173\uFF1B\u591A\u89C6\u56FE\u65F6\u5173\u5185\u5C42\u4E0D\u4F1A\u901A\u77E5\u5BBF\u4E3B\uFF0C\u53EA\u662F\u56DE\u5230\u4E0B\u9762\u90A3\u5C42
3456
3476
  - \`closeAll\`\uFF1A\u51E0\u5C42\u90FD\u5173\u6389\uFF0C\u4E00\u5B9A\u4F1A\u901A\u77E5\u5BBF\u4E3B
3457
- - \u60F3\u300C\u5173\u4E86\u5C31\u522B\u518D\u51FA\u73B0\u300D\uFF0C\u5149 \`close\` \u4E0D\u591F\uFF0C\u8981\u518D\u914D\u4E00\u6761 \`{ "type": "call", "name": "setNeverRemind" }\` \u8BA9\u5BBF\u4E3B\u8BB0\u4E0B\u6765
3477
+ - \u60F3\u300C\u5173\u4E86\u5C31\u522B\u518D\u51FA\u73B0\u300D\uFF0C\u5C31\u5199 \`{ "type": "close", "reason": "user-dismiss" }\`\u3002\u4E0A\u62A5\u51FA\u53BB\u662F \`{ "event": "close", "reason": "user-dismiss" }\`\uFF0C\u540E\u7AEF\u636E\u6B64\u6C38\u4E45\u538B\u5236\u8FD9\u6761\u7269\u6599\u3002**\u4E0D\u8981**\u518D\u914D \`call\` \u8BA9\u5BBF\u4E3B\u5199\u672C\u5730\u5B58\u50A8\u2014\u2014\u9891\u63A7\u7531\u540E\u7AEF\u7B97\uFF0C\u524D\u7AEF\u5B58\u7684\u4F9D\u636E\u540E\u7AEF\u770B\u4E0D\u89C1\uFF0C\u800C\u4E14\u6362\u8BBE\u5907\u3001\u6E05\u7F13\u5B58\u5C31\u6CA1\u4E86
3478
+ - \`call\`\uFF1A**\u8BA9\u5BBF\u4E3B\u53BB\u505A\u4E00\u4EF6\u4E8B**\uFF08\u5524\u8D77\u5145\u503C\u5F39\u7A97\u3001\u6253\u5F00\u5BA2\u670D\u4F1A\u8BDD\u3001\u590D\u5236\u5230\u526A\u8D34\u677F\u3001\u8DF3\u8F6C\u5E94\u7528\u5185\u7279\u5B9A\u9875\u9762\uFF09\u3002\u5224\u65AD\u6807\u51C6\uFF1A**\u8FD9\u4EF6\u4E8B\u540E\u7AEF\u9700\u4E0D\u9700\u8981\u77E5\u9053**\u2014\u2014\u9700\u8981\u5C31\u8D70\u4E0A\u62A5\uFF08\`close\` \u7684 \`reason\` \u6216 \`track\`\uFF09\uFF0C\u7EAF\u672C\u5730\u52A8\u4F5C\u624D\u7528 \`call\`\u3002\u7EDD\u4E0D\u7528 \`call\` \u8BB0\u5F55\u7528\u6237\u610F\u56FE\u6216\u5199\u672C\u5730\u5B58\u50A8
3458
3479
  - \`open\`\uFF1A\u6253\u5F00\u53E6\u4E00\u4E2A\u89C6\u56FE\uFF0C\u89C1\u4E0B\u4E00\u8282
3459
3480
  - \u4EFB\u4F55\u8282\u70B9\u52A0 \`action\` \u90FD\u80FD\u70B9\u51FB
3460
3481
  - **\u6BCF\u4E2A\u53EF\u70B9\u51FB\u5143\u7D20\u90FD\u5E94\u8BE5\u914D \`track\`**\uFF0C\u5426\u5219\u6CA1\u6709\u6570\u636E\u53EF\u770B
@@ -3504,7 +3525,7 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6
3504
3525
 
3505
3526
  - \`onShow\` \u5728\u6570\u636E\u5C31\u7EEA\u3001\u5F39\u7A97\u771F\u6B63\u6E32\u67D3\u51FA\u6765\u65F6\u89E6\u53D1\u4E00\u6B21
3506
3527
  - \`onClose\` \u5728\u70B9 X\u3001\u70B9\u906E\u7F69\u3001\u8D70 \`close\` \u52A8\u4F5C\u3001\u88AB \`replace\` \u9876\u6389\u65F6\u90FD\u4F1A\u89E6\u53D1
3507
- - \`{{ closeReason }}\` \u53D6\u503C\uFF1A\`close-button\` / \`mask\` / \`replace\` / \`close\` \u52A8\u4F5C\u81EA\u5E26\u7684 \`reason\`\uFF08\u5982 \`"never-remind"\`\uFF09/ \u9ED8\u8BA4 \`action\`
3528
+ - \`{{ closeReason }}\` \u53D6\u503C\uFF1A\`close-button\` / \`mask\` / \`replace\` / \`close\` \u52A8\u4F5C\u81EA\u5E26\u7684 \`reason\`\uFF08\u5982 \`"user-dismiss"\`\uFF09/ \u9ED8\u8BA4 \`action\`
3508
3529
  - \u591A\u89C6\u56FE\u65F6\u6BCF\u5C42\u5404\u5199\u5404\u7684\uFF0C\u624D\u80FD\u5206\u5F00\u7EDF\u8BA1
3509
3530
 
3510
3531
  ## \u516C\u544A\u6761 / Banner \u7684\u5173\u95ED
@@ -3517,12 +3538,11 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6
3517
3538
  "textAlign": "center", "lineHeight": 22, "cursor": "pointer" },
3518
3539
  "action": { "type": "sequence", "actions": [
3519
3540
  { "type": "track", "event": "notice_guide_close" },
3520
- { "type": "call", "name": "setNeverRemind" },
3521
3541
  { "type": "close", "reason": "user-dismiss" }
3522
3542
  ] } }
3523
3543
  \`\`\`
3524
3544
 
3525
- \`close\` \u53EA\u901A\u77E5\u5BBF\u4E3B\u300C\u7528\u6237\u5173\u6389\u4E86\u300D\uFF0C\u8981\u8BA9\u5B83\u522B\u518D\u51FA\u73B0\u5F97\u914D\u4E00\u6761 \`call\`\u3002
3545
+ \`close\` \u53EA\u901A\u77E5\u5BBF\u4E3B\u300C\u8FD9\u4E00\u6B21\u5173\u4E0D\u5173\u300D\uFF1B\u300C\u4EE5\u540E\u8FD8\u51FA\u4E0D\u51FA\u300D\u7531\u540E\u7AEF\u6309\u4E0A\u62A5\u7684 \`reason\` \u51B3\u5B9A\uFF0C\u6240\u4EE5 \`reason: "user-dismiss"\` \u5C31\u591F\u4E86\uFF0C\u4E0D\u8981\u518D\u914D \`call\`\u3002
3526
3546
  \u6574\u6761\u53EF\u70B9 + \u884C\u672B \xD7 \u4E0D\u4F1A\u51B2\u7A81\uFF1A\u5185\u5C42\u6709\u81EA\u5DF1\u7684 \`action\` \u5C31\u4E0D\u518D\u5192\u6CE1\u7ED9\u5916\u5C42\u3002`;
3527
3547
  }
3528
3548
  function viewsSection() {
@@ -47,7 +47,11 @@ var actionRows = [
47
47
  ["closeAll", "\u2014", "\u4E0D\u7BA1\u51E0\u5C42\uFF0C\u5168\u90E8\u5173\u6389"],
48
48
  ["setState", "`key`\u3001`value`", "\u6539\u5C40\u90E8\u72B6\u6001\uFF0C\u7528\u6765\u505A\u9009\u4E2D\u5207\u6362"],
49
49
  ["track", "`event`\u3001`params`", "\u57CB\u70B9\u4E0A\u62A5\u3002`event` \u6709\u547D\u540D\u89C4\u8303\uFF0C\u89C1\u4E0B"],
50
- ["call", "`name`\u3001`params`", "\u8C03\u7528\u5BBF\u4E3B\u6CE8\u518C\u7684\u65B9\u6CD5\uFF08\u5982\u9886\u5238\uFF09\uFF0C`name` \u662F\u767D\u540D\u5355"],
50
+ [
51
+ "call",
52
+ "`name`\u3001`params`",
53
+ "\u8BA9\u5BBF\u4E3B\u53BB\u505A\u4E00\u4EF6\u4E8B\uFF08\u5982\u5524\u8D77\u5145\u503C\uFF09\uFF0C`name` \u662F\u767D\u540D\u5355\uFF0C\u8FB9\u754C\u89C1\u4E0B"
54
+ ],
51
55
  ["sequence", "`actions`", "\u4F9D\u6B21\u6267\u884C\u591A\u4E2A\u52A8\u4F5C"]
52
56
  ];
53
57
  var sections = [
@@ -160,12 +164,45 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6`
160
164
  {
161
165
  t: "note",
162
166
  tone: "info",
163
- text: '`close` \u7684 `reason` \u4F1A\u4E00\u8D77\u4F20\u7ED9\u5BBF\u4E3B\uFF0C\u5BBF\u4E3B\u636E\u6B64\u51B3\u5B9A\u540E\u7EED\u884C\u4E3A\u2014\u2014\u6BD4\u5982 `reason: "user-dismiss"` \u65F6\u8BB0\u4F4F\u300C\u4ECA\u5929\u4E0D\u518D\u663E\u793A\u300D\u3002\u4E0D\u5199 `reason` \u9ED8\u8BA4\u662F `action`\u3002'
167
+ text: "`close` \u7684 `reason` \u4F1A\u4E00\u8D77\u4F20\u7ED9\u5BBF\u4E3B\uFF0C\u5BBF\u4E3B\u636E\u6B64\u51B3\u5B9A\u540E\u7EED\u884C\u4E3A\uFF0C\u5E76\u539F\u6837\u4E0A\u62A5\u7ED9\u540E\u7AEF\u3002\u4E0D\u5199 `reason` \u9ED8\u8BA4\u662F `action`\u3002"
168
+ },
169
+ { t: "h3", text: "\u300C\u4E0D\u518D\u63D0\u9192\u300D\u5C31\u5199 close \u7684 reason\uFF0C\u4E0D\u8981\u914D call" },
170
+ {
171
+ t: "p",
172
+ text: "\u60F3\u8BA9\u7269\u6599\u300C\u5173\u4E86\u5C31\u522B\u518D\u51FA\u73B0\u300D\uFF0C\u4E00\u6761 `close` \u5C31\u591F\u4E86\uFF0C\u628A\u7528\u6237\u7684\u610F\u56FE\u5199\u8FDB `reason`\uFF1A"
173
+ },
174
+ {
175
+ t: "code",
176
+ code: `{ "type": "close", "reason": "user-dismiss" }`
177
+ },
178
+ {
179
+ t: "p",
180
+ text: '\u4E0A\u62A5\u51FA\u53BB\u5C31\u662F `{ "id": "...", "event": "close", "reason": "user-dismiss" }`\uFF0C\u540E\u7AEF\u770B\u5230\u8FD9\u4E2A `reason` \u5C31\u6C38\u4E45\u538B\u5236\u8FD9\u6761\u7269\u6599\u3002\u8FD9\u4E2A\u4FE1\u606F\u8D70 `close` \u7684 `reason`\uFF0C\u4E0D\u8D70 `extra`\u2014\u2014`extra` \u53EA\u5C5E\u4E8E `click`\u3002'
164
181
  },
165
182
  {
166
183
  t: "note",
167
184
  tone: "warn",
168
- text: "\u60F3\u8BA9\u5F39\u7A97\u300C\u5173\u4E86\u5C31\u522B\u518D\u51FA\u73B0\u300D\uFF0C\u5149\u9760 `close` \u4E0D\u591F\u2014\u2014\u5B83\u53EA\u7BA1\u8FD9\u4E00\u6B21\u3002\u8981\u914D\u4E00\u6761 `call` \u8BA9\u5BBF\u4E3B\u8BB0\u4E0B\u6765\uFF08\u89C1\u793A\u4F8B \u2461 \u548C \u2464 \u7684\u300C\u4E0D\u518D\u63D0\u9192\u300D\uFF09\u3002"
185
+ text: "**\u4E0D\u8981**\u518D\u914D\u4E00\u6761 `call` \u8BA9\u5BBF\u4E3B\u5199 `localStorage` \u8BB0\u4F4F\u3002\u9891\u63A7\u7531\u540E\u7AEF\u7B97\uFF0C\u524D\u7AEF\u5B58\u4E00\u4EFD\u540E\u7AEF\u770B\u4E0D\u89C1\u7684\u4F9D\u636E\uFF0C\u4E24\u8FB9\u5C31\u5BF9\u4E0D\u4E0A\uFF1B\u800C\u4E14\u6362\u8BBE\u5907\u3001\u6E05\u7F13\u5B58\u5C31\u5931\u6548\u2014\u2014\u7528\u6237\u7684\u957F\u671F\u610F\u56FE\u8BE5\u8DDF\u7740\u8D26\u53F7\u8D70\uFF0C\u4E0D\u8BE5\u8DDF\u7740\u6D4F\u89C8\u5668\u8D70\uFF1B`call` \u7684\u65B9\u6CD5\u8FD8\u662F\u5404\u4E2A\u5BBF\u4E3B\u5404\u6CE8\u518C\u4E00\u4EFD\u7684\uFF0C\u884C\u4E3A\u8FDF\u65E9\u5206\u53C9\u3002"
186
+ },
187
+ { t: "h3", text: "call \u7684\u9002\u7528\u8FB9\u754C" },
188
+ {
189
+ t: "p",
190
+ text: "`call` \u662F\u300C**\u8BA9\u5BBF\u4E3B\u53BB\u505A\u4E00\u4EF6\u4E8B**\u300D\uFF0C\u4E0D\u662F\u300C\u8BB0\u4E00\u7B14\u8D26\u300D\u3002"
191
+ },
192
+ {
193
+ t: "table",
194
+ head: ["\u2705 \u8BE5\u7528 call", "\u274C \u4E0D\u8BE5\u7528 call"],
195
+ rows: [
196
+ [
197
+ "\u5524\u8D77\u5145\u503C\u5F39\u7A97\u3001\u6253\u5F00\u5BA2\u670D\u4F1A\u8BDD\u3001\u590D\u5236\u5230\u526A\u8D34\u677F\u3001\u8DF3\u8F6C\u5E94\u7528\u5185\u7279\u5B9A\u9875\u9762",
198
+ "\u8BB0\u5F55\u7528\u6237\u610F\u56FE\u3001\u5199\u672C\u5730\u5B58\u50A8"
199
+ ]
200
+ ]
201
+ },
202
+ {
203
+ t: "note",
204
+ tone: "info",
205
+ text: "\u5224\u65AD\u6807\u51C6\u53EA\u6709\u4E00\u6761\uFF1A**\u8FD9\u4EF6\u4E8B\u540E\u7AEF\u9700\u4E0D\u9700\u8981\u77E5\u9053**\u3002\u9700\u8981 \u2192 \u8D70\u4E0A\u62A5\uFF08`close` \u7684 `reason`\uFF0C\u6216 `track`\uFF09\uFF1B\u7EAF\u7CB9\u662F\u5BBF\u4E3B\u7684\u672C\u5730\u52A8\u4F5C\u3001\u540E\u7AEF\u4E0D\u5173\u5FC3 \u2192 \u624D\u7528 `call`\u3002"
169
206
  },
170
207
  { t: "h3", text: "\u66DD\u5149\u548C\u5173\u95ED\u4E5F\u8981\u57CB\u70B9\uFF1Astage.onShow / stage.onClose" },
171
208
  {
@@ -192,7 +229,7 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6`
192
229
  ["`close-button`", "\u70B9\u4E86\u5173\u95ED\u6309\u94AE"],
193
230
  ["`mask`", "\u70B9\u4E86\u906E\u7F69\uFF08\u9700\u8981 `maskClosable: true`\uFF09"],
194
231
  ["`replace`", "\u88AB `open` \u7684 `replace` \u6A21\u5F0F\u9876\u6389"],
195
- ["\u81EA\u5B9A\u4E49\u503C", '`close` \u52A8\u4F5C\u91CC\u81EA\u5DF1\u5199\u7684 `reason`\uFF0C\u5982 `"never-remind"`'],
232
+ ["\u81EA\u5B9A\u4E49\u503C", '`close` \u52A8\u4F5C\u91CC\u81EA\u5DF1\u5199\u7684 `reason`\uFF0C\u5982 `"user-dismiss"`'],
196
233
  ["`action`", "`close` \u52A8\u4F5C\u6CA1\u5199 `reason` \u65F6\u7684\u9ED8\u8BA4\u503C"]
197
234
  ]
198
235
  },
@@ -213,14 +250,13 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6`
213
250
  "textAlign": "center", "lineHeight": 22, "cursor": "pointer" },
214
251
  "action": { "type": "sequence", "actions": [
215
252
  { "type": "track", "event": "notice_close" },
216
- { "type": "call", "name": "setNeverRemind" }, // \u8BB0\u4F4F\u522B\u518D\u51FA
217
- { "type": "close", "reason": "user-dismiss" }
253
+ { "type": "close", "reason": "user-dismiss" } // reason \u4E0A\u62A5\u7ED9\u540E\u7AEF\uFF0C\u636E\u6B64\u4E0D\u518D\u4E0B\u53D1
218
254
  ] } }`
219
255
  },
220
256
  {
221
257
  t: "note",
222
258
  tone: "info",
223
- text: "`close` \u53EA\u662F\u901A\u77E5\u5BBF\u4E3B\u300C\u7528\u6237\u5173\u6389\u4E86\u300D\uFF0C**\u5173\u6389\u4E4B\u540E\u8981\u4E0D\u8981\u518D\u51FA\u3001\u9694\u591A\u4E45\u518D\u51FA\u7531\u5BBF\u4E3B\u51B3\u5B9A**\u3002\u60F3\u8BA9\u5B83\u522B\u518D\u51FA\u73B0\uFF0C\u914D\u4E00\u6761 `call` \u8BA9\u5BBF\u4E3B\u8BB0\u4E0B\u6765\u3002"
259
+ text: '`close` \u53EA\u662F\u901A\u77E5\u5BBF\u4E3B\u300C\u7528\u6237\u5173\u6389\u4E86\u300D\uFF0C**\u8FD9\u4E00\u6B21\u5173\u4E0D\u5173\u7531\u5BBF\u4E3B\u51B3\u5B9A**\uFF1B\u800C\u300C\u4EE5\u540E\u8FD8\u51FA\u4E0D\u51FA\u300D\u7531\u540E\u7AEF\u6839\u636E\u4E0A\u62A5\u7684 `reason` \u51B3\u5B9A\uFF0C\u6240\u4EE5\u60F3\u8BA9\u5B83\u522B\u518D\u51FA\u73B0\uFF0C\u5199 `reason: "user-dismiss"` \u5373\u53EF\uFF0C\u4E0D\u9700\u8981\u518D\u914D `call`\u3002'
224
260
  },
225
261
  {
226
262
  t: "note",
@@ -2204,13 +2240,7 @@ var noticeToPopup = {
2204
2240
  fontSize: 13,
2205
2241
  cursor: "pointer"
2206
2242
  },
2207
- action: {
2208
- type: "sequence",
2209
- actions: [
2210
- { type: "call", name: "setNeverRemind" },
2211
- { type: "close", reason: "user-dismiss" }
2212
- ]
2213
- }
2243
+ action: { type: "close", reason: "user-dismiss" }
2214
2244
  }
2215
2245
  ]
2216
2246
  }
@@ -2576,13 +2606,13 @@ var confirmAndSwitch = {
2576
2606
  background: ORANGE3,
2577
2607
  cursor: "pointer"
2578
2608
  },
2579
- // 确认后整个关掉,两层一起消失
2609
+ // 确认后整个关掉,两层一起消失。
2610
+ // 「不再提醒」的意图靠 reason 上报给后端,不配 call 写本地存储
2580
2611
  action: {
2581
2612
  type: "sequence",
2582
2613
  actions: [
2583
2614
  { type: "track", event: "popup_never_remind_confirm" },
2584
- { type: "call", name: "setNeverRemind" },
2585
- { type: "closeAll" }
2615
+ { type: "closeAll", reason: "user-dismiss" }
2586
2616
  ]
2587
2617
  },
2588
2618
  children: [
@@ -2896,13 +2926,8 @@ var imageBanner = {
2896
2926
  fontSize: 13,
2897
2927
  cursor: "pointer"
2898
2928
  },
2899
- action: {
2900
- type: "sequence",
2901
- actions: [
2902
- { type: "call", name: "setNeverRemind" },
2903
- { type: "close", reason: "user-dismiss" }
2904
- ]
2905
- }
2929
+ // 「不再提醒」写在 close 的 reason 上,上报给后端即可,不配 call 写本地存储
2930
+ action: { type: "close", reason: "user-dismiss" }
2906
2931
  }
2907
2932
  ]
2908
2933
  }
@@ -3033,16 +3058,11 @@ var imageHotspot = {
3033
3058
  {
3034
3059
  id: "hotspot-never",
3035
3060
  type: "box",
3036
- // 最底部「不再提醒」,动作是关弹窗而不是跳转
3061
+ // 最底部「不再提醒」,动作是关弹窗而不是跳转。
3062
+ // reason 上报给后端,后端据此不再下发这条物料——不需要写本地存储
3037
3063
  rect: ["30%", 478, "40%", 26],
3038
3064
  style: { radius: 13, background: HOTSPOT_BG, cursor: "pointer" },
3039
- action: {
3040
- type: "sequence",
3041
- actions: [
3042
- { type: "call", name: "setNeverRemind" },
3043
- { type: "close", reason: "never-remind" }
3044
- ]
3045
- }
3065
+ action: { type: "close", reason: "user-dismiss" }
3046
3066
  }
3047
3067
  ]
3048
3068
  };
@@ -3120,7 +3140,6 @@ var noticeBar = {
3120
3140
  type: "sequence",
3121
3141
  actions: [
3122
3142
  { type: "track", event: "notice_close", params: { notice: "guide" } },
3123
- { type: "call", name: "setNeverRemind" },
3124
3143
  { type: "close", reason: "user-dismiss" }
3125
3144
  ]
3126
3145
  }
@@ -3320,6 +3339,7 @@ function checklistSection() {
3320
3339
  \u6709\u4E00\u4E2A\u4E0D\u5408\u89C4\uFF0C\u6574\u4EFD\u914D\u7F6E\u5C31\u5B58\u4E0D\u4E0A
3321
3340
  - [ ] \u6BCF\u4E2A\u89C6\u56FE\u7684 \`stage\` \u90FD\u6709 \`onShow\` \u66DD\u5149\u57CB\u70B9\u548C \`onClose\` \u5173\u95ED\u57CB\u70B9\uFF0C\`onClose\` \u7684 params \u91CC\u5E26\u4E0A \`"reason": "{{ closeReason }}"\`
3322
3341
  - [ ] \u5173\u95ED\u6309\u94AE\u6CA1\u6709\u591A\u4F59\u7684 \`icon\`\uFF08\u9ED8\u8BA4\u7684\u5185\u7F6E\u77E2\u91CF\u56FE\u6807\u5C31\u662F\u5BF9\u7684\uFF09
3342
+ - [ ] \u6CA1\u6709\u7528 \`call\` \u53BB\u8BB0\u5F55\u7528\u6237\u610F\u56FE\u6216\u5199\u672C\u5730\u5B58\u50A8\uFF08\`call\` \u53EA\u7528\u6765\u8BA9\u5BBF\u4E3B\u505A\u4E00\u4EF6\u4E8B\uFF0C\u5982\u5524\u8D77\u5145\u503C / \u6253\u5F00\u5BA2\u670D / \u590D\u5236\u5230\u526A\u8D34\u677F\uFF09\u3002\u300C\u4E0D\u518D\u63D0\u9192\u300D\u5199\u6210 \`{ "type": "close", "reason": "user-dismiss" }\`\uFF0C\u4E0D\u914D \`call\`
3323
3343
  - [ ] \u8F93\u51FA\u7684\u662F\u5408\u6CD5 JSON\uFF08\u6CA1\u6709\u591A\u4F59\u9017\u53F7\u3001\u6CA1\u6709\u6CE8\u91CA\uFF09`;
3324
3344
  }
3325
3345
  function requirementSection() {
@@ -3357,7 +3377,8 @@ function actionSection() {
3357
3377
  - \`setState\`\uFF1A\`{ "type": "setState", "key": "period", "value": "{{ index }}" }\` \u505A\u9009\u4E2D\u5207\u6362
3358
3378
  - \`close\`\uFF1A\u5173\u6389\u5F53\u524D\u8FD9\u4E00\u5C42\uFF08\u53EA\u6709\u4E00\u5C42\u65F6\u5C31\u662F\u6574\u4F53\u5173\u95ED\uFF09\u3002\u6CE8\u610F\u5B83\u53EA\u662F**\u901A\u77E5\u5BBF\u4E3B\u9875\u9762**\u300C\u7528\u6237\u60F3\u5173\u4E86\u300D\uFF0C\u7531\u5BBF\u4E3B\u51B3\u5B9A\u5173\u4E0D\u5173\uFF1B\u591A\u89C6\u56FE\u65F6\u5173\u5185\u5C42\u4E0D\u4F1A\u901A\u77E5\u5BBF\u4E3B\uFF0C\u53EA\u662F\u56DE\u5230\u4E0B\u9762\u90A3\u5C42
3359
3379
  - \`closeAll\`\uFF1A\u51E0\u5C42\u90FD\u5173\u6389\uFF0C\u4E00\u5B9A\u4F1A\u901A\u77E5\u5BBF\u4E3B
3360
- - \u60F3\u300C\u5173\u4E86\u5C31\u522B\u518D\u51FA\u73B0\u300D\uFF0C\u5149 \`close\` \u4E0D\u591F\uFF0C\u8981\u518D\u914D\u4E00\u6761 \`{ "type": "call", "name": "setNeverRemind" }\` \u8BA9\u5BBF\u4E3B\u8BB0\u4E0B\u6765
3380
+ - \u60F3\u300C\u5173\u4E86\u5C31\u522B\u518D\u51FA\u73B0\u300D\uFF0C\u5C31\u5199 \`{ "type": "close", "reason": "user-dismiss" }\`\u3002\u4E0A\u62A5\u51FA\u53BB\u662F \`{ "event": "close", "reason": "user-dismiss" }\`\uFF0C\u540E\u7AEF\u636E\u6B64\u6C38\u4E45\u538B\u5236\u8FD9\u6761\u7269\u6599\u3002**\u4E0D\u8981**\u518D\u914D \`call\` \u8BA9\u5BBF\u4E3B\u5199\u672C\u5730\u5B58\u50A8\u2014\u2014\u9891\u63A7\u7531\u540E\u7AEF\u7B97\uFF0C\u524D\u7AEF\u5B58\u7684\u4F9D\u636E\u540E\u7AEF\u770B\u4E0D\u89C1\uFF0C\u800C\u4E14\u6362\u8BBE\u5907\u3001\u6E05\u7F13\u5B58\u5C31\u6CA1\u4E86
3381
+ - \`call\`\uFF1A**\u8BA9\u5BBF\u4E3B\u53BB\u505A\u4E00\u4EF6\u4E8B**\uFF08\u5524\u8D77\u5145\u503C\u5F39\u7A97\u3001\u6253\u5F00\u5BA2\u670D\u4F1A\u8BDD\u3001\u590D\u5236\u5230\u526A\u8D34\u677F\u3001\u8DF3\u8F6C\u5E94\u7528\u5185\u7279\u5B9A\u9875\u9762\uFF09\u3002\u5224\u65AD\u6807\u51C6\uFF1A**\u8FD9\u4EF6\u4E8B\u540E\u7AEF\u9700\u4E0D\u9700\u8981\u77E5\u9053**\u2014\u2014\u9700\u8981\u5C31\u8D70\u4E0A\u62A5\uFF08\`close\` \u7684 \`reason\` \u6216 \`track\`\uFF09\uFF0C\u7EAF\u672C\u5730\u52A8\u4F5C\u624D\u7528 \`call\`\u3002\u7EDD\u4E0D\u7528 \`call\` \u8BB0\u5F55\u7528\u6237\u610F\u56FE\u6216\u5199\u672C\u5730\u5B58\u50A8
3361
3382
  - \`open\`\uFF1A\u6253\u5F00\u53E6\u4E00\u4E2A\u89C6\u56FE\uFF0C\u89C1\u4E0B\u4E00\u8282
3362
3383
  - \u4EFB\u4F55\u8282\u70B9\u52A0 \`action\` \u90FD\u80FD\u70B9\u51FB
3363
3384
  - **\u6BCF\u4E2A\u53EF\u70B9\u51FB\u5143\u7D20\u90FD\u5E94\u8BE5\u914D \`track\`**\uFF0C\u5426\u5219\u6CA1\u6709\u6570\u636E\u53EF\u770B
@@ -3407,7 +3428,7 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6
3407
3428
 
3408
3429
  - \`onShow\` \u5728\u6570\u636E\u5C31\u7EEA\u3001\u5F39\u7A97\u771F\u6B63\u6E32\u67D3\u51FA\u6765\u65F6\u89E6\u53D1\u4E00\u6B21
3409
3430
  - \`onClose\` \u5728\u70B9 X\u3001\u70B9\u906E\u7F69\u3001\u8D70 \`close\` \u52A8\u4F5C\u3001\u88AB \`replace\` \u9876\u6389\u65F6\u90FD\u4F1A\u89E6\u53D1
3410
- - \`{{ closeReason }}\` \u53D6\u503C\uFF1A\`close-button\` / \`mask\` / \`replace\` / \`close\` \u52A8\u4F5C\u81EA\u5E26\u7684 \`reason\`\uFF08\u5982 \`"never-remind"\`\uFF09/ \u9ED8\u8BA4 \`action\`
3431
+ - \`{{ closeReason }}\` \u53D6\u503C\uFF1A\`close-button\` / \`mask\` / \`replace\` / \`close\` \u52A8\u4F5C\u81EA\u5E26\u7684 \`reason\`\uFF08\u5982 \`"user-dismiss"\`\uFF09/ \u9ED8\u8BA4 \`action\`
3411
3432
  - \u591A\u89C6\u56FE\u65F6\u6BCF\u5C42\u5404\u5199\u5404\u7684\uFF0C\u624D\u80FD\u5206\u5F00\u7EDF\u8BA1
3412
3433
 
3413
3434
  ## \u516C\u544A\u6761 / Banner \u7684\u5173\u95ED
@@ -3420,12 +3441,11 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6
3420
3441
  "textAlign": "center", "lineHeight": 22, "cursor": "pointer" },
3421
3442
  "action": { "type": "sequence", "actions": [
3422
3443
  { "type": "track", "event": "notice_guide_close" },
3423
- { "type": "call", "name": "setNeverRemind" },
3424
3444
  { "type": "close", "reason": "user-dismiss" }
3425
3445
  ] } }
3426
3446
  \`\`\`
3427
3447
 
3428
- \`close\` \u53EA\u901A\u77E5\u5BBF\u4E3B\u300C\u7528\u6237\u5173\u6389\u4E86\u300D\uFF0C\u8981\u8BA9\u5B83\u522B\u518D\u51FA\u73B0\u5F97\u914D\u4E00\u6761 \`call\`\u3002
3448
+ \`close\` \u53EA\u901A\u77E5\u5BBF\u4E3B\u300C\u8FD9\u4E00\u6B21\u5173\u4E0D\u5173\u300D\uFF1B\u300C\u4EE5\u540E\u8FD8\u51FA\u4E0D\u51FA\u300D\u7531\u540E\u7AEF\u6309\u4E0A\u62A5\u7684 \`reason\` \u51B3\u5B9A\uFF0C\u6240\u4EE5 \`reason: "user-dismiss"\` \u5C31\u591F\u4E86\uFF0C\u4E0D\u8981\u518D\u914D \`call\`\u3002
3429
3449
  \u6574\u6761\u53EF\u70B9 + \u884C\u672B \xD7 \u4E0D\u4F1A\u51B2\u7A81\uFF1A\u5185\u5C42\u6709\u81EA\u5DF1\u7684 \`action\` \u5C31\u4E0D\u518D\u5192\u6CE1\u7ED9\u5916\u5C42\u3002`;
3430
3450
  }
3431
3451
  function viewsSection() {
package/dist/index.cjs CHANGED
@@ -1173,7 +1173,7 @@ function createRuntime(dsl, options = {}) {
1173
1173
  setState,
1174
1174
  emit,
1175
1175
  openView,
1176
- // 配置里 close 可以自带 reason(如 'never-remind'),要透传给埋点,不能吞掉
1176
+ // 配置里 close 可以自带 reason(如 'user-dismiss'),要透传给埋点,不能吞掉
1177
1177
  closeTop: (reason) => closeTop(reason),
1178
1178
  closeAll: (reason) => closeAll(reason),
1179
1179
  get handlers() {
package/dist/index.js CHANGED
@@ -645,7 +645,7 @@ function createRuntime(dsl, options = {}) {
645
645
  setState,
646
646
  emit,
647
647
  openView,
648
- // 配置里 close 可以自带 reason(如 'never-remind'),要透传给埋点,不能吞掉
648
+ // 配置里 close 可以自带 reason(如 'user-dismiss'),要透传给埋点,不能吞掉
649
649
  closeTop: (reason) => closeTop(reason),
650
650
  closeAll: (reason) => closeAll(reason),
651
651
  get handlers() {
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ // src/report/map.ts
4
+ function buildExposureReport(id) {
5
+ if (!id) return null;
6
+ return { id, event: "exposure" };
7
+ }
8
+ function mapRuntimeEventToReport(id, event, payload) {
9
+ if (!id) return null;
10
+ switch (event) {
11
+ case "track": {
12
+ const data = payload != null ? payload : {};
13
+ return {
14
+ id,
15
+ event: "click",
16
+ extra: { trigger: "track", name: data.event, params: data.params }
17
+ };
18
+ }
19
+ case "navigate": {
20
+ const data = payload != null ? payload : {};
21
+ return {
22
+ id,
23
+ event: "click",
24
+ extra: { trigger: "navigate", url: data.url }
25
+ };
26
+ }
27
+ case "close": {
28
+ const data = payload != null ? payload : {};
29
+ return {
30
+ id,
31
+ event: "close",
32
+ reason: data.reason
33
+ };
34
+ }
35
+ default:
36
+ return null;
37
+ }
38
+ }
39
+
40
+ exports.buildExposureReport = buildExposureReport;
41
+ exports.mapRuntimeEventToReport = mapRuntimeEventToReport;
@@ -0,0 +1,150 @@
1
+ import { E as RuntimeEventName } from '../types-BgUaJUAu.cjs';
2
+
3
+ /**
4
+ * 营销物料上报载荷类型(上报契约的唯一数据源)。
5
+ *
6
+ * 契约正文见仓库 `docs/marketing/README.md` 的「接口契约 · 上报事件」一节,
7
+ * 本文件是它的类型化表达,前台(platform-adapters / ui-vue2)与后台调试台共用同一份。
8
+ *
9
+ * 【为什么在 core 包里】调试台不装 platform-adapters,两边唯一的共同载体就是这个已发布的 npm 包。
10
+ * 但它是**可选的附加约定**,走子路径 `@yaoxiu/marketing-dsl/report` 独立入口,
11
+ * 主入口不 re-export —— 主入口的身份仍然只是「DSL 解释器」,不该知道上报契约的存在。
12
+ */
13
+ /**
14
+ * 上报事件类型:曝光 / 点击 / 关闭
15
+ *
16
+ * 这是**物料生命周期**的固定三值枚举,由前端写死,不受运营配置影响:
17
+ * - `exposure` 曝光:物料渲染到页面时,每条一次
18
+ * - `click` 点击:用户点了物料里任何带动作的元素
19
+ * - `close` 关闭:用户关掉了这条物料
20
+ */
21
+ type MarketingReportEvent = 'exposure' | 'click' | 'close';
22
+ /**
23
+ * 点击上报的附加信息(**只在 `event='click'` 时有值**)
24
+ *
25
+ * 运营在 DSL 里自定义的埋点名 / 参数一律放这里,
26
+ * **不要塞进 `reason`** —— `reason` 的语义被「关闭原因」独占。
27
+ */
28
+ interface MarketingClickExtra {
29
+ /** 点击来源:`track` = 运营配的埋点动作;`navigate` = 跳转动作 */
30
+ trigger: 'track' | 'navigate';
31
+ /** `trigger='track'` 时:运营在配置里写的埋点名 */
32
+ name?: string;
33
+ /** `trigger='navigate'` 时:跳转目标地址 */
34
+ url?: string;
35
+ /** 运营在配置里写的自定义参数,原样透传 */
36
+ params?: Record<string, unknown>;
37
+ }
38
+ /** 曝光上报入参:只有 id */
39
+ interface MarketingExposureReportParams {
40
+ /** 物料 id,取自 `MarketingMaterial.id` */
41
+ id: string;
42
+ /** 事件类型 */
43
+ event: 'exposure';
44
+ /** 曝光事件无关闭原因 */
45
+ reason?: never;
46
+ /** 曝光事件无点击附加信息 */
47
+ extra?: never;
48
+ }
49
+ /** 关闭上报入参:可带关闭原因 */
50
+ interface MarketingCloseReportParams {
51
+ /** 物料 id */
52
+ id: string;
53
+ /** 事件类型 */
54
+ event: 'close';
55
+ /**
56
+ * 关闭原因,取自解释器抛出的 close reason:
57
+ * `close-button`(点 ×)/ `mask`(点遮罩)/ `countdown-end`(倒计时归零自动关)/
58
+ * `action`(配置里 close 动作未写 reason 的默认值)/ 运营自定义值。
59
+ */
60
+ reason?: string;
61
+ /** 关闭事件无点击附加信息 */
62
+ extra?: never;
63
+ }
64
+ /** 点击上报入参:必须带 extra 说明点了什么 */
65
+ interface MarketingClickReportParams {
66
+ /** 物料 id */
67
+ id: string;
68
+ /** 事件类型 */
69
+ event: 'click';
70
+ /** 点击事件不写关闭原因 */
71
+ reason?: never;
72
+ /** 点击详情,见 {@link MarketingClickExtra} */
73
+ extra: MarketingClickExtra;
74
+ }
75
+ /**
76
+ * 上报物料事件的入参(可辨识联合,按 `event` 区分)
77
+ *
78
+ * 用联合而非「三个可选字段」是为了把契约写进类型里:`reason` 只配 `close`、
79
+ * `extra` 只配 `click`,写错直接编译不过。不该出现的字段声明成 `?: never`
80
+ * 而不是省略,这样消费方(formatter / 上报层 / 调试台预览)仍能直接读
81
+ * `params.reason`、`params.extra`,无需先做类型窄化 —— 约束留在调用侧,消费侧照旧好写。
82
+ */
83
+ type MarketingReportParams = MarketingExposureReportParams | MarketingCloseReportParams | MarketingClickReportParams;
84
+
85
+ /**
86
+ * 「DSL 运行时事件 → 上报载荷」的映射(纯函数,无副作用、无框架依赖)。
87
+ *
88
+ * 前台坑位组件与后台调试台的「上报预览」共用这一份:
89
+ * 调试台要展示的就是**真实会发给后端的 JSON**,映射有第二份实现,
90
+ * 前台改了口径而调试台忘了改,调试台就会骗人。
91
+ */
92
+
93
+ /**
94
+ * 会产生上报的运行时事件及其载荷。
95
+ *
96
+ * 字段一律可选:宿主(Vue 组件的 `$event`、调试台的手工构造)拿到的载荷未必齐全,
97
+ * 缺字段是允许的,不该在映射这一步炸掉。
98
+ */
99
+ interface MarketingReportableEventPayloads {
100
+ /** 埋点动作:`event` 是运营配的埋点名,`params` 是自定义参数 */
101
+ track: {
102
+ event?: string;
103
+ params?: Record<string, unknown>;
104
+ };
105
+ /** 跳转动作:`url` 是目标地址(跳转本身由渲染壳完成,这里只记一笔) */
106
+ navigate: {
107
+ url?: string;
108
+ target?: string;
109
+ };
110
+ /** 关闭:`reason` 是关闭原因 */
111
+ close: {
112
+ reason?: string;
113
+ };
114
+ }
115
+ /** 会产生上报的运行时事件名 */
116
+ type MarketingReportableEventName = keyof MarketingReportableEventPayloads;
117
+ /**
118
+ * 事件名 → 载荷类型。
119
+ *
120
+ * 不产生上报的事件(`call` / `error` / `state-change` / `view-change` / `ready` / `open`)
121
+ * 载荷为 `unknown`:调用方照常传,映射返回 `null`。
122
+ */
123
+ type MarketingRuntimeEventPayload<K extends RuntimeEventName> = K extends MarketingReportableEventName ? MarketingReportableEventPayloads[K] : unknown;
124
+ /**
125
+ * 曝光上报载荷。
126
+ *
127
+ * 单独一个函数是因为曝光**不由运行时事件触发** —— 它是宿主把物料渲染到页面上时主动发的,
128
+ * 解释器根本不知道自己被画出来了。
129
+ *
130
+ * @param id 物料 id,为空(未取到物料)时返回 `null`,不产生上报
131
+ * @returns 上报载荷,或 `null`
132
+ */
133
+ declare function buildExposureReport(id: string | undefined | null): MarketingExposureReportParams | null;
134
+ /**
135
+ * 把一个 DSL 运行时事件映射成上报载荷。
136
+ *
137
+ * 口径(与 `docs/marketing/README.md` 的契约一一对应):
138
+ * - `track` → `click`,`extra = { trigger: 'track', name, params }`
139
+ * - `navigate` → `click`,`extra = { trigger: 'navigate', url }`
140
+ * - `close` → `close`,带 `reason`
141
+ * - 其余事件(`call` / `error` / `state-change` / `view-change` / `ready` / `open`)不产生上报
142
+ *
143
+ * @param id 物料 id,为空时返回 `null`(拿不到 id 的上报对后端没有意义)
144
+ * @param event 运行时事件名
145
+ * @param payload 事件载荷,可缺省
146
+ * @returns 上报载荷,或 `null`(该事件不产生上报)
147
+ */
148
+ declare function mapRuntimeEventToReport<K extends RuntimeEventName>(id: string | undefined | null, event: K, payload?: MarketingRuntimeEventPayload<K>): MarketingReportParams | null;
149
+
150
+ export { type MarketingClickExtra, type MarketingClickReportParams, type MarketingCloseReportParams, type MarketingExposureReportParams, type MarketingReportEvent, type MarketingReportParams, type MarketingReportableEventName, type MarketingReportableEventPayloads, type MarketingRuntimeEventPayload, buildExposureReport, mapRuntimeEventToReport };
@@ -0,0 +1,150 @@
1
+ import { E as RuntimeEventName } from '../types-BgUaJUAu.js';
2
+
3
+ /**
4
+ * 营销物料上报载荷类型(上报契约的唯一数据源)。
5
+ *
6
+ * 契约正文见仓库 `docs/marketing/README.md` 的「接口契约 · 上报事件」一节,
7
+ * 本文件是它的类型化表达,前台(platform-adapters / ui-vue2)与后台调试台共用同一份。
8
+ *
9
+ * 【为什么在 core 包里】调试台不装 platform-adapters,两边唯一的共同载体就是这个已发布的 npm 包。
10
+ * 但它是**可选的附加约定**,走子路径 `@yaoxiu/marketing-dsl/report` 独立入口,
11
+ * 主入口不 re-export —— 主入口的身份仍然只是「DSL 解释器」,不该知道上报契约的存在。
12
+ */
13
+ /**
14
+ * 上报事件类型:曝光 / 点击 / 关闭
15
+ *
16
+ * 这是**物料生命周期**的固定三值枚举,由前端写死,不受运营配置影响:
17
+ * - `exposure` 曝光:物料渲染到页面时,每条一次
18
+ * - `click` 点击:用户点了物料里任何带动作的元素
19
+ * - `close` 关闭:用户关掉了这条物料
20
+ */
21
+ type MarketingReportEvent = 'exposure' | 'click' | 'close';
22
+ /**
23
+ * 点击上报的附加信息(**只在 `event='click'` 时有值**)
24
+ *
25
+ * 运营在 DSL 里自定义的埋点名 / 参数一律放这里,
26
+ * **不要塞进 `reason`** —— `reason` 的语义被「关闭原因」独占。
27
+ */
28
+ interface MarketingClickExtra {
29
+ /** 点击来源:`track` = 运营配的埋点动作;`navigate` = 跳转动作 */
30
+ trigger: 'track' | 'navigate';
31
+ /** `trigger='track'` 时:运营在配置里写的埋点名 */
32
+ name?: string;
33
+ /** `trigger='navigate'` 时:跳转目标地址 */
34
+ url?: string;
35
+ /** 运营在配置里写的自定义参数,原样透传 */
36
+ params?: Record<string, unknown>;
37
+ }
38
+ /** 曝光上报入参:只有 id */
39
+ interface MarketingExposureReportParams {
40
+ /** 物料 id,取自 `MarketingMaterial.id` */
41
+ id: string;
42
+ /** 事件类型 */
43
+ event: 'exposure';
44
+ /** 曝光事件无关闭原因 */
45
+ reason?: never;
46
+ /** 曝光事件无点击附加信息 */
47
+ extra?: never;
48
+ }
49
+ /** 关闭上报入参:可带关闭原因 */
50
+ interface MarketingCloseReportParams {
51
+ /** 物料 id */
52
+ id: string;
53
+ /** 事件类型 */
54
+ event: 'close';
55
+ /**
56
+ * 关闭原因,取自解释器抛出的 close reason:
57
+ * `close-button`(点 ×)/ `mask`(点遮罩)/ `countdown-end`(倒计时归零自动关)/
58
+ * `action`(配置里 close 动作未写 reason 的默认值)/ 运营自定义值。
59
+ */
60
+ reason?: string;
61
+ /** 关闭事件无点击附加信息 */
62
+ extra?: never;
63
+ }
64
+ /** 点击上报入参:必须带 extra 说明点了什么 */
65
+ interface MarketingClickReportParams {
66
+ /** 物料 id */
67
+ id: string;
68
+ /** 事件类型 */
69
+ event: 'click';
70
+ /** 点击事件不写关闭原因 */
71
+ reason?: never;
72
+ /** 点击详情,见 {@link MarketingClickExtra} */
73
+ extra: MarketingClickExtra;
74
+ }
75
+ /**
76
+ * 上报物料事件的入参(可辨识联合,按 `event` 区分)
77
+ *
78
+ * 用联合而非「三个可选字段」是为了把契约写进类型里:`reason` 只配 `close`、
79
+ * `extra` 只配 `click`,写错直接编译不过。不该出现的字段声明成 `?: never`
80
+ * 而不是省略,这样消费方(formatter / 上报层 / 调试台预览)仍能直接读
81
+ * `params.reason`、`params.extra`,无需先做类型窄化 —— 约束留在调用侧,消费侧照旧好写。
82
+ */
83
+ type MarketingReportParams = MarketingExposureReportParams | MarketingCloseReportParams | MarketingClickReportParams;
84
+
85
+ /**
86
+ * 「DSL 运行时事件 → 上报载荷」的映射(纯函数,无副作用、无框架依赖)。
87
+ *
88
+ * 前台坑位组件与后台调试台的「上报预览」共用这一份:
89
+ * 调试台要展示的就是**真实会发给后端的 JSON**,映射有第二份实现,
90
+ * 前台改了口径而调试台忘了改,调试台就会骗人。
91
+ */
92
+
93
+ /**
94
+ * 会产生上报的运行时事件及其载荷。
95
+ *
96
+ * 字段一律可选:宿主(Vue 组件的 `$event`、调试台的手工构造)拿到的载荷未必齐全,
97
+ * 缺字段是允许的,不该在映射这一步炸掉。
98
+ */
99
+ interface MarketingReportableEventPayloads {
100
+ /** 埋点动作:`event` 是运营配的埋点名,`params` 是自定义参数 */
101
+ track: {
102
+ event?: string;
103
+ params?: Record<string, unknown>;
104
+ };
105
+ /** 跳转动作:`url` 是目标地址(跳转本身由渲染壳完成,这里只记一笔) */
106
+ navigate: {
107
+ url?: string;
108
+ target?: string;
109
+ };
110
+ /** 关闭:`reason` 是关闭原因 */
111
+ close: {
112
+ reason?: string;
113
+ };
114
+ }
115
+ /** 会产生上报的运行时事件名 */
116
+ type MarketingReportableEventName = keyof MarketingReportableEventPayloads;
117
+ /**
118
+ * 事件名 → 载荷类型。
119
+ *
120
+ * 不产生上报的事件(`call` / `error` / `state-change` / `view-change` / `ready` / `open`)
121
+ * 载荷为 `unknown`:调用方照常传,映射返回 `null`。
122
+ */
123
+ type MarketingRuntimeEventPayload<K extends RuntimeEventName> = K extends MarketingReportableEventName ? MarketingReportableEventPayloads[K] : unknown;
124
+ /**
125
+ * 曝光上报载荷。
126
+ *
127
+ * 单独一个函数是因为曝光**不由运行时事件触发** —— 它是宿主把物料渲染到页面上时主动发的,
128
+ * 解释器根本不知道自己被画出来了。
129
+ *
130
+ * @param id 物料 id,为空(未取到物料)时返回 `null`,不产生上报
131
+ * @returns 上报载荷,或 `null`
132
+ */
133
+ declare function buildExposureReport(id: string | undefined | null): MarketingExposureReportParams | null;
134
+ /**
135
+ * 把一个 DSL 运行时事件映射成上报载荷。
136
+ *
137
+ * 口径(与 `docs/marketing/README.md` 的契约一一对应):
138
+ * - `track` → `click`,`extra = { trigger: 'track', name, params }`
139
+ * - `navigate` → `click`,`extra = { trigger: 'navigate', url }`
140
+ * - `close` → `close`,带 `reason`
141
+ * - 其余事件(`call` / `error` / `state-change` / `view-change` / `ready` / `open`)不产生上报
142
+ *
143
+ * @param id 物料 id,为空时返回 `null`(拿不到 id 的上报对后端没有意义)
144
+ * @param event 运行时事件名
145
+ * @param payload 事件载荷,可缺省
146
+ * @returns 上报载荷,或 `null`(该事件不产生上报)
147
+ */
148
+ declare function mapRuntimeEventToReport<K extends RuntimeEventName>(id: string | undefined | null, event: K, payload?: MarketingRuntimeEventPayload<K>): MarketingReportParams | null;
149
+
150
+ export { type MarketingClickExtra, type MarketingClickReportParams, type MarketingCloseReportParams, type MarketingExposureReportParams, type MarketingReportEvent, type MarketingReportParams, type MarketingReportableEventName, type MarketingReportableEventPayloads, type MarketingRuntimeEventPayload, buildExposureReport, mapRuntimeEventToReport };
@@ -0,0 +1,38 @@
1
+ // src/report/map.ts
2
+ function buildExposureReport(id) {
3
+ if (!id) return null;
4
+ return { id, event: "exposure" };
5
+ }
6
+ function mapRuntimeEventToReport(id, event, payload) {
7
+ if (!id) return null;
8
+ switch (event) {
9
+ case "track": {
10
+ const data = payload != null ? payload : {};
11
+ return {
12
+ id,
13
+ event: "click",
14
+ extra: { trigger: "track", name: data.event, params: data.params }
15
+ };
16
+ }
17
+ case "navigate": {
18
+ const data = payload != null ? payload : {};
19
+ return {
20
+ id,
21
+ event: "click",
22
+ extra: { trigger: "navigate", url: data.url }
23
+ };
24
+ }
25
+ case "close": {
26
+ const data = payload != null ? payload : {};
27
+ return {
28
+ id,
29
+ event: "close",
30
+ reason: data.reason
31
+ };
32
+ }
33
+ default:
34
+ return null;
35
+ }
36
+ }
37
+
38
+ export { buildExposureReport, mapRuntimeEventToReport };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaoxiu/marketing-dsl",
3
- "version": "1.4.1",
3
+ "version": "1.5.1",
4
4
  "type": "module",
5
5
  "description": "营销弹窗 DSL 解释器核心,纯逻辑无框架依赖",
6
6
  "license": "MIT",
@@ -21,11 +21,17 @@
21
21
  "types": "./dist/docs/index.d.ts",
22
22
  "import": "./dist/docs/index.js",
23
23
  "require": "./dist/docs/index.cjs"
24
+ },
25
+ "./report": {
26
+ "types": "./dist/report/index.d.ts",
27
+ "import": "./dist/report/index.js",
28
+ "require": "./dist/report/index.cjs"
24
29
  }
25
30
  },
26
31
  "files": [
27
32
  "dist",
28
- "docs"
33
+ "docs",
34
+ "report"
29
35
  ],
30
36
  "dependencies": {},
31
37
  "devDependencies": {},
@@ -0,0 +1,7 @@
1
+ {
2
+ "//": "webpack 4 兼容垫片。它用的 enhanced-resolve 4 不认 package.json 的 exports 字段,会把 `@yaoxiu/marketing-dsl/report` 当成物理路径去找。这个目录就是那个物理路径,靠 main/module/types 把它转发到真正的产物上。消费方(后台 moveback-front 的调试台「上报预览」)因此可以照常写 `@yaoxiu/marketing-dsl/report`,不必伸进 dist 里做深路径引入。webpack 5 / Vite / Node 走的仍是根 package.json 的 exports,不经过这里。删掉它之前先确认没有 webpack 4 的消费方。",
3
+ "main": "../dist/report/index.cjs",
4
+ "module": "../dist/report/index.js",
5
+ "types": "../dist/report/index.d.ts",
6
+ "sideEffects": false
7
+ }