diff --git a/components.d.ts b/components.d.ts index d60b6d3..c1c443d 100644 --- a/components.d.ts +++ b/components.d.ts @@ -9,10 +9,7 @@ declare module '@vue/runtime-core' { export interface GlobalComponents { ElAlert: typeof import('element-plus/es')['ElAlert'] ElAside: typeof import('element-plus/es')['ElAside'] - ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] - ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] ElButton: typeof import('element-plus/es')['ElButton'] - ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup'] ElCol: typeof import('element-plus/es')['ElCol'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElContainer: typeof import('element-plus/es')['ElContainer'] @@ -37,6 +34,8 @@ declare module '@vue/runtime-core' { ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] + ElTabPane: typeof import('element-plus/es')['ElTabPane'] + ElTabs: typeof import('element-plus/es')['ElTabs'] ElTag: typeof import('element-plus/es')['ElTag'] ElTransfer: typeof import('element-plus/es')['ElTransfer'] ElTree: typeof import('element-plus/es')['ElTree'] diff --git a/src/config/menu.ts b/src/config/menu.ts index de8cc18..4c35001 100644 --- a/src/config/menu.ts +++ b/src/config/menu.ts @@ -20,12 +20,5 @@ export default [ { title: '图片资源库', path: '/api/sourceImage' }, { title: '歌曲库', path: '/api/music' } ] - },{ - name: 'tool', - title: '工具', - icon: 'Tools', - child: [ - { title: 'SQL占位符替换', path: '/tool/sqlReplace' } - ] } ] \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index e6ef261..98b8aa9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,6 @@ import { createApp } from 'vue' import App from './App.vue' -import { router, routePathes, filterExclude } from './router' +import { router, filterExclude } from './router' import store from './store' import VueAxios from 'vue-axios' @@ -50,7 +50,10 @@ service.interceptors.response.use(res=> { // 全局路由导航前置守卫 router.beforeEach(function (to, from, next) { - mountedApp.$store.commit('setBreadcrumb', routePathes[to.path] || []) + if (to.meta?.title) { + mountedApp.$store.commit('addTab', { title: to.meta.title, path: to.path, name: to.name }) + } + mountedApp.$store.state.activeTab = to.path if(filterExclude.indexOf(to.path) !== -1 || mountedApp.$store.state.loginInfo.token) { next() } else { diff --git a/src/router.ts b/src/router.ts index f1929a9..d49780a 100644 --- a/src/router.ts +++ b/src/router.ts @@ -8,17 +8,16 @@ const routes: Array = [ { path: '/login', name: 'Login', component: Login }, { path: '/', name: 'Home', component: Home, children: [ { path: '/', name: 'Welcome', component: Welcome }, - { path: '/system/user', name: 'SystemUser', component: () => import( /* webpackChunkName: "system" */ '@/views/system/SystemUser.vue') }, - { path: '/system/role', name: 'SystemRole', component: () => import( /* webpackChunkName: "system" */ '@/views/system/SystemRole.vue') }, - { path: '/system/config', name: 'SystemConfig', component: () => import( /* webpackChunkName: "system" */ '@/views/system/SystemConfig.vue') }, - { path: '/system/article', name: 'Article', component: () => import( /* webpackChunkName: "system" */ '@/views/system/Article.vue') }, - { path: '/system/statistics', name: 'Statistics', component: () => import( /* webpackChunkName: "system" */ '@/views/system/Statistics.vue') }, + { path: '/system/user', name: 'SystemUser', component: () => import( /* webpackChunkName: "system" */ '@/views/system/SystemUser.vue'), meta: { title: '用户管理' } }, + { path: '/system/role', name: 'SystemRole', component: () => import( /* webpackChunkName: "system" */ '@/views/system/SystemRole.vue'), meta: { title: '角色管理' } }, + { path: '/system/config', name: 'SystemConfig', component: () => import( /* webpackChunkName: "system" */ '@/views/system/SystemConfig.vue'), meta: { title: '系统配置' } }, + { path: '/system/article', name: 'Article', component: () => import( /* webpackChunkName: "system" */ '@/views/system/Article.vue'), meta: { title: '博客文章' } }, + { path: '/system/statistics', name: 'Statistics', component: () => import( /* webpackChunkName: "system" */ '@/views/system/Statistics.vue'), meta: { title: '分析统计' } }, - { path: '/api/music', name: 'Music', component: () => import( /* webpackChunkName: "api" */ '@/views/api/Music.vue') }, - { path: '/api/hitokoto', name: 'Hitokoto', component: () => import( /* webpackChunkName: "api" */ '@/views/api/Hitokoto.vue') }, - { path: '/api/photoWall', name: 'PhotoWall', component: () => import( /* webpackChunkName: "api" */ '@/views/api/PhotoWall.vue') }, - { path: '/api/sourceImage', name: 'SourceImage', component: () => import( /* webpackChunkName: "api" */ '@/views/api/SourceImage.vue') }, - { path: '/tool/sqlReplace', name: 'SqlReplace', component: () => import( /* webpackChunkName: "api" */ '@/views/tool/SqlReplace.vue') }, + { path: '/api/music', name: 'Music', component: () => import( /* webpackChunkName: "api" */ '@/views/api/Music.vue'), meta: { title: '歌曲库' } }, + { path: '/api/hitokoto', name: 'Hitokoto', component: () => import( /* webpackChunkName: "api" */ '@/views/api/Hitokoto.vue'), meta: { title: '一言' } }, + { path: '/api/photoWall', name: 'PhotoWall', component: () => import( /* webpackChunkName: "api" */ '@/views/api/PhotoWall.vue'), meta: { title: '照片墙' } }, + { path: '/api/sourceImage', name: 'SourceImage', component: () => import( /* webpackChunkName: "api" */ '@/views/api/SourceImage.vue'), meta: { title: '图片资源库' } }, ]} ] @@ -27,14 +26,4 @@ export const router = createRouter({ routes }) -import menus from './config/menu' -export const routePathes : {[propName: string]: string[]} = { - '/': ['首页'], -} -for(let menu of menus) { - for(let submenu of menu.child) { - routePathes[submenu.path] = ['首页', menu.title, submenu.title] - } -} - export const filterExclude = ['/login'] \ No newline at end of file diff --git a/src/static/common.less b/src/static/common.less index 235665a..62ecf82 100644 --- a/src/static/common.less +++ b/src/static/common.less @@ -1,4 +1,3 @@ -@panel-shadow-color: #ddd; html,body,#app,.layout { margin: 0; padding: 0; @@ -25,14 +24,14 @@ html,body,#app,.layout { padding: 10px !important; display: flex !important; flex-direction: column; - > .layout-breadcrumb{ - padding: 10px 15px 0; + > .layout-tabs{ + margin: 15px 15px 0 15px; } > .layout-content{ - margin: 15px; + margin: 0 15px 15px 15px; overflow: auto; background: #fff; - box-shadow: -2px -2px 5px 0px @panel-shadow-color; + border: 1px solid #e1e1e1; padding: 10px; flex-grow: 1; flex-basis: 0; @@ -74,4 +73,8 @@ html,body,#app,.layout { > .el-drawer__body { overflow: auto; } +} +.el-tabs--card.nav-tabs > .el-tabs__header { + margin-bottom: 0; + border-bottom: none; } \ No newline at end of file diff --git a/src/store/index.ts b/src/store/index.ts index 793f6a4..3972484 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,16 +1,17 @@ import { createStore } from 'vuex' -import { StateType, UserInfo } from './types' +import { StateType, UserInfo, TabItem } from './types' class Store { - constructor(breadcrumb: string[]) { - this.state.breadcrumb = breadcrumb + constructor(tabs: TabItem[]) { + this.state.tabs = tabs } state: StateType = { loginInfo: { userInfo: null, token: localStorage.getItem('login_token') }, - breadcrumb: [], + tabs: [], + activeTab: null, pageSizeOpts: [10, 20, 50, 100], pageLayout: 'sizes, prev, pager, next, total, ->, jumper' } @@ -35,16 +36,30 @@ class Store { state.loginInfo.userInfo = null }, /** - * 设置面包屑导航 + * 添加导航页签 * @param {Object} state - * @param {Array} breadcrumbArr 面包屑导航的内容 + * @param {TabItem} tab 新加入的页签 */ - setBreadcrumb(state: StateType, breadcrumbArr: string[]): void { - localStorage.setItem('breadcrumb', JSON.stringify(breadcrumbArr)) - state.breadcrumb = breadcrumbArr + addTab(state: StateType, tab: TabItem): void { + if (state.tabs.findIndex(item => item.path === tab.path) === -1) { + state.tabs.push(tab) + localStorage.setItem('tabs', JSON.stringify(state.tabs)) + } + }, + /** + * 移除导航页签 + * @param {Object} state + * @param {TabItem} name 页签标识名称 + */ + removeTab(state: StateType, path: string): void { + const removeIndex = state.tabs.findIndex(item => item.path === path) + if (removeIndex !== -1) { + state.tabs.splice(removeIndex, 1) + localStorage.setItem('tabs', JSON.stringify(state.tabs)) + } } } } -const breadcrumbStr = localStorage.getItem('breadcrumb') -export default createStore(new Store(breadcrumbStr ? JSON.parse(breadcrumbStr) : [])) \ No newline at end of file +const tabsStr = localStorage.getItem('tabs') +export default createStore(new Store(tabsStr ? JSON.parse(tabsStr) : [])) \ No newline at end of file diff --git a/src/store/types.ts b/src/store/types.ts index db72ec4..7281e35 100644 --- a/src/store/types.ts +++ b/src/store/types.ts @@ -5,12 +5,19 @@ export interface UserInfo { role_ids: string[] // 角色ID } +export interface TabItem { + title: string // 标题文字 + path: string // 路由地址 + name: string // 组件名称 +} + export interface StateType { loginInfo: { // 登录信息 userInfo: null | UserInfo token: null | string } - breadcrumb: string[] // 面包屑导航文字 + tabs: TabItem[] // 导航页签 + activeTab: string | null // 当前激活的页签 pageSizeOpts: number[] // 分页大小可选列表 pageLayout: string // 分页工具栏 } \ No newline at end of file diff --git a/src/views/Home.vue b/src/views/Home.vue index 6425be5..2c8a7f4 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -21,7 +21,7 @@ - +