hookehuyr

feat: 新增可复用底部导航栏组件并重构页面使用

- 新增 `TabBar.vue` 组件,统一底部导航栏样式与交互逻辑
- 在 `components.d.ts` 中注册 `TabBar` 和 `NutButton` 组件
- 重构首页、入职页、签单页,移除重复的导航栏代码,统一使用 `TabBar` 组件
- 替换首页自定义按钮为 NutUI 的 `nut-button` 组件,保持原有视觉样式
- 更新 CHANGELOG.md 记录相关变更
......@@ -8,11 +8,13 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
IndexNav: typeof import('./src/components/indexNav.vue')['default']
NutButton: typeof import('@nutui/nutui-taro')['Button']
Picker: typeof import('./src/components/time-picker-data/picker.vue')['default']
PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default']
QrCode: typeof import('./src/components/qrCode.vue')['default']
QrCodeSearch: typeof import('./src/components/qrCodeSearch.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
TabBar: typeof import('./src/components/TabBar.vue')['default']
}
}
......
......@@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file.
- 注册新页面路由至 `src/app.config.js`
- 新增 "签单相关" 页面 (`src/pages/signing`),复用 "入职相关" 页面布局
- 为 "签单相关" 页面配置自定义导航栏与渐变色背景样式
- 新增可复用的底部导航栏组件 (`src/components/TabBar.vue`),统一各页面的导航交互
### Changed
- 暂时禁用授权模式功能 (`ENABLE_AUTH_MODE = false`)
......@@ -35,6 +36,11 @@ All notable changes to this project will be documented in this file.
- 替换 "入职相关" 页面图标为 NutUI 标准图标库,提升加载性能与清晰度
- 优化 "入职相关" 与 "签单相关" 页面的视觉体验,引入渐变色背景系统(Header 及各板块标题)
- 修复 "入职相关" 页面首个板块与导航栏重叠的布局问题
- 优化底部导航栏样式,移除 Home Indicator (底部灰条) 以符合设计稿
- 重构 TabBar 布局,移除绝对定位与固定高度,改用 Flexbox + Padding 实现更自然的垂直居中与适配
- 增加底部导航栏 `active` 属性,支持不同页面高亮状态切换
- 重构首页、入职页、签单页,统一使用 `TabBar` 组件
- 替换首页 (`src/pages/index`) 自定义按钮为 NutUI `nut-button` 组件,并保留原有视觉样式
### Removed
- 删除项目所有离线功能相关逻辑
......
<!--
* @Date: 2026-01-29 20:33:23
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2026-01-29 20:42:52
* @FilePath: /manulife-weapp/src/components/TabBar.vue
* @Description: 文件描述
-->
<template>
<view class="fixed bottom-0 left-0 w-full bg-white shadow-[0_-4rpx_16rpx_rgba(0,0,0,0.05)] pb-[env(safe-area-inset-bottom)] z-50">
<view class="flex items-center justify-around py-[32rpx]">
<view
class="flex-1 flex flex-col items-center justify-center"
v-for="(item, index) in tabs"
:key="index"
@tap="handleTabClick(item)"
>
<component
:is="item.icon"
:class="[current === item.key ? 'text-blue-600' : 'text-gray-400']"
size="24"
/>
<text
class="text-[20rpx] mt-[8rpx]"
:class="[current === item.key ? 'text-blue-600' : 'text-gray-400']"
>{{ item.label }}</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { Home, Service, My } from '@nutui/icons-vue-taro'
import { useGo } from '@/hooks/useGo'
import Taro from '@tarojs/taro'
const props = defineProps({
current: {
type: String,
default: 'home'
}
})
const go = useGo()
const tabs = ref([
{
key: 'home',
label: '首页',
icon: Home,
path: '/pages/index/index'
},
{
key: 'ai',
label: 'AI答疑',
icon: Service,
path: '/pages/ai/index' // Placeholder path
},
{
key: 'me',
label: '我的',
icon: My,
path: '/pages/me/index' // Placeholder path
}
])
const handleTabClick = (item) => {
if (item.key === props.current) return
if (item.key === 'home') {
go(item.path)
} else if (item.key === 'me') {
// Check if 'me' page exists, otherwise show toast or just go
// For now, assuming me page might not exist or we just navigate
// go(item.path)
// Based on existing logic in index.vue:
// Taro.redirectTo({ url: '/pages/me/index' });
// But wait, standard tab bar usually uses switchTab or redirectTo.
// Since this is a custom tab bar, we use useGo (which uses navigateTo usually).
// Ideally for tab switching we might want reLaunch or redirectTo to avoid stack buildup.
// However, useGo defaults to navigateTo.
// Let's use reLaunch to simulate tab switching behavior if it's a "tab bar"
// Actually, let's look at index.vue logic:
// if (item.key === 'me') Taro.redirectTo({ url: '/pages/me/index' });
// if (item.key === 'ai') Taro.showToast({ title: '功能开发中', icon: 'none' });
// I will replicate this logic for now but make it more generic if possible.
// Since 'me' and 'ai' might not be fully implemented, I will stick to what I know.
// But 'onboarding' and 'signing' pages use this tab bar too.
// If we are on 'onboarding' (which is seemingly a sub-page but has a tab bar),
// clicking 'Home' should probably go back to Home.
Taro.reLaunch({ url: item.path })
} else if (item.key === 'ai') {
Taro.showToast({
title: '功能开发中',
icon: 'none'
})
}
}
</script>
......@@ -59,12 +59,8 @@
</view>
</view>
<view class="flex justify-between gap-[24rpx]">
<view class="flex-1 h-[64rpx] flex items-center justify-center border border-blue-600 rounded-[16rpx] bg-white">
<text class="text-blue-600 text-[26rpx]">产品资料</text>
</view>
<view class="flex-1 h-[64rpx] flex items-center justify-center bg-blue-600 rounded-[16rpx]">
<text class="text-white text-[26rpx]">计划书</text>
</view>
<nut-button plain color="#2563EB" class="flex-1 !h-[64rpx] !rounded-[16rpx] !text-[26rpx] !m-0 !border-blue-600">产品资料</nut-button>
<nut-button color="#2563EB" class="flex-1 !h-[64rpx] !rounded-[16rpx] !text-[26rpx] !m-0">计划书</nut-button>
</view>
</view>
......@@ -83,12 +79,8 @@
</view>
</view>
<view class="flex justify-between gap-[24rpx]">
<view class="flex-1 h-[64rpx] flex items-center justify-center border border-blue-600 rounded-[16rpx] bg-white">
<text class="text-blue-600 text-[26rpx]">产品资料</text>
</view>
<view class="flex-1 h-[64rpx] flex items-center justify-center bg-blue-600 rounded-[16rpx]">
<text class="text-white text-[26rpx]">计划书</text>
</view>
<nut-button plain color="#2563EB" class="flex-1 !h-[64rpx] !rounded-[16rpx] !text-[26rpx] !m-0 !border-blue-600">产品资料</nut-button>
<nut-button color="#2563EB" class="flex-1 !h-[64rpx] !rounded-[16rpx] !text-[26rpx] !m-0">计划书</nut-button>
</view>
</view>
</view>
......@@ -145,27 +137,7 @@
</view>
<!-- Bottom Tab Bar -->
<view class="fixed bottom-0 left-0 w-full bg-white shadow-[0_-4rpx_16rpx_rgba(0,0,0,0.05)] pb-safe z-50">
<view class="flex h-[110rpx] items-center">
<view
class="flex-1 flex flex-col items-center justify-center"
v-for="(item, index) in loopData1"
:key="index"
@tap="handleTabClick(item)"
>
<component
:is="item.icon"
:class="[item.key === 'home' ? 'text-blue-600' : 'text-gray-400']"
size="24"
/>
<text
class="text-[20rpx] mt-[8rpx]"
:class="[item.key === 'home' ? 'text-blue-600' : 'text-gray-400']"
>{{ item.lanhutext0 }}</text>
</view>
</view>
<view class="w-[268rpx] h-[10rpx] mx-auto bg-gray-200 rounded-full mb-[10rpx]" />
</view>
<TabBar current="home" />
</view>
</template>
......@@ -173,6 +145,7 @@
import { ref } from 'vue';
import Taro, { useShareAppMessage } from '@tarojs/taro';
import { useGo } from '@/hooks/useGo';
import TabBar from '@/components/TabBar.vue';
import {
Search,
RectRight,
......@@ -213,40 +186,9 @@ const loopData0 = ref([
},
]);
const loopData1 = ref([
{
icon: Home,
lanhutext0: '首页',
key: 'home'
},
{
icon: Service,
lanhutext0: 'AI答疑',
key: 'ai'
},
{
icon: My,
lanhutext0: '我的',
key: 'me'
},
]);
// Navigation
const go = useGo();
const handleTabClick = (item) => {
if (item.key === 'me') {
Taro.redirectTo({
url: '/pages/me/index'
});
} else if (item.key === 'ai') {
Taro.showToast({
title: '功能开发中',
icon: 'none'
});
}
};
useShareAppMessage(() => {
return {
title: '臻奇智荟圈',
......
......@@ -36,28 +36,14 @@
</div>
<!-- Tab Bar -->
<div class="fixed bottom-0 left-0 w-full bg-white border-t border-gray-100 pb-[env(safe-area-inset-bottom)] z-50">
<div class="flex justify-around items-center h-[110rpx]">
<div class="flex flex-col items-center justify-center w-1/3" @tap="switchTab('home')">
<Home class="text-gray-400" size="24" />
<span class="text-[#9ca3af] text-[24rpx] mt-[8rpx]">首页</span>
</div>
<div class="flex flex-col items-center justify-center w-1/3" @tap="switchTab('ai')">
<Service class="text-gray-400" size="24" />
<span class="text-[#9ca3af] text-[24rpx] mt-[8rpx]">AI答疑</span>
</div>
<div class="flex flex-col items-center justify-center w-1/3">
<My class="text-[#007aff]" size="24" />
<span class="text-[#007aff] text-[24rpx] mt-[8rpx]">我的</span>
</div>
</div>
</div>
<TabBar current="me" />
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useGo } from '@/hooks/useGo'
import TabBar from '@/components/TabBar.vue'
import {
Edit,
Find,
......@@ -68,10 +54,7 @@ import {
Star,
Top,
PlayCircleFill,
RectRight,
Home,
Service,
My
RectRight
} from '@nutui/icons-vue-taro'
const go = useGo()
......@@ -150,19 +133,6 @@ const handleItemClick = (item) => {
console.log('Clicked:', item.title)
// TODO: Navigate to respective page
}
/**
* Switch tab
* @param {string} tab - Tab key
*/
const switchTab = (tab) => {
if (tab === 'home') {
go('/pages/index/index')
} else if (tab === 'ai') {
// go('/pages/ai/index') // Assuming there is an AI page
console.log('Switch to AI tab')
}
}
</script>
<script>
......
......@@ -36,28 +36,14 @@
</div>
<!-- Tab Bar -->
<div class="fixed bottom-0 left-0 w-full bg-white border-t border-gray-100 pb-[env(safe-area-inset-bottom)] z-50">
<div class="flex justify-around items-center h-[110rpx]">
<div class="flex flex-col items-center justify-center w-1/3" @tap="switchTab('home')">
<Home class="text-gray-400" size="24" />
<span class="text-[#9ca3af] text-[24rpx] mt-[8rpx]">首页</span>
</div>
<div class="flex flex-col items-center justify-center w-1/3" @tap="switchTab('ai')">
<Service class="text-gray-400" size="24" />
<span class="text-[#9ca3af] text-[24rpx] mt-[8rpx]">AI答疑</span>
</div>
<div class="flex flex-col items-center justify-center w-1/3">
<My class="text-[#007aff]" size="24" />
<span class="text-[#007aff] text-[24rpx] mt-[8rpx]">我的</span>
</div>
</div>
</div>
<TabBar current="me" />
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useGo } from '@/hooks/useGo'
import TabBar from '@/components/TabBar.vue'
import {
Shop,
Category,
......@@ -70,10 +56,7 @@ import {
Clock,
Refresh,
Location,
RectRight,
Home,
Service,
My
RectRight
} from '@nutui/icons-vue-taro'
const go = useGo()
......@@ -174,19 +157,6 @@ const handleItemClick = (item) => {
console.log('Clicked:', item.title)
// TODO: Navigate to respective page
}
/**
* Switch tab
* @param {string} tab - Tab key
*/
const switchTab = (tab) => {
if (tab === 'home') {
go('/pages/index/index')
} else if (tab === 'ai') {
// go('/pages/ai/index') // Assuming there is an AI page
console.log('Switch to AI tab')
}
}
</script>
<script>
......