@yaoxiu/marketing-dsl 2.1.2 → 2.2.0

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.
@@ -3211,6 +3211,133 @@ var imageBanner = {
3211
3211
  };
3212
3212
  var image_banner_default = imageBanner;
3213
3213
 
3214
+ // src/docs/examples/drawer-banner.ts
3215
+ function buildBannerImage(options) {
3216
+ const [from, to] = options.colors;
3217
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="640" height="72" viewBox="0 0 640 72">
3218
+ <defs>
3219
+ <linearGradient id="g" x1="0" y1="0" x2="1" y2="0">
3220
+ <stop offset="0" stop-color="${from}"/>
3221
+ <stop offset="1" stop-color="${to}"/>
3222
+ </linearGradient>
3223
+ </defs>
3224
+ <rect width="640" height="72" fill="url(#g)"/>
3225
+ <text x="20" y="44" font-family="sans-serif" font-size="22" fill="#ffffff">${options.title}</text>
3226
+ <rect x="400" y="18" width="150" height="36" rx="18" fill="#ffffff" fill-opacity="0.92"/>
3227
+ <text x="475" y="43" font-family="sans-serif" font-size="20" fill="${options.ctaColor}" text-anchor="middle">${options.cta}</text>
3228
+ </svg>`;
3229
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
3230
+ }
3231
+ function createDrawerBanner(options) {
3232
+ return {
3233
+ version: 1,
3234
+ type: "banner",
3235
+ meta: { name: options.name },
3236
+ stage: {
3237
+ width: "100%",
3238
+ // 高度写死:坑位宽度是宿主固定的,不需要靠 aspectRatio 维持等比(见文件头注释)
3239
+ height: options.height,
3240
+ style: { radius: 6, overflow: "hidden" },
3241
+ onShow: {
3242
+ type: "track",
3243
+ event: "drawer_banner_show",
3244
+ params: { scene: options.scene }
3245
+ },
3246
+ onClose: {
3247
+ type: "track",
3248
+ event: "drawer_banner_dismiss",
3249
+ params: { scene: options.scene, reason: "{{ closeReason }}" }
3250
+ }
3251
+ },
3252
+ // 高度已确定,节点直接铺在 stage 上按 rect 绝对定位,不需要额外套一层定位容器
3253
+ nodes: [
3254
+ {
3255
+ type: "image",
3256
+ rect: [0, 0, "100%", "100%"],
3257
+ src: buildBannerImage(options),
3258
+ // 画布比例与坑位一致,cover 既不裁也不拉,图里元素与百分比热区天然对齐
3259
+ style: { objectFit: "cover" }
3260
+ },
3261
+ {
3262
+ // 左半边文案区:图里文字从 20/640 起,热区框大一圈
3263
+ type: "box",
3264
+ rect: ["2%", "15%", "56%", "70%"],
3265
+ style: { radius: 4, background: "rgba(255,255,255,0.14)", cursor: "pointer" },
3266
+ action: {
3267
+ type: "sequence",
3268
+ actions: [
3269
+ {
3270
+ type: "track",
3271
+ event: "drawer_banner_click",
3272
+ params: { scene: options.scene, area: "title" }
3273
+ },
3274
+ { type: "navigate", url: options.url, target: "_blank" }
3275
+ ]
3276
+ }
3277
+ },
3278
+ {
3279
+ // 右侧按钮:图里按钮是 400~550(62.5%~85.9%),热区照着框
3280
+ type: "box",
3281
+ rect: ["62%", "20%", "25%", "60%"],
3282
+ style: {
3283
+ radius: 999,
3284
+ background: "rgba(255,255,255,0.16)",
3285
+ cursor: "pointer"
3286
+ },
3287
+ action: {
3288
+ type: "sequence",
3289
+ actions: [
3290
+ {
3291
+ type: "track",
3292
+ event: "drawer_banner_click",
3293
+ params: { scene: options.scene, area: "cta" }
3294
+ },
3295
+ { type: "navigate", url: options.url, target: "_blank" }
3296
+ ]
3297
+ }
3298
+ },
3299
+ {
3300
+ // 关闭按钮贴右:x / y 用 calc 而不是百分比 —— 百分比按容器尺寸算,
3301
+ // 坑位宽度一调它就飘;calc 让它永远距右边缘 8px、垂直居中
3302
+ type: "text",
3303
+ content: "\u2715",
3304
+ rect: ["calc(100% - 20px)", "calc(50% - 6px)", 12, 12],
3305
+ style: {
3306
+ textAlign: "center",
3307
+ lineHeight: 12,
3308
+ radius: 6,
3309
+ background: "rgba(0,0,0,0.32)",
3310
+ color: "#fff",
3311
+ fontSize: 9,
3312
+ cursor: "pointer"
3313
+ },
3314
+ // 「不再提醒」写在 close 的 reason 上,上报给后端即可,不配 call 写本地存储
3315
+ action: { type: "close", reason: "user-dismiss" }
3316
+ }
3317
+ ]
3318
+ };
3319
+ }
3320
+ var drawerTopBanner = createDrawerBanner({
3321
+ name: "\u62BD\u5C49\u9876\u90E8 Banner\uFF08320\xD736\uFF09",
3322
+ height: 36,
3323
+ title: "AI \u4E00\u952E\u586B\u5199\u5C5E\u6027 \xB7 \u9650\u65F6\u514D\u8D39",
3324
+ cta: "\u53BB\u8BD5\u8BD5",
3325
+ colors: ["#2b6cf6", "#5aa0ff"],
3326
+ ctaColor: "#1a4fd0",
3327
+ scene: "product_edit_top",
3328
+ url: "https://maibaole.example.com/ai-attr"
3329
+ });
3330
+ var drawerBottomBanner = createDrawerBanner({
3331
+ name: "\u62BD\u5C49\u5E95\u90E8 Banner\uFF08400\xD745\uFF09",
3332
+ height: 45,
3333
+ title: "\u642C\u5BB6\u9047\u5230\u95EE\u9898\uFF1F\u4E13\u5C5E\u987E\u95EE 1v1",
3334
+ cta: "\u7ACB\u5373\u54A8\u8BE2",
3335
+ colors: ["#ff6a3d", "#ffa15c"],
3336
+ ctaColor: "#d43a1f",
3337
+ scene: "product_edit_bottom",
3338
+ url: "https://maibaole.example.com/support"
3339
+ });
3340
+
3214
3341
  // src/docs/examples/index.ts
3215
3342
  var HOTSPOT_BG = "rgba(255,68,51,0.16)";
3216
3343
  var imageHotspot = {
@@ -3442,7 +3569,17 @@ var examples = [
3442
3569
  dsl: confirmAndSwitch
3443
3570
  },
3444
3571
  { label: "\u2466 \u5207 tab \u6362\u80A4\u6362\u5E03\u5C40", value: "tabTheme", dsl: tab_theme_default },
3445
- { label: "\u2467 \u6574\u56FE Banner\uFF08\u7A84\u901A\u680F + \u70ED\u533A\uFF09", value: "imageBanner", dsl: image_banner_default }
3572
+ { label: "\u2467 \u6574\u56FE Banner\uFF08\u7A84\u901A\u680F + \u70ED\u533A\uFF09", value: "imageBanner", dsl: image_banner_default },
3573
+ {
3574
+ label: "\u2468 \u62BD\u5C49\u9876\u90E8 Banner\uFF08\u56FA\u5B9A 320\xD736\uFF09",
3575
+ value: "drawerTopBanner",
3576
+ dsl: drawerTopBanner
3577
+ },
3578
+ {
3579
+ label: "\u2469 \u62BD\u5C49\u5E95\u90E8 Banner\uFF08\u56FA\u5B9A 400\xD745\uFF09",
3580
+ value: "drawerBottomBanner",
3581
+ dsl: drawerBottomBanner
3582
+ }
3446
3583
  ];
3447
3584
  var examples_default = examples;
3448
3585
 
@@ -94,7 +94,8 @@ declare const meta: DocMeta;
94
94
  /**
95
95
  * 调试用示例 DSL。
96
96
  *
97
- * ①② 是最小骨架,③④ 是运营 demo 真实弹窗的完整复刻,⑤⑥ 是多视图,⑦ 是换肤换布局。
97
+ * ①② 是最小骨架,③④ 是运营 demo 真实弹窗的完整复刻,⑤⑥ 是多视图,⑦ 是换肤换布局,
98
+ * ⑧ 是通栏 banner(宽度由页面决定、只能声明比例),⑨⑩ 是商品编辑抽屉里两个**固定尺寸**坑位。
98
99
  * 后端数据源($source)用法见 ③。
99
100
  */
100
101
 
@@ -94,7 +94,8 @@ declare const meta: DocMeta;
94
94
  /**
95
95
  * 调试用示例 DSL。
96
96
  *
97
- * ①② 是最小骨架,③④ 是运营 demo 真实弹窗的完整复刻,⑤⑥ 是多视图,⑦ 是换肤换布局。
97
+ * ①② 是最小骨架,③④ 是运营 demo 真实弹窗的完整复刻,⑤⑥ 是多视图,⑦ 是换肤换布局,
98
+ * ⑧ 是通栏 banner(宽度由页面决定、只能声明比例),⑨⑩ 是商品编辑抽屉里两个**固定尺寸**坑位。
98
99
  * 后端数据源($source)用法见 ③。
99
100
  */
100
101
 
@@ -3086,6 +3086,133 @@ var imageBanner = {
3086
3086
  };
3087
3087
  var image_banner_default = imageBanner;
3088
3088
 
3089
+ // src/docs/examples/drawer-banner.ts
3090
+ function buildBannerImage(options) {
3091
+ const [from, to] = options.colors;
3092
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="640" height="72" viewBox="0 0 640 72">
3093
+ <defs>
3094
+ <linearGradient id="g" x1="0" y1="0" x2="1" y2="0">
3095
+ <stop offset="0" stop-color="${from}"/>
3096
+ <stop offset="1" stop-color="${to}"/>
3097
+ </linearGradient>
3098
+ </defs>
3099
+ <rect width="640" height="72" fill="url(#g)"/>
3100
+ <text x="20" y="44" font-family="sans-serif" font-size="22" fill="#ffffff">${options.title}</text>
3101
+ <rect x="400" y="18" width="150" height="36" rx="18" fill="#ffffff" fill-opacity="0.92"/>
3102
+ <text x="475" y="43" font-family="sans-serif" font-size="20" fill="${options.ctaColor}" text-anchor="middle">${options.cta}</text>
3103
+ </svg>`;
3104
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
3105
+ }
3106
+ function createDrawerBanner(options) {
3107
+ return {
3108
+ version: 1,
3109
+ type: "banner",
3110
+ meta: { name: options.name },
3111
+ stage: {
3112
+ width: "100%",
3113
+ // 高度写死:坑位宽度是宿主固定的,不需要靠 aspectRatio 维持等比(见文件头注释)
3114
+ height: options.height,
3115
+ style: { radius: 6, overflow: "hidden" },
3116
+ onShow: {
3117
+ type: "track",
3118
+ event: "drawer_banner_show",
3119
+ params: { scene: options.scene }
3120
+ },
3121
+ onClose: {
3122
+ type: "track",
3123
+ event: "drawer_banner_dismiss",
3124
+ params: { scene: options.scene, reason: "{{ closeReason }}" }
3125
+ }
3126
+ },
3127
+ // 高度已确定,节点直接铺在 stage 上按 rect 绝对定位,不需要额外套一层定位容器
3128
+ nodes: [
3129
+ {
3130
+ type: "image",
3131
+ rect: [0, 0, "100%", "100%"],
3132
+ src: buildBannerImage(options),
3133
+ // 画布比例与坑位一致,cover 既不裁也不拉,图里元素与百分比热区天然对齐
3134
+ style: { objectFit: "cover" }
3135
+ },
3136
+ {
3137
+ // 左半边文案区:图里文字从 20/640 起,热区框大一圈
3138
+ type: "box",
3139
+ rect: ["2%", "15%", "56%", "70%"],
3140
+ style: { radius: 4, background: "rgba(255,255,255,0.14)", cursor: "pointer" },
3141
+ action: {
3142
+ type: "sequence",
3143
+ actions: [
3144
+ {
3145
+ type: "track",
3146
+ event: "drawer_banner_click",
3147
+ params: { scene: options.scene, area: "title" }
3148
+ },
3149
+ { type: "navigate", url: options.url, target: "_blank" }
3150
+ ]
3151
+ }
3152
+ },
3153
+ {
3154
+ // 右侧按钮:图里按钮是 400~550(62.5%~85.9%),热区照着框
3155
+ type: "box",
3156
+ rect: ["62%", "20%", "25%", "60%"],
3157
+ style: {
3158
+ radius: 999,
3159
+ background: "rgba(255,255,255,0.16)",
3160
+ cursor: "pointer"
3161
+ },
3162
+ action: {
3163
+ type: "sequence",
3164
+ actions: [
3165
+ {
3166
+ type: "track",
3167
+ event: "drawer_banner_click",
3168
+ params: { scene: options.scene, area: "cta" }
3169
+ },
3170
+ { type: "navigate", url: options.url, target: "_blank" }
3171
+ ]
3172
+ }
3173
+ },
3174
+ {
3175
+ // 关闭按钮贴右:x / y 用 calc 而不是百分比 —— 百分比按容器尺寸算,
3176
+ // 坑位宽度一调它就飘;calc 让它永远距右边缘 8px、垂直居中
3177
+ type: "text",
3178
+ content: "\u2715",
3179
+ rect: ["calc(100% - 20px)", "calc(50% - 6px)", 12, 12],
3180
+ style: {
3181
+ textAlign: "center",
3182
+ lineHeight: 12,
3183
+ radius: 6,
3184
+ background: "rgba(0,0,0,0.32)",
3185
+ color: "#fff",
3186
+ fontSize: 9,
3187
+ cursor: "pointer"
3188
+ },
3189
+ // 「不再提醒」写在 close 的 reason 上,上报给后端即可,不配 call 写本地存储
3190
+ action: { type: "close", reason: "user-dismiss" }
3191
+ }
3192
+ ]
3193
+ };
3194
+ }
3195
+ var drawerTopBanner = createDrawerBanner({
3196
+ name: "\u62BD\u5C49\u9876\u90E8 Banner\uFF08320\xD736\uFF09",
3197
+ height: 36,
3198
+ title: "AI \u4E00\u952E\u586B\u5199\u5C5E\u6027 \xB7 \u9650\u65F6\u514D\u8D39",
3199
+ cta: "\u53BB\u8BD5\u8BD5",
3200
+ colors: ["#2b6cf6", "#5aa0ff"],
3201
+ ctaColor: "#1a4fd0",
3202
+ scene: "product_edit_top",
3203
+ url: "https://maibaole.example.com/ai-attr"
3204
+ });
3205
+ var drawerBottomBanner = createDrawerBanner({
3206
+ name: "\u62BD\u5C49\u5E95\u90E8 Banner\uFF08400\xD745\uFF09",
3207
+ height: 45,
3208
+ title: "\u642C\u5BB6\u9047\u5230\u95EE\u9898\uFF1F\u4E13\u5C5E\u987E\u95EE 1v1",
3209
+ cta: "\u7ACB\u5373\u54A8\u8BE2",
3210
+ colors: ["#ff6a3d", "#ffa15c"],
3211
+ ctaColor: "#d43a1f",
3212
+ scene: "product_edit_bottom",
3213
+ url: "https://maibaole.example.com/support"
3214
+ });
3215
+
3089
3216
  // src/docs/examples/index.ts
