更新小程序头像选择逻辑,采用绝对定位覆盖原生按钮,避免样式干扰。新增相关最佳实践文档,确保开发一致性和用户体验。优化个人资料页面,提升用户交互流畅性。

This commit is contained in:
Alex-larget
2026-03-19 19:18:50 +08:00
parent 588dad2518
commit 181f092402
8 changed files with 89 additions and 18 deletions

View File

@@ -0,0 +1,41 @@
# 原生按钮覆盖定位,避免样式干扰
**日期**2026-03-19
**场景**:小程序中需在头像、图片等区域触发 `open-type="chooseAvatar"` 等原生能力,用 `<button>` 包裹会导致原生样式(灰色矩形、边框等)影响界面。
**方案****不用 button 包裹**,改为 **button 绝对定位覆盖** 在目标区域上方。
## 正确结构
```html
<!-- 外层position: relative 作为定位参考 -->
<view class="avatar-wrap">
<!-- 实际展示内容:头像、徽章等 -->
<view class="avatar-inner">...</view>
<view class="vip-badge">VIP</view>
<!-- 透明 button 覆盖在上方,同级而非包裹 -->
<button class="avatar-overlay-btn" open-type="chooseAvatar" bindchooseavatar="onChooseAvatar"></button>
</view>
```
```css
.avatar-wrap { position: relative; }
.avatar-overlay-btn {
position: absolute; top: 0; left: 0;
width: 130rpx; height: 130rpx; /* 与头像一致 */
padding: 0; margin: 0;
background: transparent; border: none;
}
.avatar-overlay-btn::after { border: none; }
```
## 要点
- **同级关系**button 与展示元素是 sibling不是 parent-child
- **绝对定位**`position: absolute` 覆盖在目标区域上,不参与文档流
- **透明无内容**button 仅负责点击事件,样式完全透明
- **适用**chooseAvatar、open-type 等需 button 触发的原生能力
## 升级 Skill
已写入 `miniprogram-dev` SKILL §12。

View File

@@ -13,3 +13,4 @@
| 2026-03-14 | 我的页设置入口隐藏;资料修改引导场景梳理(登录后、@某人、找伙伴、链接卡若) | [2026-03-14.md](./2026-03-14.md) |
| 2026-03-16 | 编辑资料页分享名片:转发/朋友圈特殊处理Canvas 绘制封面,标题「昵称+为您分享名片」 | [2026-03-16.md](./2026-03-16.md) |
| 2026-03-17 | 代付美团式:读页→代付页→分享;详情页双态(发起人/好友);目录 loading、最新新增 5 条折叠 | [2026-03-17.md](./2026-03-17.md) |
| 2026-03-19 | 原生按钮覆盖定位chooseAvatar 等用绝对定位 overlay 覆盖,禁止 button 包裹,避免原生样式影响 | [2026-03-19-原生按钮覆盖定位.md](./2026-03-19-原生按钮覆盖定位.md) |

View File

@@ -53,6 +53,7 @@
| 2026-03-17 | 后端、团队 | 架构/最佳实践 | api-dev SKILL | Redis 缓存parts/hot/recommended/stats/config/章节 content容灾回退 DBOSS 上传;/health 返回 database/redis 状态 |
| 2026-03-18 | 小程序、团队 | 业务规则/最佳实践 | - | 分享链路兼容好友/朋友圈 singlePage单页模式能力降级不支付/不自动领取),引导点击底部“前往小程序”进入完整版 |
| 2026-03-18 | 产品、后端、管理端、测试 | 文档归档/需求口径 | - | 文档归档整理:以《以界面定需求》为基准,各角色重整“功能需求+验收口径+风险点”并写入各自经验库;补齐《项目落地推进表》 |
| 2026-03-19 | 小程序 | 最佳实践 | miniprogram-dev SKILL §11 | 原生按钮覆盖定位chooseAvatar 等用绝对定位 overlay 覆盖,禁止 button 包裹,避免原生样式影响(灰色矩形等) |
---

View File

@@ -41,9 +41,10 @@
| 2026-03-17 | 会议收尾:源码优化 5 项全部完成;开发环境测试通过 | 已完成 |
| 2026-03-18 | 吸收经验:分享链路需兼容好友/朋友圈 singlePage单页模式能力降级并引导“前往小程序”进入完整版 | 已完成 |
| 2026-03-18 | 会议:支付超级个体前/开通后资料默认校验,跳转 avatar-nickname 引导页(仅头像+昵称) | 已完成 |
| 2026-03-19 | 吸收经验原生按钮覆盖定位chooseAvatar 用绝对定位 overlay 覆盖头像,禁止 button 包裹,已升级 SKILL §11 | 已完成 |
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD状态用已完成 / 进行中 / 待续 / 搁置
---
**最后更新**2026-03-18
**最后更新**2026-03-19

View File

