Vue3 + TS + Antd + Pinia 从零搭建后台系统(四) ant-design-vue Layout布局,导航栏,标签页

书接上回

本篇主要介绍: Layout布局,导航栏,标签页

继续填充

目录

  • 按需引入组件
  • Layout布局,导航栏,标签页
  • css样式

按需引入组件

使用unplugin-vue-components插件完成ant-design-vue组件的按需加载。
前文中已处理过,详情见前文
链接: Vue3 + TS + Antd + Pinia 从零搭建后台系统(一)
此处还需在tsconfig.json同级添加文件 components.d.ts。
tsconfig.json文件配置如下:

{
  "compilerOptions": {
    "target": "es2019",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "allowJs": true,
    "importHelpers": true,
    "moduleResolution": "node",
    "outDir": "temp",
    "resolveJsonModule": true,
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "paths": {
      "@/*": ["src/*"]
    },
    "types": [
      "@intlify/unplugin-vue-i18n/types",
      "vite/client",
      "element-plus/global",
      "@types/qrcode",
      "vite-plugin-svg-icons/client",
      "./components.d.ts"
    ],
    "baseUrl": "./",
    "lib": ["esnext", "dom", "dom.iterable", "scripthost"]
  },
  "include": [
    "src/types/**/*.ts",
    "src/types/**/*.tsx",
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx",
    "src/components/BpmnEditor/lib/ReadOnly/ReadOnly.js",
    "src/plugin/index.js"
  ],
  "exclude": ["dist", "node_modules"]
}

Layout布局,导航栏,标签页

这里统一写在layout文件夹下面
此处用到的图标为iconfont图标库中的图标,可替换为ant-design-vue中的图标

index.vue文件:


  <Layout style="height: 100%; width: 100%">
    <!-- 左侧菜单栏 -->
    <Layout-sider
      :class="collapsed ? 'side-logo' : 'side-title'"
      v-model:collapsed="collapsed"
      :trigger="null"
      collapsible
    >
      <div style="height: 50px">
        <img class="logo-style" src="../../assets/images/logo.png" />
        <div v-if="!collapsed" class="title-style">管理平台</div>
      </div>
      <Menu
        v-model:selectedKeys="selectedKeys"
        v-model:openKeys="openKeys"
        theme="dark"
        mode="inline"
        :inline-collapsed="collapsed"
      >
        <Sub-menu v-for="sub in menuList" :key="sub.path">
          <template #title>
            <span :class="sub.meta.icon" style="margin-right: 8px"></span>
            <span>{{ !collapsed ? sub.meta.title : "" }}</span>
          </template>
          <Menu-item v-for="item in sub.children" :key="item.path">
            <span :class="item.meta.icon"></span>
            <Router-link :to="`${sub.path}/${item.path}`">{{ item.meta.title }}</Router-link>
          </Menu-item>
        </Sub-menu>
      </Menu>
    </Layout-sider>
    <!-- 右侧面包屑、标签页、界面 -->
    <Layout>
      <Layout-header style="height: 75px">
        <div class="layHeader">
          <div
            :class="`trigger icon-new ${!collapsed ? 'icon-new-outdent' : 'icon-new-indent'}`"
            @click="() => (collapsed = !collapsed)"
          />
          <!-- separator=">" 设置层级间展示的符号 默认‘/’-->
          <Breadcrumb>
            <Breadcrumb-item v-for="item in breadcrumbList" :key="item.path">
              <span>{{ item.meta.title }}</span>
            </Breadcrumb-item>
          </Breadcrumb>
          <Dropdown>
            <Button class="icon-new icon-new-user loginOut" type="primary" ghost></Button>
            <template #overlay>
              <Menu>
                <Menu-item>{{ `用户名:${username}` }}</Menu-item>
                <Menu-item @click="loginOut">退出系统</Menu-item>
              </Menu>
            </template></Dropdown
          >
        </div>
        <div style="display: flex">
          <span class="icon-new icon-new-doubleleft spanIcon"></span>
          <Tabs
            v-model:activeKey="activeKey"
            hide-add
            type="editable-card"
            @edit="onEdit"
            @tabClick="onTabClick"
          >
            <Tab-pane v-for="item in tabPanes" :key="item.path" :tab="item.meta.title"> </Tab-pane>
          </Tabs>
          <div class="icon-new icon-new-doubleright spanIcon" @click="showAll"></div>
          <div
            class="icon-new icon-new-close-circle spanIcon"
            title="关闭其他"
            @click="closeOthers"
          ></div>
          <div class="icon-new icon-new-sync spanIcon" title="刷新" @click="reLoad"></div>
        </div>
      </Layout-header>
      <Layout-content class="content-style">
        <Router-view />
      </Layout-content>
    </Layout>
  </Layout>

index.ts文件:

