前端框架搭建(九)搭建页面布局框架【vite】

news2024/11/16 19:30:24
## 1.创建目录
  • BasicLayout——全局布局

  • components——布局组件

    • GlobalContent:全局内容
    • GlobalHeader:全局头部页面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sS27aM4E-1672640417261)(assets/image-20230102123323-wfk5e4g.png)]

2.处理GlobalHeader

创建HeaderMenu——头部菜单

声明相关类型

typings目录下创建system.d.ts

  declare namespace App {
    /** 全局头部属性 */
    interface GlobalHeaderProps {
      /** 显示logo */
      showLogo: boolean;
      /** 显示头部菜单 */
      showHeaderMenu: boolean;
      /** 显示菜单折叠按钮 */
      showMenuCollapse: boolean;
    }
  
    /** 菜单项配置 */
    type GlobalMenuOption = import('naive-ui').MenuOption & {
      key: string;
      label: string;
      routeName: string;
      routePath: string;
      icon?: () => import('vue').VNodeChild;
      children?: GlobalMenuOption[];
    };
  
    /** 面包屑 */
    type GlobalBreadcrumb = import('naive-ui').DropdownOption & {
      key: string;
      label: string;
      disabled: boolean;
      routeName: string;
      hasChildren: boolean;
      children?: GlobalBreadcrumb[];
    };
  
    /** 多页签Tab的路由 */
    interface GlobalTabRoute
      extends Pick<import('vue-router').RouteLocationNormalizedLoaded, 'name' | 'fullPath' | 'meta'> {
      /** 滚动的位置 */
      scrollPosition: {
        left: number;
        top: number;
      };
    }
  
    interface MessageTab {
      /** tab的key */
      key: number;
      /** tab名称 */
      name: string;
      /** badge类型 */
      badgeProps?: import('naive-ui').BadgeProps;
      /** 消息数据 */
      list: MessageList[];
    }
  
    interface MessageList {
      /** 数据唯一值 */
      id: number;
      /** 头像 */
      avatar?: string;
      /** 消息icon */
      icon?: string;
      svgIcon?: string;
      /** 消息标题 */
      title: string;
      /** 消息发送时间 */
      date?: string;
      /** 消息是否已读 */
      isRead?: boolean;
      /** 消息描述 */
      description?: string;
      /** 标签名称 */
      tagTitle?: string;
      /** 标签props */
      tagProps?: import('naive-ui').TagProps;
    }
  }

配置路由相关状态管理

    import {  constRouter } from '@/router';
    import { defineStore } from 'pinia';
import { computed } from 'vue';
import { useRoute } from 'vue-router';

    interface RouteState {
        /** 菜单 */
        menus: App.GlobalMenuOption[];
    }

    export const useRouteStore = defineStore('route-store', {
        state:():RouteState => ({
            menus:[],
        }),
        actions:{
            transformRouteToMenu(){
                let menu:App.GlobalMenuOption[] = [] ;
                constRouter.forEach(route => {
                    const{name , path} = route
                    const menuItem : App.GlobalMenuOption = {
                        key:path,
                        label:String(name)
                    }
                    if(path != '/'){
                        menu.push(menuItem);
                    }
                })
                return menu
            },
            getRoute(){
                (this.menus as App.GlobalMenuOption[]) = this.transformRouteToMenu();
            },
            isLogin(){
                const route = useRoute();
                const isLogin = computed(() => route.fullPath === '/')
                return isLogin.value
            }
        }
    });

添加路由守卫

router->guard下创建dynamic.ts

import type { Router, RouteLocationNormalized, NavigationGuardNext } from 'vue-router';
import { useRouteStore } from '@/store';

/**
 * 动态路由
 */
export async function createDynamicRouteGuard(
  to: RouteLocationNormalized,
  _from: RouteLocationNormalized,
  next: NavigationGuardNext,
  router: Router
) {
  const route = useRouteStore();

    await route.getRoute();

    next()

  }

修改路由配置

修改路由守卫的简单next

import type { Router } from 'vue-router';
import { useTitle } from '@vueuse/core';
import { createDynamicRouteGuard } from './dynamic';

/**
 * 路由守卫函数
 * @param router - 路由实例
 */
export function createRouterGuard(router: Router) {
  router.beforeEach(async (to, from, next) => {
    // 开始 loadingBar
    window.$loadingBar?.start();
    // 页面跳转权限处理
    createDynamicRouteGuard(to, from, next, router)
  });
  router.afterEach(to => {
    // 设置document title
    useTitle(to.name);
    // 结束 loadingBar
    window.$loadingBar?.finish();
  });
}

创建HeaderMenu组件