@@ -104,7 +104,24 @@ description: Soul 创业派对小程序开发规范。在 miniprogram/ 下编辑
---
## 11. 何时使用本 Skill
## 11. 原生按钮覆盖定位(避免样式干扰)
- **场景**:在头像、图片等区域需触发 `open-type="chooseAvatar"` 等原生能力时,**禁止用 button 包裹**目标元素,否则会受原生样式影响(灰色矩形、边框等)。
- **正确做法**:用 **button 绝对定位覆盖** 在目标区域上方,与展示元素为同级关系。
- **结构示例**
```html
<view class="avatar-wrap">
<view class="avatar-inner">...</view>
<view class="vip-badge">VIP</view>
<button class="avatar-overlay-btn" open-type="chooseAvatar" bindchooseavatar="onChooseAvatar"></button>
</view>
```
- **样式**`.avatar-wrap { position: relative; }``.avatar-overlay-btn { position: absolute; top: 0; left: 0; width/height 与目标一致; background: transparent; border: none; }``::after { border: none; }`。
- **口诀**:同级覆盖,绝对定位,透明按钮。
---
## 12. 何时使用本 Skill
- 在 **miniprogram/** 下新增或修改页面、组件、utils 时。
- 在小程序内新增或修改任何网络请求路径时(必须保持 `/api/miniprogram/...`)。
@@ -114,5 +131,6 @@ description: Soul 创业派对小程序开发规范。在 miniprogram/ 下编辑
- 做个人中心、设置页布局时(遵循 §7卡片区边距 16rpx
- 做阅读、文章等需长按复制的文本时(遵循 §9text 加 user-select
- 做编辑资料页分享名片时(遵循 §10
- 做头像上传、chooseAvatar 等需 button 触发的原生能力时(遵循 §11用绝对定位覆盖禁止 button 包裹)。
遵循本 Skill 可保证小程序只与 soul-api 的 miniprogram 路由组对接,避免与管理端或 next-project 接口混用。

View File

@@ -409,6 +409,8 @@ Page({
wx.hideLoading()
this.setData({ receivingAll: false })
this.loadPendingConfirm()
this.loadMyEarnings()
this.loadWalletBalance()
}
},
@@ -914,6 +916,7 @@ Page({
wx.hideLoading()
wx.showToast({ title: '提现申请已提交', icon: 'success' })
this.loadMyEarnings()
this.loadWalletBalance()
} catch (e) {
wx.hideLoading()
wx.showToast({ title: e.message || '提现失败', icon: 'none' })

View File

@@ -23,16 +23,15 @@
<view class="profile-card" wx:else>
<view class="profile-card-inner">
<view class="profile-top-row">
<button class="avatar-wrap-btn" open-type="chooseAvatar" bindchooseavatar="onChooseAvatar">
<view class="avatar-wrap">
<view class="avatar-inner {{isVip ? 'avatar-vip' : ''}}">
<image wx:if="{{userInfo.avatar}}" class="avatar-img" src="{{userInfo.avatar}}" mode="aspectFill"/>
<text wx:else class="avatar-text">{{userInfo.nickname ? userInfo.nickname[0] : '?'}}</text>
</view>
<view class="vip-badge" wx:if="{{isVip}}">VIP</view>
<view class="vip-badge vip-badge-gray" wx:else>VIP</view>
<view class="avatar-wrap">
<view class="avatar-inner {{isVip ? 'avatar-vip' : ''}}">
<image wx:if="{{userInfo.avatar}}" class="avatar-img" src="{{userInfo.avatar}}" mode="aspectFill"/>
<text wx:else class="avatar-text">{{userInfo.nickname ? userInfo.nickname[0] : '?'}}</text>
</view>
</button>
<view class="vip-badge" wx:if="{{isVip}}">VIP</view>
<view class="vip-badge vip-badge-gray" wx:else>VIP</view>
<button class="avatar-overlay-btn" open-type="chooseAvatar" bindchooseavatar="onChooseAvatar"></button>
</view>
<view class="profile-meta">
<view class="profile-name-row">
<text class="user-name" bindtap="editNickname">{{userInfo.nickname || '点击设置昵称'}}</text>

View File

@@ -41,15 +41,22 @@
border: 1rpx solid rgba(75,85,99,0.5);
}
.profile-top-row { display: flex; align-items: flex-start; gap: 32rpx; }
/* 头像按钮:透明无边框,宽高与头像一致,点击直接唤起微信选择器(微信头像/相册/拍照) */
.avatar-wrap-btn {
width: 40rpx; height: 130rpx;
padding: 0; margin: 0; background: transparent; border: none;
display: flex; align-items: center; justify-content: center;
/* 头像区域view 负责展示button 绝对定位覆盖其上,避免原生样式影响 */
.avatar-wrap {
position: relative;
width: 130rpx; height: 130rpx;
flex-shrink: 0;
}
.avatar-wrap-btn::after { border: none; }
.avatar-wrap { position: relative; flex-shrink: 0; }
/* 绝对定位的按钮覆盖在头像上,透明无样式,点击唤起微信选择器(微信头像/相册/拍照) */
.avatar-overlay-btn {
position: absolute;
left: 0; top: 0;
width: 130rpx; height: 130rpx;
padding: 0; margin: 0;
background: transparent; border: none;
display: block;
}
.avatar-overlay-btn::after { border: none; }
.avatar-inner {
width: 130rpx; height: 130rpx; border-radius: 50%; overflow: hidden;
background: #1C2524; border: 5rpx solid #374151;