| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- <template>
- <view class="family-energy-bar">
- <view class="bar-header" @click="toggleMembers">
- <text class="bar-icon">{{ dimensionIcon }}</text>
- <text class="bar-title">{{ dimensionName }}能量</text>
- <text class="bar-total-score">{{ familyScore }}</text>
- <text class="bar-expand-icon">{{ showMembers ? '▼' : '▶' }}</text>
- </view>
- <view class="bar-list" v-if="showMembers">
- <!-- 非堆叠模式:单色条 -->
- <template v-if="!isStacked">
- <view class="bar-row bar-row-family">
- <text class="bar-label">全家</text>
- <view class="bar-track">
- <view class="bar-fill" :style="{ width: familyScore + '%', background: dimensionColor }"></view>
- </view>
- <text class="bar-value">{{ familyScore }}</text>
- </view>
- <view
- class="bar-row bar-row-member"
- v-for="member in members"
- :key="member.memberId"
- @click="onMemberClick(member)"
- >
- <text class="bar-label">{{ member.name }}</text>
- <view class="bar-track">
- <view class="bar-fill" :style="{ width: getMemberScore(member) + '%', background: memberColor(member) }"></view>
- </view>
- <text class="bar-value">{{ getMemberScore(member) }}</text>
- <text class="bar-arrow">›</text>
- </view>
- </template>
- <!-- 堆叠模式:主维度 + 副维度两段色 -->
- <template v-else>
- <view class="bar-row bar-row-family">
- <text class="bar-label">全家</text>
- <view class="bar-track stacked-track">
- <view class="bar-fill bar-fill-primary" :style="{ width: (sandboxData[dualDimension.primary.code + 'Score'] || 0) + '%', background: dualDimension.primary.color || '#8B5CF6' }"></view>
- <view class="bar-fill bar-fill-secondary" :style="{ width: (sandboxData[dualDimension.secondary.code + 'Score'] || 0) + '%', background: dualDimension.secondary.color || '#3B82F6' }"></view>
- </view>
- <text class="bar-value">{{ familyScore }}</text>
- </view>
- <view
- class="bar-row bar-row-member"
- v-for="member in members"
- :key="member.memberId"
- @click="onMemberClick(member)"
- >
- <text class="bar-label">{{ member.name }}</text>
- <view class="bar-track stacked-track">
- <view class="bar-fill bar-fill-primary" :style="{ width: getMemberPrimaryScore(member) + '%', background: dualDimension.primary.color || '#8B5CF6' }"></view>
- <view class="bar-fill bar-fill-secondary" :style="{ width: getMemberSecondaryScore(member) + '%', background: dualDimension.secondary.color || '#3B82F6' }"></view>
- </view>
- <text class="bar-value">{{ getMemberScore(member) }}</text>
- <text class="bar-arrow">›</text>
- </view>
- <!-- 图例 -->
- <view class="stacked-legend">
- <view class="legend-item">
- <view class="legend-dot" :style="{ background: dualDimension.primary.color || '#8B5CF6' }"></view>
- <text class="legend-label">{{ dualDimension.primary.label || '心' }}</text>
- </view>
- <view class="legend-item">
- <view class="legend-dot" :style="{ background: dualDimension.secondary.color || '#3B82F6' }"></view>
- <text class="legend-label">{{ dualDimension.secondary.label || '智' }}</text>
- </view>
- </view>
- </template>
- </view>
- </view>
- </template>
- <script>
- export default {
- props: {
- dimensionCode: { type: String, required: true },
- sandboxData: { type: Object, default: null },
- dualDimension: { type: [String, Object], default: '' }
- },
- data: function() {
- return {
- showMembers: false
- }
- },
- computed: {
- isStacked: function() {
- return typeof this.dualDimension === 'object' && this.dualDimension !== null && this.dualDimension.primary && this.dualDimension.secondary
- },
- dimensionConfig: function() {
- var maps = {
- body: { name: '身体', icon: '\u{1F3C3}', color: '#10B981' },
- mind: { name: '心', icon: '\u2764', color: '#8B5CF6' },
- wisdom: { name: '智', icon: '\u{1F9E0}', color: '#3B82F6' },
- action: { name: '行动', icon: '\u{1F3CB}', color: '#F97316' },
- wealth: { name: '富', icon: '\u{1F4B0}', color: '#F59E0B' }
- }
- return maps[this.dimensionCode] || { name: '', icon: '', color: '#999' }
- },
- dimensionIcon: function() { return this.dimensionConfig.icon },
- dimensionName: function() { return this.dimensionConfig.name },
- dimensionColor: function() { return this.dimensionConfig.color },
- familyScore: function() {
- if (!this.sandboxData) return 0
- if (this.isStacked) {
- var primary = this.sandboxData[this.dualDimension.primary.code + 'Score'] || 0
- var secondary = this.sandboxData[this.dualDimension.secondary.code + 'Score'] || 0
- return primary + secondary
- }
- return this.sandboxData[this.dimensionCode + 'Score'] || 0
- },
- members: function() {
- return this.sandboxData && this.sandboxData.members ? this.sandboxData.members : []
- }
- },
- methods: {
- toggleMembers: function() {
- this.showMembers = !this.showMembers
- },
- getMemberScore: function(member) {
- if (this.isStacked) {
- var primary = member[this.dualDimension.primary.code + 'Score'] || 0
- var secondary = member[this.dualDimension.secondary.code + 'Score'] || 0
- return primary + secondary
- }
- return member[this.dimensionCode + 'Score'] || 0
- },
- getMemberPrimaryScore: function(member) {
- if (!this.isStacked) return 0
- return member[this.dualDimension.primary.code + 'Score'] || 0
- },
- getMemberSecondaryScore: function(member) {
- if (!this.isStacked) return 0
- return member[this.dualDimension.secondary.code + 'Score'] || 0
- },
- memberColor: function(member) {
- var score = this.getMemberScore(member)
- if (score >= 80) return '#10B981'
- if (score >= 60) return '#3B82F6'
- if (score >= 40) return '#F97316'
- return '#EF4444'
- },
- onMemberClick: function(member) {
- var self = this
- uni.navigateTo({
- url: '/pages/member-detail/member-detail?memberId=' + member.memberId +
- '&memberType=' + (member.memberType || 'child') +
- '&memberName=' + encodeURIComponent(member.name) +
- '&dimensionCode=' + self.dimensionCode +
- '&entrySource=body-card'
- })
- }
- }
- }
- </script>
- <style scoped>
- .family-energy-bar {
- margin: 20rpx 30rpx;
- background: #fff;
- border-radius: 24rpx;
- padding: 28rpx 24rpx;
- box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
- }
- .bar-header {
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-bottom: 20rpx;
- }
- .bar-icon { font-size: 32rpx; margin-right: 8rpx; }
- .bar-title { font-size: 28rpx; font-weight: bold; color: #333; flex: 1; }
- .bar-total-score { font-size: 40rpx; font-weight: bold; color: #F97316; margin-right: 12rpx; }
- .bar-expand-icon {
- font-size: 20rpx;
- color: #bbb;
- transition: transform 0.3s ease;
- }
- .bar-row {
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-bottom: 16rpx;
- padding: 8rpx 0;
- }
- .bar-row:last-child { margin-bottom: 0; }
- .bar-row-member:active { opacity: 0.6; }
- .bar-label {
- font-size: 24rpx;
- color: #666;
- width: 80rpx;
- flex-shrink: 0;
- }
- .bar-track {
- flex: 1;
- height: 24rpx;
- background: #f0f0f0;
- border-radius: 12rpx;
- overflow: hidden;
- margin: 0 12rpx;
- }
- .bar-fill {
- height: 100%;
- border-radius: 12rpx;
- transition: width 0.5s ease;
- }
- .bar-value {
- font-size: 26rpx;
- font-weight: bold;
- color: #333;
- width: 60rpx;
- text-align: right;
- flex-shrink: 0;
- }
- .bar-arrow {
- font-size: 28rpx;
- color: #ccc;
- margin-left: 8rpx;
- }
- .bar-row-family .bar-fill {
- background: linear-gradient(90deg, #F97316, #FB923C);
- }
- /* ===== 堆叠模式 ===== */
- .stacked-track {
- display: flex;
- flex-direction: row;
- overflow: hidden;
- }
- .bar-fill-primary {
- border-radius: 12rpx 0 0 12rpx;
- }
- .bar-fill-secondary {
- border-radius: 0 12rpx 12rpx 0;
- margin-left: 2rpx;
- }
- .stacked-legend {
- display: flex;
- flex-direction: row;
- justify-content: center;
- margin-top: 12rpx;
- gap: 24rpx;
- }
- .legend-item {
- display: flex;
- flex-direction: row;
- align-items: center;
- }
- .legend-dot {
- width: 16rpx;
- height: 16rpx;
- border-radius: 8rpx;
- margin-right: 6rpx;
- }
- .legend-label {
- font-size: 22rpx;
- color: #999;
- }
- </style>
|