mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 04:39:12 +08:00
feat: persistent save tab, fix #359
This commit is contained in:
@@ -13,6 +13,10 @@
|
|||||||
- 移除 `useDebounceFn` 使用`vueuse`-`useDebounceFn`代替
|
- 移除 `useDebounceFn` 使用`vueuse`-`useDebounceFn`代替
|
||||||
- 移除 `useThrottle` 使用`vueuse`-`useThrottleFn`代替
|
- 移除 `useThrottle` 使用`vueuse`-`useThrottleFn`代替
|
||||||
|
|
||||||
|
### ✨ Features
|
||||||
|
|
||||||
|
- 标签页支持持久化保存
|
||||||
|
|
||||||
### ✨ Refactor
|
### ✨ Refactor
|
||||||
|
|
||||||
- 移除 `useElResize`
|
- 移除 `useElResize`
|
||||||
|
@@ -40,7 +40,7 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
|
|||||||
// black: '#0e1117',
|
// black: '#0e1117',
|
||||||
// #8b949e
|
// #8b949e
|
||||||
'text-color-secondary': '#8b949e',
|
'text-color-secondary': '#8b949e',
|
||||||
// 'border-color-base': '#30363d',
|
'border-color-base': '#303030',
|
||||||
// 'border-color-split': '#30363d',
|
// 'border-color-split': '#30363d',
|
||||||
'item-active-bg': '#111b26',
|
'item-active-bg': '#111b26',
|
||||||
},
|
},
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex px-2 py-1.5 items-center border-b-1">
|
<div class="flex px-2 py-1.5 items-center basic-tree-header">
|
||||||
<slot name="headerTitle" v-if="$slots.headerTitle"></slot>
|
<slot name="headerTitle" v-if="$slots.headerTitle"></slot>
|
||||||
<BasicTitle :helpMessage="helpMessage" v-if="!$slots.headerTitle && title">
|
<BasicTitle :helpMessage="helpMessage" v-if="!$slots.headerTitle && title">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
@@ -138,3 +138,8 @@
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.basic-tree-header {
|
||||||
|
border-bottom: 1px solid @border-color-base;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@@ -15,6 +15,8 @@ export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__';
|
|||||||
// lock info
|
// lock info
|
||||||
export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__';
|
export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__';
|
||||||
|
|
||||||
|
export const MULTIPLE_TABS_KEY = 'MULTIPLE_TABS__KEY__';
|
||||||
|
|
||||||
export const APP_DARK_MODE_KEY_ = '__APP__DARK__MODE__';
|
export const APP_DARK_MODE_KEY_ = '__APP__DARK__MODE__';
|
||||||
|
|
||||||
// base global local key
|
// base global local key
|
||||||
|
@@ -10,7 +10,7 @@ const page: AppRouteModule = {
|
|||||||
path: '/page-demo',
|
path: '/page-demo',
|
||||||
name: 'PageDemo',
|
name: 'PageDemo',
|
||||||
component: LAYOUT,
|
component: LAYOUT,
|
||||||
redirect: '/page-demo/exception',
|
redirect: '/page-demo/form/basic',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'ion:aperture-outline',
|
icon: 'ion:aperture-outline',
|
||||||
title: t('routes.demo.page.page'),
|
title: t('routes.demo.page.page'),
|
||||||
|
@@ -114,13 +114,13 @@ const setting: ProjectConfig = {
|
|||||||
|
|
||||||
// Multi-label
|
// Multi-label
|
||||||
multiTabsSetting: {
|
multiTabsSetting: {
|
||||||
|
cache: false,
|
||||||
// Turn on
|
// Turn on
|
||||||
show: true,
|
show: true,
|
||||||
// Is it possible to drag and drop sorting tabs
|
// Is it possible to drag and drop sorting tabs
|
||||||
canDrag: true,
|
canDrag: true,
|
||||||
// Turn on quick actions
|
// Turn on quick actions
|
||||||
showQuick: true,
|
showQuick: true,
|
||||||
|
|
||||||
// Whether to show the refresh button
|
// Whether to show the refresh button
|
||||||
showRedo: true,
|
showRedo: true,
|
||||||
// Whether to show the collapse button
|
// Whether to show the collapse button
|
||||||
|
@@ -12,7 +12,7 @@ import { resetRouter } from '/@/router';
|
|||||||
import { deepMerge } from '/@/utils';
|
import { deepMerge } from '/@/utils';
|
||||||
|
|
||||||
interface AppState {
|
interface AppState {
|
||||||
darkMode: ThemeEnum;
|
darkMode?: ThemeEnum;
|
||||||
// Page loading status
|
// Page loading status
|
||||||
pageLoading: boolean;
|
pageLoading: boolean;
|
||||||
// project config
|
// project config
|
||||||
@@ -24,7 +24,7 @@ let timeId: TimeoutHandle;
|
|||||||
export const useAppStore = defineStore({
|
export const useAppStore = defineStore({
|
||||||
id: 'app',
|
id: 'app',
|
||||||
state: (): AppState => ({
|
state: (): AppState => ({
|
||||||
darkMode: ThemeEnum.LIGHT,
|
darkMode: undefined,
|
||||||
pageLoading: false,
|
pageLoading: false,
|
||||||
projectConfig: Persistent.getLocal(PROJ_CFG_KEY),
|
projectConfig: Persistent.getLocal(PROJ_CFG_KEY),
|
||||||
beforeMiniInfo: {},
|
beforeMiniInfo: {},
|
||||||
|
@@ -5,10 +5,14 @@ import { defineStore } from 'pinia';
|
|||||||
import { store } from '/@/store';
|
import { store } from '/@/store';
|
||||||
|
|
||||||
import { useGo, useRedo } from '/@/hooks/web/usePage';
|
import { useGo, useRedo } from '/@/hooks/web/usePage';
|
||||||
|
import { Persistent } from '/@/utils/cache/persistent';
|
||||||
|
|
||||||
import { PageEnum } from '/@/enums/pageEnum';
|
import { PageEnum } from '/@/enums/pageEnum';
|
||||||
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
|
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
|
||||||
import { getRawRoute } from '/@/utils';
|
import { getRawRoute } from '/@/utils';
|
||||||
|
import { MULTIPLE_TABS_KEY } from '/@/enums/cacheEnum';
|
||||||
|
|
||||||
|
import projectSetting from '/@/settings/projectSetting';
|
||||||
|
|
||||||
export interface MultipleTabState {
|
export interface MultipleTabState {
|
||||||
cacheTabList: Set<string>;
|
cacheTabList: Set<string>;
|
||||||
@@ -21,13 +25,15 @@ function handleGotoPage(router: Router) {
|
|||||||
go(unref(router.currentRoute).path, true);
|
go(unref(router.currentRoute).path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cacheTab = projectSetting.multiTabsSetting.cache;
|
||||||
|
|
||||||
export const useMultipleTabStore = defineStore({
|
export const useMultipleTabStore = defineStore({
|
||||||
id: 'app-multiple-tab',
|
id: 'app-multiple-tab',
|
||||||
state: (): MultipleTabState => ({
|
state: (): MultipleTabState => ({
|
||||||
// Tabs that need to be cached
|
// Tabs that need to be cached
|
||||||
cacheTabList: new Set(),
|
cacheTabList: new Set(),
|
||||||
// multiple tab list
|
// multiple tab list
|
||||||
tabList: [],
|
tabList: cacheTab ? Persistent.getLocal(MULTIPLE_TABS_KEY) || [] : [],
|
||||||
// Index of the last moved tab
|
// Index of the last moved tab
|
||||||
lastDragEndIndex: 0,
|
lastDragEndIndex: 0,
|
||||||
}),
|
}),
|
||||||
@@ -135,6 +141,7 @@ export const useMultipleTabStore = defineStore({
|
|||||||
// Add tab
|
// Add tab
|
||||||
this.tabList.push(route);
|
this.tabList.push(route);
|
||||||
this.updateCacheTab();
|
this.updateCacheTab();
|
||||||
|
cacheTab && Persistent.setLocal(MULTIPLE_TABS_KEY, this.tabList);
|
||||||
},
|
},
|
||||||
|
|
||||||
async closeTab(tab: RouteLocationNormalized, router: Router) {
|
async closeTab(tab: RouteLocationNormalized, router: Router) {
|
||||||
|
3
src/utils/cache/persistent.ts
vendored
3
src/utils/cache/persistent.ts
vendored
@@ -1,5 +1,6 @@
|
|||||||
import type { LockInfo, UserInfo } from '/#/store';
|
import type { LockInfo, UserInfo } from '/#/store';
|
||||||
import type { ProjectConfig } from '/#/config';
|
import type { ProjectConfig } from '/#/config';
|
||||||
|
import type { RouteLocationNormalized } from 'vue-router';
|
||||||
|
|
||||||
import { createLocalStorage, createSessionStorage } from '/@/utils/cache';
|
import { createLocalStorage, createSessionStorage } from '/@/utils/cache';
|
||||||
import { Memory } from './memory';
|
import { Memory } from './memory';
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
PROJ_CFG_KEY,
|
PROJ_CFG_KEY,
|
||||||
APP_LOCAL_CACHE_KEY,
|
APP_LOCAL_CACHE_KEY,
|
||||||
APP_SESSION_CACHE_KEY,
|
APP_SESSION_CACHE_KEY,
|
||||||
|
MULTIPLE_TABS_KEY,
|
||||||
} from '/@/enums/cacheEnum';
|
} from '/@/enums/cacheEnum';
|
||||||
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
|
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
|
||||||
import { toRaw } from 'vue';
|
import { toRaw } from 'vue';
|
||||||
@@ -21,6 +23,7 @@ interface BasicStore {
|
|||||||
[ROLES_KEY]: string[];
|
[ROLES_KEY]: string[];
|
||||||
[LOCK_INFO_KEY]: LockInfo;
|
[LOCK_INFO_KEY]: LockInfo;
|
||||||
[PROJ_CFG_KEY]: ProjectConfig;
|
[PROJ_CFG_KEY]: ProjectConfig;
|
||||||
|
[MULTIPLE_TABS_KEY]: RouteLocationNormalized[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocalStore = BasicStore;
|
type LocalStore = BasicStore;
|
||||||
|
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
<div class="max-h-80 overflow-auto">
|
<div class="max-h-80 overflow-auto">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="item in getList" class="border-b-1 mt-2" :key="item.time">
|
<li v-for="item in getList" class="mt-2" :key="item.time">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="mr-2 text-primary font-medium">收到消息:</span>
|
<span class="mr-2 text-primary font-medium">收到消息:</span>
|
||||||
<span>{{ formatToDateTime(item.time) }}</span>
|
<span>{{ formatToDateTime(item.time) }}</span>
|
||||||
|
1
types/config.d.ts
vendored
1
types/config.d.ts
vendored
@@ -33,6 +33,7 @@ export interface MenuSetting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MultiTabsSetting {
|
export interface MultiTabsSetting {
|
||||||
|
cache: boolean;
|
||||||
show: boolean;
|
show: boolean;
|
||||||
showQuick: boolean;
|
showQuick: boolean;
|
||||||
canDrag: boolean;
|
canDrag: boolean;
|
||||||
|
Reference in New Issue
Block a user