<template>
  <div class="flex-1-hidden h-full px-10px">
    <n-scrollbar :x-scrollable="true" class="flex-1-hidden h-full" content-class="h-full">
      <div class="flex-y-center h-full">
        <n-menu
          :value="activeKey"
          mode="horizontal"
          :options="menus"
          @update:value="handleUpdateMenu"
        />
      </div>
    </n-scrollbar>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import type { MenuOption } from 'naive-ui';
import { useRouteStore } from '@/store';
import { useRouterPush } from '@/utils/composables/router';


const route = useRoute();
const routeStore = useRouteStore();
const { routerPush } = useRouterPush();

const menus = computed(() => routeStore.menus as App.GlobalMenuOption[]);
const activeKey = computed(() => route.name as string);

function handleUpdateMenu(_key: string, item: MenuOption) {
  const menuItem = item as App.GlobalMenuOption;
  routerPush(menuItem.key);
}
</script>

<style scoped>
:deep(.n-menu-item-content-header) {
  overflow: inherit !important;
}
</style>

创建index.ts导出

import HeaderMenu from './HeaderMenu.vue';

export{
    HeaderMenu,
} 

创建GlobalHeader

<template>
      <div class="flex-1-hidden flex-y-center h-full">
        <header-menu />
      </div>
  </template>
  
  <script setup lang="ts">
  import {
    HeaderMenu,
  } from './components';

  </script>
  <style scoped>
  .global-header {
    box-shadow: 0 1px 2px rgb(0 21 41 / 8%);
  }
  </style>
  

3.全局布局组件

BasicLayout目录下创建简单布局组件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GZsE4Spu-1672640417263)(assets/image-20230102135018-jxl8420.png)]

LayoutHeader

<template>
    <header class="job-collect_header" :style="style">
        <slot></slot>
    </header>
</template>

<script setup lang='ts'>
import { computed } from 'vue';

interface Props {
    /** 开启fixed布局 */
    fixed?: boolean,
    /** fixed布局的层级 */
    zIndex?: number,
    /** 最小宽度 */
    minWidth?: number,
    /** 高度 */
    height?: number,
    /** 左侧内边距 */
    paddingLeft?: number,
    /** 动画过渡时间 */
    transitionDuration?: number,
    /** 动画过渡函数 */
    transitionTimingFunction?: string,
}

const props = withDefaults(defineProps<Props>(), {
    fixed: true,
    zIndex: 1001,
    minWidth: 900,
    height: 56,
    paddingLeft: 0,
    transitionDuration: 300,
    transitionTimingFunction: 'ease-in-out',
})

const style = computed(() => {
    const {
        fixed,
        zIndex,
        minWidth,
        height,
        paddingLeft,
        transitionDuration,
        transitionTimingFunction,
    } = props
    const position = fixed ? 'fixed' : 'staic';
    return `position:${position};
    z-index:${zIndex};
    min-width:${minWidth}px;
    height:${height}px;
    padding-left:${paddingLeft}px;
    transition-duration:${transitionDuration};
    transition-timing-function:${transitionTimingFunction};`
})

</script>

<style scoped>
.job-collect_header {
    left: 0;
    top: 0;
    flex-shrink: 0;
    width: 100%;
    transition-property: padding-left;
}
</style>

LayoutMain

<template>
  <main class="job-collect_main" :style="style">
    <slot></slot>
  </main>
</template>

<script setup lang="ts">
import { computed } from "vue";

interface Props {
  /** 顶部内边距 与 头部高度一致 */
  paddingTop?: number;
  /** 底部内边距 */
  paddingBottom?: number;
  /** 左侧内边距 */
  paddingLeft?: number;
  /** 动画过渡时间 */
  transitionDuration?: number;
  /** 动画过渡函数 */
  transitionTimingFunction?: string;
}

const props = withDefaults(defineProps<Props>(), {
  paddingTop: 56,
  paddingBottom: 0,
  paddingLeft: 0,
  transitionDuration: 300,
  transitionTimingFunction: "ease-in-out",
});

const style = computed(() => {
  const {
    paddingTop,
    paddingBottom,
    paddingLeft,
    transitionDuration,
    transitionTimingFunction,
  } = props;

  return `padding-top:${paddingTop}px;
    padding-bottom:${paddingBottom}px;
    padding-left:${paddingLeft}px;
    transition-duration:${transitionDuration};
    transition-timing-function:${transitionTimingFunction}`;
});
</script>

<style scoped>
.job-collect_main {
  flex-grow: 1;
  width: 100%;
}
</style>

layout组件

仅针对login不显示menu

<template>
  <div class="job-collect" :style="{ minWidth: minWidth + 'px' }">
    <layout-header
      :padding-left="headerPaddingLeft"
      v-if="paddingTop"
    >
      <slot name="header"></slot>
    </layout-header>
  

    <layout-content :padding-left="mainPaddingLeft" :padding-top="paddingTop">
      <slot></slot>
    </layout-content>
  </div>
