更新输入框边距规范,增加资源对接弹窗的布局修正,确保在小程序开发中避免文字贴边问题。补充相关口诀以提升开发一致性,并在经验清单中记录最新最佳实践。调整项目索引以反映最新进展,增强文档的可用性与可追溯性。

This commit is contained in:
Alex-larget
2026-03-03 11:12:56 +08:00
parent 8e2ea9b7c1
commit 3097d796e0
18 changed files with 220 additions and 94 deletions

View File

@@ -58,6 +58,9 @@ Page({
showNicknameModal: false,
editingNickname: '',
// 头像弹窗(含 chooseAvatar 按钮,必须用户点击才可获取微信头像)
showAvatarModal: false,
// 手机/微信号弹窗stitch_soul comprehensive_profile_editor_v1_2
showContactModal: false,
contactPhone: '',
@@ -290,11 +293,11 @@ Page({
wx.showToast({ title: '已刷新', icon: 'success' })
},
// 微信原生获取头像button open-type="chooseAvatar" 回调)
// 微信原生获取头像button open-type="chooseAvatar" 回调,真正获取微信头像
async onChooseAvatar(e) {
const tempAvatarUrl = e.detail.avatarUrl
const tempAvatarUrl = e.detail?.avatarUrl
this.setData({ showAvatarModal: false })
if (!tempAvatarUrl) return
wx.showLoading({ title: '上传中...', mask: true })
try {
@@ -659,32 +662,59 @@ Page({
} catch (e) { console.log('[My] VIP查询失败', e) }
},
// 头像点击:已登录弹出选项(改头像/进VIP
// 头像点击:已登录弹出选项(微信头像 / 相册 / VIP
onAvatarTap() {
if (!this.data.isLoggedIn) { this.showLogin(); return }
wx.showActionSheet({
itemList: ['获取微信头像', '开通/管理VIP'],
itemList: ['获取微信头像', '从相册选择', '开通/管理VIP'],
success: (res) => {
if (res.tapIndex === 0) this.chooseAvatarFallback()
if (res.tapIndex === 1) this.goToVip()
if (res.tapIndex === 0) this.setData({ showAvatarModal: true })
if (res.tapIndex === 1) this.chooseAvatarFromAlbum()
if (res.tapIndex === 2) this.goToVip()
}
})
},
chooseAvatarFallback() {
closeAvatarModal() {
this.setData({ showAvatarModal: false })
},
// 从相册/相机选择(自定义图片)
chooseAvatarFromAlbum() {
wx.chooseMedia({
count: 1, mediaType: ['image'], sourceType: ['album', 'camera'],
success: async (res) => {
const tempPath = res.tempFiles[0].tempFilePath
const userInfo = this.data.userInfo
userInfo.avatar = tempPath
this.setData({ userInfo })
app.globalData.userInfo = userInfo
wx.setStorageSync('userInfo', userInfo)
wx.showLoading({ title: '上传中...', mask: true })
try {
await app.request('/api/miniprogram/user/update', { method: 'POST', data: { userId: userInfo.id, avatar: tempPath } })
} catch (e) { console.log('头像同步失败', e) }
wx.showToast({ title: '头像已更新', icon: 'success' })
const uploadRes = await new Promise((resolve, reject) => {
wx.uploadFile({
url: app.globalData.baseUrl + '/api/miniprogram/upload',
filePath: tempPath,
name: 'file',
formData: { folder: 'avatars' },
success: (r) => {
try {
const data = JSON.parse(r.data)
data.success ? resolve(data) : reject(new Error(data.error || '上传失败'))
} catch (e) { reject(new Error('解析失败')) }
},
fail: (e) => reject(e)
})
})
const avatarUrl = app.globalData.baseUrl + uploadRes.data.url
const userInfo = this.data.userInfo
userInfo.avatar = avatarUrl
this.setData({ userInfo })
app.globalData.userInfo = userInfo
wx.setStorageSync('userInfo', userInfo)
await app.request('/api/miniprogram/user/update', { method: 'POST', data: { userId: userInfo.id, avatar: avatarUrl } })
wx.hideLoading()
wx.showToast({ title: '头像已更新', icon: 'success' })
} catch (e) {
wx.hideLoading()
wx.showToast({ title: e.message || '上传失败,请重试', icon: 'none' })
}
}
})
},

View File

@@ -186,6 +186,17 @@
</view>
</view>
<!-- 头像弹窗:必须点击 button 才能获取微信头像(隐私规范) -->
<view class="modal-overlay" wx:if="{{showAvatarModal}}" bindtap="closeAvatarModal">
<view class="modal-content avatar-modal" catchtap="stopPropagation">
<view class="modal-close" bindtap="closeAvatarModal">✕</view>
<text class="avatar-modal-title">获取微信头像</text>
<text class="avatar-modal-desc">点击下方按钮使用你的微信头像</text>
<button class="btn-choose-avatar" open-type="chooseAvatar" bindchooseavatar="onChooseAvatar">使用微信头像</button>
<view class="avatar-modal-cancel" bindtap="closeAvatarModal">取消</view>
</view>
</view>
<!-- 修改昵称弹窗 -->
<view class="modal-overlay" wx:if="{{showNicknameModal}}" bindtap="closeNicknameModal">
<view class="modal-content nickname-modal" catchtap="stopPropagation">

View File

@@ -3,14 +3,20 @@
* 设计稿primary #4FD1C5, vip-gold #C8A146, card-dark #1A1A1A, card-inner #252525
*/
.page { min-height: 100vh; background: #121212; padding-bottom: 220rpx; }
/* 真机适配:底部留足 TabBar + 安全区,避免「我的订单」被遮挡 */
.page {
min-height: 100vh;
background: #121212;
padding-bottom: calc(220rpx + env(safe-area-inset-bottom, 0px));
}
/* ===== 导航栏 ===== */
/* ===== 导航栏(避让右上角系统胶囊) ===== */
.nav-bar {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
background: rgba(18,18,18,0.9); backdrop-filter: blur(8rpx);
display: flex; align-items: center;
min-height: 44px; padding: 0 32rpx;
min-height: 44px;
padding: 0 200rpx 0 32rpx; /* 右侧 200rpx 避让真机右上角胶囊 */
border-bottom: 1rpx solid rgba(255,255,255,0.05);
}
.nav-title { font-size: 40rpx; font-weight: bold; color: #4FD1C5; }
@@ -155,6 +161,18 @@
.agree-link { color: #4FD1C5; text-decoration: underline; padding: 0 4rpx; }
.btn-wechat-disabled { opacity: 0.6; }
/* 头像弹窗 */
.avatar-modal .avatar-modal-title { display: block; font-size: 36rpx; font-weight: bold; color: #fff; text-align: center; margin-bottom: 16rpx; }
.avatar-modal .avatar-modal-desc { display: block; font-size: 26rpx; color: rgba(255,255,255,0.6); text-align: center; margin-bottom: 32rpx; }
.avatar-modal .btn-choose-avatar {
width: 100%; height: 88rpx; margin: 0 0 24rpx 0; padding: 0;
display: flex; align-items: center; justify-content: center;
background: #4FD1C5; color: #000; font-size: 30rpx; font-weight: 600;
border-radius: 44rpx; border: none;
}
.avatar-modal .btn-choose-avatar::after { border: none; }
.avatar-modal .avatar-modal-cancel { display: block; text-align: center; font-size: 28rpx; color: rgba(255,255,255,0.5); padding: 16rpx; }
/* 手机/微信号弹窗 */
.contact-modal-overlay { background: rgba(0,0,0,0.85); backdrop-filter: blur(8rpx); }
.contact-modal { width: 90%; max-width: 600rpx; background: #1A1A1A; border-radius: 48rpx; padding: 48rpx 40rpx; border: 1rpx solid rgba(255,255,255,0.1); }
@@ -181,4 +199,5 @@
.modal-btn-cancel { background: rgba(255,255,255,0.1); color: #fff; }
.modal-btn-confirm { background: #4FD1C5; color: #000; font-weight: 600; }
.bottom-space { height: 80rpx; }
/* 底部留白:配合 page padding-bottom避免内容被 TabBar 遮挡 */
.bottom-space { height: calc(80rpx + env(safe-area-inset-bottom, 0px)); }