既然目前的现状是必须保持 URL 路径一致,仅通过 query 参数(如 ?source=platform)来区分,那么最优雅且改动成本最低的方案是使用**"路由分发组件(Dispatcher Component)"**。
这个方案的核心思想是:把原来的那个大文件变成一个"空壳",它什么 UI 都不画,只负责"读取 query -> 判断来源 -> 动态渲染对应的真实页面组件"。
以下是基于 Vue 3 + Vue Router + Element Plus 的具体代码实现步骤:
1. 调整路由配置 (Router)
你的路由配置不需要大改,只需要把指向原来的"面条代码"文件,改为指向我们新建的"分发组件"。
typescript
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
// ... 其他路由
{
path: '/detail', // 路径保持不变
name: 'DetailView',
// 指向我们新建的"空壳"分发组件
component: () => import('@/views/Detail/DetailDispatcher.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
2. 创建路由分发组件 (DetailDispatcher.vue)
这是核心所在。为了优化性能,避免一次性加载两个页面的代码,我们使用 Vue 3 的 defineAsyncComponent 来异步按需加载子页面。
vue
<template>
<div class="dispatcher-container">
<component :is="activeComponent" :record-id="recordId" />
</div>
</template>
<script setup lang="ts">
import { computed, defineAsyncComponent } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
// 提取共用的业务 ID(假设你的 query 里有 id,例如 ?id=123&source=platform)
const recordId = computed(() => route.query.id as string)
// 异步引入两个拆分后的独立页面
// 这样做的好处是:从运行总览进来的用户,完全不会下载平台专属页面的代码包
const OverviewDetail = defineAsyncComponent(() => import('./OverviewDetail.vue'))
const PlatformDetail = defineAsyncComponent(() => import('./PlatformDetail.vue'))
// 核心逻辑:根据 query 动态决定渲染哪个组件
const activeComponent = computed(() => {
if (route.query.source === 'platform') {
return PlatformDetail
}
return OverviewDetail // 默认走运行总览的逻辑
})
</script>
3. 创建两个完全独立的页面组件
现在你可以放开手脚,在这两个文件里分别写差异化的 UI 和调用不同的接口,再也不用写各种 v-if="isPlatform" 了。
页面 A:平台跳转专属页面 (PlatformDetail.vue)
vue
<template>
<div class="platform-detail">
<el-page-header @back="goBack" title="平台详情视图" />
<el-card class="mt-4">
<template #header>
<div class="card-header">
<span>平台专属操作区</span>
<el-button type="primary" size="small">平台特有按钮</el-button>
</div>
</template>
<el-table :data="tableData" v-loading="loading" border>
<el-table-column prop="platformName" label="平台名称" />
<el-table-column prop="status" label="状态" />
</el-table>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
// 接收分发器传过来的 ID
const props = defineProps<{
recordId: string
}>()
const router = useRouter()
const loading = ref(false)
const tableData = ref([])
const goBack = () => router.back()
// 调用平台专属接口
const fetchPlatformData = async () => {
loading.value = true
try {
// const res = await api.getPlatformDetail(props.recordId)
// tableData.value = res.data
} finally {
loading.value = false
}
}
onMounted(() => {
fetchPlatformData()
})
</script>
页面 B:运行总览专属页面 (OverviewDetail.vue)
vue
<template>
<div class="overview-detail">
<h2>运行总览详情 (ID: {{ recordId }})</h2>
<el-row :gutter="20">
<el-col :span="12">
<el-card shadow="hover">总览数据卡片 A</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="hover">总览数据卡片 B</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const props = defineProps<{
recordId: string
}>()
// 这里调用总览专属的接口,逻辑与平台页面完全物理隔离
onMounted(() => {
console.log('Fetching overview data for ID:', props.recordId)
})
</script>
方案的优势
- 零改动 URL:外部跳转的链接不需要做任何修改,对前端路由配置和后端完全透明。
- 性能优化 :通过
defineAsyncComponent,用户访问哪个端,浏览器就只下载哪个端的代码,避免了原先大文件的性能负担。 - 心智负担极低 :开发平台功能时,你只需要看
PlatformDetail.vue,不用再提心吊胆地担心改动会影响到运行总览的样式。
你目前在这两个入口的页面中,是否有大量长得一模一样且逻辑相同的区域(比如底部的某个通用表单或组件)?如果有,我们可以接着讨论如何将这部分代码提取出来,只维护一份。需要我为你演示如何提取公共组件或 Pinia 状态吗?