AppTabbar.vue 2.84 KB
<template>
  <view class="app-tabbar">
    <view class="app-tabbar__placeholder" />

    <view class="app-tabbar__wrap">
      <view class="app-tabbar__panel">
        <view
          v-for="item in tabItems"
          :key="item.key"
          class="app-tabbar__item"
          :class="{ 'is-active': isActive(item.key) }"
          @tap="handleTabClick(item)"
        >
          <view class="app-tabbar__item-inner">
            <view class="app-tabbar__icon">
              <component
                :is="item.icon"
                size="18"
                :color="isActive(item.key) ? activeColor : inactiveColor"
              />
            </view>
            <text class="app-tabbar__label">{{ item.title }}</text>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup>
import { computed, onMounted } from 'vue'
import Taro from '@tarojs/taro'
import { Category, Home, My, Notice, Service } from '@nutui/icons-vue-taro'
import { useTabbarStore } from '@/stores/tabbar'

const props = defineProps({
  current: {
    type: String,
    required: true,
  },
})

const activeColor = '#a67939'
const inactiveColor = '#8b95a7'

const tabbarStore = useTabbarStore()

const tabIconMap = {
  home: Home,
  message: Notice,
  application: Category,
  mine: My,
}

const tabItems = computed(() => (
  tabbarStore.visibleTabItems.map((item) => ({
    ...item,
    icon: tabIconMap[item.key] || Service,
  }))
))

const isActive = (name) => name === props.current

const handleTabClick = (item) => {
  if (!item?.page_url || item.key === props.current) {
    return
  }

  Taro.redirectTo({ url: item.page_url })
}

onMounted(() => {
  tabbarStore.ensureLoaded()
})
</script>

<style lang="less">
.app-tabbar {
  .app-tabbar__placeholder {
    height: 132rpx;
    flex-shrink: 0;
  }

  .app-tabbar__wrap {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 100;
    box-sizing: border-box;
  }

  .app-tabbar__panel {
    display: flex;
    align-items: stretch;
    min-height: 132rpx;
    padding: 16rpx 24rpx;
    border-top: 2rpx solid rgba(166, 121, 57, 0.12);
    background: rgba(255, 255, 255, 0.98);
    box-sizing: border-box;
    backdrop-filter: blur(12rpx);
  }

  .app-tabbar__item {
    flex: 1;
    min-width: 0;
  }

  .app-tabbar__item-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10rpx;
    min-height: 100rpx;
    border-radius: 20rpx;
    color: #8b95a7;
    transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
  }

  .app-tabbar__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
  }

  .app-tabbar__label {
    font-size: 22rpx;
    font-weight: 600;
    line-height: 1.2;
  }

  .app-tabbar__item.is-active .app-tabbar__item-inner {
    color: #a67939;
  }
}
</style>