敖欣乐 15 órája
szülő
commit
12a3f90bc5

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+node_modules

+ 85 - 78
common-page/pages/web-view/index.js

@@ -8,114 +8,67 @@ Page({
      */
     data: {
         path: "",
+        currentTitle: "", // 当前标题
     },
 
+    // 标题轮询定时器
+    titlePollingTimer: null,
+
     /**
      * 生命周期函数--监听页面加载
      */
     onLoad: function (options) {
-        console.log('===========================================');
-        console.log('======= web-view 页面加载 =======');
-        console.log('===========================================');
-        console.log('📦 接收到的 options:', options);
-        
-        // 解码 URL(如果被编码了)
+        // 解码 URL
         let path = decodeURIComponent(options.path || '');
-        console.log('🔓 解码后的 path:', path);
-        console.log('   - path 长度:', path.length);
-        
-        // 检查 URL 中是否包含 token
-        if (path.includes('token=')) {
-            console.log('✅ URL 中包含 token 参数');
-            const tokenMatch = path.match(/token=([^&]+)/);
-            if (tokenMatch && tokenMatch[1]) {
-                console.log('🔑 提取到的 token (前20字符):', tokenMatch[1].substring(0, 20));
-            }
-        } else {
-            console.log('⚠️ URL 中没有 token 参数!');
-        }
-        
-        // 如果还有额外的参数(除了 path 和 url),拼接到 URL 后面
+
+        // 拼接额外参数
         let hasQuery = path.indexOf('?') !== -1;
         let parsm = hasQuery ? '&' : '?';
         let params = [];
-        
+
         for (const key in options) {
             if(key != 'path' && key != 'url'){
                 params.push(key + '=' + options[key]);
-                console.log(`   - 额外参数: ${key} = ${options[key]}`);
             }
         }
-        
-        // 只有当有额外参数时才拼接
+
         if(params.length > 0) {
             parsm = parsm + params.join('&');
             path = path + parsm;
-            console.log('🔗 拼接额外参数后的 path:', path);
         }
-        
-        console.log('🌐 最终加载的 URL:', path);
-        console.log('===========================================');
-        
+
         this.setData({
             path: path
         })
 
-        // 立即设置标题(优先使用传入的 storeName)
+        // 立即设置标题
         const passedStoreName = options.storeName ? decodeURIComponent(options.storeName) : '';
         const passedStoreId = options.storeId || '';
-        
-        console.log('🏷️ 接收到的店铺信息:');
-        console.log('   - storeId:', passedStoreId);
-        console.log('   - storeName (原始):', options.storeName);
-        console.log('   - storeName (解码):', passedStoreName);
-        
-        // 立即同步设置标题
+
         if (passedStoreName) {
-            console.log('🎯 立即设置标题为:', passedStoreName);
-            wx.setNavigationBarTitle({
-                title: passedStoreName,
-                success: () => {
-                    console.log('✅✅✅ 标题设置成功:', passedStoreName);
-                },
-                fail: (err) => {
-                    console.error('❌❌❌ 标题设置失败:', err);
-                }
-            });
+            this.setNavigationTitle(passedStoreName);
         }
-        
+
         // 异步加载完整店铺信息(作为备份)
-        this.loadAndSetStoreTitle(passedStoreId, passedStoreName)
+        this.loadAndSetStoreTitle(passedStoreId, passedStoreName);
+
+        // 启动标题轮询监听
+        this.startTitlePolling();
     },
 
-    /**
-     * 生命周期函数--监听页面初次渲染完成
-     */
     onReady: function () {
-        console.log('📌 onReady: 页面渲染完成');
-        // 不在这里设置,避免覆盖 onLoad 中的设置
     },
 
-    /**
-     * 生命周期函数--监听页面显示
-     */
     onShow: function () {
-        console.log('📌 onShow: 页面显示');
-        // 不在这里设置,避免覆盖 onLoad 中的设置
+        this.startTitlePolling();
     },
 
-    /**
-     * 生命周期函数--监听页面隐藏
-     */
     onHide: function () {
-
+        this.stopTitlePolling();
     },
 
-    /**
-     * 生命周期函数--监听页面卸载
-     */
     onUnload: function () {
-
+        this.stopTitlePolling();
     },
 
     /**
@@ -139,6 +92,68 @@ Page({
 
     },
 
+    /**
+     * 处理来自 H5 页面的消息
+     */
+    handleMessage: function (e) {
+        try {
+            const messages = e.detail.data || [];
+
+            // 找到最后一个标题更新消息
+            let lastTitleMessage = null;
+            for (let i = messages.length - 1; i >= 0; i--) {
+                const msg = messages[i];
+                if (msg.type === 'updateTitle' && msg.title) {
+                    lastTitleMessage = msg;
+                    break;
+                }
+            }
+
+            // 更新标题
+            if (lastTitleMessage) {
+                this.setNavigationTitle(lastTitleMessage.title);
+            }
+        } catch (error) {
+            console.error('❌ 处理消息失败:', error);
+        }
+    },
+
+    /**
+     * 设置导航栏标题(统一方法)
+     */
+    setNavigationTitle: function (title) {
+        if (!title) {
+            return;
+        }
+
+        // 更新当前标题记录
+        this.setData({
+            currentTitle: title
+        });
+
+        // 调用微信 API 设置标题
+        wx.setNavigationBarTitle({
+            title: title,
+            success: () => {
+                console.log('✅ 标题已更新:', title);
+            },
+            fail: (err) => {
+                console.error('❌ 标题设置失败:', err);
+            }
+        });
+    },
+
+    startTitlePolling: function () {
+        this.stopTitlePolling();
+    },
+
+    stopTitlePolling: function () {
+        if (this.titlePollingTimer) {
+            clearInterval(this.titlePollingTimer);
+            this.titlePollingTimer = null;
+        }
+    },
+
     /**
      * 加载店铺信息并设置页面标题
      */
@@ -165,16 +180,8 @@ Page({
 
             if (!finalName) return;
 
-            // 延迟到本帧结束设置标题,避免被覆盖
-            setTimeout(() => {
-                const pages = getCurrentPages();
-                const current = pages[pages.length - 1];
-                console.log('📌 web-view 当前页面路由:', current && current.route);
-
-                wx.setNavigationBarTitle({
-                    title: finalName
-                })
-            }, 0)
+            // 使用统一的设置标题方法
+            this.setNavigationTitle(finalName);
         } catch (e) {
             console.error('设置 web-view 标题失败:', e);
         }

+ 1 - 1
common-page/pages/web-view/index.wxml

@@ -1 +1 @@
-<web-view src="{{path}}"></web-view>
+<web-view src="{{path}}" bindmessage="handleMessage"></web-view>

+ 26 - 2
index.js

@@ -54,6 +54,24 @@ Page({
       wx.setStorageSync("invite", obj.invite);
     }
     obj && Object.keys(obj).forEach(key=> options[key] = obj[key])
+    // 兼容从编译参数或页面直达传入的 storeId
+    if (options && options.scene && !options.storeId) {
+      try {
+        const sceneStr = decodeURIComponent(options.scene)
+        if (sceneStr) {
+          const pairs = sceneStr.split('&')
+          for (const p of pairs) {
+            const [k, v] = p.split('=')
+            if (k === 'storeId' && v) {
+              options.storeId = v
+              break
+            }
+          }
+        }
+      } catch (e) {
+        console.warn('解析 scene 失败:', e)
+      }
+    }
     this.setData({
       options: options
     })
@@ -66,7 +84,8 @@ Page({
       invite,
       activityId,
       company,
-      inviteHost
+      inviteHost,
+      storeId
     } = options
     time && wx.setStorageSync("time", time);
     dramaId && wx.setStorageSync("dramaId", dramaId);
@@ -76,6 +95,11 @@ Page({
     invite && wx.setStorageSync("invite", invite);
     activityId && wx.setStorageSync("activityId", activityId);
     inviteHost && wx.setStorageSync("inviteHost", true);
+    if (storeId) {
+      wx.setStorageSync('storeId', storeId)
+      getApp().globalData.storeId = storeId
+      console.log('✅ 入口页已设置店铺 ID:', storeId)
+    }
     if (company) getApp().globalData.toCompany = true;
     plugin.init(config, wx.getStorageSync('invite'))
   },
@@ -238,4 +262,4 @@ Page({
     })
     return obj
   }
-});
+});

+ 20 - 33
nova-pbf/components/home/index.js

@@ -167,28 +167,13 @@ Component({
     async getStoreId() {
       try {
         console.log('🏪 开始获取店铺 ID...');
-        
-        // 默认使用固定的店铺 ID
         const defaultStoreId = 'Zr65KGWXHx';
-        console.log('✅ 使用默认店铺 ID:', defaultStoreId);
-        return defaultStoreId;
-        
-        // 原有的查询逻辑(已注释)
-        // const storeQuery = new Parse.Query('ShopStore');
-        // storeQuery.equalTo('company', company);
-        // storeQuery.ascending('score');
-        // storeQuery.limit(1);
-        // 
-        // const store = await storeQuery.first();
-        // 
-        // if (store) {
-        //   const storeId = store.id;
-        //   console.log('✅ 获取店铺 ID 成功:', storeId);
-        //   return storeId;
-        // } else {
-        //   console.log('⚠️ 未找到店铺,将不带 storeId 参数');
-        //   return null;
-        // }
+        const configuredStoreId =
+          wx.getStorageSync('storeId') ||
+          getApp().globalData.storeId ||
+          defaultStoreId;
+        console.log('✅ 使用店铺 ID:', configuredStoreId);
+        return configuredStoreId;
       } catch (error) {
         console.error('❌ 获取店铺 ID 失败:', error);
         return null;
@@ -200,25 +185,27 @@ Component({
      */
     async getStoreInfo() {
       try {
-        // 默认使用固定的店铺 ID
         const defaultStoreId = 'Zr65KGWXHx';
-        
-        // 尝试获取该店铺的名称
+        const effectiveStoreId =
+          wx.getStorageSync('storeId') ||
+          getApp().globalData.storeId ||
+          defaultStoreId;
+
         const query = new Parse.Query('ShopStore');
-        query.equalTo('objectId', defaultStoreId);
+        query.equalTo('objectId', effectiveStoreId);
         const store = await query.first();
-        
+
         if (store) {
-          console.log('✅ 获取到默认店铺信息:', { id: defaultStoreId, name: store.get('storeName') || '' });
-          return { id: defaultStoreId, name: store.get('storeName') || '' };
+          const name = store.get('storeName') || '';
+          console.log('✅ 获取店铺信息:', { id: effectiveStoreId, name });
+          return { id: effectiveStoreId, name };
         } else {
-          console.log('⚠️ 未找到默认店铺,仅返回 ID');
-          return { id: defaultStoreId, name: '' };
+          console.log('⚠️ 未找到店铺,仅返回 ID');
+          return { id: effectiveStoreId, name: '' };
         }
       } catch (e) {
         console.error('获取店铺信息失败:', e);
-        // 即使出错也返回默认 ID
-        return { id: 'Zr65KGWXHx', name: '' };
+        return { id: wx.getStorageSync('storeId') || 'Zr65KGWXHx', name: '' };
       }
     },
 
@@ -508,4 +495,4 @@ Component({
       });
     }
   }
-})
+})

+ 38 - 7
nova-pbf/pages/index/index.js

@@ -16,7 +16,37 @@ Page({
    */
   onLoad: async function (options) {
     // login.loginNow()
-    
+
+    try {
+      let finalStoreId = options && options.storeId ? options.storeId : '';
+
+      // 支持从 scene 读取(小程序码 getwxacodeunlimit 传参)
+      if (!finalStoreId && options && options.scene) {
+        const sceneStr = decodeURIComponent(options.scene);
+        console.log('🎯 scene 参数:', sceneStr);
+        if (sceneStr) {
+          const pairs = sceneStr.split('&');
+          for (const p of pairs) {
+            const [k, v] = p.split('=');
+            if (k === 'storeId' && v) {
+              finalStoreId = v;
+              break;
+            }
+          }
+        }
+      }
+
+      if (finalStoreId) {
+        wx.setStorageSync('storeId', finalStoreId);
+        getApp().globalData.storeId = finalStoreId;
+        console.log('✅ 已设置店铺 ID:', finalStoreId);
+      } else {
+        console.log('ℹ️ 未传入店铺 ID,继续使用默认值');
+      }
+    } catch (e) {
+      console.error('设置店铺 ID 失败:', e);
+    }
+
     // ✅ 立即加载并设置店铺名称作为页面标题
     await this.loadAndSetStoreTitle()
   },
@@ -85,13 +115,14 @@ Page({
       console.log('======= 引导页:开始设置页面标题 =======');
       console.log('===========================================');
       
-      // 默认使用固定的店铺 ID
+      // 计算当前有效的店铺 ID
       const defaultStoreId = 'Zr65KGWXHx';
-      console.log('🏪 使用默认店铺 ID:', defaultStoreId);
+      const effectiveStoreId = wx.getStorageSync('storeId') || getApp().globalData.storeId || defaultStoreId;
+      console.log('🏪 使用店铺 ID:', effectiveStoreId);
       
       // 查询指定的店铺
       const storeQuery = new Parse.Query('ShopStore');
-      storeQuery.equalTo('objectId', defaultStoreId);
+      storeQuery.equalTo('objectId', effectiveStoreId);
       
       console.log('🔍 正在查询 ShopStore 表...');
       const store = await storeQuery.first();
@@ -123,10 +154,10 @@ Page({
           console.log('===========================================');
         }
       } else {
-        console.warn('⚠️ 未找到指定店铺信息 (ID:', defaultStoreId, ')');
+        console.warn('⚠️ 未找到指定店铺信息 (ID:', effectiveStoreId, ')');
         console.log('💡 请检查:');
         console.log('   1. ShopStore 表中是否存在该店铺');
-        console.log('   2. 店铺 ID 是否正确:', defaultStoreId);
+        console.log('   2. 店铺 ID 是否正确:', effectiveStoreId);
         console.log('===========================================');
       }
     } catch (error) {
@@ -134,4 +165,4 @@ Page({
       console.log('===========================================');
     }
   }
-})
+})

+ 347 - 0
package-lock.json

@@ -16,6 +16,7 @@
         "fs-extra": "^10.0.0",
         "mp-html": "^2.4.1",
         "parse": "2.6.0",
+        "qrcode": "^1.5.4",
         "shelljs": "^0.8.5",
         "swiper": "^7.2.0",
         "wxml2canvas": "^1.0.1",
@@ -598,6 +599,15 @@
         "get-intrinsic": "^1.0.2"
       }
     },
