AppTabbar.vue 3.11 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.name"
          class="app-tabbar__item"
          :class="{ 'is-active': isActive(item.name) }"
          @tap="handleTabClick(item)"
        >
          <view class="app-tabbar__item-inner">
            <view class="app-tabbar__icon">
              <component
                :is="item.icon"
                size="18"
                :color="isActive(item.name) ? activeColor : inactiveColor"
              />
            </view>
            <text class="app-tabbar__label">{{ item.title }}</text>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup>
import Taro from '@tarojs/taro'
import { Home, Message, My } from '@nutui/icons-vue-taro'
// 迁移到独立 H5 项目时,别忘了把 miniProgram helper 一起带走。
import { isH5Env, navigateToMiniProgramPage } from '@/utils/miniProgram'

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

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

const tabItems = [
  {
    name: 'home',
    title: '首页',
    icon: Home,
    url: '/pages/index/index',
  },
  {
    name: 'message',
    title: '消息',
    icon: Message,
    url: '/pages/message/index',
  },
  {
    name: 'mine',
    title: '我的',
    icon: My,
    url: '/pages/mine/index',
  },
]

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

const handleTabClick = async (item) => {
  if (!item?.url || item.name === props.current) {
    return
  }

  if (isH5Env()) {
    try {
      const has_navigated = await navigateToMiniProgramPage(item.url)

      if (has_navigated) {
        return
      }
    } catch (error) {
      console.error('H5 跳转小程序页面失败:', error)
    }
  }

  Taro.redirectTo({ url: item.url })
}
</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>