重构pagetype列表页和详情页路由分发逻辑,增加专门的路由分发层

This commit is contained in:
jingrow 2025-11-02 18:14:59 +08:00
parent 44b55d4df1
commit 35aa3081ce
5 changed files with 114 additions and 46 deletions

View File

@ -76,23 +76,23 @@ const router = createRouter({
{
path: 'app/:entity',
name: 'PageTypeList',
component: () => import('../../core/pagetype/GenericListPage.vue')
component: () => import('../../core/pagetype/ListPage.vue')
},
{
path: 'app/:entity/:id',
name: 'PageTypeDetail',
component: () => import('../../core/pagetype/GenericDetailPage.vue')
component: () => import('../../core/pagetype/DetailPage.vue')
},
// 保持向后兼容
{
path: 'page/:entity',
name: 'PageTypeListLegacy',
component: () => import('../../core/pagetype/GenericListPage.vue')
component: () => import('../../core/pagetype/ListPage.vue')
},
{
path: 'page/:entity/:id',
name: 'PageTypeDetailLegacy',
component: () => import('../../core/pagetype/GenericDetailPage.vue')
component: () => import('../../core/pagetype/DetailPage.vue')
},
{
path: 'settings/menu',

View File

@ -0,0 +1,51 @@
<template>
<!-- 详情页路由分发器决定使用覆盖组件还是默认组件 -->
<component
v-if="overrideComponent"
:is="overrideComponent"
:context="context"
/>
<GenericDetailPage v-else />
</template>
<script setup lang="ts">
import { onMounted, shallowRef, markRaw, computed, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { usePageTypeSlug } from '@/shared/utils/slug'
import { resolvePagetypeDetailOverride } from '@/core/registry/pagetypeOverride'
import GenericDetailPage from './GenericDetailPage.vue'
const route = useRoute()
const router = useRouter()
const { pagetypeSlug } = usePageTypeSlug(route)
const overrideComponent = shallowRef<any | null>(null)
// context GenericDetailPage
const context = computed(() => ({
route,
router,
pagetypeSlug: pagetypeSlug.value,
entity: route.params.entity as string,
id: route.params.id as string
}))
onMounted(async () => {
//
const detailComp = await resolvePagetypeDetailOverride(pagetypeSlug.value)
overrideComponent.value = detailComp ? markRaw(detailComp) : null
})
// entity id
watch(() => [route.params.entity, route.params.id], async ([newEntity, newId], [oldEntity, oldId]) => {
if (newEntity !== oldEntity || newId !== oldId) {
const detailComp = await resolvePagetypeDetailOverride(String(newEntity || pagetypeSlug.value))
overrideComponent.value = detailComp ? markRaw(detailComp) : null
}
}, { immediate: false })
</script>
<style scoped>
/* DetailPage 仅作为路由分发器,不需要样式 */
</style>

View File

@ -1,6 +1,5 @@
<template>
<component :is="overrideComponent || 'div'" v-if="overrideComponent" />
<div v-else class="generic-detail-page">
<div class="generic-detail-page">
<!-- 页面头部 -->
<div class="page-header">
<n-space justify="space-between" align="center">
@ -321,7 +320,7 @@ import { get_session_api_headers } from '@/shared/api/auth'
import { updateRecord, getRecord, getRecordAttachments, deleteAttachment, uploadAttachment, deleteRecords } from '@/shared/api/common'
import { downloadImageToLocal } from '@/shared/api/common'
import { usePageTypeSlug } from '@/shared/utils/slug'
import { resolvePagetypeDetailOverride, resolvePagetypeToolbarOverride } from '@/core/registry/pagetypeOverride'
import { resolvePagetypeToolbarOverride } from '@/core/registry/pagetypeOverride'
const route = useRoute()
const router = useRouter()
@ -330,7 +329,7 @@ const dialog = useDialog()
// 使URL slug
const { pagetypeSlug, entity } = usePageTypeSlug(route)
const overrideComponent = shallowRef<any | null>(null)
// DetailPage.vue
const toolbarComponent = shallowRef<any | null>(null)
const id = computed(() => route.params.id as string)
const isNew = computed(() => {
@ -1144,14 +1143,10 @@ onMounted(async () => {
//
loadSidebarPreferences()
// apps 使 URL slug - _
const comp = await resolvePagetypeDetailOverride(pagetypeSlug.value)
overrideComponent.value = comp ? markRaw(comp) : null
//
const tb = await resolvePagetypeToolbarOverride(pagetypeSlug.value)
toolbarComponent.value = tb ? markRaw(tb) : null
if (overrideComponent.value) {
return
}
await loadMeta()
await loadDetail()
})
@ -1164,23 +1159,17 @@ onUnmounted(() => {
//
watch(() => route.params.id, async (newId, oldId) => {
if (newId && newId !== oldId) {
if (!overrideComponent.value) {
await loadDetail()
}
await loadDetail()
}
})
// entity/slug
watch(() => route.params.entity, async (newEntity, oldEntity) => {
if (newEntity !== oldEntity) {
const comp = await resolvePagetypeDetailOverride(String(newEntity))
overrideComponent.value = comp ? markRaw(comp) : null
const tb = await resolvePagetypeToolbarOverride(String(newEntity))
toolbarComponent.value = tb ? markRaw(tb) : null
if (!overrideComponent.value) {
await loadMeta()
await loadDetail()
}
await loadMeta()
await loadDetail()
}
})
</script>

View File

@ -1,8 +1,6 @@
<template>
<!-- 如果是单页模式直接显示单页详情组件 -->
<SinglePageDetail v-if="isSinglePage" />
<!-- 列表页整体覆盖 -->
<component :is="listOverrideComponent || 'div'" v-else-if="listOverrideComponent" />
<div v-else class="page">
<!-- 头部 AI 智能体列表一致的结构 -->
<div class="page-header">
@ -303,7 +301,6 @@ import GenericListPageToolBar from './GenericListPageToolBar.vue'
import GenericListPageFilterBar from './GenericListPageFilterBar.vue'
import GenericListPageActions from './GenericListPageActions.vue'
import {
resolvePagetypeListOverride,
resolvePagetypeListToolbarOverride,
resolvePagetypeListFilterBarOverride,
resolvePagetypeListActionsOverride
@ -319,8 +316,7 @@ const dialog = useDialog()
const { pagetypeSlug, entity } = usePageTypeSlug(route)
const title = computed(() => entity.value)
//
const listOverrideComponent = shallowRef<any | null>(null)
// ListPage.vue
const toolbarComponent = shallowRef<any | null>(null)
const filterBarComponent = shallowRef<any | null>(null)
const actionsComponent = shallowRef<any | null>(null)
@ -913,10 +909,7 @@ async function loadData() {
}
onMounted(async () => {
//
const listComp = await resolvePagetypeListOverride(pagetypeSlug.value)
listOverrideComponent.value = listComp ? markRaw(listComp) : null
//
const listToolbarComp = await resolvePagetypeListToolbarOverride(pagetypeSlug.value)
toolbarComponent.value = listToolbarComp ? markRaw(listToolbarComp) : null
@ -926,11 +919,6 @@ onMounted(async () => {
const listActionsComp = await resolvePagetypeListActionsOverride(pagetypeSlug.value)
actionsComponent.value = listActionsComp ? markRaw(listActionsComp) : null
//
if (listOverrideComponent.value) {
return
}
await loadMeta()
//
if (!isSinglePage.value) {
@ -946,10 +934,7 @@ watch([page], () => {
// entity
watch(() => route.params.entity, async (newEntity, oldEntity) => {
if (newEntity !== oldEntity) {
//
const listComp = await resolvePagetypeListOverride(String(newEntity))
listOverrideComponent.value = listComp ? markRaw(listComp) : null
//
const listToolbarComp = await resolvePagetypeListToolbarOverride(String(newEntity))
toolbarComponent.value = listToolbarComp ? markRaw(listToolbarComp) : null
@ -959,11 +944,6 @@ watch(() => route.params.entity, async (newEntity, oldEntity) => {
const listActionsComp = await resolvePagetypeListActionsOverride(String(newEntity))
actionsComponent.value = listActionsComp ? markRaw(listActionsComp) : null
//
if (listOverrideComponent.value) {
return
}
//
page.value = 1
searchQuery.value = ''

View File

@ -0,0 +1,48 @@
<template>
<!-- 列表页路由分发器决定使用覆盖组件还是默认组件 -->
<component
v-if="overrideComponent"
:is="overrideComponent"
:context="context"
/>
<GenericListPage v-else />
</template>
<script setup lang="ts">
import { onMounted, shallowRef, markRaw, computed, watch } from 'vue'
import { useRoute } from 'vue-router'
import { usePageTypeSlug } from '@/shared/utils/slug'
import { resolvePagetypeListOverride } from '@/core/registry/pagetypeOverride'
import GenericListPage from './GenericListPage.vue'
const route = useRoute()
const { pagetypeSlug } = usePageTypeSlug(route)
const overrideComponent = shallowRef<any | null>(null)
// context GenericListPage
const context = computed(() => ({
route,
pagetypeSlug: pagetypeSlug.value,
entity: route.params.entity as string
}))
onMounted(async () => {
//
const listComp = await resolvePagetypeListOverride(pagetypeSlug.value)
overrideComponent.value = listComp ? markRaw(listComp) : null
})
// entity
watch(() => route.params.entity, async (newEntity, oldEntity) => {
if (newEntity !== oldEntity) {
const listComp = await resolvePagetypeListOverride(String(newEntity))
overrideComponent.value = listComp ? markRaw(listComp) : null
}
})
</script>
<style scoped>
/* ListPage 仅作为路由分发器,不需要样式 */
</style>