vue3后台管理系统之顶部tabbar组件搭建

news2024/11/16 15:27:40

1.1静态页面搭建

<template>
    <div class="tabbar">
        <div class="tabbar_left">
            <!-- 面包屑 -->
            <Breadcrumb />
        </div>
        <div class="tabbar_right">
            <!-- 设置 -->
            <Setting />
        </div>
    </div>
</template>

<script setup lang="ts">
import Breadcrumb from './breadcrumb/index.vue'
import Setting from './setting/index.vue'
</script>
<script lang="ts">
export default {
    name: 'Tabbar',
}
</script>
<style scoped lang="scss">
.tabbar {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: space-between;
    // background-image: linear-gradient(to right, rgb(232, 223, 223), rgb(201, 178, 178), rgb(197, 165, 165));

    .tabbar_left {
        display: flex;
        align-items: center;
        margin-left: 20px;
    }

    .tabbar_right {
        display: flex;
        align-items: center;
    }
}
</style>

面包屑

<template>
  <!-- 顶部左侧静态 -->
  <el-icon style="margin-right: 10px" @click="changeIcon">
    <!-- <component :is="LayOutSettingStore.fold ? 'Fold' : 'Expand'" /> -->
  </el-icon>
  <!-- 左侧面包屑 -->
  <el-breadcrumb separator-icon="ArrowRight">
    <!-- 面包动态展示路由名字与标题 -->
    <el-breadcrumb-item v-for="(item, index) in $route.matched" v-show="item.meta.title" :key="index" :to="item.path">
      <!-- 图标 -->
      <el-icon>
        <component :is="item.meta.icon" />
      </el-icon>
      <!-- 面包屑展示匹配路由的标题 -->
      <span>{{ item.meta.title }}</span>
    </el-breadcrumb-item>
  </el-breadcrumb>
</template>

<script setup lang="ts">
import { useRoute } from 'vue-router'
// import useLayOutSettingStore from '@/store/moudles/setting'
// //获取layout配置相关的仓库
// const LayOutSettingStore = useLayOutSettingStore()
//获取路由对象
const $route = useRoute()
console.log($route.matched, '111')

//点击图标的方法
const changeIcon = () => {
  //图标进行切换
  LayOutSettingStore.fold = !LayOutSettingStore.fold
}
</script>
<script lang="ts">
export default {
  name: 'Breadcrumb'
}
</script>

<style scoped></style>

设置

<template>
  <el-button size="small" icon="Refresh" circle @click="updateRefsh" />
  <el-button size="small" icon="FullScreen" circle @click="fullScreen" />

  <el-popover placement="bottom" title="主题设置" :width="300" trigger="hover">
    <!-- 表单元素 -->
    <el-form>
      <el-form-item label="主题颜色">
        <!-- <el-color-picker v-model="color" size="small" show-alpha :predefine="predefineColors" @change="setColor" /> -->
      </el-form-item>
      <el-form-item label="暗黑模式">
        <el-switch
          v-model="dark"
          class="mt-2"
          style="margin-left: 24px"
          inline-prompt
          active-icon="MoonNight"
          inactive-icon="Sunny"
          @change="changeDark"
        />
      </el-form-item>
    </el-form>
    <template #reference>
      <el-button size="small" icon="Setting" circle />
    </template>
  </el-popover>
  <img :src="userStore.avatar" style="width: 24px; height: 24px; margin: 0px 10px; border-radius: 50%" />
  <!-- 下拉菜单 -->
  <el-dropdown>
    <span class="el-dropdown-link">
      admin
      <el-icon class="el-icon--right">
        <arrow-down />
      </el-icon>
    </span>
    <template #dropdown>
      <el-dropdown-menu>
        <el-dropdown-item @click="logout">退出登录</el-dropdown-item>
      </el-dropdown-menu>
    </template>
  </el-dropdown>
</template>

