Vue3+Ts项目(Naive UI组件)——创建有图标可伸缩的左边菜单栏

本文主要是介绍Vue3+Ts项目(Naive UI组件)——创建有图标可伸缩的左边菜单栏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

      • 安装、配置vue-router
        • 1、安装
        • 2、main.ts配置
        • 3、在App.vue中,渲染路由配置到的组件
      • 创建测试路径页面
        • 1、src\views\dashboard\index.vue
        • 2、src\views\dashboard\test.vue
        • 3、src\views\table\index.vue
      • 配置页面路由
        • 1、src\router\modules\dashboard.ts
        • 2、src\router\modules\index.ts (主要用于测试不想展示的菜单路径隐藏)
        • 3、src\router\modules\table.ts
        • 4、src\router\index.ts 启用 vue-router
        • 5、src\router\routes.ts 动态获取所有路由配置
        • 6、src\router\type.ts
      • 绘制有图标可伸缩的菜单栏(重要部分)
        • 1、src\layouts\BasicLayout.vue
        • 2、src\composables\useMenu.ts 主要是这个文件
      • 处理找不到@vicons/carbon报红以及菜单栏不是整个画面样式
        • 1、naive-ui 推荐使用 [xicons](https://xicons.org/#/) 作为图标库。
        • 2、菜单栏不是整个画面样式

前言:在我不懈的摸索中终于实现了。我主要是总结另一位博主的创建过程,就是他标题取的,感觉有被白嫖到(手摸手创建…),哈哈哈哈哈哈,后面会附上原博主地址。以及我个人的总结理解。
在这里插入图片描述

安装、配置vue-router

1、安装
npm install vue-router
2、main.ts配置
// routerimport { useRouter } from '@/router'useRouter(app)
// 要放在app.mount('#app')之前app.mount('#app')
3、在App.vue中,渲染路由配置到的组件
<script setup lang="ts">
</script><template><router-view></router-view>
</template><style scoped>
</style>

创建测试路径页面

后面都请无脑复制,因为我已经把原博主的过程都走了一遍。虽然他有提供项目git地址,但是很多地方不一样。(原文有提供处理没设置路径404页面)

在 src 目录下,新建 views 目录,用于存放页面文件(我们创建三个页面

1、src\views\dashboard\index.vue
<script setup lang="ts">
</script><template><div v-for="i of 10"><h3>Dashboard {{ i }}</h3><router-link to="/table">Go to Table</router-link></div>
</template><style scoped></style>
2、src\views\dashboard\test.vue
<script setup lang="ts">
</script><template><div>测试页面</div>
</template><style scoped></style>
3、src\views\table\index.vue
  <script setup lang="ts"></script><template><div><h3>Table</h3><router-link to="/dashboard">Go to Dashboard</router-link></div></template><style scoped></style>

配置页面路由

在 src 目录下,新建 router 文件夹,用于存放路由配置文件。首先在其下创建一个 modules 文件夹,区分各模块不同的路由配置文件(接下来我们要创建6个ts文件)页面会因为找不到一些依赖和文件而报红,咱先不管,后面再处理

1、src\router\modules\dashboard.ts
import type { RouteRecord } from '@/router/type'
import BasicLayout from "@/layouts/BasicLayout.vue"import { CalendarSettings } from '@vicons/carbon'const dashboardRoutes: RouteRecord[] = [{path: "/",name:'combination',component: BasicLayout,meta:{icon: CalendarSettings},children: [{path: "/dashboard",name: "dashboard",component: () => import("@/views/dashboard/index.vue"),},{path: "/test",name: "test",component: () => import("@/views/dashboard/test.vue"),},],},
]export default dashboardRoutes
2、src\router\modules\index.ts (主要用于测试不想展示的菜单路径隐藏)
import type { RouteRecord } from '@/router/type'const rootRoutes: RouteRecord[] = [{path: '/',name: 'home',redirect: '/dashboard',meta: {hidden: true},}
]export default rootRoutes
3、src\router\modules\table.ts
import type { RouteRecord } from '@/router/type'
import BasicLayout from "@/layouts/BasicLayout.vue";
import { CalendarTools } from '@vicons/carbon'  const tableRoutes: RouteRecord[] = [{path: "/",name:'tableCombination',component: BasicLayout,meta:{icon: CalendarTools},children: [{path: "/table",name: "table",component: () => import("@/views/table/index.vue"),},],},
]export default tableRoutes
4、src\router\index.ts 启用 vue-router
import {createWebHistory, createRouter} from "vue-router";
import type {App} from 'vue'
// 获取所有路由
import routes from './routes'const router = createRouter({routes,// 这里使用历史记录模式history: createWebHistory()
})export const useRouter = (app: App<Element>): void => {app.use(router)
}
5、src\router\routes.ts 动态获取所有路由配置
/*** 动态加载路由配置*/
import type { RouteRecordRaw } from "vue-router";const modules = import.meta.glob("./modules/**/*.ts", { eager: true });const routes = Object.keys(modules).reduce((routes: RouteRecordRaw[], key: string) => {// @ts-ignoreconst module = modules[key].defaultconsole.log('module===', module);if (Array.isArray(module)) {return [...routes, ...module]} else {return [...routes, ...module.routes]}}, [] as RouteRecordRaw[]
);
console.log('routes===>',routes);export default routes
6、src\router\type.ts
import type { RouteRecordRaw } from "vue-router"
import type { Component } from 'vue'interface RouteRecordMeta {hidden?: boolean,icon?: Component
}// @ts-expect-error
export interface RouteRecord extends Omit<RouteRecordRaw, 'meta'> {name?: string,meta?: RouteRecordMeta,children?: RouteRecord[]
}

绘制有图标可伸缩的菜单栏(重要部分)

这里需要创建一个菜单栏vue页面以及一个ts文件用来数据处理。我这个项目Naive UI组件是手动导入。所以会跟原文有些许不一样。原文没有可伸缩部分。

1、src\layouts\BasicLayout.vue
<script lang="ts">
import { useMenu } from "@/composables/useMenu";
import { ref ,defineComponent} from "vue";
import {NLayout, NLayoutSider, NScrollbar,NMenu, NCol, NSwitch} from 'naive-ui'
export default defineComponent({components: {NLayout,NLayoutSider,NScrollbar,NMenu,NCol,NSwitch,},setup(){let collapsed = ref(false)const { menuOptions, expandKeys, updateExpandKeys, currentMenu, updateValue } = useMenu();return{menuOptions,expandKeys,updateExpandKeys,currentMenu,updateValue,collapsed}}})
</script><template><!-- <n-switch v-model:value="collapsed" /> --><n-layout has-sider><n-layout-siderborderedcollapse-mode="width":width="240":collapsed-width="64":collapsed="collapsed"show-trigger@collapse="collapsed = true"@expand="collapsed = false":native-scrollbar="false"><n-scrollbar><n-menu:options="menuOptions":expanded-keys="expandKeys":on-update:expanded-keys="updateExpandKeys":value="currentMenu":on-update:value="updateValue"></n-menu></n-scrollbar></n-layout-sider><article flex-1 flex flex-col overflow-hidden><section flex-1 overflow-hidden bg="#f5f6fb"><router-view v-slot="{ Component, route }"><template v-if="Component"><component :is="Component" :key="route.path" /></template></router-view></section></article></n-layout>
</template><style scoped></style>
2、src\composables\useMenu.ts 主要是这个文件
import type { Ref,Component  } from "vue";
import { ref, watch, h } from "vue";
import type{ MenuOption } from "naive-ui";
import { NIcon } from "naive-ui";
import routes from "@/router/routes";
// import type { RouteRecordRaw } from "vue-router";
import type { RouteRecord } from '@/router/type'import { useRoute, RouterLink } from "vue-router";const renderIcon = (icon: Component) => {return () => h(NIcon, null, { default: () => h(icon) })
}export interface UserMenu {/*** 菜单选项*/menuOptions: Ref<MenuOption[]>;/*** 展开的子菜单标识符数组*/expandKeys: Ref<string[]>;/*** 更改子菜单标识符数组回调方法*/updateExpandKeys: (keys: string[]) => void;/*** 当前选中的菜单*/currentMenu: Ref<string>;/*** 修改选中菜单时的回调方法*/updateValue: (key: string) => void;
}/*** 判断路由是否只有一个子路由* @param route  路由* @returns  如果该路由只有一个子路由,则返回 true;否则返回 false*/
const isSingleChildren = (route: RouteRecord): boolean => {// return route?.children?.length === 1; //看需求需要一个children时是否展示上级name。false:展示父级(后期可以根据meta中字段判断某一菜单是否展示父级)return false;
};/*** 过滤路由配置中需要在菜单中隐藏的路由* @param routes 路由列表* @returns 路由列表*/
const filterHiddenRouter = (routes: RouteRecord[]): RouteRecord[] => {return routes.filter((item: RouteRecord) => {return !item.meta?.hidden;});
};/*** 将路由信息转换为菜单信息* @param route  路由信息* @returns   菜单信息*/
const getMenuOption = (route: RouteRecord[]): MenuOption | undefined => {const routeInfo = isSingleChildren(route) ? route.children[0] : route;const menuOption: MenuOption = {label: () => {if (routeInfo.children && Array.isArray(routeInfo.children)) {return routeInfo.name;} else {return h(RouterLink,{ to: { name: routeInfo.name } },{ default: () => routeInfo.name });}},key: routeInfo.name as string,icon: routeInfo.meta?.icon ? renderIcon(routeInfo.meta?.icon as Component) : undefined};if (routeInfo.children && routeInfo.children.length > 0) {menuOption.children = getMenuOptions(routeInfo.children);}return menuOption;
};const getMenuOptions = (routes: RouteRecord[]): MenuOption[] => {let menuOptions: MenuOption[] = [];filterHiddenRouter(routes).forEach((route: RouteRecord) => {// @ts-ignoreconst menuOption = getMenuOption(route);if (menuOption) {menuOptions.push(menuOption);}});return menuOptions;
};export function useMenu(): UserMenu {const menus: MenuOption[] = getMenuOptions(routes);/*** 菜单选项*/const menuOptions = ref(menus);/*** 展开的子菜单标识符数组*/const expandKeys: Ref<string[]> = ref<string[]>([]);/*** 当前菜单*/const currentMenu: Ref<string> = ref<string>("");const route = useRoute();/*** 监听路由变化*/watch(() => route.path,() => {routeChanged();},{ immediate: true });/*** 判断路由是否包含在菜单列表中** @param routeName 路由名称* @param menuList  菜单列表* @returns 如果包含则返回 true;否则返回 false*/function menuContains(routeName: string, menuList: MenuOption[]): boolean {for (let menu of menuList) {if (menu.key === routeName) {return true;}if (menu.children && menu.children.length > 0) {const childMenuContains = menuContains(routeName, menu.children);if (childMenuContains) {return true;}}}return false;}/*** 路由发生变化时的回调*/function routeChanged(): void {// 获取匹配到的路由列表const matched = route.matched;// 获取匹配到路由名称const matchedNames = matched.filter((it) => menuContains(it.name as string, menus)).map((it) => it.name as string);const matchLen = matchedNames.length;const matchExpandKeys = matchedNames.slice(0, matchLen - 1);const openKey = matchedNames[matchLen - 1];expandKeys.value = matchExpandKeys;currentMenu.value = openKey;}/*** 更改子菜单标识符数组回调方法*/function updateExpandKeys(keys: string[]): void {expandKeys.value = keys}/*** 选中的菜单发生改变*/function updateValue(key: string): void {currentMenu.value = key}return {menuOptions,expandKeys,updateExpandKeys,currentMenu,updateValue} as UserMenu
}

处理找不到@vicons/carbon报红以及菜单栏不是整个画面样式

1、naive-ui 推荐使用 xicons 作为图标库。

个人理解vicons是个图标库,你想使用谁的图标引入谁的

npm i -D @vicons/fluent
npm i -D @vicons/ionicons4
npm i -D @vicons/ionicons5
npm i -D @vicons/antd
npm i -D @vicons/material
npm i -D @vicons/fa 
npm i -D @vicons/tabler
npm i -D @vicons/carbon

在这里插入图片描述

2、菜单栏不是整个画面样式

我这边是简单处理:创建了一个css文件。import '@/styles/index.css'引入main.ts
我看很多推荐使用Tailwind CSS,我还需要再研究研究。

src\styles\index.css

html,
body {width: 100%;height: 100%;overflow: hidden;
}#app {width: 100%;height: 100%;
}.n-layout {height: 100%;width: 100%;
}

原地址:手摸手创建一个 Vue + Ts 项目(二) —— 实现一个左侧菜单栏

这篇关于Vue3+Ts项目(Naive UI组件)——创建有图标可伸缩的左边菜单栏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/493684

相关文章

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

如何在Spring Boot项目中集成MQTT协议

《如何在SpringBoot项目中集成MQTT协议》本文介绍在SpringBoot中集成MQTT的步骤,包括安装Broker、添加EclipsePaho依赖、配置连接参数、实现消息发布订阅、测试接口... 目录1. 准备工作2. 引入依赖3. 配置MQTT连接4. 创建MQTT配置类5. 实现消息发布与订阅

springboot项目打jar制作成镜像并指定配置文件位置方式

《springboot项目打jar制作成镜像并指定配置文件位置方式》:本文主要介绍springboot项目打jar制作成镜像并指定配置文件位置方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录一、上传jar到服务器二、编写dockerfile三、新建对应配置文件所存放的数据卷目录四、将配置文

python如何创建等差数列

《python如何创建等差数列》:本文主要介绍python如何创建等差数列的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录python创建等差数列例题运行代码回车输出结果总结python创建等差数列import numpy as np x=int(in

前端如何通过nginx访问本地端口

《前端如何通过nginx访问本地端口》:本文主要介绍前端如何通过nginx访问本地端口的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、nginx安装1、下载(1)下载地址(2)系统选择(3)版本选择2、安装部署(1)解压(2)配置文件修改(3)启动(4)

怎么用idea创建一个SpringBoot项目

《怎么用idea创建一个SpringBoot项目》本文介绍了在IDEA中创建SpringBoot项目的步骤,包括环境准备(JDK1.8+、Maven3.2.5+)、使用SpringInitializr... 目录如何在idea中创建一个SpringBoot项目环境准备1.1打开IDEA,点击New新建一个项

如何使用Maven创建web目录结构

《如何使用Maven创建web目录结构》:本文主要介绍如何使用Maven创建web目录结构的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录创建web工程第一步第二步第三步第四步第五步第六步第七步总结创建web工程第一步js通过Maven骨架创pytho

HTML中meta标签的常见使用案例(示例详解)

《HTML中meta标签的常见使用案例(示例详解)》HTMLmeta标签用于提供文档元数据,涵盖字符编码、SEO优化、社交媒体集成、移动设备适配、浏览器控制及安全隐私设置,优化页面显示与搜索引擎索引... 目录html中meta标签的常见使用案例一、基础功能二、搜索引擎优化(seo)三、社交媒体集成四、移动

HTML input 标签示例详解

《HTMLinput标签示例详解》input标签主要用于接收用户的输入,随type属性值的不同,变换其具体功能,本文通过实例图文并茂的形式给大家介绍HTMLinput标签,感兴趣的朋友一... 目录通用属性输入框单行文本输入框 text密码输入框 password数字输入框 number电子邮件输入编程框

HTML img标签和超链接标签详细介绍

《HTMLimg标签和超链接标签详细介绍》:本文主要介绍了HTML中img标签的使用,包括src属性(指定图片路径)、相对/绝对路径区别、alt替代文本、title提示、宽高控制及边框设置等,详细内容请阅读本文,希望能对你有所帮助... 目录img 标签src 属性alt 属性title 属性width/h