+    "node_modules/camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/caniuse-lite": {
       "version": "1.0.30001344",
       "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz",
@@ -972,6 +982,15 @@
         "ms": "2.0.0"
       }
     },
+    "node_modules/decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/depd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
@@ -1006,6 +1025,12 @@
       "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
       "dev": true
     },
+    "node_modules/dijkstrajs": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
+      "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
+      "license": "MIT"
+    },
     "node_modules/docdash": {
       "version": "2.0.1",
       "resolved": "https://registry.npmmirror.com/docdash/-/docdash-2.0.1.tgz",
@@ -1426,6 +1451,19 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/findup-sync": {
       "version": "5.0.0",
       "resolved": "https://registry.npmmirror.com/findup-sync/-/findup-sync-5.0.0.tgz",
@@ -2765,6 +2803,18 @@
         "node": ">=6.11.5"
       }
     },
+    "node_modules/locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
@@ -3318,6 +3368,42 @@
         "os-tmpdir": "^1.0.0"
       }
     },
+    "node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "license": "MIT",
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/parse": {
       "version": "2.6.0",
       "resolved": "https://registry.npmmirror.com/parse/-/parse-2.6.0.tgz",
@@ -3375,6 +3461,15 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -3434,6 +3529,15 @@
         "node": ">=6"
       }
     },