</template>

<script setup lang="ts">
import { useRouteStore } from "@/store";
import { computed } from "vue";
import { LayoutHeader, LayoutContent } from "./";

const route = useRouteStore()

const isLogin = computed(() => route.isLogin)

const paddingTop = computed(() => {
  if(isLogin.value()){
    return 0
  }
    return 56
})

interface Props {
  /** 最小宽度 */
  minWidth?: number;
  /** 头部偏移左侧高度 */
  headerPaddingLeft?:number,
  /** 左侧头部高度 */
  siderPaddingTop?:number
  /** main左侧偏移 */
  mainPaddingLeft?:number
  /** 侧边可见 */
  siderVisible?: boolean;
}

withDefaults(defineProps<Props>(), {
  minWidth: 900,
  headerPaddingLeft: 0,
  siderPaddingTop: 56,
  siderVisible: false,
});

</script>

<style scoped>
.job-collect {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}
</style>

全局布局组件

<template>
    <admin-layout>
      <template #header>
        <global-header />
      </template>
   
      <global-content />

    </admin-layout>
  </template>
  
  <script setup lang="ts">
  import AdminLayout from './components/layout.vue';
  import {
    GlobalContent,
    GlobalHeader,
  } from '../components';
</script>
  
  <style scoped></style>
  

测试

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/134148.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Canadian Coding Competition(CCC) 2021 Junior 题解

目录 Problem J1: Boiling Water Problem J2: Silent Auction Problem J3: Secret Instructions Problem J4: Arranging Books Problem J5/S2: Modern Art Problem J1: Boiling Water Problem Description At sea level, atmospheric pressure is 100 kPa and water begi…

自动挂载USB和TF卡

转自链接https://zhuanlan.zhihu.com/p/443745437 ①创建用于挂载U盘的目录 mkdir /mnt/usb –p②在/etc/udev/rules.d目录下添加用于检测U盘插入规则&#xff08;add&#xff09;&#xff0c;终端下执行以下命令创建第一个U盘插入规则。 vim /etc/udev/rules.d/11-add-usb.r…

【ROS】—— ROS通信机制——服务通信(三)

文章目录前言1. 服务通信理论模型2. 服务通信自定义srv2.1 定义srv文件2.2 编辑配置文件2.3 编译3. 服务通信自定义srv调用(C)3.1 vscode配置3.2 服务端3.3 客户端3.4 配置 CMakeLists.txt4. 服务通信自定义srv调用(python)4.1 vscode配置4.2 服务端4.3 客户端4.4 配置 CMakeLi…

将Android进行到底之内容提供者(ContentProvider)

文章目录前言一、ContentProvider是什么&#xff1f;二、使用示例1.为应用创建内容提供者2.使用内容提供者2.1 内容URI2.2 Uri参数解析2.2 使用内容URI操作数据3.ContentProvider妙用4 内容URI对应的MIME类型5.ContentProvider重点注意6 演示demo源码总结前言 随着现在的应用越…

java通过JDBC连接mysql8.0数据库,并对数据库进行操作

目录 一、JDBC简介 二、添加依赖 三、JDBC操作数据库的步骤 四、JDBC操作数据库——增删改查 (一)新增数据 (二)删除数据 (三)修改数据 (四)查询数据 (五)多表连接查询 一、JDBC简介 Java数据库连接&#xff0c;&#xff08;Java Database Connectivity&#xff0c;简…

C进阶:字符串相关函数及其模拟实现

目录 &#x1f431;&#x1f638;一.strlen &#x1f54a;️1.功能 &#x1f43f;️2.模拟实现 &#x1f42c;&#x1f40b;二.strcpy &#x1f432;1.功能 &#x1f916;2.注意事项 &#x1f47b;3.模拟实现 &#x1f431;&#x1f42f;三.strcat &#x1f984;1.功能…

i.MX8MP平台开发分享(IOMUX篇)- Linux注册PAD

专栏目录&#xff1a;专栏目录传送门 平台内核i.MX8MP5.15.71文章目录1. pinfunc.h2.pinctrl-imx8mp.c3.PAD信息注册这一篇开始我们深入Linux中的pinctl框架。1. pinfunc.h pinfunc.h中定义了所有的引脚&#xff0c;命名方式是MX8MP_IOMUXC___&#xff0c;例如下面的MX8MP_IO…

【JDBC】----------ServletContext和过滤器

分享第二十四篇励志语句 幸福是什么&#xff1f;幸福不一定是腰缠万贯、高官显禄、呼风唤雨。平凡人自有平凡人的幸福&#xff0c;只要你懂得怎样生活&#xff0c;只要你不放弃对美好生活的追求&#xff0c;你就不会被幸福抛弃。 一&#xff1a;ServletContext&#xff08;重要…

js对象篇

