fix: scroll behaviour
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
<AppDebugMessage v-if="settings.app_debug"/>
|
||||
|
||||
<RouterView v-slot="{ Component, route }">
|
||||
<KeepAlive include="Home" :key="filtersStore.paramsHashForRouter">
|
||||
<KeepAlive include="Home,Products" :key="filtersStore.paramsHashForRouter">
|
||||
<component :is="Component" :key="route.fullPath"/>
|
||||
</KeepAlive>
|
||||
</RouterView>
|
||||
|
||||
@@ -34,6 +34,7 @@ import FullScreenImageViewer from "@/components/FullScreenImageViewer.vue";
|
||||
import {useHapticFeedback} from "@/composables/useHapticFeedback.js";
|
||||
import {onMounted, onUnmounted, ref} from "vue";
|
||||
import {useHapticScroll} from "@/composables/useHapticScroll.js";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const emit = defineEmits(['onLoad']);
|
||||
|
||||
@@ -44,6 +45,7 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const haptic = useHapticFeedback();
|
||||
const hapticScroll = useHapticScroll();
|
||||
const pagination = {
|
||||
|
||||
@@ -35,6 +35,22 @@ export const router = createRouter({
|
||||
history: createWebHashHistory('/image/catalog/tgshopspa/'),
|
||||
routes,
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
// Для страницы товара всегда скроллим наверх мгновенно
|
||||
if (to.name === 'product.show') {
|
||||
return {top: 0, behavior: 'instant'};
|
||||
}
|
||||
|
||||
// Для страницы категории скролл будет восстановлен в компоненте через onActivated
|
||||
// Здесь просто предотвращаем автоматический скролл наверх
|
||||
if (to.name === 'product.categories.show') {
|
||||
// Если возвращаемся назад на категорию - используем savedPosition
|
||||
if (savedPosition) {
|
||||
return savedPosition;
|
||||
}
|
||||
return false; // Не скроллить автоматически
|
||||
}
|
||||
|
||||
// Для остальных страниц используем savedPosition если есть, иначе наверх
|
||||
if (savedPosition) {
|
||||
return savedPosition;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ export const useProductsStore = defineStore('products', {
|
||||
loadFinished: false,
|
||||
savedScrollY: 0,
|
||||
currentLoadedParamsHash: null,
|
||||
lastCategoryId: null,
|
||||
scrollPositions: {}, // Сохраняем позиции скролла для каждой категории
|
||||
}),
|
||||
|
||||
getters: {
|
||||
@@ -118,5 +120,16 @@ export const useProductsStore = defineStore('products', {
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
saveScrollPosition(categoryId, position) {
|
||||
if (categoryId) {
|
||||
this.scrollPositions[categoryId] = position;
|
||||
this.savedScrollY = position;
|
||||
}
|
||||
},
|
||||
|
||||
getScrollPosition(categoryId) {
|
||||
return this.scrollPositions[categoryId] || 0;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -305,6 +305,9 @@ function setQuantity(newQuantity) {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// Явно сбрасываем скролл наверх при открытии страницы товара
|
||||
window.scrollTo({ top: 0, behavior: 'instant' });
|
||||
|
||||
isLoading.value = true;
|
||||
imagesLoaded.value = false;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<script setup>
|
||||
import ProductsList from "@/components/ProductsList.vue";
|
||||
import {onMounted, ref} from "vue";
|
||||
import {onMounted, onActivated, onDeactivated, ref, nextTick} from "vue";
|
||||
import {useRoute} from "vue-router";
|
||||
import {useProductsStore} from "@/stores/ProductsStore.js";
|
||||
import {useCategoriesStore} from "@/stores/CategoriesStore.js";
|
||||
@@ -31,12 +31,77 @@ const yaMetrika = useYaMetrikaStore();
|
||||
const categoryId = route.params.category_id ?? null;
|
||||
const category = ref(null);
|
||||
|
||||
onMounted(async () => {
|
||||
console.debug("[Category] Category Products Mounted");
|
||||
console.debug("[Category] Load products for category: ", categoryId);
|
||||
category.value = await categoriesStore.findCategoryById(categoryId);
|
||||
console.debug("[Category] Category Name: ", category.value?.name);
|
||||
// Опционально сохраняем позицию при деактивации (KeepAlive автоматически сохраняет DOM состояние)
|
||||
onDeactivated(() => {
|
||||
const currentCategoryId = route.params.category_id ?? null;
|
||||
if (currentCategoryId) {
|
||||
const scrollY = window.scrollY || window.pageYOffset || document.documentElement.scrollTop;
|
||||
if (scrollY > 0) {
|
||||
productsStore.saveScrollPosition(currentCategoryId, scrollY);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Обработчик активации компонента (KeepAlive)
|
||||
onActivated(async () => {
|
||||
console.debug("[Category] Products component activated");
|
||||
const currentCategoryId = route.params.category_id ?? null;
|
||||
|
||||
// Если категория изменилась, загружаем новые товары
|
||||
if (currentCategoryId !== categoryId) {
|
||||
console.debug("[Category] Category changed, reloading products");
|
||||
const newCategory = await categoriesStore.findCategoryById(currentCategoryId);
|
||||
category.value = newCategory;
|
||||
window.document.title = `${newCategory?.name ?? 'Неизвестная категория'}`;
|
||||
yaMetrika.pushHit(route.path, {
|
||||
title: `${newCategory?.name ?? 'Неизвестная категория'}`,
|
||||
});
|
||||
|
||||
productsStore.reset();
|
||||
productsStore.filtersFullUrl = route.fullPath;
|
||||
await productsStore.loadProducts({
|
||||
operand: "AND",
|
||||
rules: {
|
||||
RULE_PRODUCT_CATEGORIES: {
|
||||
criteria: {
|
||||
product_category_ids: {
|
||||
type: "product_categories",
|
||||
params: {
|
||||
operator: "contains",
|
||||
value: [currentCategoryId]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
productsStore.lastCategoryId = currentCategoryId;
|
||||
return;
|
||||
}
|
||||
|
||||
// Если возвращаемся на ту же категорию - не перезагружаем товары
|
||||
const isReturningToSameCategory = productsStore.lastCategoryId === currentCategoryId &&
|
||||
productsStore.products.data.length > 0 &&
|
||||
productsStore.filtersFullUrl === route.fullPath;
|
||||
|
||||
if (isReturningToSameCategory) {
|
||||
// KeepAlive должен восстановить позицию автоматически, но если нужно - восстанавливаем явно
|
||||
await nextTick();
|
||||
const savedPosition = productsStore.getScrollPosition(currentCategoryId);
|
||||
if (savedPosition > 0) {
|
||||
// Используем requestAnimationFrame для плавного восстановления
|
||||
requestAnimationFrame(() => {
|
||||
window.scrollTo({
|
||||
top: savedPosition,
|
||||
behavior: 'instant'
|
||||
});
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Первая загрузка категории
|
||||
category.value = await categoriesStore.findCategoryById(currentCategoryId);
|
||||
window.document.title = `${category.value?.name ?? 'Неизвестная категория'}`;
|
||||
yaMetrika.pushHit(route.path, {
|
||||
title: `${category.value?.name ?? 'Неизвестная категория'}`,
|
||||
@@ -52,9 +117,7 @@ onMounted(async () => {
|
||||
type: "product_categories",
|
||||
params: {
|
||||
operator: "contains",
|
||||
value: [
|
||||
categoryId
|
||||
]
|
||||
value: [currentCategoryId]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,9 +136,7 @@ onMounted(async () => {
|
||||
type: "product_categories",
|
||||
params: {
|
||||
operator: "contains",
|
||||
value: [
|
||||
categoryId
|
||||
]
|
||||
value: [currentCategoryId]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,5 +144,17 @@ onMounted(async () => {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
productsStore.lastCategoryId = currentCategoryId;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
// Только для первой загрузки, если компонент не был в KeepAlive
|
||||
const currentCategoryId = route.params.category_id ?? null;
|
||||
category.value = await categoriesStore.findCategoryById(currentCategoryId);
|
||||
window.document.title = `${category.value?.name ?? 'Неизвестная категория'}`;
|
||||
yaMetrika.pushHit(route.path, {
|
||||
title: `${category.value?.name ?? 'Неизвестная категория'}`,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user