pinia使用+viewTags缓存

This commit is contained in:
cfn@leapy.cn 2025-06-12 23:34:34 +08:00
parent aa11c68bad
commit da31de71c5
18 changed files with 254 additions and 216 deletions

View File

@ -7,6 +7,3 @@ VITE_APP_ENV='production'
# 生产环境
VITE_API_BASE='https://dev.api.leapy.cn/merchant/'
VITE_WS_URL='wss://dev.api.leapy.cn/mms'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS=gzip

View File

@ -21,8 +21,7 @@
"vue": "^3.5.14",
"vue-i18n": "^11.1.5",
"vue-router": "^4.5.1",
"vuedraggable": "^2.24.3",
"vuex": "^4.1.0"
"vuedraggable": "^2.24.3"
},
"devDependencies": {
"@types/node": "^22.15.21",

View File

@ -8,12 +8,16 @@
<script setup>
import {computed, watch} from "vue"
import {useRoute} from "vue-router";
import store from "@/store";
import globalStore from "@/store/global.js";
import iframeStore from "@/store/iframe.js";
const iframe = iframeStore()
const global = globalStore()
const route = useRoute()
const iframeList = computed(() => store.state.iframe.iframeList)
const ismobile = computed(() => store.state.global.ismobile)
const layoutTags = computed(() => store.state.global.layoutTags)
const iframeList = computed(() => iframe.iframeList)
const ismobile = computed(() => global.ismobile)
const layoutTags = computed(() => global.layoutTags)
push(route)
@ -24,13 +28,13 @@ watch(route, (e) => {
function push(route) {
if (route.meta.type == 'iframe') {
if (ismobile || !layoutTags) {
store.commit("setIframeList", route)
iframe.setIframeList(route)
} else {
store.commit("pushIframeList", route)
iframe.pushIframeList(route)
}
} else {
if (ismobile || !layoutTags) {
store.commit("clearIframeList")
iframe.clearIframeList()
}
}
}

View File

@ -44,14 +44,15 @@ import {ref, watch} from "vue";
import colorTool from '@/utils/color'
import tools from "@/utils/tools";
import config from "@/config";
import store from '@/store'
import globalStore from "@/store/global.js";
import {useI18n} from "vue-i18n";
const global = globalStore()
const {t, locale} = useI18n()
let layout = ref(tools.data.get('APP_LAYOUT') || store.state.global.layout);
let menuIsCollapse = ref(store.state.global.menuIsCollapse);
let layoutTags = ref(store.state.global.layoutTags);
let layout = ref(tools.data.get('APP_LAYOUT') || global.layout);
let menuIsCollapse = ref(global.menuIsCollapse);
let layoutTags = ref(global.layoutTags);
let lang = ref(tools.data.get('APP_LANG') || config.LANG);
let dark = ref(localStorage.getItem('APP_DARK') || config.DARK,);
let colorList = ref(['#409EFF', '#009688', '#536dfe', '#ff5c93', '#c62f2f', '#fd726d']);
@ -60,15 +61,15 @@ let weakMode = ref(localStorage.getItem('APP_WEAK') || false)
watch(layout, (val) => {
tools.data.set("APP_LAYOUT", val);
store.commit("SET_layout", val)
global.SET_layout(val)
})
watch(menuIsCollapse, () => {
store.commit("TOGGLE_menuIsCollapse")
global.TOGGLE_menuIsCollapse()
})
watch(layoutTags, () => {
store.commit("TOGGLE_layoutTags")
global.TOGGLE_layoutTags()
})
watch(weakMode, (val) => {

View File

@ -51,13 +51,21 @@
</transition>
</template>
<script setup name="tags">
<script setup>
defineOptions({
name: 'tags'
})
import Sortable from 'sortablejs'
import {getCurrentInstance, nextTick, ref, watch, onMounted, toRaw} from "vue";
import store from "@/store/index";
import {getCurrentInstance, nextTick, ref, watch, onMounted} from "vue";
import viewTagsStore from "@/store/viewTags";
import keepAliveStore from "@/store/keepAlive";
import iframeStore from "@/store/iframe";
import {useRouter, useRoute} from "vue-router";
import {getMenu} from "@/utils/route"
const viewTags = viewTagsStore()
const keepAlive = keepAliveStore()
const iframe = iframeStore()
const route = useRoute()
const router = useRouter()
const {proxy} = getCurrentInstance();
@ -68,7 +76,7 @@ let contextMenuVisible = ref(false)
let contextMenuItem = ref(null)
let left = ref(0)
let top = ref(0)
let tagList = store.state.viewTags.viewTags
let tagList = viewTags.viewTags
let tipDisplayed = ref(false)
@ -144,8 +152,8 @@ function tagDrop() {
//tag
function addViewTags(route) {
if (route.name && !route.meta.fullpage) {
store.commit("pushViewTags", route)
store.commit("pushKeepLive", route.name)
viewTags.pushViewTags(route)
keepAlive.pushKeepLive(route.name)
}
}
@ -157,9 +165,9 @@ function isActive(r) {
//tag
function closeSelectedTag(tag, autoPushLatestView = true) {
const nowTagIndex = tagList.findIndex(item => item.fullPath == tag.fullPath)
store.commit("removeViewTags", tag)
store.commit("removeIframeList", tag)
store.commit("removeKeepLive", tag.name)
viewTags.removeViewTags(tag)
iframe.removeIframeList(tag)
keepAlive.removeKeepLive(tag.name)
if (autoPushLatestView && isActive(tag)) {
const leftView = tagList[nowTagIndex - 1]
if (leftView) {
@ -204,13 +212,13 @@ function refreshTab() {
query: nowTag.query
})
}
store.commit("refreshIframe", nowTag)
iframe.refreshIframe(nowTag)
setTimeout(function () {
store.commit("removeKeepLive", nowTag.name)
store.commit("setRouteShow", false)
keepAlive.removeKeepLive(nowTag.name)
keepAlive.setRouteShow(false)
nextTick(() => {
store.commit("pushKeepLive", nowTag.name)
store.commit("setRouteShow", true)
keepAlive.pushKeepLive(nowTag.name)
keepAlive.setRouteShow(true)
})
}, 0);
}

View File

@ -35,7 +35,7 @@
</el-menu>
</el-scrollbar>
</div>
<div class="pi-side-bottom" @click="store.commit('TOGGLE_menuIsCollapse')">
<div class="pi-side-bottom" @click="global.TOGGLE_menuIsCollapse()">
<el-icon>
<el-icon-expand v-if="menuIsCollapse"/>
<el-icon-fold v-else/>
@ -48,8 +48,8 @@
<Tags v-if="!ismobile && layoutTags"></Tags>
<div class="pi-main" id="pi-main">
<router-view v-slot="{ Component }">
<keep-alive :include="store.state.keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="store.state.keepAlive.routeShow"/>
<keep-alive :include="keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="keepAlive.routeShow"/>
</keep-alive>
</router-view>
<iframe-view></iframe-view>
@ -74,7 +74,7 @@
</el-menu>
</el-scrollbar>
</div>
<div class="pi-side-bottom" @click="store.commit('TOGGLE_menuIsCollapse')">
<div class="pi-side-bottom" @click="global.TOGGLE_menuIsCollapse()">
<el-icon>
<el-icon-expand v-if="menuIsCollapse"/>
<el-icon-fold v-else/>
@ -89,8 +89,8 @@
<Tags v-if="!ismobile && layoutTags"></Tags>
<div class="pi-main" id="pi-main">
<router-view v-slot="{ Component }">
<keep-alive :include="store.state.keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="store.state.keepAlive.routeShow"/>
<keep-alive :include="keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="keepAlive.routeShow"/>
</keep-alive>
</router-view>
<iframe-view></iframe-view>
@ -125,8 +125,8 @@
<Tags v-if="!ismobile && layoutTags"></Tags>
<div class="pi-main" id="pi-main" :class="ismobile?'':'pi-pad'">
<router-view v-slot="{ Component }">
<keep-alive :include="store.state.keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="store.state.keepAlive.routeShow"/>
<keep-alive :include="keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="keepAlive.routeShow"/>
</keep-alive>
</router-view>
<iframe-view></iframe-view>
@ -160,8 +160,8 @@
<Tags v-if="!ismobile && layoutTags"></Tags>
<div class="pi-main" id="pi-main">
<router-view v-slot="{ Component }">
<keep-alive :include="store.state.keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="store.state.keepAlive.routeShow"/>
<keep-alive :include="keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="keepAlive.routeShow"/>
</keep-alive>
</router-view>
<iframe-view></iframe-view>
@ -206,7 +206,7 @@
</el-menu>
</el-scrollbar>
</div>
<div class="pi-side-bottom" @click="store.commit('TOGGLE_menuIsCollapse')">
<div class="pi-side-bottom" @click="global.TOGGLE_menuIsCollapse()">
<el-icon>
<el-icon-expand v-if="menuIsCollapse"/>
<el-icon-fold v-else/>
@ -221,8 +221,8 @@
<Tags v-if="!ismobile && layoutTags"></Tags>
<div class="pi-main" id="pi-main">
<router-view v-slot="{ Component }">
<keep-alive :include="store.state.keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="store.state.keepAlive.routeShow"/>
<keep-alive :include="keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="keepAlive.routeShow"/>
</keep-alive>
</router-view>
<iframe-view></iframe-view>
@ -246,7 +246,9 @@ import Tags from './components/tags.vue';
import NavMenu from './components/NavMenu.vue';
import userbar from './components/userbar.vue';
import iframeView from './components/iframeView.vue';
import store from '@/store'
import globalStore from "@/store/global.js";
import keepAliveStore from "@/store/keepAlive.js";
import {useRoute, useRouter} from 'vue-router'
import config from "@/config";
import {getMenu} from '@/utils/route'
@ -257,11 +259,13 @@ let nextMenu = ref([])
let pmenu = ref([])
let active = ref("")
let menu = filterUrl(getMenu());
const global = globalStore()
const keepAlive = keepAliveStore()
const ismobile = computed(() => store.state.global.ismobile)
const layout = computed(() => store.state.global.layout)
const layoutTags = computed(() => store.state.global.layoutTags)
const menuIsCollapse = computed(() => store.state.global.menuIsCollapse)
const ismobile = computed(() => global.ismobile)
const layout = computed(() => global.layout)
const layoutTags = computed(() => global.layoutTags)
const menuIsCollapse = computed(() => global.menuIsCollapse)
onLayoutResize();
window.addEventListener('resize', onLayoutResize);
@ -317,7 +321,7 @@ function showThis() {
}
function onLayoutResize() {
store.commit("SET_ismobile", document.body.clientWidth < 992)
global.SET_ismobile(document.body.clientWidth < 992)
}
</script>

View File

@ -5,13 +5,13 @@ import 'element-plus/theme-chalk/display.css'
import i18n from './locales'
import App from './App.vue'
import pinia from './store'
import router from './router'
import store from './store'
import pi from './pi'
const app = createApp(App);
app.use(store);
app.use(pinia);
app.use(router);
app.use(ElementPlus);
app.use(i18n);

View File

@ -84,7 +84,7 @@ router.beforeEach(async (to, from, next) => {
});
router.afterEach((to, from) => {
// afterEach(to)
afterEach(to)
NProgress.done()
});

33
src/store/global.ts Normal file
View File

@ -0,0 +1,33 @@
import tools from "@/utils/tools";
import config from "@/config";
import {defineStore} from "pinia";
const globalStore = defineStore('global', {
state: () => ({
//移动端布局
ismobile: false,
//布局
layout: tools.data.get('APP_LAYOUT') || config.APP_LAYOUT || 'header',
//菜单是否折叠 toggle
menuIsCollapse: false,
//多标签栏
layoutTags: true
}),
actions: {
SET_ismobile(key) {
this.ismobile = key
},
SET_layout(key) {
this.layout = key
},
TOGGLE_menuIsCollapse() {
this.menuIsCollapse = !this.menuIsCollapse
},
TOGGLE_layoutTags() {
this.layoutTags = !this.layoutTags
}
},
persist: true
})
export default globalStore

43
src/store/iframe.ts Normal file
View File

@ -0,0 +1,43 @@
import {defineStore} from "pinia";
const iframeStore = defineStore("iframe", {
state: () => ({
iframeList: []
}),
actions: {
setIframeList(route){
this.iframeList = []
this.iframeList.push(route)
},
pushIframeList(route){
let target = this.iframeList.find((item) => item.path === route.path)
if(!target){
this.iframeList.push(route)
}
},
removeIframeList(route){
this.iframeList.forEach((item, index) => {
if (item.path === route.path){
this.iframeList.splice(index, 1)
}
})
},
refreshIframe(route){
this.iframeList.forEach((item) => {
if (item.path == route.path){
var url = route.meta.url;
item.meta.url = '';
setTimeout(function() {
item.meta.url = url
}, 200);
}
})
},
clearIframeList(){
this.iframeList = []
}
},
persist: true
})
export default iframeStore

View File

@ -1,7 +1,7 @@
import { createPinia } from "pinia"
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const store = createPinia()
store.use(piniaPluginPersistedstate)
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
export default store
export default pinia

34
src/store/keepAlive.ts Normal file
View File

@ -0,0 +1,34 @@
import {defineStore} from "pinia";
const keepAliveStore = defineStore("keepAlive", {
state: () => ({
keepLiveRoute: [],
routeKey: null,
routeShow: true
}),
actions: {
pushKeepLive(component) {
if (!this.keepLiveRoute.includes(component)) {
this.keepLiveRoute.push(component)
}
},
removeKeepLive(component) {
var index = this.keepLiveRoute.indexOf(component);
if (index !== -1) {
this.keepLiveRoute.splice(index, 1);
}
},
clearKeepLive() {
this.keepLiveRoute = []
},
setRouteKey(key) {
this.routeKey = key
},
setRouteShow(key) {
this.routeShow = key
}
},
persist: true
})
export default keepAliveStore

View File

@ -1,36 +0,0 @@
import tools from "@/utils/tools";
import config from "@/config";
import {defineStore} from "pinia";
// const globalStore = defineStore('global', {
// state: () => {
//
// }
// })
export default {
state: {
//移动端布局
ismobile: false,
//布局
layout: tools.data.get('APP_LAYOUT') || config.APP_LAYOUT || 'header',
//菜单是否折叠 toggle
menuIsCollapse: false,
//多标签栏
layoutTags: true
},
mutations: {
SET_ismobile(state, key) {
state.ismobile = key
},
SET_layout(state, key) {
state.layout = key
},
TOGGLE_menuIsCollapse(state) {
state.menuIsCollapse = !state.menuIsCollapse
},
TOGGLE_layoutTags(state) {
state.layoutTags = !state.layoutTags
}
}
}

View File

@ -1,38 +0,0 @@
export default {
state: {
iframeList: []
},
mutations: {
setIframeList(state, route){
state.iframeList = []
state.iframeList.push(route)
},
pushIframeList(state, route){
let target = state.iframeList.find((item) => item.path === route.path)
if(!target){
state.iframeList.push(route)
}
},
removeIframeList(state, route){
state.iframeList.forEach((item, index) => {
if (item.path === route.path){
state.iframeList.splice(index, 1)
}
})
},
refreshIframe(state, route){
state.iframeList.forEach((item) => {
if (item.path == route.path){
var url = route.meta.url;
item.meta.url = '';
setTimeout(function() {
item.meta.url = url
}, 200);
}
})
},
clearIframeList(state){
state.iframeList = []
}
}
}

View File

@ -1,34 +0,0 @@
export default {
state: {
keepLiveRoute: [],
routeKey: null,
routeShow: true
},
mutations: {
pushKeepLive(state, component){
if(!state.keepLiveRoute.includes(component)){
state.keepLiveRoute.push(component)
}
},
removeKeepLive(state, component){
var index = state.keepLiveRoute.indexOf(component);
if(index !== -1){
state.keepLiveRoute.splice(index, 1);
}
},
clearKeepLive(state){
state.keepLiveRoute = []
},
setRouteKey(state, key){
state.routeKey = key
},
setRouteShow(state, key){
state.routeShow = key
}
},
actions: {
setRouteKey({ commit }, key) {
commit('setRouteKey', key);
}
}
}

View File

@ -1,47 +0,0 @@
import router from '@/router'
export default {
state: {
viewTags: []
},
mutations: {
pushViewTags(state, route){
let backPathIndex = state.viewTags.findIndex(item => item.fullPath == router.options.history.state.back)
let target = state.viewTags.find((item) => item.fullPath === route.fullPath)
let isName = route.name
if(!target && isName){
if(backPathIndex == -1){
state.viewTags.push(Object.assign({}, route))
}else{
state.viewTags.splice(backPathIndex+1, 0, Object.assign({}, route))
}
}
},
removeViewTags(state, route){
state.viewTags.forEach((item, index) => {
if (item.fullPath === route.fullPath){
state.viewTags.splice(index, 1)
}
})
},
updateViewTags(state, route){
state.viewTags.forEach((item) => {
if (item.fullPath == route.fullPath){
// 追加数据
item = Object.assign(item, route)
}
})
},
updateViewTagsTitle(state, title=''){
const nowFullPath = location.hash.substring(1)
state.viewTags.forEach((item) => {
if (item.fullPath == nowFullPath){
item.meta.title = title
}
})
},
clearViewTags(state){
state.viewTags = []
}
}
}

68
src/store/viewTags.ts Normal file
View File

@ -0,0 +1,68 @@
import {defineStore} from "pinia";
import router from '@/router'
const viewTagsStore = defineStore("viewTags", {
state: () => ({
viewTags: []
}),
actions: {
pushViewTags(route){
let backPathIndex = this.viewTags.findIndex(item => item.fullPath == router.options.history.state.back)
let target = this.viewTags.find((item) => item.fullPath === route.fullPath)
let isName = route.name
if(!target && isName){
const _route = {
name: route.name,
fullPath: route.fullPath,
component: route.component,
path: route.path,
scrollTop: route.scrollTop,
meta: {
hidden: route?.meta?.hidden,
icon: route?.meta?.icon,
affix: route?.meta?.affix,
type: route?.meta?.type,
title: route?.meta?.title,
},
query: route.query,
params: route.params,
hash: route.hash
}
if(backPathIndex == -1){
this.viewTags.push(_route)
}else{
this.viewTags.splice(backPathIndex+1, 0, _route)
}
}
},
removeViewTags(route){
this.viewTags.forEach((item, index) => {
if (item.fullPath === route.fullPath){
this.viewTags.splice(index, 1)
}
})
},
updateViewTags(route){
this.viewTags.forEach((item) => {
if (item.fullPath == route.fullPath){
// 追加数据
item = Object.assign(item, route)
}
})
},
updateViewTagsTitle(title=''){
const nowFullPath = location.hash.substring(1)
this.viewTags.forEach((item) => {
if (item.fullPath == nowFullPath){
item.meta.title = title
}
})
},
clearViewTags(){
this.viewTags = []
}
},
persist: true
})
export default viewTagsStore

View File

@ -1,4 +1,4 @@
import store from '@/store'
import viewTagsStore from "@/store/viewTags";
import {nextTick} from 'vue'
import {RouteLocationNormalized, RouteLocationNormalizedLoaded} from "vue-router";
import tools from "@/utils/tools";
@ -8,10 +8,11 @@ const notFound = () => import('/src/layout/404.vue')
export function beforeEach(to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded) {
const piMain = document.querySelector('#pi-main');
const viewTags = viewTagsStore()
if (!piMain) {
return false
}
store.commit("updateViewTags", {
viewTags.updateViewTags({
fullPath: from.fullPath,
scrollTop: piMain.scrollTop
})
@ -19,12 +20,13 @@ export function beforeEach(to: RouteLocationNormalized, from: RouteLocationNorma
export function afterEach(to: RouteLocationNormalized) {
const piMain = document.querySelector('#pi-main');
const viewTags = viewTagsStore()
if (!piMain) {
return false
}
nextTick(() => {
// @ts-ignore
const beforeRoute = store.state.viewTags.viewTags.filter(v => v.fullPath == to.fullPath)[0];
const beforeRoute = viewTags.viewTags.filter(v => v.fullPath == to.fullPath)[0];
if (beforeRoute) {
piMain.scrollTop = beforeRoute.scrollTop || 0
}