+    "node_modules/pngjs": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz",
+      "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
     "node_modules/portfinder": {
       "version": "1.0.32",
       "resolved": "https://registry.npmmirror.com/portfinder/-/portfinder-1.0.32.tgz",
@@ -3522,6 +3626,89 @@
         "node": ">=0.9"
       }
     },
+    "node_modules/qrcode": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz",
+      "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
+      "license": "MIT",
+      "dependencies": {
+        "dijkstrajs": "^1.0.1",
+        "pngjs": "^5.0.0",
+        "yargs": "^15.3.1"
+      },
+      "bin": {
+        "qrcode": "bin/qrcode"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/qrcode/node_modules/cliui": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz",
+      "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^6.2.0"
+      }
+    },
+    "node_modules/qrcode/node_modules/wrap-ansi": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/qrcode/node_modules/y18n": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz",
+      "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+      "license": "ISC"
+    },
+    "node_modules/qrcode/node_modules/yargs": {
+      "version": "15.4.1",
+      "resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz",
+      "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^6.0.0",
+        "decamelize": "^1.2.0",
+        "find-up": "^4.1.0",
+        "get-caller-file": "^2.0.1",
+        "require-directory": "^2.1.1",
+        "require-main-filename": "^2.0.0",
+        "set-blocking": "^2.0.0",
+        "string-width": "^4.2.0",
+        "which-module": "^2.0.0",
+        "y18n": "^4.0.0",
+        "yargs-parser": "^18.1.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/qrcode/node_modules/yargs-parser": {
+      "version": "18.1.3",
+      "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz",
+      "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+      "license": "ISC",
+      "dependencies": {
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/qs": {
       "version": "6.10.3",
       "resolved": "https://registry.npmmirror.com/qs/-/qs-6.10.3.tgz",
@@ -3618,6 +3805,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/require-main-filename": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz",
+      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+      "license": "ISC"
+    },
     "node_modules/requires-port": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz",
@@ -3816,6 +4009,12 @@
         "randombytes": "^2.1.0"
       }
     },
