Move sidebar nav entirely into App.vue (eliminate component boundary)

Previous attempts used a separate NavBar component (nav-bar.vue).
Even with styles in App.vue's global scope, the component wrapper
created an abstraction layer that prevented the nav from rendering.

Now the nav markup, logic, and styles are all in App.vue directly:
- <div class="app-nav"> renders inline in App.vue template
- .app-nav{display:none} on mobile, display:flex on desktop (>=1024px)
- No component boundary, no data-v-xxxxx scoping issues
- Deleted components/nav-bar.vue
This commit is contained in:
TradeMate Dev
2026-05-21 10:21:49 +08:00
parent d14c98e93c
commit 18c6cf5406
2 changed files with 60 additions and 68 deletions
+60 -13
View File
@@ -1,12 +1,59 @@
<template>
<div class="app-wrapper">
<NavBar />
<div class="app-nav" id="appNav">
<div class="app-nav-brand">TradeMate</div>
<div
v-for="(item, index) in navList"
:key="index"
class="app-nav-item"
:class="{ active: currentIndex === index }"
@click="switchTab(index)"
>
<span class="app-nav-icon">{{ item.icon }}</span>
<span class="app-nav-text">{{ item.text }}</span>
</div>
</div>
<router-view />
</div>
</template>
<script setup>
import NavBar from './components/nav-bar.vue'
import { ref, onMounted, onUnmounted } from 'vue'
const currentIndex = ref(0)
const navList = [
{ pagePath: '/pages/index/index', text: '首页', icon: '🏠' },
{ pagePath: '/pages/customers/customers', text: '客户', icon: '👥' },
{ pagePath: '/pages/marketing/marketing', text: '营销', icon: '📢' },
{ pagePath: '/pages/quotation/quotation', text: '报价', icon: '📄' },
{ pagePath: '/pages/profile/profile', text: '我的', icon: '👤' },
]
const updateCurrentIndex = () => {
const hash = window.location.hash || ''
for (let i = 0; i < navList.length; i++) {
if (hash.includes(navList[i].pagePath)) {
currentIndex.value = i
return
}
}
currentIndex.value = 0
}
const switchTab = (index) => {
if (currentIndex.value === index) return
uni.switchTab({ url: navList[index].pagePath })
}
onMounted(() => {
updateCurrentIndex()
window.addEventListener('hashchange', updateCurrentIndex)
})
onUnmounted(() => {
window.removeEventListener('hashchange', updateCurrentIndex)
})
</script>
<style>
@@ -31,15 +78,15 @@ uni-page-body {
min-height: 100% !important;
}
/* ===== NavBar — hidden on mobile (uni-app default tab bar is used) ===== */
.app-wrapper > .nav-bar {
/* ===== Nav: hidden on mobile (uni-app default tab bar is used) ===== */
.app-nav {
display: none;
}
/* ===== Desktop responsive (≥1024px) ===== */
@media (min-width: 1024px) {
/* NavBar sidebar */
.app-wrapper > .nav-bar {
/* Sidebar nav */
.app-nav {
display: flex;
flex-direction: column;
position: fixed;
@@ -54,7 +101,7 @@ uni-page-body {
padding-top: 24px;
}
.nav-brand {
.app-nav-brand {
font-size: 20px;
font-weight: 700;
color: #1890ff;
@@ -62,7 +109,7 @@ uni-page-body {
text-align: center;
}
.nav-item {
.app-nav-item {
display: flex;
flex-direction: row;
align-items: center;
@@ -74,26 +121,26 @@ uni-page-body {
transition: background 0.15s;
}
.nav-item:hover {
.app-nav-item:hover {
background: #f0f7ff;
}
.nav-item.active {
.app-nav-item.active {
background: #e6f0ff;
}
.nav-icon {
.app-nav-icon {
font-size: 22px;
line-height: 1;
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
}
.nav-text {
.app-nav-text {
font-size: 15px;
color: #555;
}
.nav-item.active .nav-text {
.app-nav-item.active .app-nav-text {
color: #1890ff;
font-weight: 600;
}