|
@@ -1,6 +1,12 @@
|
|
|
// common-page/pages/web-view/index.js
|
|
// common-page/pages/web-view/index.js
|
|
|
const Parse = getApp().Parse;
|
|
const Parse = getApp().Parse;
|
|
|
const company = getApp().globalData.company;
|
|
const company = getApp().globalData.company;
|
|
|
|
|
+const DEBUG_WEBVIEW = false;
|
|
|
|
|
+const debugLog = (...args) => {
|
|
|
|
|
+ if (DEBUG_WEBVIEW) {
|
|
|
|
|
+ console.log(...args);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
Page({
|
|
Page({
|
|
|
/**
|
|
/**
|
|
@@ -9,6 +15,16 @@ Page({
|
|
|
data: {
|
|
data: {
|
|
|
path: "",
|
|
path: "",
|
|
|
currentTitle: "", // 当前标题
|
|
currentTitle: "", // 当前标题
|
|
|
|
|
+ miniPayVisible: false,
|
|
|
|
|
+ miniPayPrice: 0,
|
|
|
|
|
+ miniPayTradeNo: "",
|
|
|
|
|
+ miniPayOrderId: "",
|
|
|
|
|
+ miniPayOrderType: "shopgoods",
|
|
|
|
|
+ miniPayShowType: "all",
|
|
|
|
|
+ miniPayShowBonus: false,
|
|
|
|
|
+ miniPayProfileId: "",
|
|
|
|
|
+ miniPayProcessing: false,
|
|
|
|
|
+ miniPayRequestContext: null,
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
// 标题轮询定时器
|
|
// 标题轮询定时器
|
|
@@ -18,24 +34,30 @@ Page({
|
|
|
* 生命周期函数--监听页面加载
|
|
* 生命周期函数--监听页面加载
|
|
|
*/
|
|
*/
|
|
|
onLoad: async function (options) {
|
|
onLoad: async function (options) {
|
|
|
- console.log('===========================================');
|
|
|
|
|
- console.log('======= web-view 页面加载 =======');
|
|
|
|
|
|
|
+ debugLog('======= web-view 页面加载 =======');
|
|
|
|
|
|
|
|
// 1. 先检查用户登录状态
|
|
// 1. 先检查用户登录状态
|
|
|
const loginCheck = await this.checkUserLogin(options);
|
|
const loginCheck = await this.checkUserLogin(options);
|
|
|
if (!loginCheck) {
|
|
if (!loginCheck) {
|
|
|
- console.log('⚠️ 用户未登录或没有手机号,已跳转到授权页面');
|
|
|
|
|
- console.log('===========================================');
|
|
|
|
|
|
|
+ debugLog('⚠️ 用户未登录或没有手机号,已跳转到授权页面');
|
|
|
return; // 停止后续加载
|
|
return; // 停止后续加载
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- console.log('✅ 用户已登录且有手机号,继续加载 web-view');
|
|
|
|
|
|
|
+ debugLog('✅ 用户已登录且有手机号,继续加载 web-view');
|
|
|
|
|
|
|
|
// 2. 解码 URL
|
|
// 2. 解码 URL
|
|
|
let path = decodeURIComponent(options.path || '');
|
|
let path = decodeURIComponent(options.path || '');
|
|
|
|
|
+ const isSkipAuth = options.skipAuth === 'true';
|
|
|
|
|
+ const isHomePath = /^https?:\/\/www\.yuban\.co\/home([/?#]|$)/.test(path);
|
|
|
|
|
+
|
|
|
|
|
+ // 引导页直达 H5 首页时走快速路径:
|
|
|
|
|
+ // 仅设置 web-view URL,避免额外参数拼接和 Parse 查询造成首开延迟。
|
|
|
|
|
+ if (isSkipAuth && isHomePath) {
|
|
|
|
|
+ this.setData({ path });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- console.log('原始 options.path:', options.path);
|
|
|
|
|
- console.log('解码后的 path:', path);
|
|
|
|
|
|
|
+ debugLog('原始 options.path:', options.path);
|
|
|
|
|
+ debugLog('解码后的 path:', path);
|
|
|
|
|
|
|
|
// 拼接额外参数(避免重复添加已存在的参数)
|
|
// 拼接额外参数(避免重复添加已存在的参数)
|
|
|
let hasQuery = path.indexOf('?') !== -1;
|
|
let hasQuery = path.indexOf('?') !== -1;
|
|
@@ -85,9 +107,8 @@ Page({
|
|
|
path = path + parsm;
|
|
path = path + parsm;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- console.log('最终 web-view URL:', path);
|
|
|
|
|
- console.log('URL 长度:', path.length);
|
|
|
|
|
- console.log('===========================================');
|
|
|
|
|
|
|
+ debugLog('最终 web-view URL:', path);
|
|
|
|
|
+ debugLog('URL 长度:', path.length);
|
|
|
|
|
|
|
|
this.setData({
|
|
this.setData({
|
|
|
path: path
|
|
path: path
|
|
@@ -98,7 +119,7 @@ Page({
|
|
|
const passedStoreId = options.storeId || '';
|
|
const passedStoreId = options.storeId || '';
|
|
|
// /home 场景下默认不需要门店标题:跳过 ShopStore 的 Parse 查询以提速。
|
|
// /home 场景下默认不需要门店标题:跳过 ShopStore 的 Parse 查询以提速。
|
|
|
// 这里根据 H5 path 判断:例如 https://www.yuban.co/home?token=...&skipAuth=true...
|
|
// 这里根据 H5 path 判断:例如 https://www.yuban.co/home?token=...&skipAuth=true...
|
|
|
- const isHome = /^https?:\/\/www\.yuban\.co\/home([/?#]|$)/.test(path);
|
|
|
|
|
|
|
+ const isHome = isHomePath;
|
|
|
|
|
|
|
|
if (passedStoreName) {
|
|
if (passedStoreName) {
|
|
|
this.setNavigationTitle(passedStoreName);
|
|
this.setNavigationTitle(passedStoreName);
|
|
@@ -120,28 +141,26 @@ Page({
|
|
|
*/
|
|
*/
|
|
|
checkUserLogin: async function(options) {
|
|
checkUserLogin: async function(options) {
|
|
|
try {
|
|
try {
|
|
|
- console.log('===========================================');
|
|
|
|
|
- console.log('🔍 开始检查用户登录状态...');
|
|
|
|
|
|
|
+ debugLog('🔍 开始检查用户登录状态...');
|
|
|
|
|
|
|
|
// 检查是否有 skipAuth 参数(用于跳过登录检查)
|
|
// 检查是否有 skipAuth 参数(用于跳过登录检查)
|
|
|
if (options.skipAuth === 'true') {
|
|
if (options.skipAuth === 'true') {
|
|
|
- console.log('ℹ️ 检测到 skipAuth 参数,跳过登录检查');
|
|
|
|
|
- console.log('===========================================');
|
|
|
|
|
|
|
+ debugLog('ℹ️ 检测到 skipAuth 参数,跳过登录检查');
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 检查是否是扫码进入
|
|
// 检查是否是扫码进入
|
|
|
const isScanEntry = options && (options.storeId && (options.scanCount || options.partnerId || options.userId || options.employeeId));
|
|
const isScanEntry = options && (options.storeId && (options.scanCount || options.partnerId || options.userId || options.employeeId));
|
|
|
if (isScanEntry) {
|
|
if (isScanEntry) {
|
|
|
- console.log('🚀 检测到扫码进入,准备强制登录以确保流量扣减');
|
|
|
|
|
|
|
+ debugLog('🚀 检测到扫码进入,准备强制登录以确保流量扣减');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 1. 先调用 checkAuth 初始化用户(扫码进入时强制授权)
|
|
// 1. 先调用 checkAuth 初始化用户(扫码进入时强制授权)
|
|
|
- console.log('📱 调用 checkAuth 初始化用户...');
|
|
|
|
|
|
|
+ debugLog('📱 调用 checkAuth 初始化用户...');
|
|
|
try {
|
|
try {
|
|
|
// 如果是扫码进入,则强制授权;否则不强制(由后面逻辑决定)
|
|
// 如果是扫码进入,则强制授权;否则不强制(由后面逻辑决定)
|
|
|
await getApp().checkAuth(isScanEntry);
|
|
await getApp().checkAuth(isScanEntry);
|
|
|
- console.log('✅ checkAuth 调用成功');
|
|
|
|
|
|
|
+ debugLog('✅ checkAuth 调用成功');
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
console.warn('⚠️ checkAuth 调用失败:', err);
|
|
console.warn('⚠️ checkAuth 调用失败:', err);
|
|
|
}
|
|
}
|
|
@@ -151,36 +170,34 @@ Page({
|
|
|
const hasMobile = currentUser?.get('mobile');
|
|
const hasMobile = currentUser?.get('mobile');
|
|
|
const userLogin = wx.getStorageSync('userLogin');
|
|
const userLogin = wx.getStorageSync('userLogin');
|
|
|
|
|
|
|
|
- console.log('📊 用户状态:');
|
|
|
|
|
- console.log(' - 当前用户:', currentUser ? currentUser.id : '无');
|
|
|
|
|
- console.log(' - 手机号:', hasMobile || '无');
|
|
|
|
|
- console.log(' - userLogin 存储:', userLogin || '无');
|
|
|
|
|
|
|
+ debugLog('📊 用户状态:');
|
|
|
|
|
+ debugLog(' - 当前用户:', currentUser ? currentUser.id : '无');
|
|
|
|
|
+ debugLog(' - 手机号:', hasMobile || '无');
|
|
|
|
|
+ debugLog(' - userLogin 存储:', userLogin || '无');
|
|
|
|
|
|
|
|
// 只有同时满足以下条件才认为已完整登录:
|
|
// 只有同时满足以下条件才认为已完整登录:
|
|
|
// 1. Parse.User.current() 存在
|
|
// 1. Parse.User.current() 存在
|
|
|
// 2. 用户有手机号
|
|
// 2. 用户有手机号
|
|
|
// 3. userLogin 存储存在
|
|
// 3. userLogin 存储存在
|
|
|
if (currentUser && hasMobile && userLogin) {
|
|
if (currentUser && hasMobile && userLogin) {
|
|
|
- console.log('✅ 用户已完整登录');
|
|
|
|
|
- console.log('===========================================');
|
|
|
|
|
|
|
+ debugLog('✅ 用户已完整登录');
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 用户未登录或没有手机号,跳转到授权页面
|
|
// 用户未登录或没有手机号,跳转到授权页面
|
|
|
- console.log('⚠️ 用户未完整登录,准备跳转到授权页面');
|
|
|
|
|
|
|
+ debugLog('⚠️ 用户未完整登录,准备跳转到授权页面');
|
|
|
|
|
|
|
|
// 构建返回 URL(登录成功后返回当前页面)
|
|
// 构建返回 URL(登录成功后返回当前页面)
|
|
|
const currentPath = options.path || '';
|
|
const currentPath = options.path || '';
|
|
|
const returnUrl = encodeURIComponent(currentPath);
|
|
const returnUrl = encodeURIComponent(currentPath);
|
|
|
|
|
|
|
|
- console.log('🔗 returnUrl:', returnUrl);
|
|
|
|
|
- console.log('===========================================');
|
|
|
|
|
|
|
+ debugLog('🔗 returnUrl:', returnUrl);
|
|
|
|
|
|
|
|
// 跳转到授权页面,并传递 returnUrl
|
|
// 跳转到授权页面,并传递 returnUrl
|
|
|
wx.redirectTo({
|
|
wx.redirectTo({
|
|
|
url: `/components/app-auth/index?returnUrl=${returnUrl}`,
|
|
url: `/components/app-auth/index?returnUrl=${returnUrl}`,
|
|
|
success: () => {
|
|
success: () => {
|
|
|
- console.log('✅ redirectTo 到授权页面成功');
|
|
|
|
|
|
|
+ debugLog('✅ redirectTo 到授权页面成功');
|
|
|
},
|
|
},
|
|
|
fail: (err) => {
|
|
fail: (err) => {
|
|
|
console.error('❌ redirectTo 失败:', err);
|
|
console.error('❌ redirectTo 失败:', err);
|
|
@@ -189,7 +206,7 @@ Page({
|
|
|
wx.navigateTo({
|
|
wx.navigateTo({
|
|
|
url: `/components/app-auth/index?returnUrl=${returnUrl}`,
|
|
url: `/components/app-auth/index?returnUrl=${returnUrl}`,
|
|
|
success: () => {
|
|
success: () => {
|
|
|
- console.log('✅ navigateTo 到授权页面成功');
|
|
|
|
|
|
|
+ debugLog('✅ navigateTo 到授权页面成功');
|
|
|
},
|
|
},
|
|
|
fail: (err2) => {
|
|
fail: (err2) => {
|
|
|
console.error('❌ navigateTo 也失败:', err2);
|
|
console.error('❌ navigateTo 也失败:', err2);
|
|
@@ -198,7 +215,7 @@ Page({
|
|
|
wx.reLaunch({
|
|
wx.reLaunch({
|
|
|
url: `/components/app-auth/index?returnUrl=${returnUrl}`,
|
|
url: `/components/app-auth/index?returnUrl=${returnUrl}`,
|
|
|
success: () => {
|
|
success: () => {
|
|
|
- console.log('✅ reLaunch 到授权页面成功');
|
|
|
|
|
|
|
+ debugLog('✅ reLaunch 到授权页面成功');
|
|
|
},
|
|
},
|
|
|
fail: (err3) => {
|
|
fail: (err3) => {
|
|
|
console.error('❌ 所有跳转方式都失败:', err3);
|
|
console.error('❌ 所有跳转方式都失败:', err3);
|
|
@@ -213,7 +230,7 @@ Page({
|
|
|
|
|
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
console.error('❌ 检查登录状态失败:', err);
|
|
console.error('❌ 检查登录状态失败:', err);
|
|
|
- console.log('===========================================');
|
|
|
|
|
|
|
+ debugLog('checkUserLogin error');
|
|
|
|
|
|
|
|
// 出错时也跳转到授权页面
|
|
// 出错时也跳转到授权页面
|
|
|
const returnUrl = options.path ? encodeURIComponent(options.path) : '';
|
|
const returnUrl = options.path ? encodeURIComponent(options.path) : '';
|
|
@@ -275,10 +292,16 @@ Page({
|
|
|
|
|
|
|
|
// 找到最后一个标题更新消息
|
|
// 找到最后一个标题更新消息
|
|
|
let lastTitleMessage = null;
|
|
let lastTitleMessage = null;
|
|
|
|
|
+ let lastPayMessage = null;
|
|
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
|
const msg = messages[i];
|
|
const msg = messages[i];
|
|
|
if (msg.type === 'updateTitle' && msg.title) {
|
|
if (msg.type === 'updateTitle' && msg.title) {
|
|
|
lastTitleMessage = msg;
|
|
lastTitleMessage = msg;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!lastPayMessage && msg.type === 'requestMiniPay') {
|
|
|
|
|
+ lastPayMessage = msg;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (lastTitleMessage && lastPayMessage) {
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -287,11 +310,127 @@ Page({
|
|
|
if (lastTitleMessage) {
|
|
if (lastTitleMessage) {
|
|
|
this.setNavigationTitle(lastTitleMessage.title);
|
|
this.setNavigationTitle(lastTitleMessage.title);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ if (lastPayMessage) {
|
|
|
|
|
+ this.handleMiniPayRequest(lastPayMessage.payload || {});
|
|
|
|
|
+ }
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('❌ 处理消息失败:', error);
|
|
console.error('❌ 处理消息失败:', error);
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
|
|
+ normalizeMiniPayPayload(payload = {}) {
|
|
|
|
|
+ const price = Number(payload.price);
|
|
|
|
|
+ return {
|
|
|
|
|
+ tradeNo: payload.tradeNo || '',
|
|
|
|
|
+ price: Number.isFinite(price) ? price : 0,
|
|
|
|
|
+ orderId: payload.orderId || '',
|
|
|
|
|
+ orderType: payload.orderType || 'shopgoods',
|
|
|
|
|
+ showType: payload.showType || 'all',
|
|
|
|
|
+ showBonus: !!payload.showBonus,
|
|
|
|
|
+ profileId: payload.profileId || '',
|
|
|
|
|
+ scene: payload.scene || '',
|
|
|
|
|
+ bizId: payload.bizId || '',
|
|
|
|
|
+ callbackUrl: payload.callbackUrl || ''
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleMiniPayRequest(payload = {}) {
|
|
|
|
|
+ const parsed = this.normalizeMiniPayPayload(payload);
|
|
|
|
|
+ if (!parsed.tradeNo) {
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '支付参数缺少tradeNo',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (parsed.price < 0) {
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '支付金额无效',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.data.miniPayProcessing) {
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '支付进行中,请稍后',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ miniPayVisible: false,
|
|
|
|
|
+ miniPayPrice: parsed.price,
|
|
|
|
|
+ miniPayTradeNo: parsed.tradeNo,
|
|
|
|
|
+ miniPayOrderId: parsed.orderId,
|
|
|
|
|
+ miniPayOrderType: parsed.orderType,
|
|
|
|
|
+ miniPayShowType: parsed.showType,
|
|
|
|
|
+ miniPayShowBonus: parsed.showBonus,
|
|
|
|
|
+ miniPayProfileId: parsed.profileId,
|
|
|
|
|
+ miniPayProcessing: true,
|
|
|
|
|
+ miniPayRequestContext: parsed
|
|
|
|
|
+ }, () => {
|
|
|
|
|
+ const paymentComp = this.selectComponent('#miniPayComponent');
|
|
|
|
|
+ if (!paymentComp || typeof paymentComp.pay !== 'function') {
|
|
|
|
|
+ this.setData({ miniPayProcessing: false });
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '支付组件未就绪',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ paymentComp.pay();
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ onMiniPayResult(e) {
|
|
|
|
|
+ const detail = e.detail || {};
|
|
|
|
|
+ const payState = detail.params;
|
|
|
|
|
+ const result = {
|
|
|
|
|
+ type: 'miniPayResult',
|
|
|
|
|
+ payload: {
|
|
|
|
|
+ success: payState === 'ok',
|
|
|
|
|
+ tradeNo: detail.no || this.data.miniPayTradeNo,
|
|
|
|
|
+ payType: detail.type || 'wxpay',
|
|
|
|
|
+ cancelled: payState === 'Cancel the payment',
|
|
|
|
|
+ errMsg: payState && payState !== 'ok' && payState !== 'Cancel the payment'
|
|
|
|
|
+ ? (typeof payState === 'string' ? payState : (payState.errMsg || ''))
|
|
|
|
|
+ : ''
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ miniPayVisible: false,
|
|
|
|
|
+ miniPayProcessing: false
|
|
|
|
|
+ });
|
|
|
|
|
+ this.notifyH5MiniPayResult(result);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ notifyH5MiniPayResult(result) {
|
|
|
|
|
+ const ctx = this.data.miniPayRequestContext || {};
|
|
|
|
|
+ console.log('miniPayResult:', result);
|
|
|
|
|
+
|
|
|
|
|
+ // web-view 不支持直接由小程序向 H5 反向 postMessage,
|
|
|
|
|
+ // 若 H5 传入 callbackUrl,则通过刷新 web-view URL 回传支付结果。
|
|
|
|
|
+ if (!ctx.callbackUrl) return;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const separator = ctx.callbackUrl.includes('?') ? '&' : '?';
|
|
|
|
|
+ const query = [
|
|
|
|
|
+ 'miniPayResult=1',
|
|
|
|
|
+ `success=${result.payload.success ? 1 : 0}`,
|
|
|
|
|
+ `tradeNo=${encodeURIComponent(result.payload.tradeNo || '')}`,
|
|
|
|
|
+ `payType=${encodeURIComponent(result.payload.payType || '')}`,
|
|
|
|
|
+ `cancelled=${result.payload.cancelled ? 1 : 0}`,
|
|
|
|
|
+ `errMsg=${encodeURIComponent(result.payload.errMsg || '')}`
|
|
|
|
|
+ ].join('&');
|
|
|
|
|
+ const callbackPath = `${ctx.callbackUrl}${separator}${query}`;
|
|
|
|
|
+ this.setData({ path: callbackPath });
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('❌ 回传支付结果失败:', error);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 设置导航栏标题(统一方法)
|
|
* 设置导航栏标题(统一方法)
|
|
|
*/
|
|
*/
|