<script setup lang="ts">
import { ref } from 'vue'
// import { useRouter, useRoute } from 'vue-router'
//获取用户相关的小仓库
import useUserStore from '@/store/modules/user'
//获取骨架的小仓库
// import useLayOutSettingStore from '@/store/modules/setting'
// const layoutSettingStore = useLayOutSettingStore()
const userStore = useUserStore()
//获取路由器对象
// let $router = useRouter()
//获取路由对向
// let $route = useRoute()
//收集开关的数据
const dark = ref<boolean>(false)
//刷新按钮点击回调
const updateRefsh = () => {
  layoutSettingStore.refsh = !layoutSettingStore.refsh
}
//全屏按钮点击的回调
const fullScreen = () => {
  //DOM对象的一个属性:可以用来判断当前是不是全屏模式[全屏:true,不是全屏:false]
  const full = document.fullscreenElement
  //切换为全屏模式
  if (!full) {
    //文档根节点的方法requestFullscreen,实现全屏模式
    document.documentElement.requestFullscreen()
  } else {
    //变为不是全屏模式->退出全屏模式
    document.exitFullscreen()
  }
}
//退出登录点击回调
const logout = async () => {
  //第一件事情:需要向服务器发请求[退出登录接口]******
  //第二件事情:仓库当中关于用于相关的数据清空[token|username|avatar]
  //第三件事情:跳转到登录页面
  //跳转到登录页面
}

//颜色组件组件的数据
const color = ref('rgba(255, 69, 0, 0.68)')
const predefineColors = ref([
  '#ff4500',
  '#ff8c00',
  '#ffd700',
  '#90ee90',
  '#00ced1',
  '#1e90ff',
  '#c71585',
  'rgba(255, 69, 0, 0.68)',
  'rgb(255, 120, 0)',
  'hsv(51, 100, 98)',
  'hsva(120, 40, 94, 0.5)',
  'hsl(181, 100%, 37%)',
  'hsla(209, 100%, 56%, 0.73)',
  '#c7158577'
])

//switch开关的chang事件进行暗黑模式的切换
const changeDark = () => {
  //获取HTML根节点
  const html = document.documentElement
  //判断HTML标签是否有类名dark
  dark.value ? (html.className = 'dark') : (html.className = '')
}

//主题颜色的设置
const setColor = () => {
  //通知js修改根节点的样式对象的属性与属性值
  const html = document.documentElement
  html.style.setProperty('--el-color-primary', color.value)
}
</script>

<script lang="ts">
export default {
  name: 'Setting'
}
</script>
<style scoped></style>

1.2菜单折叠效果实现

//小仓库:layout组件相关配置仓库
import { defineStore } from 'pinia'

const useLayOutSettingStore = defineStore('SettingStore', {
    state: () => {
        return {
            fold: false, //用户控制菜单折叠还是收起控制
            refsh: false, //仓库这个属性用于控制刷新效果
        }
    },
})

export default useLayOutSettingStore

动态判断菜单是否折叠,然后情况修改样式

<template>
    <div class="layout_container">
        <!-- 左侧导航 -->
        <div
            class="layout_slider"
            :class="{ fold: LayOutSettingStore.fold ? true : false }"
        >
            <Logo />
            <!-- 展示菜单 -->
            <!-- 滚动组件 -->
            <el-scrollbar class="scrollbar">
                <!-- 菜单组件-->
                <el-menu
                    :collapse="LayOutSettingStore.fold ? true : false"
                    :default-active="$route.path"
                    background-color="#001529"
                    text-color="white"
                    active-text-color="yellowgreen"
                >
                    <!-- 根据路由动态生成菜单 -->
                    <Menu :menuList="userStore.menuRoutes"></Menu>
                </el-menu>
            </el-scrollbar>
        </div>
        <!-- 顶部导航 -->
        <div
            class="layout_tabbar"
            :class="{ fold: LayOutSettingStore.fold ? true : false }"
        >
            <Tabbar></Tabbar>
        </div>
        <!-- 内容展示区 -->
        <div
            class="layout_main"
            :class="{ fold: LayOutSettingStore.fold ? true : false }"
        >
            <router-view></router-view>
        </div>
    </div>
