роутинг пагинации
This commit is contained in:
@@ -58,6 +58,8 @@ export interface IRoute {
|
||||
ownTab?: OwnTab;
|
||||
/** Фильтр по статусу заявки; задан только при nav='own' и ownTab='created'. */
|
||||
ownStatus?: string;
|
||||
/** Номер страницы таблицы (1-based). 1 — значение по умолчанию, в адрес не пишется. */
|
||||
page?: number;
|
||||
}
|
||||
|
||||
const ROUTE_CHANGE_EVENT = 'rp-route-change';
|
||||
@@ -88,6 +90,50 @@ const ROUTE_CHANGE_EVENT = 'rp-route-change';
|
||||
// вернувшись обратно, фильтр каждый раз сбрасывался на «Все статусы».
|
||||
const OWN_TAB_STORAGE_KEY = 'rp-own-tab';
|
||||
const OWN_STATUS_STORAGE_KEY = 'rp-own-status';
|
||||
// Страница запоминается ОТДЕЛЬНО ДЛЯ КАЖДОЙ таблицы (см. viewKeyOf): один общий
|
||||
// номер переносился бы между разделами, где у каждого свой объём данных, —
|
||||
// уйдя со страницы 3 «Моих заявок» в «На согласовании», где страница всего
|
||||
// одна, пользователь попал бы в пустоту.
|
||||
const PAGE_STORAGE_PREFIX = 'rp-page:';
|
||||
|
||||
/**
|
||||
* Ключ таблицы для запоминания страницы. Совпадает по смыслу с ListView в
|
||||
* RequestsTableView (плюс 'search'): у каждой подвкладки «Моих заявок» свой
|
||||
* список и, значит, своя страница.
|
||||
*/
|
||||
function viewKeyOf(nav: NavSection, ownTab: OwnTab | undefined): string {
|
||||
return nav === 'own' ? `own:${ownTab ?? 'created'}` : nav;
|
||||
}
|
||||
|
||||
function readStoredPage(viewKey: string): number | undefined {
|
||||
try {
|
||||
const raw = window.localStorage.getItem(PAGE_STORAGE_PREFIX + viewKey);
|
||||
if (!raw) return undefined;
|
||||
const page = Number(raw);
|
||||
return Number.isInteger(page) && page > 1 ? page : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Запомнить страницу текущей таблицы. Ключ выводится из самого маршрута —
|
||||
* вызывающей стороне не нужно знать, как таблицы различаются между собой.
|
||||
*/
|
||||
export function persistPage(route: IRoute, page: number): void {
|
||||
const viewKey = viewKeyOf(route.nav, route.ownTab);
|
||||
try {
|
||||
// Первая страница — значение по умолчанию: храним её ОТСУТСТВИЕМ ключа,
|
||||
// иначе в localStorage копился бы мусор из единиц по каждой таблице.
|
||||
if (page > 1) {
|
||||
window.localStorage.setItem(PAGE_STORAGE_PREFIX + viewKey, String(page));
|
||||
} else {
|
||||
window.localStorage.removeItem(PAGE_STORAGE_PREFIX + viewKey);
|
||||
}
|
||||
} catch {
|
||||
// localStorage недоступен (приватный режим и т.п.) — просто не сохраняем.
|
||||
}
|
||||
}
|
||||
|
||||
function readStoredOwnTab(): OwnTab {
|
||||
try {
|
||||
@@ -164,21 +210,29 @@ export function parseRoute(search: string = window.location.search): IRoute {
|
||||
const ownTab = nav === 'own' ? parseOwnTab(params.get('rptab') ?? undefined) : undefined;
|
||||
const ownStatus = ownTab === 'created' ? params.get('rpstatus') ?? readStoredOwnStatus() : undefined;
|
||||
|
||||
// Страница: из адреса, иначе — запомненная для ЭТОЙ таблицы. Мусор в
|
||||
// параметре (0, минус, буквы) трактуем как первую страницу, а не как ошибку:
|
||||
// ссылка приходит извне, ронять из-за неё раздел незачем.
|
||||
const pageRaw = Number(params.get('rppage'));
|
||||
const page =
|
||||
Number.isInteger(pageRaw) && pageRaw > 1 ? pageRaw : readStoredPage(viewKeyOf(nav, ownTab)) ?? 1;
|
||||
|
||||
// Открытая заявка (панель поверх раздела) — при полном наборе параметров.
|
||||
// Раздел под панелью сохраняется целиком, вместе с подвкладкой и фильтром:
|
||||
// закрыв панель, пользователь должен вернуться ровно в тот же список.
|
||||
// Раздел под панелью сохраняется целиком, вместе с подвкладкой, фильтром и
|
||||
// страницей: закрыв панель, пользователь должен вернуться ровно в тот же
|
||||
// список и на то же место в нём.
|
||||
if (mode === 'approve' && formKey && itemId && !isNaN(itemId)) {
|
||||
return { mode: 'approve', nav, formKey, itemId, ownTab, ownStatus };
|
||||
return { mode: 'approve', nav, formKey, itemId, ownTab, ownStatus, page };
|
||||
}
|
||||
// Редактирование собственной незавершённой заявки — та же панель, но с формой.
|
||||
if (mode === 'edit' && formKey && itemId && !isNaN(itemId)) {
|
||||
return { mode: 'edit', nav, formKey, itemId, ownTab, ownStatus };
|
||||
return { mode: 'edit', nav, formKey, itemId, ownTab, ownStatus, page };
|
||||
}
|
||||
// Создание производной заявки с предзаполнением из исходной.
|
||||
if (formKey && sourceId && !isNaN(sourceId)) {
|
||||
return { mode: 'create', nav: 'new', formKey, sourceId };
|
||||
}
|
||||
return { mode: 'submit', nav, ownTab, ownStatus };
|
||||
return { mode: 'submit', nav, ownTab, ownStatus, page };
|
||||
}
|
||||
|
||||
// Собрать query-строку маршрута, сохранив посторонние параметры страницы.
|
||||
@@ -191,6 +245,7 @@ function buildQuery(route: IRoute): string {
|
||||
params.delete('rpsource');
|
||||
params.delete('rptab');
|
||||
params.delete('rpstatus');
|
||||
params.delete('rppage');
|
||||
|
||||
if (route.nav !== 'new') {
|
||||
params.set('rpnav', route.nav);
|
||||
@@ -202,6 +257,8 @@ function buildQuery(route: IRoute): string {
|
||||
if (ownTab !== 'created') params.set('rptab', ownTab);
|
||||
else if (route.ownStatus) params.set('rpstatus', route.ownStatus);
|
||||
}
|
||||
// Первая страница — тоже значение по умолчанию (см. выше про чистую ссылку).
|
||||
if (route.page && route.page > 1) params.set('rppage', String(route.page));
|
||||
if ((route.mode === 'approve' || route.mode === 'edit') && route.formKey && route.itemId) {
|
||||
params.set('rpmode', route.mode);
|
||||
params.set('rptype', route.formKey);
|
||||
|
||||
Reference in New Issue
Block a user