278 lines
15 KiB
HTML
278 lines
15 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||
<meta name="theme-color" content="#F4593C">
|
||
<title>wx-h5-demo · 共享授权规范示例</title>
|
||
<style>
|
||
* { margin:0; padding:0; box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
|
||
body { font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif; background:#F8F3EF; color:#2B2320; min-height:100vh; display:flex; justify-content:center; }
|
||
.wrap { width:100%; max-width:430px; padding:24px 20px 40px; }
|
||
.brand { font-size:22px; font-weight:800; color:#F4593C; } .brand small{color:#A79C95;font-size:13px;font-weight:500;margin-left:8px}
|
||
.env { margin:14px 0 8px; font-size:13px; color:#6B615B; } .env b{color:#2B2320}
|
||
.tag { display:inline-block; padding:2px 8px; border-radius:8px; font-size:12px; }
|
||
.tag.ok{background:#E8F2EB;color:#5F8A4A} .tag.no{background:#F6DCD6;color:#C4553A}
|
||
.card { background:#fff; border:1px solid #EFE7E1; border-radius:16px; padding:16px; margin-top:14px; }
|
||
.card h2{font-size:15px;font-weight:800;margin-bottom:10px}
|
||
.row{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px dashed #EFE7E1;font-size:14px}
|
||
.row:last-child{border-bottom:none} .row .k{color:#6B615B} .row .v{font-weight:600} .row .v.placeholder{color:#C9B7AC;font-weight:500}
|
||
.avatar{width:44px;height:44px;border-radius:50%;background:#F1E6E0;overflow:hidden;display:flex;align-items:center;justify-content:center;font-size:18px}
|
||
.avatar img{width:100%;height:100%;object-fit:cover}
|
||
.btn{display:block;width:100%;padding:15px;border:none;border-radius:14px;font-size:15px;font-weight:700;margin-top:14px;cursor:pointer}
|
||
.btn.main{background:#F4593C;color:#fff} .btn.ghost{background:#fff;color:#6B615B;border:1px solid #EFE7E1}
|
||
.hint{font-size:12px;color:#A79C95;margin-top:12px;line-height:1.6}
|
||
.env-hint{font-size:12.5px;color:#C4553A;background:#FBF0EE;border:1px solid #F0D8D2;border-radius:10px;padding:10px 12px;margin-top:12px;line-height:1.6}
|
||
#toast{position:fixed;left:50%;top:14%;transform:translateX(-50%);background:rgba(30,22,18,.9);color:#fff;padding:10px 18px;border-radius:10px;font-size:13px;opacity:0;pointer-events:none;transition:opacity .25s;z-index:99}
|
||
#toast.show{opacity:1}
|
||
|
||
/* ==== 调试日志面板 ==== */
|
||
#dbgToggle{position:fixed;right:12px;bottom:12px;z-index:200;background:#2B2320;color:#fff;border:none;border-radius:20px;padding:9px 14px;font-size:13px;font-weight:700;box-shadow:0 4px 14px rgba(0,0,0,.25);cursor:pointer;opacity:.92}
|
||
#dbgPanel{position:fixed;right:0;bottom:0;left:0;z-index:210;background:#1A1512;color:#E7DAD1;font-family:Menlo,Consolas,monospace;font-size:12px;line-height:1.5;border-top:2px solid #F4593C;transform:translateY(100%);transition:transform .25s;max-height:62vh;display:flex;flex-direction:column}
|
||
#dbgPanel.open{transform:translateY(0)}
|
||
#dbgHead{display:flex;justify-content:space-between;align-items:center;padding:9px 12px;background:#241D18;border-bottom:1px solid #3A2F28}
|
||
#dbgHead b{font-size:13px;color:#F4593C}
|
||
#dbgHead span{font-size:11px;color:#A79C95}
|
||
#dbgHead button{margin-left:8px;background:none;border:1px solid #4A3D34;color:#E7DAD1;border-radius:6px;padding:3px 10px;font-size:12px;cursor:pointer}
|
||
#dbgLog{overflow-y:auto;padding:8px 12px;flex:1;word-break:break-all}
|
||
#dbgLog .ln{margin:3px 0;padding:3px 6px;border-left:3px solid #4A3D34;color:#CBBDB2}
|
||
#dbgLog .ln.err{border-left-color:#F4593C;color:#FF9E86;background:rgba(244,89,60,.08)}
|
||
#dbgLog .ln.ok{border-left-color:#5F8A4A;color:#9BC48A}
|
||
#dbgLog .ln.warn{border-left-color:#E0B33E;color:#EED49A}
|
||
#dbgLog .ln.muted{color:#8B7E75;border-left-color:#3A2F28}
|
||
#dbgLog .ln b{color:#fff;font-weight:700}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
<div class="brand">wx-h5-demo <small>共享授权规范示例</small></div>
|
||
<div class="env">环境:<b id="envTxt">检测中…</b> <span id="envTag" class="tag no">纯浏览器</span></div>
|
||
|
||
<div class="card">
|
||
<h2>授权状态</h2>
|
||
<p id="authState" style="font-size:13px;color:#6B615B;line-height:1.6">检测中…</p>
|
||
<button class="btn main" id="btnAuth" style="display:none">授权登录</button>
|
||
<button class="btn ghost" id="btnMall">进商城</button>
|
||
<p id="envHint" class="env-hint" style="display:none">⚠️ 当前不是小程序 webview,无法完成授权登录。请在<strong>微信小程序</strong>里打开本页后使用(浏览器/微信内直开仅能查看)。</p>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>授权结果(宿主页回传后读取 #phone/nickname/avatar)</h2>
|
||
<div class="row"><span class="k">手机号</span><span class="v placeholder" id="rPhone">—</span></div>
|
||
<div class="row"><span class="k">昵称</span><span class="v placeholder" id="rNick">—</span></div>
|
||
<div class="row"><span class="k">头像</span><span class="v"><span class="avatar" id="rAvatarWrap"><span id="rAvatarText">宝</span></span></span></div>
|
||
</div>
|
||
|
||
<p class="hint">
|
||
本页演示<b>共享授权规范</b>:<code>wx-auth.js</code>(<code>wxAuth.init/requestProfile/goMall/readAuth/isAuthorized</code>)。
|
||
授权流程:点「授权登录」→ <code>wx.miniProgram.navigateTo('/pages/annex/auth/index')</code> →
|
||
小程序端独立授权页(头像/昵称/手机号,非 web-view 原生组件页)→ <code>POST /api/mp/public/auth/profile</code> 落库共享 UserAuth →
|
||
授权返回后宿主页更新 <code>web-view src</code>(hash 带结果)→ 本页重载 → <code>wxAuth.readAuth()</code> 显示。
|
||
「进商城」→ <code>wx.miniProgram.switchTab('/pages/goods_cate/goods_cate')</code>。
|
||
</p>
|
||
|
||
<div id="ver" style="text-align:center;color:#B3A69C;font-size:11px;margin-top:22px;padding-bottom:8px">v<span id="verNo"></span></div>
|
||
</div>
|
||
<div id="toast"></div>
|
||
|
||
<!-- 调试日志面板 -->
|
||
<button id="dbgToggle">🐛 调试日志</button>
|
||
<div id="dbgPanel">
|
||
<div id="dbgHead">
|
||
<b>调试日志</b>
|
||
<span id="dbgCount"></span>
|
||
<span>
|
||
<button id="dbgCopy">复制</button>
|
||
<button id="dbgClear">清空</button>
|
||
<button id="dbgClose">收起</button>
|
||
</span>
|
||
</div>
|
||
<div id="dbgLog"></div>
|
||
</div>
|
||
|
||
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
|
||
<script src="/mama-box/js/wx-auth.js?v=20260908"></script>
|
||
<script>
|
||
(function () {
|
||
'use strict';
|
||
|
||
/* ============================================================
|
||
① 页面版本号(YYMMDD + 两位序号)—— 用于排查 webview 是否缓存了旧页面
|
||
============================================================ */
|
||
var VER = 26090901;
|
||
var verNoEl = el('verNo');
|
||
if (verNoEl) verNoEl.textContent = VER;
|
||
|
||
/* ============================================================
|
||
② 日志面板(真机上 console/alert 看不到,用可视化面板)
|
||
============================================================ */
|
||
var dbgCount = 0;
|
||
function el(id) { return document.getElementById(id); }
|
||
function dbg(msg, cls) {
|
||
dbgCount++;
|
||
var logBox = el('dbgLog');
|
||
var ln = document.createElement('div');
|
||
ln.className = 'ln' + (cls ? ' ' + cls : '');
|
||
var t = new Date().toTimeString().replace(/.*(\d\d:\d\d:\d\d).*/, '$1');
|
||
ln.innerHTML = '<b>' + t + '</b> ' + msg;
|
||
logBox.appendChild(ln);
|
||
logBox.scrollTop = logBox.scrollHeight;
|
||
el('dbgCount').textContent = '共 ' + dbgCount + ' 条';
|
||
try { console.log('[dbg]', msg); } catch (e) {}
|
||
}
|
||
function dbgErr(msg) { dbg('❌ ' + msg, 'err'); }
|
||
window.__dbg = dbg;
|
||
|
||
// 全局错误捕获
|
||
window.addEventListener('error', function (e) {
|
||
dbgErr('window.onerror: ' + (e.message || '未知') + ' @ ' + (e.filename || '') + ':' + (e.lineno || '') + ':' + (e.colno || ''));
|
||
});
|
||
window.addEventListener('unhandledrejection', function (e) {
|
||
var r = e.reason;
|
||
dbgErr('unhandledrejection: ' + (r && (r.message || r.stack) ? (r.message || r.stack) : r));
|
||
});
|
||
|
||
// 面板开关
|
||
el('dbgToggle').addEventListener('click', function () { el('dbgPanel').classList.toggle('open'); });
|
||
el('dbgClose').addEventListener('click', function () { el('dbgPanel').classList.remove('open'); });
|
||
el('dbgClear').addEventListener('click', function () { el('dbgLog').innerHTML = ''; dbgCount = 0; el('dbgCount').textContent = ''; });
|
||
|
||
// 复制日志:隐藏 textarea + execCommand('copy'),微信 webview 通用做法
|
||
el('dbgCopy').addEventListener('click', function () {
|
||
var text = '';
|
||
var lns = el('dbgLog').querySelectorAll('.ln');
|
||
for (var i = 0; i < lns.length; i++) {
|
||
var t = (lns[i].querySelector('b') ? lns[i].querySelector('b').textContent : '');
|
||
text += t + ' ' + lns[i].textContent.replace(t, '').trim() + '\n';
|
||
}
|
||
text = '[wx-h5-demo 调试日志 v' + VER + ']\n' + text;
|
||
function ok() { dbg('已复制 ' + lns.length + ' 条日志到剪贴板', 'ok'); }
|
||
try {
|
||
var ta = document.createElement('textarea');
|
||
ta.value = text;
|
||
ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0;height:1px;width:1px;overflow:hidden';
|
||
document.body.appendChild(ta);
|
||
ta.select();
|
||
var did = document.execCommand('copy');
|
||
document.body.removeChild(ta);
|
||
if (did) ok(); else dbgErr('复制失败(execCommand 返回 false)');
|
||
} catch (e) {
|
||
dbgErr('复制失败: ' + (e && e.message ? e.message : e));
|
||
}
|
||
});
|
||
|
||
function show(tag, title) {
|
||
el('envTag').textContent = title; el('envTag').className = 'tag ' + (tag === 'ok' ? 'ok' : 'no');
|
||
}
|
||
|
||
/* ============================================================
|
||
② jweixin / wx.miniProgram 环境检测(先记录关键前提)
|
||
============================================================ */
|
||
dbg('脚本初始化开始');
|
||
dbg('jweixin 已加载: window.wx = ' + (typeof window.wx !== 'undefined' ? '✅ 存在' : '❌ 不存在(CDN 加载失败或未进入微信环境)'));
|
||
dbg('window.wx.miniProgram = ' + ((window.wx && window.wx.miniProgram) ? '✅ 存在' : '❌ 不存在'), 'muted');
|
||
dbg('navigator.userAgent 含 MicroMessenger: ' + (/MicroMessenger/i.test(navigator.userAgent) ? '✅ 是' : '❌ 否'), 'muted');
|
||
dbg('location.hash = ' + (location.hash || '(空)') + ' location.search = ' + (location.search || '(空)'), 'muted');
|
||
|
||
// 给 navigateTo 打补丁:记录每一次跳转调用 + 捕获异常 + 转发 fail/success
|
||
// (navigateTo 跳不存在的页面 / 页面栈超 10 层时是静默失败,必须靠 fail 才能看到原因)
|
||
if (window.wx && window.wx.miniProgram && window.wx.miniProgram.navigateTo) {
|
||
var _nav = window.wx.miniProgram.navigateTo.bind(window.wx.miniProgram);
|
||
window.wx.miniProgram.navigateTo = function (o) {
|
||
var opt = o || {};
|
||
dbg('navigateTo 调用 → ' + opt.url, 'ok');
|
||
var _fail = opt.fail, _succ = opt.success;
|
||
opt.fail = function (err) {
|
||
dbgErr('navigateTo fail: ' + ((err && err.errMsg) || '未知原因'));
|
||
if (_fail) { try { _fail(err); } catch (e) {} }
|
||
};
|
||
opt.success = function (res) {
|
||
dbg('navigateTo success', 'ok');
|
||
if (_succ) { try { _succ(res); } catch (e) {} }
|
||
};
|
||
try { _nav(opt); dbg('navigateTo 已调用(结果见 success/fail)', 'ok'); }
|
||
catch (ex) { dbgErr('navigateTo 抛异常: ' + (ex && ex.message ? ex.message : ex)); }
|
||
};
|
||
}
|
||
|
||
// 给 switchTab 打补丁:记录每一次跳转调用 + 捕获异常 + 转发 fail/success
|
||
if (window.wx && window.wx.miniProgram && window.wx.miniProgram.switchTab) {
|
||
var _st = window.wx.miniProgram.switchTab.bind(window.wx.miniProgram);
|
||
window.wx.miniProgram.switchTab = function (o) {
|
||
var opt = o || {};
|
||
dbg('switchTab 调用 → ' + opt.url, 'ok');
|
||
var _fail = opt.fail, _succ = opt.success;
|
||
opt.fail = function (err) {
|
||
dbgErr('switchTab fail: ' + ((err && err.errMsg) || '未知原因'));
|
||
if (_fail) { try { _fail(err); } catch (e) {} }
|
||
};
|
||
opt.success = function (res) {
|
||
dbg('switchTab success', 'ok');
|
||
if (_succ) { try { _succ(res); } catch (e) {} }
|
||
};
|
||
try { _st(opt); dbg('switchTab 已调用(结果见 success/fail)', 'ok'); }
|
||
catch (ex) { dbgErr('switchTab 抛异常: ' + (ex && ex.message ? ex.message : ex)); }
|
||
};
|
||
}
|
||
|
||
// 给 postMessage 打补丁:记录每一次调用 + 捕获异常
|
||
var origPost = (window.wx && window.wx.miniProgram && window.wx.miniProgram.postMessage) ? window.wx.miniProgram.postMessage.bind(window.wx.miniProgram) : null;
|
||
if (window.wx && window.wx.miniProgram) {
|
||
window.wx.miniProgram.postMessage = function (msg) {
|
||
dbg('postMessage 调用 → ' + JSON.stringify(msg), 'ok');
|
||
if (!origPost) { dbgErr('postMessage: 无原始方法,无法发送'); return; }
|
||
try { origPost(msg); dbg('postMessage 已发送', 'ok'); }
|
||
catch (ex) { dbgErr('postMessage 抛异常: ' + (ex && ex.message ? ex.message : ex)); }
|
||
};
|
||
} else {
|
||
dbg('未安装 postMessage 补丁(wx.miniProgram 不存在)', 'warn');
|
||
}
|
||
|
||
/* ============================================================
|
||
③ 页面数据渲染
|
||
============================================================ */
|
||
function render(a) {
|
||
var authed = a.isAuthorized();
|
||
dbg('render(): isAuthorized=' + authed + ' readAuth=' + JSON.stringify(a.readAuth()), 'muted');
|
||
el('authState').textContent = authed ? '✅ 已授权(昵称/头像/手机号)' : '🔓 未授权,点下方「授权登录」';
|
||
el('btnAuth').style.display = authed ? 'none' : 'block';
|
||
el('rPhone').textContent = a.readAuth().phone || '—';
|
||
el('rNick').textContent = a.readAuth().nickname || '—';
|
||
if (a.readAuth().avatar) { el('rAvatarWrap').innerHTML = '<img src="' + a.readAuth().avatar + '" alt="头像">'; }
|
||
}
|
||
|
||
/* ============================================================
|
||
④ 初始化:环境检测 + 读取授权
|
||
============================================================ */
|
||
function init() {
|
||
wxAuth.init(function (a) {
|
||
var envTxt = a.inWebview ? '小程序 web-view' : (a.MODE === 'wechat-browser' ? '微信浏览器' : '纯浏览器');
|
||
dbg('init 回调: envTxt=' + envTxt + ' MODE=' + a.MODE + ' inWebview=' + a.inWebview, 'ok');
|
||
el('envTxt').textContent = envTxt;
|
||
show(a.inWebview ? 'ok' : 'no', envTxt); // 修:之前 show() 从未被调用,标签永远停在初始「纯浏览器」
|
||
el('envHint').style.display = a.inWebview ? 'none' : 'block';
|
||
render(a);
|
||
});
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
init();
|
||
el('btnAuth').addEventListener('click', function () {
|
||
dbg('🖱️ 点击「授权登录」按钮');
|
||
if (!(window.wx && window.wx.miniProgram)) {
|
||
dbgErr('requestProfile 被拒绝:window.wx.miniProgram 不存在(不在微信 web-view 或 jweixin 未加载)');
|
||
} else {
|
||
dbg('requestProfile: UA 含 MicroMessenger = ' + (/MicroMessenger/i.test(navigator.userAgent) ? '✅ 是' : '❌ 否') + '(否→秒提示;是→getEnv 确认后跳转)', 'ok');
|
||
}
|
||
wxAuth.requestProfile();
|
||
});
|
||
el('btnMall').addEventListener('click', function () {
|
||
dbg('🖱️ 点击「进商城」按钮');
|
||
wxAuth.goMall();
|
||
});
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|