import { defineComponent, reactive, toRefs, watch } from "vue";
import router from "@/router";
import { useUserStore } from "@/pinia/user";

export default defineComponent({
  name: "Layout",
  setup() {
    const datas = reactive({
      selectedKeys: ["BBB"],
      openKeys: ["/AAA"],
      collapsed: false,
      menuList: [] as any,
      breadcrumbList: [],
      activeKey: "",
      tabPanes: [] as any,
      username: null as any,
    });
    const methods = reactive({
      init() {
        datas.username = JSON.parse(localStorage.getItem("user") as any).userInfo?.username;
        datas.menuList = router.options.routes.filter((v: any) => !v.meta.hideInMenu);
        datas.selectedKeys = [router.currentRoute.value.name];
        datas.openKeys = [router.currentRoute.value.matched[0].path];
        datas.breadcrumbList = router.currentRoute.value.matched;
      },
      onEdit(val: any) {
        let lastIndex = 0;
        if (datas.tabPanes.length > 1) {
          datas.tabPanes.forEach((item: any, i: number) => {
            if (item.path == val) {
              lastIndex = i - 1;
            }
          });
          datas.tabPanes = datas.tabPanes.filter((v: { path: any }) => v.path !== val);
          if (datas.tabPanes.length && datas.activeKey == val) {
            if (lastIndex >= 0) {
              datas.activeKey = datas.tabPanes[lastIndex].path;
              datas.selectedKeys = [datas.tabPanes[lastIndex].name];
              router.push(datas.tabPanes[lastIndex].path);
            } else {
              datas.activeKey = datas.tabPanes[0].path;
              datas.selectedKeys = [datas.tabPanes[0].name];
            }
          }
        }
      },
      onTabClick(val: any) {
        router.push(val);
      },
      loginOut() {
      	// 使用Pinia中定义的退出系统的方法
        const userStore = useUserStore();
        userStore.logoutConfirm();
      },
      showAll() {
        // 此方法用来使用dropdown展示所有标签页的列表
      },
      closeOthers() {
        datas.tabPanes = datas.tabPanes.filter((v: { path: any }) => v.path == datas.activeKey);
      },
      reLoad() {
        window.location.reload();
      },
    });
    methods.init();
    watch(
      () => router.currentRoute.value.matched,
      (val) => {
        // 路由变化时,更新面包屑及标签页
        datas.breadcrumbList = val;
        datas.activeKey = router.currentRoute.value.path;
        if (datas.tabPanes.findIndex((v: any) => v.path == router.currentRoute.value.path) == -1) {
          datas.tabPanes.push(router.currentRoute.value);
        }
      },
      { immediate: true }
    );
    return {
      ...toRefs(datas),
      ...toRefs(methods),
    };
  },
});

css样式

style文件夹下,创建index.css 、antd.css、 layout.css
index.css
@import './antd.css';

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}
a:hover {
  color: #535bf2;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
  min-width: 320px;
  min-height: 100vh;
}

h1 {
  font-size: 3.2em;
  line-height: 1.1;
}

.card {
  padding: 2em;
}

#app {
  margin: 0 auto;
}

.main-content {
  margin: 10px;
  height: 100%;
  overflow-y: auto;
}
layout.css 样式
.side-logo {
  min-width: 60px !important;
  flex: 0 0 60px !important;
}
.side-title {
  flex: 0 0 180px !important;
  min-width: 180px !important;
}
.logo-style {
  float: left;
  margin: 4px;
  width: 40px;
}
.title-style {
  color: white;
  font-size: 18px;
  line-height: 48px;
  float: left;
  margin: 4px;
  font-weight: 700;
}
.trigger {
  width: 32px;
  font-size: 18px;
  line-height: 40px;
  padding: 0px;
  cursor: pointer;
  transition: color 0.3s;
  margin: 0 6px;
}

.trigger:hover {
  color: #5487eae0 !important;
}
.content-style {
  margin: 8px;
  padding: 4px;
  background: #fff;
  min-height: 280px;
}
.loginOut {
  margin: 6px 0px 0;
  font-size: 15px;
  cursor: pointer;
  border-radius: 50% !important;
  padding: 0px 0px !important;
  width: 30px;
}
.loginOut:hover {
  color: #5487eae0 !important;
}
.fade-enter-active .fade-leave-active {
  transition: opacity 0.5s;
}
.layHeader {
  display: flex;
  height: 40px;
  border-bottom: 1px solid #05050530;
}
.spanIcon {
  width: 32px;
  height: 32px;
  border: 1px solid #05050530;
  padding: 8px 6px;
  opacity: 0.5;
  line-height: 14px;
}
.msg-style {
  background-color: #5487eae0;
}
.collased-menu-dropdown {
  transition: background 0.2s ease-in-out;
}

