uniapp 微信小程序webview 和 h5数据通信
项目是uniapp编写,因为是先开发了h5和app,小程序是突然要用的,做兼容开发已经来不及,由于微信小程序webview载入h5 因为通信必须要特殊限制(网页向小程序 postMessage 时,会在以下特定时机触发并收到消息:小程序后退、组件销毁、分享、复制链接(2.31.1)。e.detail = { data },data是多次 postMessage 的参数组成的数组。),不能满足使用,所以搞了一套特别low的通信机制,缺点是通信时候会有一个loading的过渡页面
业务逻辑大致为:
1、微信小程序webview访问h5带参数的url,获取初始的参数,可以携带页面参数或者登录token等,通过reciver接受h5返回的参数。
2、h5通过一个中间html页面存储小程序的通信逻辑,然后在h5页面通过定时器一直获取缓存本地的数据,根据数据信息执行不同的业务逻辑
1、微信小程序项目
(1)微信小程序项目
index.vue
<template>
<web-view v-if="src" :src="src" bindload="bindload" binderror="binderror"></web-view>
</template>
<script lang="ts">
import { cfg } from '@/cfg'
import Base64 from '@/utils/Base64'
export default {
data() {
return {
src: '',
}
},
onLoad(e: any) {
let loginA = parseInt(e.loginA)
console.log(e.loginA)
if (loginA) {
let self = this
wx.login({
success(res: any) {
// 自动登录成功
self.msg(Base64.encode64('loginA,' + JSON.stringify([loginA, cfg.mpApp, res.code])))
},
fail(res: any) {
// 自动登录失败
self.msg(Base64.encode64('loginAFail,' + JSON.stringify([loginA, res.errMsg])))
},
})
return
}
this.msg(e.msg)
},
methods: {
msg(msg: any) {
if (msg) {
this.src = cfg.mpUrl + 'static/mpMsg.html?uid=' + cfg.mpid + '&msg=' + msg
// this.src = 'http://www.baidu.com/#/'
console.log(this.src)
// 超时自动关闭
// @ts-ignore
this['$timer'] = setTimeout(() => {
this.navBack()
}, 3000)
return
}
this.navBack()
},
bindload() {
console.log('bindload')
this.navBack()
},
binderror() {
console.log('binderror')
this.navBack()
},
navBack() {
let pages = getCurrentPages()
if (pages[pages.length - 1].$vm == this) {
uni.navigateBack()
}
// @ts-ignore
let timer = this['$timer']
if (timer) {
// @ts-ignore
delete this['$timer']
clearTimeout(timer)
}
},
},
}
</script>
<style></style>
(2)reciver.vue
<template>
<view id="preloader"></view>
</template>
<script lang="ts">
import { App } from '@/store/app'
import Base64 from '@/utils/Base64'
export default {
data() {
return {
src: '',
}
},
onLoad(e: any) {
try {
let msg = e.msg
if (msg) {
msg = Base64.decode64(msg)
let i = msg.indexOf(',')
let key = msg
let val = ''
if (i > 0) {
key = msg.substring(0, i)
val = msg.substring(i + 1)
}
// 如果是pc小程序bridge支付,则直接跳转到支付页面
// const info = uni.getSystemInfoSync()
// if ((info?.deviceType == 'pc' || info?.deviceType == 'PC') && key == 'pay') {
// uni.redirectTo({
// url: `/pages/index/pay?pay=${Base64.encode64(val)}`,
// })
// } else {
// uni.navigateBack({
// complete() {
// // setTimeout(() => {
// App.mpRecieverMsg(key, val)
// // }, 100)
// },
// })
// }
uni.navigateBack({
complete() {
// setTimeout(() => {
App.mpRecieverMsg(key, val)
// }, 100)
},
})
return
}
} catch (e) {
console.error(e)
}
uni.navigateBack()
},
methods: {},
}
</script>
<style scoped>
#preloader {
position: absolute;
width: 30px;
height: 30px;
background: rgba(253, 87, 17, 1);
border-radius: 50px;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
-webkit-animation: preloader_1 1.5s infinite linear;
-moz-animation: preloader_1 1.5s infinite linear;
-ms-animation: preloader_1 1.5s infinite linear;
animation: preloader_1 1.5s infinite linear;
}
#preloader:after {
position: absolute;
width: 50px;
height: 50px;
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-radius: 50px;
content: '';
top: -20px;
left: -20px;
-webkit-animation: preloader_1_after 1.5s infinite linear;
-moz-animation: preloader_1_after 1.5s infinite linear;
-ms-animation: preloader_1_after 1.5s infinite linear;
animation: preloader_1_after 1.5s infinite linear;
}
@-webkit-keyframes preloader_1 {
0% {
-webkit-transform: rotate(0deg);
}
50% {
-webkit-transform: rotate(180deg);
background: #ff4f11;
}
100% {
-webkit-transform: rotate(360deg);
}
}
@-webkit-keyframes preloader_1_after {
0% {
border-top: 10px solid rgba(253, 87, 17, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
50% {
border-top: 10px solid rgba(253, 87, 17, 1);
border-bottom: 10px solid rgba(253, 87, 17, 1);
}
100% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
}
@-moz-keyframes preloader_1 {
0% {
-moz-transform: rotate(0deg);
}
50% {
-moz-transform: rotate(180deg);
background: #ff4f11;
}
100% {
-moz-transform: rotate(360deg);
}
}
@-moz-keyframes preloader_1_after {
0% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
50% {
border-top: 10px solid rgba(253, 87, 17, 1);
border-bottom: 10px solid rgba(253, 87, 17, 1);
}
100% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
}
@-ms-keyframes preloader_1 {
0% {
-ms-transform: rotate(0deg);
}
50% {
-ms-transform: rotate(180deg);
background: #ff4f11;
}
100% {
-ms-transform: rotate(360deg);
}
}
@-ms-keyframes preloader_1_after {
0% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
50% {
border-top: 10px solid rgba(253, 87, 17, 1);
border-bottom: 10px solid rgba(253, 87, 17, 1);
}
100% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
}
@keyframes preloader_1 {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(180deg);
background: #ff4f11;
}
100% {
transform: rotate(360deg);
}
}
@keyframes preloader_1_after {
0% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
50% {
border-top: 10px solid rgba(253, 87, 17, 1);
border-bottom: 10px solid rgba(253, 87, 17, 1);
}
100% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
}
</style>
(3)app.ts
import cfg from '@/cfg'
import Base64 from '@/utils/Base64'
export const App = {
shareData: {},
mpPostMsg(key: string, val?: string, redirect: boolean = true) {
const msg = val ? key + ',' + val : key
if (redirect) {
uni.redirectTo({ url: '/pages/msg/post?msg=' + Base64.encode64(msg) })
} else {
uni.navigateTo({ url: '/pages/msg/post?msg=' + Base64.encode64(msg) })
}
},
mpRecievers: {
userStorageCert(cert: string) {
console.log('userCert', cert)
if (cert) {
uni.setStorageSync(cfg.mpApp + 'userStorageCert', cert)
}
},
shareAppMessage(v: string) {
let share
try {
share = JSON.parse(v)
if (share.path) {
// 使用正则表达式匹配 _i= 后面的字符串
const match1 = share.path.match(/_i=([^&]+)/)
// 如果匹配成功,获取匹配到的字符串
if (match1) {
const extractedString = match1[1]
share.path = '/pages/index/index?i=' + extractedString
console.log(' share.path', share.path)
}
}
} catch (error) {}
try {
if (share.imageUrl) {
share.imageUrl = share.imageUrl.replace(/(w_\d+)|(h_\d+)/g, (match: string) => {
if (match.startsWith('w_')) {
return 'w_300'
} else if (match.startsWith('h_')) {
return 'h_300'
}
})
}
} catch (error) {}
if (share) {
App.shareData = share
}
},
pay(v: string) {
const pay = JSON.parse(v)
pay.success = function () {
// 需要通知
App.mpPostMsg('pay,1', undefined, false)
}
// pay.complete = function () {
// // 需要通知
// App.mpPostMsg('pay,1', undefined, false)
// }
pay.fail = function (e: any) {
console.log('pay fail ' + JSON.stringify(e))
uni.showToast({
title: '支付失败',
icon: 'none',
duration: 2000,
})
}
// 支付参数
console.log(JSON.stringify(pay))
const info = uni.getSystemInfoSync()
// 如果是pc小程序bridge支付,则延迟掉起,防止不显示支付弹窗
if (info?.deviceType == 'pc' || info?.deviceType == 'PC') {
uni.showLoading()
setTimeout(() => {
uni.hideLoading()
wx.requestPayment(pay)
}, 1000)
} else {
wx.requestPayment(pay)
}
},
bindWx() {
wx.login({
success: function (res: any) {
let data = ''
if (res) {
let appid = ''
if (cfg.mpApp) {
appid = cfg.mpApp
}
data = JSON.stringify([appid, res.code])
}
App.mpPostMsg('bindWx', data, false)
},
fail: function (err: any) {
console.log('bindWx fail', err)
App.mpPostMsg('bindWx', '', false)
},
})
},
openLocation(adress: string) {
const data = JSON.parse(adress)
wx.openLocation({
latitude: Number(data.latitude),
longitude: Number(data.longitude),
name: data.name,
scale: 18,
complete: e => {
console.log('openLocation complete', e)
},
})
console.log('openLocation data', data)
},
downloadAndSave(res: string) {
const data = JSON.parse(res)
const $delay = data['$delay']
if ($delay > 0) {
setTimeout(() => {
wx.downloadFile({
url: data.url,
success: function (res) {
wx.openDocument({
filePath: res.tempFilePath,
fileType: 'pdf',
showMenu: true,
})
},
})
}, $delay)
} else {
wx.downloadFile({
url: data.url,
success: function (res) {
wx.openDocument({
filePath: res.tempFilePath,
fileType: 'pdf',
showMenu: true,
})
},
})
}
},
callFun(res: string) {
const data = JSON.parse(res)
// @ts-ignore
const fun = wx[data['$fun']]
if (fun) {
delete data['$fun']
const $delay = data['$delay']
if ($delay > 0) {
delete data['$delay']
setTimeout(() => {
fun.call(wx, data)
}, $delay)
} else {
fun.call(wx, data)
}
}
},
},
mpRecieverMsg(key: string, val?: string) {
// @ts-ignore
const fun = App.mpRecievers[key]
if (fun) {
fun(val)
return
}
},
}
(3)Base64.ts
// private property
let _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// private method for UTF-8 encoding
let _utf8_encode = function (str: string) {
str = str.replace(/\r\n/g, "\n");
let utftext = "";
for (let n = 0; n < str.length; n++) {
let c = str.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
// private method for UTF-8 decoding
let _utf8_decode = function (utftext: string) {
let string = "";
let i = 0;
let c, c1, c2, c3
c = c1 = c2 = 0;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i + 1);
c3 = utftext.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
const Base64 = {
// public method for encoding
encode(input: string) {
let output = "";
let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
let i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
},
// public method for decoding
decode(input: string) {
let output = "";
let chr1, chr2, chr3;
let enc1, enc2, enc3, enc4;
let i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = _keyStr.indexOf(input.charAt(i++));
enc2 = _keyStr.indexOf(input.charAt(i++));
enc3 = _keyStr.indexOf(input.charAt(i++));
enc4 = _keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = _utf8_decode(output);
return output;
},
// 编码
encode64(str: string, uri: boolean = true) {
str = Base64.encode(str);
if (uri) {
str = str.replace(/[+|=|/]/g, function (word) {
switch (word) {
case "+":
return "-";
case "=":
return "_";
case "/":
return ".";
}
return word;
});
}
return str;
},
// 解码
decode64(str: string, uri: boolean = true) {
if (uri) {
str = str.replace(/[-|_|.]/g, function (word) {
switch (word) {
case "-":
return "+";
case "_":
return "=";
case ".":
return "/";
}
return word;
});
}
str = Base64.decode(str);
return str;
},
}
export default Base64
index.vue
<template>
<web-view :src="src" @message="receiveMessage"> </web-view>
</template>
<script lang="ts">
import { cfg } from '@/cfg'
import { App } from '@/store/app'
import Base64 from '@/utils/Base64'
export default {
data() {
return {
src: '',
srcP: '',
shareData: {
imageUrl:
'https://yjy.yiyiny.com//static/images/20240122/' +
cfg.mpApp +
'.png?x-oss-process=image/auto-orient,1/resize,m_fixed,w_100,h_100',
},
shareI: '',
}
},
onLoad(e: any) {
// e.scene 生成小程序码必须是这个key
this.setSrc(cfg.entryUrl, e.i || e.scene)
wx.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage'],
success(res) {
console.log('mixin share success', res ? JSON.stringify(res) : res)
},
fail(err) {
console.log('mixin share fail', err ? JSON.stringify(err) : err)
},
})
},
onShow() {
cfg.mpSrcP = this.srcP
App.shareData = {}
},
onShareAppMessage(res: any) {
console.log(res)
if (res.from === 'button') {
return this.shareData
}
if (res.from === 'menu') {
return this.shareData || {}
}
},
onShareTimeline() {
let data = {
title: '一乙艺术山庄',
query: 'id=1',
// imageUrl: '',
}
return data
// return axCc.vueSelf.shareData || {};
},
onHide() {},
methods: {
setSrc(src: string, shareI?: string) {
let cert = uni.getStorageSync(cfg.mpApp + 'userStorageCert')
console.log('setSrc src', src)
console.log('setSrc shareI', shareI)
let i = src.indexOf('#')
let j = src.indexOf('?')
let srcP = i > 0 && i < j ? src.substring(0, i) : j > 0 ? src.substring(0, j) : src
if (srcP && srcP[srcP.length - 1] != '/') {
srcP = srcP + '/'
}
if (i > 0) {
cfg.mpUrl = src.substring(0, i)
} else {
cfg.mpUrl = cfg.entryUrl
}
console.log('[ cfg.mpUrl ] >', cfg.mpUrl)
this.srcP = srcP
cfg.mpSrcP = this.srcP
src = j > 0 ? src + '&_mpid_=' + cfg.mpid : src + '?_mpid_=' + cfg.mpid
if (shareI) {
src = src + '&_i=' + shareI
// 分享地址拼接
const startIndex = src.indexOf('#')
const endIndex = src.indexOf('?')
if (startIndex !== -1 && endIndex !== -1) {
src = src.substring(0, startIndex) + src.substring(endIndex)
console.log(src)
}
this.shareI = shareI
}
src = src + '&ver=' + cfg.version
if (cert) {
src = src + '&_cert_=' + encodeURIComponent(cert)
}
this.src = src
console.log('setSrc this.src', this.src)
},
// 监听h5 消息
// { merber 用户信息 imageUrl 默认分享图 path 分享地址 title:分享标题 desc }
receiveMessage(e: any) {
console.log('receiveMessage333', e)
let arr = e?.detail?.data
let data = arr && arr.length > 0 ? arr[arr.length - 1] : null
let url
if (data && data?.merber?.id) {
url = data?.path
? data.path
: cfg.mpName == 'mp_wdysj'
? 'pages/artisthome/artisthome'
: '/pagesShop/shop/wHome'
let parms = this.gen(url, false, { mmId: data?.merber.id }, 13050, data?.merber?.id)
console.log('data222', data)
this.shareData = {
path: '/pages/index/index?i=' + parms + '&fk=1',
imageUrl: data?.imageUrl
? data.imageUrl
: 'https://yjy.yiyiny.com//static/images/20240122/' +
cfg.mpApp +
'.png?x-oss-process=image/auto-orient,1/resize,m_fixed,w_100,h_100',
title: data?.title ? data.title : cfg.mpName,
desc: data?.desc ? data.desc : '',
query: parms,
}
} else {
wx.hideShareMenu()
}
},
// 地址加密
gen(uri: string, bind: boolean, reg: any, eid?: number, memberId?: number): string {
// 标准编码
let ps = [memberId, '', false, uri || '', reg || '', eid]
for (let i = ps.length - 1; i >= 0; i--) {
if (ps[i]) {
let del = ps.length - i - 1
if (del > 0) {
ps.splice(i + 1, del)
}
break
}
}
let _ps: any = ps
_ps[0] = _ps[0] || ''
if (bind) {
_ps[2] = uri
_ps[3] = ''
} else {
_ps[2] = ''
}
return Base64.encode64(JSON.stringify(ps))
},
},
}
</script>
<style></style>
cfg.ts
export const cfg = {
mpApp: 'mp_wdysj', //一乙艺术商城
mpName: '一乙艺术商城',
mpUrl: 'https://p.yiyiny.com/xxx/',
mpid: '',
mpSrcP: '',
version: '1.0.8',
entryUrl: 'https://p.yiyiny.com/xxx/#/pagesShop/shop/wHome',
}
const info = uni.getSystemInfoSync()
if (!cfg.mpid) {
const host = info.host
// @ts-ignore
if (host && host.appid) {
// @ts-ignore
cfg.mpid = host.appid
}
if (!cfg.mpid) {
cfg.mpid = cfg.mpApp
}
}
export default cfg
2、h5端
(1)项目初始化时候h5载入微信sdk
Ainit.ts
// #ifdef H5
let _mpid_;
try {
let search = <any>(
(launcher.h5Search || ab_.route(false, false, false).search)
);
_mpid_ = search['_mpid_'];
let _cert_ = search['_cert_'];
// 获取小程序传递的证书
if (_cert_) {
_cert_ = decodeURIComponent(_cert_)
User.status.storage.cert = _cert_
}
let app = axConfig.webType || 'web'
if (Weixin.isWeiXin()) {
axCc.Loader().wait("weixinLogin");
ab_.reqJs(
axCc.https
? "https://res.wx.qq.com/open/js/jweixin-1.6.0.js"
: "http://res.wx.qq.com/open/js/jweixin-1.6.0.js",
null,
function () {
function wxH5() {
if (search["code"]) {
let parms: Array<string> = [axConfig.webType, search["code"]];
User.loginProvBack(
"wx",
function (logined) {
console.log("微信登陆成功", logined);
try {
if (logined) {
axCc.saveStorage("wxLogined", {
token: App.client.plt.headers["atoken"],
authPar: User.status.authPar,
authParas: User.status.authParas,
});
} else {
axCc.saveStorage("wxLogined", {});
}
} finally {
axCc.Loader().done("weixinLogin");
// 微信jsConfig
Weixin.wxConfig();
}
},
parms,
true,
true,
true
);
} else {
try {
// 关闭自动登录
// User.autoLogin = false;
let wxLogined = axCc.getStorage("wxLogined");
if (wxLogined && wxLogined.token && wxLogined.authParas && wxLogined.authParas[0] === app) {
User.loginToken(wxLogined.token, (logined) => {
if (logined) {
User.status.authPar = wxLogined.authPar;
User.status.authParas = wxLogined.authParas;
axCc.Loader().done("weixinLogin");
} else {
//微信浏览器 吊起微信支付必须要获取openid
console.log("掉起微信支付必须要获取openid");
Weixin.snsapi_base();
}
});
} else {
//微信浏览器 吊起微信支付必须要获取openid
console.log("掉起微信支付必须要获取openid");
Weixin.snsapi_base();
}
} finally {
// 微信jsConfig
Weixin.wxConfig();
}
}
}
// @ts-ignore
if (window.wx) {
User.autoLogin = false;
try {
// @ts-ignore
if (typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function") {
handleFontSize();
} else {
if (document.addEventListener) {
document.addEventListener("WeixinJSBridgeReady", handleFontSize, false);
// @ts-ignore
} else if (document.attachEvent) {
// @ts-ignore
document.attachEvent("WeixinJSBridgeReady", handleFontSize);
// @ts-ignore
document.attachEvent("onWeixinJSBridgeReady", handleFontSize);
}
}
function handleFontSize() {
// 设置网页字体为默认大小
// @ts-ignore
WeixinJSBridge.invoke('setFontSizeCallback', { 'fontSize': 0 });
// 重写设置网页字体大小的事件
// @ts-ignore
WeixinJSBridge.on('menu:setfont', function () {
// @ts-ignore
WeixinJSBridge.invoke('setFontSizeCallback', { 'fontSize': 0 });
});
}
// @ts-ignore
if (wx.miniProgram) {
// @ts-ignore
wx.miniProgram.getEnv((res) => {
if (res.miniprogram) {
User.status.info.cert = true
User.autoLogin = true;
// && search['_mpid_'] == "mp_wdysj_shop"
if (_mpid_) {
if ((_mpid_ == "mp_wdysj_shop" || _mpid_ == "mp_wdysj_yiyi_shop")) {
axConfig.pltName = "商城"
axConfig.home = "/pagesShop/shop/wHome"
App.shopScoreName = '商城'
App.client.storeHttp.head('platform', 'mp')
axConfig.appProv = _mpid_
axConfig.appEid = 13061
}
axConfig.pltApp = _mpid_
User.status.info.app = _mpid_
}
// 小程序
Weixin.initWxMpH5(_mpid_, search['ver'])
axCc.Loader().done("weixinLogin");
} else {
wxH5()
}
})
return
}
} catch (e) {
console.error(e)
}
wxH5()
} else {
axCc.Loader().done("weixinLogin");
}
},
undefined
);
} else {
}
} catch (e) {
console.error(e);
}
// #endif
2、Weixin.ts
import ab_ from 'axj-ab_'; //npm install axj-ab_
import Base64 from '../util/Base64';
interface ShareInfo {
title: string,
desc?: string,
link: string,
imageUrl?: string,
logo?: string,//兼容老项目
imgUrl?: string,//兼容老项目
scene?: string
}
let weixin = false;
let readyReg = false;
let wxConfigState = -1;
let wxMpH5 = false;
let wxMpApp = '';
// let wxMpToken = '';
const Weixin = {
initStepsArray: [1000, 2000, 4000, 8000, 16000, 32000, 64000],
initI: 0,
erred: false,
readyed: false,
shareInfo: <undefined | ShareInfo>undefined,
// 如果wx授权失败,则定时再次获取授权
wxConfigRe(e?: any) {
console.log('wxConfig error ' + e)
// alert('wxConfig error ' + JSON.stringify(e))
Weixin.erred = true
// @ts-ignore
setTimeout(Weixin.wxConfig, Weixin.initI < Weixin.initStepsArray.length ? Weixin.initStepsArray[Weixin.initI++] : Weixin.initStepsArray[Weixin.initStepsArray.length - 1])
},
// 通过config接口注入权限验证配置
wxConfig() {
let authUrl = location.href
wxConfigState++
switch (wxConfigState) {
case 1:
authUrl = Ainit.h5Href
break
// case 2:
// authUrl = location.protocol + '//' + location.host + location.pathname + location.search + location.hash
// break
default:
wxConfigState = 0
break
}
//调接口授权的方法可以自己写
App.client.plt.reqA(-1, 'C/wxConfig', [axConfig.webType, authUrl], function (err, rep) {
console.log('pltClient C/wxConfig err,data:', err, rep)
if (rep && rep.appId) {
// rep.debug = true
rep.jsApiList = [
'updateAppMessageShareData',
'updateTimelineShareData',
'onMenuShareAppMessage',
'onMenuShareTimeline',
"getLocation",
"scanQRCode",
// 'wx-open-launch-weapp',
// 'chooseWXPay'
]
// rep.debug = true
console.log('wxready wx.config')
Weixin.erred = false
// @ts-ignore
wx.config(rep)
// @ts-ignore
wx.error(Weixin.wxConfigRe)
let readyFun = function () {
if (Weixin.erred) {
return
}
Weixin.readyed = true
}
if (readyReg) {
setTimeout(readyFun, 1000);
} else {
readyReg = true
// @ts-ignore
wx.ready(readyFun)
}
return;
}
Weixin.wxConfigRe()
})
},
wxShare(v: ShareInfo) {
console.log('h5 wxShare alert', v);
if (weixin) {
// @ts-ignore
v = typeof (v) === 'object' ? v : JSON.parse(v)
if (!Weixin.readyed) {
Weixin.shareInfo = v
return
}
delete Weixin.shareInfo
var share = {
title: v.title || 'xx', // 分享标题
desc: v.desc || '', // 分享描述
link: v.link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: v.imageUrl || v.logo || v.imgUrl, // 分享图标
success: (e) => {
console.log('wxShare success ', e)
// 显示微信分享
// @ts-ignore
// if (window.WeixinJSBridge) {
// // @ts-ignore
// window.WeixinJSBridge.call('showOptionMenu')
// }
if (window.wx && window.wx.showMenuItems) {
// @ts-ignore
window.wx.showMenuItems({
menuList: [
'menuItem:share:appMessage',
'menuItem:share:timeline',
'menuItem:favorite',
'menuItem:share:qq',
'menuItem:share:QZone',
]
})
}
},
fail: function (e) {
console.log('wxShare fail', e)
}
}
console.log('updateWx', share)
// @ts-ignore
wx.updateAppMessageShareData(share)
// @ts-ignore
wx.updateTimelineShareData(share)
// WeixinJSBridge.call("showOptionMenu");
}
},
// 静默授权
snsapi_base() {
var backUri = Ainit.h5Href || location.href
console.log(backUri)
var redirectUri = axConfig.pltH5 + 'static/wx.html?u=' + Base64.encode64(backUri, true)
redirectUri =
'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' + (axConfig.webType == 'wdysjh5' ? 'wxab385cf8a7ec96d8' : 'wx59a0fb7a9bd076e5') + '&redirect_uri=' +
encodeURIComponent(redirectUri) +
'&response_type=code&scope=snsapi_userinfo&state=1'
console.log(redirectUri)
location.replace(redirectUri)
// setTimeout(() => {
// location.replace(redirectUri)
// }, 3000);
},
isWeiXin() {
return weixin;
},
isWxMpH5() {
return wxMpH5
},
getWxMpApp() {
return wxMpApp
},
initWxMpH5(mpid: string, ver?: string) {
console.log('initWxMpH5 ' + mpid)
weixin = false
wxMpH5 = true
if (mpid) {
wxMpApp = mpid
} else {
console.log('initWxMpH5 no mpid' + location.href)
console.log(launcher.h5Href)
console.log(launcher.h5Search)
}
Artist.getReviewVersion(`mp-${ver}`)
// 消息通道
setInterval(function () {
let msg = localStorage.getItem('_mpmsg_')
if (msg) {
try {
msg = JSON.parse(msg)
if (!mpid || msg[0] === mpid) {
// 是发给我的消息
msg = msg[1]
msg = Base64.decode64(msg)
let i = msg.indexOf(',')
if (i > 0) {
Weixin.onWxMpMsg(msg.substring(0, i), msg.substring(i + 1))
} else {
Weixin.onWxMpMsg(msg, '')
}
localStorage.removeItem('_mpmsg_')
}
} catch (e) {
console.error(e)
localStorage.removeItem('_mpmsg_')
}
}
}, 100)
},
onWxMpMsg(key: string, val?: string) {
console.log('onWxMpMsg,' + key + ',' + val)
if (key == 'pay') {
console.log('updatePay,更新支付状态' + key)
uni.$emit('updatePay', { msg: '更新支付状态' })
}
// @ts-ignore
wx.miniProgram.navigateBack()
let fun = Weixin.onWxMpCenter[key]
if (fun) {
fun(val)
}
},
forWxMpToken(back: (err?: any) => void) {
if (User.status.authPar) {
back && back()
return
}
// @ts-ignore
let nextT = ab_.nextT()
// @ts-ignore
Weixin['forWxMpTokenBacks'] = [nextT, back]
// @ts-ignore
wx.miniProgram.navigateTo({ url: '/pages/msg/post?loginA=' + nextT })
},
onWxMpCenter: {
loginA(val: string) {
let paras = JSON.parse(val)
let nextT = 0
let back: any
let loginA = false
if (typeof (paras[0]) === 'number') {
loginA = true
nextT = paras[0]
paras = ab_.args(paras, 1, paras.length)
let backs = Weixin['forWxMpTokenBacks']
if (backs) {
delete Weixin['forWxMpTokenBacks']
if (backs[0] === nextT) {
back = backs[1]
}
}
}
let noRep = nextT && User.state.logined
User.loginProvBack('wx', (succ, rep) => {
console.log('loginA ', succ, rep)
if (succ) {
Page.loginSuccBack()
if (loginA) {
// @ts-ignore
wx.miniProgram.navigateBack()
}
} else if (!User.state.authing && !nextT) {
console.log('toLogin--------')
Weixin.postWxMpMsg('toLogin')
}
// 回调
back && back(User.status.authToken ? undefined : rep)
}, paras, false, 2, false, noRep)
},
loginAFail(val: string) {
let paras = JSON.parse(val)
let nextT = paras[0]
let backs = Weixin['forWxMpTokenBacks']
if (backs) {
delete Weixin['forWxMpTokenBacks']
if (backs[0] === nextT) {
backs[1] && backs[1](paras[1])
}
}
},
loginInfo(val: string) {
//let loginInfo = JSON.parse(val)
let paras = JSON.parse(val)
if (User.status.authToken) {
paras[1] = User.status.authToken
}
User.loginProvBack('wx', (succ) => {
if (succ) {
Page.loginSuccBack()
} else if (!User.state.authing) {
User.status.authToken = ''
}
}, paras, true, 2)
},
bindWx(val: string) {
uni.$emit('updateBindWx', { msg: val })
}
},
postWxMpMsg(key: string, val?: string) {
console.log('postWxMpMsg,' + key + ',' + val)
if (key === 'toLogin') {
// ios 微信新用户第一次登录 会导致不执行此逻辑
setTimeout(() => {
// @ts-ignore
wx.miniProgram.navigateTo({ url: '/pages/index/login?t=' + User.status.authToken })
}, 400);
return
}
// if (key === 'toLoginA') {
// // @ts-ignore
// wx.miniProgram.navigateTo({ url: '/pages/index/login' })
// return
// }
if (key === 'share') {
// @ts-ignore
wx.miniProgram.navigateTo({ url: '/pages/msg/share?s=' + val })
return
}
let msg = val ? (key + ',' + val) : key
setTimeout(() => {
// @ts-ignore
wx.miniProgram.navigateTo({ url: '/pages/msg/reciver?msg=' + Base64.encode64(msg) })
}, 400);
console.log('postWxMpMsg.navigateTo');
},
}
// #ifdef H5
try {
// 微信环境判断
let userAgent: any = window.navigator.userAgent.toLowerCase();
weixin =
userAgent.match(/MicroMessenger/i) == "micromessenger" &&
!(window.parent && window.parent !== window);
//兼容 微信支付路径
if (weixin) {
let h5Uri = location.href;
if (h5Uri.indexOf('/#/') < 0) {
// axCc.Loader().wait("weixinHref");
try {
h5Uri = location.origin + location.pathname + "#/" + location.search
location.href = h5Uri
setTimeout(() => {
location.reload()
}, 1000);
axCc.Loader().wait("weixinHref");
} catch (error) {
}
}
}
} catch (e) {
console.error(e);
}
// #endif
export default Weixin;
3、公共h5跳转页
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,maximum-scale=1,viewport-fit=cover"
/>
<title>···</title>
<style>
#loadingBg {
width: 100vw;
height: 100vh;
position: fixed;
z-index: 10000;
top: 0;
}
body {
margin: 0px;
font-size: 12px;
}
#preloader {
position: absolute;
width: 30px;
height: 30px;
background: rgba(253, 87, 17, 1);
border-radius: 50px;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
-webkit-animation: preloader_1 1.5s infinite linear;
-moz-animation: preloader_1 1.5s infinite linear;
-ms-animation: preloader_1 1.5s infinite linear;
animation: preloader_1 1.5s infinite linear;
}
#preloader:after {
position: absolute;
width: 50px;
height: 50px;
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-radius: 50px;
content: "";
top: -20px;
left: -20px;
-webkit-animation: preloader_1_after 1.5s infinite linear;
-moz-animation: preloader_1_after 1.5s infinite linear;
-ms-animation: preloader_1_after 1.5s infinite linear;
animation: preloader_1_after 1.5s infinite linear;
}
@-webkit-keyframes preloader_1 {
0% {
-webkit-transform: rotate(0deg);
}
50% {
-webkit-transform: rotate(180deg);
background: #ff4f11;
}
100% {
-webkit-transform: rotate(360deg);
}
}
@-webkit-keyframes preloader_1_after {
0% {
border-top: 10px solid rgba(253, 87, 17, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
50% {
border-top: 10px solid rgba(253, 87, 17, 1);
border-bottom: 10px solid rgba(253, 87, 17, 1);
}
100% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
}
@-moz-keyframes preloader_1 {
0% {
-moz-transform: rotate(0deg);
}
50% {
-moz-transform: rotate(180deg);
background: #ff4f11;
}
100% {
-moz-transform: rotate(360deg);
}
}
@-moz-keyframes preloader_1_after {
0% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
50% {
border-top: 10px solid rgba(253, 87, 17, 1);
border-bottom: 10px solid rgba(253, 87, 17, 1);
}
100% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
}
@-ms-keyframes preloader_1 {
0% {
-ms-transform: rotate(0deg);
}
50% {
-ms-transform: rotate(180deg);
background: #ff4f11;
}
100% {
-ms-transform: rotate(360deg);
}
}
@-ms-keyframes preloader_1_after {
0% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
50% {
border-top: 10px solid rgba(253, 87, 17, 1);
border-bottom: 10px solid rgba(253, 87, 17, 1);
}
100% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
}
@keyframes preloader_1 {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(180deg);
background: #ff4f11;
}
100% {
transform: rotate(360deg);
}
}
@keyframes preloader_1_after {
0% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
50% {
border-top: 10px solid rgba(253, 87, 17, 1);
border-bottom: 10px solid rgba(253, 87, 17, 1);
}
100% {
border-top: 10px solid rgba(255, 139, 9, 1);
border-bottom: 10px solid rgba(255, 139, 9, 1);
}
}
</style>
</head>
<script>
function getPara(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
}
let uid = getPara("uid");
let msg = getPara("msg");
if (msg) {
localStorage.setItem("_mpmsg_", JSON.stringify([uid, msg]));
}
</script>
<body>
<div id="loadingBg">
<div id="preloader"></div>
<!-- <div>加载中</div> -->
</div>
</body>
</html>