添加tab栏

master
liuyx 2 years ago
parent a72a80d58f
commit 8776fcafe9

@ -1,3 +1,3 @@
{
"recommendations": ["Vue.volar"]
"recommendations": ["vue.volar", "cnblogs.vscode-cnb"]
}

@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/src/assets/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
<title>后台管理</title>
</head>
<body>
<div id="app"></div>

@ -13,6 +13,7 @@
"axios": "^1.1.3",
"element-plus": "^2.2.19",
"pinia": "^2.0.23",
"pinia-plugin-persist": "^1.0.0",
"prismjs": "^1.29.0",
"screenfull": "^6.0.2",
"vue": "^3.2.41",
@ -4720,6 +4721,46 @@
}
}
},
"node_modules/pinia-plugin-persist": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/pinia-plugin-persist/-/pinia-plugin-persist-1.0.0.tgz",
"integrity": "sha512-M4hBBd8fz/GgNmUPaaUsC29y1M09lqbXrMAHcusVoU8xlQi1TqgkWnnhvMikZwr7Le/hVyMx8KUcumGGrR6GVw==",
"dependencies": {
"vue-demi": "^0.12.1"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0",
"pinia": "^2.0.0",
"vue": "^2.0.0 || >=3.0.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/pinia-plugin-persist/node_modules/vue-demi": {
"version": "0.12.5",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.12.5.tgz",
"integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/pinia/node_modules/vue-demi": {
"version": "0.13.11",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
@ -9645,6 +9686,22 @@
}
}
},
"pinia-plugin-persist": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/pinia-plugin-persist/-/pinia-plugin-persist-1.0.0.tgz",
"integrity": "sha512-M4hBBd8fz/GgNmUPaaUsC29y1M09lqbXrMAHcusVoU8xlQi1TqgkWnnhvMikZwr7Le/hVyMx8KUcumGGrR6GVw==",
"requires": {
"vue-demi": "^0.12.1"
},
"dependencies": {
"vue-demi": {
"version": "0.12.5",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.12.5.tgz",
"integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==",
"requires": {}
}
}
},
"posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmmirror.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz",

@ -14,6 +14,7 @@
"axios": "^1.1.3",
"element-plus": "^2.2.19",
"pinia": "^2.0.23",
"pinia-plugin-persist": "^1.0.0",
"prismjs": "^1.29.0",
"screenfull": "^6.0.2",
"vue": "^3.2.41",

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -10,4 +10,5 @@
.el-header {
--el-header-padding: none;
height: 84px;
}

@ -8,5 +8,5 @@
#app {
height: 100vh;
// font-family: 'Times New Roman', '仿宋';
font-family: 'Times New Roman', '仿宋';
}

@ -0,0 +1,156 @@
<template>
<div class="header-container">
<div class="navbar">
<!-- 折叠按钮 -->
<div class="hamburger-container" @click="toggleCollapse">
<el-icon size="25px">
<component :is="icon"></component>
</el-icon>
</div>
<!-- 面包屑 -->
<b>{{ $route.name }}</b>
<div class="right-navbar">
<!-- 全屏按钮 -->
<el-icon size="25px" class="screenfull" @click="toggleScreen">
<FullScreen />
</el-icon>
<!-- 用户头像 -->
<el-dropdown class="right-item">
<span class="el-dropdown-link">
<el-avatar :src="circleUrl" />
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="logout">退</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<!-- 历史标签栏 -->
<div class="tabs-container">
<div class="tabs-content">
<span
v-for="item of store.tabList"
:key="item.path"
class="tabs-content-item"
:class="isActive(item)"
@click="goTo(item)"
>
{{ item.name }}
<el-icon v-if="item.name !== '首页'" @click.stop="removeTab(item)"><Close /></el-icon>
</span>
</div>
<div class="tabs-close" @click="closeAllTab"></div>
</div>
</div>
</template>
<script setup>
import screenfull from 'screenfull'
import { ref, computed, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from '@/store'
const router = useRouter()
const route = useRoute()
const circleUrl = ref('https://liuyxcc.github.io/img/avatar.png')
const store = useStore()
const toggleCollapse = () => {
store.changeCollapse()
}
const closeAllTab = () => {
store.resetTab()
router.replace('/home') //
}
const goTo = (item) => {
router.push(item)
}
const removeTab = (item) => {
store.removeTab(item)
if (route.path === item.path) {
router.replace('/home')
}
}
const logout = () => {
localStorage.clear()
router.replace('/login')
console.log('logout')
}
const toggleScreen = () => {
if (screenfull.isEnabled) {
screenfull.toggle()
}
}
const icon = computed(() => {
return !store.collapse ? 'Fold' : 'Expand'
})
const isActive = computed(() => {
return (item) => (item.path === route.path ? 'tabs-content-item-active' : '')
})
// piniastoretabList
watch(
() => route.path,
() => {
// tabListstore
if (store.tabList.findIndex((item) => item.path === route.path) === -1) {
store.tabList.push({ name: route.name, path: route.path })
}
}
)
</script>
<style lang="scss" scoped>
.header-container {
padding: 0 10px;
height: 100%;
box-shadow: 0 2px 3px #888888;
height: 84px;
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 42px;
.right-navbar {
display: flex;
justify-content: center;
align-items: center;
.right-item {
margin-left: 10px;
}
}
}
}
.tabs-container {
display: flex;
align-items: center;
height: 42px;
justify-content: space-between;
.tabs-content {
text-align: center;
.tabs-content-item {
cursor: pointer;
margin-right: 5px;
background-color: #eee;
padding: 5px;
border-radius: 5px;
}
.tabs-content-item-active {
background-color: #579572;
color: #eee;
}
}
.tabs-close {
cursor: pointer;
}
}
</style>