3090
3217
  var HOTSPOT_BG = "rgba(255,68,51,0.16)";
3091
3218
  var imageHotspot = {
@@ -3317,7 +3444,17 @@ var examples = [
3317
3444
  dsl: confirmAndSwitch
3318
3445
  },
3319
3446
  { label: "\u2466 \u5207 tab \u6362\u80A4\u6362\u5E03\u5C40", value: "tabTheme", dsl: tab_theme_default },
3320
- { label: "\u2467 \u6574\u56FE Banner\uFF08\u7A84\u901A\u680F + \u70ED\u533A\uFF09", value: "imageBanner", dsl: image_banner_default }
3447
+ { label: "\u2467 \u6574\u56FE Banner\uFF08\u7A84\u901A\u680F + \u70ED\u533A\uFF09", value: "imageBanner", dsl: image_banner_default },
3448
+ {
3449
+ label: "\u2468 \u62BD\u5C49\u9876\u90E8 Banner\uFF08\u56FA\u5B9A 320\xD736\uFF09",
3450
+ value: "drawerTopBanner",
3451
+ dsl: drawerTopBanner
3452
+ },
3453
+ {
3454
+ label: "\u2469 \u62BD\u5C49\u5E95\u90E8 Banner\uFF08\u56FA\u5B9A 400\xD745\uFF09",
3455
+ value: "drawerBottomBanner",
3456
+ dsl: drawerBottomBanner
3457
+ }
3321
3458
  ];
3322
3459
  var examples_default = examples;
3323
3460
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaoxiu/marketing-dsl",
3
- "version": "2.1.2",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "description": "营销弹窗 DSL 解释器核心,纯逻辑无框架依赖",
6
6
  "license": "MIT",