Files
lucky_shop/App.vue

725 lines
27 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script>
import auth from 'common/js/auth.js';
import configExternal, { themeConfig, privacyConfig } from 'common/js/config-external.js'
import Lang from 'common/js/lang.js';
import ThemeOverride from 'common/js/theme-override.js';
import PrivacyManager from 'common/js/privacy.js';
import splashState from 'common/js/splash-state.js';
import {
Weixin
} from 'common/js/wx-jssdk.js';
// ============================================================
// 隐私弹窗 URL/路由规则 (H5 + 微信小程序 + 鸿蒙元服务)
//
// 配置来源 (沿用 config-external.js 同构模式):
// 1. 本地兜底: privacyConfig.urlRulesBase (与 langConfig/themeConfig 同构)
// 2. 远程 patch: /api/config/init data.privacy_url_rules (由 store/init 融合)
// 融合策略 (同上一轮):
// - mode: 远程有值则覆盖, 否则用本地
// - patterns: 按 patch.merge 字段决定 (replace 默认 / union / intersect)
// 匹配: 子串匹配
// H5: pathname + search + hash
// 小程序/鸿蒙: 当前页面 route + query
// 命中白名单后, 仍会走 storage / 平台级隐私检查, 已同意则不再弹
// ============================================================
const PRIVACY_URL_RULES_BASE = privacyConfig.urlRulesBase
// 启动期配置注入: 把 privacyConfig 的 bootTimeout / waitChannels / signSecret 灌进 PrivacyManager
// 这样 PrivacyManager.show() 即便被业务侧独立调用, 也能感知启动期资源就绪状态
// 签名密钥注入: 远程 patch 必须通过 verifyRemotePatch() 才允许 applyWithMerge
PrivacyManager.setExternalBootTimeout(configExternal.getPrivacyBootTimeout())
PrivacyManager.setWaitChannels(privacyConfig.waitChannels || {})
PrivacyManager.setSignSecret(privacyConfig.signSecret || '')
// 登记本地 base 规则, 供后续远程 patch 融合时读取
// 必须在 setStorageKey / loadPersistedRules 之前完成, 因为 store/init action
// 会通过 PrivacyManager.__getBaseRules() 读取这个 base
PrivacyManager.setBaseRules(PRIVACY_URL_RULES_BASE)
// uniacid 隔离的持久化 key
// 关键: 持久化必须基于 uniacid 隔离, 否则多租户切换会读到对方租户的规则
// 无 uniacid 时降级到 _anon (使用 localStorage 之外的隔离, 避免与任何具体租户冲突)
const _uniacid = uni.getStorageSync('uniacid')
const _persistKey = _uniacid
? ('privacy_url_rules_' + _uniacid)
: 'privacy_url_rules__anon'
PrivacyManager.setStorageKey(_persistKey)
// 1) 先尝试恢复上次的"已融合规则" (跨冷启动)
// 无持久化值 → 退化为 base
const _persisted = PrivacyManager.loadPersistedRules()
if (_persisted && _persisted.mode && Array.isArray(_persisted.patterns)) {
PrivacyManager.setUrlRules(_persisted)
} else {
// 2) 用本地 base 兜底
PrivacyManager.setUrlRules(PRIVACY_URL_RULES_BASE)
}
// 业务侧可监听: 远程下发完成后, 会触发 'privacy:rules-updated'
uni.$on && uni.$on('privacy:rules-updated', (info) => {
// 只在远程下发时打印, 避免被本地 base 加载/持久化恢复时的 emit 噪音淹没
if (info && info.source === 'remote' && console && console.log) {
console.log('[privacy] rules updated by remote, mode=' + info.mode + ', patterns=' + info.patterns.length)
}
})
/**
* 取 onLaunch options 作为首次启动的兜底路径
* H5: options.path/query 不一定存在, 此时用 window.location
* 小程序/鸿蒙: options.path 是启动路径, options.query 是启动参数
* @param {Object} launchOptions onLaunch 的 options
* @returns {Object} urlFallback
*/
function _buildPrivacyUrlFallback(launchOptions) {
const fallback = { pathname: '', search: '', hash: '', route: '', query: null }
// #ifdef H5
if (typeof window !== 'undefined' && window.location) {
fallback.pathname = window.location.pathname || ''
fallback.search = window.location.search || ''
fallback.hash = window.location.hash || ''
}
// #endif
// #ifndef H5
if (launchOptions && launchOptions.path) {
fallback.route = launchOptions.path
fallback.query = launchOptions.query || null
}
// #endif
return fallback
}
// ============================================================
// ✅ 推荐码 / 邀请码(来源追踪)全局兜底
// 规则:只写非空值,不主动清空(diy-form 提交成功后自己清)
// ============================================================
const FORM_INVITE_KEY = 'form_invite_pending_code'
function _persistInviteCodeFromOptions(options) {
try {
var code = ''
// 1) onLaunch/onShow options.query:优先 recommend_code,次选 invite(历史兼容)
if (options && options.query) {
if (options.query.recommend_code) {
code = String(options.query.recommend_code)
} else if (options.query.invite) {
code = String(options.query.invite)
}
}
// 2) H5 兜底:从当前 location.search 取(同样优先 recommend_code)
if (!code) {
// #ifdef H5
try {
var sp = (new URL(window.location.href)).searchParams
var s1 = sp.get('recommend_code')
var s2 = sp.get('invite')
if (s1) code = String(s1)
else if (s2) code = String(s2)
} catch (e) {}
// #endif
}
// 3) 只写非空,不擦除已有暂存
if (code) {
uni.setStorageSync(FORM_INVITE_KEY, code)
}
} catch (e) {
// 极端失败静默,diy-form 仍有 P2/P3 兜底读取
}
}
// ============================================================
// 主题/语言启动
// ============================================================
export default {
mixins: [auth],
data() {
return {
// remote-only 模式下,业务可通过 await getApp().$langReady 等待语言包就绪
$langReady: Promise.resolve({ ok: true, source: 'local' })
};
},
// 同步钩子:在 page mounted 之前同步设置隐私弹窗的 skip 状态
// 必须在 onLaunch (async) 之前跑,否则 onLaunch 第一个 await 让出事件循环时
// privacy-popup.vue 已经挂载,会走 consumePendingShow 兜底分支把弹窗弹出来
created() {
if (!PrivacyManager.shouldShowByUrlRule()) {
PrivacyManager.markSkipped()
}
},
onLaunch: async function(options) {
// ============================================================
// 主题提前加载(关键):让首屏渲染前 CSS 变量已挂上,避免闪一下
// ============================================================
// ---- 0) 同步预热: 立即读 storage 拿 preset,触发 require(不 await)
// 目的: 让 loadTheme 的 require 提前进入 microtask,后续 await 几乎瞬时
const cached = uni.getStorageSync('themeStyle');
let presetName = themeConfig.defaultTheme;
if (cached) {
presetName = (typeof cached === 'object') ? cached.name : cached;
}
// 触发 preset require(不 await,缓存 promise 到 configExternal.loadedConfigs)
configExternal.loadTheme(presetName);
// ---- 1) uniacid 立即写入(后续所有 HTTP 都要用到)
const uniacidChanged = options.query && options.query.uniacid && String(uni.getStorageSync('uniacid') || '') !== String(options.query.uniacid);
if (options.query && options.query.uniacid) {
uni.setStorageSync('uniacid', options.query.uniacid);
}
// 关键: 即便 uniacid 未变化 (首次启动 options.query 没传 uniacid),
// 也要尝试从 storage 拿真实 uniacid 并设置 PrivacyManager 的 persist key
// 否则 App.vue 顶层用 _anon 占位, 后续 persistRules 写到 _anon key 下,
// 下次冷启动 (有 uniacid) 切到 uniacid key 时读不到上次结果
try {
const _nowUniacid = uni.getStorageSync('uniacid')
const _newPersistKey = _nowUniacid
? ('privacy_url_rules_' + _nowUniacid)
: 'privacy_url_rules__anon'
if (_newPersistKey !== _persistKey) {
PrivacyManager.setStorageKey(_newPersistKey)
const _newPersisted = PrivacyManager.loadPersistedRules()
if (_newPersisted && _newPersisted.mode && Array.isArray(_newPersisted.patterns)) {
PrivacyManager.setUrlRules(_newPersisted)
} else {
PrivacyManager.setUrlRules(PRIVACY_URL_RULES_BASE)
}
}
} catch (e) {}
if (uniacidChanged) {
try { Lang.onUniacidChange(); } catch (e) {}
try { ThemeOverride.onUniacidChange(); } catch (e) {}
// 切租户: 切换 PrivacyManager 持久化 key 并重新应用本地 base
// (远程 patch 由后续 /api/config/init 重新下发并融合)
try {
const newUniacid = String(options.query.uniacid)
const newPersistKey = newUniacid
? ('privacy_url_rules_' + newUniacid)
: 'privacy_url_rules__anon'
PrivacyManager.setStorageKey(newPersistKey)
PrivacyManager.setBaseRules(PRIVACY_URL_RULES_BASE)
// 切租户: 重新加载该租户上次持久化的规则
// 无持久化值则退化为 base
const _newPersisted = PrivacyManager.loadPersistedRules()
if (_newPersisted && _newPersisted.mode && Array.isArray(_newPersisted.patterns)) {
PrivacyManager.setUrlRules(_newPersisted)
} else {
PrivacyManager.setUrlRules(PRIVACY_URL_RULES_BASE)
}
} catch (e) {}
try { uni.$emit && uni.$emit('uniacid:changed'); } catch (e) {}
}
// remote-only 模式:
// H5: main.js 已 await boot 完成, 这里秒回 (splash 也由 main.js 全权负责)
// 小程序/鸿蒙: main.js 同步 $mount() 之前已经调用 splashState.show(),
// 这里不需要重复调用
const langBoot = Lang.boot();
this.$langReady = langBoot;
// ---- 2) 启动期关键资源就绪 (并行 + 显式 await)
// 隐私弹窗的诉求: 必须在 i18n + theme + remote-config 三态都就绪后才弹
// 这里把三个通道收敛为一个 await 链, 不再用 fire-and-forget
// 任何通道自身有超时保护 (Lang.bootTimeout=langConfig.remote.bootTimeout=5000,
// ThemeOverride.bootTimeout=themeConfig.overrideRemote.bootTimeout=5000)
// 总超时熔断走 configExternal.getPrivacyBootTimeout() (默认取两者最大值+1000ms)
// 业务侧想"宁愿晚一点也要全资源就绪" → 调大 privacyConfig.bootTimeout
// 业务侧想"快速失败" → 调小或关闭 privacyConfig.waitChannels
const _i18nDone = langBoot.then((res) => {
// 进度上报: i18n 完成 → 30%
try { splashState.reportProgress('lang', { source: 'channel' }); } catch (e) {}
// i18n fallback toast (原逻辑保留)
if (res && res.status === 'fail' && res.reason && res.reason !== 'unknown') {
if (globalThis.__i18nFallbackToastShown) return res;
globalThis.__i18nFallbackToastShown = true;
setTimeout(() => {
try {
uni.showToast({
title: `Failed to load i18n. ${res.reason} Please check your network and try again.`,
icon: 'none',
duration: 10000,
});
} catch (e) {}
}, 3000);
}
return res;
}, (e) => {
console.warn('[i18n] boot fail', e);
try { splashState.reportProgress('lang', { source: 'error' }); } catch (e) {}
return { ok: false, source: 'remote-failed', status: 'fail', reason: 'exception', lang: '' };
})
const _themeFirstApply = this.$store.dispatch('applyThemeWithRemote', presetName)
.then(() => {
try { splashState.reportProgress('theme', { source: 'channel' }); } catch (e) {}
})
.catch(e => {
console.warn('[theme] pre-apply fail', e);
try { splashState.reportProgress('theme', { source: 'error' }); } catch (e) {}
return null
})
const _themeOverride = ThemeOverride.boot()
.then(() => {
try { splashState.reportProgress('override', { source: 'channel' }); } catch (e) {}
})
.catch(e => {
console.warn('[theme] boot fail', e);
try { splashState.reportProgress('override', { source: 'error' }); } catch (e) {}
return null
})
// /api/config/init 走 fire-and-forget:
// - race 不再等它, popup 可以更早弹出
// - 远程 patch (privacy_url_rules 等) 后续异步到达时通过 applyWithMerge 触发双阶段弹窗
// - 失败也不阻塞 boot 链路
//
// 进度上报单独走 (展示效果, 不依赖 _remoteConfig resolve)
this.$store.dispatch('init').then(() => {
try { splashState.reportProgress('config', { source: 'channel' }); } catch (e) {}
}).catch(e => {
console.warn('[config] init fail', e);
try { splashState.reportProgress('config', { source: 'error' }); } catch (e) {}
})
const _remoteConfig = Promise.resolve(null) // 占位 promise, race 不再等 init
// 注入启动期等待通道 → ConfigExternal 持有
// PrivacyManager.show() 时通过 getPrivacyReadyGuard(waitChannels) 自动拼成 readyGuard
configExternal.setReadyWaiters({
lang: _i18nDone,
theme: _themeFirstApply,
themeOverride: _themeOverride,
remoteConfig: _remoteConfig,
})
const _privacyBootTimeout = configExternal.getPrivacyBootTimeout()
const _bootRaceResult = await Promise.race([
Promise.all([_i18nDone, _themeFirstApply, _themeOverride, _remoteConfig]),
new Promise(resolve => setTimeout(() => resolve('timeout'), _privacyBootTimeout))
])
if (_bootRaceResult === 'timeout') {
// race 超时上报 (source: 'timeout', 不允许 percent 后退, 已有最大值为准)
try { splashState.reportProgress('config', { percent: 80, source: 'timeout' }); } catch (e) {}
}
// privacy 阶段开始上报
try { splashState.reportProgress('privacy', { source: 'channel' }); } catch (e) {}
// 隐私协议弹窗: 启动期关键资源 (i18n/theme/remote-config) 就绪后触发
// 通过 PrivacyManager → uni.$emit('privacy:show') 通知当前页面的 privacy-popup 组件
// 只弹一次: 同意后写 storage,后续不再弹
// 异步查询平台隐私状态(微信/快应用), H5 同步返回
// H5 直访特定 URL(子串匹配)时跳过本次弹窗,不写 storage,下次正常访问其他页面还会弹
// urlFallback: 首次启动 onLaunch 时 getCurrentPages 还没页面, 用 onLaunch options 兜底
// readyGuard: 双保险 — 若调用方没传, PrivacyManager 内部从 _externalReadyGuard 取
// (= ConfigExternal.getPrivacyReadyGuard(privacyConfig.waitChannels))
// 二次 await 仍保留: race 后还有可能某个通道没真完成, 二次 await 兜底
//
// 鸿蒙元服务(双轨制):
// 已接入平台隐私托管, 鸿蒙系统自动弹出系统级隐私弹窗.
// 应用层不调 PrivacyManager.show() (会和系统弹窗叠加被驳回).
// #ifdef MP-HARMONY
// 鸿蒙: 走平台隐私托管, markSkipped 写 storage
try {
await Promise.all([_i18nDone, _themeFirstApply, _themeOverride, _remoteConfig])
PrivacyManager.markSkipped()
} catch (e) { console.warn('[privacy] MP-HARMONY markSkipped fail', e); }
// #endif
// #ifndef MP-HARMONY
try {
await PrivacyManager.show({
urlFallback: _buildPrivacyUrlFallback(options),
readyGuard: Promise.all([_i18nDone, _themeFirstApply, _themeOverride, _remoteConfig]),
})
} catch (e) { console.warn('[privacy] show fail', e); }
// #endif
// splash 必须在隐私弹窗决策后再 hide (否则 splash 关掉瞬间弹窗还没渲染, 视觉闪一下)
// mode='fast': 隐私弹窗即将出现, splash 用 80ms 短动画, 让 modal 更快接管视觉
// 避免 splash 和弹窗同时显示造成的"双遮罩"感, 减少约 400ms 启动时间
// 只有 remote-only 模式才用了 splash (其他场景 src 模式不需要等远端)
//
// 关键 (鸿蒙 + 其他平台): 这里只发"软隐藏"信号, splash 实际关闭要等
// splashState._softHide() 看到 _hasMounted && _pageReady 才真关。
// _pageReady 由首页 diy-group 渲染完时调 notifyPageReady() 触发。
// 这样保证 splash **一直显示**, 直到首页 diy 内容真的渲染好,
// 避免 splash 关闭后到 diy 内容出现之间出现白屏。
//
// 鸿蒙端 (mode='strict'):
// 鸿蒙系统弹窗是用户视觉锚点, 应用层 splash 必须**真等到**diy 渲染好
// 才能关, 不走 MOUNT_GRACE_MS/PAGE_READY_GRACE_MS 软隐藏兜底
// (软隐藏兜底 1+2=3s 经常不够用, 会导致 splash 提前 hide → 白屏)
// strict 模式只受 DEFAULT_TIMEOUT=12s 总超时兜底, 实际由 notifyPageReady() 触发
// #ifndef H5
const _needSplash = typeof Lang !== 'undefined' && Lang.isRemoteOnly && Lang.isRemoteOnly();
if (_needSplash) {
// 所有平台都用 strict 模式: 不走 1+2s 软隐藏兜底
// splash 真等到 notifyPageReady (diy 渲染好) 或 notifyDiyLoadError (diy 错误) 才关
// DEFAULT_TIMEOUT=12s 总超时仍然兜底
// 避免 splash 提前关导致白屏
splashState.hide({ reason: 'boot-complete', mode: 'strict' });
}
// #endif
// ---- 3) 远端就绪后,主题再 apply 一次(preset + remote 合并 )
// 这一次 setThemeStyle + themeColorSet 是同步路径(preset 已在 cache,remote 也在 cache)
// 如果跟 step 2 的 apply 值相同,Vuex 会重复 commit 但页面无视觉变化
await this.$store.dispatch('applyThemeWithRemote', presetName);
// ============================================================
// 后续: 监听器 / 平台条件 / 其它 storage commit(全部同步/轻量)
// ============================================================
// 监听远端翻译更新事件(管理后台改了词条后由后端推送 / 主动 reload)
// 注意:lang.js 内 emitUpdated 已经 commit 了 setComponentRefresh,这里
// 只做副作用(navigation title 等),不重复 forceUpdate
uni.$on && uni.$on('lang:updated', (info) => {
if (info && info.lang) {
Lang.refresh(true); // true = 显式推送 emitUpdated(虽然 loadOverrides 内部已发,这里保险起见)
}
});
// 监听远端主题覆写更新(管理后台改了 token 后,本地 Vuex 重新合并并应用)
// 关键: 走 applyThemeWithRemote action,保留原 setThemeStyle/themeColorSet 链路不动
uni.$on && uni.$on('theme:updated', () => {
// 重新从 storage 读 preset(可能用户在管理页切了预设)
const cur = uni.getStorageSync('themeStyle');
const curName = (cur && typeof cur === 'object') ? cur.name : (cur || 'default');
this.$store.dispatch('applyThemeWithRemote', curName);
});
// 监听 uniacid 切换: 通知主题运行时清旧租户缓存,下次 refresh 拿到新值
uni.$on && uni.$on('uniacid:changed', () => {
try { ThemeOverride.onUniacidChange(); } catch (e) {}
});
// i18n 远端拉取失败的 toast 提示已迁移到上方 boot() 完成的 then() 中,
// 直接根据 boot 返回值判断,不再依赖 lang:fallback 事件
uni.hideTabBar();
// #ifdef MP
if (uni.getUpdateManager) {
const updateManager = uni.getUpdateManager();
updateManager.onCheckForUpdate(function(res) {
// 请求完新版本信息的回调
});
updateManager.onUpdateReady(function(res) {
uni.showModal({
title: Lang.lang('common.updateTip'),
content: Lang.lang('common.updateReady'),
success(res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate();
}
}
});
});
updateManager.onUpdateFailed(function(res) {
// 新的版本下载失败
});
}
// #endif
// #ifdef H5
if (this.$util.getDeviceInfo().platform == 'ios') {
uni.setStorageSync('initUrl', location.href);
}
// #endif
if (uni.onNetworkStatusChange) {
uni.onNetworkStatusChange(function(res) {
if (!res.isConnected) {
uni.showModal({
title: Lang.lang('common.networkLost'),
content: Lang.lang('common.networkCheck'),
showCancel: false
});
}
});
}
// /api/config/init 已在 step 2.5 await 过 (含 800ms 超时), 此处不再重复触发
// 原 fire-and-forget 注释: 不阻塞后续同步 commit
// 现在 init 已被升级为 "首次配置等待通道", 这里留空以避免重复 dispatch
// 插件是否存在
if (uni.getStorageSync('addonIsExist')) {
this.$store.commit('setAddonIsExist', uni.getStorageSync('addonIsExist'));
}
// 默认图
if (uni.getStorageSync('defaultImg')) {
this.$store.commit('setDefaultImg', uni.getStorageSync('defaultImg'));
}
// 站点信息
if (uni.getStorageSync('siteInfo')) {
this.$store.commit('setSiteInfo', uni.getStorageSync('siteInfo'));
}
// 门店配置
if (uni.getStorageSync('globalStoreConfig')) {
this.$store.commit('setGlobalStoreConfig', uni.getStorageSync('globalStoreConfig'));
}
// 门店信息
if (uni.getStorageSync('globalStoreInfo')) {
this.$store.commit('setGlobalStoreInfo', uni.getStorageSync('globalStoreInfo'));
}
// 默认门店信息
if (uni.getStorageSync('defaultStoreInfo')) {
this.$store.commit('setDefaultStoreInfo', uni.getStorageSync('defaultStoreInfo'));
}
// 客服配置
if (uni.getStorageSync('servicerConfig')) {
this.$store.commit('setServicerConfig', uni.getStorageSync('servicerConfig'));
}
// 企业微信配置
if (uni.getStorageSync('wxworkConfig')) {
this.$store.commit('setWxworkConfig', uni.getStorageSync('wxworkConfig'));
}
// 版权信息
if (uni.getStorageSync('copyright')) {
this.$store.commit('setCopyright', uni.getStorageSync('copyright'));
}
// 地址配置
if (uni.getStorageSync('mapConfig')) {
this.$store.commit('setMapConfig', uni.getStorageSync('mapConfig'));
}
if (uni.getStorageSync('token')) {
this.$store.commit('setToken', uni.getStorageSync('token'));
}
// 会员信息
if (uni.getStorageSync('memberInfo')) {
this.$store.commit('setMemberInfo', uni.getStorageSync('memberInfo'));
// 查询购物车信息
this.$store.dispatch('getCartNumber');
}
// #ifdef H5
// 自动授权登录
// 未登录情况下
if (!uni.getStorageSync('memberInfo')) {
this.getAuthInfo();
}
if (this.$store.state.token) {
this.$api.sendRequest({
url: '/api/member/info',
success: (res) => {
if (res.code >= 0) {
this.$store.commit('setMemberInfo', res.data);
}
}
});
}
// #endif
// #ifdef MP-ALIPAY
if (options.query && options.query.m) uni.setStorageSync('source_member', options.query.m);
// #endif
},
onShow: function(options) {
// #ifdef MP
// 自动授权登录
this.getAuthInfo();
if (this.$store.state.token) {
this.$api.sendRequest({
url: '/api/member/info',
success: (res) => {
if (res.code >= 0) {
this.$store.commit('setMemberInfo', res.data);
}
}
});
}
// #endif
// #ifdef MP-ALIPAY
if (options.query && options.query.m) uni.setStorageSync('source_member', options.query.m);
// #endif
// ✅ 邀请码(来源追踪):onLaunch 冷启动兜底解析 URL query → storage
_persistInviteCodeFromOptions(options);
},
onShow: function(options) {
// ✅ 邀请码(来源追踪):onShow 热启动/后台切回 兜底解析
// 注:onShow 的 options 仅部分平台(MP-WEIXIN, MP-HARMONY, MP-ALIPAY 等)提供 query
// 若 options 为空,则 helper 内部自动 fallback 到 H5 location.search 或已有 storage
_persistInviteCodeFromOptions(options || {});
// #ifdef MP
// 小程序端:每次回到前台 → 自动授权登录刷新会话 token + memberInfo(与行业模板一致)
if (typeof this.getAuthInfo === 'function') {
this.getAuthInfo();
}
if (this.$store && this.$store.state && this.$store.state.token && this.$api) {
this.$api.sendRequest({
url: '/api/member/info',
success: (res) => {
if (res && res.code >= 0 && res.data && this.$store && this.$store.commit) {
this.$store.commit('setMemberInfo', res.data);
}
}
});
}
// #endif
// #ifdef MP-ALIPAY
if (options && options.query && options.query.m) uni.setStorageSync('source_member', options.query.m);
// #endif
},
onHide: function() {},
methods: {
/**
* 获取授权信息
*/
getAuthInfo() {
// #ifdef H5
if (this.$util.isWeiXin()) {
this.$util.getUrlCode(urlParams => {
if (urlParams.source_member) uni.setStorageSync('source_member', urlParams
.source_member);
if (urlParams.code == undefined) {
this.$api.sendRequest({
url: '/wechat/api/wechat/authcode',
data: {
redirect_url: location.href,
scopes: 'snsapi_userinfo'
},
success: res => {
if (res.code >= 0) {
location.href = res.data;
}
}
});
} else {
this.$api.sendRequest({
url: '/wechat/api/wechat/authcodetoopenid',
data: {
code: urlParams.code
},
success: res => {
if (res.code >= 0) {
let data = {};
if (res.data.openid) data.wx_openid = res.data.openid;
if (res.data.unionid) data.wx_unionid = res.data.unionid;
if (res.data.userinfo) Object.assign(data, res.data
.userinfo);
this.authLogin(data);
}
}
});
}
});
}
// #endif
// #ifdef MP
this.getCode(data => {
this.authLogin(data, 'authOnlyLogin');
});
// #endif
},
/**
* 授权登录
*/
authLogin(data, type = 'authLogin') {
if (uni.getStorageSync('source_member')) data.source_member = uni.getStorageSync('source_member');
uni.setStorageSync('authInfo', data);
this.$api.sendRequest({
url: type == 'authLogin' ? '/api/login/auth' : '/api/login/authonlylogin',
data,
success: res => {
if (res.code >= 0) {
this.$store.commit('setToken', res.data.token);
this.getMemberInfo()
this.$store.dispatch('getCartNumber');
}
}
});
},
/**
* 公众号分享设置
*/
shareConfig() {
this.$api.sendRequest({
url: '/wechat/api/wechat/share',
data: {
url: window.location.href
},
success: res => {
if (res.code == 0) {
var wxJS = new Weixin();
wxJS.init(res.data.jssdk_config);
let share_data = JSON.parse(JSON.stringify(res.data.share_config.data));
if (share_data) {
wxJS.setShareData({
title: share_data.title,
desc: share_data.desc,
link: share_data.link,
imgUrl: this.$util.img(share_data.imgUrl)
},
res => {
}
);
}
let hideOptionMenu = res.data.share_config.permission.hideOptionMenu;
let hideMenuItems = res.data.share_config.permission.hideMenuItems;
if (hideOptionMenu) {
wxJS.weixin.hideOptionMenu(); //屏蔽分享好友等按钮
} else {
wxJS.weixin.showOptionMenu(); //放开分享好友等按钮
}
}
},
fail: err => {}
});
},
getMemberInfo() {
this.$api.sendRequest({
url: '/api/member/info',
success: (res) => {
if (res.code >= 0) {
// 登录成功,存储会员信息
this.$store.commit('setMemberInfo', res.data);
}
}
});
}
},
watch: {
$route: {
handler(newName, oldName) {
if (this.$util.isWeiXin()) {
this.shareConfig();
}
},
// 代表在watch里声明了firstName这个方法之后立即先去执行handler方法
immediate: true
}
}
};
</script>
<style lang="scss">
@import './common/css/main.scss';
@import './common/css/iconfont.css';
@import './common/css/icondiy.css'; // 自定义图标库
@import './common/css/icon/extend.css'; // 扩展图标库
page {
background: #f4f6fa;
}
</style>