antd.css 调整组件库中的样式
.ant-btn {
  font-size: 14px !important;
  height: 28px !important;
  padding: 0px 10px !important;
  border-radius: 4px !important; 
  margin: 2px 8px 2px 0px;
}
.ant-btn::before {
  margin: 4px;
}
.ant-btn:hover { 
  opacity: 0.8;
}
.ant-form-inline .ant-form-item {
  margin: 0 8px 0 0 !important;
}
.ant-menu-dark .ant-menu-item-selected {
  border-radius: 4px !important; 
}
.ant-layout .ant-layout-header {
  padding-inline: 3px !important;
  background: #fff;
}
.ant-breadcrumb {
  padding: 8px 0 !important;
  font-size: 15px !important;
  width: calc(100% - 80px) !important;
  height: 50px;
}
.ant-tabs {
  width: calc(100% - 80px);
}
.ant-tabs-nav {
  height: 32px !important;
  margin-bottom: 8px !important;
}
.ant-tabs-nav .ant-tabs-nav-wrap {
  border-bottom: 1px solid #e6ebf3;
}
.ant-tabs-nav .ant-tabs-tab {
   border-top-right-radius: 4px !important;
   border-top-left-radius: 4px !important;
   padding: 8px 8px !important;
}
.ant-tabs .ant-tabs-tab-remove {
  margin: 2px -4px 0px 4px !important;
}
.ant-modal .ant-modal-header {
  margin-bottom: 20px;
}

项目效果图:
在这里插入图片描述

