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 @@
+
+
+
+
+
+
+
+ 同城陪玩生活圈
+ 年轻人的同城搭子与温暖陪伴社交平台
+
+
+
+
+
+
+
+
+
+
+ 点击设置微信头像
+ 轻触一键拉起微信头像快速选用
+
+
+
+
+
+ 昵称
+
+
+
+
+
+
+
+ 微信官方安全风控 · 个人信息严格加密存储
+
+
+
+
+
+ 多角色体验账号直登
+ 评审与体验专用 · 一键切换角色
+
+
+
+
+
+
+
+ {{ acc.nickname }}
+ {{ acc.roleDesc }}
+
+ {{ acc.desc }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 登录注册即代表您已阅读并同意
+ 《用户服务协议》
+ 与
+ 《隐私保护指引》
+
+
+
+
+
+
+
+
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 @@
-
-
-
-
-
-
-
-
- 支持一键切换指定身份体验账号:
-
-
-
-
- {{ acc.nickname }}
- {{ acc.roleDesc }}
-
- UID: {{ acc.id }} · {{ acc.desc }}
-
- {{ Number(user.id) === acc.id && isLoggedIn ? '当前登录' : '切换至此号' }}
-
-
-
-
-
-
@@ -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);
+ });
+ });
});