Vue3+Vite+TypeScript+Element Plus开发-17.Tags-组件构建

news2025/5/25 2:07:09

 系列文档目录

Vue3+Vite+TypeScript安装

Element Plus安装与配置

主页设计与router配置

静态菜单设计

Pinia引入

Header响应式菜单缩展

Mockjs引用与Axios封装

登录设计

登录成功跳转主页

多用户动态加载菜单

Pinia持久化

动态路由 -动态增加路由

动态路由-动态删除路由 

路由守卫-无路由跳转404

 路由守卫-未登录跳转登录界面

 登录退出

Tags-组件构建 

Tags-与菜单联动  


 文章目录

目录

 系列文档目录

 文章目录

         

前言

Tags组件构建

main界面调整

效果首现

 Tags存储初始化

Tags组件初始化调整

运行效果

后续


前言

         在上一章节中,详细探讨了登录与退出机制的构建。本章节将聚焦于 Tags 组件的开发,其核心目标是:当用户点击菜单项时,能够在   main   区域以 Tab 的形式动态呈现对应内容。


Tags组件构建

在   components   文件夹中新建   MainTagsCont.vue   文件

代码如下:

<template>
    <div class="tabs">
        <el-tab v-for="(item,index) in tabs"
            :key="index"
            :label="item.label"
            :name="item.name"
            :icon="item.icon"
            :path="item.path"
            :closable="item.name !== 'home'"
            :effect="route.name===item.name?'dark':'plain'"
            >
 {{ item.label }}
        </el-tab>
    </div>
</template>
<script lang="ts" setup>
import{ effect, ref } from "vue"
import { useRoute } from "vue-router";

 const tabs=ref([
    {path:"/home"
    ,name:"home"
    ,label:"首页"
    ,icon:"home"}
   
])
const route=useRoute()
</script>
<style scoped>
 .tabs{
    margin: 20px 0 0 20px;
}
.el-tab{
    margin-right: 10px;
}
</style>

main界面调整

        修改   main.vue   文件在   main.vue   文件中导入   MainTagsCont组件,并在   <el-header>   和   <el-main>   之间插入   <MainTagsCont/>   组件。

 <el-header>
          <MainHdrCont />
 </el-header>
 <MainTagsCont />
<el-main>
          <router-view></router-view>
</el-main>

调整后完整代码:

<template>
  <div class="common-layout">
    <el-container class="container-aside" >
      <el-aside  :width="ContAsideWidth">
        <MainAsideCont />       
      </el-aside>
      <el-container>
        <el-header>
          <MainHdrCont />
        </el-header>
        <MainTagsCont />
        <el-main>
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>
<script lang="ts" setup>
import { defineComponent, computed , onMounted} from 'vue';
import MainAsideCont from '@/components/MainAsideCont.vue';
import MainHdrCont from '@/components/MainHdrCont.vue'
import MainTagsCont from '@/components/MainTagsCont.vue';
import { useAllDataStore } from '@/stores';
import { ReloadData } from '@/stores';

const store = useAllDataStore();

const ContAsideWidth = computed(() => {
return store.isCollapse ? '60px' : '180px';
});

// 在组件挂载时调用 ReloadData
onMounted(() => {
 // ReloadData();
});
</script>

<style lang="less" scoped>
.common-layout {
  height: 100%; /* 设置整个布局的高度为 100%,确保布局占满整个视口 */
  width: 100%; /* 设置整个布局的宽度为 100%,确保布局占满整个视口 */
  .container-aside {
    height: 100%; /* 确保内部的 el-container 也占满整个父容器的高度 */
  }
  .el-header {
    background-color: #141515 ; /* 设置表头的背景色为深黑色 fff 141515*/
    color: #fff; /* 设置表头文字颜色为白色,以便在深色背景上更清晰 */
    display: flex; /* 使用 flex 布局,方便对齐内容 */
    align-items: center; /* 垂直居中对齐内容 */
    justify-content: center; /* 水平居中对齐内容 */
    font-size: 18px; /* 设置文字大小为 18px */
    font-weight: bold; /* 设置文字为加粗 */
  }
  .el-aside {
    background-color:rgba(242, 242, 242, 0.19); /* 设置侧边栏的背景色为浅灰色 */
    color: #333; /* 设置侧边栏文字颜色为深灰色 */
    display: flex; /* 使用 flex 布局,方便对齐内容 */    
    align-items: center; /* 垂直居中对齐内容 */
    justify-content: center; /* 水平居中对齐内容 */
    font-size: 16px; /* 设置文字大小为 16px */
    font-weight: normal; /* 设置文字为正常粗细 */
    height: 100%; /* 确保侧边栏高度占满 */
    
  }
  .el-main {
    background-color: #fff; /* 设置主内容区域的背景色为白色 */
    color: #333; /* 设置主内容区域文字颜色为深灰色 */
    padding: 20px; /* 添加内边距,使内容不紧贴边缘 */
    font-size: 14px; /* 设置文字大小为 14px */
  }
}
</style>

