fix iOS back transition
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
import type { RouteLocationNormalized } from "vue-router";
|
||||
import { viewDepthKey } from "vue-router";
|
||||
import { useGlobalUIStore } from "@/store/globalUIStore";
|
||||
import { hasProgrammaticBackNavigation } from "@/utils/backNavigationSignal";
|
||||
|
||||
defineOptions({
|
||||
name: "PageTransition"
|
||||
@@ -26,6 +27,8 @@ const viewDepth = inject(viewDepthKey, 0);
|
||||
|
||||
const transitionName = ref("");
|
||||
const hasTransition = ref(false);
|
||||
// iOS 左滑返回由系统接管,页面侧不应叠加离场动画。
|
||||
const isIOS = /iP(ad|hone|od)/i.test(window.navigator.userAgent);
|
||||
const pageBackground = computed(() => globalUIStore.bodyBackground);
|
||||
const currentDepth = computed(() => Number(unref(viewDepth)));
|
||||
const pageKey = computed(() => {
|
||||
@@ -72,6 +75,17 @@ const unregisterGuard = router.beforeEach((to, from) => {
|
||||
|
||||
const toDepth = calcDepth(to);
|
||||
const fromDepth = calcDepth(from);
|
||||
const isBackwardNavigation = toDepth < fromDepth;
|
||||
// 主动触发的返回(如导航栏返回)保留动画。
|
||||
const isProgrammaticBack = hasProgrammaticBackNavigation();
|
||||
// 仅在 iOS 系统返回手势下关闭动画。
|
||||
const shouldDisableTransition = isIOS && isBackwardNavigation && !isProgrammaticBack;
|
||||
|
||||
if (shouldDisableTransition) {
|
||||
transitionName.value = "";
|
||||
hasTransition.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fromDepth < toDepth) {
|
||||
transitionName.value = "push-left";
|
||||
|
||||
@@ -129,33 +129,29 @@ function resolveTabValue(path: string): string {
|
||||
if (path === "/" || path.startsWith("/files/")) {
|
||||
return "/";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(newPath: string) => {
|
||||
tabVal.value = resolveTabValue(newPath);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
watch(() => route.path, (newPath: string) => {
|
||||
tabVal.value = resolveTabValue(newPath);
|
||||
}, { immediate: true });
|
||||
|
||||
function onChangeTab(value: string): void {
|
||||
async function onChangeTab(value: string): Promise<void> {
|
||||
if (value === "__music__") {
|
||||
musicPlayerStore.setPopupVisible(true);
|
||||
tabVal.value = resolveTabValue(route.path);
|
||||
return;
|
||||
}
|
||||
|
||||
router.push(value);
|
||||
if (resolveTabValue(route.path) === value) {
|
||||
return;
|
||||
}
|
||||
await router.replace(value);
|
||||
}
|
||||
|
||||
const isTabBarPaddingNeeded = computed(() => {
|
||||
if (!tabBarStore.isShowing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return route.meta.tabBarPadding !== false;
|
||||
});
|
||||
|
||||
@@ -163,7 +159,6 @@ const tabBarPadding = computed(() => {
|
||||
if (isTabBarPaddingNeeded.value) {
|
||||
return "6rem";
|
||||
}
|
||||
|
||||
return "0rem";
|
||||
});
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ function saveConnect(): void {
|
||||
theme: "success",
|
||||
message: "连接配置已保存"
|
||||
});
|
||||
router.back();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -14,6 +14,10 @@ import ServerDetail from "@/pages/dashboard/ServerDashboard/ServerDetail.vue";
|
||||
import ServerPerformanceDetail from "@/pages/dashboard/ServerDashboard/ServerPerformanceDetail.vue";
|
||||
import ServerPartitionsDetail from "@/pages/dashboard/ServerDashboard/ServerPartitionsDetail.vue";
|
||||
import DockerContainerDetail from "@/pages/dashboard/DockerDashboard/DockerContainerDetail.vue";
|
||||
import {
|
||||
clearProgrammaticBackNavigation,
|
||||
markProgrammaticBackNavigation
|
||||
} from "@/utils/backNavigationSignal";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory("/"),
|
||||
@@ -180,6 +184,23 @@ const router = createRouter({
|
||||
]
|
||||
});
|
||||
|
||||
const rawBack = router.back.bind(router);
|
||||
const rawGo = router.go.bind(router);
|
||||
|
||||
// 全局代理:所有代码触发的后退都自动标记为“主动返回”。
|
||||
router.back = () => {
|
||||
markProgrammaticBackNavigation();
|
||||
rawBack();
|
||||
};
|
||||
|
||||
// 覆盖 go(-1 / -n) 场景,保证与 back() 行为一致。
|
||||
router.go = (delta) => {
|
||||
if (delta < 0) {
|
||||
markProgrammaticBackNavigation();
|
||||
}
|
||||
rawGo(delta);
|
||||
};
|
||||
|
||||
router.beforeEach((to: RouteLocationNormalized) => {
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
@@ -200,6 +221,8 @@ router.beforeEach((to: RouteLocationNormalized) => {
|
||||
});
|
||||
|
||||
router.afterEach((to: RouteLocationNormalized) => {
|
||||
// 一次导航结束后清理信号,防止影响后续动画判定。
|
||||
clearProgrammaticBackNavigation();
|
||||
const globalUIStore = useGlobalUIStore();
|
||||
const targetBackground = typeof to.meta.bodyBackground === "string" ? to.meta.bodyBackground : DEFAULT_BODY_BACKGROUND;
|
||||
globalUIStore.setBodyBackground(targetBackground);
|
||||
|
||||
28
src/utils/backNavigationSignal.ts
Normal file
28
src/utils/backNavigationSignal.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
let programmaticBackAt = 0;
|
||||
|
||||
// 主动返回信号有效期,避免历史信号污染后续导航
|
||||
const PROGRAMMATIC_BACK_SIGNAL_TTL_MS = 1200;
|
||||
|
||||
// 由代码触发 router.back / router.go(-1) 时打标
|
||||
export function markProgrammaticBackNavigation(): void {
|
||||
programmaticBackAt = Date.now();
|
||||
}
|
||||
|
||||
// 读取主动返回信号,超时后自动清理
|
||||
export function hasProgrammaticBackNavigation(): boolean {
|
||||
if (programmaticBackAt < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasSignal = Date.now() - programmaticBackAt <= PROGRAMMATIC_BACK_SIGNAL_TTL_MS;
|
||||
if (!hasSignal) {
|
||||
programmaticBackAt = 0;
|
||||
}
|
||||
|
||||
return hasSignal;
|
||||
}
|
||||
|
||||
// 在导航结束后统一清理信号
|
||||
export function clearProgrammaticBackNavigation(): void {
|
||||
programmaticBackAt = 0;
|
||||
}
|
||||
Reference in New Issue
Block a user