diff --git a/miniprogram/src/pages/scan-login/index.jsx b/miniprogram/src/pages/scan-login/index.jsx index e80ec42..6857e71 100644 --- a/miniprogram/src/pages/scan-login/index.jsx +++ b/miniprogram/src/pages/scan-login/index.jsx @@ -1,66 +1,124 @@ -import { View, Text, Button } from '@tarojs/components'; +import { View, Text, Button, Input } from '@tarojs/components'; import Taro, { useDidShow } from '@tarojs/taro'; import { useState } from 'react'; -import { mpQrConfirm } from '@/utils/auth'; +import { isAuthed, wxLogin, phoneLogin, sendCode, mpQrConfirm } from '@/utils/auth'; import './index.scss'; /** - * 小程序扫码登录确认页:由「小程序码(wxacode)」自动打开(微信扫一扫→自动跳到本页), - * 或用户在小程序内「我的→扫码登录」扫二维码后进入。读取携带的 scene, - * 复用当前微信登录资格(Taro.login→code→平台归户)完成目标端口登录。 + * 小程序扫码登录确认页:由「小程序码(wxacode)」自动打开,或「我的→扫码登录」扫码进入。 + * 规则:若小程序当前未登录 → 必须**先登录**(一键微信登录 / 手机号验证码)才能进行扫码确认; + * 登录后复用该微信身份 `Taro.login` 完成目标端口登录/绑定。 */ export default function ScanLogin() { const [scene, setScene] = useState(''); + const [authed, setAuthed] = useState(isAuthed()); const [busy, setBusy] = useState(false); const [msg, setMsg] = useState(''); const [done, setDone] = useState(false); + const [loginMode, setLoginMode] = useState<'wx' | 'phone'>('wx'); + const [gatePhone, setGatePhone] = useState(''); + const [gateCode, setGateCode] = useState(''); + const [cd, setCd] = useState(0); useDidShow(() => { const opts = Taro.getEnterOptionsSync(); const q = (opts && opts.query) || {}; - // wxacode 的 scene 会放进 launch options.query.scene;普通 navigateTo 带 ?scene= 同理 const s = String(q.scene || opts.scene || ''); setScene(s); - setDone(false); - setMsg(''); + setDone(false); setMsg(''); }); - const confirm = async () => { - if (!scene) { setMsg('无效的登录请求(缺少场景)'); return; } + const wxLoginNow = async () => { setBusy(true); setMsg(''); try { - const { code } = await Taro.login(); // 复用微信登录资格 - await mpQrConfirm(scene, code); // 目标端口轮询取到令牌 - setDone(true); - setMsg('登录成功,请回到电脑 / 网页查看'); - } catch (e) { - setMsg((e && e.message) || '登录失败,请重试'); - } finally { setBusy(false); } + const { code } = await Taro.login(); + await wxLogin({ code }); + setAuthed(true); + setMsg('已登录,请确认'); + } catch (e) { setMsg((e && e.message) || '微信登录失败'); } + finally { setBusy(false); } }; - const cancel = () => { - // 取消:返回上一页;无上一页则回首页 - Taro.navigateBack().catch(() => Taro.switchTab({ url: '/pages/index/index' })); + const sendSms = async () => { + setMsg(''); + if (!/^1\d{10}$/.test(gatePhone)) { setMsg('请输入正确的 11 位手机号'); return; } + setBusy(true); + try { + const r = await sendCode(gatePhone); + setMsg(`验证码已发送${r.debugCode ? `(演示 ${r.debugCode})` : ''}`); + setCd(60); + const t = window.setInterval(() => setCd((c) => (c <= 1 ? (window.clearInterval(t), 0) : c - 1)), 1000); + } catch (e) { setMsg((e && e.message) || '发送失败'); } + finally { setBusy(false); } }; + const phoneLoginNow = async () => { + setMsg(''); + if (!/^1\d{10}$/.test(gatePhone) || !gateCode) { setMsg('请填写手机号与验证码'); return; } + setBusy(true); + try { await phoneLogin(gatePhone, gateCode); setAuthed(true); setMsg('已登录,请确认'); } + catch (e) { setMsg((e && e.message) || '登录失败'); } + finally { setBusy(false); } + }; + + const confirm = async () => { + setMsg(''); + if (!scene) { setMsg('无效的扫码请求'); return; } + setBusy(true); + try { + const { code } = await Taro.login(); + const r = await mpQrConfirm(scene, code); + setDone(true); + setMsg(r && r.bound ? '绑定成功,请回电脑/网页查看' : '登录成功,请回电脑/网页查看'); + } catch (e) { setMsg((e && e.message) || '操作失败,请重试'); } + finally { setBusy(false); } + }; + + const cancel = () => { Taro.navigateBack().catch(() => Taro.switchTab({ url: '/pages/index/index' })); }; return ( - {done ? '✓' : '🔐'} - {done ? '已登录' : '扫码登录确认'} - - {done ? msg : '确认使用当前微信账号登录你正在操作的端口?'} - - {!done && scene && 登录请求 #{scene.slice(0, 12)}} - {msg && !done && {msg}} - {!done ? ( - - - - + {/* 未登录:先登录 */} + {!authed ? ( + <> + 🔐 + 请先登录小程序 + 为完成扫码登录 / 绑定,请先登录小程序(复用你的微信/手机号身份)。 + + setLoginMode('wx')}>微信一键登录 + setLoginMode('phone')}>手机号登录 + + {loginMode === 'wx' ? ( + + ) : ( + + setGatePhone(e.detail.value)} /> + + setGateCode(e.detail.value)} /> + + + + + )} + ) : ( - + <> + {done ? '✓' : '✅'} + {done ? '已完成' : '确认扫码操作'} + + {done ? msg : `确认使用当前微信账号${scene.includes('bind') || true ? '' : ''}在你操作的端口${scene ? '' : ' '}完成登录 / 绑定?`} + + {!done && scene && 请求 #{scene.slice(0, 12)}} + {!done && ( + + + + + )} + {done && } + )} + {msg && {msg}} ); diff --git a/miniprogram/src/pages/scan-login/index.scss b/miniprogram/src/pages/scan-login/index.scss index 48f4307..fdcb16f 100644 --- a/miniprogram/src/pages/scan-login/index.scss +++ b/miniprogram/src/pages/scan-login/index.scss @@ -11,3 +11,13 @@ .sl-scene { display: block; margin-top: 12rpx; font-size: 23rpx; color: #8e8e8e; } .sl-err { display: block; margin-top: 16rpx; font-size: 25rpx; color: #f0b8b8; } .sl-actions { display: flex; flex-direction: column; gap: 16rpx; margin-top: 40rpx; } + +/* 登录门 */ +.sl-tabs { display: flex; gap: 4px; margin: 24rpx 0; padding: 4px; border-radius: 12px; background: #161618; border: 1px solid #2a2a2d; } +.sl-tab { flex: 1; text-align: center; padding: 12rpx 0; border-radius: 9px; color: #9b9b9b; font-size: 26rpx; } +.sl-tab.on { background: #fff; color: #111; } +.sl-field { display: flex; flex-direction: column; gap: 14rpx; margin-top: 8rpx; } +.sl-code { display: flex; gap: 12rpx; align-items: center; } +.sl-code .input { flex: 1; } +.btn-sm { font-size: 24rpx; padding: 0 24rpx; line-height: 2.4; } +.sl-err { display: block; margin-top: 20rpx; font-size: 25rpx; color: #f0b8b8; }