docs(design): 删除磁盘监控设计文档并更新前端页面结构规范

- 删除 frontend/src/views/systemMonitor/2026-04-22-disk-monitor-design.md 设计文档
- 删除 frontend/src/views/tools/addLedger/API_DEBUG.md 调试文档
- 在 AGENTS.md 中新增前端页面结构归档章节,规范复杂工具页结构
- 明确 index.vue、components/、utils/ 职责边界和拆分原则
- 规定页面级类型和 contract 脚本管理方式
- 统一复杂页面拆分优先顺序和注意事项
This commit is contained in:
2026-05-14 09:17:25 +08:00
parent 5b3ca264c4
commit f7d297decf
72 changed files with 5125 additions and 3028 deletions

View File

@@ -0,0 +1,72 @@
import { readFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const root = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..')
const read = path => readFileSync(resolve(root, path), 'utf8')
const staticRouterSource = read('src/routers/modules/staticRouter.ts')
const mainSource = read('src/layouts/components/Main/index.vue')
const tabsSource = read('src/stores/modules/tabs.ts')
const routeContracts = [
['tools', 'src/views/tools/index.vue'],
['toolWaveform', 'src/views/tools/waveform/index.vue'],
['toolMmsMapping', 'src/views/tools/mmsMapping/index.vue'],
['toolAddData', 'src/views/tools/addData/index.vue'],
['toolAddLedger', 'src/views/tools/addLedger/index.vue'],
['eventList', 'src/views/event/eventList/index.vue'],
['systemMonitor', 'src/views/systemMonitor/index.vue'],
['diskMonitor', 'src/views/systemMonitor/diskMonitor/index.vue']
]
const extractRouteBlock = routeName => {
const routeNameIndex = staticRouterSource.indexOf(`name: '${routeName}'`)
if (routeNameIndex === -1) {
throw new Error(`Route ${routeName} was not found in staticRouter.ts`)
}
const nextRouteIndex = staticRouterSource.indexOf('\n {', routeNameIndex + 1)
return staticRouterSource.slice(routeNameIndex, nextRouteIndex === -1 ? undefined : nextRouteIndex)
}
const extractComponentName = viewPath => {
const viewSource = read(viewPath)
const componentName = viewSource.match(/defineOptions\(\s*\{\s*name:\s*'([^']+)'/s)?.[1]
if (!componentName) {
throw new Error(`${viewPath} must define an explicit component name for keep-alive`)
}
return componentName
}
const errors = []
for (const [routeName, viewPath] of routeContracts) {
const routeBlock = extractRouteBlock(routeName)
const componentName = extractComponentName(viewPath)
const cacheName = routeBlock.match(/cacheName:\s*'([^']+)'/)?.[1]
if (cacheName !== componentName) {
errors.push(`${routeName} meta.cacheName should be ${componentName}, got ${cacheName || '<missing>'}`)
}
}
if (!mainSource.includes('v-if="isRouterShow"')) {
errors.push('Main router component must use isRouterShow so tab refresh can rebuild the current page')
}
if (!mainSource.includes('keepAliveName')) {
errors.push('Main keep-alive include list must come from keepAliveName')
}
if (!tabsSource.includes('cacheName')) {
errors.push('Tabs store must keep actual component cacheName values in keepAliveName')
}
if (errors.length) {
console.error(errors.join('\n'))
process.exit(1)
}

View File

@@ -5,8 +5,8 @@
<router-view v-slot="{ Component, route }" style="height: 100%">
<!-- {{ keepAliveName}} -->
<!-- <transition name="slide-right" mode="out-in"> -->
<keep-alive :include="tabsMenuList">
<component :is="Component" :key="route.fullPath" />
<keep-alive :include="keepAliveName">
<component v-if="isRouterShow" :is="Component" :key="route.fullPath" />
</keep-alive>
<!-- </transition> -->
</router-view>
@@ -26,13 +26,14 @@ import Maximize from './components/Maximize.vue'
import Tabs from '@/layouts/components/Tabs/index.vue'
import Footer from '@/layouts/components/Footer/index.vue'
import { useAuthStore } from '@/stores/modules/auth'
import { useTabsStore } from '@/stores/modules/tabs'
const tabStore = useTabsStore()
defineOptions({
name: 'LayoutMain'
})
const globalStore = useGlobalStore()
const tabsMenuList = computed(() => tabStore.tabsMenuList.map(item => item.name))
const authStore = useAuthStore()
const { maximize, isCollapse, layout, tabs, footer } = storeToRefs(globalStore)
const { maximize, isCollapse, layout, tabs } = storeToRefs(globalStore)
const keepAliveStore = useKeepAliveStore()
const { keepAliveName } = storeToRefs(keepAliveStore)
//是否显示导航栏

View File

@@ -21,6 +21,7 @@
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { resolveBusinessMenuPath } from '@/stores/modules/auth'
defineProps<{ menuList: Menu.MenuOptions[] }>()
const router = useRouter()
@@ -29,7 +30,7 @@ const handleClickMenu = async (subItem: Menu.MenuOptions) => {
window.open(subItem.meta.isLink, '_blank')
return
}
await router.push(subItem.path)
await router.push(resolveBusinessMenuPath(subItem))
}
</script>
<style lang="scss">

View File

@@ -63,14 +63,22 @@ const currentTabRoute = computed(() => {
return router.getRoutes().find(item => item.path === currentTabPath.value)
})
const currentCacheName = computed(() => {
return (
(route.meta.cacheName as string | undefined) ||
(currentTabRoute.value?.meta.cacheName as string | undefined) ||
(route.name as string)
)
})
// refresh current page
const refreshCurrentPage: Function = inject('refresh') as Function
const refresh = () => {
setTimeout(() => {
keepAliveStore.removeKeepAliveName(route.name as string)
keepAliveStore.removeKeepAliveName(currentCacheName.value)
refreshCurrentPage(false)
nextTick(() => {
keepAliveStore.addKeepAliveName(route.name as string)
keepAliveStore.addKeepAliveName(currentCacheName.value)
refreshCurrentPage(true)
})
}, 0)

View File

@@ -57,7 +57,7 @@ onMounted(() => {
const ensureParentTab = () => {
const parentPath = resolveCurrentTabPath()
if (!parentPath || tabStore.tabsMenuList.some(item => item.path === parentPath)) return
if (!parentPath) return
const parentRoute = router.getRoutes().find(item => item.path === parentPath && item.name)
if (!parentRoute) return
@@ -69,7 +69,8 @@ const ensureParentTab = () => {
path: parentRoute.path,
name: parentRoute.name as string,
close: !parentRoute.meta.isAffix,
isKeepAlive: parentRoute.meta.isKeepAlive as boolean
isKeepAlive: (route.meta.isKeepAlive ?? parentRoute.meta.isKeepAlive) as boolean,
cacheName: (route.meta.cacheName || parentRoute.meta.cacheName) as string | undefined
})
}
@@ -87,6 +88,7 @@ watch(
title: route.meta.title as string,
path: route.fullPath,
name: route.name as string,
cacheName: route.meta.cacheName as string | undefined,
close: !route.meta.isAffix,
isKeepAlive: route.meta.isKeepAlive as boolean
}
@@ -113,6 +115,7 @@ const initTabs = () => {
title: item.meta.title as string,
path: item.path,
name: item.name as string,
cacheName: item.meta.cacheName as string | undefined,
close: !item.meta.isAffix,
isKeepAlive: item.meta.isKeepAlive as boolean,
unshift: true