# 登录跳转改造为弹窗方案 > **改造日期**: 2025-10-14 > **改造范围**: 全项目登录/注册交互流程 > **改造目标**: 将所有页面跳转式登录改为弹窗式登录,提升用户体验 --- ## 📋 目录 - [1. 改造目标](#1-改造目标) - [2. 影响范围分析](#2-影响范围分析) - [3. 技术方案设计](#3-技术方案设计) - [4. 实施步骤](#4-实施步骤) - [5. 测试用例](#5-测试用例) - [6. 兼容性处理](#6-兼容性处理) --- ## 1. 改造目标 ### 1.1 用户体验提升 **改造前**: ``` 用户访问需登录页面 → 页面跳转到 /auth/signin → 登录成功 → 跳转回原页面 ``` **改造后**: ``` 用户访问需登录页面 → 弹出登录弹窗 → 登录成功 → 弹窗关闭,继续访问原页面 ``` ### 1.2 优势 ✅ **减少页面跳转**:无需离开当前页面,保持上下文 ✅ **流畅体验**:弹窗式交互更现代、更友好 ✅ **保留页面状态**:当前页面的表单数据、滚动位置等不会丢失 ✅ **支持快速切换**:在弹窗内切换登录/注册,无页面刷新 ✅ **更好的 SEO**:减少不必要的 URL 跳转 --- ## 2. 影响范围分析 ### 2.1 需要登录/注册的场景统计 | 场景类别 | 触发位置 | 当前实现 | 影响文件 | 优先级 | |---------|---------|---------|---------|-------| | **导航栏登录按钮** | HomeNavbar、AdminNavbarLinks | `navigate('/auth/signin')` | 2个文件 | 🔴 高 | | **导航栏注册按钮** | HomeNavbar("登录/注册"按钮) | 集成在登录按钮中 | 1个文件 | 🔴 高 | | **用户登出** | AuthContext.logout() | `navigate('/auth/signin')` | 1个文件 | 🔴 高 | | **受保护路由拦截** | ProtectedRoute组件 | `` | 1个文件 | 🔴 高 | | **登录/注册页面切换** | SignInIllustration、SignUpIllustration | `linkTo="/auth/sign-up"` | 2个文件 | 🟡 中 | | **其他认证页面** | SignInBasic、SignUpCentered等 | `navigate()` | 4个文件 | 🟢 低 | ### 2.2 详细文件列表 #### 🔴 核心文件(必须修改) 1. **`src/contexts/AuthContext.js`** (459行, 466行) - `logout()` 函数中的 `navigate('/auth/signin')` - **影响**:所有登出操作 2. **`src/components/ProtectedRoute.js`** (30行, 34行) - `` - **影响**:所有受保护路由的未登录拦截 3. **`src/components/Navbars/HomeNavbar.js`** (236行, 518-530行) - `handleLoginClick()` 函数 - "登录/注册"按钮(需拆分为登录和注册两个选项) - **影响**:首页顶部导航栏登录/注册按钮 4. **`src/components/Navbars/AdminNavbarLinks.js`** (86行, 147行) - `navigate("/auth/signin")` - **影响**:管理后台导航栏登录按钮 #### 🟡 次要文件(建议修改) 5. **`src/views/Authentication/SignIn/SignInIllustration.js`** (464行) - AuthFooter组件的 `linkTo="/auth/sign-up"` - **影响**:登录页面内的"去注册"链接 6. **`src/views/Authentication/SignUp/SignUpIllustration.js`** (373行) - AuthFooter组件的 `linkTo="/auth/sign-in"` - **影响**:注册页面内的"去登录"链接 #### 🟢 可选文件(保持兼容) 7-10. **其他认证页面变体**: - `src/views/Authentication/SignIn/SignInCentered.js` - `src/views/Authentication/SignIn/SignInBasic.js` - `src/views/Authentication/SignUp/SignUpBasic.js` - `src/views/Authentication/SignUp/SignUpCentered.js` 这些是模板中的备用页面,可以保持现有实现,不影响核心功能。 --- ## 3. 技术方案设计 ### 3.1 架构设计 ``` ┌─────────────────────────────────────────────┐ │ AuthModalContext │ │ - isLoginModalOpen │ │ - isSignUpModalOpen │ │ - openLoginModal(redirectUrl?) │ │ - openSignUpModal() │ │ - closeModal() │ │ - onLoginSuccess(callback?) │ └─────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ AuthModalManager 组件 │ │ - 渲染登录/注册弹窗 │ │ - 管理弹窗状态 │ │ - 处理登录成功回调 │ └─────────────────────────────────────────────┘ ↓ ┌──────────────────┬─────────────────────────┐ │ LoginModal │ SignUpModal │ │ - 复用现有UI │ - 复用现有UI │ │ - Chakra Modal │ - Chakra Modal │ └──────────────────┴─────────────────────────┘ ``` ### 3.2 核心组件设计 #### 3.2.1 AuthModalContext ```javascript // src/contexts/AuthModalContext.js import { createContext, useContext, useState, useCallback } from 'react'; const AuthModalContext = createContext(); export const useAuthModal = () => { const context = useContext(AuthModalContext); if (!context) { throw new Error('useAuthModal must be used within AuthModalProvider'); } return context; }; export const AuthModalProvider = ({ children }) => { const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); const [isSignUpModalOpen, setIsSignUpModalOpen] = useState(false); const [redirectUrl, setRedirectUrl] = useState(null); const [onSuccessCallback, setOnSuccessCallback] = useState(null); // 打开登录弹窗 const openLoginModal = useCallback((url = null, callback = null) => { setRedirectUrl(url); setOnSuccessCallback(() => callback); setIsLoginModalOpen(true); setIsSignUpModalOpen(false); }, []); // 打开注册弹窗 const openSignUpModal = useCallback((callback = null) => { setOnSuccessCallback(() => callback); setIsSignUpModalOpen(true); setIsLoginModalOpen(false); }, []); // 切换到注册弹窗 const switchToSignUp = useCallback(() => { setIsLoginModalOpen(false); setIsSignUpModalOpen(true); }, []); // 切换到登录弹窗 const switchToLogin = useCallback(() => { setIsSignUpModalOpen(false); setIsLoginModalOpen(true); }, []); // 关闭弹窗 const closeModal = useCallback(() => { setIsLoginModalOpen(false); setIsSignUpModalOpen(false); setRedirectUrl(null); setOnSuccessCallback(null); }, []); // 登录成功处理 const handleLoginSuccess = useCallback((user) => { if (onSuccessCallback) { onSuccessCallback(user); } // 如果有重定向URL,则跳转 if (redirectUrl) { window.location.href = redirectUrl; } closeModal(); }, [onSuccessCallback, redirectUrl, closeModal]); const value = { isLoginModalOpen, isSignUpModalOpen, openLoginModal, openSignUpModal, switchToSignUp, switchToLogin, closeModal, handleLoginSuccess, redirectUrl }; return ( {children} ); }; ``` #### 3.2.2 AuthModalManager 组件 ```javascript // src/components/Auth/AuthModalManager.js import React from 'react'; import { Modal, ModalOverlay, ModalContent, ModalBody, ModalCloseButton, useBreakpointValue } from '@chakra-ui/react'; import { useAuthModal } from '../../contexts/AuthModalContext'; import LoginModalContent from './LoginModalContent'; import SignUpModalContent from './SignUpModalContent'; export default function AuthModalManager() { const { isLoginModalOpen, isSignUpModalOpen, closeModal } = useAuthModal(); const modalSize = useBreakpointValue({ base: "full", sm: "xl", md: "2xl", lg: "4xl" }); const isOpen = isLoginModalOpen || isSignUpModalOpen; return ( {isLoginModalOpen && } {isSignUpModalOpen && } ); } ``` #### 3.2.3 LoginModalContent 组件 ```javascript // src/components/Auth/LoginModalContent.js // 复用 SignInIllustration.js 的核心UI逻辑 // 移除页面级的 Flex minH="100vh",改为 Box // 移除 navigate 跳转,改为调用 useAuthModal 的方法 ``` #### 3.2.4 SignUpModalContent 组件 ```javascript // src/components/Auth/SignUpModalContent.js // 复用 SignUpIllustration.js 的核心UI逻辑 // 移除页面级的 Flex minH="100vh",改为 Box // 注册成功后调用 handleLoginSuccess 而不是 navigate ``` ### 3.3 集成到 App.js ```javascript // src/App.js import { AuthModalProvider } from "contexts/AuthModalContext"; import AuthModalManager from "components/Auth/AuthModalManager"; export default function App() { return ( {/* 全局弹窗管理器 */} ); } ``` --- ## 4. 实施步骤 ### 阶段1:创建基础设施(1-2小时) - [ ] **Step 1.1**: 创建 `AuthModalContext.js` - 实现状态管理 - 实现打开/关闭方法 - 实现成功回调处理 - [ ] **Step 1.2**: 创建 `AuthModalManager.js` - 实现 Modal 容器 - 处理响应式布局 - 添加关闭按钮 - [ ] **Step 1.3**: 提取登录UI组件 - 从 `SignInIllustration.js` 提取核心UI - 创建 `LoginModalContent.js` - 移除页面级布局代码 - 替换 navigate 为 modal 方法 - [ ] **Step 1.4**: 提取注册UI组件 - 从 `SignUpIllustration.js` 提取核心UI - 创建 `SignUpModalContent.js` - 移除页面级布局代码 - 替换 navigate 为 modal 方法 ### 阶段2:集成到应用(0.5-1小时) - [ ] **Step 2.1**: 在 `App.js` 中集成 - 导入 `AuthModalProvider` - 包裹 `AppContent` - 添加 `` - [ ] **Step 2.2**: 验证基础功能 - 测试弹窗打开/关闭 - 测试登录/注册切换 - 测试响应式布局 ### 阶段3:替换现有跳转(1-2小时) - [ ] **Step 3.1**: 修改 `HomeNavbar.js` - 添加登录和注册弹窗 ```javascript // 修改前 const handleLoginClick = () => { navigate('/auth/signin'); }; // 未登录状态显示"登录/注册"按钮 // 修改后 import { useAuthModal } from '../../contexts/AuthModalContext'; import { Menu, MenuButton, MenuList, MenuItem } from '@chakra-ui/react'; const { openLoginModal, openSignUpModal } = useAuthModal(); // 方式1:下拉菜单方式(推荐) } > 登录 / 注册 openLoginModal()}> 🔐 登录 openSignUpModal()}> ✍️ 注册 // 方式2:并排按钮方式(备选) ``` - [ ] **Step 3.2**: 修改 `AdminNavbarLinks.js` - 替换 `navigate("/auth/signin")` 为 `openLoginModal()` - [ ] **Step 3.3**: 修改 `AuthContext.js` logout函数 ```javascript // 修改前 const logout = async () => { // ... 清理逻辑 navigate('/auth/signin'); }; // 修改后 const logout = async () => { // ... 清理逻辑 // 不再跳转,用户留在当前页面 toast({ title: "已登出", description: "您已成功退出登录", status: "info", duration: 2000 }); }; ``` - [ ] **Step 3.4**: 修改 `ProtectedRoute.js` ```javascript // 修改前 if (!isAuthenticated || !user) { return ; } // 修改后 import { useAuthModal } from '../contexts/AuthModalContext'; import { useEffect } from 'react'; const { openLoginModal, isLoginModalOpen } = useAuthModal(); useEffect(() => { if (!isAuthenticated && !user && !isLoginModalOpen) { openLoginModal(currentPath); } }, [isAuthenticated, user, isLoginModalOpen, currentPath, openLoginModal]); // 未登录时显示占位符(不再跳转) if (!isAuthenticated || !user) { return ( 请先登录... ); } ``` ### 阶段4:测试与优化(1-2小时) - [ ] **Step 4.1**: 功能测试(见第5节) - [ ] **Step 4.2**: 边界情况处理 - [ ] **Step 4.3**: 性能优化 - [ ] **Step 4.4**: 用户体验优化 --- ## 5. 测试用例 ### 5.1 基础功能测试 | 测试项 | 测试步骤 | 预期结果 | 状态 | |-------|---------|---------|-----| | **登录弹窗打开** | 1. 点击导航栏"登录/注册"下拉菜单
2. 点击"登录" | 弹窗正常打开,显示登录表单 | ⬜ | | **注册弹窗打开** | 1. 点击导航栏"登录/注册"下拉菜单
2. 点击"注册" | 弹窗正常打开,显示注册表单 | ⬜ | | **登录弹窗关闭** | 1. 打开登录弹窗
2. 点击关闭按钮 | 弹窗正常关闭,返回原页面 | ⬜ | | **注册弹窗关闭** | 1. 打开注册弹窗
2. 点击关闭按钮 | 弹窗正常关闭,返回原页面 | ⬜ | | **从登录切换到注册** | 1. 打开登录弹窗
2. 点击"去注册" | 弹窗切换到注册表单,无页面刷新 | ⬜ | | **从注册切换到登录** | 1. 打开注册弹窗
2. 点击"去登录" | 弹窗切换到登录表单,无页面刷新 | ⬜ | | **手机号+密码登录** | 1. 打开登录弹窗
2. 输入手机号和密码
3. 点击登录 | 登录成功,弹窗关闭,显示成功提示 | ⬜ | | **验证码登录** | 1. 打开登录弹窗
2. 切换到验证码登录
3. 发送并输入验证码
4. 点击登录 | 登录成功,弹窗关闭 | ⬜ | | **微信登录** | 1. 打开登录弹窗
2. 点击微信登录
3. 扫码授权 | 登录成功,弹窗关闭 | ⬜ | | **手机号+密码注册** | 1. 打开注册弹窗
2. 填写手机号、密码等信息
3. 点击注册 | 注册成功,弹窗关闭,自动登录 | ⬜ | | **验证码注册** | 1. 打开注册弹窗
2. 切换到验证码注册
3. 发送并输入验证码
4. 点击注册 | 注册成功,弹窗关闭,自动登录 | ⬜ | | **微信注册** | 1. 打开注册弹窗
2. 点击微信注册
3. 扫码授权 | 注册成功,弹窗关闭,自动登录 | ⬜ | ### 5.2 受保护路由测试 | 测试项 | 测试步骤 | 预期结果 | 状态 | |-------|---------|---------|-----| | **未登录访问概念中心** | 1. 未登录状态
2. 访问 `/concepts` | 自动弹出登录弹窗 | ⬜ | | **登录后继续访问** | 1. 在上述弹窗中登录
2. 查看页面状态 | 弹窗关闭,概念中心页面正常显示 | ⬜ | | **未登录访问社区** | 1. 未登录状态
2. 访问 `/community` | 自动弹出登录弹窗 | ⬜ | | **未登录访问个股中心** | 1. 未登录状态
2. 访问 `/stocks` | 自动弹出登录弹窗 | ⬜ | | **未登录访问模拟盘** | 1. 未登录状态
2. 访问 `/trading-simulation` | 自动弹出登录弹窗 | ⬜ | | **未登录访问管理后台** | 1. 未登录状态
2. 访问 `/admin/*` | 自动弹出登录弹窗 | ⬜ | ### 5.3 登出测试 | 测试项 | 测试步骤 | 预期结果 | 状态 | |-------|---------|---------|-----| | **从导航栏登出** | 1. 已登录状态
2. 点击用户菜单"退出登录" | 登出成功,留在当前页面,显示未登录状态 | ⬜ | | **登出后访问受保护页面** | 1. 登出后
2. 尝试访问 `/concepts` | 自动弹出登录弹窗 | ⬜ | ### 5.4 边界情况测试 | 测试项 | 测试步骤 | 预期结果 | 状态 | |-------|---------|---------|-----| | **登录失败** | 1. 输入错误的手机号或密码
2. 点击登录 | 显示错误提示,弹窗保持打开 | ⬜ | | **网络断开** | 1. 断开网络
2. 尝试登录 | 显示网络错误提示 | ⬜ | | **倒计时中关闭弹窗** | 1. 发送验证码(60秒倒计时)
2. 关闭弹窗
3. 重新打开 | 倒计时正确清理,无内存泄漏 | ⬜ | | **重复打开弹窗** | 1. 快速连续点击登录按钮多次 | 只显示一个弹窗,无重复 | ⬜ | | **响应式布局** | 1. 在手机端打开登录弹窗 | 弹窗全屏显示,UI适配良好 | ⬜ | ### 5.5 兼容性测试 | 测试项 | 测试步骤 | 预期结果 | 状态 | |-------|---------|---------|-----| | **直接访问登录页面** | 1. 访问 `/auth/sign-in` | 页面正常显示(保持路由兼容) | ⬜ | | **直接访问注册页面** | 1. 访问 `/auth/sign-up` | 页面正常显示(保持路由兼容) | ⬜ | | **SEO爬虫访问** | 1. 模拟搜索引擎爬虫访问 | 页面可访问,无JavaScript错误 | ⬜ | --- ## 6. 兼容性处理 ### 6.1 保留现有路由 为了兼容性和SEO,保留现有的登录/注册页面路由: ```javascript // src/layouts/Auth.js // 保持不变,继续支持 /auth/sign-in 和 /auth/sign-up 路由 } /> } /> ``` **好处**: - 外部链接(邮件、短信中的登录链接)仍然有效 - SEO友好,搜索引擎可以正常抓取 - 用户可以直接访问登录页面(如果他们更喜欢) ### 6.2 渐进式迁移 **阶段1**:保留两种方式 - 弹窗登录(新实现) - 页面跳转登录(旧实现) **阶段2**:逐步迁移 - 核心场景使用弹窗(导航栏、受保护路由) - 非核心场景保持原样(备用认证页面) **阶段3**:全面切换(可选) - 所有场景统一使用弹窗 - 页面路由仅作为后备 ### 6.3 微信登录兼容 微信登录涉及OAuth回调,需要特殊处理: ```javascript // WechatRegister.js 中 // 微信授权成功后会跳转回 /auth/callback // 需要在回调页面检测到登录成功后: // 1. 更新 AuthContext 状态 // 2. 如果是从弹窗发起的,关闭弹窗并回到原页面 // 3. 如果是从页面发起的,跳转到目标页面 ``` --- ## 7. 实施时间表 ### 总预计时间:4-6小时 | 阶段 | 预计时间 | 实际时间 | 负责人 | 状态 | |-----|---------|---------|-------|------| | 阶段1:创建基础设施 | 1-2小时 | - | - | ⬜ 待开始 | | 阶段2:集成到应用 | 0.5-1小时 | - | - | ⬜ 待开始 | | 阶段3:替换现有跳转 | 1-2小时 | - | - | ⬜ 待开始 | | 阶段4:测试与优化 | 1-2小时 | - | - | ⬜ 待开始 | --- ## 8. 风险评估 ### 8.1 技术风险 | 风险 | 等级 | 应对措施 | |-----|------|---------| | 微信登录回调兼容性 | 🟡 中 | 保留页面路由,微信回调仍跳转到页面 | | 受保护路由逻辑复杂化 | 🟡 中 | 详细测试,确保所有场景覆盖 | | 弹窗状态管理冲突 | 🟢 低 | 使用独立的Context,避免与AuthContext冲突 | | 内存泄漏 | 🟢 低 | 复用已有的内存管理模式(isMountedRef) | ### 8.2 用户体验风险 | 风险 | 等级 | 应对措施 | |-----|------|---------| | 用户不习惯弹窗登录 | 🟢 低 | 保留页面路由,提供选择 | | 移动端弹窗体验差 | 🟡 中 | 移动端使用全屏Modal | | 弹窗被误关闭 | 🟢 低 | 添加确认提示或表单状态保存 | --- ## 9. 后续优化建议 ### 9.1 短期优化(1周内) - [ ] 添加登录/注册进度指示器 - [ ] 优化弹窗动画效果 - [ ] 添加键盘快捷键支持(Esc关闭) - [ ] 优化移动端触摸体验 ### 9.2 中期优化(1月内) - [ ] 添加第三方登录(Google、GitHub等) - [ ] 实现记住登录状态 - [ ] 添加生物识别登录(指纹、Face ID) - [ ] 优化表单验证提示 ### 9.3 长期优化(3月内) - [ ] 实现SSO单点登录 - [ ] 添加多因素认证(2FA) - [ ] 实现社交账号关联 - [ ] 完善审计日志 --- ## 10. 参考资料 - [Chakra UI Modal 文档](https://chakra-ui.com/docs/components/modal) - [React Context API 最佳实践](https://react.dev/learn/passing-data-deeply-with-context) - [用户认证最佳实践](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html) --- **文档维护**: - 创建日期:2025-10-14 - 最后更新:2025-10-14 - 维护人:Claude Code - 状态:📝 规划阶段 --- ## 附录A:关键代码片段 ### A.1 修改前后对比 - HomeNavbar.js ```diff // src/components/Navbars/HomeNavbar.js - import { useNavigate } from 'react-router-dom'; + import { useAuthModal } from '../../contexts/AuthModalContext'; export default function HomeNavbar() { - const navigate = useNavigate(); + const { openLoginModal, openSignUpModal } = useAuthModal(); - // 处理登录按钮点击 - const handleLoginClick = () => { - navigate('/auth/signin'); - }; return ( // ... 其他代码 {/* 未登录状态 */} - + {/* 方式1:下拉菜单(推荐) */} + + } + > + 登录 / 注册 + + + openLoginModal()}> + 🔐 登录 + + openSignUpModal()}> + ✍️ 注册 + + + + + {/* 方式2:并排按钮(备选) */} + + + + ); } ``` ### A.2 修改前后对比 - ProtectedRoute.js ```diff // src/components/ProtectedRoute.js + import { useAuthModal } from '../contexts/AuthModalContext'; + import { useEffect } from 'react'; const ProtectedRoute = ({ children }) => { - const { isAuthenticated, isLoading, user } = useAuth(); + const { isAuthenticated, isLoading, user } = useAuth(); + const { openLoginModal, isLoginModalOpen } = useAuthModal(); - if (isLoading) { - return ...Loading Spinner...; - } let currentPath = window.location.pathname + window.location.search; - let redirectUrl = `/auth/signin?redirect=${encodeURIComponent(currentPath)}`; + // 未登录时自动弹出登录窗口 + useEffect(() => { + if (!isAuthenticated && !user && !isLoginModalOpen) { + openLoginModal(currentPath); + } + }, [isAuthenticated, user, isLoginModalOpen, currentPath, openLoginModal]); if (!isAuthenticated || !user) { - return ; + return ( + + + + 请先登录... + + + ); } return children; }; ``` ### A.3 修改前后对比 - AuthContext.js ```diff // src/contexts/AuthContext.js const logout = async () => { try { await fetch(`${API_BASE_URL}/api/auth/logout`, { method: 'POST', credentials: 'include' }); setUser(null); setIsAuthenticated(false); toast({ title: "已登出", description: "您已成功退出登录", status: "info", duration: 2000, isClosable: true, }); - navigate('/auth/signin'); } catch (error) { console.error('Logout error:', error); setUser(null); setIsAuthenticated(false); - navigate('/auth/signin'); } }; ``` ### A.4 修改前后对比 - LoginModalContent 和 SignUpModalContent 切换 ```diff // src/components/Auth/LoginModalContent.js + import { useAuthModal } from '../../contexts/AuthModalContext'; export default function LoginModalContent() { + const { switchToSignUp, handleLoginSuccess } = useAuthModal(); // 登录成功处理 const handleSubmit = async (e) => { e.preventDefault(); // ... 登录逻辑 if (loginSuccess) { - navigate("/home"); + handleLoginSuccess(userData); } }; return ( {/* 登录表单 */}
{/* ... 表单内容 */}
{/* 底部切换链接 */} switchToSignUp()} />
); } ``` ```diff // src/components/Auth/SignUpModalContent.js + import { useAuthModal } from '../../contexts/AuthModalContext'; export default function SignUpModalContent() { + const { switchToLogin, handleLoginSuccess } = useAuthModal(); // 注册成功处理 const handleSubmit = async (e) => { e.preventDefault(); // ... 注册逻辑 if (registerSuccess) { - toast({ title: "注册成功" }); - setTimeout(() => navigate("/auth/sign-in"), 2000); + toast({ title: "注册成功,自动登录中..." }); + // 注册成功后自动登录,然后关闭弹窗 + handleLoginSuccess(userData); } }; return ( {/* 注册表单 */}
{/* ... 表单内容 */}
{/* 底部切换链接 */} switchToLogin()} />
); } ``` ### A.5 AuthFooter 组件修改(支持弹窗切换) ```diff // src/components/Auth/AuthFooter.js export default function AuthFooter({ linkText, linkLabel, - linkTo, + onClick, useVerificationCode, onSwitchMethod }) { return ( {linkText} - + {linkLabel} {onSwitchMethod && ( )} ); } ``` --- **准备好开始实施了吗?** 请确认以下事项: - [ ] 已备份当前代码(git commit) - [ ] 已在开发环境测试 - [ ] 团队成员已了解改造方案 - [ ] 准备好测试设备(桌面端、移动端) **开始命令**: ```bash # 创建功能分支 git checkout -b feature/login-modal-refactor # 开始实施... ```