куча правок

This commit is contained in:
gorbenko.pavel
2026-08-07 11:06:15 +05:00
parent 9d9f390548
commit 7ef4f53a6a
16 changed files with 1417 additions and 13 deletions
@@ -0,0 +1,69 @@
// Общая механика «шагов, которые могут идти параллельно» — 1:1 повторяет
// request-portal/src/webparts/requestPortal/shared/approval/useMatrixEditor.ts
// (blockRanges/swapBlocks/withOrders/markParallel), только обобщена под
// generic-тип с полями order/parallelWithPrev, чтобы работать и с
// ManagedApprovalRow здесь, и не тащить зависимость на request-portal.
//
// Параллельность кодируется ЧЕРЕЗ РАВНЫЙ order у соседних строк — отдельного
// флага в SharePoint нет (см. FIELDS в requestManageService.ts: там только
// StepOrder). parallelWithPrev — это производное поле, вычисляемое на клиенте
// из совпадения order с предыдущей строкой (см. markParallel), а не то, что
// хранится в списке.
export interface IOrderedItem {
order: number;
parallelWithPrev?: boolean;
}
export interface IBlockRange {
start: number;
end: number;
}
/** Разбивает строки (уже отсортированные по order) на шаги — блоки с общим order. */
export function blockRanges<T extends IOrderedItem>(rows: T[]): IBlockRange[] {
const ranges: IBlockRange[] = [];
rows.forEach((r, i) => {
if (i === 0 || !r.parallelWithPrev) ranges.push({ start: i, end: i });
else ranges[ranges.length - 1].end = i;
});
return ranges;
}
export function blockIndexOf(ranges: IBlockRange[], row: number): number {
for (let i = 0; i < ranges.length; i += 1) {
if (row >= ranges[i].start && row <= ranges[i].end) return i;
}
return -1;
}
/** Меняет местами два блока (диапазона индексов), не трогая порядок строк внутри них. */
export function swapBlocks<T>(rows: T[], a: IBlockRange, b: IBlockRange): T[] {
return [
...rows.slice(0, a.start),
...rows.slice(b.start, b.end + 1),
...rows.slice(a.start, a.end + 1),
...rows.slice(b.end + 1)
];
}
/**
* Пересчитывает order начиная с floor+1: у параллельных строк (parallelWithPrev)
* order общий с предыдущей, у остальных — на единицу больше. Вызывается после
* КАЖДОГО перемещения/переключения параллельности, иначе order разъедется с тем,
* что показано на экране. floor — order последней ЗАБЛОКИРОВАННОЙ (уже решённой)
* строки фазы; строки после неё не могут получить order <= floor — это и есть
* гарантия «нельзя поставить согласующего выше того, кто уже одобрил».
*/
export function withOrdersFrom<T extends IOrderedItem>(rows: T[], floor: number): T[] {
let order = floor;
return rows.map((r, i) => {
const parallel = i > 0 && !!r.parallelWithPrev;
if (!parallel) order += 1;
return { ...r, order, parallelWithPrev: parallel };
});
}
/** Заполняет parallelWithPrev по факту совпадения order с предыдущей строкой (строки уже отсортированы по order). */
export function markParallel<T extends IOrderedItem>(rows: T[]): T[] {
return rows.map((r, i) => ({ ...r, parallelWithPrev: i > 0 && r.order === rows[i - 1].order }));
}