fix:修复页面bug
This commit is contained in:
@@ -596,36 +596,53 @@
|
||||
</section>
|
||||
|
||||
<aside>
|
||||
<div class="qr-card">
|
||||
<div class="qr-header">
|
||||
<span>扫码登录二维码</span>
|
||||
<span class="badge" id="login-state-badge">未创建</span>
|
||||
</div>
|
||||
<div class="qr-box">
|
||||
<div class="qr-img" id="qr-img-box">
|
||||
<div class="qr-placeholder" id="qr-placeholder">
|
||||
点击左侧「获取登录二维码」后,将在这里显示二维码图片或登录链接信息。
|
||||
<div id="qr-area">
|
||||
<div class="qr-card">
|
||||
<div class="qr-header">
|
||||
<span>扫码登录二维码</span>
|
||||
<span class="badge" id="login-state-badge">未创建</span>
|
||||
</div>
|
||||
<div class="qr-box">
|
||||
<div class="qr-img" id="qr-img-box">
|
||||
<div class="qr-placeholder" id="qr-placeholder">
|
||||
点击左侧「获取登录二维码」后,将在这里显示二维码图片或登录链接信息。
|
||||
</div>
|
||||
</div>
|
||||
<div class="hint" id="qr-hint">
|
||||
请使用微信客户端扫描二维码完成登录。
|
||||
</div>
|
||||
</div>
|
||||
<div class="hint" id="qr-hint">
|
||||
请使用微信客户端扫描二维码完成登录。
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 12px"></div>
|
||||
<div class="card">
|
||||
<div class="small-label">当前账号在线状态</div>
|
||||
<div class="login-state unknown" id="login-state-text">未知</div>
|
||||
<div class="small-label" id="login-extra">尚未查询</div>
|
||||
</div>
|
||||
<div id="slider-reopen-wrap" style="display: none; margin-top: 12px;">
|
||||
<button type="button" class="secondary" id="btn-show-slider">打开滑块验证</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="height: 12px"></div>
|
||||
|
||||
<div class="card">
|
||||
<div class="small-label">当前账号在线状态</div>
|
||||
<div class="login-state unknown" id="login-state-text">未知</div>
|
||||
<div class="small-label" id="login-extra">尚未查询</div>
|
||||
<div id="slider-area" style="display: none;">
|
||||
<div class="card" style="max-width: 480px;">
|
||||
<div class="card-title">滑块验证</div>
|
||||
<div id="slider-app" data-v-app="">
|
||||
<div class="params-section" style="margin-bottom: 12px;">
|
||||
<label class="form-label" for="keyInput">Key:</label>
|
||||
<input type="text" class="form-control" id="keyInput" placeholder="请输入key" style="width:100%;box-sizing:border-box;padding:8px;margin-bottom:8px;border:1px solid var(--border);border-radius:8px;background:rgba(15,23,42,0.6);color:var(--text);">
|
||||
<label class="form-label" for="data62Input">Data62:</label>
|
||||
<input type="text" class="form-control" id="data62Input" placeholder="请输入data62" style="width:100%;box-sizing:border-box;padding:8px;margin-bottom:8px;border:1px solid var(--border);border-radius:8px;background:rgba(15,23,42,0.6);color:var(--text);">
|
||||
<label class="form-label" for="originalTicketInput">Original Ticket:</label>
|
||||
<input type="text" class="form-control" id="originalTicketInput" placeholder="请输入original_ticket" style="width:100%;box-sizing:border-box;padding:8px;margin-bottom:12px;border:1px solid var(--border);border-radius:8px;background:rgba(15,23,42,0.6);color:var(--text);">
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button type="button" class="btn btn-verify btn-lg" id="slider-btn-verify" disabled style="padding:10px 20px;background:var(--accent);color:#fff;border:none;border-radius:8px;cursor:pointer;">开始验证</button>
|
||||
<div class="text-muted mt-2" style="font-size:12px;color:var(--muted);margin-top:8px;">请先填写完整的参数信息</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="slider-card" id="slider-card" style="display: none">
|
||||
<div class="small-label">需完成滑块验证,已在弹窗打开验证页(与 7765 同结构,本地实现)</div>
|
||||
<a id="slider-open-link" href="#" target="_blank" rel="noopener" class="btn secondary">重新打开滑块验证</a>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
@@ -634,15 +651,78 @@
|
||||
<script>
|
||||
const $ = (id) => document.getElementById(id);
|
||||
|
||||
// 相对路径,由 Node 代理到后端,适配 -p/-b 任意端口
|
||||
const API_BASE = '';
|
||||
// 相对路径由 Node 代理到后端;若从 file:// 打开则请求会失败,改用同源或默认地址
|
||||
const API_BASE = (typeof location !== 'undefined' && location.origin && (location.protocol === 'http:' || location.protocol === 'https:'))
|
||||
? '' : (typeof location !== 'undefined' ? 'http://127.0.0.1:3000' : '');
|
||||
|
||||
const state = {
|
||||
pollingScan: null,
|
||||
pollingOnline: null,
|
||||
sliderOpened: false,
|
||||
sliderParams: null,
|
||||
sliderScriptLoaded: false,
|
||||
sliderListenersBound: false,
|
||||
};
|
||||
|
||||
function parseSliderUrlParams(sliderUrl) {
|
||||
if (!sliderUrl || typeof sliderUrl !== 'string') return null;
|
||||
const q = sliderUrl.indexOf('?');
|
||||
if (q === -1) return null;
|
||||
const params = {};
|
||||
sliderUrl.slice(q + 1).split('&').forEach(function(pair) {
|
||||
const i = pair.indexOf('=');
|
||||
if (i > 0) params[decodeURIComponent(pair.slice(0, i).replace(/\+/g, ' '))] = decodeURIComponent((pair.slice(i + 1) || '').replace(/\+/g, ' '));
|
||||
});
|
||||
return params;
|
||||
}
|
||||
|
||||
function showSliderAreaAndFill(params) {
|
||||
var qrArea = $('qr-area');
|
||||
var sliderArea = $('slider-area');
|
||||
if (!qrArea || !sliderArea) return;
|
||||
qrArea.style.display = 'none';
|
||||
sliderArea.style.display = 'block';
|
||||
var keyInput = $('keyInput');
|
||||
var data62Input = $('data62Input');
|
||||
var ticketInput = $('originalTicketInput');
|
||||
var btnVerify = $('slider-btn-verify');
|
||||
if (params) {
|
||||
if (keyInput) keyInput.value = params.key || params.Key || '';
|
||||
if (data62Input) data62Input.value = params.data62 || '';
|
||||
if (ticketInput) ticketInput.value = params.ticket || params.original_ticket || '';
|
||||
}
|
||||
function toggleVerifyBtn() {
|
||||
if (!btnVerify) return;
|
||||
var hasAll = keyInput && data62Input && ticketInput &&
|
||||
(keyInput.value || '').trim() && (data62Input.value || '').trim() && (ticketInput.value || '').trim();
|
||||
btnVerify.disabled = !hasAll;
|
||||
}
|
||||
if (!state.sliderListenersBound) {
|
||||
state.sliderListenersBound = true;
|
||||
if (keyInput) keyInput.addEventListener('input', toggleVerifyBtn);
|
||||
if (data62Input) data62Input.addEventListener('input', toggleVerifyBtn);
|
||||
if (ticketInput) ticketInput.addEventListener('input', toggleVerifyBtn);
|
||||
}
|
||||
toggleVerifyBtn();
|
||||
if (!state.sliderScriptLoaded) {
|
||||
state.sliderScriptLoaded = true;
|
||||
var script = document.createElement('script');
|
||||
script.type = 'module';
|
||||
script.crossOrigin = 'anonymous';
|
||||
script.src = (API_BASE || '') + '/auth/slider-assets/N_jYM_2V.js';
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
function showQrArea() {
|
||||
var qrArea = $('qr-area');
|
||||
var sliderArea = $('slider-area');
|
||||
var reopenWrap = $('slider-reopen-wrap');
|
||||
if (qrArea) qrArea.style.display = 'block';
|
||||
if (sliderArea) sliderArea.style.display = 'none';
|
||||
if (reopenWrap) reopenWrap.style.display = state.sliderParams ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function log(message, level = 'info') {
|
||||
const logBody = $('log-body');
|
||||
const line = document.createElement('div');
|
||||
@@ -883,14 +963,22 @@
|
||||
}
|
||||
|
||||
async function callApi(path, options = {}) {
|
||||
const url = API_BASE + path;
|
||||
const res = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(options.headers || {}),
|
||||
},
|
||||
});
|
||||
const url = (API_BASE || '') + path;
|
||||
let res;
|
||||
try {
|
||||
res = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(options.headers || {}),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = (e && e.message) ? String(e.message) : String(e);
|
||||
const isRefused = /failed to fetch|networkerror|connection refused|load failed/i.test(msg);
|
||||
log(isRefused ? '无法连接服务(ERR_CONNECTION_REFUSED):请通过前端地址访问(如 http://localhost:3000)并确保已启动 run-dev.sh 或 Docker。' : '请求失败: ' + msg, 'error');
|
||||
throw new Error(isRefused ? '无法连接服务,请确认已通过 http://localhost:3000 打开页面且前后端已启动。' : msg);
|
||||
}
|
||||
let body = null;
|
||||
try {
|
||||
body = await res.json();
|
||||
@@ -922,10 +1010,11 @@
|
||||
log('获取二维码成功');
|
||||
renderQrFromResponse(data);
|
||||
updateLoginState('等待扫码 / 确认中', 'pending', '请在 60 秒内使用微信扫码。');
|
||||
// 启动自动轮询,每 5 秒检测一次扫码状态
|
||||
// 扫完码后自动检测:先 2 秒做一次检测,再每 5 秒轮询,避免误判为「二维码失效」
|
||||
if (state.pollingScan) {
|
||||
clearInterval(state.pollingScan);
|
||||
}
|
||||
setTimeout(() => onCheckScanStatus(true), 2000);
|
||||
state.pollingScan = setInterval(() => {
|
||||
onCheckScanStatus(true);
|
||||
}, 5000);
|
||||
@@ -954,41 +1043,50 @@
|
||||
const d = obj.Data && typeof obj.Data === 'object' ? obj.Data : obj;
|
||||
const stateVal = d.state ?? d.State;
|
||||
|
||||
// 后端返回滑块页 path,在新窗口打开(本地页,与 7765 同 DOM 结构 + 加载 7765 的 module 脚本),不用 iframe
|
||||
// 后端返回滑块 path,在当前页右侧替换为滑块区域并自动填充参数
|
||||
const sliderUrl = data.slider_url;
|
||||
if (sliderUrl && typeof sliderUrl === 'string') {
|
||||
state.sliderOpened = true;
|
||||
log('需完成滑块验证,已在新窗口打开验证页。', 'warn');
|
||||
const sliderCard = $('slider-card');
|
||||
const openLink = $('slider-open-link');
|
||||
const fullUrl = sliderUrl.startsWith('/') ? (API_BASE + sliderUrl) : sliderUrl;
|
||||
if (sliderCard) sliderCard.style.display = 'flex';
|
||||
if (openLink) {
|
||||
openLink.href = fullUrl;
|
||||
openLink.target = '_blank';
|
||||
}
|
||||
try { window.open(fullUrl, 'slider-verify', 'width=520,height=520,scrollbars=yes'); } catch (e) { log('弹窗被拦截,请点击上方链接打开验证页', 'warn'); }
|
||||
log('需完成滑块验证,已切换到滑块验证区域。', 'warn');
|
||||
const params = parseSliderUrlParams(sliderUrl);
|
||||
if (params) state.sliderParams = params;
|
||||
showSliderAreaAndFill(params);
|
||||
}
|
||||
|
||||
// state == 2 → 登录成功,跳转后端管理页
|
||||
if (stateVal === 2 || lower.includes('\"state\":2') || /\"state\"\\s*:\\s*2/.test(lower)) {
|
||||
updateLoginState('登录成功', 'ok', text);
|
||||
updateLoginState('登录成功', 'ok', '');
|
||||
if (state.pollingScan) {
|
||||
clearInterval(state.pollingScan);
|
||||
state.pollingScan = null;
|
||||
}
|
||||
showQrExpired('登录成功,正在跳转…');
|
||||
window.location.href = 'manage.html';
|
||||
// state == 1 → 二维码失效 / 不可用(按你的规则)
|
||||
// state == 1 → 先判定是否明确「过期」,否则一律视为可能需验证,继续轮询并优先根据 slider_url 切到滑块
|
||||
} else if (stateVal === 1 || lower.includes('\"state\":1') || /\"state\"\\s*:\\s*1/.test(lower)) {
|
||||
updateLoginState('二维码已过期,请重新获取', 'offline', text);
|
||||
if (state.pollingScan) {
|
||||
clearInterval(state.pollingScan);
|
||||
state.pollingScan = null;
|
||||
var needVerify = /请提交验证码|提交验证码后登录|验证码后登录|安全验证|完成验证/.test(text) ||
|
||||
(d && (d.msg && /请提交验证码|验证码后登录|安全验证|完成验证/.test(d.msg)) || (obj.Text && /请提交验证码|验证码后登录|安全验证|完成验证/.test(obj.Text)));
|
||||
var explicitExpired = /二维码已过期|已过期|已失效|失效|请重新获取|重新获取二维码/.test(text) ||
|
||||
(d && (d.msg && /已过期|已失效|失效|请重新获取/.test(d.msg)) || (obj.Text && /已过期|已失效|失效|请重新获取/.test(obj.Text)));
|
||||
if (explicitExpired && !sliderUrl) {
|
||||
updateLoginState('二维码已过期,请重新获取', 'offline', '');
|
||||
if (state.pollingScan) {
|
||||
clearInterval(state.pollingScan);
|
||||
state.pollingScan = null;
|
||||
}
|
||||
showQrExpired('二维码已过期,请重新获取');
|
||||
} else {
|
||||
updateLoginState(needVerify || sliderUrl ? '请完成滑块验证' : '正在确认登录状态…', 'pending',
|
||||
needVerify || sliderUrl ? '扫码完成,请完成验证后登录' : '');
|
||||
// 判定到需验证时直接切换验证模块,不等用户点「检测扫码状态」;有 slider_url 已在上方填参,无则先切过去,后续轮询会带参
|
||||
if (needVerify || sliderUrl) {
|
||||
state.sliderOpened = true;
|
||||
if (!sliderUrl) log('需完成滑块验证,已切换到滑块验证区域,等待参数…', 'warn');
|
||||
showSliderAreaAndFill(state.sliderParams || {});
|
||||
}
|
||||
}
|
||||
showQrExpired('二维码已过期,请重新获取');
|
||||
} else {
|
||||
updateLoginState('扫码状态更新', 'pending', text);
|
||||
updateLoginState('扫码状态更新', 'pending', '');
|
||||
}
|
||||
} catch (e) {
|
||||
if (!silent) log('检测扫码状态失败: ' + e.message, 'error');
|
||||
@@ -1008,9 +1106,9 @@
|
||||
log('在线状态: ' + text);
|
||||
const lower = text.toLowerCase();
|
||||
if (lower.includes('online') || lower.includes('true') || lower.includes('登录')) {
|
||||
updateLoginState('当前账号在线', 'ok', text);
|
||||
updateLoginState('当前账号在线', 'ok', '');
|
||||
} else {
|
||||
updateLoginState('当前账号离线或未登录', 'offline', text);
|
||||
updateLoginState('当前账号离线或未登录', 'offline', '');
|
||||
}
|
||||
} catch (e) {
|
||||
log('获取在线状态失败: ' + e.message, 'error');
|
||||
@@ -1047,8 +1145,14 @@
|
||||
function bindEvents() {
|
||||
$('btn-qrcode').addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
if ($('slider-area') && $('slider-area').style.display !== 'none') {
|
||||
showQrArea();
|
||||
}
|
||||
onGetQrCode();
|
||||
});
|
||||
$('btn-show-slider') && $('btn-show-slider').addEventListener('click', function() {
|
||||
if (state.sliderParams) showSliderAreaAndFill(state.sliderParams);
|
||||
});
|
||||
$('btn-check-scan').addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
onCheckScanStatus(false);
|
||||
|
||||
Reference in New Issue
Block a user