diff --git a/.workbuddy/expert-history.json b/.workbuddy/expert-history.json index e8fc3ce..5f73fa1 100644 --- a/.workbuddy/expert-history.json +++ b/.workbuddy/expert-history.json @@ -13,5 +13,5 @@ } ] }, - "lastUpdated": 1775583343042 + "lastUpdated": 1775584636978 } \ No newline at end of file diff --git a/.workbuddy/memory/2026-04-08.md b/.workbuddy/memory/2026-04-08.md new file mode 100644 index 0000000..3779d76 --- /dev/null +++ b/.workbuddy/memory/2026-04-08.md @@ -0,0 +1,42 @@ +# 2026-04-08 工作记录 + +## passport/qr-confirm/index.tsx 页面改造 + +### 改造内容 +将扫码登录确认页面的授权登录界面改造成参考权大师的设计风格: + +1. **UI 改造** + - 顶部标题"请登录" + - 中间 Logo + 品牌名 + 标语(权大师风格) + - 橙色主按钮"手机号授权登录" + - 灰色"取消"按钮 + - 底部协议勾选(带链接可点击查看详情) + +2. **新增功能** + - 协议勾选状态管理 + - 服务协议和隐私政策弹窗 + - 未勾选协议时点击授权按钮会提示 + +3. **保留功能** + - 已注册用户的自动登录确认流程 + - 未注册用户的手机号授权登录 + - 扫码登录成功后的跳转逻辑 + +### 页面流程 +``` +用户扫码 → qr-confirm 页面 + ↓ +检测用户状态 + ↓ +┌──────────────┬──────────────┐ + 已注册 未注册 + ↓ ↓ +自动确认登录 显示授权登录界面 + ↓ ↓ +登录成功 一键授权登录 +``` + +### 技术要点 +- 使用 Taro 的 Button 组件 openType="getPhoneNumber" 获取微信手机号授权 +- 授权前校验协议勾选状态 +- 调用后端 loginByMpWxPhone 接口(带 notVerifyPhone: true 自动注册) diff --git a/src/passport/qr-confirm/index.tsx b/src/passport/qr-confirm/index.tsx index 7acfa5e..f872826 100644 --- a/src/passport/qr-confirm/index.tsx +++ b/src/passport/qr-confirm/index.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; -import { View, Text } from '@tarojs/components'; -import { Button, Loading, Card } from '@nutui/nutui-react-taro'; -import { Success, Failure, Tips, User } from '@nutui/icons-react-taro'; +import { View, Text, Button as TaroButton } from '@tarojs/components'; +import { Loading, Card } from '@nutui/nutui-react-taro'; +import { Success, Failure, Tips, User, Checklist } from '@nutui/icons-react-taro'; import Taro, { useRouter } from '@tarojs/taro'; import { confirmQRLogin } from '@/api/passport/qr-login'; import { loginByOpenId } from '@/api/passport/wx-login'; @@ -51,6 +51,9 @@ interface LoginResponse { message: string; } +// 协议弹窗类型 +type AgreementType = 'service' | 'privacy' | null; + const QRConfirmPage: React.FC = () => { const router = useRouter(); const [loading, setLoading] = useState(false); @@ -61,6 +64,8 @@ const QRConfirmPage: React.FC = () => { const [userInfo, setUserInfo] = useState(null); const [needAuth, setNeedAuth] = useState(false); // 是否需要手机号授权 const [authLoading, setAuthLoading] = useState(false); // 授权中状态 + const [agreementChecked, setAgreementChecked] = useState(false); // 协议勾选状态 + const [showAgreementPopup, setShowAgreementPopup] = useState(null); // 协议弹窗 useEffect(() => { // 从 URL 参数中获取 token @@ -158,6 +163,15 @@ const QRConfirmPage: React.FC = () => { const handleGetPhoneNumber = async ({ detail }: GetPhoneNumberEvent) => { const { code, encryptedData, iv, errMsg } = detail; + // 检查协议是否勾选 + if (!agreementChecked) { + Taro.showToast({ + title: '请先同意服务协议和隐私政策', + icon: 'none' + }); + return; + } + // 用户拒绝授权 if (errMsg && errMsg.includes('fail')) { Taro.showToast({ @@ -411,6 +425,184 @@ const QRConfirmPage: React.FC = () => { }); }; + // 打开协议弹窗 + const openAgreement = (type: AgreementType) => { + setShowAgreementPopup(type); + }; + + // 关闭协议弹窗 + const closeAgreement = () => { + setShowAgreementPopup(null); + }; + + // 渲染协议弹窗内容 + const renderAgreementContent = () => { + if (showAgreementPopup === 'service') { + return ( + + + 服务协议 + + + 欢迎使用我们的服务! + 1. 服务条款 + 本服务协议是您与我们之间关于使用我们提供的各项服务的协议。 + 2. 账号注册 + 您需要注册账号才能使用我们的部分服务。注册时您需要提供真实、准确的信息。 + 3. 服务使用 + 您同意遵守相关法律法规,不得利用我们的服务从事违法违规活动。 + 4. 隐私保护 + 我们重视您的隐私保护,具体请参见《隐私政策》。 + 5. 协议修改 + 我们有权在必要时修改本协议,修改后的协议将在公布时立即生效。 + + + 我知道了 + + + ); + } + + if (showAgreementPopup === 'privacy') { + return ( + + + 隐私政策 + + + 我们非常重视您的隐私保护。 + 1. 信息收集 + 我们可能会收集您的手机号、微信信息等,用于提供服务和身份验证。 + 2. 信息使用 + 我们仅将您的信息用于提供服务、改进用户体验和保障账号安全。 + 3. 信息保护 + 我们采用行业标准的加密技术保护您的信息安全。 + 4. 信息共享 + 我们不会将您的个人信息出售或分享给第三方,除非获得您的同意或法律法规要求。 + 5. 联系我们 + 如有任何隐私相关问题,请联系我们。 + + + 我知道了 + + + ); + } + + return null; + }; + + // 授权登录页面(参考权大师风格) + const renderAuthPage = () => { + return ( + + {/* 顶部导航 */} + + + 请登录 + + + + {/* Logo 区域 */} + + {/* Logo */} + + + + + + + + {/* 品牌名 */} + 权大师 + + {/* 标语 */} + 让知识产权更简单 + + + {/* 底部操作区域 */} + + {/* 主按钮 - 手机号授权登录 */} + + {authLoading ? '授权中...' : '手机号授权登录'} + + + {/* 取消按钮 */} + + 取消 + + + {/* 协议勾选 */} + + setAgreementChecked(!agreementChecked)} + > + {agreementChecked && ( + + )} + + + 我已阅读并同意权大师的 + + { + e.stopPropagation(); + openAgreement('service'); + }} + > + 《服务协议》 + + + { + e.stopPropagation(); + openAgreement('privacy'); + }} + > + 《隐私政策》 + + + + + {/* 协议弹窗 */} + {showAgreementPopup && ( + + + {renderAgreementContent()} + + + )} + + ); + }; + // 渲染状态图标 const renderStatusIcon = () => { if (loading || authLoading) return ( @@ -437,15 +629,10 @@ const QRConfirmPage: React.FC = () => { ); - if (needAuth) return ( - - - - - - - - ); + if (needAuth) { + // 需要授权时显示授权页面 + return renderAuthPage(); + } return ( @@ -460,7 +647,7 @@ const QRConfirmPage: React.FC = () => { const getTitle = () => { if (loading || authLoading) return '正在处理...'; if (confirmed) return '登录确认成功'; - if (needAuth) return '首次登录授权'; + if (needAuth) return ''; // 授权页面有自己的标题 if (error) return '登录确认失败'; return loginMethod === 'url' ? '扫码登录确认' : '确认登录'; }; @@ -470,7 +657,7 @@ const QRConfirmPage: React.FC = () => { if (loading) return '请稍候,正在为您确认登录'; if (authLoading) return '正在授权登录...'; if (confirmed) return '您已成功确认登录,网页端将自动登录'; - if (needAuth) return '检测到您是首次使用,请授权手机号完成注册并登录'; + if (needAuth) return ''; // 授权页面有自己的描述 if (error) return error; if (loginMethod === 'url') { return '检测到登录请求,是否确认登录?'; @@ -481,130 +668,93 @@ const QRConfirmPage: React.FC = () => { // 渲染操作按钮 const renderActions = () => { - // 需要授权登录 + // 需要授权登录时,按钮在授权页面内部渲染 if (needAuth) { - return ( - - - - - ); + return null; } if (loading) { return ( - + ); } if (confirmed) { return ( - + ); } if (error) { return ( - - - - + ); } if (loginMethod === 'scan') { return ( - - - + ); } return ( - + ); }; + // 如果是授权页面,直接返回授权页面 + if (needAuth) { + return renderAuthPage(); + } + return ( - + {/* Logo/品牌区域 */} @@ -621,14 +771,18 @@ const QRConfirmPage: React.FC = () => { {renderStatusIcon()} {/* 标题 */} - - {getTitle()} - + {getTitle() && ( + + {getTitle()} + + )} {/* 描述 */} - - {getDescription()} - + {getDescription() && ( + + {getDescription()} + + )} {/* 用户信息 */} {!loading && !confirmed && !error && !needAuth && userInfo && (