效果图:
home.vue页面代码
<template> <el-container> <el-aside width="collapse ? 200px : 70px"> <el-button color="#626aef" @click="collapseToggle()"> <el-icon> <Expand v-if="collapse" /> <Fold v-else /> </el-icon> </el-button> <el-menu :collapse="collapse" :default-active="store.bbc" class="el-menu-vertical-demo" unique-opened active-text-color="#ffd04b" text-color="#fff" background-color="transparent" @select="store.vv" > <left :dataToSon="store.mm" /> </el-menu> </el-aside> <el-container> <el-header height="80px"> <h1 @click="fff">大可的管理系统 - v1.0</h1> <div> <img src="@/assets/111.jpg" alt=""> <span></span> <el-button type="primary" @click="LogOut">退出登录</el-button> </div> </el-header> <el-main> <tab></tab> </el-main> <el-footer height="50px"> <p>© 版权所有: 大可</p> </el-footer> </el-container> </el-container> </template> <script setup lang="ts"> import { ref } from 'vue'; import { useRouter } from "vue-router"; import left from "../left.vue"; import tab from '../tab.vue'; import { ElMessage, ElMessageBox} from "element-plus"; import { useAuthStore } from '@/store'; import preventBack from 'vue-prevent-browser-back';//组件内单独引入 const mixins = [preventBack]; const store = useAuthStore(); const collapse = ref<boolean>(false) const router = useRouter(); const tiao = () => { console.log('路由') router.push('/son1') } const fff = () => { router.replace('/son2') } const collapseToggle = () => { collapse.value = !collapse.value } const ggvv = ref([1,2,3]) const handleOpen = () => { console.log() } const gg = (e) => { console.log(e) } const handleClose = () => { console.log() } const LogOut = () => { ElMessageBox.confirm( '确定要退出登录?', 'Warning', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', } ) .then(() => { router.replace('/login') ElMessage({ type: 'success', message: '退出成功', }) }) .catch(() => { ElMessage({ type: 'info', message: '您取消了退出', }) }) } </script> <style scoped> .el-header { background: url("@/assets/111.jpg"); background-color: #f3d19e; display: flex; align-items: center; justify-content: space-between; } .el-header h1 { font-size: 26px; color: #fff; letter-spacing: 10px; } .el-header div { margin-right: 30px; } .el-header img { width: 40px; border-radius: 40px; vertical-align: middle; margin-right: 10px; } .el-header span { font-size: 18px; color: #fff; margin-right: 10px; } .el-header el-button { margin-left: 10px; } .el-aside { height: 100vh; background: url('@/assets/111.jpg'); transition: width 0.3s; text-align: right; } .el-aside .el-button { margin: 20px 10px 20px 0; } .el-aside .el-menu { border-right: none; } .el-footer { background-color: #EBEEF5; display: flex; align-items: center; } .el-footer p { font-size: 12px; color: #666; } </style>
left.vue页面代码
<script setup lang="ts"> import { ref, defineProps } from 'vue'; import { useAuthStore } from '@/store'; type HeaderProps = { dataToSon: number[]; }; // 2. type 声明的HeaderProps 用 defineProps注册 const zz = defineProps<HeaderProps>(); const hhkk = zz.dataToSon; const list = ref<Array<object>>([]); const store = useAuthStore(); const gg = () => { } </script> <template> <template v-for="(item, index) in hhkk" :key="item.id"> <!-- 非叶子节点 --> <el-sub-menu v-if="item.children" :index="item.id"> <template #title> <el-icon> <Setting /> </el-icon> <span v-text="item.name"></span> </template> <left :dataToSon="item.children"/> </el-sub-menu> <!-- 叶子节点(功能节点) --> <el-menu-item v-else :index="item.path"> <el-icon> <Menu /> </el-icon> <span v-text="item.name"></span> </el-menu-item> </template> </template> <style scoped lang="stylus"> </style>
tab.vue页面代码
<template> <el-tabs v-model="store.bbc" type="card" class="demo-tabs" closable @tab-remove="store.kk" @tab-click="jj" > <!-- @tab-click="jj"--> <el-tab-pane v-for="item in ff" :key="item.id" :label="item.name" :name="item.path" > <router-view :name="item.path" v-slot="{Component}"> <keep-alive> <component :is="Component"></component> </keep-alive> </router-view> </el-tab-pane> </el-tabs> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue' import {useAuthStore} from '@/store'; import {useRouter} from "vue-router"; const store = useAuthStore(); const router = useRouter(); onMounted(() => { // store.bbc = router.currentRoute._value.fullPath; }) const fv = store.bbc; const jj = (e) => { // router.replace(e.props.name) console.log(e) } const hh = (ee) => { console.log(ee) } let tabIndex = 2 const editableTabsValue = ref(store.zx.length) const ff = store.zx; const gg = (e) => { ff.forEach(item => { if (item.path == e) { ff.splice(item,1) } }) } </script> <style scoped> .demo-tabs > .el-tabs__content { padding: 32px; color: #6b778c; font-size: 32px; font-weight: 600; } </style>
pinia里面的代码
import { defineStore } from 'pinia'; export default defineStore('auth', { state: () => { return { loginName: "张三", ss: [], mm: [ { path: 'vv', name:'首页', func_fid: 0, id:"1000", children: [ { path: 'sy', name:'首页儿子', func_fid: 1000, id:"1212", } ] }, { path:'hh', name:'系统管理', func_fid: 0, id: '1', children: [ { id: '1', func_fid: 1, path:'son1', name: '系统管理儿子', }, { id:' 2', func_fid: 1, path:'hhh', name: '系统管理-角色', children: [ { id: '222', func_fid: 1, path: 'son1-1-1', name: '角色管理', children:[ { id: '12', func_fid: 2, path: 'son1-1-1', name: '角色管理儿子', children:[ { id:' 122', func_fid: 2, path: 'son1-1-1', name: '角色管理儿子-----孙子', } ] } ] } ] }, { id: '12', path: 'son1-2', name: '用户管理' } ] }, { path:'ss', name:'教学管理', id: '22', func_fid: 0, children: [ { path:'son2', name:'教学管理儿子', id: '202', func_fid: 22, } ] }, { path:'zz', name:'行政管理', id: '3', func_fid: 0, children: [ { path:'son3', name:'行政管理儿子', id: '33', func_fid: 3, } ] }, ], zx:[], bbx:[], bbc: '', } }, persist: { enabled: true, // 开启缓存 默认会存储在本地localstorage storage: sessionStorage, // 缓存使用方式 paths:[] // 需要缓存键 }, actions: { zz(ss:string) { this.loginName = ss }, vv(e) { let hh = []; function traverse(node) { hh.push(JSON.parse(JSON.stringify(node))) if (node.children && node.children.length > 0) { for (let i = 0; i < node.children.length; i++) { traverse(node.children[i]); } } } this.mm.forEach(item => { traverse(item) }) const bb =[] hh.forEach(item => { if (item.path == e) { const index = this.zx.findIndex(i => JSON.stringify(i) === JSON.stringify(item)); this.bbc = item.path; if (index === -1) { this.zx.push({...item}) } } }) }, kk(e) { let i = this.zx.findIndex(item => item.path === e); if(e != this.bbc) {} //删除的是一个不激活的tab那么就什么都不做 else if (this.zx.length === 1) //删除的是最后剩下的一个激活的tab this.bbc = ''; else if (i === this.zx.length - 1) //删除的是最末尾的一个tab,让前面那个激活 this.bbc = this.zx[i - 1].path; else //删除的是中间的一个激活的tab this.bbc = this.zx[i + 1].path; this.zx.splice(i, 1); }, } })
安装
yarn add pinia-plugin-persistedstate
or
npm i pinia-plugin-persistedstate
使用插件 在main.ts中注册
import { createApp } from "vue";
import App from "./App.vue";
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
createApp(App).use(pinia);
路由代码
// import {createRouter, createWebHistory, createWebHashHistory} from "vue-router"; // // 1. 配置路由 // const routes: ({ redirect: string; path: string } | { path: string; component: () => Promise<any> } | { path: string; component: () => Promise<any>; children: ({ path: string; components: { son2: () => Promise<any> } } | { path: string; components: { son3: () => Promise<any> } })[] } | { path: string; components: { son1: () => Promise<any> }; name: string; props: { son1: boolean } })[] = [ // {path: '/', redirect: '/login'}, // {path: '/login', component: () => import('../views/login.vue')}, // { // path: '/home', // component: () => import('../views/home/home.vue'), // children: [ // // {path: 'son1', name: 'son1', components: {son1: () => import('../views/home/son/son1.vue')}}, // {path: 'son2', components: {son2: () => import('../views/home/son/son2.vue')}}, // {path: 'son3', components: {son3: () => import('../views/home/son/son3.vue')}}, // ] // }, // { // path: '/son1', // name: 'son1', // components: {son1: () => import('../views/home/son/son1.vue')}, // props: { son1: true } // } // // ]; // // 2.返回一个 router 实列,为函数,配置 history 模式 // const router = createRouter({ // history: createWebHashHistory(), // routes, // }); // // // // 3.导出路由 去 main.ts 注册 router.ts // // export default router import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(), routes: [ {path: '/', redirect: '/login'}, {path: '/login', component: () => import('../views/login.vue')}, { path: '/home', component: () => import('../views/home/home.vue'), children: [ { path:'', components: { son1: () => import('../views/home/son/son1.vue'), son2: () => import('../views/home/son/son2.vue'), son3: () => import('../views/home/son/son3.vue'), sy:() => import('../views/home/son/sy.vue'), } }, ] }, ] }) export default router
我把代码放git上了,有需要的自行拉取