Explorar el Código

fix(frontend): adjust position of heart icon and update tab icons

asus hace 3 meses
padre
commit
f90eda90c3

+ 131 - 107
cfc-frontend/custom-tab-bar/index.vue

@@ -1,27 +1,43 @@
 <template>
   <view class="tab-bar-wrap">
-    <!-- 普通用户 TabBar:4 Tab + 中间五维按钮 -->
+    <!-- 普通用户 TabBar:4 Tab(5等分,中间放按钮) -->
     <view v-if="currentRole !== 'teacher'" class="tab-bar">
       <view
-        v-for="(item, index) in normalTabs"
+        v-for="(item, index) in leftTabs"
         :key="index"
         class="tab-item"
-        :class="{ active: currentIndex === index }"
-        @click="switchTab(item, index)"
+        :class="{ active: isActive(item) }"
+        @click="switchTab(item)"
       >
         <image
           class="tab-icon"
-          :src="currentIndex === index ? item.selectedIcon : item.icon"
+          :src="isActive(item) ? item.selectedIcon : item.icon"
           mode="aspectFit"
         ></image>
         <text class="tab-text">{{ item.text }}</text>
       </view>
 
-      <!-- 中间五维启动器按钮 -->
+      <!-- 中间五维按钮(占 20% 槽位) -->
       <view class="center-btn" @click="openDimPanel">
         <view class="center-btn-inner">
           <text class="center-btn-icon">⭐</text>
         </view>
+        <text class="center-btn-label">五维</text>
+      </view>
+
+      <view
+        v-for="(item, index) in rightTabs"
+        :key="index"
+        class="tab-item"
+        :class="{ active: isActive(item) }"
+        @click="switchTab(item)"
+      >
+        <image
+          class="tab-icon"
+          :src="isActive(item) ? item.selectedIcon : item.icon"
+          mode="aspectFit"
+        ></image>
+        <text class="tab-text">{{ item.text }}</text>
       </view>
     </view>
 
@@ -31,37 +47,39 @@
         v-for="(item, index) in teacherTabs"
         :key="index"
         class="tab-item"
-        :class="{ active: currentIndex === index }"
-        @click="switchTab(item, index)"
+        :class="{ active: isActive(item) }"
+        @click="switchTab(item)"
       >
         <image
           class="tab-icon"
-          :src="currentIndex === index ? item.selectedIcon : item.icon"
+          :src="isActive(item) ? item.selectedIcon : item.icon"
           mode="aspectFit"
         ></image>
         <text class="tab-text">{{ item.text }}</text>
       </view>
     </view>
 
-    <!-- 五维半屏浮层 -->
-    <view class="dim-overlay" v-if="showDimPanel" @click="closeDimPanel">
-      <view class="dim-panel" @click.stop>
-        <view class="dim-header">
-          <text class="dim-title">五维能量</text>
-          <text class="dim-close" @click="closeDimPanel">✕</text>
-        </view>
-        <view class="dim-grid">
+    <!-- 五维横排展开 -->
+    <view class="dim-fan-overlay" v-if="showDimPanel" @click="closeDimPanel">
+      <view class="dim-fan-row">
+        <view
+          v-for="(dim, idx) in dimensions"
+          :key="idx"
+          :class="['dim-fan-item', { 'dim-fan-item-up': dim.code === 'mind', 'dim-fan-item-mid': dim.code === 'action' || dim.code === 'body' }]"
+          @click.stop="goDimension(dim)"
+        >
           <view
-            v-for="(dim, idx) in dimensions"
-            :key="idx"
-            class="dim-card"
-            :style="{ backgroundColor: dim.bgColor }"
-            @click="goDimension(dim)"
+            class="dim-fan-circle"
+            :class="{ 'fan-active': fanActive }"
+            :style="{ backgroundColor: dim.color, transitionDelay: (0.05 * idx) + 's' }"
           >
-            <text class="dim-icon">{{ dim.icon }}</text>
-            <text class="dim-name">{{ dim.name }}</text>
-            <text class="dim-desc">{{ dim.desc }}</text>
+            <text class="dim-fan-emoji">{{ dim.icon }}</text>
           </view>
+          <text
+            class="dim-fan-label"
+            :class="{ 'fan-active': fanActive }"
+            :style="{ transitionDelay: (0.05 * idx) + 's' }"
+          >{{ dim.name }}</text>
         </view>
       </view>
     </view>
