vue web端页面组件展示

95 阅读3分钟

有一部分页面没有左侧菜单,顶部导航几乎所有页面都有,特殊的搜索页面,登录页是独立的,

一、最终实现思路(非常重要)

  1. 登录页、搜索页:独立页面,无任何公共布局(头 / 左 / 底全没有)
  2. 首页 home:走 Layout 布局 → 只有顶部导航 + 内容 + 底部,【隐藏左侧菜单】免登录
  3. 栏目 A (menuA):走 Layout → 顶 + 左侧菜单 + 内容 + 底部,免登录
  4. noSidePage:走 Layout → 顶 + 内容 + 底部,无左侧菜单,免登录
  5. Admin/OrderManage:走 Layout → 顶 + 左侧菜单 + 内容 + 底部,【必须登录】

控制规则统一用路由 meta:isHideSide:true = 隐藏左侧菜单requireAuth:true = 需要登录验证

一、目录结构不变

plaintext

src
├── App.vue
├── router/index.js
├── views
    ├── Login/index.vue        # 独立页面
    ├── Search/index.vue       # 独立页面
    ├── Layout/
    │     ├── index.vue
    │     └── components/Header.vue、Sidebar.vue、Footer.vue
    ├── Home/index.vue         # 首页:Layout+无左侧+免登
    ├── MenuA/index.vue        # 栏目A:Layout+有左侧+免登
    ├── NoSidePage/index.vue   # 普通无侧边:Layout+无左侧+免登
    ├── Admin/index.vue        # 管理页:Layout+有左侧+需登录
    ├── OrderManage/index.vue  # 订单页:Layout+有左侧+需登录

1. App.vue(完全不变)

vue

<template>
  <router-view />
</template>

<script setup>
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html,body,#app{
  height:100%;
}
</style>

2. router/index.js【关键改动:首页加 isHideSide:true】

js

