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> <template>
<div class="page-layout-root"> <div class="page-layout-root">
<router-view v-slot="{ Component, route }"> <router-view v-slot="{ Component, route }">
<Transition <keep-alive :include="cacheList" :max="keepAliveMax">
:name="transitionName || 'page-none'" <component
:css="Boolean(transitionName)" :is="Component"
:mode="transitionName ? 'out-in' : undefined" v-if="Component"
> :key="getComponentKey(route)"
<keep-alive :include="cacheList" :max="keepAliveMax"> />
<component </keep-alive>
:is="Component"
v-if="Component"
:key="getComponentKey(route)"
/>
</keep-alive>
</Transition>
</router-view> </router-view>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, nextTick, ref, watch } from 'vue'; import { computed, watch } from 'vue';
import type { RouteLocationNormalized } from 'vue-router'; import type { RouteLocationNormalized } from 'vue-router';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useTabBarStore } from '@/store'; import { useTabBarStore } from '@/store';
import { MAX_KEEP_ALIVE_TABS } from '@/store/modules/tab-bar'; import { MAX_KEEP_ALIVE_TABS } from '@/store/modules/tab-bar';
import { consumeSkipPageTransition } from '@/utils/page-transition-flag';
const route = useRoute(); const route = useRoute();
const tabBarStore = useTabBarStore(); const tabBarStore = useTabBarStore();
const cacheList = computed(() => tabBarStore.getCacheList); const cacheList = computed(() => tabBarStore.getCacheList);
const keepAliveMax = MAX_KEEP_ALIVE_TABS; 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( watch(
() => String(route.name || route.path), () => String(route.name || route.path),
(name) => { (name) => {
if (!name) return; if (!name || route.meta?.ignoreCache) return;
// LRU:回访时刷新缓存顺序 tabBarStore.touchCache(name);
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);
});
}, },
{ immediate: true }, { immediate: true },
); );
@ -89,22 +56,4 @@
flex: 1; flex: 1;
min-height: 0; 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> </style>

View File

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

View File

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

View File

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