feat: настраиваемая колонка отображения в дереве справочника

- Добавлено поле data_tables.tree_display_column_index.
- Backend /api/directories/:id/rows/tree возвращает displayColumnIndex и
  формирует label для row-узлов на основе выбранной колонки.
- В редакторе справочника добавлен селектор «Колонка для отображения в дереве».
- DirectoryRowTree, DirectoryColumnTree, DirectoryTreeSelect,
  DirectoryTreeMultiSelect используют настроенную колонку.
- TableFieldSelect / TableFieldMultiSelect используют
  treeDisplayColumnIndex справочника как fallback, если в поле формы
  не задан displayColumn.
- Миграция 0057 + запись в _journal.json.
This commit is contained in:
2026-07-12 23:07:34 +03:00
parent 4e2a622265
commit 09861fc966
10 changed files with 76 additions and 57 deletions

View File

@@ -323,12 +323,13 @@ export function registerDataTableRoutes(app: import("express").Express): void {
return res.status(403).json({ error: 'Нет прав для редактирования таблицы' });
}
const { name, description, columns, treeColumnIndex } = req.body;
const { name, description, columns, treeColumnIndex, treeDisplayColumnIndex } = req.body;
const updates: Record<string, unknown> = {};
if (name !== undefined) updates.name = name;
if (description !== undefined) updates.description = description;
if (columns !== undefined) updates.columns = columns;
if (treeColumnIndex !== undefined) updates.treeColumnIndex = treeColumnIndex === null ? null : Number(treeColumnIndex);
if (treeDisplayColumnIndex !== undefined) updates.treeDisplayColumnIndex = treeDisplayColumnIndex === null ? null : Number(treeDisplayColumnIndex);
const updatedTable = await storage.updateDataTable(tableId, req.organizationId!, updates as Parameters<typeof storage.updateDataTable>[2]);
res.json({ table: updatedTable });
@@ -504,9 +505,16 @@ export function registerDataTableRoutes(app: import("express").Express): void {
const treeColumnIndex = table.treeColumnIndex;
const search = String(req.query.search || '').toLowerCase();
const displayColumnIndex = table.treeDisplayColumnIndex ?? 0;
const getRowLabel = (row: typeof rows[number]): string => {
const values = Array.isArray(row.values) ? row.values : [];
return String(values[displayColumnIndex] ?? values[0] ?? '') || '(без названия)';
};
type TreeNode =
| { type: 'group'; id: string; label: string; children: TreeNode[] }
| { type: 'row'; id: number; row: typeof rows[number] };
| { type: 'row'; id: number; label: string; row: typeof rows[number] };
const buildParentTree = (parentRows: typeof rows, parentId: number | null): TreeNode[] => {
const items = parentRows
@@ -514,10 +522,11 @@ export function registerDataTableRoutes(app: import("express").Express): void {
.sort((a, b) => a.position - b.position);
return items.map((row) => {
const children = buildParentTree(parentRows, row.id);
const label = getRowLabel(row);
if (children.length > 0) {
return { type: 'group' as const, id: `row-group:${row.id}`, label: String(row.values[0] ?? ''), children: [...children, { type: 'row' as const, id: row.id, row }] };
return { type: 'group' as const, id: `row-group:${row.id}`, label, children: [...children, { type: 'row' as const, id: row.id, label, row }] };
}
return { type: 'row' as const, id: row.id, row };
return { type: 'row' as const, id: row.id, label, row };
});
};
@@ -541,16 +550,16 @@ export function registerDataTableRoutes(app: import("express").Express): void {
type: 'group',
id: `group:${label}`,
label,
children: sorted.map((row) => ({ type: 'row', id: row.id, row })),
children: sorted.map((row) => ({ type: 'row', id: row.id, label: getRowLabel(row), row })),
});
}
if (rootRows.length > 0) {
rootRows.sort((a, b) => a.position - b.position);
nodes.push(...rootRows.map((row) => ({ type: 'row' as const, id: row.id, row })));
nodes.push(...rootRows.map((row) => ({ type: 'row' as const, id: row.id, label: getRowLabel(row), row })));
}
return nodes.sort((a, b) => {
const al = a.type === 'group' ? a.label : String(a.row.values[columnIndex] ?? '');
const bl = b.type === 'group' ? b.label : String(b.row.values[columnIndex] ?? '');
const al = a.type === 'group' ? a.label : getRowLabel(a.row);
const bl = b.type === 'group' ? b.label : getRowLabel(b.row);
return al.localeCompare(bl, 'ru');
});
};
@@ -560,7 +569,7 @@ export function registerDataTableRoutes(app: import("express").Express): void {
const result: TreeNode[] = [];
for (const node of nodes) {
if (node.type === 'row') {
const label = String(node.row.values[0] ?? '').toLowerCase();
const label = node.label.toLowerCase();
if (label.includes(query)) result.push(node);
} else {
const children = filterNodes(node.children, query);
@@ -579,7 +588,7 @@ export function registerDataTableRoutes(app: import("express").Express): void {
: buildParentTree(rows, null);
const nodes = search ? filterNodes(rawNodes, search) : rawNodes;
res.json({ columnIndex: treeColumnIndex, nodes });
res.json({ columnIndex: treeColumnIndex, displayColumnIndex, nodes });
} catch (error) {
console.error('Get table rows tree error:', error);
res.status(500).json({ error: 'Ошибка получения дерева строк' });