@@ -74,6 +92,7 @@ export default {
     return {
       currentIndex: 0,
       showDimPanel: false,
+      fanActive: false,
       normalTabs: [
         { text: '首页', pagePath: '/pages/index-home/index', icon: '/static/tab-home.png', selectedIcon: '/static/tab-home-active.png' },
         { text: '健康', pagePath: '/pages/health-main/index', icon: '/static/tab-health.png', selectedIcon: '/static/tab-health-active.png' },
@@ -88,17 +107,23 @@ export default {
         { text: '我的', pagePath: '/pages/teacher/teacher-profile', icon: '/static/tab-profile.png', selectedIcon: '/static/tab-profile-active.png' }
       ],
       dimensions: [
+        { code: 'wealth', name: '富', desc: '利他创富', icon: '💧', color: '#F59E0B', bgColor: 'rgba(245,158,11,0.15)' },
         { code: 'action', name: '行', desc: '知行合一', icon: '🌿', color: '#10B981', bgColor: 'rgba(16,185,129,0.15)' },
         { code: 'mind', name: '心', desc: '大爱传承', icon: '🔥', color: '#FF6B9D', bgColor: 'rgba(255,107,157,0.15)' },
         { code: 'body', name: '身', desc: '主动健康', icon: '🌏', color: '#FF8C42', bgColor: 'rgba(255,140,66,0.15)' },
-        { code: 'wisdom', name: '智', desc: '赋能未来', icon: '⚡', color: '#6366F1', bgColor: 'rgba(99,102,241,0.15)' },
-        { code: 'wealth', name: '富', desc: '利他创富', icon: '💧', color: '#F59E0B', bgColor: 'rgba(245,158,11,0.15)' }
+        { code: 'wisdom', name: '智', desc: '赋能未来', icon: '⚡', color: '#6366F1', bgColor: 'rgba(99,102,241,0.15)' }
       ]
     }
   },
   computed: {
     currentRole() {
       return uni.getStorageSync('currentRole') || 'parent'
+    },
+    leftTabs() {
+      return this.normalTabs.slice(0, 2)
+    },
+    rightTabs() {
+      return this.normalTabs.slice(2)
     }
   },
   mounted() {
@@ -108,19 +133,23 @@ export default {
     })
   },
   methods: {
+    isActive(item) {
+      var pages = getCurrentPages()
+      if (!pages.length) return false
+      var currentPath = '/' + pages[pages.length - 1].route
+      return item.pagePath === currentPath
+    },
     updateSelected() {
       var pages = getCurrentPages()
       if (pages.length) {
         var currentPage = pages[pages.length - 1]
         var currentPath = '/' + currentPage.route
-
         var tabs = this.currentRole === 'teacher' ? this.teacherTabs : this.normalTabs
         var idx = tabs.findIndex(function(t) { return t.pagePath === currentPath })
         this.currentIndex = idx >= 0 ? idx : 0
       }
     },
-    switchTab(item, index) {
-      this.currentIndex = index
+    switchTab(item) {
       // TabBar 注册页用 switchTab,非 TabBar 页用 redirectTo
       var tabBarPages = ['/pages/index-home/index', '/pages/health-main/index', '/pages/growth-main/index', '/pages/profile-main/profile']
       if (tabBarPages.indexOf(item.pagePath) !== -1) {
@@ -131,9 +160,18 @@ export default {
     },
     openDimPanel() {
       this.showDimPanel = true
+      this.fanActive = false
+      var self = this
+      setTimeout(function() {
+        self.fanActive = true
+      }, 30)
     },
     closeDimPanel() {
-      this.showDimPanel = false
+      this.fanActive = false
+      var self = this
+      setTimeout(function() {
+        self.showDimPanel = false
+      }, 200)
     },
     goDimension(dim) {
       this.closeDimPanel()
@@ -176,7 +214,8 @@ export default {
 }
 
 .tab-item {
-  flex: 1;
+  width: 20%;
+  flex: none;
   display: flex;
   flex-direction: column;
   align-items: center;
@@ -185,14 +224,6 @@ export default {
   z-index: 1;
 }
 
-.tab-item:first-child {
-  margin-right: 40rpx;
-}
-
-.tab-item:nth-child(4) {
-  margin-left: 40rpx;
-}
-
 .tab-icon {
   width: 48rpx;
   height: 48rpx;
@@ -208,18 +239,17 @@ export default {
   color: #F97316;
 }
 
-/* 中间五维启动器按钮 */
+/* 中间五维按钮(占 20% 槽位) */
 .center-btn {
-  position: absolute;
-  left: 50%;
-  top: -20rpx;
-  transform: translateX(-50%);
-  width: 100rpx;
-  height: 100rpx;
-  z-index: 2;
+  width: 20%;
+  flex: none;
   display: flex;
+  flex-direction: column;
   align-items: center;
-  justify-content: center;
+  justify-content: flex-start;
+  padding-top: 0;
+  position: relative;
+  z-index: 2;
 }
 
 .center-btn-inner {
@@ -230,6 +260,7 @@ export default {
   display: flex;
   align-items: center;
   justify-content: center;
+  margin-top: -44rpx;
   box-shadow: 0 4rpx 16rpx rgba(249,115,22,0.4);
 }
 
@@ -237,89 +268,82 @@ export default {
   font-size: 40rpx;
   line-height: 1;
 }
+.center-btn-label {
+  font-size: 20rpx;
+  color: #999;
+  text-align: center;
+  margin-top: 2rpx;
+}
 
-/* 五维半屏浮层 */
-.dim-overlay {
+/* 五维横排展开 */
+.dim-fan-overlay {
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
-  background: rgba(0,0,0,0.5);
   z-index: 1000;
-  display: flex;
-  align-items: flex-end;
+  background: rgba(0,0,0,0.4);
   animation: fadeIn 0.2s ease;
 }
-
-.dim-panel {
-  width: 100%;
-  background: #fff;
-  border-radius: 32rpx 32rpx 0 0;
-  padding: 40rpx 32rpx;
-  padding-bottom: calc(100rpx + env(safe-area-inset-bottom) + 32rpx);
-  animation: slideUp 0.3s ease;
+.dim-fan-row {
+  position: absolute;
+  bottom: 140rpx;
+  left: 0;
+  right: 0;
+  display: flex;
+  justify-content: center;
+  padding: 0 16rpx;
 }
-
-.dim-header {
+.dim-fan-item {
   display: flex;
+  flex-direction: column;
   align-items: center;
-  justify-content: space-between;
-  margin-bottom: 32rpx;
+  width: 120rpx;
 }
-
-.dim-title {
-  font-size: 32rpx;
-  font-weight: 600;
-  color: #333;
-}
-
-.dim-close {
-  font-size: 32rpx;
-  color: #999;
-  padding: 8rpx;
+.dim-fan-item.dim-fan-item-up {
+  margin-top: -120rpx;
 }
-
-.dim-grid {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 20rpx;
+.dim-fan-item.dim-fan-item-mid {
+  margin-top: -60rpx;
 }
-
-.dim-card {
-  width: calc(33.33% - 14rpx);
-  border-radius: 20rpx;
-  padding: 24rpx 16rpx;
+.dim-fan-circle {
+  width: 88rpx;
+  height: 88rpx;
+  border-radius: 50%;
   display: flex;
-  flex-direction: column;
   align-items: center;
-  box-sizing: border-box;
+  justify-content: center;
+  opacity: 0;
+  transform: scale(0);
+  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
+  box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.15);
 }
-
-.dim-icon {
-  font-size: 48rpx;
-  margin-bottom: 8rpx;
+.dim-fan-circle.fan-active {
+  opacity: 1;
+  transform: scale(1);
 }
-
-.dim-name {
-  font-size: 28rpx;
+.dim-fan-emoji {
+  font-size: 40rpx;
+  line-height: 1;
+}
+.dim-fan-label {
+  font-size: 22rpx;
+  color: #fff;
   font-weight: 600;
-  color: #333;
-  margin-bottom: 4rpx;
+  margin-top: 8rpx;
+  text-align: center;
+  opacity: 0;
+  transform: translateY(-10rpx);
+  transition: all 0.3s ease;
 }
-
-.dim-desc {
-  font-size: 20rpx;
-  color: #999;
+.dim-fan-label.fan-active {
+  opacity: 1;
+  transform: translateY(0);
 }
 
 @keyframes fadeIn {
   from { opacity: 0; }
   to { opacity: 1; }
 }
-
-@keyframes slideUp {
-  from { transform: translateY(100%); }
-  to { transform: translateY(0); }
-}
 </style>

BIN
cfc-frontend/static/tab-health-active.png


BIN
cfc-frontend/static/tab-health.png


BIN
cfc-frontend/static/tab-home-active.png


BIN
cfc-frontend/static/tab-home.png