- React 18 + TypeScript strict + AntV X6 2.x + AntD 5 + Zustand - Custom nodes: SiteNode, CrossDeviceNode, SpliceNode, DeviceNode, CardNode - 8-слойный автолейаут, порты (left/right), линии с цветами по статусу - Toolbar, дерево навигации, карточка объекта, таблица соединений - Контекстные меню, легенда, drag линий/нод, создание линий из портов - Моковые данные: 3 сайта, 10 устройств, 15 линий Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
77 lines
2.1 KiB
TypeScript
77 lines
2.1 KiB
TypeScript
import type { Line, Port, Device, SchemaData } from '../../../types/index.ts';
|
|
import { STATUS_COLORS, STATUS_LABELS } from '../../../constants/statusColors.ts';
|
|
|
|
export interface LineGroup {
|
|
key: string;
|
|
deviceAId: string;
|
|
deviceZId: string;
|
|
lines: Line[];
|
|
representativeLine: Line;
|
|
count: number;
|
|
}
|
|
|
|
function getDeviceIdForPort(
|
|
portId: string,
|
|
data: SchemaData,
|
|
): string | null {
|
|
const port = data.ports.find((p: Port) => p.id === portId);
|
|
return port ? port.deviceId : null;
|
|
}
|
|
|
|
export function groupLinesByDevicePair(data: SchemaData): LineGroup[] {
|
|
const groupMap = new Map<string, Line[]>();
|
|
|
|
for (const line of data.lines) {
|
|
const devA = getDeviceIdForPort(line.portAId, data);
|
|
const devZ = getDeviceIdForPort(line.portZId, data);
|
|
if (!devA || !devZ) continue;
|
|
|
|
const key = [devA, devZ].sort().join('::');
|
|
const existing = groupMap.get(key);
|
|
if (existing) {
|
|
existing.push(line);
|
|
} else {
|
|
groupMap.set(key, [line]);
|
|
}
|
|
}
|
|
|
|
const groups: LineGroup[] = [];
|
|
for (const [key, lines] of groupMap.entries()) {
|
|
const [deviceAId, deviceZId] = key.split('::');
|
|
groups.push({
|
|
key,
|
|
deviceAId,
|
|
deviceZId,
|
|
lines,
|
|
representativeLine: lines[0],
|
|
count: lines.length,
|
|
});
|
|
}
|
|
|
|
return groups;
|
|
}
|
|
|
|
export function getGroupTooltip(
|
|
group: LineGroup,
|
|
devices: Device[],
|
|
): string {
|
|
const devA = devices.find((d) => d.id === group.deviceAId);
|
|
const devZ = devices.find((d) => d.id === group.deviceZId);
|
|
|
|
const statusCounts = new Map<string, number>();
|
|
for (const line of group.lines) {
|
|
const current = statusCounts.get(line.status) ?? 0;
|
|
statusCounts.set(line.status, current + 1);
|
|
}
|
|
|
|
let tooltip = `${devA?.name ?? '?'} — ${devZ?.name ?? '?'}\n`;
|
|
tooltip += `Линий: ${group.count}\n`;
|
|
for (const [status, count] of statusCounts.entries()) {
|
|
const color = STATUS_COLORS[status as keyof typeof STATUS_COLORS];
|
|
const label = STATUS_LABELS[status as keyof typeof STATUS_LABELS];
|
|
tooltip += ` ${label}: ${count} (${color.border})\n`;
|
|
}
|
|
|
|
return tooltip;
|
|
}
|