| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>微信扫码登录 - 官方实现</title>
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
- }
-
- body {
- background-color: #f5f7fa;
- color: #333;
- line-height: 1.6;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 20px;
- }
-
- .container {
- width: 100%;
- max-width: 900px;
- display: flex;
- flex-direction: column;
- align-items: center;
- }
-
- header {
- text-align: center;
- margin-bottom: 40px;
- width: 100%;
- }
-
- h1 {
- color: #2c3e50;
- margin-bottom: 10px;
- font-size: 2.5rem;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 15px;
- }
-
- .subtitle {
- color: #7f8c8d;
- font-size: 1.1rem;
- max-width: 600px;
- margin: 0 auto;
- }
-
- .content-wrapper {
- display: flex;
- flex-direction: row;
- width: 100%;
- gap: 30px;
- margin-bottom: 40px;
- }
-
- .login-section {
- flex: 1;
- background-color: white;
- border-radius: 16px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
- padding: 40px;
- display: flex;
- flex-direction: column;
- }
-
- .info-section {
- flex: 1;
- background-color: white;
- border-radius: 16px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
- padding: 40px;
- display: flex;
- flex-direction: column;
- }
-
- .section-title {
- font-size: 1.5rem;
- color: #2c3e50;
- margin-bottom: 25px;
- padding-bottom: 15px;
- border-bottom: 2px solid #f0f2f5;
- display: flex;
- align-items: center;
- gap: 10px;
- }
-
- #login_container {
- width: 100%;
- min-height: 320px;
- display: flex;
- justify-content: center;
- align-items: center;
- border-radius: 12px;
- background-color: #f8fafc;
- margin-bottom: 25px;
- border: 1px solid #eaeaea;
- }
-
- .qrcode-loading {
- text-align: center;
- color: #999;
- font-size: 1.1rem;
- }
-
- .qrcode-loading i {
- font-size: 3rem;
- margin-bottom: 15px;
- color: #07c160;
- display: block;
- }
-
- .status-indicator {
- margin-top: 20px;
- padding: 15px;
- border-radius: 10px;
- background-color: #f8fafc;
- border-left: 4px solid #07c160;
- }
-
- .status-title {
- font-weight: 600;
- color: #2c3e50;
- margin-bottom: 8px;
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .status-desc {
- color: #666;
- font-size: 0.95rem;
- }
-
- .steps-container {
- margin-top: 10px;
- }
-
- .step {
- display: flex;
- margin-bottom: 20px;
- padding-bottom: 20px;
- border-bottom: 1px solid #f0f2f5;
- }
-
- .step:last-child {
- margin-bottom: 0;
- padding-bottom: 0;
- border-bottom: none;
- }
-
- .step-number {
- width: 32px;
- height: 32px;
- border-radius: 50%;
- background-color: #07c160;
- color: white;
- display: flex;
- align-items: center;
- justify-content: center;
- font-weight: bold;
- margin-right: 15px;
- flex-shrink: 0;
- }
-
- .step-content h4 {
- color: #2c3e50;
- margin-bottom: 5px;
- }
-
- .step-content p {
- color: #666;
- font-size: 0.95rem;
- }
-
- .config-section {
- margin-top: 30px;
- background-color: #f8fafc;
- padding: 20px;
- border-radius: 10px;
- }
-
- .config-title {
- font-size: 1.1rem;
- color: #2c3e50;
- margin-bottom: 15px;
- display: flex;
- align-items: center;
- gap: 10px;
- }
-
- .config-item {
- margin-bottom: 15px;
- display: flex;
- flex-direction: column;
- }
-
- .config-item label {
- font-weight: 600;
- color: #555;
- margin-bottom: 5px;
- font-size: 0.95rem;
- }
-
- .config-item input {
- padding: 10px 12px;
- border-radius: 6px;
- border: 1px solid #ddd;
- font-size: 0.95rem;
- transition: border-color 0.3s;
- }
-
- .config-item input:focus {
- outline: none;
- border-color: #07c160;
- box-shadow: 0 0 0 2px rgba(7, 193, 96, 0.1);
- }
-
- .config-hint {
- color: #888;
- font-size: 0.85rem;
- margin-top: 5px;
- }
-
- .buttons-container {
- display: flex;
- gap: 15px;
- margin-top: 25px;
- flex-wrap: wrap;
- }
-
- .btn {
- padding: 12px 24px;
- border-radius: 8px;
- border: none;
- font-size: 1rem;
- font-weight: 600;
- cursor: pointer;
- transition: all 0.3s ease;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 8px;
- }
-
- .btn-primary {
- background-color: #07c160;
- color: white;
- }
-
- .btn-primary:hover {
- background-color: #06ad56;
- transform: translateY(-2px);
- box-shadow: 0 5px 15px rgba(7, 193, 96, 0.2);
- }
-
- .btn-secondary {
- background-color: #f0f2f5;
- color: #606266;
- }
-
- .btn-secondary:hover {
- background-color: #e4e6e9;
- transform: translateY(-2px);
- }
-
- .btn-refresh {
- background-color: #1677ff;
- color: white;
- }
-
- .btn-refresh:hover {
- background-color: #0d66d9;
- transform: translateY(-2px);
- box-shadow: 0 5px 15px rgba(22, 119, 255, 0.2);
- }
-
- .btn:disabled {
- opacity: 0.6;
- cursor: not-allowed;
- transform: none !important;
- box-shadow: none !important;
- }
-
- .note-box {
- margin-top: 25px;
- padding: 15px;
- border-radius: 10px;
- background-color: #fff9e6;
- border-left: 4px solid #ffc107;
- }
-
- .note-title {
- font-weight: 600;
- color: #e6a700;
- margin-bottom: 8px;
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .note-content {
- color: #8a6d3b;
- font-size: 0.9rem;
- line-height: 1.5;
- }
-
- .footer {
- margin-top: 40px;
- text-align: center;
- color: #95a5a6;
- font-size: 0.9rem;
- width: 100%;
- max-width: 900px;
- padding-top: 20px;
- border-top: 1px solid #eee;
- }
-
- .wechat-qrcode {
- text-align: center;
- margin-top: 20px;
- }
-
- .wechat-qrcode img {
- max-width: 200px;
- border-radius: 8px;
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
- }
-
- @media (max-width: 768px) {
- .content-wrapper {
- flex-direction: column;
- }
-
- h1 {
- font-size: 2rem;
- flex-direction: column;
- gap: 10px;
- }
-
- .login-section, .info-section {
- padding: 30px 20px;
- }
-
- .buttons-container {
- flex-direction: column;
- }
-
- .btn {
- width: 100%;
- }
- }
-
- @media (max-width: 480px) {
- .section-title {
- font-size: 1.3rem;
- }
-
- #login_container {
- min-height: 280px;
- }
- }
- </style>
- </head>
- <body>
- <div class="container">
- <header>
- <h1>
- <i class="fab fa-weixin"></i>
- 微信官方扫码登录
- </h1>
- <p class="subtitle">
- 本页面使用微信官方提供的JS SDK实现扫码登录功能。请按照下方步骤配置参数并体验微信扫码登录流程。
- </p>
- </header>
-
- <div class="content-wrapper">
- <div class="login-section">
- <h2 class="section-title">
- <i class="fas fa-qrcode"></i>
- 微信扫码登录
- </h2>
-
- <!-- 微信登录二维码容器 -->
- <div id="login_container">
- <div class="qrcode-loading">
- <i class="fas fa-spinner fa-spin"></i>
- <p>正在加载微信登录二维码...</p>
- </div>
- </div>
-
- <div class="status-indicator">
- <div class="status-title">
- <i class="fas fa-info-circle"></i>
- 状态监控
- </div>
- <p class="status-desc" id="statusText">等待二维码加载完成</p>
- </div>
-
- <div class="buttons-container">
- <button class="btn btn-refresh" id="refreshBtn">
- <i class="fas fa-sync-alt"></i>
- 刷新二维码
- </button>
- <button class="btn btn-secondary" id="testBtn">
- <i class="fas fa-mobile-alt"></i>
- 模拟扫码测试
- </button>
- </div>
-
- <div class="note-box">
- <div class="note-title">
- <i class="fas fa-exclamation-triangle"></i>
- 重要提示
- </div>
- <div class="note-content">
- 此演示使用了测试参数,在实际生产环境中需要:
- 1. 在微信开放平台注册应用获取真实appid
- 2. 配置有效的redirect_uri(授权回调地址)
- 3. 部署在备案的域名下(微信要求HTTPS)
- </div>
- </div>
- </div>
-
- <div class="info-section">
- <h2 class="section-title">
- <i class="fas fa-cogs"></i>
- 配置说明
- </h2>
-
- <div class="steps-container">
- <div class="step">
- <div class="step-number">1</div>
- <div class="step-content">
- <h4>获取AppID</h4>
- <p>访问微信开放平台,注册并创建网站应用,获取AppID和AppSecret</p>
- </div>
- </div>
-
- <div class="step">
- <div class="step-number">2</div>
- <div class="step-content">
- <h4>配置回调地址</h4>
- <p>在微信开放平台配置授权回调域名,必须与redirect_uri域名一致</p>
- </div>
- </div>
-
- <div class="step">
- <div class="step-number">3</div>
- <div class="step-content">
- <h4>部署代码</h4>
- <p>将配置好参数的代码部署到已备案的域名(必须支持HTTPS)</p>
- </div>
- </div>
- </div>
-
- <div class="config-section">
- <h3 class="config-title">
- <i class="fas fa-sliders-h"></i>
- 参数配置
- </h3>
-
- <div class="config-item">
- <label for="appid">AppID</label>
- <input type="text" id="appid" placeholder="请输入微信开放平台AppID" value="wxbdc5610cc59c1631">
- <div class="config-hint">此处为测试AppID,实际使用时请替换为自己的AppID</div>
- </div>
-
- <div class="config-item">
- <label for="redirect_uri">Redirect URI</label>
- <input type="text" id="redirect_uri" placeholder="请输入授权回调地址" value="https://open.weixin.qq.com/connect/qrconnect">
- <div class="config-hint">用户授权后微信会跳转到此地址,需与开放平台配置一致</div>
- </div>
-
- <div class="config-item">
- <label for="scope">Scope</label>
- <input type="text" id="scope" placeholder="请输入授权作用域" value="snsapi_login">
- <div class="config-hint">应用授权作用域,snsapi_login为微信登录专用</div>
- </div>
-
- <div class="buttons-container">
- <button class="btn btn-primary" id="applyConfigBtn">
- <i class="fas fa-check-circle"></i>
- 应用配置
- </button>
- <button class="btn btn-secondary" id="resetConfigBtn">
- <i class="fas fa-undo"></i>
- 重置配置
- </button>
- </div>
- </div>
-
- <div class="wechat-qrcode">
- <p>使用微信扫描体验</p>
- <img src="https://res.wx.qq.com/a/wx_fed/assets/res/OTE0YTAw.png" alt="微信扫码">
- </div>
- </div>
- </div>
-
- <footer class="footer">
- <p>本页面使用微信官方JS SDK实现扫码登录功能 | 请遵循微信开放平台相关规范</p>
- <p>© 2023 微信扫码登录演示 | 仅用于学习和演示目的</p>
- </footer>
- </div>
- <!-- 微信官方JS SDK -->
- <script src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
-
- <script>
- // 微信登录对象
- let wxLoginObj = null;
-
- // 默认配置参数
- const defaultConfig = {
- self_redirect: true,
- id: "login_container",
- appid: "wxbdc5610cc59c1631", // 微信提供的测试AppID:wxbdc5610cc59c1631
- scope: "snsapi_login",
- redirect_uri: encodeURIComponent("https://passport.yhd.com/wechat/callback.do"),
- state: "STATE", // 可以自定义的参数,用于防止CSRF攻击
- style: "black", // 二维码样式:black黑色,white白色
- href: "" // 自定义样式链接,可以为空
- };
-
- // DOM元素
- const loginContainer = document.getElementById('login_container');
- const statusText = document.getElementById('statusText');
- const refreshBtn = document.getElementById('refreshBtn');
- const testBtn = document.getElementById('testBtn');
- const applyConfigBtn = document.getElementById('applyConfigBtn');
- const resetConfigBtn = document.getElementById('resetConfigBtn');
- const appidInput = document.getElementById('appid');
- const redirectUriInput = document.getElementById('redirect_uri');
- const scopeInput = document.getElementById('scope');
-
- // 初始化函数
- function initWeChatLogin(config) {
- // 清除现有二维码
- loginContainer.innerHTML = '<div class="qrcode-loading"><i class="fas fa-spinner fa-spin"></i><p>正在加载微信登录二维码...</p></div>';
- statusText.textContent = "正在初始化微信登录...";
-
- // 销毁之前的实例
- if (wxLoginObj) {
- // 微信官方SDK没有提供销毁方法,我们只能替换容器内容
- loginContainer.innerHTML = '';
- }
-
- // 创建微信登录实例
- try {
- wxLoginObj = new WxLogin({
- self_redirect: config.self_redirect,
- id: config.id,
- appid: config.appid,
- scope: config.scope,
- redirect_uri: config.redirect_uri,
- state: config.state,
- style: config.style,
- href: config.href,
- onReady: function(isReady) {
- console.log("微信登录二维码加载状态:", isReady);
- if (isReady) {
- statusText.textContent = "二维码已就绪,请使用微信扫描";
- refreshBtn.disabled = false;
- } else {
- statusText.textContent = "二维码加载失败,请检查配置";
- }
- }
- });
-
- // 监听二维码状态变化(模拟)
- setTimeout(() => {
- statusText.textContent = "二维码已加载完成,请使用微信扫描";
- }, 1000);
-
- } catch (error) {
- console.error("微信登录初始化失败:", error);
- statusText.textContent = "微信登录初始化失败: " + error.message;
- loginContainer.innerHTML = `<div class="qrcode-loading" style="color:#f44336;">
- <i class="fas fa-exclamation-triangle"></i>
- <p>微信登录初始化失败</p>
- <p style="font-size:0.8rem; margin-top:10px;">${error.message}</p>
- </div>`;
- }
- }
-
- // 刷新二维码
- function refreshQRCode() {
- // 获取当前配置
- const currentConfig = {
- ...defaultConfig,
- appid: appidInput.value.trim() || defaultConfig.appid,
- scope: scopeInput.value.trim() || defaultConfig.scope,
- redirect_uri: encodeURIComponent(redirectUriInput.value.trim() || defaultConfig.redirect_uri)
- };
-
- // 重新初始化
- initWeChatLogin(currentConfig);
-
- // 禁用按钮避免重复点击
- refreshBtn.disabled = true;
- refreshBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 刷新中...';
-
- // 3秒后启用按钮
- setTimeout(() => {
- refreshBtn.disabled = false;
- refreshBtn.innerHTML = '<i class="fas fa-sync-alt"></i> 刷新二维码';
- }, 30000);
- }
-
- // 模拟扫码测试
- function simulateScanTest() {
- statusText.textContent = "已模拟扫描,请在手机上确认登录";
- testBtn.disabled = true;
- testBtn.innerHTML = '<i class="fas fa-check-circle"></i> 已模拟扫码';
-
- // 5秒后重置
- setTimeout(() => {
- statusText.textContent = "二维码已就绪,请使用微信扫描";
- testBtn.disabled = false;
- testBtn.innerHTML = '<i class="fas fa-mobile-alt"></i> 模拟扫码测试';
- }, 5000);
- }
-
- // 应用配置
- function applyConfig() {
- // 验证配置
- const appid = appidInput.value.trim();
- const redirectUri = redirectUriInput.value.trim();
-
- if (!appid) {
- alert("请填写AppID");
- appidInput.focus();
- return;
- }
-
- if (!redirectUri) {
- alert("请填写Redirect URI");
- redirectUriInput.focus();
- return;
- }
-
- // 应用配置并刷新二维码
- applyConfigBtn.disabled = true;
- applyConfigBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 应用中...';
-
- setTimeout(() => {
- refreshQRCode();
- applyConfigBtn.disabled = false;
- applyConfigBtn.innerHTML = '<i class="fas fa-check-circle"></i> 应用配置';
- }, 1000);
- }
-
- // 重置配置
- function resetConfig() {
- appidInput.value = defaultConfig.appid;
- redirectUriInput.value = decodeURIComponent(defaultConfig.redirect_uri);
- scopeInput.value = defaultConfig.scope;
-
- // 应用重置的配置
- applyConfig();
- }
-
- // 页面加载完成后初始化
- document.addEventListener('DOMContentLoaded', function() {
- // 初始化微信登录
- initWeChatLogin(defaultConfig);
-
- // 设置输入框默认值
- appidInput.value = defaultConfig.appid;
- redirectUriInput.value = decodeURIComponent(defaultConfig.redirect_uri);
- scopeInput.value = defaultConfig.scope;
-
- // 绑定按钮事件
- refreshBtn.addEventListener('click', refreshQRCode);
- testBtn.addEventListener('click', simulateScanTest);
- applyConfigBtn.addEventListener('click', applyConfig);
- resetConfigBtn.addEventListener('click', resetConfig);
-
- // 监听输入框变化
- appidInput.addEventListener('input', function() {
- applyConfigBtn.disabled = false;
- });
-
- redirectUriInput.addEventListener('input', function() {
- applyConfigBtn.disabled = false;
- });
-
- scopeInput.addEventListener('input', function() {
- applyConfigBtn.disabled = false;
- });
-
- // 添加键盘快捷键
- document.addEventListener('keydown', function(e) {
- // Ctrl+R 刷新二维码
- if (e.ctrlKey && e.key === 'r') {
- e.preventDefault();
- if (!refreshBtn.disabled) refreshQRCode();
- }
-
- // Enter 在输入框时应用配置
- if (e.key === 'Enter' && document.activeElement.tagName === 'INPUT') {
- applyConfig();
- }
- });
- });
-
- // 页面可见性变化时刷新二维码
- document.addEventListener('visibilitychange', function() {
- if (!document.hidden && wxLoginObj) {
- // 页面从后台切换回来时刷新二维码
- setTimeout(refreshQRCode, 500);
- }
- });
- </script>
- </body>
- </html>
|