fix: prevent order list tab cache mix-up and service channel layout

Remove Transition wrapper around KeepAlive, disable cache on my orders and waybill pages, tighten dynamic route index matching, and fix service channel page root layout plus left menu.
This commit is contained in:
zzh 2026-07-23 10:49:19 +08:00
parent 6441ca6543
commit 0a2ff6993d
4 changed files with 21 additions and 79 deletions

View File

@ -1,67 +1,34 @@
<template>
<div class="page-layout-root">
<router-view v-slot="{ Component, route }">
<Transition
:name="transitionName || 'page-none'"
:css="Boolean(transitionName)"
:mode="transitionName ? 'out-in' : undefined"
>
<keep-alive :include="cacheList" :max="keepAliveMax">
<component
:is="Component"
v-if="Component"
:key="getComponentKey(route)"
/>
</keep-alive>
</Transition>
<keep-alive :include="cacheList" :max="keepAliveMax">
<component
:is="Component"
v-if="Component"
:key="getComponentKey(route)"
/>
</keep-alive>
</router-view>
</div>
</template>
<script lang="ts" setup>
import { computed, nextTick, ref, watch } from 'vue';
import { computed, watch } from 'vue';
import type { RouteLocationNormalized } from 'vue-router';
import { useRoute } from 'vue-router';
import { useTabBarStore } from '@/store';
import { MAX_KEEP_ALIVE_TABS } from '@/store/modules/tab-bar';
import { consumeSkipPageTransition } from '@/utils/page-transition-flag';
const route = useRoute();
const tabBarStore = useTabBarStore();
const cacheList = computed(() => tabBarStore.getCacheList);
const keepAliveMax = MAX_KEEP_ALIVE_TABS;
/** 已访问过的路由:Tab 回访关掉 CSS 过渡 */
const visitedNames = ref(new Set<string>(tabBarStore.getCacheList));
/** '' = 无 CSS 过渡;page-fade = 首次进入 */
const transitionName = ref('page-fade');
watch(
() => String(route.name || route.path),
(name) => {
if (!name) return;
// LRU:回访时刷新缓存顺序
if (tabBarStore.getCacheList.includes(name) || !route.meta?.ignoreCache) {
if (!route.meta?.ignoreCache) {
tabBarStore.touchCache(name);
}
}
const refreshToken = route.query._r ?? route.query._refresh;
if (consumeSkipPageTransition()) {
transitionName.value = '';
} else if (refreshToken != null && String(refreshToken) !== '') {
transitionName.value = 'page-fade';
} else if (
visitedNames.value.has(name) ||
tabBarStore.getCacheList.includes(name)
) {
transitionName.value = '';
} else {
transitionName.value = 'page-fade';
}
nextTick(() => {
visitedNames.value.add(name);
});
if (!name || route.meta?.ignoreCache) return;
tabBarStore.touchCache(name);
},
{ immediate: true },
);
@ -89,22 +56,4 @@
flex: 1;
min-height: 0;
}
.page-fade-enter-active {
transition: opacity 0.18s ease-out, transform 0.18s ease-out;
}
.page-fade-leave-active {
transition: opacity 0.1s ease-in, transform 0.1s ease-in;
}
.page-fade-enter-from {
opacity: 0;
transform: translateY(6px);
}
.page-fade-leave-to {
opacity: 0;
transform: translateY(-3px);
}
</style>

View File

@ -174,16 +174,19 @@ export const initDynamicRouter = async (options?: {
if (normalizedKey.endsWith(`${last2}.vue`)) return true;
}
// 策略4: 包含匹配(检查是否包含路径的关键部分)
// 策略4: 非 index 文件名才做宽松匹配,避免 order/myOrders/index 与 order/waybillManage/index 串页
const keyParts = normalizedKey.split('/').filter(Boolean);
if (pathSegments.length > 0) {
// 检查最后一段是否匹配(文件名)
const lastSegment = pathSegments[pathSegments.length - 1];
if (lastSegment === 'index') {
return false;
}
const keyLastPart = keyParts[keyParts.length - 1];
if (keyLastPart && keyLastPart.replace('.vue', '') === lastSegment) {
// 检查前面的路径段是否也匹配
if (pathSegments.length === 1 ||
keyParts.slice(0, -1).join('/').includes(pathSegments.slice(0, -1).join('/'))) {
if (
pathSegments.length === 1
|| keyParts.slice(0, -1).join('/').includes(pathSegments.slice(0, -1).join('/'))
) {
return true;
}
}

View File

@ -48,6 +48,7 @@ const ORDER: AppRouteRecordRaw = {
meta: {
locale: 'menu.order.myOrders',
requiresAuth: true,
ignoreCache: true,
},
},
{
@ -59,6 +60,7 @@ const ORDER: AppRouteRecordRaw = {
requiresAuth: true,
adminPortalOnly: true,
permission: 'Order.WaybillManage',
ignoreCache: true,
order: 3,
icon: 'icon-storage',
},

View File

@ -37,10 +37,6 @@
</a-doption>
</template>
<template #node-menu="{ nodeData }">
<a-doption @click="() => handleLeftMenuLeafEdit(nodeData)">
<icon-edit style="margin-right: 6px" />
{{ t('channel.serviceChannels.leftMenu.editChannel') }}
</a-doption>
<a-doption @click="() => handleLeftMenuLeafDelete(nodeData)">
<icon-delete style="margin-right: 6px" />
{{ t('channel.serviceChannels.actions.delete') }}
@ -382,7 +378,6 @@
</div>
</a-modal>
</div>
</div>
<!-- 新增/编辑价格配置弹窗 -->
<a-modal
@ -1120,6 +1115,7 @@
</div>
</div>
</a-modal>
</div>
</template>
<script setup lang="ts">
@ -1602,14 +1598,6 @@ function handleLeftMenuBranchAddChannel(nodeData: any) {
if (g) handleAddChannelToGroup(g)
}
function handleLeftMenuLeafEdit(nodeData: any) {
const ch = findChannelById(nodeData?.id)
if (ch) {
void selectChannel(ch)
activeTab.value = 'config'
}
}
function handleLeftMenuLeafDelete(nodeData: any) {
const ch = findChannelById(nodeData?.id)
if (ch) handleDeleteChannel(ch)