效果首现

登录后,会显示 Tags 标签组件首页

 Tags存储初始化

         在构建 Tags 组件 时,Tags 的初始数据在组件内部进行初始化。为了实现对 Tags 的动态调整,使用 Pinia 来管理 Tags 的状态,并通过 Pinia 的状态管理功能对 Tags 的初始数据进行统一管理和调整。

tabs:[
      {path:"/home"
      ,name:"home"
      ,label:"home"
      ,icon:"home"}
     
      ],


   // tabs
    setTabsData(tabs: any)
    {
      this.tabs = tabs
    },
    getTabsData(): [] {
      return this.tabs;
    },

 调整后完整语句:

// src/stores/index.ts

import { defineStore } from 'pinia';
import router from '../router';
import type { Component } from 'vue';
import { get } from 'http';



 
 

type Modules = Record<string, () => Promise<{ default: Component }>>;
 

// 定义公共 store
export const useAllDataStore = defineStore('useAllData', {
  // 定义状态
  state: () => ({
    isCollapse: false, // 定义初始状态
    username: '',
    token_key: '',
    menuData:[],
    tabs:[
      {path:"/home"
      ,name:"home"
      ,label:"home"
      ,icon:"home"}
     
      ],
  }),

  // 定义 actions
  actions: {
    // 设置用户名
    setUsername(username: string) {
      this.username = username;
    },

    // 获取用户名
    getUsername(): string {
      return this.username;
    },

    // 设置 token_key
    setTokenKey(token_key: string) {
      // sessionStorage.setItem('useAllData-session-store', JSON.stringify({ token_key: token_key, menuData: this.menuData}));
      this.token_key = token_key;
    },

    // 获取 token_key
    getTokenKey(): string {
      /*
      const sessionData = sessionStorage.getItem('useAllData-session-store');
      console.log(sessionData)
      if (sessionData) {
        const data = JSON.parse(sessionData);
        this.token_key = data.token_key;
        this.menuData = data.menuData;
      }
      else
      {
        this.token_key = ''
      }
      */
      return this.token_key;
    },
    // 设置菜单数据
    setMenuData(menuData: any){
      // sessionStorage.setItem('useAllData-session-store', JSON.stringify({ token_key: this.token_key, menuData: menuData}));
      addRouter(menuData)
      this.menuData = menuData
    },
    // 获取菜单数据
    getMenuData(): [] {
      return this.menuData;
    },
    // tabs
    setTabsData(tabs: any)
    {
      this.tabs = tabs
    },
    getTabsData(): [] {
      return this.tabs;
    },
     // 登出方法
     logout() {
      sessionStorage.removeItem('useAllData-store'); // 清除 sessionStorage 中的数据
      /*
      this.username = '';
      this.token_key = '';
      this.menuData = [];*/
       router.push({ name: 'login' }); // 重定向到登录页面
    },

  },
  persist: {
    enabled: true,
    key: 'useAllData-store',
    storage: sessionStorage, // // localStorage  sessionStorage
    paths: ['token_key'], // 指定持久化的字段
  },
  /*
  persist: {
    enabled: true,
    strategies: [
      {
        key: 'useAllData-store',
        storage: sessionStorage,// localStorage  sessionStorage
        paths: ['token_key','menuData'], // 指定需要持久化的字段
      },
       
    ],
  },
  */


});

function addRouter(menuData: any){
  const routerList=router.getRoutes()
  const modules: Modules = import.meta.glob('../views/**/*.vue') as Modules;
  const routerArr=[]

  menuData.forEach((item:any) => {
   // console.log(item) 
    if(item.children){
     item.children.forEach((child:any) => {
       
       const componentPath = `../${child.path}.vue`;
       
       const module = modules[componentPath];
  
       if (module) {
         /*
         module().then(({ default: component }) => {
           child.component = component;
         });
         */
         child.component = module;
         routerArr.push(child)
          
       }
     });
   }
   else
   {
     const componentPath = `../${item.path}.vue`;
     const module = modules[componentPath];
     if(module)
     {
       item.component = module;
       routerArr.push(item)
     }

   }
  });
  // 增加删除路由
  routerList.forEach((item:any) => {
    
    if (item.name === 'main' 
        || item.name === 'home' 
        || item.name === '404' 
        || item.name === 'login'
        || item.name === 'error'
        || item.name === 'undefined'
        || item.path === '/'
        || item.path === '/main') 
      return
    router.removeRoute(item.name)
  });
  
  routerArr.forEach((item:any) => {
 
   router.addRoute('main',
     {
     path: item.index,
     name: item.label,
     component: item.component,
     
   });
 
})
const routerListLast=router.getRoutes()
console.log(routerListLast)

}