面向对象 对象 如果对象的属性键名不符合JS标识符命名规范&#xff0c;则这个键名必须用引号包裹 访问属性有2种方法&#xff0c;有点语法和方括号填写法&#xff0c;特别地&#xff0c;如果属性名不符合JS命名规范或者使用变量存储属性名&#xff0c;则必须使用方括号访问 属…

【王道操作系统】2.3.6 进程同步与互斥经典问题(生产者消费者问题、吸烟者问题、读者写者问题、哲学家进餐问题)

进程同步与互斥经典问题(生产者消费者问题、吸烟者问题、读者写者问题、哲学家进餐问题) 文章目录进程同步与互斥经典问题(生产者消费者问题、吸烟者问题、读者写者问题、哲学家进餐问题)1.生产者-消费者问题1.1 问题描述1.2 问题分析1.3 如何实现1.4 实现互斥的P操作一定在实现…

深化全面To C战略魏牌发布与用户共创大六座SUV蓝山

对魏牌而言&#xff0c;与用户共创不是吸引眼球的营销噱头&#xff0c;而是“直面用户需求&#xff0c;真实倾听用户意见”的有效途径。 2022年12月30日&#xff0c;第二十届广州国际汽车展览会&#xff08;以下简称“广州车展”&#xff09;正式启幕。魏牌以“品位蓝山 有咖有…

神经网络必备基础知识:卷积、池化、全连接(通道数问题、kernel与filter的概念)

文章目录卷积操作实际操作filter与kernel1x1的卷积层可视化的例子池化全连接卷积操作 这个不难理解。我们知道图像在计算机中是由一个个的像素组成的&#xff0c;可以用矩阵表示。 假设一个5x5的输入图像&#xff0c;我们定义一个3x3的矩阵&#xff08;其中的数值是随机生成的…

excel图表美化:设置标记样式让拆线图精巧有趣

折线图作为我们平时数据视图化非常常规的表现方式&#xff0c;想必大家已经司空见惯了。折线图很简单&#xff0c;每个人都会做&#xff0c;但是不同的人做出来的折线图却千差万别。大多数人的折线图都是直接插入默认折线图样式生成的&#xff0c;这样的折线图先不说有没有用心…

五、IDEA中创建Web项目

文章目录5.1 创建Web项目5.1.1 创建项目5.1.2 编写Servlet类5.2 手动部署项目5.3 自动部署项目5.3.1 IDEA集成Tomcat5.3.2 IDEA部署JavaWeb项目5.4 war包部署5.4.1 导出war包5.1 创建Web项目 5.1.1 创建项目 1、打开IDEA&#xff0c;单击“New Project”或者通过File–>ne…

Perl语言入门

一、简介 Perl语言是拉里.沃尔&#xff08;Larry Wall&#xff09;在1987年开发的一种编程语言&#xff0c;借鉴了C、sed、awk、shell脚本语言以及其他语言的特性&#xff0c;专门用于文本处理。 它可以在各种平台上运行&#xff0c;例如Windows&#xff0c;Mac OS和各种UNIX…

bean生命周期

1.Aware和InitializingBean接口 Aware 接口用于注入一些与容器相关信息&#xff0c;例如 BeanNameAware: 注入bean的名字BeanFactorAware&#xff1a; 注入beanFactor容器ApplicationContextAware&#xff1a; 注入applicationContext容器EmbeddedValueResolverAware: ${} 代码…

爬虫进阶一(基础一)

文章目录简介cookie爬取雪球热帖代理模拟登陆防盗链异步爬虫协程asyncioM3U8HLS爬取seleniumbilibili无头浏览器规避检测MySQLMongoDBRedis简介 这个系列分四部分 基础进阶Scrapy 框架逆向分析实战运用 先补充一些爬虫需要的基础知识和技能预热&#xff0c;爬取个简历模板网站…

浅谈Git

Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理,也是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。 版本控制 什么是版本控制&#xff1f; 版本控制是一种在开发的过程中用于管理我们对文…

如何评价唐卫国公李靖的战功、军事才能、政治才能?

link 一鞭直渡清河洛Research and no development已关注470 人赞同了该回答个人以为&#xff0c;在军事上&#xff0c;李靖是当之无愧的唐朝第一名将&#xff0c;他用兵如神&#xff0c;精于谋略&#xff0c;无论是在实际的军事指挥上&#xff0c;还是军事理论上&#xff0c;他…

Vue3 中computed计算属性的使用

目录前言&#xff1a;什么是计算属性选项式中的计算属性组合式中的计算属性计算属性和方法的区别&#xff1a;计算属性/计算方法注意事项&#xff1a;总结前言&#xff1a; 目标&#xff1a;现在vue3的使用越来越普遍了&#xff0c;vue3这方面的学习我们要赶上&#xff0c;今天…