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

1.1静态页面搭建

javascript 复制代码
<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>

面包屑

javascript 复制代码
<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>

设置

javascript 复制代码
<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菜单折叠效果实现

javascript 复制代码
//小仓库:layout组件相关配置仓库
import { defineStore } from 'pinia'

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

export default useLayOutSettingStore

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

javascript 复制代码
<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面包屑的实现

javascript 复制代码
<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刷新业务的实现

设置一个全局变量

javascript 复制代码
//获取骨架的小仓库
import useLayOutSettingStore from '@/store/modules/setting'
const layoutSettingStore = useLayOutSettingStore()
//刷新按钮点击回调
const updateRefsh = () => {
  layoutSettingStore.refsh = !layoutSettingStore.refsh
}
javascript 复制代码
<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全屏功能的实现

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

1.6获取用户信息

javascript 复制代码
//获取用户信息方法
        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
            }
        },
javascript 复制代码
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退出登录业务

javascript 复制代码
// 存储
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')
}
javascript 复制代码
userLogout() {
            this.token = ''
            this.username = ''
            this.avatar = ''
            REMOVE_TOKEN()
        },
javascript 复制代码
//退出登录点击回调
const logout = async () => {
    //第一件事情:需要向服务器发请求[退出登录接口]******
    //第二件事情:仓库当中关于用于相关的数据清空[token|username|avatar]
    //第三件事情:跳转到登录页面
    //跳转到登录页面
    userStore.userLogout()
    $router.push({ path: '/login', query: { redirect: $route.path } })
}

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

相关推荐
Redstone Monstrosity1 分钟前
字节二面
前端·面试
东方翱翔9 分钟前
CSS的三种基本选择器
前端·css
Fan_web31 分钟前
JavaScript高级——闭包应用-自定义js模块
开发语言·前端·javascript·css·html
yanglamei196239 分钟前
基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue
前端·数据库·flask
千穹凌帝40 分钟前
SpinalHDL之结构(二)
开发语言·前端·fpga开发
冯宝宝^43 分钟前
基于mongodb+flask(Python)+vue的实验室器材管理系统
vue.js·python·flask
dot.Net安全矩阵1 小时前
.NET内网实战:通过命令行解密Web.config
前端·学习·安全·web安全·矩阵·.net
叫我:松哥1 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
Hellc0071 小时前
MacOS升级ruby版本
前端·macos·ruby
前端西瓜哥1 小时前
贝塞尔曲线算法:求贝塞尔曲线和直线的交点
前端·算法