|
|
@@ -16,6 +16,7 @@
|
|
|
:isSelected="member.id === selectedMemberId"
|
|
|
:energyScore="getMemberEnergy(member)"
|
|
|
@select="onSelect"
|
|
|
+ @longpress="onLongPress"
|
|
|
/>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
@@ -27,8 +28,37 @@
|
|
|
:isSelected="member.id === selectedMemberId"
|
|
|
:energyScore="getMemberEnergy(member)"
|
|
|
@select="onSelect"
|
|
|
+ @longpress="onLongPress"
|
|
|
/>
|
|
|
</view>
|
|
|
+
|
|
|
+ <!-- 长按操作菜单 -->
|
|
|
+ <view class="action-overlay" v-if="showActionMenu" @click="closeMenu"></view>
|
|
|
+ <view class="action-sheet" v-if="showActionMenu">
|
|
|
+ <view class="action-header">
|
|
|
+ <text class="action-title">{{ actionMember ? actionMember.nickname : '' }}</text>
|
|
|
+ <text class="action-relation">{{ actionMember ? (actionMember.relationship || actionMember.relativeLabel || '') : '' }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="action-item" @click="switchToMember" v-if="canSwitch">
|
|
|
+ <text class="action-item-icon">🔄</text>
|
|
|
+ <text class="action-item-text">切换到该成员视角</text>
|
|
|
+ </view>
|
|
|
+ <view class="action-item" @click="editMember">
|
|
|
+ <text class="action-item-icon">✏️</text>
|
|
|
+ <text class="action-item-text">编辑资料</text>
|
|
|
+ </view>
|
|
|
+ <view class="action-item" @click="callMember" v-if="actionMember && actionMember.phone">
|
|
|
+ <text class="action-item-icon">📞</text>
|
|
|
+ <text class="action-item-text">拨打电话</text>
|
|
|
+ </view>
|
|
|
+ <view class="action-item action-danger" @click="removeMember" v-if="canRemove">
|
|
|
+ <text class="action-item-icon">🗑️</text>
|
|
|
+ <text class="action-item-text">移除成员</text>
|
|
|
+ </view>
|
|
|
+ <view class="action-item action-cancel" @click="closeMenu">
|
|
|
+ <text class="action-item-text">取消</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
@@ -40,12 +70,87 @@ export default {
|
|
|
props: {
|
|
|
members: { type: Array, default: function() { return [] } },
|
|
|
selectedMemberId: { type: Number, default: null },
|
|
|
- energyMap: { type: Object, default: function() { return {} } }
|
|
|
+ energyMap: { type: Object, default: function() { return {} } },
|
|
|
+ isParent: { type: Boolean, default: false }
|
|
|
+ },
|
|
|
+ data: function() {
|
|
|
+ return {
|
|
|
+ showActionMenu: false,
|
|
|
+ actionMember: null
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ canSwitch: function() {
|
|
|
+ return this.actionMember && this.actionMember.userId
|
|
|
+ },
|
|
|
+ canRemove: function() {
|
|
|
+ return this.isParent && this.actionMember && !this.actionMember.isSelf
|
|
|
+ }
|
|
|
},
|
|
|
methods: {
|
|
|
onSelect: function(member) {
|
|
|
this.$emit('select', member)
|
|
|
},
|
|
|
+ onLongPress: function(member) {
|
|
|
+ this.actionMember = member
|
|
|
+ this.showActionMenu = true
|
|
|
+ },
|
|
|
+ closeMenu: function() {
|
|
|
+ this.showActionMenu = false
|
|
|
+ this.actionMember = null
|
|
|
+ },
|
|
|
+ switchToMember: function() {
|
|
|
+ var member = this.actionMember
|
|
|
+ this.closeMenu()
|
|
|
+ if (member && member.userId) {
|
|
|
+ this.$emit('switch', member)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ editMember: function() {
|
|
|
+ var member = this.actionMember
|
|
|
+ this.closeMenu()
|
|
|
+ if (member) {
|
|
|
+ uni.navigateTo({ url: '/pages/profile-extra/family-members?editId=' + member.id })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ callMember: function() {
|
|
|
+ var member = this.actionMember
|
|
|
+ this.closeMenu()
|
|
|
+ if (member && member.phone) {
|
|
|
+ uni.makePhoneCall({ phoneNumber: member.phone })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ removeMember: function() {
|
|
|
+ var member = this.actionMember
|
|
|
+ this.closeMenu()
|
|
|
+ if (member) {
|
|
|
+ uni.showModal({
|
|
|
+ title: '确认移除',
|
|
|
+ content: '确定要移除 ' + member.nickname + ' 吗?',
|
|
|
+ success: function(res) {
|
|
|
+ if (res.confirm) {
|
|
|
+ uni.showLoading({ title: '移除中...' })
|
|
|
+ uni.request({
|
|
|
+ url: getApp().globalData.baseUrl + '/api/family/member/kick',
|
|
|
+ method: 'POST',
|
|
|
+ data: { memberId: member.id },
|
|
|
+ header: { 'Authorization': 'Bearer ' + uni.getStorageSync('token') },
|
|
|
+ success: function(resp) {
|
|
|
+ uni.hideLoading()
|
|
|
+ if (resp.data && resp.data.code === 200) {
|
|
|
+ uni.showToast({ title: '已移除', icon: 'success' })
|
|
|
+ // 通知父组件刷新
|
|
|
+ getApp().globalData.refreshFamilyMembers = true
|
|
|
+ } else {
|
|
|
+ uni.showToast({ title: resp.data.message || '移除失败', icon: 'none' })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
getMemberEnergy: function(member) {
|
|
|
if (!member || !this.energyMap) return null
|
|
|
return this.energyMap[member.id] || this.energyMap[member.memberId] || null
|
|
|
@@ -75,8 +180,70 @@ export default {
|
|
|
gap: 24rpx;
|
|
|
flex-wrap: wrap;
|
|
|
}
|
|
|
-/* scroll-view 内的 FamilyMemberCard 需要 display: inline-flex 支持横向滚动 */
|
|
|
.strip-scroll .strip-inner.scroll-mode {
|
|
|
display: inline-flex;
|
|
|
}
|
|
|
-</style>
|
|
|
+/* 操作菜单 */
|
|
|
+.action-overlay {
|
|
|
+ position: fixed;
|
|
|
+ top: 0; left: 0; right: 0; bottom: 0;
|
|
|
+ background: rgba(0,0,0,0.4);
|
|
|
+ z-index: 999;
|
|
|
+}
|
|
|
+.action-sheet {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ background: #FFFFFF;
|
|
|
+ border-radius: 24rpx 24rpx 0 0;
|
|
|
+ z-index: 1000;
|
|
|
+ padding: 20rpx 0;
|
|
|
+ padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
|
|
+}
|
|
|
+.action-header {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ padding: 16rpx 30rpx 20rpx;
|
|
|
+ border-bottom: 1rpx solid #F3F4F6;
|
|
|
+}
|
|
|
+.action-title {
|
|
|
+ font-size: 30rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1F2937;
|
|
|
+}
|
|
|
+.action-relation {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #9CA3AF;
|
|
|
+ margin-top: 4rpx;
|
|
|
+}
|
|
|
+.action-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 24rpx 30rpx;
|
|
|
+ border-bottom: 1rpx solid #F9FAFB;
|
|
|
+}
|
|
|
+.action-item:active {
|
|
|
+ background: #F3F4F6;
|
|
|
+}
|
|
|
+.action-item-icon {
|
|
|
+ font-size: 32rpx;
|
|
|
+ margin-right: 16rpx;
|
|
|
+}
|
|
|
+.action-item-text {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #374151;
|
|
|
+}
|
|
|
+.action-danger .action-item-text {
|
|
|
+ color: #EF4444;
|
|
|
+}
|
|
|
+.action-cancel {
|
|
|
+ justify-content: center;
|
|
|
+ margin-top: 10rpx;
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+.action-cancel .action-item-text {
|
|
|
+ color: #6B7280;
|
|
|
+}
|
|
|
+</style>
|