index.vue 3.27 KB
<template>
  <el-container class="app-layout">
    <!-- 侧边栏 -->
    <el-aside :width="sidebarWidth" class="sidebar-container">
      <Sidebar />
    </el-aside>
    
    <el-container>
      <!-- 顶部导航栏 -->
      <el-header class="header-container">
        <Header />
      </el-header>
      
      <!-- 面包屑导航 -->
      <el-breadcrumb class="breadcrumb-container" separator="/">
        <el-breadcrumb-item
          v-for="item in breadcrumbList"
          :key="item.path"
          :to="item.path"
        >
          <el-icon v-if="item.icon" class="breadcrumb-icon">
            <component :is="item.icon" />
          </el-icon>
          {{ item.title }}
        </el-breadcrumb-item>
      </el-breadcrumb>
      
      <!-- 主内容区域 -->
      <el-main class="main-container">
        <router-view v-slot="{ Component }">
          <transition name="fade-transform" mode="out-in">
            <component :is="Component" />
          </transition>
        </router-view>
      </el-main>
    </el-container>
  </el-container>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { useAppStore } from '@/stores/modules/app'
import Sidebar from '@/components/layout/Sidebar.vue'
import Header from '@/components/layout/Header.vue'
import type { BreadcrumbItem } from '@/types'

const route = useRoute()
const appStore = useAppStore()

// 侧边栏宽度
const sidebarWidth = computed(() => {
  return appStore.sidebarCollapsed ? '64px' : '210px'
})

// 面包屑导航列表
const breadcrumbList = computed<BreadcrumbItem[]>(() => {
  const matched = route.matched.filter(item => item.meta && item.meta.title)
  const breadcrumbs: BreadcrumbItem[] = []
  
  // 添加首页
  breadcrumbs.push({
    title: '首页',
    path: '/dashboard'
  })
  
  // 添加当前路由的面包屑
  matched.forEach(item => {
    if (item.meta?.title && item.meta.title !== '首页') {
      breadcrumbs.push({
        title: item.meta.title as string,
        path: item.path,
        icon: item.meta.icon as string
      })
    }
  })
  
  return breadcrumbs
})
</script>

<style lang="scss" scoped>
.app-layout {
  height: 100vh;
  
  .sidebar-container {
    background-color: var(--el-bg-color);
    border-right: 1px solid var(--el-border-color-lighter);
    transition: width 0.3s ease;
  }
  
  .header-container {
    background-color: var(--el-bg-color);
    border-bottom: 1px solid var(--el-border-color-lighter);
    padding: 0 var(--el-spacing-base);
    height: var(--header-height) !important;
    line-height: var(--header-height);
  }
  
  .breadcrumb-container {
    height: var(--breadcrumb-height);
    line-height: var(--breadcrumb-height);
    padding: 0 var(--el-spacing-base);
    background-color: var(--el-bg-color);
    border-bottom: 1px solid var(--el-border-color-lighter);
    
    .breadcrumb-icon {
      margin-right: 4px;
    }
  }
  
  .main-container {
    background-color: var(--el-bg-color-page);
    padding: 0;
    overflow-y: auto;
  }
}

// 页面切换动画
.fade-transform-enter-active,
.fade-transform-leave-active {
  transition: all 0.3s ease;
}

.fade-transform-enter-from {
  opacity: 0;
  transform: translateX(30px);
}

.fade-transform-leave-to {
  opacity: 0;
  transform: translateX(-30px);
}
</style>