AppTabbar.vue 2.95 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">
              <i
                class="app-tabbar__icon-font"
                :class="getIconClass(item)"
              ></i>
            </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 { useTabbarStore } from '@/stores/tabbar'

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

const activeColor = '#a67939'
const inactiveColor = '#8b95a7'
const defaultIcon = 'fa-circle-o'

const tabbarStore = useTabbarStore()
const tabItems = computed(() => tabbarStore.visibleTabItems)

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

const getIconClass = (item) => {
  const icon = String(item?.class || item?.icon || defaultIcon).trim() || defaultIcon
  const fontClass = icon.startsWith('fa-') ? 'fa' : 'iconfont'
  return [fontClass, icon]
}

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;
    color: #8b95a7;
    font-size: 38rpx;
  }

  .app-tabbar__icon-font {
    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;
  }

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