项目至此基本搭建结束。后续优化或添加功能,不定期更新。ヾ(•ω•`)o

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SSM的个人博客管理系统

有需要请加文章底部Q哦 可远程调试 基于SSM的个人博客管理系统 一 介绍 个人博客管理系统基于SSM框架开发&#xff0c;数据库mysql&#xff0c;前端layui。系统角色分为用户和管理员。(附带配套设计文档!) 技术栈&#xff1a;JavaSSM框架mysqllayuiIDEAMaventomcat8.5jdk1.8 …

彻底卸载Ubuntu双系统

操作系统 文章目录 操作系统前言一、把开机启动项设为默认Windows启动二、删除Ubuntu系统分区三、删除开机启动引导项 前言 我们卸载Ubuntu双系统&#xff0c;可能出于以下原因&#xff1a; 1、Ubuntu系统内核损坏无法正常进入 2、Ubuntu系统分配空间不足&#xff0c;直接扩区…

就因为没在大屏项目加全屏按钮,早上在地铁挨了领导一顿骂

“嗯嗯”&#xff0c;“嗯嗯”&#xff0c;“那产品也没说加呀”&#xff0c;“按F11不行吗&#xff1f;”&#xff0c;“嗯嗯”&#xff0c;“好的”。 早上在4号线上&#xff0c;我正坐在地铁里&#xff0c;边上站着的妹子&#xff0c;我看他背着双肩包&#xff0c;打着电话…

HeidiSQL导入与导出数据

HeidiSQL两种导入与导出数据的方法&#xff1a;整个库复制&#xff0c;和仅复制数据 一 整个库复制 1 选中需要导出的数据库(这里是MyDBdata)&#xff0c;点击导出为SQL脚本。 2 按照如图进行选择 3 选做&#xff1a;删除当前数据库【如果有】 -- 删除数据库 USE mysql; D…

白酒:中国的酒文化的传承与发扬

中国&#xff0c;一个拥有五千年文明史的国度&#xff0c;其深厚的文化底蕴孕育出了丰富多彩的酒文化。在这片广袤的土地上&#xff0c;酒不仅仅是一种产品&#xff0c;更是一种情感的寄托&#xff0c;一种文化的传承。云仓酒庄的豪迈白酒&#xff0c;正是这一文化脉络中的一颗…

图卷积网络(Graph Convolutional Network, GCN)

图卷积网络&#xff08;Graph Convolutional Network, GCN&#xff09;是一种用于处理图结构数据的深度学习模型。GCN编码器的核心思想是通过邻接节点的信息聚合来更新节点表示。 图的表示 一个图 G通常表示为 G(V,E)&#xff0c;其中&#xff1a; V 是节点集合&#xff0c;…

2024青海三支一扶报名流程图解❗

报考公告 1、招考人数&#xff1a; 1910 人 2、报名时间&#xff1a;6月20-6月25 3、笔试时间&#xff1a;7月6日 4、笔试内容&#xff1a;综合知识和能力素质测验 &#x1f534;线上报名流程图解 一、本次报名采用线上报名方式&#xff0c;考生需登录《青海省人事考试信息网》…

xss一些笔记

&#xff08;乱写的一些笔记&#xff09; innerHTML只防script像是img就不会防 innerText都防 上面代码执行避免用户交互 js也可以用’‘执行 例子 alert’1‘ document.location.hash // #号后的部分&#xff0c;包括#号 document.location.host // 域名…

广告权重及出价解析

由于算法一直在不断改变&#xff0c;所以公式只能作为参考&#xff0c;不过核心是不变的。一、广告权重及出价解析 链接质量分CTR分值**CR分值&#xff08;点击率*转化率&#xff09;广告质量分广告出价*链接质量分我们的出价下一名产品的广告质量评分/我们的链接质量分0.01 …

Building wheels for collected packages: mmcv, mmcv-full 卡住

安装 anime-face-detector 的时候遇到一个问题&#xff1a;Installation takes forever #1386&#xff1a;在构建mmcv-full时卡住&#xff0c;这里分享下解决方法&#xff08;安装 mmcv 同理&#xff0c;将下面命令中的 mmcv-full 替换成 mmcv&#xff09; 具体表现如下&#x…

“华数杯”全国大学生数学建模竞赛含金量如何?

“华数杯”全国大学生数学建模竞赛是由华中师范大学主办的一项全国性的大学生数学建模竞赛。该竞赛旨在提高大学生的数学建模能力和实践能力&#xff0c;增强大学生的创新意识和团队协作精神。 搜集一些评价&#xff0c;有人说该竞赛的含金量较高&#xff0c;但是也有一些人认…

什么是进程?

目录 进程 进程的特征, 概念 我们下面先简单介绍一下什么是进程 接下来看看一个程序的运行过程 进程的组成 进程的状态和转换 进程的状态 进程状态的转换 ​编辑 进程的组织方式 进程控制 如何实现进程控制 为什么进程控制的过程需要一气呵成? 进程控制的实现…

05-对混合app应用中的元素进行定位

本文介绍对于混合app应用中的元素如何进行定位。 一、app的类型 1&#xff09;Native App&#xff08;原生应用&#xff09; 原生应用是指利用Android、IOS平台官方的开发语言、开发类库、工具等进行开发的app应用&#xff0c;在应用性能和交互体验上应该是最好的。 通俗点来…

【论文精读】RayMVSNet

今天读的是发表在CVPR2022上的无监督MVS文章&#xff0c;作者来自于国防科大。 文章链接&#xff1a;RayMVSNet 项目地址&#xff1a;Github Abstract 作者希望直接优化每个camera ray上的深度值&#xff0c;所以提出这个RayMVSNet来学习1D implicit field的序列预测。使用了…

springboot+vue+mybatis酒店管理系统+PPT+论文+讲解+售后

基于Spring框架的小型宾馆旅客信息管理系统采用B/S结构、java开发语言、以及Mysql数据库等技术。系统主要分为管理员和用户二部分&#xff0c;管理员&#xff1a;首页、个人中心、用户管理、客房类型管理、客房信息管理、客房预订管理、入住登记管理、退房评价管理、系统管理&a…

基于Redis提高查询性能(保持数据一致性)

Redis实战篇 | Kyles Blog (cyborg2077.github.io) 目录 背景 商户查询缓存(根据ID查询&#xff09; 根据店铺类型查询&#xff08;List型&#xff09; 缓存更新策略&#xff08;保证数据一致性&#xff09; 案例&#xff08;利用缓存更新策略&#xff09; 背景 起初客户端…

2024-06-19,面试官问的问题

文章目录 1、采用minIO完成了图片存储&#xff0c;采用阿里云OSS服务器存储图片这两个功能面试官理解为重复&#xff0c;面试官又问minIO怎么同步到OSS&#xff1f;2、讲一下ThreadLocal&#xff1f;3、为什么用ThreadLocal存数据&#xff1f;4、redis有几种数据结构&#xff1…

Office--加载宏-CS上线

免责声明:本文仅做技术交流与学习... 目录 关于宏上线的格式: 操作流程: 其他office文本的上线格式一样: 关于宏上线的格式: doc宏病毒: 1-生成格式为dotm 只要点启用宏就上线 2-生成格式为()word 97-2003 .doc) 被杀 操作流…

13.1.资源清单的管理工具-helm

目录 一、helm的介绍 1.helm的价值概述 2.helm的关键名词 二、安装部署helm 1.解压安装包并设置全局命令 2.添加命令补全 三、使用helm部署服务管理 1.使用helm创建chart 1.1创建工作目录 1.2.helm创建chart 2.响应式创建名称空间 3.安装chart到名称空间 4.使用hel…

机器人运动学笔记

一、建模 参考资料&#xff1a;https://zhuanlan.zhihu.com/p/137960186 1、三维模型和连杆、关节定义 2、设置z轴 SDH和MDH会不一样&#xff0c;主要的区别在于SDH中坐标系在连杆末端&#xff0c;MDH中坐标系在连杆首端。虽然这里只是给出z轴&#xff0c;但是由于后面原点位…