JlsBottomNav.vue 3.7 KB
<template>
  <div v-if="isMiniProgramWebView" class="bottom-nav jls-bottom-nav">
    <div
      v-for="item in navItems"
      :key="item.path"
      @click="navigate(item.path)"
      :class="['nav-item', isActive(item.name) ? 'nav-item-active' : 'nav-item-inactive']"
    >
      <div class="nav-item-inner">
        <div class="nav-icon">
          <component :is="item.icon" size="18" :color="getIconColor(item.name)" />
        </div>
        <span class="nav-label">{{ item.label }}</span>
      </div>
    </div>
  </div>
</template>

<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import HomeIcon from '@nutui/icons-vue/dist/es/icons/Home.js';
import MessageIcon from '@nutui/icons-vue/dist/es/icons/Message.js';
import MyIcon from '@nutui/icons-vue/dist/es/icons/My.js';
import wx from 'weixin-js-sdk';

const ACTIVE_COLOR = '#a67939';
const INACTIVE_COLOR = '#8b95a7';

const route = useRoute();

const isMiniProgramWebView = computed(() => {
  return navigator.userAgent.includes('miniProgram');
});

const navItems = [
  { name: 'home', path: '/pages/index/index', icon: HomeIcon, label: '首页' },
  { name: 'message', path: '/pages/message/index', icon: MessageIcon, label: '消息' },
  { name: 'mine', path: '/pages/mine/index', icon: MyIcon, label: '我的' },
];

const reservedActiveTab = computed(() => {
  const rawTab = Array.isArray(route.query.activeTab) ? route.query.activeTab[0] : route.query.activeTab;
  return String(rawTab || '').trim().toLowerCase();
});

const isActive = () => {
  // 地图页目前不属于 JLS tab 列表中的任何一项。
  // activeTab 参数先预留,待业务确认后再决定是否映射到某个 tab 并启用高亮。
  void reservedActiveTab.value;
  return false;
};

const getIconColor = (name) => {
  return isActive(name) ? ACTIVE_COLOR : INACTIVE_COLOR;
};

const navigate = (path) => {
  if (!path) {
    return;
  }

  if (wx?.miniProgram?.navigateTo) {
    wx.miniProgram.navigateTo({
      url: path,
      fail: () => {
        wx?.miniProgram?.reLaunch?.({ url: path });
      },
    });
    return;
  }

  wx?.miniProgram?.reLaunch?.({ url: path });
};
</script>

<style scoped>
.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: stretch;
  justify-content: space-around;
  box-sizing: border-box;
  padding: 8px 12px;
  z-index: 50;
  min-height: 66px;
  background: rgba(255, 255, 255, 0.98);
  border-top: 1px solid rgba(166, 121, 57, 0.12);
  backdrop-filter: blur(6px);
}

.nav-item {
  display: flex;
  flex: 1;
  min-width: 0;
  align-items: stretch;
  justify-content: center;
  padding: 0;
  position: relative;
  cursor: pointer;
  transition: all 0.2s ease;
}

.nav-item-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 50px;
  gap: 5px;
  border-radius: 10px;
  color: #8b95a7;
  transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
}

.nav-item-active {
  color: #a67939;
}

.nav-item-inactive {
  color: #8b95a7;
}

.nav-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  line-height: 1;
  transition: transform 0.2s ease;
}

.nav-label {
  font-size: 11px;
  margin-top: 0;
  line-height: 1.2;
  font-weight: 600;
}

.jls-bottom-nav .nav-item-active .nav-item-inner {
  color: #a67939;
}

@media screen and (min-width: 768px) {
  .bottom-nav {
    min-height: 72px;
    padding: 8px 16px;
  }

  .nav-item-inner {
    min-height: 56px;
    gap: 6px;
  }

  .nav-icon {
    width: 20px;
    height: 20px;
  }

  .nav-label {
    font-size: 12px;
  }
}

@media (hover: hover) {
  .nav-item:hover .nav-item-inner {
    transform: translateY(-1px);
  }
}
</style>