</template>
<script setup lang="ts">
import Menu from './components/menu/index.vue'
import Logo from './components/logo/index.vue'
import Tabbar from './components/tabbar/index.vue'
//获取用户相关的小仓库
import useUserStore from '@/store/moudules/user'
let userStore = useUserStore()
// 獲取路有對象
import { useRoute } from 'vue-router'
import useLayOutSettingStore from '@/store/moudules/setting'
//获取layout配置仓库
let LayOutSettingStore = useLayOutSettingStore()
//获取路由器
let $route = useRoute()
console.log($route.path)
</script>
<script lang="ts">
export default {
    name: 'Layout',
}
</script>
<style scoped lang="scss">
.layout_container {
    width: 100%;
    height: 100vh;

    .layout_slider {
        color: white;
        width: $base-menu-width;
        height: 100vh;
        background: $base-menu-background;
        transition: all 0.3s;

        .scrollbar {
            width: 100%;
            height: calc(100vh - $base-menu-logo-height);

            .el-menu {
                border-right: none;
            }
        }
    }

    .layout_tabbar {
        position: fixed;
        width: calc(100% - $base-menu-width);
        height: $base-tabbar-height;
        top: 0px;
        left: $base-menu-width;
        transition: all 0.3s;
        &.fold {
            width: calc(100vw - $base-menu-min-width);
            left: $base-menu-min-width;
            background-color: #fff;
        }
    }

    .layout_main {
        transition: all 0.3s;
        position: absolute;
        width: calc(100% - $base-menu-width);
        height: calc(100vh - $base-tabbar-height);
        left: $base-menu-width;
        top: $base-tabbar-height;
        padding: 20px;
        overflow: auto;
        background-color: yellowgreen;
        &.fold {
            width: calc(100vw - $base-menu-min-width);
            left: $base-menu-min-width;
        }
    }
}
</style>

1.3面包屑的实现

<template>
    <!-- 顶部左侧静态 -->
    <el-icon style="margin-right: 10px" @click="changeIcon">
        <component
            :is="LayOutSettingStore.fold ? 'Fold' : 'Expand'"
        ></component>
    </el-icon>
    <!-- 左侧面包屑 -->
    <el-breadcrumb separator-icon="ArrowRight">
        <!-- 面包动态展示路由名字与标题 -->
        <el-breadcrumb-item
            v-for="(item, index) in $route.matched"
            :key="index"
            v-show="item.meta.title"
            :to="item.path"
        >
            <!-- 图标 -->
            <el-icon>
                <component :is="item.meta.icon"></component>
            </el-icon>
            <!-- 面包屑展示匹配路由的标题 -->
            <span>{{ item.meta.title }}</span>
        </el-breadcrumb-item>
    </el-breadcrumb>
</template>

<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router'
import useLayOutSettingStore from '@/store/moudules/setting'
//获取layout配置相关的仓库
let LayOutSettingStore = useLayOutSettingStore()
//获取路由对象
let $route = useRoute()
console.log($route)
console.log($route.matched, '111')
let $router = useRouter()
console.log($router, '111')

//点击图标的方法
const changeIcon = () => {
    //图标进行切换
    LayOutSettingStore.fold = !LayOutSettingStore.fold
}
</script>
<script lang="ts">
export default {
    name: 'Breadcrumb',
}
</script>

<style scoped></style>

1.4刷新业务的实现

设置一个全局变量

//获取骨架的小仓库
import useLayOutSettingStore from '@/store/modules/setting'
const layoutSettingStore = useLayOutSettingStore()
//刷新按钮点击回调
const updateRefsh = () => {
  layoutSettingStore.refsh = !layoutSettingStore.refsh
}

<template>
    <!-- 路由组件出口的位置 -->
    <router-view v-slot="{ Component }">
        <transition name="fade">
            <!-- 渲染layout一级路由组件的子路由 -->
            <component :is="Component" v-if="flag" />
        </transition>
    </router-view>
</template>

<script setup lang="ts">
import { watch, ref, nextTick } from 'vue'
import useLayOutSettingStore from '@/store/moudules/setting'
let layOutSettingStore = useLayOutSettingStore()

