|
|
@@ -1,50 +1,61 @@
|
|
|
<template>
|
|
|
- <view v-if="hasFamily" class="family-member-strip">
|
|
|
- <!-- 待审批加入请求徽章 -->
|
|
|
- <view class="strip-toolbar" v-if="isAdmin">
|
|
|
- <view class="pending-badge" v-if="pendingCount > 0" @tap="openRequests">
|
|
|
- <text class="badge-icon">🔔</text>
|
|
|
- <text class="badge-count">{{ pendingCount }}</text>
|
|
|
+ <view class="member-strip">
|
|
|
+ <!-- 浮标头像 -->
|
|
|
+ <view class="fab-wrap">
|
|
|
+ <view class="fab-avatar" :class="{ active: fanOpen }" @tap="toggleFan">
|
|
|
+ <image class="fab-img" :src="currentMember.avatar || defaultAvatar" mode="aspectFill" />
|
|
|
+ <view class="fab-badge" v-if="pendingCount > 0 && isAdmin" @tap.stop="openRequests">
|
|
|
+ <text class="badge-count">{{ pendingCount }}</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
+ <view class="fab-pulse" v-if="fanOpen"></view>
|
|
|
</view>
|
|
|
- <scroll-view
|
|
|
- class="strip-scroll"
|
|
|
- scroll-x
|
|
|
- :scroll-with-animation="true"
|
|
|
- :enhanced="true"
|
|
|
- show-scrollbar="false"
|
|
|
- v-if="members.length >= 4"
|
|
|
- >
|
|
|
- <view class="strip-inner scroll-mode">
|
|
|
- <FamilyMemberCard
|
|
|
- v-for="member in members"
|
|
|
+
|
|
|
+ <!-- 扇形展开成员 -->
|
|
|
+ <view class="fan-wrap" :class="{ show: fanOpen }">
|
|
|
+ <view class="fan-overlay" @tap="closeFan"></view>
|
|
|
+ <view class="fan-container">
|
|
|
+ <view
|
|
|
+ class="fan-card"
|
|
|
+ v-for="(member, index) in members"
|
|
|
:key="member.id"
|
|
|
- :member="member"
|
|
|
- :isSelected="member.id === selectedMemberId"
|
|
|
- :energyScore="getMemberEnergy(member)"
|
|
|
- @select="onSelect"
|
|
|
- @longpress="onLongPress"
|
|
|
- />
|
|
|
+ :style="getFanPosition(member, index)"
|
|
|
+ :class="{ selected: member.id === selectedMemberId }"
|
|
|
+ @tap="selectMember(member)"
|
|
|
+ @longpress="onFanLongPress(member)"
|
|
|
+ >
|
|
|
+ <view class="fan-avatar-wrap">
|
|
|
+ <image class="fan-avatar" :src="member.avatar || defaultAvatar" mode="aspectFill" />
|
|
|
+ <view class="fan-relation-badge" :style="{ background: getRelationColor(member) }">
|
|
|
+ <text class="fan-relation-text">{{ member.relationship || member.relativeLabel || member.roleLabel || '成员' }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="fan-spouse-mark" v-if="member.isSpouse">
|
|
|
+ <text class="spouse-icon">💍</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <text class="fan-nickname">{{ member.nickname }}</text>
|
|
|
+ <view class="fan-score" v-if="getMemberEnergy(member) !== null">
|
|
|
+ <text class="score-value">{{ getMemberEnergy(member) }}</text>
|
|
|
+ <text class="score-unit">分</text>
|
|
|
+ </view>
|
|
|
+ <view class="fan-selected-mark" v-if="member.id === selectedMemberId">
|
|
|
+ <text class="mark-icon">✓</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
- </scroll-view>
|
|
|
- <view class="strip-inner center-mode" v-else>
|
|
|
- <FamilyMemberCard
|
|
|
- v-for="member in members"
|
|
|
- :key="member.id"
|
|
|
- :member="member"
|
|
|
- :isSelected="member.id === selectedMemberId"
|
|
|
- :energyScore="getMemberEnergy(member)"
|
|
|
- @select="onSelect"
|
|
|
- @longpress="onLongPress"
|
|
|
- />
|
|
|
</view>
|
|
|
|
|
|
- <!-- 长按操作菜单 -->
|
|
|
+ <!-- 长按/点击浮标弹出的个人菜单 -->
|
|
|
<view class="action-overlay" v-if="showActionMenu" @tap="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 class="action-avatar-wrap">
|
|
|
+ <image class="action-avatar" :src="actionMember ? (actionMember.avatar || defaultAvatar) : (currentMember.avatar || defaultAvatar)" mode="aspectFill" />
|
|
|
+ </view>
|
|
|
+ <view class="action-info">
|
|
|
+ <text class="action-title">{{ actionMember ? actionMember.nickname : (currentMember.nickname || '我') }}</text>
|
|
|
+ <text class="action-relation">{{ actionMember ? (actionMember.relationship || actionMember.relativeLabel || '') : '' }}</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
<view class="action-item" @tap="switchToMember" v-if="canSwitch">
|
|
|
<text class="action-item-icon">🔄</text>
|
|
|
@@ -91,24 +102,19 @@
|
|
|
<text class="req-name">{{ req.requesterNickname }}</text>
|
|
|
<text class="req-time">{{ req.createdAt }}</text>
|
|
|
</view>
|
|
|
- <view class="req-actions">
|
|
|
- <button class="req-btn btn-approve" @tap="approveRequest(req.id)">通过</button>
|
|
|
+ <view class="req-actions">
|
|
|
+ <button class="req-btn btn-approve" @tap="approveRequest(req.id)">通过</button>
|
|
|
<button class="req-btn btn-reject" @tap="rejectRequest(req.id)">拒绝</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <view v-else>
|
|
|
- <FamilyEmptyState type="card" />
|
|
|
- </view>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import FamilyMemberCard from '@/components/FamilyMemberCard'
|
|
|
import { kickFamilyMemberById, getPendingFamilyRequests, approveFamilyJoinRequest, rejectFamilyJoinRequest } from '@/utils/api'
|
|
|
|
|
|
export default {
|
|
|
- components: { FamilyMemberCard },
|
|
|
props: {
|
|
|
members: { type: Array, default: function() { return [] } },
|
|
|
selectedMemberId: { type: Number, default: null },
|
|
|
@@ -118,6 +124,7 @@ export default {
|
|
|
},
|
|
|
data: function() {
|
|
|
return {
|
|
|
+ fanOpen: false,
|
|
|
showActionMenu: false,
|
|
|
actionMember: null,
|
|
|
pendingCount: 0,
|
|
|
@@ -128,26 +135,86 @@ export default {
|
|
|
mounted: function() {
|
|
|
this.loadPendingRequests()
|
|
|
},
|
|
|
- computed: {
|
|
|
- hasFamily: function() {
|
|
|
- return this.$store.getters.hasFamily
|
|
|
+ computed: {
|
|
|
+ defaultAvatar: function() {
|
|
|
+ return '/static/default-avatar.png'
|
|
|
+ },
|
|
|
+ currentMember: function() {
|
|
|
+ if (!this.members || this.members.length === 0) return {}
|
|
|
+ for (var i = 0; i < this.members.length; i++) {
|
|
|
+ if (this.members[i].id === this.selectedMemberId) return this.members[i]
|
|
|
+ }
|
|
|
+ return this.members[0]
|
|
|
},
|
|
|
canSwitch: function() {
|
|
|
- // 仅家庭成员记录(source=family_member)且已关联账号时才能切换视角;
|
|
|
- // 家长卡片(source=parent)是 users 表记录,switch 接口只查 family_members 表,切换会失败
|
|
|
- return this.actionMember && this.actionMember.userId && this.actionMember.source === 'family_member'
|
|
|
+ var target = this.actionMember || this.currentMember
|
|
|
+ return target && target.userId && target.source === 'family_member'
|
|
|
},
|
|
|
canEdit: function() {
|
|
|
- return this.isParent && this.actionMember && !this.actionMember.userId && this.actionMember.source === 'family_member'
|
|
|
+ var target = this.actionMember || this.currentMember
|
|
|
+ return this.isParent && target && !target.userId && target.source === 'family_member'
|
|
|
},
|
|
|
canRemove: function() {
|
|
|
- // 仅家庭成员记录可移除(kick 接口只查 family_members 表),且不能移除自己
|
|
|
- return this.isParent && this.actionMember && !this.actionMember.isSelf && this.actionMember.source === 'family_member'
|
|
|
+ var target = this.actionMember || this.currentMember
|
|
|
+ return this.isParent && target && !target.isSelf && target.source === 'family_member'
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
- onSelect: function(member) {
|
|
|
+ // ===== 扇形展开 =====
|
|
|
+ toggleFan: function(e) {
|
|
|
+ if (this.fanOpen) {
|
|
|
+ this.closeFan()
|
|
|
+ } else {
|
|
|
+ this.fanOpen = true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ closeFan: function() {
|
|
|
+ this.fanOpen = false
|
|
|
+ },
|
|
|
+ // 计算扇形位置: 头像在左边,成员向右扇形排列
|
|
|
+ // 用固定的偏移量数组,避免用 transform-origin 的兼容问题
|
|
|
+ getFanPosition: function(member, index) {
|
|
|
+ if (!this.fanOpen) return 'transform: translate(0rpx, 0rpx) rotate(0deg);'
|
|
|
+ var total = this.members.length
|
|
|
+ var angleRange = 50
|
|
|
+ var steps = Math.max(total - 1, 1)
|
|
|
+ var angle = -angleRange + (index / steps) * angleRange * 2
|
|
|
+ var rad = angle * Math.PI / 180
|
|
|
+ var radius = 280
|
|
|
+ var dx = Math.cos(rad) * radius
|
|
|
+ var dy = Math.sin(rad) * radius
|
|
|
+ var rotation = angle * 0.3
|
|
|
+ return 'transform: translate(' + dx + 'rpx,' + dy + 'rpx) rotate(' + rotation + 'deg);'
|
|
|
+ },
|
|
|
+ getMemberEnergy: function(member) {
|
|
|
+ if (!member || !this.energyMap) return null
|
|
|
+ return this.energyMap[member.id] || this.energyMap[member.memberId] || null
|
|
|
+ },
|
|
|
+ getRelationColor: function(member) {
|
|
|
+ var rel = member && member.relationship
|
|
|
+ if (rel === '爸爸' || rel === '妈妈' || rel === '父亲' || rel === '母亲') return '#F97316'
|
|
|
+ if (rel === '儿子' || rel === '女儿') return '#0EA5E9'
|
|
|
+ if (rel === '爷爷' || rel === '奶奶' || rel === '外公' || rel === '外婆') return '#6B7280'
|
|
|
+ if (rel === '哥哥' || rel === '姐姐' || rel === '弟弟' || rel === '妹妹') return '#10B981'
|
|
|
+ if (member && member.isSpouse) return '#EC4899'
|
|
|
+ var role = member && member.effectiveRole
|
|
|
+ if (role === 'parent') return '#F97316'
|
|
|
+ if (role === 'child') return '#0EA5E9'
|
|
|
+ if (role === 'teacher') return '#8B5CF6'
|
|
|
+ return '#9CA3AF'
|
|
|
+ },
|
|
|
+
|
|
|
+ // ===== 成员选择 =====
|
|
|
+ selectMember: function(member) {
|
|
|
this.$emit('select', member)
|
|
|
+ // 关闭扇形
|
|
|
+ this.fanOpen = false
|
|
|
+ },
|
|
|
+
|
|
|
+ // ===== 个人菜单 =====
|
|
|
+ onFanLongPress: function(member) {
|
|
|
+ this.actionMember = member
|
|
|
+ this.showActionMenu = true
|
|
|
},
|
|
|
onLongPress: function(member) {
|
|
|
this.actionMember = member
|
|
|
@@ -158,14 +225,14 @@ export default {
|
|
|
this.actionMember = null
|
|
|
},
|
|
|
switchToMember: function() {
|
|
|
- var member = this.actionMember
|
|
|
+ var member = this.actionMember || this.currentMember
|
|
|
this.closeMenu()
|
|
|
if (member && member.userId) {
|
|
|
this.$emit('switch', member)
|
|
|
}
|
|
|
},
|
|
|
editMember: function() {
|
|
|
- var member = this.actionMember
|
|
|
+ var member = this.actionMember || this.currentMember
|
|
|
this.closeMenu()
|
|
|
if (member) {
|
|
|
var params = 'memberId=' + member.id + '&nickname=' + encodeURIComponent(member.nickname || '')
|
|
|
@@ -176,21 +243,21 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
questionnaireMember: function() {
|
|
|
- var member = this.actionMember
|
|
|
+ var member = this.actionMember || this.currentMember
|
|
|
this.closeMenu()
|
|
|
if (member) {
|
|
|
uni.navigateTo({ url: '/pages/action-detail/relationship-questionnaire?memberId=' + member.id })
|
|
|
}
|
|
|
},
|
|
|
interactionMember: function() {
|
|
|
- var member = this.actionMember
|
|
|
+ var member = this.actionMember || this.currentMember
|
|
|
this.closeMenu()
|
|
|
if (member) {
|
|
|
uni.navigateTo({ url: '/pages/action-detail/interaction-log?memberId=' + member.id })
|
|
|
}
|
|
|
},
|
|
|
callMember: function() {
|
|
|
- var member = this.actionMember
|
|
|
+ var member = this.actionMember || this.currentMember
|
|
|
this.closeMenu()
|
|
|
if (member && member.phone) {
|
|
|
uni.makePhoneCall({ phoneNumber: member.phone })
|
|
|
@@ -198,7 +265,7 @@ export default {
|
|
|
},
|
|
|
removeMember: function() {
|
|
|
var self = this
|
|
|
- var member = this.actionMember
|
|
|
+ var member = this.actionMember || this.currentMember
|
|
|
this.closeMenu()
|
|
|
if (member) {
|
|
|
uni.showModal({
|
|
|
@@ -211,7 +278,6 @@ export default {
|
|
|
.then(function() {
|
|
|
uni.hideLoading()
|
|
|
uni.showToast({ title: '已移除', icon: 'success' })
|
|
|
- // 通知父组件刷新成员列表(替代无效的 globalData 标志)
|
|
|
self.$emit('removed', member)
|
|
|
})
|
|
|
.catch(function(err) {
|
|
|
@@ -223,10 +289,8 @@ export default {
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
- getMemberEnergy: function(member) {
|
|
|
- if (!member || !this.energyMap) return null
|
|
|
- return this.energyMap[member.id] || this.energyMap[member.memberId] || null
|
|
|
- },
|
|
|
+
|
|
|
+ // ===== 待审批请求 =====
|
|
|
loadPendingRequests: function() {
|
|
|
var self = this
|
|
|
if (!this.isAdmin) return
|
|
|
@@ -293,35 +357,226 @@ export default {
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.family-member-strip {
|
|
|
- margin: 16rpx 0;
|
|
|
- padding: 0 30rpx;
|
|
|
+.member-strip {
|
|
|
+ position: relative;
|
|
|
+ margin: 8rpx 0;
|
|
|
+ padding: 0 20rpx;
|
|
|
+ z-index: 100;
|
|
|
+}
|
|
|
+
|
|
|
+/* ===== 浮标 ===== */
|
|
|
+.fab-wrap {
|
|
|
+ position: relative;
|
|
|
+ display: inline-block;
|
|
|
+ margin: 4rpx 0;
|
|
|
+ z-index: 1001;
|
|
|
+}
|
|
|
+.fab-avatar {
|
|
|
+ width: 68rpx;
|
|
|
+ height: 68rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #FFFFFF;
|
|
|
+ box-shadow: 0 4rpx 16rpx rgba(249, 115, 22, 0.25);
|
|
|
+ border: 3rpx solid #F97316;
|
|
|
+ position: relative;
|
|
|
+ transition: transform 0.3s ease;
|
|
|
+}
|
|
|
+.fab-avatar:active {
|
|
|
+ transform: scale(0.92);
|
|
|
+}
|
|
|
+.fab-avatar.active {
|
|
|
+ transform: rotate(180deg);
|
|
|
}
|
|
|
-.strip-scroll {
|
|
|
+.fab-img {
|
|
|
width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ border-radius: 50%;
|
|
|
+}
|
|
|
+.fab-badge {
|
|
|
+ position: absolute;
|
|
|
+ top: -4rpx;
|
|
|
+ right: -8rpx;
|
|
|
+ width: 32rpx;
|
|
|
+ height: 32rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #EF4444;
|
|
|
+ border: 2rpx solid #FFFFFF;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+.badge-count {
|
|
|
+ font-size: 16rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+.fab-pulse {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ width: 68rpx;
|
|
|
+ height: 68rpx;
|
|
|
+ margin-top: -34rpx;
|
|
|
+ margin-left: -34rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ border: 2rpx solid #F97316;
|
|
|
+ animation: fab-pulse-anim 2s ease-out infinite;
|
|
|
+}
|
|
|
+@keyframes fab-pulse-anim {
|
|
|
+ 0% { transform: scale(1); opacity: 0.8; }
|
|
|
+ 100% { transform: scale(2); opacity: 0; }
|
|
|
+}
|
|
|
+
|
|
|
+/* ===== 扇形展开 ===== */
|
|
|
+.fan-wrap {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100vh;
|
|
|
+ z-index: 999;
|
|
|
+ pointer-events: none;
|
|
|
+}
|
|
|
+.fan-wrap.show {
|
|
|
+ pointer-events: auto;
|
|
|
+}
|
|
|
+.fan-overlay {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background: rgba(0, 0, 0, 0.25);
|
|
|
+ opacity: 0;
|
|
|
+ transition: opacity 0.3s ease;
|
|
|
+ z-index: 999;
|
|
|
+}
|
|
|
+.fan-wrap.show .fan-overlay {
|
|
|
+ opacity: 1;
|
|
|
+}
|
|
|
+.fan-container {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.fan-card {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100rpx;
|
|
|
+ transform: translate(0, 0) rotate(0deg);
|
|
|
+ transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease;
|
|
|
+ opacity: 0;
|
|
|
+}
|
|
|
+.fan-wrap.show .fan-card {
|
|
|
+ opacity: 1;
|
|
|
+}
|
|
|
+.fan-card.selected {
|
|
|
+ filter: brightness(0.95);
|
|
|
+}
|
|
|
+.fan-avatar-wrap {
|
|
|
+ position: relative;
|
|
|
+ width: 64rpx;
|
|
|
+ height: 64rpx;
|
|
|
+ margin: 0 auto;
|
|
|
+}
|
|
|
+.fan-avatar {
|
|
|
+ width: 64rpx;
|
|
|
+ height: 64rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #FFFFFF;
|
|
|
+ border: 2rpx solid #FFFFFF;
|
|
|
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
|
|
|
+}
|
|
|
+.fan-relation-badge {
|
|
|
+ position: absolute;
|
|
|
+ bottom: -4rpx;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ padding: 1rpx 8rpx;
|
|
|
+ border-radius: 10rpx;
|
|
|
white-space: nowrap;
|
|
|
+ max-width: 76rpx;
|
|
|
}
|
|
|
-.strip-inner {
|
|
|
+.fan-relation-text {
|
|
|
+ font-size: 15rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
+.fan-spouse-mark {
|
|
|
+ position: absolute;
|
|
|
+ top: -2rpx;
|
|
|
+ right: -2rpx;
|
|
|
+ width: 22rpx;
|
|
|
+ height: 22rpx;
|
|
|
display: flex;
|
|
|
- flex-direction: row;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
}
|
|
|
-.strip-inner.scroll-mode {
|
|
|
- gap: 16rpx;
|
|
|
+.spouse-icon {
|
|
|
+ font-size: 16rpx;
|
|
|
}
|
|
|
-.strip-inner.center-mode {
|
|
|
+.fan-nickname {
|
|
|
+ display: block;
|
|
|
+ margin-top: 4rpx;
|
|
|
+ text-align: center;
|
|
|
+ font-size: 20rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ font-weight: 600;
|
|
|
+ text-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.4);
|
|
|
+ line-height: 1.2;
|
|
|
+ max-width: 90rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+.fan-score {
|
|
|
+ display: flex;
|
|
|
+ align-items: baseline;
|
|
|
+ justify-content: center;
|
|
|
+ margin-top: 2rpx;
|
|
|
+}
|
|
|
+.score-value {
|
|
|
+ font-size: 18rpx;
|
|
|
+ color: #FBBF24;
|
|
|
+ font-weight: bold;
|
|
|
+ text-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.4);
|
|
|
+}
|
|
|
+.score-unit {
|
|
|
+ font-size: 15rpx;
|
|
|
+ color: rgba(255, 255, 255, 0.8);
|
|
|
+ margin-left: 2rpx;
|
|
|
+ text-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.4);
|
|
|
+}
|
|
|
+.fan-selected-mark {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ right: 0;
|
|
|
+ width: 22rpx;
|
|
|
+ height: 22rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #F97316;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
justify-content: center;
|
|
|
- gap: 24rpx;
|
|
|
- flex-wrap: wrap;
|
|
|
}
|
|
|
-.strip-scroll .strip-inner.scroll-mode {
|
|
|
- display: inline-flex;
|
|
|
+.mark-icon {
|
|
|
+ font-size: 14rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ font-weight: bold;
|
|
|
}
|
|
|
-/* 操作菜单 */
|
|
|
+
|
|
|
+/* ===== 操作菜单 ===== */
|
|
|
.action-overlay {
|
|
|
position: fixed;
|
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
|
background: rgba(0,0,0,0.4);
|
|
|
- z-index: 999;
|
|
|
+ z-index: 1001;
|
|
|
}
|
|
|
.action-sheet {
|
|
|
position: fixed;
|
|
|
@@ -330,42 +585,63 @@ export default {
|
|
|
right: 0;
|
|
|
background: #FFFFFF;
|
|
|
border-radius: 24rpx 24rpx 0 0;
|
|
|
- z-index: 1000;
|
|
|
+ z-index: 1002;
|
|
|
padding: 20rpx 0;
|
|
|
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
|
|
+ animation: sheet-up 0.3s ease;
|
|
|
+}
|
|
|
+@keyframes sheet-up {
|
|
|
+ from { transform: translateY(100%); }
|
|
|
+ to { transform: translateY(0); }
|
|
|
}
|
|
|
.action-header {
|
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
align-items: center;
|
|
|
padding: 16rpx 30rpx 20rpx;
|
|
|
border-bottom: 1rpx solid #F3F4F6;
|
|
|
+ gap: 16rpx;
|
|
|
+}
|
|
|
+.action-avatar-wrap {
|
|
|
+ width: 56rpx;
|
|
|
+ height: 56rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ overflow: hidden;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+.action-avatar {
|
|
|
+ width: 56rpx;
|
|
|
+ height: 56rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+}
|
|
|
+.action-info {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 2rpx;
|
|
|
}
|
|
|
.action-title {
|
|
|
- font-size: 30rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
font-weight: 600;
|
|
|
color: #1F2937;
|
|
|
}
|
|
|
.action-relation {
|
|
|
- font-size: 24rpx;
|
|
|
+ font-size: 22rpx;
|
|
|
color: #9CA3AF;
|
|
|
- margin-top: 4rpx;
|
|
|
}
|
|
|
.action-item {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- padding: 24rpx 30rpx;
|
|
|
+ padding: 22rpx 30rpx;
|
|
|
border-bottom: 1rpx solid #F9FAFB;
|
|
|
}
|
|
|
.action-item:active {
|
|
|
background: #F3F4F6;
|
|
|
}
|
|
|
.action-item-icon {
|
|
|
- font-size: 32rpx;
|
|
|
+ font-size: 30rpx;
|
|
|
margin-right: 16rpx;
|
|
|
}
|
|
|
.action-item-text {
|
|
|
- font-size: 28rpx;
|
|
|
+ font-size: 26rpx;
|
|
|
color: #374151;
|
|
|
}
|
|
|
.action-danger .action-item-text {
|
|
|
@@ -373,34 +649,101 @@ export default {
|
|
|
}
|
|
|
.action-cancel {
|
|
|
justify-content: center;
|
|
|
- margin-top: 10rpx;
|
|
|
+ margin-top: 8rpx;
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
.action-cancel .action-item-text {
|
|
|
color: #6B7280;
|
|
|
}
|
|
|
|
|
|
-/* 待审批徽章 */
|
|
|
-.strip-toolbar {
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- padding: 8rpx 30rpx 4rpx;
|
|
|
+/* ===== 待审批请求 ===== */
|
|
|
+.request-overlay {
|
|
|
+ position: fixed;
|
|
|
+ top: 0; left: 0; right: 0; bottom: 0;
|
|
|
+ background: rgba(0,0,0,0.4);
|
|
|
+ z-index: 1003;
|
|
|
}
|
|
|
-
|
|
|
-.pending-badge {
|
|
|
+.request-sheet {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ background: #FFFFFF;
|
|
|
+ border-radius: 24rpx 24rpx 0 0;
|
|
|
+ z-index: 1004;
|
|
|
+ padding: 24rpx 0;
|
|
|
+ padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
|
|
+ animation: sheet-up 0.3s ease;
|
|
|
+ max-height: 60vh;
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+.sheet-header {
|
|
|
display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
align-items: center;
|
|
|
- padding: 8rpx 16rpx;
|
|
|
- background: #FFFFFF;
|
|
|
- border: 2rpx solid #EF4444;
|
|
|
- border-radius: 32rpx;
|
|
|
- box-shadow: 0 2rpx 8rpx rgba(239, 68, 68, 0.15);
|
|
|
+ padding: 0 30rpx 16rpx;
|
|
|
+ border-bottom: 1rpx solid #F3F4F6;
|
|
|
}
|
|
|
-
|
|
|
-.badge-icon {
|
|
|
+.sheet-title {
|
|
|
font-size: 28rpx;
|
|
|
- margin-right: 6rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1F2937;
|
|
|
+}
|
|
|
+.sheet-close {
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+.empty-requests {
|
|
|
+ padding: 60rpx 0;
|
|
|
+ text-align: center;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+.request-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 20rpx 30rpx;
|
|
|
+ gap: 16rpx;
|
|
|
+}
|
|
|
+.req-avatar {
|
|
|
+ width: 56rpx;
|
|
|
+ height: 56rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+.req-info {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 2rpx;
|
|
|
+}
|
|
|
+.req-name {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+.req-time {
|
|
|
+ font-size: 20rpx;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+.req-actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 12rpx;
|
|
|
+}
|
|
|
+.req-btn {
|
|
|
+ padding: 8rpx 20rpx;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ font-size: 22rpx;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+.btn-approve {
|
|
|
+ background: #10B981;
|
|
|
+ color: #FFFFFF;
|
|
|
+}
|
|
|
+.btn-reject {
|
|
|
+ background: #F3F4F6;
|
|
|
+ color: #666;
|
|
|
}
|
|
|
+</style>
|
|
|
|
|
|
.badge-count {
|
|
|
font-size: 26rpx;
|