refactor PageTransition and PullDownRefresh
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
/>
|
||||
</t-dropdown-menu>
|
||||
</div>
|
||||
<t-pull-down-refresh v-model="isRefreshing" :refresh-timeout="30000" @refresh="refresh" @scrolltolower="loadMore">
|
||||
<app-pull-down-refresh v-model="isRefreshing" :refresh-timeout="30000" @refresh="refresh" @scrolltolower="loadMore">
|
||||
<t-cell-group v-if="categoryList.length" theme="card" class="list fix-last-cell-line">
|
||||
<t-swipe-cell v-for="(item, index) in categoryList" :key="item.id" class="item" v-entrance-transition="index * 30">
|
||||
<t-cell arrow hover @click="openEditDialog(item)">
|
||||
@@ -37,7 +37,7 @@
|
||||
</t-cell-group>
|
||||
<div v-else class="center-tips" v-text="emptyText" />
|
||||
<div v-if="categoryList.length" class="center-tips" v-text="isLoading ? '正在加载更多' : isNoMore ? '已经到底了' : '上拉加载更多'" />
|
||||
</t-pull-down-refresh>
|
||||
</app-pull-down-refresh>
|
||||
</div>
|
||||
<t-dialog
|
||||
v-model:visible="editDialogVisible"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-can="permissionCodes.product.read" class="product-list">
|
||||
<div ref="productListRef" v-can="permissionCodes.product.read" class="product-list">
|
||||
<div class="filters bg-white">
|
||||
<t-input
|
||||
v-model="keyword"
|
||||
@@ -34,7 +34,7 @@
|
||||
/>
|
||||
</t-dropdown-menu>
|
||||
</div>
|
||||
<t-pull-down-refresh v-model="isRefreshing" :refresh-timeout="30000" @refresh="refresh" @scrolltolower="loadMore">
|
||||
<app-pull-down-refresh v-model="isRefreshing" :refresh-timeout="30000" @refresh="refresh" @scrolltolower="loadMore">
|
||||
<t-cell-group
|
||||
v-if="productList.length"
|
||||
class="list fix-last-cell-line empty-cell-title-margin"
|
||||
@@ -84,14 +84,15 @@
|
||||
</t-cell-group>
|
||||
<div v-else class="center-tips" v-text="emptyText" />
|
||||
<div v-if="productList.length" class="center-tips" v-text="isLoading ? '正在加载更多' : isNoMore ? '已经到底了' : '上拉加载更多'" />
|
||||
</t-pull-down-refresh>
|
||||
</app-pull-down-refresh>
|
||||
</div>
|
||||
<nav-bar-action-menu v-if="canCreate" :actions="actionList" />
|
||||
<nav-bar-action-menu v-if="route.name === 'ProductList' && canCreate" :actions="actionList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type DropdownValue } from "tdesign-mobile-vue";
|
||||
import { useUserStore } from "timi-web-api";
|
||||
import { onBeforeRouteLeave } from "vue-router";
|
||||
import ProductAPI from "@/api/ProductAPI";
|
||||
import NavBarActionMenu from "@/components/NavBarActionMenu.vue";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
@@ -105,6 +106,8 @@ import { permissionCodes } from "@/utils/permission";
|
||||
defineOptions({ name: "ProductList" });
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const productListRef = ref<HTMLElement | null>(null);
|
||||
const navBarStore = useNavBarStore();
|
||||
const userStore = useUserStore();
|
||||
const canCreate = computed(() => userStore.hasPermissions(permissionCodes.product.create));
|
||||
@@ -129,6 +132,42 @@ const isRefreshing = ref(false);
|
||||
const isNoMore = ref(false);
|
||||
let keywordTimer: number | undefined;
|
||||
let requestToken = 0;
|
||||
let savedPageScrollTop: number | undefined;
|
||||
|
||||
function getProductListPage(): HTMLElement | null {
|
||||
return productListRef.value?.closest<HTMLElement>(".page") || null;
|
||||
}
|
||||
|
||||
function savePageScrollPosition(): void {
|
||||
const page = getProductListPage();
|
||||
if (!page) {
|
||||
return;
|
||||
}
|
||||
|
||||
savedPageScrollTop = page.scrollTop;
|
||||
}
|
||||
|
||||
function restorePageScrollPosition(): void {
|
||||
if (savedPageScrollTop === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollTop = savedPageScrollTop;
|
||||
requestAnimationFrame(() => {
|
||||
const page = getProductListPage();
|
||||
if (!page) {
|
||||
return;
|
||||
}
|
||||
|
||||
page.scrollTop = scrollTop;
|
||||
savedPageScrollTop = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function syncNavBar(): void {
|
||||
navBarStore.clearRight();
|
||||
navBarStore.setCustomRightVisible(route.name === "ProductList" && canCreate.value);
|
||||
}
|
||||
|
||||
const statusOptions = [
|
||||
{ label: "全部状态", value: "ALL" },
|
||||
@@ -265,8 +304,7 @@ async function toDetail(item: Product): Promise<void> {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
navBarStore.clearRight();
|
||||
navBarStore.setCustomRightVisible(canCreate.value);
|
||||
syncNavBar();
|
||||
try {
|
||||
await Promise.all([loadCategories(), fetch(true)]);
|
||||
} catch (error) {
|
||||
@@ -274,6 +312,15 @@ onMounted(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
restorePageScrollPosition();
|
||||
syncNavBar();
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
savePageScrollPosition();
|
||||
});
|
||||
|
||||
watch(keyword, () => {
|
||||
window.clearTimeout(keywordTimer);
|
||||
keywordTimer = window.setTimeout(async () => {
|
||||
@@ -285,6 +332,11 @@ watch(keyword, () => {
|
||||
}, 300);
|
||||
});
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
savePageScrollPosition();
|
||||
navBarStore.clearRight();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
navBarStore.clearRight();
|
||||
window.clearTimeout(keywordTimer);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-can="permissionCodes.productStock.read" class="product-stock-list">
|
||||
<div ref="productStockListRef" v-can="permissionCodes.productStock.read" class="product-stock-list">
|
||||
<div class="search bg-white">
|
||||
<t-input
|
||||
v-model="keyword"
|
||||
@@ -11,7 +11,7 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<t-pull-down-refresh
|
||||
<app-pull-down-refresh
|
||||
class="list-wrap empty-cell-title-margin"
|
||||
v-model="isRefreshing"
|
||||
:refresh-timeout="30000"
|
||||
@@ -64,12 +64,13 @@
|
||||
</t-cell-group>
|
||||
<div v-else class="center-tips" v-text="emptyText" />
|
||||
<div v-if="productList.length" class="center-tips" v-text="isLoading ? '正在加载更多' : isNoMore ? '已经到底了' : '上拉加载更多'" />
|
||||
</t-pull-down-refresh>
|
||||
</app-pull-down-refresh>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from "timi-web-api";
|
||||
import { onBeforeRouteLeave } from "vue-router";
|
||||
import ProductAPI from "@/api/ProductAPI";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { ProductStockProduct } from "@/types/Product";
|
||||
@@ -82,6 +83,7 @@ defineOptions({ name: "ProductStockList" });
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const productStockListRef = ref<HTMLElement | null>(null);
|
||||
const navBarStore = useNavBarStore();
|
||||
const userStore = useUserStore();
|
||||
const keyword = ref("");
|
||||
@@ -93,6 +95,37 @@ const isRefreshing = ref(false);
|
||||
const isNoMore = ref(false);
|
||||
let keywordTimer: number | undefined;
|
||||
let requestToken = 0;
|
||||
let savedPageScrollTop: number | undefined;
|
||||
|
||||
function getProductStockListPage(): HTMLElement | null {
|
||||
return productStockListRef.value?.closest<HTMLElement>(".page") || null;
|
||||
}
|
||||
|
||||
function savePageScrollPosition(): void {
|
||||
const page = getProductStockListPage();
|
||||
if (!page) {
|
||||
return;
|
||||
}
|
||||
|
||||
savedPageScrollTop = page.scrollTop;
|
||||
}
|
||||
|
||||
function restorePageScrollPosition(): void {
|
||||
if (savedPageScrollTop === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollTop = savedPageScrollTop;
|
||||
requestAnimationFrame(() => {
|
||||
const page = getProductStockListPage();
|
||||
if (!page) {
|
||||
return;
|
||||
}
|
||||
|
||||
page.scrollTop = scrollTop;
|
||||
savedPageScrollTop = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
const emptyText = computed(() => isLoading.value ? "正在加载产品" : keyword.value ? "未找到匹配产品" : "暂无启用库存管理的产品");
|
||||
|
||||
@@ -175,6 +208,14 @@ onMounted(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
restorePageScrollPosition();
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
savePageScrollPosition();
|
||||
});
|
||||
|
||||
watch(keyword, () => {
|
||||
window.clearTimeout(keywordTimer);
|
||||
keywordTimer = window.setTimeout(async () => {
|
||||
@@ -186,6 +227,11 @@ watch(keyword, () => {
|
||||
}, 300);
|
||||
});
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
savePageScrollPosition();
|
||||
navBarStore.clearRight();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
navBarStore.clearRight();
|
||||
window.clearTimeout(keywordTimer);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<t-dropdown-item v-model="direction" label="出入库" :options="directionOptions" @change="selectDirection" />
|
||||
</t-dropdown-menu>
|
||||
</div>
|
||||
<t-pull-down-refresh
|
||||
<app-pull-down-refresh
|
||||
v-model="isRefreshing"
|
||||
:refresh-timeout="30000"
|
||||
@refresh="refresh"
|
||||
@@ -84,7 +84,7 @@
|
||||
</t-cell-group>
|
||||
<div v-else class="center-tips" v-text="emptyText" />
|
||||
<div v-if="recordList.length" class="center-tips" v-text="loadStateText" />
|
||||
</t-pull-down-refresh>
|
||||
</app-pull-down-refresh>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
placeholder="搜索 SKU 名称、编码或规格"
|
||||
/>
|
||||
</div>
|
||||
<t-pull-down-refresh
|
||||
<app-pull-down-refresh
|
||||
class="list-wrap empty-cell-title-margin"
|
||||
v-model="isRefreshing"
|
||||
:refresh-timeout="30000"
|
||||
@@ -68,7 +68,7 @@
|
||||
<div v-else class="center-tips" v-text="emptyText" />
|
||||
<div v-if="skuStockList.length" class="center-tips" v-text="isLoading ? '正在加载更多' : isNoMore ? '已经到底了' : '上拉加载更多'" />
|
||||
</div>
|
||||
</t-pull-down-refresh>
|
||||
</app-pull-down-refresh>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user