add listDataEvents.ts

This commit is contained in:
Timi
2026-09-22 01:29:34 +08:00
parent c4d71078b3
commit fd4146476b
22 changed files with 377 additions and 49 deletions
+102 -17
View File
@@ -132,6 +132,7 @@ import {
} from "@/types/SalesOrderFilter";
import { buildPresetDateRange, parseDateTime } from "@/utils/date";
import { getErrorMessage } from "@/utils/error";
import { events, isCurrentStoreEvent, listDataEventKeys, type ListDataChangedPayload } from "@/utils/listDataEvents";
import { Message } from "@/utils/message";
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
@@ -153,6 +154,10 @@ const canRefund = computed(() => userStore.hasPermissions(permissionCodes.fund.r
const canStoreManager = computed(() => userStore.hasRoles(gaoRoleCodes.storeManager));
let savedPageScrollTop: number | undefined;
let shouldReloadOnActivate = false;
let eventRefreshPromise: Promise<void> | undefined;
let hasPendingEventRefresh = false;
let unregisterSalesOrderListEvent: (() => void) | undefined;
let isDisposed = false;
function getSalesOrderPage(): HTMLElement | null {
return salesOrderListRef.value?.closest<HTMLElement>(".page") || null;
@@ -216,6 +221,7 @@ const selectedPackageName = computed(() => selectedPackage.value?.name || queryV
const filterVisible = ref(false);
const appliedFilter = ref<SalesOrderListFilter>(createDefaultSalesOrderFilter());
const filterStorageKey = computed(() => `${SALES_ORDER_FILTER_STORAGE_KEY}:${currentStoreStore.currentStoreId || "default"}:${packageConfigId.value || "all"}`);
let cachedPackageConfigId = "";
const orderTypeOptions: Array<{ label: string; value: GaoSalesOrderType }> = [
{ label: "套餐订单", value: "PACKAGE" },
{ label: "产品订单", value: "DIRECT_PRODUCT" }
@@ -333,6 +339,12 @@ function applyRouteFilters(): void {
appliedFilter.value = nextFilter;
}
function syncCachedRouteContext(): void {
if (route.name === "SalesOrderList") {
cachedPackageConfigId = packageConfigId.value;
}
}
async function loadRoutePackage(): Promise<void> {
selectedPackage.value = undefined;
if (!packageConfigId.value) {
@@ -385,6 +397,7 @@ function resetForProductTab(): void {
refreshing.value = false;
noMore.value = false;
savedPageScrollTop = undefined;
cachedPackageConfigId = "";
localStorage.removeItem(filterStorageKey.value);
const page = getSalesOrderPage();
if (page) {
@@ -407,44 +420,83 @@ function getPaymentStatusValues(valueList: SalesOrderPaymentFilter[]): GaoSalesP
return paymentStatusValues.filter(value => valueSet.has(value));
}
function buildPageFilter(): SalesOrderPage {
function buildPageFilter(index = pageIndex.value): SalesOrderPage {
const filter = appliedFilter.value;
const [beginText, endText] = filter.dateRange;
const currentPackageConfigId = cachedPackageConfigId || packageConfigId.value;
return {
customerId: customerId.value || undefined,
packageConfigId: packageConfigId.value || undefined,
packageConfigId: currentPackageConfigId || undefined,
keyword: keyword.value || undefined,
orderTypeList: packageConfigId.value ? ["PACKAGE"] : getEffectiveList(filter.orderTypeList, orderTypeOptions.map(item => item.value)),
orderTypeList: currentPackageConfigId ? ["PACKAGE"] : getEffectiveList(filter.orderTypeList, orderTypeOptions.map(item => item.value)),
statusList: getEffectiveList(filter.statusList, statusOptions.map(item => item.value)),
paymentStatusList: getEffectiveList(getPaymentStatusValues(filter.paymentStatusList), paymentStatusValues),
refundStatusList: getEffectiveList(filter.refundStatusList, refundOptions.map(item => item.value)),
beginAt: beginText ? parseDateTime(beginText, false).getTime() : undefined,
endAt: endText ? parseDateTime(endText, true).getTime() : undefined,
index: pageIndex.value,
index,
size: pageSize
};
}
async function fetchList(reset: boolean): Promise<void> {
if (loading.value || (!reset && noMore.value)) return;
async function fetchList(reset: boolean, preserveScroll = false): Promise<void> {
if (!reset && (loading.value || noMore.value)) return;
const loadedPageCount = reset && preserveScroll ? Math.max(pageIndex.value, 1) : 1;
const preserveScrollTop = preserveScroll ? getSalesOrderPage()?.scrollTop : undefined;
if (reset) {
pageIndex.value = 0;
total.value = 0;
noMore.value = false;
list.value = [];
if (!preserveScroll) {
const page = getSalesOrderPage();
if (page) {
page.scrollTop = 0;
}
}
}
const requestId = ++listRequestId;
loading.value = true;
try {
const result = await SalesOrderAPI.list(buildPageFilter());
if (requestId !== listRequestId) {
return;
if (reset && preserveScroll) {
const nextList: SalesOrder[] = [];
let nextTotal = 0;
let nextPageIndex = 0;
for (let index = 0; index < loadedPageCount; index += 1) {
const result = await SalesOrderAPI.list(buildPageFilter(index));
if (requestId !== listRequestId) {
return;
}
const rows = result.list || [];
nextList.push(...rows);
nextTotal = result.total || 0;
nextPageIndex += 1;
if (rows.length < pageSize || nextTotal <= nextList.length) {
break;
}
}
list.value = nextList;
total.value = nextTotal;
pageIndex.value = nextPageIndex;
noMore.value = nextList.length < pageSize || nextTotal <= nextList.length;
} else {
const result = await SalesOrderAPI.list(buildPageFilter());
if (requestId !== listRequestId) {
return;
}
const rows = result.list || [];
list.value = reset ? rows : list.value.concat(rows);
total.value = result.total || 0;
pageIndex.value += 1;
noMore.value = rows.length < pageSize || total.value <= list.value.length;
}
if (preserveScrollTop !== undefined) {
requestAnimationFrame(() => {
const page = getSalesOrderPage();
if (page) {
page.scrollTop = preserveScrollTop;
}
});
}
const rows = result.list || [];
list.value = reset ? rows : list.value.concat(rows);
total.value = result.total || 0;
pageIndex.value += 1;
noMore.value = rows.length < pageSize || total.value <= list.value.length;
} finally {
if (requestId === listRequestId) {
loading.value = false;
@@ -452,9 +504,35 @@ async function fetchList(reset: boolean): Promise<void> {
}
}
async function reloadList(): Promise<void> {
async function reloadList(preserveScroll = false): Promise<void> {
await customerOrderSummaryRef.value?.refresh();
await fetchList(true);
await fetchList(true, preserveScroll);
}
function startEventRefresh(): void {
if (isDisposed) {
return;
}
eventRefreshPromise = reloadList(true)
.catch(error => Message.error(getErrorMessage(error, "销售订单加载失败")))
.finally(() => {
eventRefreshPromise = undefined;
if (!isDisposed && hasPendingEventRefresh) {
hasPendingEventRefresh = false;
startEventRefresh();
}
});
}
function requestEventRefresh(payload?: ListDataChangedPayload): void {
if (!isCurrentStoreEvent(payload)) {
return;
}
if (eventRefreshPromise) {
hasPendingEventRefresh = true;
return;
}
startEventRefresh();
}
async function refresh(): Promise<void> {
@@ -550,6 +628,7 @@ watch(routeFilterKey, async value => {
return;
}
lastAppliedRouteFilterKey = value;
syncCachedRouteContext();
applyRouteFilters();
try {
await loadRouteCustomer();
@@ -574,6 +653,8 @@ watch(() => currentStoreStore.currentStoreId, async (value, oldValue) => {
});
onMounted(async () => {
unregisterSalesOrderListEvent = events.register(listDataEventKeys.salesOrderChanged, (payload: ListDataChangedPayload) => requestEventRefresh(payload));
syncCachedRouteContext();
navBarStore.clearRight();
navBarStore.setCustomRightVisible(!!actions.value.length);
applyRouteFilters();
@@ -589,6 +670,7 @@ onMounted(async () => {
});
onActivated(async () => {
syncCachedRouteContext();
if (shouldReloadOnActivate) {
shouldReloadOnActivate = false;
applyRouteFilters();
@@ -625,6 +707,9 @@ onBeforeRouteLeave(to => {
});
onUnmounted(() => {
isDisposed = true;
unregisterSalesOrderListEvent?.();
unregisterSalesOrderListEvent = undefined;
navBarStore.clearRight();
search.cancel();
});