Template
1
0
mirror of https://github.com/un-pany/v3-admin-vite.git synced 2025-04-21 11:29:20 +08:00

feat: 新增 keep alive 缓存功能

This commit is contained in:
pany 2023-02-22 15:53:04 +08:00
parent 95b9691b20
commit 15fa81f5fe
13 changed files with 155 additions and 10 deletions

View File

@ -10,7 +10,9 @@ module.exports = {
defineProps: "readonly",
defineEmits: "readonly",
defineExpose: "readonly",
withDefaults: "readonly"
withDefaults: "readonly",
// unplugin-vue-define-options
defineOptions: "readonly"
},
extends: [
"plugin:vue/vue3-essential",

View File

@ -66,6 +66,7 @@
"terser": "^5.16.4",
"typescript": "^4.9.5",
"unocss": "^0.49.7",
"unplugin-vue-define-options": "^1.2.2",
"vite": "^4.1.2",
"vite-plugin-svg-icons": "^2.0.1",
"vite-svg-loader": "^4.0.0",

57
pnpm-lock.yaml generated
View File

@ -36,6 +36,7 @@ specifiers:
terser: ^5.16.4
typescript: ^4.9.5
unocss: ^0.49.7
unplugin-vue-define-options: ^1.2.2
vite: ^4.1.2
vite-plugin-svg-icons: ^2.0.1
vite-svg-loader: ^4.0.0
@ -91,6 +92,7 @@ devDependencies:
terser: 5.16.4
typescript: 4.9.5
unocss: 0.49.7_vite@4.1.2
unplugin-vue-define-options: 1.2.2_vue@3.2.47
vite: 4.1.2_3ujtmoa5y2j7mu6cb7nhm4axba
vite-plugin-svg-icons: 2.0.1_vite@4.1.2
vite-svg-loader: 4.0.0
@ -1231,6 +1233,22 @@ packages:
'@volar/vue-language-core': 1.1.4
dev: true
/@vue-macros/common/1.0.1_vue@3.2.47:
resolution: {integrity: sha512-61rD1NEqSwTJaZgHwOr//nyfWNow6dFdcuTJegOKiKY+Y4Xu+uTsBDhHDo+M7Rp+ZxjCS6DNThn24wP+8e+QmA==}
engines: {node: '>=14.19.0'}
peerDependencies:
vue: ^2.7.0 || ^3.2.25
peerDependenciesMeta:
vue:
optional: true
dependencies:
'@babel/types': 7.20.7
'@vue/compiler-sfc': 3.2.47
local-pkg: 0.4.3
magic-string: 0.29.0
vue: 3.2.47
dev: true
/@vue/babel-helper-vue-transform-on/1.0.2:
resolution: {integrity: sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==}
dev: true
@ -1556,6 +1574,14 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/ast-walker-scope/0.4.0:
resolution: {integrity: sha512-THVisYmmqkcopZXJDniGgVW6BRKtjutRLytqAgw0XDabYZmxC0GfFggTFZouMhvNT7jPBkx0vOy/2Y+udCDwgg==}
engines: {node: '>=14.19.0'}
dependencies:
'@babel/parser': 7.20.15
'@babel/types': 7.20.7
dev: true
/astral-regex/2.0.0:
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
engines: {node: '>=8'}
@ -4797,6 +4823,28 @@ packages:
- vite
dev: true
/unplugin-vue-define-options/1.2.2_vue@3.2.47:
resolution: {integrity: sha512-7LWYNqESu6uXZARwKOs6nhQY7l+UHkW2rtpeGv7iAYFeUp1dk1Esu5qzT2arZ93Wt25fL6HfLBQp3hCkov2d2A==}
engines: {node: '>=14.19.0'}
dependencies:
'@rollup/pluginutils': 5.0.2
'@vue-macros/common': 1.0.1_vue@3.2.47
ast-walker-scope: 0.4.0
unplugin: 1.1.0
transitivePeerDependencies:
- rollup
- vue
dev: true
/unplugin/1.1.0:
resolution: {integrity: sha512-I8obQ8Rs/hnkxokRV6g8JKOQFgYNnTd9DL58vcSt5IJ9AkK8wbrtsnzD5hi4BJlvcY536JzfEXj9L6h7j559/A==}
dependencies:
acorn: 8.8.2
chokidar: 3.5.3
webpack-sources: 3.2.3
webpack-virtual-modules: 0.5.0
dev: true
/unset-value/1.0.0:
resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==}
engines: {node: '>=0.10.0'}
@ -5087,6 +5135,15 @@ packages:
engines: {node: '>=12'}
dev: true
/webpack-sources/3.2.3:
resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
engines: {node: '>=10.13.0'}
dev: true
/webpack-virtual-modules/0.5.0:
resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
dev: true
/whatwg-encoding/2.0.0:
resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
engines: {node: '>=12'}

View File

@ -1,8 +1,11 @@
<script lang="ts" setup>
import { computed } from "vue"
import { useRoute } from "vue-router"
import { useTagsViewStore } from "@/store/modules/tags-view"
const route = useRoute()
const tagsViewStore = useTagsViewStore()
const key = computed(() => {
return route.path
})
@ -12,9 +15,9 @@ const key = computed(() => {
<section class="app-main">
<router-view v-slot="{ Component }">
<transition name="fade-transform" mode="out-in">
<!-- <keep-alive> -->
<component :is="Component" :key="key" />
<!-- </keep-alive> -->
<keep-alive :include="tagsViewStore.cachedViews">
<component :is="Component" :key="key" />
</keep-alive>
</transition>
</router-view>
</section>

View File

@ -62,15 +62,18 @@ const initTags = () => {
const addTags = () => {
if (route.name) {
tagsViewStore.addVisitedView(route)
tagsViewStore.addCachedView(route)
}
}
const refreshSelectedTag = (view: ITagView) => {
tagsViewStore.delCachedView(view)
router.replace({ path: "/redirect" + view.path, query: view.query })
}
const closeSelectedTag = (view: ITagView) => {
tagsViewStore.delVisitedView(view)
tagsViewStore.delCachedView(view)
if (isActive(view)) {
toLastView(tagsViewStore.visitedViews, view)
}
@ -81,10 +84,12 @@ const closeOthersTags = () => {
router.push(selectedTag.value.fullPath)
}
tagsViewStore.delOthersVisitedViews(selectedTag.value)
tagsViewStore.delOthersCachedViews(selectedTag.value)
}
const closeAllTags = (view: ITagView) => {
tagsViewStore.delAllVisitedViews()
tagsViewStore.delAllCachedViews()
if (affixTags.some((tag) => tag.path === route.path)) {
return
}

View File

@ -102,7 +102,8 @@ export const constantRoutes: RouteRecordRaw[] = [
component: () => import("@/views/table/element-plus/index.vue"),
name: "ElementPlus",
meta: {
title: "Element Plus"
title: "Element Plus",
keepAlive: true
}
},
{
@ -110,7 +111,8 @@ export const constantRoutes: RouteRecordRaw[] = [
component: () => import("@/views/table/vxe-table/index.vue"),
name: "VxeTable",
meta: {
title: "Vxe Table"
title: "Vxe Table",
keepAlive: true
}
}
]

View File

@ -6,7 +6,9 @@ export type ITagView = Partial<RouteLocationNormalized>
export const useTagsViewStore = defineStore("tags-view", () => {
const visitedViews = ref<ITagView[]>([])
const cachedViews = ref<string[]>([])
//#region add
const addVisitedView = (view: ITagView) => {
if (
visitedViews.value.some((v, index) => {
@ -23,6 +25,16 @@ export const useTagsViewStore = defineStore("tags-view", () => {
}
visitedViews.value.push(Object.assign({}, view))
}
const addCachedView = (view: ITagView) => {
if (typeof view.name !== "string") return
if (cachedViews.value.includes(view.name)) return
if (view.meta?.keepAlive) {
cachedViews.value.push(view.name)
}
}
//#endregion
//#region del
const delVisitedView = (view: ITagView) => {
for (const [i, v] of visitedViews.value.entries()) {
if (v.path === view.path) {
@ -31,16 +43,52 @@ export const useTagsViewStore = defineStore("tags-view", () => {
}
}
}
const delCachedView = (view: ITagView) => {
if (typeof view.name !== "string") return
const index = cachedViews.value.indexOf(view.name)
index > -1 && cachedViews.value.splice(index, 1)
}
//#endregion
//#region delOthers
const delOthersVisitedViews = (view: ITagView) => {
visitedViews.value = visitedViews.value.filter((v) => {
return v.meta?.affix || v.path === view.path
})
}
const delOthersCachedViews = (view: ITagView) => {
if (typeof view.name !== "string") return
const index = cachedViews.value.indexOf(view.name)
if (index > -1) {
cachedViews.value = cachedViews.value.slice(index, index + 1)
} else {
// 如果 index = -1, 没有缓存的 tags
cachedViews.value = []
}
}
//#endregion
//#region delAll
const delAllVisitedViews = () => {
// keep affix tags
const affixTags = visitedViews.value.filter((tag) => tag.meta?.affix)
visitedViews.value = affixTags
}
const delAllCachedViews = () => {
cachedViews.value = []
}
//#endregion
return { visitedViews, addVisitedView, delVisitedView, delOthersVisitedViews, delAllVisitedViews }
return {
visitedViews,
cachedViews,
addVisitedView,
addCachedView,
delVisitedView,
delCachedView,
delOthersVisitedViews,
delOthersCachedViews,
delAllVisitedViews,
delAllCachedViews
}
})

View File

@ -2,6 +2,7 @@ import { ref } from "vue"
import store from "@/store"
import { defineStore } from "pinia"
import { usePermissionStore } from "./permission"
import { useTagsViewStore } from "./tags-view"
import { getToken, removeToken, setToken } from "@/utils/cache/cookies"
import router, { resetRouter } from "@/router"
import { loginApi, getUserInfoApi } from "@/api/login"
@ -13,6 +14,9 @@ export const useUserStore = defineStore("user", () => {
const roles = ref<string[]>([])
const username = ref<string>("")
const permissionStore = usePermissionStore()
const tagsViewStore = useTagsViewStore()
/** 设置角色数组 */
const setRoles = (value: string[]) => {
roles.value = value
@ -55,12 +59,12 @@ export const useUserStore = defineStore("user", () => {
token.value = newToken
setToken(newToken)
await getInfo()
const permissionStore = usePermissionStore()
permissionStore.setRoutes(roles.value)
resetRouter()
permissionStore.dynamicRoutes.forEach((item: RouteRecordRaw) => {
router.addRoute(item)
})
_resetTagsView()
}
/** 登出 */
const logout = () => {
@ -68,6 +72,7 @@ export const useUserStore = defineStore("user", () => {
token.value = ""
roles.value = []
resetRouter()
_resetTagsView()
}
/** 重置 Token */
const resetToken = () => {
@ -75,6 +80,11 @@ export const useUserStore = defineStore("user", () => {
token.value = ""
roles.value = []
}
/** 重置 visited views 和 cached views */
const _resetTagsView = () => {
tagsViewStore.delAllVisitedViews()
tagsViewStore.delAllCachedViews()
}
return { token, roles, username, setRoles, login, getInfo, changeRoles, logout, resetToken }
})

View File

@ -5,6 +5,10 @@ import { type FormInstance, type FormRules, ElMessage, ElMessageBox } from "elem
import { Search, Refresh, CirclePlus, Delete, Download, RefreshRight } from "@element-plus/icons-vue"
import { usePagination } from "@/hooks/usePagination"
defineOptions({
name: "ElementPlus"
})
const loading = ref<boolean>(false)
const { paginationData, handleCurrentChange, handleSizeChange } = usePagination()

View File

@ -15,6 +15,10 @@ import {
type VxeFormDefines
} from "vxe-table"
defineOptions({
name: "VxeTable"
})
//#region vxe-grid
interface IRowMeta {
id: string

View File

@ -24,7 +24,8 @@
/** Element Plus Volar */
"element-plus/global",
"vitest",
"vitest/globals"
"vitest/globals",
"unplugin-vue-define-options/macros-global"
],
/** baseUrl 使 */
"baseUrl": ".",

View File

@ -43,5 +43,10 @@ declare module "vue-router" {
* 使 hidden: true
*/
activeMenu?: string
/**
*
* false true Name
*/
keepAlive?: boolean
}
}

View File

@ -7,6 +7,7 @@ import vueJsx from "@vitejs/plugin-vue-jsx"
import { createSvgIconsPlugin } from "vite-plugin-svg-icons"
import svgLoader from "vite-svg-loader"
import UnoCSS from "unocss/vite"
import DefineOptions from "unplugin-vue-define-options/vite"
/** 配置项文档https://cn.vitejs.dev/config */
export default (configEnv: ConfigEnv): UserConfigExport => {
@ -77,7 +78,9 @@ export default (configEnv: ConfigEnv): UserConfigExport => {
symbolId: "icon-[dir]-[name]"
}),
/** UnoCSS */
UnoCSS()
UnoCSS(),
/** DefineOptions 可以更简单的注册组件名称 */
DefineOptions()
/** 自动按需引入 (已更改为完整引入,所以注释了) */
// AutoImport({
// dts: "./types/auto-imports.d.ts",