+    "node_modules/set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+      "license": "ISC"
+    },
     "node_modules/setprototypeof": {
       "version": "1.2.0",
       "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -4547,6 +4746,12 @@
         "node": ">= 8"
       }
     },
+    "node_modules/which-module": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz",
+      "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
+      "license": "ISC"
+    },
     "node_modules/wordwrap": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/wordwrap/-/wordwrap-1.0.0.tgz",
@@ -5148,6 +5353,11 @@
         "get-intrinsic": "^1.0.2"
       }
     },
+    "camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+    },
     "caniuse-lite": {
       "version": "1.0.30001344",
       "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz",
@@ -5453,6 +5663,11 @@
         "ms": "2.0.0"
       }
     },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
+    },
     "depd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
@@ -5477,6 +5692,11 @@
       "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
       "dev": true
     },
+    "dijkstrajs": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
+      "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
+    },
     "docdash": {
       "version": "2.0.1",
       "resolved": "https://registry.npmmirror.com/docdash/-/docdash-2.0.1.tgz",
@@ -5835,6 +6055,15 @@
         }
       }
     },
+    "find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+      "requires": {
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
+      }
+    },
     "findup-sync": {
       "version": "5.0.0",
       "resolved": "https://registry.npmmirror.com/findup-sync/-/findup-sync-5.0.0.tgz",
@@ -6902,6 +7131,14 @@
       "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
       "dev": true
     },