//控制当前组件是否销毁重建
let flag = ref(true)

//监听仓库内部数据是否发生变化,如果发生变化,说明用户点击过刷新按钮
watch(
    () => layOutSettingStore.refsh,
    () => {
        //点击刷新按钮:路由组件销毁
        flag.value = false
        nextTick(() => {
            flag.value = true
        })
    },
)
</script>
<script lang="ts">
export default {
    // eslint-disable-next-line vue/no-reserved-component-names
    name: 'Main',
}
</script>

<style scoped>
.fade-enter-from {
    opacity: 0;
    transform: scale(0);
}

.fade-enter-active {
    transition: all 0.3s;
}

.fade-enter-to {
    opacity: 1;
    transform: scale(1);
}
</style>

1.5全屏功能的实现

//全屏按钮点击的回调
const fullScreen = () => {
  //DOM对象的一个属性:可以用来判断当前是不是全屏模式[全屏:true,不是全屏:false]
  const full = document.fullscreenElement
  //切换为全屏模式
  if (!full) {
    //文档根节点的方法requestFullscreen,实现全屏模式
    document.documentElement.requestFullscreen()
  } else {
    //变为不是全屏模式->退出全屏模式
    document.exitFullscreen()
  }
}

1.6获取用户信息

//获取用户信息方法
        async userInfo() {
            //获取用户信息进行存储仓库当中[用户头像、名字]

            const result: userInfoReponseData = await reqUserInfo()
            console.log(result)
            //如果获取用户信息成功,存储一下用户信息
            if (result.code == 200) {
                this.username = result.data.checkUser.username
                this.avatar = result.data.checkUser.avatar
            }
        },

import axios from 'axios'
import { ElMessage } from 'element-plus'
//引入用户相关的仓库
import useUserStore from '@/store/modules/user'
//创建axios实例
const request = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_URL,

  timeout: 5000
})
//请求拦截器
request.interceptors.request.use((config) => {
  //获取用户相关的小仓库:获取仓库内部token,登录成功以后携带给服务器
  const userStore = useUserStore()
  if (userStore.token) {
    config.headers.token = userStore.token
  }
  //config配置对象,headers属性请求头,经常给服务器端携带公共参数
  //返回配置对象
  return config
})
//响应拦截器
request.interceptors.response.use(
  (response) => {
    return response.data
  },
  (error) => {
    //处理网络错误
    let msg = ''
    const status = error.response.status
    switch (status) {
      case 401:
        msg = 'token过期'
        break
      case 403:
        msg = '无权访问'
        break
      case 404:
        msg = '请求地址错误'
        break
      case 500:
        msg = '服务器出现问题'
        break
      default:
        msg = '无网络'
    }
    ElMessage({
      type: 'error',
      message: msg
    })
    return Promise.reject(error)
  }
)
export default request

1.7退出登录业务

// 存储
export const SET_TOKEN = (token: string) => {
  localStorage.setItem('TOKEN', token)
}
// 读取
export const GET_TOKEN = () => {
  return localStorage.getItem('TOKEN')
}
// 移除token
export const REMOVE_TOKEN = () => {
  localStorage.removeItem('TOKEN')
}
userLogout() {
            this.token = ''
            this.username = ''
            this.avatar = ''
            REMOVE_TOKEN()
        },
//退出登录点击回调
const logout = async () => {
    //第一件事情:需要向服务器发请求[退出登录接口]******
    //第二件事情:仓库当中关于用于相关的数据清空[token|username|avatar]
    //第三件事情:跳转到登录页面
    //跳转到登录页面
    userStore.userLogout()
    $router.push({ path: '/login', query: { redirect: $route.path } })
}

再设置一个退出登录再登录返回原来的路由

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

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

相关文章

有源RFID标签系统SOC单发芯片——Si24R2E

Si24R2E是针对有源RFID市场设计的低功耗、高性能的2.4GHz标签系统的SoC单芯片&#xff0c;集成嵌入式2.4GHz无线射频发射器模块、128次可编程NVM存储器模块以及自动发射控Z器模块等。 Si24R2E采用GFSK/FSK数字调制与解调技术。数据传输速率可配置&#xff0c;支持2Mbps、1Mbps和…

