Во всех разделах со списками заявок (На согласовании, На исполнении, Доработка, Мои заявки, Все заявки): - фильтры по типу, статусу и периоду даты создания, кнопка «Сбросить»; - сортировка по клику на заголовок: номер (по числам), тип, дата, статус. Без фильтров загрузка прежняя, постраничная. При активном фильтре или сортировке загружается полный список раздела и обрабатывается в памяти, иначе отсортировалась бы только текущая страница. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -414,6 +414,22 @@ export async function getApprovalQueue(
|
||||
kind: ApprovalQueueKind,
|
||||
page: number = 1
|
||||
): Promise<IPendingApprovalPage> {
|
||||
const { types, rows, debug } = await getApprovalQueueRows(context, kind);
|
||||
|
||||
const pageRows = rows.slice((page - 1) * PENDING_PAGE_SIZE, page * PENDING_PAGE_SIZE);
|
||||
await hydrateAuthors(context, types, pageRows);
|
||||
|
||||
return { rows: pageRows, totalRows: rows.length, debug };
|
||||
}
|
||||
|
||||
// ВСЯ очередь целиком, без нарезки на страницы, — для фильтров и сортировки
|
||||
// таблицы (см. requestsTableFilters.ts): отобрать и упорядочить честно можно
|
||||
// только полный набор, а не одну страницу. Автор не дочитывается — его
|
||||
// дочитывает вызывающая сторона для видимой страницы (hydratePendingAuthors).
|
||||
export async function getApprovalQueueRows(
|
||||
context: WebPartContext,
|
||||
kind: ApprovalQueueKind
|
||||
): Promise<{ rows: IMyRequest[]; types: IRequestType[]; debug: IMyRequestsTypeDebug[] }> {
|
||||
// Очереди действий («На согласовании» и «На исполнении») собираются ОДНОЙ
|
||||
// выборкой по обоим статусам, а разносятся уже по myStepStatus — ровно так же,
|
||||
// как это делают счётчики меню (см. getNavCountsInner). Смысл: это тот же
|
||||
@@ -433,10 +449,7 @@ export async function getApprovalQueue(
|
||||
// finalizeApprovalTarget перечитывает статус REST-ом.
|
||||
const rows = shared ? index.filter((r) => r.myStepStatus === QUEUE_STATUSES[kind][0]) : index;
|
||||
|
||||
const pageRows = rows.slice((page - 1) * PENDING_PAGE_SIZE, page * PENDING_PAGE_SIZE);
|
||||
await hydrateAuthors(context, types, pageRows);
|
||||
|
||||
return { rows: pageRows, totalRows: rows.length, debug };
|
||||
return { rows, types, debug };
|
||||
}
|
||||
|
||||
// С текстовым поиском: полный индекс (без автора) — фильтрует его вызывающая
|
||||
@@ -1409,6 +1422,18 @@ export async function getAllRequestsForType(
|
||||
page: number,
|
||||
query: string
|
||||
): Promise<IOwnRequestsPage> {
|
||||
const rows = await getAllRequestsRowsForType(context, type, query);
|
||||
return { rows: rows.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE), totalRows: rows.length };
|
||||
}
|
||||
|
||||
// Весь набор «Все заявки» одного типа, без нарезки на страницы — его же режет
|
||||
// getAllRequestsForType, и его же фильтрует/сортирует таблица (см.
|
||||
// requestsTableFilters.ts).
|
||||
export async function getAllRequestsRowsForType(
|
||||
context: WebPartContext,
|
||||
type: IRequestType,
|
||||
query: string
|
||||
): Promise<IMyRequest[]> {
|
||||
const sp = spForSite(type.siteUrl as string, context);
|
||||
const clauses = [pathClauseForTypes([type])];
|
||||
const text = freeTextClause(query);
|
||||
@@ -1421,9 +1446,7 @@ export async function getAllRequestsForType(
|
||||
|
||||
// kind: 'other' — заявка не обязательно текущего пользователя, открывается
|
||||
// только на просмотр (см. IMyRequest.kind, openRequest в RequestPortal.tsx).
|
||||
const rows = hitsToEntries({ PrimarySearchResults: hits }, [type]).map((e) => toMyRequestFromSearch(e, 'other'));
|
||||
|
||||
return { rows: rows.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE), totalRows: rows.length };
|
||||
return hitsToEntries({ PrimarySearchResults: hits }, [type]).map((e) => toMyRequestFromSearch(e, 'other'));
|
||||
}
|
||||
|
||||
// Свои заявки с конкретным статусом ЗАЯВКИ (не строки маршрута) — «Доработка»
|
||||
@@ -1516,6 +1539,45 @@ async function ownRequestsByStatus(
|
||||
return raceThenMerge(searchPromise, freshPromise, onPartial);
|
||||
}
|
||||
|
||||
/**
|
||||
* ВСЕ свои заявки (любого статуса), без нарезки на страницы — для фильтров и
|
||||
* сортировки таблицы (см. requestsTableFilters.ts). Отбор по статусу делает
|
||||
* вызывающая сторона в памяти.
|
||||
*
|
||||
* Search находит весь архив (searchOwnAll), а без текстового запроса к нему
|
||||
* подмешивается REST-довесок по свежести — тот же, что у постраничных
|
||||
* getOwnRequests (новые заявки и черновики) и getOwnRequestsByStatus
|
||||
* (недавно изменённые, напр. только что возвращённые на доработку). У одной и
|
||||
* той же заявки довесок свежее индекса, поэтому при совпадении берётся он.
|
||||
*/
|
||||
export async function getAllOwnRequests(
|
||||
context: WebPartContext,
|
||||
types: IRequestType[],
|
||||
query: string
|
||||
): Promise<IMyRequest[]> {
|
||||
if (types.length === 0) return [];
|
||||
|
||||
const searchPromise = searchOwnAll(context, types, query);
|
||||
if (query.trim()) return searchPromise;
|
||||
|
||||
const freshPromise = Promise.all(
|
||||
types.map(async (t) => {
|
||||
const [created, changed] = await Promise.all([
|
||||
getFreshOwnRowsForType(context, t),
|
||||
getRecentlyChangedOwnRowsForType(context, t),
|
||||
]);
|
||||
return created.concat(changed);
|
||||
})
|
||||
).then((r) => r.reduce((all, x) => all.concat(x), [] as IMyRequest[]));
|
||||
|
||||
const [searchRows, freshRows] = await Promise.all([searchPromise, freshPromise]);
|
||||
|
||||
const byKey = new Map<string, IMyRequest>();
|
||||
searchRows.forEach((r) => byKey.set(rowKey(r), r));
|
||||
freshRows.forEach((r) => byKey.set(rowKey(r), r));
|
||||
return Array.from(byKey.values()).sort((a, b) => b.created.localeCompare(a.created));
|
||||
}
|
||||
|
||||
// Сколько строк маршрута максимум забираем за раз при дочитывании комментариев:
|
||||
// на странице ≤20 заявок, у каждой обычно единицы шагов — запас многократный.
|
||||
const DECISION_COMMENT_ROW_LIMIT = 2000;
|
||||
|
||||
Reference in New Issue
Block a user