diff --git a/pwuniapp/pages/user/index.vue b/pwuniapp/pages/user/index.vue index 2fb1d00..942f08c 100644 --- a/pwuniapp/pages/user/index.vue +++ b/pwuniapp/pages/user/index.vue @@ -2,11 +2,16 @@ - + @@ -164,13 +169,20 @@ - + 点击更换推荐头像 - 相册上传 + + + + + 相册上传 + - + 用户昵称 @@ -520,6 +533,32 @@ const editProfile = () => { showEditModal.value = true; }; +// 点击头像或名称行:已登录时直接打开个人资料设置弹窗,未登录时引导登录 +const handleAvatarOrNameClick = () => { + if (isLoggedIn.value) { + editProfile(); + } else { + openLoginModal(); + } +}; + +// 微信官方快捷选取头像回调 +const onWechatChooseAvatar = async (e) => { + const avatarUrl = e?.detail?.avatarUrl; + if (!avatarUrl) return; + uni.showLoading({ title: '正在同步微信头像...' }); + try { + const onlineUrl = await uploadImage(avatarUrl); + editForm.value.avatar = onlineUrl || avatarUrl; + uni.hideLoading(); + uni.showToast({ title: '微信头像获取成功', icon: 'success' }); + } catch (err) { + uni.hideLoading(); + editForm.value.avatar = avatarUrl; + uni.showToast({ title: '已选取微信头像', icon: 'none' }); + } +}; + const chooseCustomAvatar = () => { uni.chooseImage({ count: 1, @@ -873,6 +912,13 @@ const navTo = (url) => { border: 4rpx solid #6366F1; margin-right: 24rpx; flex-shrink: 0; + cursor: pointer; + transition: transform 0.15s ease, box-shadow 0.15s ease; + + &:active { + transform: scale(0.95); + box-shadow: 0 0 16rpx rgba(99, 102, 241, 0.4); + } } .user-info { @@ -892,6 +938,12 @@ const navTo = (url) => { gap: 12rpx; min-width: 0; flex: 1; + cursor: pointer; + transition: opacity 0.15s ease; + + &:active { + opacity: 0.75; + } .user-name { font-size: 32rpx; @@ -986,6 +1038,12 @@ const navTo = (url) => { font-size: 22rpx; color: #64748B; font-family: monospace; + cursor: pointer; + display: inline-block; + + &:active { + opacity: 0.7; + } } } } @@ -1328,15 +1386,40 @@ const navTo = (url) => { display: block; } - .btn-custom-upload { - font-size: 22rpx; - color: #6366F1; - font-weight: 600; - padding: 4rpx 14rpx; - background: rgba(99, 102, 241, 0.12); - border: 1px solid rgba(99, 102, 241, 0.3); - border-radius: 8rpx; - cursor: pointer; + .avatar-action-links { + display: flex; + align-items: center; + gap: 12rpx; + + .btn-wechat-choose { + margin: 0; + padding: 4rpx 14rpx; + font-size: 22rpx; + color: #10B981; + font-weight: 600; + background: rgba(16, 185, 129, 0.12); + border: 1px solid rgba(16, 185, 129, 0.3); + border-radius: 8rpx; + line-height: 1.4; + cursor: pointer; + + &::after { + display: none; + border: none; + } + } + + .btn-custom-upload { + font-size: 22rpx; + color: #6366F1; + font-weight: 600; + padding: 4rpx 14rpx; + background: rgba(99, 102, 241, 0.12); + border: 1px solid rgba(99, 102, 241, 0.3); + border-radius: 8rpx; + cursor: pointer; + line-height: 1.4; + } } } @@ -1551,8 +1634,17 @@ const navTo = (url) => { } .avatar-meta { - .avatar-label { - color: #78716C; + .avatar-action-links { + .btn-wechat-choose { + color: #059669; + background: #ECFDF5; + border-color: rgba(5, 150, 105, 0.3); + } + .btn-custom-upload { + color: #EA580C; + background: #FFF7ED; + border-color: rgba(234, 88, 12, 0.3); + } } .preset-avatars-row .preset-avatar-item.selected { diff --git a/server/tests/content-security.spec.ts b/server/tests/content-security.spec.ts index c77c632..ea80830 100644 --- a/server/tests/content-security.spec.ts +++ b/server/tests/content-security.spec.ts @@ -397,6 +397,17 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安 expect(content).toContain('if (!ensureLoggedIn()) return;'); }); + it('个人中心支持轻触头像、名称与UID呼出个人信息设置弹窗,并支持微信官方选头像与快速填入昵称', () => { + const userIndexPath = path.join(rootDir, 'pwuniapp', 'pages', 'user', 'index.vue'); + const content = fs.readFileSync(userIndexPath, 'utf-8'); + expect(content).toContain('handleAvatarOrNameClick'); + expect(content).toContain('showEditModal'); + expect(content).toContain('editProfile'); + expect(content).toContain('open-type="chooseAvatar"'); + expect(content).toContain('type="nickname"'); + expect(content).toContain('onWechatChooseAvatar'); + }); + it('所有个人中心改造代码严禁包含任何系统 Emoji', () => { const userIndexPath = path.join(rootDir, 'pwuniapp', 'pages', 'user', 'index.vue'); const text = fs.readFileSync(userIndexPath, 'utf-8');