UML(Unified Modeling Language)统一建模语言,及工具介绍、使用

1. UML介绍&#xff1a; UML&#xff08;Unified Modeling Language&#xff09;统一建模语言。是一种图形化语言。 在UML 2.5 中共包含14种图形&#xff1a;类图、用例图、活动图、对象图、时序图、交互概述图、包图、配置文件图、部署图、组件图、组合结构图、状态机图、通…

[ Windows-Nginx ]Windows服务器,Tomcat容器部署项目,整合Nginx

一、官网下载Nginx http://nginx.org/en/download.html 稳定版&#xff1a;windows的stable版本 注意&#xff1a;Nginx安装包不要放在中文目录下 二、conf目录下&#xff0c;修改nginx.conf文件 修改Nginx服务端口&#xff1a; 默认端口为80&#xff0c;即外界访问的入口…

图论01-【无权无向】-图的基本表示-邻接矩阵/邻接表

文章目录 1. 代码仓库2. 图的基本表示的比较3. 邻接矩阵&#xff1a;Array和TreeSet3.1 图示3.2 Array主要代码解析3.3 测试输出3.4 使用TreeSet的代码 4. 邻接表&#xff1a;LinkedList4.1 图示4.2 LinkedList主要代码解析4.3 测试输出 5. 完整代码5.1 邻接表 - Array5.2 邻接…

Hadoop3教程(二十七):(生产调优篇)HDFS读写压测

文章目录 &#xff08;146&#xff09;HDFS压测环境准备&#xff08;147&#xff09;HDFS读写压测写压测读压测 参考文献 &#xff08;146&#xff09;HDFS压测环境准备 对开发人员来讲&#xff0c;压测这个技能很重要。 假设你刚搭建好一个集群&#xff0c;就可以直接投入生…

屏幕录像推荐:Apeaksoft Screen Recorder 中文 for mac

Apeaksoft Screen Recorder 是一款功能强大的屏幕录制软件&#xff0c;它允许用户在 Windows 和 Mac 系统上捕捉和录制屏幕活动。无论是记录游戏过程、创建教学视频、制作演示文稿还是捕捉在线流媒体内容&#xff0c;该软件都提供了丰富的功能和工具。 以下是 Apeaksoft Scree…

【LeetCode每日一题合集】2023.10.9-2023.10.15(贪心⭐位运算的应用:只出现一次的数字)

文章目录 2578. 最小和分割&#xff08;贪心&#xff09;2731. 移动机器人&#xff08;脑筋急转弯排序统计&#xff09;2512. 奖励最顶尖的 K 名学生&#xff08;哈希表排序&#xff09;&#xff08;练习Java语法&#xff09;代码风格1代码风格2 2562. 找出数组的串联值&#x…

基于Linux的驱动开发:内核模块传参、内核到处符号表、字符设备驱动

内核模块传参 内核模块&#xff1a; int a , b; 安装内核模块时&#xff1a;insmod demo.ko a 100 b 10; 1.内核模块传参的意义 在安装内核模块时给内核模块中的变量进行数值传递&#xff0c;这样可以让我们的内核模块向上兼容更为复杂的应用程序&#xff0c;向下适配多种硬件…

网络协议--ARP:地址解析协议

4.1 引言 本章我们要讨论的问题是只对TCP/IP协议簇有意义的IP地址。数据链路如以太网或令牌环网都有自己的寻址机制&#xff08;常常为48 bit地址&#xff09;&#xff0c;这是使用数据链路的任何网络层都必须遵从的。一个网络如以太网可以同时被不同的网络层使用。例如&#…

解剖—单链表相关OJ练习题

目录 一、移除链表元素 二、找出链表的中间节点 三、合并两个有序链表 四、反转链表 五、求链表中倒数第k个结点 六、链表分割 七、链表的回文结构 八、判断链表是否相交 九、判断链表中是否有环(一) 十、 判断链表中是否有环(二) 注&#xff1a;第六题和第七题牛…