export function ReloadData() {
  const store = useAllDataStore();
  const menuData = store.getMenuData();
  addRouter(menuData);

 

}

Tags组件初始化调整

Tags 的初始化内容主要通过 Pinia store 获取。

const store = useAllDataStore();

 const tabs=computed(() => store.getTabsData()); 

调整后语句:

<template>
    <div class="tabs">
        <el-tab v-for="(item,index) in tabs"
            :key="index"
            :label="item.label"
            :name="item.name"
            :icon="item.icon"
            :path="item.path"
            :closable="item.name !== 'home'"
            :effect="route.name===item.name?'dark':'plain'"
            >
 {{ item.label }}
        </el-tab>
    </div>
</template>
<script lang="ts" setup>
import{ effect, ref ,computed} from "vue"
import { useRoute } from "vue-router";
import { useAllDataStore } from '@/stores';
const store = useAllDataStore();

 const tabs=computed(() => store.getTabsData()); 
const route=useRoute()
</script>
<style scoped>
 .tabs{
    margin: 20px 0 0 20px;
}
.el-tab{
    margin-right: 10px;
}
</style>

运行效果


后续

        本章节已完成了 Tags 组件的初步构建。在下一章节中,我们将继续完善 Tags 组件,使其与菜单实现完整联动。

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

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

相关文章

3D语义地图中的全局路径规划!iPPD:基于3D语义地图的指令引导路径规划视觉语言导航

作者&#xff1a; Zehao Wang, Mingxiao Li, Minye Wu, Marie-Francine Moens, Tinne Tuytelaars 单位&#xff1a;鲁汶大学电气工程系&#xff0c;鲁汶大学计算机科学系 论文标题&#xff1a; Instruction-guided path planning with 3D semantic maps for vision-language …

ShellScript脚本编程

语法基础 脚本结构 我们先从这个小demo程序来窥探一下我们shell脚本的程序结构 #!/bin/bash# 注释信息echo_str"hello world"test(){echo $echo_str }test echo_str 首先我们可以通过文本编辑器(在这里我们使用linux自带文本编辑神器vim)&#xff0c;新建一个文件…

【HarmonyOS 5】敏感信息本地存储详解

【HarmonyOS 5】敏感信息本地存储详解 前言 鸿蒙其实自身已经通过多层次的安全机制&#xff0c;确保用户敏感信息本地存储安全。不过再此基础上&#xff0c;用户敏感信息一般三方应用还需要再进行加密存储。 本文章会从鸿蒙自身的安全机制进行展开&#xff0c;最后再说明本地…

探索鸿蒙沉浸式:打造无界交互体验

一、鸿蒙沉浸式简介 在鸿蒙系统中&#xff0c;沉浸式是一种极具特色的设计理念&#xff0c;它致力于让用户在使用应用时能够全身心投入到内容本身&#xff0c;而尽可能减少被系统界面元素的干扰。通常来说&#xff0c;就是将应用的内容区巧妙地延伸到状态栏和导航栏所在的界面…

网站301搬家后谷歌一直不收录新页面怎么办?

当网站因更换域名或架构调整启用301重定向后&#xff0c;许多站长发现谷歌迟迟不收录新页面&#xff0c;甚至流量大幅下滑。 例如&#xff0c;301跳转设置错误可能导致权重传递失效&#xff0c;而新站内容与原站高度重复则可能被谷歌判定为“低价值页面”。 即使技术层面无误&a…

在Mac上离线安装k3s

目录 首先是安装multipass。 1. 系统要求 2. 环境准备 本来想照着网上文档学习安装一下k3s&#xff0c;没想到在docker被封了之后&#xff0c;现在想通过命令行去下载github的资源也不行了&#xff08;如果有网友看到这个文档、并且知道问题原因的&#xff0c;请留言告知&am…

2025低代码平台选型策略:ROI导向下的功能与成本权衡

在当今快速变化的商业环境中&#xff0c;企业面临着前所未有的挑战与机遇。数字化转型已成为企业提升竞争力的关键&#xff0c;而软件开发的高成本和长周期无疑是实现这一转型的绊脚石。 低代码平台的兴起&#xff0c;为企业提供了一种高效、灵活的解决方案&#xff0c;使得非…

Redis的IO多路复用

1 传统的socket编码模型 传统 Socket 模型通常采用 多线程/多进程 或 阻塞 I/O 的方式处理网络请求。以下是典型实现步骤&#xff1a; 创建套接字&#xff08;Socket&#xff09; 步骤&#xff1a;调用 socket() 创建一个 TCP/UDP 套接字。通常把这个套接字称为【主动套接字】…

