// common-page/pages/web-view/index.js const Parse = getApp().Parse; const company = getApp().globalData.company; Page({ /** * 页面的初始数据 */ data: { path: "", currentTitle: "", // 当前标题 }, // 标题轮询定时器 titlePollingTimer: null, /** * 生命周期函数--监听页面加载 */ onLoad: async function (options) { console.log('==========================================='); console.log('======= web-view 页面加载 ======='); // 1. 先检查用户登录状态 const loginCheck = await this.checkUserLogin(options); if (!loginCheck) { console.log('⚠️ 用户未登录或没有手机号,已跳转到授权页面'); console.log('==========================================='); return; // 停止后续加载 } console.log('✅ 用户已登录且有手机号,继续加载 web-view'); const postAuthModal = wx.getStorageSync('post_auth_modal'); if (postAuthModal && (postAuthModal.title || postAuthModal.content)) { wx.removeStorageSync('post_auth_modal'); await new Promise((resolve) => { wx.showModal({ title: postAuthModal.title || '温馨提示', content: postAuthModal.content || '', showCancel: false, success: () => resolve(), fail: () => resolve(), complete: () => resolve(), }); }); } const upsertQueryParam = (url, key, value) => { if (!url || !key) return url; const safeValue = value === undefined || value === null ? '' : String(value); const [base, hash] = url.split('#'); const [pathPart, queryString] = base.split('?'); const pairs = (queryString || '') .split('&') .filter(Boolean) .map((p) => { const idx = p.indexOf('='); if (idx === -1) return [p, '']; return [p.slice(0, idx), p.slice(idx + 1)]; }); let found = false; const nextPairs = pairs.map(([k, v]) => { if (k === key) { found = true; return [k, encodeURIComponent(safeValue)]; } return [k, v]; }); if (!found) nextPairs.push([key, encodeURIComponent(safeValue)]); const nextQuery = nextPairs.length ? nextPairs.map(([k, v]) => `${k}=${v}`).join('&') : ''; const nextBase = nextQuery ? `${pathPart}?${nextQuery}` : pathPart; return hash !== undefined ? `${nextBase}#${hash}` : nextBase; }; const removeQueryParam = (url, key) => { if (!url || !key) return url; const [base, hash] = url.split('#'); const [pathPart, queryString] = base.split('?'); if (!queryString) return url; const nextPairs = queryString .split('&') .filter(Boolean) .map((p) => { const idx = p.indexOf('='); if (idx === -1) return [p, '']; return [p.slice(0, idx), p.slice(idx + 1)]; }) .filter(([k]) => k !== key); const nextQuery = nextPairs.length ? nextPairs.map(([k, v]) => `${k}=${v}`).join('&') : ''; const nextBase = nextQuery ? `${pathPart}?${nextQuery}` : pathPart; return hash !== undefined ? `${nextBase}#${hash}` : nextBase; }; // 2. 解码 URL let path = decodeURIComponent(options.path || ''); console.log('原始 options.path:', options.path); console.log('解码后的 path:', path); const currentUser = Parse.User.current(); const sessionToken = currentUser?.getSessionToken ? currentUser.getSessionToken() : null; if (sessionToken) { path = upsertQueryParam(path, 'token', sessionToken); path = removeQueryParam(path, 'guestMode'); } else { const hasToken = path.includes('token='); const hasGuestMode = path.includes('guestMode='); if (!hasToken && !hasGuestMode) { path = upsertQueryParam(path, 'guestMode', 'true'); } } // 拼接额外参数(避免重复添加已存在的参数) let hasQuery = path.indexOf('?') !== -1; let parsm = hasQuery ? '&' : '?'; let params = []; // 提取 path 中已有的参数 let existingParams = new Set(); if (hasQuery) { const queryString = path.split('?')[1]; if (queryString) { queryString.split('&').forEach(param => { const key = param.split('=')[0]; existingParams.add(key); }); } } // 只添加 path 中不存在的参数 for (const key in options) { if(key != 'path' && key != 'url' && !existingParams.has(key)){ params.push(key + '=' + options[key]); } } if(params.length > 0) { parsm = parsm + params.join('&'); path = path + parsm; } console.log('最终 web-view URL:', path); console.log('URL 长度:', path.length); console.log('==========================================='); this.setData({ path: path }) // 立即设置标题 const passedStoreName = options.storeName ? decodeURIComponent(options.storeName) : ''; const passedStoreId = options.storeId || ''; if (passedStoreName) { this.setNavigationTitle(passedStoreName); } // 异步加载完整店铺信息(作为备份) this.loadAndSetStoreTitle(passedStoreId, passedStoreName); // 启动标题轮询监听 this.startTitlePolling(); }, /** * 检查用户登录状态 * @returns {Promise} true: 已登录且有手机号,false: 需要登录 */ checkUserLogin: async function(options) { try { console.log('==========================================='); console.log('🔍 开始检查用户登录状态...'); // 检查是否有 skipAuth 参数(用于跳过登录检查) if (options.skipAuth === 'true') { console.log('ℹ️ 检测到 skipAuth 参数,跳过登录检查'); console.log('==========================================='); return true; } // 1. 先调用 checkAuth 初始化用户(不强制授权) console.log('📱 调用 checkAuth 初始化用户...'); try { await getApp().checkAuth(false); // false = 不强制授权,只是初始化 console.log('✅ checkAuth 调用成功'); } catch (err) { console.warn('⚠️ checkAuth 调用失败(可能是游客模式):', err); } // 2. 检查用户状态 const currentUser = Parse.User.current(); const hasMobile = currentUser?.get('mobile'); const userLogin = wx.getStorageSync('userLogin'); console.log('📊 用户状态:'); console.log(' - 当前用户:', currentUser ? currentUser.id : '无'); console.log(' - 手机号:', hasMobile || '无'); console.log(' - userLogin 存储:', userLogin || '无'); // 只有同时满足以下条件才认为已完整登录: // 1. Parse.User.current() 存在 // 2. 用户有手机号 // 3. userLogin 存储存在 if (currentUser && hasMobile && userLogin) { console.log('✅ 用户已完整登录'); console.log('==========================================='); return true; } // 用户未登录或没有手机号,跳转到授权页面 console.log('⚠️ 用户未完整登录,准备跳转到授权页面'); // 构建返回 URL(登录成功后返回当前页面) const currentPath = options.path || ''; const returnUrl = currentPath; const storeId = options.storeId ? String(options.storeId) : ''; const storeName = options.storeName ? String(options.storeName) : ''; console.log('🔗 returnUrl:', returnUrl); console.log('==========================================='); wx.setStorageSync('post_auth_modal', { title: '授权成功', content: '已完成授权,正在为你打开分享内容。' }); // 跳转到授权页面,并传递 returnUrl let authPageUrl = `/components/app-auth/index?returnUrl=${returnUrl}`; if (storeId) { authPageUrl += `&storeId=${storeId}`; } if (storeName) { authPageUrl += `&storeName=${storeName}`; } wx.redirectTo({ url: authPageUrl, success: () => { console.log('✅ redirectTo 到授权页面成功'); }, fail: (err) => { console.error('❌ redirectTo 失败:', err); // 降级:使用 navigateTo wx.navigateTo({ url: authPageUrl, success: () => { console.log('✅ navigateTo 到授权页面成功'); }, fail: (err2) => { console.error('❌ navigateTo 也失败:', err2); // 最后降级:使用 reLaunch wx.reLaunch({ url: authPageUrl, success: () => { console.log('✅ reLaunch 到授权页面成功'); }, fail: (err3) => { console.error('❌ 所有跳转方式都失败:', err3); } }); } }); } }); return false; } catch (err) { console.error('❌ 检查登录状态失败:', err); console.log('==========================================='); // 出错时也跳转到授权页面 const returnUrl = options.path || ''; wx.setStorageSync('post_auth_modal', { title: '授权成功', content: '已完成授权,正在为你打开分享内容。' }); wx.redirectTo({ url: `/components/app-auth/index?returnUrl=${returnUrl}`, fail: () => { wx.navigateTo({ url: `/components/app-auth/index?returnUrl=${returnUrl}` }); } }); return false; } }, onReady: function () { }, onShow: function () { this.startTitlePolling(); }, onHide: function () { this.stopTitlePolling(); }, onUnload: function () { this.stopTitlePolling(); }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { }, /** * 处理来自 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; } // 若与当前标题一致则跳过,避免频繁触发 if (title === this.data.currentTitle) { return; } // 简单节流:500ms 内重复更新跳过 if (!this._lastTitleUpdateTs) { this._lastTitleUpdateTs = 0; } const now = Date.now(); if (now - this._lastTitleUpdateTs < 500) { return; } this._lastTitleUpdateTs = now; // 更新当前标题记录 this.setData({ currentTitle: title }); // 延迟调用微信 API 设置标题,确保页面已准备好 setTimeout(() => { wx.setNavigationBarTitle({ title: title, success: () => { console.log('✅ web-view 标题设置成功:', title); }, fail: (err) => { console.warn('⚠️ web-view 标题设置失败(可忽略):', err.errMsg); // 不影响主流程,静默失败 } }); }, 100); }, startTitlePolling: function () { this.stopTitlePolling(); }, stopTitlePolling: function () { if (this.titlePollingTimer) { clearInterval(this.titlePollingTimer); this.titlePollingTimer = null; } }, /** * 加载店铺信息并设置页面标题 */ loadAndSetStoreTitle: async function (storeId = '', storeName = '') { try { let finalTitle = storeName; if (!finalTitle) { // 如果没有传入名字,按传入的 storeId 精确查询;再不行按 company 兜底 if (storeId) { const q = new Parse.Query('ShopStore'); const s = await q.get(storeId); if (s) { // 优先使用门店地址,如果没有地址则使用门店名称 const address = s.get('address'); const name = s.get('storeName'); finalTitle = address || name || ''; console.log('📍 web-view 门店信息:', { id: storeId, name: name, address: address, displayTitle: finalTitle }); } } if (!finalTitle) { const storeQuery = new Parse.Query('ShopStore'); storeQuery.equalTo('company', company); storeQuery.ascending('score'); storeQuery.limit(1); const store = await storeQuery.first(); if (store) { // 优先使用门店地址,如果没有地址则使用门店名称 const address = store.get('address'); const name = store.get('storeName'); finalTitle = address || name || ''; } } } if (!finalTitle) return; // 使用统一的设置标题方法 this.setNavigationTitle(finalTitle); } catch (e) { console.error('设置 web-view 标题失败:', e); } } })