@ -4,13 +4,13 @@
background-color="#304156"
:default-active="$router.currentRoute.value.path"
text-color="#fff"
unique-opened="true"
:unique-opened="true"
:collapse="store.collapse"
router="true"
:router="true"
class="side-nav-bar"
>
<!-- 首页栏 -->
<el-menu-item index="/">
<el-menu-item index="/home">
<el-icon><HomeFilled /></el-icon>
<span>首页</span>
</el-menu-item>

@ -1,28 +0,0 @@
<template>
<el-dropdown>
<span class="el-dropdown-link">
<el-avatar :src="circleUrl" />
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="logout">退</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const circleUrl = ref('https://liuyxcc.github.io/img/avatar.png')
const logout = () => {
localStorage.clear()
router.replace('/login')
console.log('logout')
}
</script>
<style lang="scss" scoped></style>

@ -1,28 +0,0 @@
<template>
<div class="hamburger-container" @click="toggleCollapse">
<el-icon size="25px">
<component :is="icon"></component>
</el-icon>
</div>
</template>
<script setup>
import { useStore } from '../../../store'
import { computed } from 'vue'
const store = useStore()
const icon = computed(() => {
return !store.collapse ? 'Fold' : 'Expand'
})
const toggleCollapse = () => {
store.changeCollapse()
}
</script>
<style lang="scss" scoped>
.hamburger-container {
cursor: pointer;
}
</style>

@ -1,22 +0,0 @@
<template>
<el-icon size="25px" class="screenfull" @click="toggleScreen">
<FullScreen />
</el-icon>
</template>
<script setup>
import screenfull from 'screenfull'
// screenfull
const toggleScreen = () => {
if (screenfull.isEnabled) {
screenfull.toggle()
}
}
</script>
<style lang="scss" scoped>
.screenfull {
cursor: pointer;
}
</style>

@ -1,40 +0,0 @@
<template>
<div class="header-container">
<div class="navbar">
<Hamburger />
<div class="right-navbar">
<Screenfull />
<Avatar class="right-item" />
</div>
</div>
<div class="tabs-view-container">tabs</div>
</div>
</template>
<script setup>
import Hamburger from './components/Hamburger.vue'
import Avatar from './components/Avatar.vue'
import Screenfull from './components/Screenfull.vue'
</script>
<style lang="scss" scoped>
.header-container {
padding: 0 10px;
height: 100%;
box-shadow: 0 2px 3px #888888;
height: 60px;
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
.right-navbar {
display: flex;
justify-content: center;
align-items: center;
.right-item {
margin-left: 10px;
}
}
}
}
</style>

@ -6,17 +6,22 @@
<el-container :class="'main-container ' + isHide">
<el-header><Header /></el-header>
<el-main>
<transition name="fade" mode="out-in">
<!-- <transition name="fade" mode="out-in">
<router-view class="m-content"></router-view>
</transition> -->
<router-view class="m-content" v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</el-main>
</el-container>
</el-container>
</template>
<script setup>
import Menu from './menu/Menu.vue'
import Header from './header/index.vue'
import Menu from './components/Menu.vue'
import Header from './components/Header.vue'
import { useStore } from '../store'
import { computed } from 'vue'

@ -1,5 +1,4 @@
import { createApp } from 'vue'
// import './style.css'
import { createApp, h } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
@ -7,6 +6,7 @@ import './assets/scss/index.scss'
import router from './router'
import { createPinia } from 'pinia'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import piniaPersist from 'pinia-plugin-persist'
// 引入md编辑器
import VueMarkdownEditor from '@kangc/v-md-editor'
@ -15,9 +15,12 @@ import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js'
import '@kangc/v-md-editor/lib/theme/style/vuepress.css'
import Prism from 'prismjs'
const app = createApp(App)
const app = createApp({
render: () => h(App)
})
const pinia = createPinia()
pinia.use(piniaPersist)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)

@ -9,34 +9,42 @@ const routes = [
children: [
{
path: '/home',
name: '首页',
component: () => import('@/views/home/Home.vue')
},
{
path: '/article',
name: '发布文章',
component: () => import('@/views/article/Article.vue')
},
{
path: '/article-list',
name: '文章列表',
component: () => import('@/views/article/ArticleList.vue')
},
{
path: '/tags',
name: '标签管理',
component: () => import('@/views/tag/Tag.vue')
},
{
path: '/categories',
name: '分类管理',
component: () => import('@/views/category/Category.vue')
},
{
path: '/page',
name: '页面图片',
component: () => import('@/views/page/Page.vue')
},
{
path: '/about',
name: '关于我',
component: () => import('@/views/about/About.vue')
},
{
path: '/users',
name: '用户列表',
component: () => import('@/views/user/User.vue')
}
]

@ -3,11 +3,28 @@ import { defineStore } from 'pinia'
export const useStore = defineStore('store', {
state: () => ({
collapse: false,
token: localStorage.getItem('token') || ''
token: localStorage.getItem('token') || '',
tabList: [{ name: '首页', path: '/home' }]
}),
actions: {
changeCollapse() {
this.collapse = !this.collapse
},
resetTab() {
// 重置tab列表
this.tabList = [{ name: '首页', path: '/home' }]
},
removeTab(item) {
this.tabList.splice(
this.tabList.findIndex((it) => it.name === item.name),
1
)
}
},
persist: {
enabled: true,
strategies: [
{ storage: sessionStorage, paths: ['collapse', 'tabList'] }
]
}
})

Loading…
Cancel
Save