+    "locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "requires": {
+        "p-locate": "^4.1.0"
+      }
+    },
     "lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
@@ -7359,6 +7596,27 @@
         "os-tmpdir": "^1.0.0"
       }
     },
+    "p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "requires": {
+        "p-try": "^2.0.0"
+      }
+    },
+    "p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+      "requires": {
+        "p-limit": "^2.2.0"
+      }
+    },
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+    },
     "parse": {
       "version": "2.6.0",
       "resolved": "https://registry.npmmirror.com/parse/-/parse-2.6.0.tgz",
@@ -7409,6 +7667,11 @@
       "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
       "dev": true
     },
+    "path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+    },
     "path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -7453,6 +7716,11 @@
       "dev": true,
       "optional": true
     },
+    "pngjs": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz",
+      "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="
+    },
     "portfinder": {
       "version": "1.0.32",
       "resolved": "https://registry.npmmirror.com/portfinder/-/portfinder-1.0.32.tgz",
@@ -7528,6 +7796,70 @@
       "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
       "dev": true
     },
+    "qrcode": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz",
+      "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
+      "requires": {
+        "dijkstrajs": "^1.0.1",
+        "pngjs": "^5.0.0",
+        "yargs": "^15.3.1"
+      },
+      "dependencies": {
+        "cliui": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz",
+          "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+          "requires": {
+            "string-width": "^4.2.0",
+            "strip-ansi": "^6.0.0",
+            "wrap-ansi": "^6.2.0"
+          }
+        },
+        "wrap-ansi": {
+          "version": "6.2.0",
+          "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+          "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+          "requires": {
+            "ansi-styles": "^4.0.0",
+            "string-width": "^4.1.0",
+            "strip-ansi": "^6.0.0"
+          }
+        },
+        "y18n": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz",
+          "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
+        },
+        "yargs": {
+          "version": "15.4.1",
+          "resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz",
+          "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+          "requires": {
+            "cliui": "^6.0.0",
+            "decamelize": "^1.2.0",
+            "find-up": "^4.1.0",
+            "get-caller-file": "^2.0.1",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^2.0.0",
+            "set-blocking": "^2.0.0",
+            "string-width": "^4.2.0",
+            "which-module": "^2.0.0",
+            "y18n": "^4.0.0",
+            "yargs-parser": "^18.1.2"
+          }
+        },
+        "yargs-parser": {
+          "version": "18.1.3",
+          "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz",
+          "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+          "requires": {
+            "camelcase": "^5.0.0",
+            "decamelize": "^1.2.0"
+          }
+        }
+      }
+    },
     "qs": {
       "version": "6.10.3",
       "resolved": "https://registry.npmmirror.com/qs/-/qs-6.10.3.tgz",
@@ -7603,6 +7935,11 @@
       "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
       "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
     },
