mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 11:27:17 +08:00
feat: 前端路由固定
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/store'
|
||||
import { useTabStore } from '@/store'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const tabStore = useTabStore()
|
||||
|
||||
const keepAliveNames = computed(() => {
|
||||
return tabStore.tabs.filter((item) => item.keepAlive).map((item) => item.name)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<component :is="Component" v-if="appStore.reloadFlag" :key="route.path" />
|
||||
<keep-alive :include="keepAliveNames">
|
||||
<component :is="Component" v-if="!tabStore.reloading" :key="route.path" />
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
</template>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/store'
|
||||
import { useTabStore } from '@/store'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const tabStore = useTabStore()
|
||||
|
||||
const handleReloadPage = () => {
|
||||
appStore.reloadPage()
|
||||
tabStore.reloadTab(tabStore.active)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore, usePermissionStore, useThemeStore } from '@/store'
|
||||
import { usePermissionStore, useTabStore, useThemeStore } from '@/store'
|
||||
import { isUrl, renderIcon } from '@/utils'
|
||||
import type { MenuInst, MenuOption } from 'naive-ui'
|
||||
import type { VNodeChild } from 'vue'
|
||||
@@ -11,7 +11,7 @@ const router = useRouter()
|
||||
const currentRoute = useRoute()
|
||||
const permissionStore = usePermissionStore()
|
||||
const themeStore = useThemeStore()
|
||||
const appStore = useAppStore()
|
||||
const tabStore = useTabStore()
|
||||
|
||||
const menu = ref<MenuInst>()
|
||||
watch(currentRoute, async () => {
|
||||
@@ -93,7 +93,8 @@ function handleMenuSelect(key: string, item: MenuOption) {
|
||||
window.open(menuItem.path)
|
||||
return
|
||||
}
|
||||
if (menuItem.path === currentRoute.path && !currentRoute.meta?.keepAlive) appStore.reloadPage()
|
||||
if (menuItem.path === currentRoute.path && !currentRoute.meta?.keepAlive)
|
||||
tabStore.reloadTab(currentRoute.path)
|
||||
else router.push(menuItem.path)
|
||||
|
||||
// 手机端自动收起菜单
|
||||
|
||||
@@ -8,6 +8,7 @@ const tabStore = useTabStore()
|
||||
|
||||
interface ContextMenuOption {
|
||||
show: boolean
|
||||
keepAlive: boolean
|
||||
x: number
|
||||
y: number
|
||||
currentPath: string
|
||||
@@ -15,6 +16,7 @@ interface ContextMenuOption {
|
||||
|
||||
const contextMenuOption = reactive<ContextMenuOption>({
|
||||
show: false,
|
||||
keepAlive: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
currentPath: ''
|
||||
@@ -33,15 +35,15 @@ function hideContextMenu() {
|
||||
contextMenuOption.show = false
|
||||
}
|
||||
|
||||
function setContextMenu(x: number, y: number, currentPath: string) {
|
||||
Object.assign(contextMenuOption, { x, y, currentPath })
|
||||
function setContextMenu(x: number, y: number, keepAlive: boolean, currentPath: string) {
|
||||
Object.assign(contextMenuOption, { x, y, keepAlive, currentPath })
|
||||
}
|
||||
|
||||
// 右击菜单
|
||||
async function handleContextMenu(e: MouseEvent, tabItem: TabItem) {
|
||||
const { clientX, clientY } = e
|
||||
hideContextMenu()
|
||||
setContextMenu(clientX, clientY, tabItem.path)
|
||||
setContextMenu(clientX, clientY, tabItem.keepAlive, tabItem.path)
|
||||
await nextTick()
|
||||
showContextMenu()
|
||||
}
|
||||
@@ -68,6 +70,7 @@ async function handleContextMenu(e: MouseEvent, tabItem: TabItem) {
|
||||
<ContextMenu
|
||||
v-model:show="contextMenuOption.show"
|
||||
:current-path="contextMenuOption.currentPath"
|
||||
:keep-alive="contextMenuOption.keepAlive"
|
||||
:x="contextMenuOption.x"
|
||||
:y="contextMenuOption.y"
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore, useTabStore } from '@/store'
|
||||
import { useTabStore } from '@/store'
|
||||
import { renderIcon } from '@/utils'
|
||||
|
||||
interface Props {
|
||||
show?: boolean
|
||||
keepAlive?: boolean
|
||||
currentPath?: string
|
||||
x: number
|
||||
y: number
|
||||
@@ -11,27 +12,39 @@ interface Props {
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
show: false,
|
||||
keepAlive: false,
|
||||
currentPath: ''
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:show'])
|
||||
|
||||
const tabStore = useTabStore()
|
||||
const appStore = useAppStore()
|
||||
|
||||
const options = computed(() => [
|
||||
{
|
||||
label: '重新加载',
|
||||
key: 'reload',
|
||||
disabled: props.currentPath !== tabStore.active,
|
||||
icon: renderIcon('mdi:refresh', { size: 14 })
|
||||
},
|
||||
{
|
||||
label: '关闭',
|
||||
key: 'close',
|
||||
disabled: tabStore.tabs.length <= 1,
|
||||
icon: renderIcon('mdi:close', { size: 14 })
|
||||
},
|
||||
{
|
||||
label: '重载',
|
||||
key: 'reload',
|
||||
disabled: props.currentPath !== tabStore.active,
|
||||
icon: renderIcon('mdi:refresh', { size: 14 })
|
||||
},
|
||||
{
|
||||
label: '固定',
|
||||
key: 'pin',
|
||||
disabled: props.keepAlive,
|
||||
icon: renderIcon('mdi:pin', { size: 14 })
|
||||
},
|
||||
{
|
||||
label: '取消固定',
|
||||
key: 'unpin',
|
||||
disabled: !props.keepAlive,
|
||||
icon: renderIcon('mdi:pin-off', { size: 14 })
|
||||
},
|
||||
{
|
||||
label: '关闭其他',
|
||||
key: 'close-other',
|
||||
@@ -64,18 +77,30 @@ const dropdownShow = computed({
|
||||
})
|
||||
|
||||
const actionMap = new Map([
|
||||
[
|
||||
'reload',
|
||||
() => {
|
||||
appStore.reloadPage()
|
||||
}
|
||||
],
|
||||
[
|
||||
'close',
|
||||
() => {
|
||||
tabStore.removeTab(props.currentPath)
|
||||
}
|
||||
],
|
||||
[
|
||||
'reload',
|
||||
() => {
|
||||
tabStore.reloadTab(props.currentPath)
|
||||
}
|
||||
],
|
||||
[
|
||||
'pin',
|
||||
() => {
|
||||
tabStore.pinTab(props.currentPath)
|
||||
}
|
||||
],
|
||||
[
|
||||
'unpin',
|
||||
() => {
|
||||
tabStore.unpinTab(props.currentPath)
|
||||
}
|
||||
],
|
||||
[
|
||||
'close-other',
|
||||
() => {
|
||||
|
||||
@@ -12,7 +12,8 @@ export function createTabGuard(router: Router) {
|
||||
tabStore.addTab({
|
||||
name: String(name),
|
||||
path,
|
||||
title
|
||||
title,
|
||||
keepAlive: false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,21 +2,7 @@ import { defineStore } from 'pinia'
|
||||
|
||||
export const useAppStore = defineStore('app', {
|
||||
state() {
|
||||
return {
|
||||
reloadFlag: <boolean>true
|
||||
}
|
||||
return {}
|
||||
},
|
||||
actions: {
|
||||
async reloadPage() {
|
||||
window.$loadingBar?.start()
|
||||
this.reloadFlag = false
|
||||
await nextTick()
|
||||
this.reloadFlag = true
|
||||
|
||||
setTimeout(() => {
|
||||
document.documentElement.scrollTo({ left: 0, top: 0 })
|
||||
window.$loadingBar?.finish()
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
actions: {}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './app'
|
||||
export * from './permission'
|
||||
export * from './tab'
|
||||
export * from './theme'
|
||||
|
||||
@@ -5,6 +5,7 @@ export const WITHOUT_TAB_PATHS = ['/404', '/login']
|
||||
|
||||
export interface Tab {
|
||||
active: string
|
||||
reloading: boolean
|
||||
tabs: Array<TabItem>
|
||||
}
|
||||
|
||||
@@ -12,17 +13,20 @@ export interface TabItem {
|
||||
name: string
|
||||
path: string
|
||||
title: string
|
||||
keepAlive: boolean
|
||||
}
|
||||
|
||||
export const useTabStore = defineStore('tab', {
|
||||
state: (): Tab => {
|
||||
return {
|
||||
active: '',
|
||||
reloading: false,
|
||||
tabs: []
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setActiveTab(path: string) {
|
||||
async setActiveTab(path: string) {
|
||||
await nextTick()
|
||||
this.active = path
|
||||
},
|
||||
setTabs(tabs: Array<TabItem>) {
|
||||
@@ -34,6 +38,30 @@ export const useTabStore = defineStore('tab', {
|
||||
return
|
||||
this.setTabs([...this.tabs, tab])
|
||||
},
|
||||
async reloadTab(path: string) {
|
||||
const findItem = this.tabs.find((item) => item.path === path)
|
||||
if (!findItem) return
|
||||
const keepLive = findItem.keepAlive
|
||||
findItem.keepAlive = false // 取消keepAlive
|
||||
window.$loadingBar.start()
|
||||
this.reloading = true
|
||||
await nextTick()
|
||||
this.reloading = false
|
||||
await nextTick()
|
||||
findItem.keepAlive = keepLive // 恢复keepAlive原状态
|
||||
setTimeout(() => {
|
||||
document.documentElement.scrollTo({ left: 0, top: 0 })
|
||||
window.$loadingBar.finish()
|
||||
}, 100)
|
||||
},
|
||||
pinTab(path: string) {
|
||||
const findItem = this.tabs.find((item) => item.path === path)
|
||||
if (findItem) findItem.keepAlive = true
|
||||
},
|
||||
unpinTab(path: string) {
|
||||
const findItem = this.tabs.find((item) => item.path === path)
|
||||
if (findItem) findItem.keepAlive = false
|
||||
},
|
||||
removeTab(path: string) {
|
||||
if (path === this.active) {
|
||||
const activeIndex = this.tabs.findIndex((item) => item.path === path)
|
||||
@@ -65,5 +93,7 @@ export const useTabStore = defineStore('tab', {
|
||||
this.setActiveTab('')
|
||||
}
|
||||
},
|
||||
persist: true
|
||||
persist: {
|
||||
storage: sessionStorage
|
||||
}
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import dashboard from '@/api/panel/dashboard'
|
||||
import { router } from '@/router'
|
||||
import { useAppStore } from '@/store'
|
||||
import { useTabStore } from '@/store'
|
||||
import { formatDateTime, formatDuration, toTimestamp } from '@/utils/common'
|
||||
import { formatBytes, formatPercent } from '@/utils/file'
|
||||
import VChart from 'vue-echarts'
|
||||
@@ -31,7 +31,7 @@ use([
|
||||
])
|
||||
|
||||
const { locale } = useI18n()
|
||||
const appStore = useAppStore()
|
||||
const tabStore = useTabStore()
|
||||
const realtime = ref<Realtime | null>(null)
|
||||
const systemInfo = ref<SystemInfo | null>(null)
|
||||
const homeApps = ref<HomeApp[] | null>(null)
|
||||
@@ -310,7 +310,7 @@ const handleRestartPanel = () => {
|
||||
dashboard.restart().then(() => {
|
||||
window.$message.success('面板重启成功')
|
||||
setTimeout(() => {
|
||||
appStore.reloadPage()
|
||||
tabStore.reloadTab(tabStore.active)
|
||||
}, 3000)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: 'website-edit'
|
||||
})
|
||||
|
||||
import Editor from '@guolao/vue-monaco-editor'
|
||||
import { NButton } from 'naive-ui'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user