Vue3+Vite+Ts プロジェクト戦闘 04 ビルド レイアウト レイアウト

レイアウト レイアウトコンテナ

<!-- src\layout\AppLayout.vue -->
<template>
  <el-container>
    <el-aside width="200px">
      Aside
    </el-aside>
    <el-container>
      <el-header>Header</el-header>
      <el-main>
        <!-- 子路由出口 -->
        <router-view />
      </el-main>
    </el-container>
  </el-container>
</template>

<script setup lang="ts"></script>

<style scoped lang="scss">
.el-container {
      
      
  height: 100vh;
}
.el-header {
      
      
  background-color: #B3C0D1;
}
.el-aside {
      
      
  width: auto;
  background-color: #304156;
}
.el-main {
      
      
  background-color: #E9EEF3;
}
</style>

// src\styles\common.scss
* {
    
    
  margin: 0;
  padding: 0;
}

// src\router\index.ts
import {
    
     createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import AppLayout from '@/layout/AppLayout.vue'

const routes:RouteRecordRaw[] = [
  {
    
    
    path: '/',
    component: AppLayout,
    children: [
      {
    
    
        path: '/',
        name: 'home',
        component: () => import('@/views/home/index.vue')
      }
    ]
  },
  {
    
    
    path: '/login',
    name: 'login',
    component: () => import('@/views/login/index.vue')
  }
]

const router = createRouter({
    
    
  // history: createWebHashHistory(), // hash 路由模式
  history: createWebHistory(), // history 路由模式
  routes // 路由规则
})

export default router

ページ ルーティング ナビゲーションを構成する

ルーティングディレクトリを初期化する

他のいくつかのページ ファイルを作成します (後で追加する可能性があります)。

└─ product # 商品相关
    ├─ attr # 商品规格
    │   └─ index.vue
    ├─ category # 商品分类
    │   └─ index.vue
    └─ list # 商品列表
        └─ index.vue

ルーティングを構成します。

// src\router\modules\products.ts
import {
    
     RouteRecordRaw, RouterView } from 'vue-router'

const routes:RouteRecordRaw = {
    
    
  path: 'product',
  component: RouterView,
  children: [
    {
    
    
      path: 'list',
      name: 'product_list',
      component: () => import('@/views/product/list/index.vue')
    },
    {
    
    
      path: 'category',
      name: 'product_category',
      component: () => import('@/views/product/category/index.vue')
    },
    {
    
    
      path: 'attr',
      name: 'product_attr',
      component: () => import('@/views/product/attr/index.vue')
    },
    {
    
    
      path: 'reply',
      name: 'product_reply',
      component: () => import('@/views/product/reply/index.vue')
    }
  ]
}

export default routes

// src\router\index.ts
import {
    
     createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import AppLayout from '@/layout/AppLayout.vue'
import productRoutes from './modules/product'

const routes:RouteRecordRaw[] = [
  {
    
    
    path: '/',
    component: AppLayout,
    children: [
      {
    
    
        path: '/',
        name: 'home',
        component: () => import('@/views/home/index.vue')
      },
      productRoutes
    ]
  },
  {
    
    
    path: '/login',
    name: 'login',
    component: () => import('@/views/login/index.vue')
  }
]

const router = createRouter({
    
    
  // history: createWebHashHistory(), // hash 路由模式
  history: createWebHistory(), // history 路由模式
  routes // 路由规则
})

export default router

メニューナビゲーション

いくつかのメニュー内容を一時的に静的に書き込みます。

<!-- src\layout\components\AppMenu.vue -->
<template>
  <el-menu
    active-text-color="#ffd04b"
    background-color="#304156"
    class="el-menu-vertical-demo"
    default-active="2"
    text-color="#fff"
    router
  >
    <el-menu-item index="/">
      <!-- <Menu> 首字母要大写,否则会和浏览器原生的 <menu> 冲突 -->
      <el-icon><Menu /></el-icon>
      <span>首页</span>
    </el-menu-item>
    <el-sub-menu index="1">
      <template #title>
        <el-icon><location /></el-icon>
        <span>商品</span>
      </template>
      <el-menu-item index="/product/list">
        <el-icon><Menu /></el-icon>
        <span>商品列表</span>
      </el-menu-item>
      <el-menu-item index="/product/category">
        <el-icon><Menu /></el-icon>
        <span>商品分类</span>
      </el-menu-item>
      <el-menu-item index="/product/attr">
        <el-icon><Menu /></el-icon>
        <span>商品规格</span>
      </el-menu-item>
    </el-sub-menu>
  </el-menu>
</template>

<script setup lang="ts"></script>

<style scoped></style>

<!-- src\layout\AppLayout.vue -->
<template>
  <el-container>
    <el-aside width="200px">
      <AppMenu />
    </el-aside>
    <el-container>
      <el-header>Header</el-header>
      <el-main>
        <!-- 子路由出口 -->
        <router-view />
      </el-main>
    </el-container>
  </el-container>
</template>

<script setup lang="ts">
import AppMenu from './AppMenu/index.vue'
</script>

<style scoped lang="scss">...</style>

サイドバーの展開/折りたたみの切り替え

サイドバーの展開状態を保存します。

// src\store\index.ts
import {
    
     defineStore } from 'pinia'

const useStore = defineStore('main', {
    
    
  state: () => ({
    
    
    count: 0,
    isCollapse: false
  }),
  getters: {
    
    
    doubleCount(state) {
    
    
      return state.count * 2
    }
  },
  actions: {
    
    
    increment() {
    
    
      this.count++
    }
  }
})

export default useStore

ヘッダー レイアウト コンポーネントを作成し、サイドバー コントロール ボタンを作成します。

<!-- src\layout\AppHeader\index.vue -->
<template>
  <ToggleSidebar />

  <!-- 面包屑 -->
</template>

<script setup lang="ts">
import ToggleSidebar from './ToggleSidebar.vue'
</script>

<style scoped lang="scss" >
i {
      
      
  font-size: 19px;
  cursor: pointer;
}
</style>

<!-- src\layout\AppHeader\ToggleSidebar.vue -->
<template>
  <el-icon>
    <component
      :is="store.isCollapse ? 'expand' : 'fold'"
      @click="handleCollapse"
    />
  </el-icon>
</template>

<script setup lang="ts">
import useStore from '@/store'

const store = useStore()

// 因为没有其他地方可以修改侧边栏状态
// 所以这里直接修改
const handleCollapse = () => {
      
      
  store.isCollapse = !store.isCollapse
}
</script>

<style scoped></style>

サイドバーのステータスをバインドし、Header コンポーネントをロードし、el-header スタイルを変更します。

<!-- src\layout\AppLayout.vue -->
<template>
  <el-container>
    <el-aside width="200px">
      <AppMenu />
    </el-aside>
    <el-container>
      <el-header>
        <AppHeader />
      </el-header>
      <el-main>
        <!-- 子路由出口 -->
        <router-view />
      </el-main>
    </el-container>
  </el-container>
</template>

<script setup lang="ts">
import AppMenu from './AppMenu/index.vue'
import AppHeader from './AppHeader/index.vue'
</script>

<style scoped lang="scss">
.el-container {
      
      
  height: 100vh;
}
.el-header {
      
      
  background-color: #fff;
  color: #333;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.el-aside {
      
      
  width: auto;
  background-color: #304156;
}
.el-main {
      
      
  background-color: #E9EEF3;
}
</style>

パン粉

ルーティング メタ情報を使用してルーティング ヘッダーを構成する

// src\router\modules\products.ts
import {
    
     RouteRecordRaw, RouterView } from 'vue-router'

const routes:RouteRecordRaw = {
    
    
  path: 'product',
  component: RouterView,
  meta: {
    
    
    title: '商品'
  },
  children: [
    {
    
    
      path: 'list',
      name: 'product_list',
      component: () => import('@/views/product/list/index.vue'),
      meta: {
    
    
        title: '商品列表'
      }
    },
    {
    
    
      path: 'category',
      name: 'product_category',
      component: () => import('@/views/product/category/index.vue'),
      meta: {
    
    
        title: '商品分类'
      }
    },
    {
    
    
      path: 'attr',
      name: 'product_attr',
      component: () => import('@/views/product/attr/index.vue'),
      meta: {
    
    
        title: '商品规格'
      }
    }
  ]
}

export default routes

// src\router\index.ts
...

const routes:RouteRecordRaw[] = [
  {
    
    
    path: '/',
    component: AppLayout,
    children: [
      {
    
    
        path: '/',
        name: 'home',
        component: () => import('@/views/home/index.vue'),
        meta: {
    
    
          title: '首页'
        }
      },
      ...
    ]
  },
  ...
]

...

ブレッドクラムコンポーネント

<!-- src\layout\AppHeader\Breadcrumb.vue -->
<template>
  <el-breadcrumb separator-icon="arrow-right">
    <el-breadcrumb-item
      v-for="item in routes"
      :key="item.path"
    >
      {
   
   { item.meta.title }}
    </el-breadcrumb-item>
  </el-breadcrumb>
</template>

<script setup lang="ts">
import {
      
       useRouter } from 'vue-router'
import {
      
       computed } from 'vue'

// 获取路由,类似 Vue2 的 this.$router
const router = useRouter()

// 获取当前路由的匹配记录
const routes = computed(() => {
      
      
  return router.currentRoute.value.matched.filter(item => item.meta.title)
})

</script>

<style scoped></style>

ブレッドクラムコンポーネントをロードする

<!-- src\layout\AppHeader\index.vue -->
<template>
  <el-space size="large">
    <ToggleSidebar />
    <Breadcrumb />
  </el-space>
</template>

<script setup lang="ts">
import ToggleSidebar from './ToggleSidebar.vue'
import Breadcrumb from './Breadcrumb.vue'
</script>

<style scoped lang="scss" >
i {
      
      
  font-size: 19px;
  cursor: pointer;
}
</style>

ディレクトリ内でのカスタム作成型宣言ファイルの作成を容易にするために、ルーティング メタ情報TypeScript サポートを構成します。src/types

// src\types\vue-router.d.ts
import 'vue-router'

declare module 'vue-router' {
    
    
  // eslint-disable-next-line no-unused-vars
  interface RouteMeta {
    
    
    title?: string
  }
}

他の

ページタイトルはnuxt/vue-meta (next ブランチ)を使用して設定できます

フルスクリーン機能

フルスクリーン API - Web API インターフェイス リファレンス | MDN

全画面ボタンコンポーネントを作成します。

<!-- src\layout\AppHeader\FullScreen.vue -->
<template>
  <el-icon><full-screen @click="toggleFullScreen" /></el-icon>
</template>

<script setup lang="ts">
const toggleFullScreen = () => {
      
      
  if (!document.fullscreenElement) {
      
      
    document.documentElement.requestFullscreen()
  } else {
      
      
    if (document.exitFullscreen) {
      
      
      document.exitFullscreen()
    }
  }
}
</script>

<style scoped></style>

負荷コンポーネント

<!-- src\layout\AppHeader\index.vue -->
<template>
  <el-space size="large">
    <ToggleSidebar />
    <Breadcrumb />
  </el-space>
  <el-space size="large">
    <FullScreen />
  </el-space>
</template>

<script setup lang="ts">
import ToggleSidebar from './ToggleSidebar.vue'
import Breadcrumb from './Breadcrumb.vue'
import FullScreen from './FullScreen.vue'
</script>

<style scoped lang="scss" >
i {
      
      
  font-size: 19px;
  cursor: pointer;
}
</style>

ページ読み込みの進行状況バー

nprogressを使用して、ページ読み込みの進行状況バー効果を実現します。

npm i --save nprogress
# TS 类型补充模块
npm i --save-dev @types/nprogress
// src\router\index.ts
...
import nprogress from 'nprogress'
import 'nprogress/nprogress.css'

// 关闭 loading 图标
nprogress.configure({
    
     showSpinner: false })

...

router.beforeEach(() => {
    
    
  // 开始加载进度条
  nprogress.start()
})

router.afterEach(() => {
    
    
  // 结束加载进度条
  nprogress.done()
})

export default router

注: Vue Router v4.x 以降、next()次のナビゲーション ガードを呼び出すためにナビゲーション ガードでこれを使用することは推奨されません。代わりにreturn制御に使用します。return はfalse現在のナビゲーションをキャンセルします。ルーティング アドレスを返すと、これにジャンプします。ルート; デフォルトのルートが呼び出します。

おすすめ

転載: blog.csdn.net/u012961419/article/details/124300171