基于YOLOv9的课堂行为检测系统

基于YOLOv9的课堂行为检测系统 项目概述 本项目是一个基于YOLOv9深度学习模型的课堂行为检测系统&#xff0c;旨在通过计算机视觉技术自动识别和监测课堂中学生的各种行为状态&#xff0c;帮助教师更好地了解课堂教学效果。 项目结构 课堂行为检测/ ├── data/ │ ├──…

端、管、云一体化原生安全架构 告别外挂式防护!

面对数字化转型浪潮&#xff0c;企业网络安全风险日益凸显。数据泄露、黑客勒索等事件频发&#xff0c;合规要求加速推进。尽管企业纷纷部署了防病毒、身份认证、文件加密、入侵防护、流量监控等多种安全系统&#xff0c;但分散且孤立的架构非但没有有效抵御风险&#xff0c;反…

BI面向模型开发和面向报表开发,有什么区别?

在数字化时代&#xff0c;商业智能&#xff08;BI&#xff09;已成为企业决策不可或缺的工具。BI项目实施时&#xff0c;通常有两种开发模式&#xff1a;面向模型开发和面向报表开发。虽然两者都旨在通过数据驱动决策&#xff0c;但在开发逻辑、目标价值和技术路径上存在显著差…

进程控制(上)【Linux操作系统】

进程控制 写时拷贝 本质是一种减少深拷贝的方法 Linux中有很多拷贝的场景都用得上写时拷贝&#xff0c;下面以创建子进程时的写时拷贝为例&#xff1a; 子进程被创建的时候&#xff1a; 会继承父进程的mm_struct和页表 所以子进程刚刚继承时&#xff0c;父子进程的代码和数据…

5G网络下客户端数据业务掉线频繁

上层应用的日志和界面在待机状态下&#xff08;即没有做通话等业务操作&#xff09;&#xff0c;会频繁提示“离线”。 主要先看有没有丢网&#xff0c;UL BLER有没有问题。确认没有问题。看到业务信道释放后也可以成功重新建链。所以以为这个只是终端业务进入dormant态的提示…

【Docker项目实战】使用Docker部署Gitblit服务器

【Docker项目实战】使用Docker部署Gitblit服务器 一、Gitblit介绍1.1 Gitblit 介绍1.2 主要特点 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Gitblit镜像五、部署Gitbli…

Vitis: 使用自定义IP时 Makefile错误 导致编译报错

参考文章: 【小梅哥FPGA】 Vitis开发中自定义IP的Makefile路径问题解决方案 Vitis IDE自定义IP Makefile错误&#xff08;arm-xilinx-eabi-gcc.exe: error: *.c: Invalid argument&#xff09;解决方法 Vitis 使用自定义IP时: Makefile 文件里的语句是需要修改的&#xff0c;…

helm的go模板语法学习

1、helm chart 1.0、什么是helm&#xff1f; 介绍&#xff1a;就是个包管理器。理解为java的maven、linux的yum就好。 安装方法也可参见官网&#xff1a; https://helm.sh/docs/intro/install 通过前面的演示我们知道&#xff0c;有了helm之后应用的安装、升级、查看、停止都…

AI 语音公司 ElevenLabs 进军亚太市场设立东京子公司;EverTutor Live :语音交互 AI 教育平台丨日报

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的 技术 」、「有亮点的 产品 」、「有思考的 文章 」、「有态度的 观…

STM32启动流程详解

STM32启动流程详解 本文档详细介绍STM32微控制器从上电到main函数执行的完整启动流程。 1. 上电与复位过程 当STM32芯片上电或复位时&#xff0c;硬件会执行以下步骤&#xff1a; 上电复位(POR)/低电平复位(PDR): 芯片接通电源或NRST引脚置低时触发初始PC值设置: 程序计数器…

Langchain + Gemini API调用基本操作

本文参考Langchain中ChatGoogleGenerativeAI的官方文档&#xff0c;在本地的jupyter notebook中运行。 关于API的细节在官方文档最开头给出&#xff1a; 我们在使用时&#xff0c;可以选择model"gemini-2.0-flash-001"或者生成图片的ChatGoogleGenerativeAI(model“…

【数据结构】4.单链表实现通讯录

在上一篇文章我们学会了用单链表来实现各种方法&#xff0c;在这一篇文章我们将在单链表的基础上实现通讯录。 0、准备工作 实现通讯录之前&#xff0c;我们还需要在单链表的基础上添加2个文件&#xff0c;头文件Contact.h和源文件Contact.c。Contact.c来实现通讯录方法的声明…