前端框架搭建——从零到一搭建一个高颜值Vue3后台管理系统(三)

上一篇我们集成项目所需要的插件,在整个main.ts里的结构都抽离到了各自相应的文件中,使整体main的结构看起来更加简介明了。今天我们讲下项目页面的布局

布局分析

前端框架搭建——从零到一搭建一个高颜值Vue3后台管理系统(三)_第1张图片

分析我们页面的布局,主要是比较常见的左右结构布局,这在后台管理系统中是很常见的布局。当然也有其他的布局,在我们这次的项目里没有其它类型,但你可以自己添加,这里提供一种思路

  • 首先在页面提供component这个动态组件,我们设置不同类型的布局或者Json搜索项都可以采用这种方式
  • 其次将需要的页面布局模式引入到当前页面,并通过对象模式引用
  • 通过结合pinia的store改变layout的属性值
<template>
  <component :is="components[layout]" />
template>

<script setup lang="ts">
import { computed } from "vue";
import { useThemeStore } from "@/stores/modules/theme";
import mode1 from "xxx.vue"; // 布局模式1
import mode2 from "xxx.vue"; // 布局模式2
import mode2 from "xxx.vue"; // 布局模式3

const components = {
  mode1: mode1, 
  mode2: mode2,
  mode3: mode3,
};

const themeStore = useThemeStore();
const layout = computed(() => themeStore.layout);
script>

下面我们说下本项目具体的布局方式

<template>
  <el-container class="h-full" ref="appWrapperRef">
    <AppMask v-show="showAppMask" @click="closeAppMask" />
    <Asider />
    <el-container direction="vertical" class="relative">
      <Header />
      <NavTab />
      <Main />
      <AppSetting />
    el-container>
  el-container>
template>

Asider

Asider主要有两个组件,分别是Logo组件和Menu组件,Logo就放个文本和图片通过Store控制表现形式就可以了,主要讲下Menu组件

在Menu组件外部需要包裹一个el-scrollbar组件,通过这个组件可以让menu过多时产生滑动效果,里面内部放置一个menuItem组件,注意组件拆分,方便维护。

在menu组件中,我们主要把一些不太常用的属性功能交给全局Store去配置,对于默认激活项这里采用useRouter获取当前路由的path,点击事件使用select事件,可以通过回调拿到menuItem的index,这里我们通过一个正则判断/http(s)?:/.test(key)判断是否是外链链接,如果是通过window.open跳转,如果不是通过router.push跳转就行了

// menu



这里需要提醒一点,使用el-icon的setting图标,如果size设置大小为18,在浏览器下会出现卡段,所以这里我们直接设置size大小19避开它。这里的Icon是自己的封装组件,后面会详细介绍

// menuItem



NavTab

header比较简单我们就不说了,navTab组件有几种方式实现,可以使用el-scrollbar配置滑动自行实现,还有一种就是借助element的el-tabs

  • 初始化tab需要的tag

首先获取本地缓存的tab属性值,如果没有,从router获取路由传递给filterAffixTags方法进行过滤,拿到meta属性里affix

  • 添加、切换、删除tab都需要通过store里的方法进行调用
  • 在onMounted里调用initTabs & addTab
  • 通过watch检测route.fullPath,如果发生变化触发addTab,内部可以对已存在的tag进行判断,这样可以保证刷新页面或者切换菜单自动增加tab
// index.ts
// 初始化tabs
const router = useRouter();
const initTabs = () => {
  const routes = router.getRoutes();
  const tabs = storage.get('navTab') ?? filterAffixTags(routes);
  for (const tab of tabs) {
    navTabStore.add(tab);
  }
};
// 添加tab
const addTab = () => {
  const tab = {
    fullPath: route.fullPath,
    title: route.meta.title,
    affix: route.meta.affix || false,
    icon: route.meta.icon
  };
  navTabStore.add(tab);
};

// 切换tab
const handleChange = (fullPath: TabPaneName) => {
  router.push(fullPath as string);
  resetMenuStatus();
};

// 删除tab
const handleRemove = (fullPath: TabPaneName) => {
  navTabStore.closeCurrent(fullPath as string);
};

onMounted(() => {
  initTabs();
  addTab();
});

watch(
  () => route.fullPath,
  () => {
    addTab();
  }
);

// ./helper.ts
// 过滤固定页签
import { RouteRecordRaw } from 'vue-router';
export const filterAffixTags = (routes: RouteRecordRaw[]) => {
  const tags: App.TabsView[] = [];
  routes.forEach((route: RouteRecordRaw) => {
    if (route.meta?.affix) {
      tags.push({
        title: route.meta?.title,
        fullPath: route.path,
        icon: route.meta?.icon,
        affix: route.meta?.affix
      });
    }
  });
  return tags;
};

NavTab 右键菜单

  • 使用@contextmenu.prevent检测右键事件
  • 准备一个ul的列表,可以搭配transition在切换时做下动画
//右键事件
//打开ul列表显示,通过传入的affix和fullPath控制ul列表的显示隐藏和禁用状态,
调整ul的left和top属性防止溢出
const handleContextMenu = (e: any, fullPath: string, affix?: boolean) => {
  showFilterMenu(fullPath, affix);  //筛选显示的右键菜单
  const menuMinWidth = 105;
  const offsetLeft = navTabRef.value.getBoundingClientRect().left; // container margin left
  const offsetWidth = navTabRef.value.offsetWidth; // container width
  const maxLeft = offsetWidth - menuMinWidth; // left boundary
  const left = e.clientX - offsetLeft + 15; // 15: margin right
  
  contextmenuLeft.value = left > maxLeft ? maxLeft : left;
  contextmenuTop.value = e.clientY;
  contextmenuVisible.value = true;

};

Main

  • el-main配置一下背景颜色,我们项目的页面基础颜色全部采用它的颜色
  • 配置el-scrollbar
  • router-view使用配置缺口路由
  • 使用keep-alive缓存页面

对于缓存在vue里实现比react简单,但是如果路由是嵌套状态的情况下,配置根路由的缓存对子路由是不起作用的,我们需要在子路由下配置缓存状态,或者还有一种方法,可以结合beforeEach路由钩子在这里将路由缓存到store里

<template>
  <el-main class="mt-1px !p-0 bg-[var(--el-bg-color-page)]">
    <el-scrollbar>
      <div class="p-3 h-full">
        <router-view>
          <template #default="{ Component, route }">
            <el-backtop title="回到顶部" target=".el-main .el-scrollbar__wrap" />
            <transition :name="themeStore.animateMode" mode="out-in" appear>
              <keep-alive :include="routeStore.cacheList">
                <component :is="Component" :key="route.fullPath" v-if="appStore.reloadFlag" />
              keep-alive>
            transition>
          template>
        router-view>
      div>
    el-scrollbar>
  el-main>
template>

本文项目地址:Element-Admin

这样项目的布局结构就介绍完毕了,下一篇将介绍一下本项目的一些hooks封装
前端框架搭建——从零到一搭建一个高颜值Vue3后台管理系统(三)_第2张图片

你可能感兴趣的:(前端框架,前端,javascript)