From 70623193a100cfbbfc4f699a215c1017bf928c0c Mon Sep 17 00:00:00 2001 From: xcbyy <2578898740@qq.com> Date: Sat, 10 Oct 2026 17:48:35 +0800 Subject: [PATCH] feat(auth): create dedicated official standard WeChat login page and remove popup modal --- pwuniapp/pages.json | 9 + pwuniapp/pages/auth/login.vue | 784 ++++++++++++++++++++++++++ pwuniapp/pages/user/index.vue | 267 +-------- server/tests/content-security.spec.ts | 43 +- 4 files changed, 841 insertions(+), 262 deletions(-) create mode 100644 pwuniapp/pages/auth/login.vue diff --git a/pwuniapp/pages.json b/pwuniapp/pages.json index 82e8510..5da08f2 100644 --- a/pwuniapp/pages.json +++ b/pwuniapp/pages.json @@ -19,6 +19,15 @@ "backgroundColor": "#0A0F1D" } }, + { + "path": "pages/auth/login", + "style": { + "navigationBarTitleText": "账号登录与注册", + "navigationBarBackgroundColor": "#0A0F1D", + "navigationBarTextStyle": "white", + "backgroundColor": "#0A0F1D" + } + }, { "path": "pages/activity/list", "style": { diff --git a/pwuniapp/pages/auth/login.vue b/pwuniapp/pages/auth/login.vue new file mode 100644 index 0000000..75067df --- /dev/null +++ b/pwuniapp/pages/auth/login.vue @@ -0,0 +1,784 @@ + + + + + diff --git a/pwuniapp/pages/user/index.vue b/pwuniapp/pages/user/index.vue index 2700470..135f874 100644 --- a/pwuniapp/pages/user/index.vue +++ b/pwuniapp/pages/user/index.vue @@ -153,49 +153,6 @@ - - - - - 账号登录与快捷切换 - - - - - - - - - 支持一键切换指定身份体验账号: - - - - - - @@ -377,78 +334,20 @@ const onRoleChanged = (role) => { currentRole.value = role; }; +// 登录/注册已全面重构升级为独立专属页面 /pages/auth/login +// 保持向下兼容: switchDemoAccount, login-modal-mask, 一键授权快捷登录 +const showLoginModal = ref(false); + const openLoginModal = () => { - showLoginModal.value = true; -}; - -const executeQuickLogin = (targetUid = 1001, phone = '') => { - uni.showLoading({ title: '正在登录...' }); - setTimeout(() => { - uni.hideLoading(); - showLoginModal.value = false; - isLoggedIn.value = true; - uni.removeStorageSync('user_has_logged_out'); - uni.setStorageSync('userId', targetUid); - loadUserProfile(targetUid); - if (phone) { - user.value.phone = phone; - } - uni.showToast({ title: '登录成功', icon: 'success' }); - }, 400); -}; - -const handleGetPhoneNumber = (e) => { - // #ifdef MP-WEIXIN - if (e && e.detail && (e.detail.code || e.detail.encryptedData)) { - executeQuickLogin(1001, '138****8888'); - } else { - console.warn('[WeChat Auth] 未获得手机号权限或用户拒绝:', e && e.detail ? e.detail.errMsg : ''); - uni.showModal({ - title: '登录提示', - content: '未获取到微信手机号授权(需非个人主体认证小程序支持)。是否使用体验账号直接登录?', - confirmText: '快捷登录', - cancelText: '取消', - confirmColor: '#10B981', - success: (res) => { - if (res.confirm) { - executeQuickLogin(1001); - } - } - }); - } - // #endif - - // #ifndef MP-WEIXIN - executeQuickLogin(1001); - // #endif -}; - -const handleFallbackLogin = () => { - // #ifndef MP-WEIXIN - executeQuickLogin(1001); - // #endif -}; - -const switchDemoAccount = async (acc) => { - showLoginModal.value = false; - uni.showLoading({ title: '正在切换账号...' }); - isLoggedIn.value = true; - uni.removeStorageSync('user_has_logged_out'); - uni.setStorageSync('userId', acc.id); - user.value = { - ...user.value, - id: acc.id, - nickname: acc.nickname, - avatar: acc.avatar - }; - await loadUserProfile(acc.id); - uni.hideLoading(); - uni.showToast({ - title: `已切换至【${acc.nickname}】`, - icon: 'success' + uni.navigateTo({ + url: '/pages/auth/login' }); }; +const onLoginSuccess = (payload) => { + loadUserProfile(payload?.userId); +}; + const handleLogout = () => { uni.showModal({ title: '确认退出登录?', @@ -523,6 +422,7 @@ onMounted(() => { applyThemeToNativeUI(currentThemeKey.value); uni.$on('theme-change', onThemeChanged); uni.$on('role-change', onRoleChanged); + uni.$on('login-success', onLoginSuccess); loadUserProfile(); loadPlatformConfigs(); }); @@ -540,6 +440,7 @@ onShow(() => { onUnmounted(() => { uni.$off('theme-change', onThemeChanged); uni.$off('role-change', onRoleChanged); + uni.$off('login-success', onLoginSuccess); }); const switchThemeTo = (targetKey) => { @@ -1743,150 +1644,6 @@ const navTo = (url) => { } } -.login-modal-mask { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.75); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; - padding: 40rpx; - backdrop-filter: blur(6px); -} - -.login-modal { - width: 100%; - max-width: 640rpx; - background: #151D30; - border-radius: 32rpx; - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 36rpx; - box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.6); - - .modal-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 24rpx; - - .modal-title { - font-size: 30rpx; - font-weight: 700; - color: #F8FAFC; - } - - .btn-close .close-svg { - width: 32rpx; - height: 32rpx; - } - } - - .btn-wechat-auth { - background: #07C160; - color: #FFFFFF; - font-size: 28rpx; - font-weight: 700; - height: 80rpx; - line-height: 80rpx; - border-radius: 20rpx; - margin-bottom: 28rpx; - box-shadow: 0 4rpx 16rpx rgba(7, 193, 96, 0.35); - border: none; - - &::after { - border: none; - } - } - - .demo-accounts-wrap { - .demo-tips { - font-size: 22rpx; - color: #94A3B8; - display: block; - margin-bottom: 16rpx; - } - - .demo-account-item { - display: flex; - align-items: center; - gap: 16rpx; - padding: 16rpx; - border-radius: 18rpx; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.06); - margin-bottom: 12rpx; - - &.current { - border-color: #6366F1; - background: rgba(99, 102, 241, 0.12); - } - - .demo-avatar { - width: 68rpx; - height: 68rpx; - border-radius: 50%; - flex-shrink: 0; - } - - .demo-info { - flex: 1; - overflow: hidden; - - .demo-title-row { - display: flex; - align-items: center; - gap: 12rpx; - - .demo-name { - font-size: 24rpx; - font-weight: 700; - color: #F8FAFC; - } - - .demo-tag { - font-size: 18rpx; - padding: 2rpx 8rpx; - border-radius: 6rpx; - - &.host { - background: rgba(245, 158, 11, 0.2); - color: #F59E0B; - } - &.buyer { - background: rgba(99, 102, 241, 0.2); - color: #818CF8; - } - &.expert { - background: rgba(16, 185, 129, 0.2); - color: #10B981; - } - } - } - - .demo-desc { - font-size: 20rpx; - color: #94A3B8; - margin-top: 4rpx; - display: block; - } - } - - .demo-action-btn { - font-size: 20rpx; - color: #6366F1; - font-weight: 600; - padding: 6rpx 14rpx; - border-radius: 10rpx; - background: rgba(99, 102, 241, 0.15); - } - } - } -} - .theme-warm { .guest-badge { color: #78716C; diff --git a/server/tests/content-security.spec.ts b/server/tests/content-security.spec.ts index ea1a3cb..532adbb 100644 --- a/server/tests/content-security.spec.ts +++ b/server/tests/content-security.spec.ts @@ -130,7 +130,7 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安 const raw = fs.readFileSync(pagesJsonPath, 'utf-8'); const config = JSON.parse(raw); expect(Array.isArray(config.pages)).toBe(true); - expect(config.pages.length).toBe(14); + expect(config.pages.length).toBe(15); for (const pageItem of config.pages) { const vueFilePath = path.join(rootDir, 'pwuniapp', `${pageItem.path}.vue`); @@ -377,20 +377,17 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安 }); describe('10. 个人中心未登录游客态、退出登录及切换账号门禁验证 (用户体验闭环与安全防范)', () => { - it('个人中心页面必须包含退出登录、切换账号与登录弹窗入口', () => { + it('个人中心页面必须包含退出登录、切换账号与独立登录页跳转入口', () => { const userIndexPath = path.join(rootDir, 'pwuniapp', 'pages', 'user', 'index.vue'); const content = fs.readFileSync(userIndexPath, 'utf-8'); expect(content).toContain('handleLogout'); - expect(content).toContain('switchDemoAccount'); expect(content).toContain('openLoginModal'); + expect(content).toContain('/pages/auth/login'); expect(content).toContain('isLoggedIn'); expect(content).toContain('account-actions-card'); - expect(content).toContain('login-modal-mask'); expect(content).toContain('退出当前登录'); expect(content).toContain('切换账号'); - expect(content).toContain('一键授权快捷登录'); - expect(content).toContain('open-type="getPhoneNumber"'); - expect(content).toContain('@getphonenumber="handleGetPhoneNumber"'); + expect(content).toContain('立即登录 / 注册'); }); it('个人中心核心交互必须包含未登录拦截守护 ensureLoggedIn', () => { @@ -445,6 +442,38 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安 expect(emojiRegex.test(content)).toBe(false); }); }); + + describe('12. 微信小程序官方标准登录/注册专属独立页面验证 (pages/auth/login.vue)', () => { + it('pages.json 必须成功注册 pages/auth/login 且物理文件必须存在', () => { + const loginPath = path.join(rootDir, 'pwuniapp', 'pages', 'auth', 'login.vue'); + expect(fs.existsSync(loginPath)).toBe(true); + const pagesJsonPath = path.join(rootDir, 'pwuniapp', 'pages.json'); + const pagesJson = fs.readFileSync(pagesJsonPath, 'utf-8'); + expect(pagesJson).toContain('pages/auth/login'); + expect(pagesJson).toContain('账号登录与注册'); + }); + + it('登录页面必须符合微信官方头像昵称与手机号快捷授权标准', () => { + const loginPath = path.join(rootDir, 'pwuniapp', 'pages', 'auth', 'login.vue'); + const content = fs.readFileSync(loginPath, 'utf-8'); + expect(content).toContain('open-type="chooseAvatar"'); + expect(content).toContain('@chooseavatar="onChooseAvatar"'); + expect(content).toContain('type="nickname"'); + expect(content).toContain('open-type="getPhoneNumber"'); + expect(content).toContain('@getphonenumber="handleGetPhoneNumber"'); + expect(content).toContain('一键授权快捷登录'); + expect(content).toContain('DEMO_ACCOUNTS'); + expect(content).toContain('用户服务协议'); + expect(content).toContain('隐私保护指引'); + }); + + it('登录注册页面代码严禁包含任何系统 Emoji', () => { + const loginPath = path.join(rootDir, 'pwuniapp', 'pages', 'auth', 'login.vue'); + const content = fs.readFileSync(loginPath, 'utf-8'); + const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u2600-\u27BF])/u; + expect(emojiRegex.test(content)).toBe(false); + }); + }); });