三网话费余额查询的API系统 基于thinkphp6.0框架

本套系统是用thinkphp6.0框架开发的&#xff0c;PHP需大于8.2&#xff0c;系统支持用户中心在线查询和通过API接口对接发起查询&#xff0c;用户余额充值是对接usdt接口&#xff0c;源码全开源&#xff0c;支持懂技术的人二次开发~搭建教程1、源码上传后&#xff0c;吧运行目录…

异常数据检测 | Python基于Hampel的离群点检测

文章目录 文章概述模型描述源码分享文章概述 在时间序列数据分析领域,识别和处理异常点是至关重要的任务。异常点或离群点是明显偏离预期模式的数据点,可能表明存在错误、欺诈或有价值的见解。 应对这一挑战的一种有效技术是汉普尔过滤器(Hampel Filter)。 模型描述 汉…

Manacher学习笔记

Manacher 算法&#xff0c;俗称马拉车算法&#xff0c;是一种解决最长回文子串问题的算法。 在 Ybt 的哈希章节中出现了这个&#xff1a; 数据范围 1 0 6 10^6 106&#xff0c;数据相对于马拉车模板较弱。 对于一个普通的字符串&#xff0c;我们要想求出它的回文子串需要考虑它…

聊天机器人语料在开发中的重要性

语料在聊天机器人的开发中起着至关重要的作用&#xff0c;使其能够有效理解和回应用户的查询。语料是聊天机器人的训练数据&#xff0c;通过分析和学习这个语料&#xff0c;聊天机器人可以提高对用户意图的准确理解&#xff0c;并生成恰当的回应。 | 一、聊天机器人语料好在哪&…

Unity 单例-接口模式

单例-接口模式 使用接口方式实现的单例可以继承其它类&#xff0c;更加方便 using System.Collections; using System.Collections.Generic; using UniRx; using UniRx.Triggers; using UnityEngine; namespace ZYF {public interface ISingleton<TMono> where TMono : M…

Redis-Sentinel高可用架构学习

Redis-Sentinel高可用架构 Redis主从复制过程&#xff1a; 主从同步原理 Redis Sentinel&#xff08;哨兵&#xff09;高可用集群方案&#xff1a;Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案。 当用Redis做Master-slave的高可用方案时&#xff0c;假如master宕机了…

微信小程序之后台首页交互

目录 一.与后台数据进行交互&request封装 后台准备 测试结果 ​编辑 前端 测试结果 二.wxs的介绍以及入门 测试结果 一.与后台数据进行交互&request封装 后台准备 pom.xml文件编写 <?xml version"1.0" encoding"UTF-8"?> <proj…

【JavaEE】常见的锁策略 -- 多线程篇(4)

文章目录 乐观锁 vs 悲观锁读写锁重量级锁 vs 轻量级锁自旋锁&#xff08;Spin Lock&#xff09;公平锁 vs 非公平锁可重入锁 vs 不可重入锁 乐观锁 vs 悲观锁 悲观锁: 总是假设最坏的情况&#xff0c;每次去拿数据的时候都认为别人会修改&#xff0c;所以每次在拿数据的时候都…

全连接网络参数Xavier初始化

1.梯度消失 考虑下图的神经网络&#xff0c;在使用梯度下降法迭代更新W_ki和W_ij时&#xff0c;它们的梯度方向间有什么关系&#xff1f; 它们的梯度关系如下&#xff1a; 从上述两个式子我们大致可以看出&#xff0c;损失函数L关于第h层参数的梯度由两部分组成&#xff1a;…

sql server2014如何添加多个实例 | 以及如何删除多个实例中的单个实例

标题sql server2014如何添加多个实例 前提&#xff08;已安装sql server2014 且已有默认实例MSSQLSERVER&#xff09; 添加新的实例 其实就是根据安装步骤再安装一次&#xff08;区别在过程中说明&#xff09; 双击安装 选择“全新独立安装或添加现有功能” 然后下一步下一…