+    "require-main-filename": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz",
+      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+    },
     "requires-port": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz",
@@ -7783,6 +8120,11 @@
         "randombytes": "^2.1.0"
       }
     },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
+    },
     "setprototypeof": {
       "version": "1.2.0",
       "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -8348,6 +8690,11 @@
         "isexe": "^2.0.0"
       }
     },
+    "which-module": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz",
+      "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
+    },
     "wordwrap": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/wordwrap/-/wordwrap-1.0.0.tgz",

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
     "fs-extra": "^10.0.0",
     "mp-html": "^2.4.1",
     "parse": "2.6.0",
+    "qrcode": "^1.5.4",
     "shelljs": "^0.8.5",
     "swiper": "^7.2.0",
     "wxml2canvas": "^1.0.1",

+ 7 - 0
project.private.config.json

@@ -19,6 +19,13 @@
   "condition": {
     "miniprogram": {
       "list": [
+        {
+          "name": "nova-pbf/pages/index/index",
+          "pathName": "nova-pbf/pages/index/index",
+          "query": "storeId=ARhMGM5Y1W",
+          "launchMode": "default",
+          "scene": null
+        },
         {
           "name": "nova-vbank/pages/home/activity/index",
           "pathName": "nova-vbank/pages/home/activity/index",

+ 113 - 0
server/generate-minicode.js

@@ -0,0 +1,113 @@
+// Node 脚本:生成携带 storeId 的小程序码(永久码)
+// 使用方法:在命令行设置环境变量并运行
+//   Windows PowerShell:
+//     $env:WX_APPID="<你的AppID>"; $env:WX_SECRET="<你的AppSecret>"; node server/generate-minicode.js
+//   或在同一命令行:
+//     node server/generate-minicode.js
+// 将在 server/output/ 目录下生成 PNG 文件
+
+const https = require('https');
+const fs = require('fs');
+const path = require('path');
+
+const APPID = process.env.WX_APPID || 'wxbb048b80cd2a14b9';
+const SECRET = process.env.WX_SECRET; // 必须提供 AppSecret
+
+const STORE_ID = 'ARhMGM5Y1W';
+const PAGE = 'nova-pbf/pages/index/index';
+
+function httpGetJson(url) {
+  return new Promise((resolve, reject) => {
+    https
+      .get(url, (res) => {
+        let data = '';
+        res.on('data', (chunk) => (data += chunk));
+        res.on('end', () => {
+          try {
+            const json = JSON.parse(data);
+            resolve(json);
+          } catch (e) {
+            reject(e);
+          }
+        });
+      })
+      .on('error', reject);
+  });
+}
+
+function httpPostBuffer(url, bodyObj) {
+  const body = JSON.stringify(bodyObj);
+  return new Promise((resolve, reject) => {
+    const req = https.request(url, {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+        'Content-Length': Buffer.byteLength(body),
+      },
+    });
+    const chunks = [];
+    req.on('response', (res) => {
+      res.on('data', (chunk) => chunks.push(chunk));
+      res.on('end', () => resolve(Buffer.concat(chunks)));
+    });
+    req.on('error', reject);
+    req.write(body);
+    req.end();
+  });
+}
+
+async function main() {
+  if (!SECRET) {
+    console.error('❌ 未提供 WX_SECRET(AppSecret)。请设置环境变量后重试。');
+    process.exit(1);
+  }
+
+  console.log('🔑 获取 access_token...');
+  const tokenResp = await httpGetJson(
+    `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}`
+  );
+  if (!tokenResp.access_token) {
+    console.error('❌ 获取 access_token 失败:', tokenResp);
+    process.exit(1);
+  }
+  const accessToken = tokenResp.access_token;
+  console.log('✅ access_token 获取成功');
+
+  console.log('🖨️ 生成携带参数的小程序码...');
+  const apiUrl = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${accessToken}`;
+  const payload = {
+    scene: `storeId=${STORE_ID}`,
+    page: PAGE,
+    check_path: false, // 允许未发布页面,仅用于测试/体验
+    is_hyaline: true,
+    width: 430,
+  };
+  const buf = await httpPostBuffer(apiUrl, payload);
+
+  // 微信接口在错误时也可能返回 JSON 文本,需要粗略判断
+  const isJson = buf.slice(0, 1).toString() === '{';
+  if (isJson) {
+    try {
+      const err = JSON.parse(buf.toString('utf8'));
+      console.error('❌ 生成小程序码失败:', err);
+      process.exit(1);
+    } catch (_) {
+      // ignore
+    }
+  }
+
+  const outDir = path.join(__dirname, 'output');
+  if (!fs.existsSync(outDir)) {
+    fs.mkdirSync(outDir, { recursive: true });
+  }
+  const outPath = path.join(outDir, `minicode_store_${STORE_ID}.png`);
+  fs.writeFileSync(outPath, buf);
+  console.log(`✅ 已生成:${outPath}`);
+  console.log('📌 扫码后将进入页面并携带参数:storeId=', STORE_ID);
+}
+
+main().catch((e) => {
+  console.error('程序异常:', e);
+  process.exit(1);
+});
+

+ 104 - 0
server/generate-url-link.js

@@ -0,0 +1,104 @@
+// Node 脚本:生成带 storeId 的小程序 URL Link(可指定体验版)
+// 优点:可直接分享为链接或转成二维码,支持 env_version
+// 用法(Windows PowerShell):
+//   $env:WX_APPID="wxbb048b80cd2a14b9"; $env:WX_SECRET="<你的AppSecret>"; node server/generate-url-link.js
+
+const https = require('https');
+
+const APPID = process.env.WX_APPID || 'wxbb048b80cd2a14b9';
+const SECRET = process.env.WX_SECRET; // 必须提供 AppSecret
+
+const STORE_ID = 'ARhMGM5Y1W';
+const PATH = 'nova-pbf/pages/index/index';
+const QUERY = `storeId=${STORE_ID}`;
+const ENV_VERSION = 'trial'; // 可选: 'develop' | 'trial' | 'release'
+
+function httpGetJson(url) {
+  return new Promise((resolve, reject) => {
+    https
+      .get(url, (res) => {
+        let data = '';
+        res.on('data', (chunk) => (data += chunk));
+        res.on('end', () => {
+          try {
+            const json = JSON.parse(data);
+            resolve(json);
+          } catch (e) {
+            reject(e);
+          }
+        });
+      })
+      .on('error', reject);
+  });
+}
+
+function httpPostJson(url, bodyObj) {
+  const body = JSON.stringify(bodyObj);
+  return new Promise((resolve, reject) => {
+    const req = https.request(url, {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+        'Content-Length': Buffer.byteLength(body),
+      },
+    });
+    let data = '';
+    req.on('response', (res) => {
+      res.on('data', (chunk) => (data += chunk));
+      res.on('end', () => {
+        try {
+          const json = JSON.parse(data);
+          resolve(json);
+        } catch (e) {
+          reject(e);
+        }
+      });
+    });
+    req.on('error', reject);
+    req.write(body);
+    req.end();
+  });
+}
+
+async function main() {
+  if (!SECRET) {
+    console.error('❌ 未提供 WX_SECRET(AppSecret)。请设置环境变量后重试。');
+    process.exit(1);
+  }
+
+  console.log('🔑 获取 access_token...');
+  const tokenResp = await httpGetJson(
+    `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}`
+  );
+  if (!tokenResp.access_token) {
+    console.error('❌ 获取 access_token 失败:', tokenResp);
+    process.exit(1);
+  }
+  const accessToken = tokenResp.access_token;
+  console.log('✅ access_token 获取成功');
+
+  console.log('🔗 生成 URL Link...');
+  const apiUrl = `https://api.weixin.qq.com/wxa/generate_urllink?access_token=${accessToken}`;
+  const payload = {
+    path: PATH,
+    query: QUERY,
+    env_version: ENV_VERSION,
+    is_expire: false,
+  };
+
+  const resp = await httpPostJson(apiUrl, payload);
+  if (!resp.url_link) {
+    console.error('❌ 生成 URL Link 失败:', resp);
+    process.exit(1);
+  }
+
+  console.log('✅ URL Link 生成成功:');
+  console.log(resp.url_link);
+  console.log('📌 打开该链接即可跳转到小程序(环境:', ENV_VERSION, '),并携带 query:', QUERY);
+}
+
+main().catch((e) => {
+  console.error('程序异常:', e);
+  process.exit(1);
+});
+