import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    redirect: '/home'
  },
  // 独立页面,不使用Layout
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/Login/index.vue')
  },
  {
    path: '/search',
    name: 'Search',
    component: () => import('@/views/Search/index.vue')
  },

  // 公共Layout容器
  {
    path: '/',
    component: () => import('@/views/Layout/index.vue'),
    children: [
      // ★首页:免登 + 隐藏左侧菜单 isHideSide:true
      {
        path: 'home',
        component: () => import('@/views/Home/index.vue'),
        meta: { title: '首页', isHideSide: true }
      },
      // 栏目A:免登 + 显示左侧菜单(无任何meta标记)
      {
        path: 'menuA',
        component: () => import('@/views/MenuA/index.vue'),
        meta: { title: '栏目A' }
      },
      // 普通无侧边页面:免登、无左侧
      {
        path: 'noSidePage',
        component: () => import('@/views/NoSidePage/index.vue'),
        meta: { title: '无侧边页面', isHideSide: true }
      },
      // 需要登录、显示左侧菜单
      {
        path: 'admin',
        component: () => import('@/views/Admin/index.vue'),
        meta: { title: '管理员后台', requireAuth: true }
      },
      {
        path: 'orderManage',
        component: () => import('@/views/OrderManage/index.vue'),
        meta: { title: '订单管理', requireAuth: true }
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

// 路由守卫
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token')
  // 已登录禁止进登录页,跳首页
  if(to.path === '/login' && token){
    next('/home')
    return
  }
  // 仅requireAuth页面校验登录
  if(to.meta.requireAuth){
    if(token) next()
    else next({path:'/login', query:{redirect:to.fullPath}})
  }else{
    next()
  }
})

export default router

3. Layout/index.vue(代码不变,依靠 meta 自动隐藏侧边)

vue

<template>
  <div class="layout-wrap">
    <LayoutHeader />
    <div class="layout-body">
      <!-- isHideSide=true 就不渲染左侧菜单 -->
      <LayoutSidebar v-if="!route.meta.isHideSide"/>
      <div class="layout-right" :class="{fullWidth: route.meta.isHideSide}">
        <div class="layout-content">
          <router-view/>
        </div>
        <LayoutFooter/>
      </div>
    </div>
  </div>
</template>

<script setup>
import LayoutHeader from './components/Header.vue'
import LayoutSidebar from './components/Sidebar.vue'
import LayoutFooter from './components/Footer.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
</script>

<style scoped>
.layout-wrap{
  height:100vh;
  display:flex;
  flex-direction:column;
}
.layout-body{
  flex:1;
  display:flex;
  overflow:hidden;
}
.layout-right{
  flex:1;
  display:flex;
  flex-direction:column;
  overflow:auto;
}
.layout-right.fullWidth{
  flex:1 1 100%;
}
.layout-content{
  flex:1;
  padding:15px;
}
</style>

3.1 Layout/components/Header.vue(顶部导航不变)

vue

<template>
  <header class="header">
    <div class="logo">网站顶部导航</div>
    <div class="search">
      <input v-model="key" placeholder="搜索" @keyup.enter="toSearch"/>
    </div>
    <div class="user">
      <span v-if="token">已登录</span>
      <button v-if="token" @click="logout">退出</button>
      <button v-else @click="$router.push('/login')">去登录</button>
    </div>
  </header>
</template>

<script setup>
import {ref,onMounted} from 'vue'
import {useRouter} from 'vue-router'
const router = useRouter()
const key = ref('')
const token = ref('')

onMounted(()=>{
  token.value = localStorage.getItem('token')
})

const toSearch = ()=>{
  router.push({path:'/search',query:{k:key.value}})
}
const logout = ()=>{
  localStorage.removeItem('token')
  router.push('/home')
}
</script>

<style scoped>
.header{
  height:60px;
  background:#409EFF;
  display:flex;
  align-items:center;
  padding:0 20px;
  color:#fff;
  justify-content:space-between;
}
input{padding:6px 10px;border-radius:4px;border:none;}
</style>

3.2 Layout/components/Sidebar.vue(左侧菜单,自动根据当前路由切换菜单)

vue

<template>
  <aside class="sidebar">
    <ul>
      <li v-for="item in menuList" :key="item.path">
        <router-link :to="item.path">{{item.name}}</router-link>
      </li>
    </ul>
  </aside>
</template>

<script setup>
import {ref,watch} from 'vue'
import {useRoute} from 'vue-router'
const route = useRoute()

const menuConfig = {
  home: [
    {path:'/home',name:'首页'},
    {path:'/menuA',name:'栏目A'}
  ],
  menuA: [
    {path:'/menuA',name:'栏目A详情'},
    {path:'/home',name:'返回首页'}
  ],
  admin: [
    {path:'/admin',name:'管理员首页'},
    {path:'/orderManage',name:'订单管理'}
  ],
  orderManage: [
    {path:'/orderManage',name:'订单列表'},
    {path:'/admin',name:'返回管理首页'}
  ]
}

const menuList = ref([])
function setMenu(path){
  if(path.includes('home')) menuList.value = menuConfig.home
  else if(path.includes('menuA')) menuList.value = menuConfig.menuA
  else if(path.includes('admin')) menuList.value = menuConfig.admin
  else if(path.includes('orderManage')) menuList.value = menuConfig.orderManage
  else menuList.value = []
}
setMenu(route.path)
watch(()=>route.path,val=>setMenu(val))
</script>

<style scoped>
.sidebar{
  width:220px;
  background:#304156;
  height:100%;
}
li{
  line-height:46px;
}
a{
  display:block;
  padding:0 20px;
  color:#bfcbd9;
  text-decoration:none;
}
a.router-link-active{
  background:#409EFF;
  color:#fff;
}
</style>

3.3 Layout/components/Footer.vue(底部不变)

vue

<template>
  <footer class="footer">
    ©2025 网站版权所有
  </footer>
</template>
<style scoped>
.footer{
  height:50px;
  line-height:50px;
  text-align:center;
  background:#f5f5f5;
  border-top:1px solid #eee;
}
</style>

4. 独立页面代码

4.1 Login/index.vue(独立无布局)

vue

<template>
  <div class="login-box">
    <div class="content">
      <h3>用户登录</h3>
      <input v-model="form.username" placeholder="账号">
      <input v-model="form.pwd" type="password" placeholder="密码">
      <button @click="loginBtn">登录</button>
    </div>
  </div>
</template>

<script setup>
import {reactive} from 'vue'
import {useRouter,useRoute} from 'vue-router'
const router = useRouter()
const route = useRoute()

const form = reactive({
  username:'',
  pwd:''
})

const loginBtn = ()=>{
  localStorage.setItem('token','user-token')
  const redirect = route.query.redirect || '/home'
  router.push(redirect)
}
</script>

<style scoped>
.login-box{
  height:100vh;
  background:#f5f7fa;
  display:flex;
  align-items:center;
  justify-content:center;
}
.content{
  width:360px;
  padding:30px;
  background:#fff;
}
input{
  width:100%;
  margin:8px 0;
  padding:10px;
  border:1px solid #ddd;
}
button{
  width:100%;
  padding:10px;
  background:#409EFF;
  color:#fff;
  border:none;
}
</style>

4.2 Search/index.vue(独立无布局)

vue

<template>
  <div class="search-page">
    <h2>搜索结果页(独立页面)</h2>
    <p>搜索关键词:{{$route.query.k}}</p>
    <button @click="$router.push('/home')">返回首页</button>
  </div>
</template>
<style>
.search-page{padding:30px;}
</style>

5. 各个业务页面内容

5.1 Home/index.vue(首页:头部 + 内容 + 底部,无左侧菜单|免登)

vue

<template>
  <div>
    <h2>【首页】只有顶部导航+底部,没有左侧菜单,免登录访问</h2>
  </div>
</template>

5.2 MenuA/index.vue(栏目 A:头 + 左菜单 + 内容 + 底|免登)

vue

<template>
  <div>
    <h2>栏目A页面:带左侧菜单,免登录</h2>
  </div>
</template>

5.3 NoSidePage/index.vue(无头左,头 + 内容 + 底|免登)

vue

<template>
  <div>
    <h2>自定义无侧边页面:头部+底部,无左侧菜单</h2>
  </div>
</template>

5.4 Admin/index.vue(带左侧菜单,需要登录)

vue

<template>
  <div>
    <h2>管理员后台:带左侧菜单,必须登录</h2>
  </div>
</template>

5.5 OrderManage/index.vue(带左侧菜单,需要登录)

vue

<template>
  <div>
    <h2>订单管理页面:带左侧菜单,必须登录</h2>
  </div>
</template>

页面分类汇总表

表格

页面布局来源左侧菜单登录权限meta 配置
Login、Search独立一级路由不用鉴权无 meta
Home(首页)Layout 布局❌隐藏免登录isHideSide:true
MenuALayout 布局✅显示免登录仅 title
NoSidePageLayout 布局❌隐藏免登录isHideSide:true
Admin、OrderManageLayout 布局✅显示必须登录requireAuth:true

访问效果:/home → 上下布局(头 + 内容 + 底)/menuA → 左菜单 + 右上内容 + 底/admin 未登录→自动跳登录页,登录后正常显示左侧菜单