|
|
@@ -9,12 +9,10 @@ export interface CategoryDef {
|
|
|
symbolSize: number; // 节点大小
|
|
|
}
|
|
|
|
|
|
-/** 4 类节点:省局、执法处、煤矿、测风点 */
|
|
|
+/** 2 类节点:测风点位(注册的 level=4 测风点)、数据布点(Windrect 测风装置) */
|
|
|
export const categories: CategoryDef[] = [
|
|
|
- { name: '省局', color: '#0960bd', symbol: 'diamond', symbolSize: 50 },
|
|
|
- { name: '执法处', color: '#1890ff', symbol: 'roundRect', symbolSize: 40 },
|
|
|
- { name: '煤矿', color: '#52c41a', symbol: 'circle', symbolSize: 35 },
|
|
|
- { name: '测风点', color: '#fa8c16', symbol: 'pin', symbolSize: 25 },
|
|
|
+ { name: '测风点位', color: '#fa8c16', symbol: 'pin', symbolSize: 30 },
|
|
|
+ { name: '数据布点', color: '#1890ff', symbol: 'roundRect', symbolSize: 28 },
|
|
|
];
|
|
|
|
|
|
// ==================== 状态颜色映射 ====================
|
|
|
@@ -25,17 +23,14 @@ export const statusColorMap: Record<string, string> = {
|
|
|
offline: '#999999', // 离线
|
|
|
};
|
|
|
|
|
|
-// ==================== 层级布局参数(树形但本质是图) ====================
|
|
|
+// ==================== 两行布局参数 ====================
|
|
|
|
|
|
-/** 每个层级在画布高度中的百分比 y 坐标(从上到下) */
|
|
|
-export const levelY = [8, 30, 55, 85]; // 省局、执法处、煤矿、测风点
|
|
|
+/** 每个分类在画布高度中的百分比 y 坐标(从上到下):测风点位在上、数据布点在下 */
|
|
|
+export const levelY = [30, 70];
|
|
|
|
|
|
/** 每类节点用于排序的权重字段(值越大越靠左) */
|
|
|
export const weightKey: Record<number, string> = {
|
|
|
- 0: 'childCount', // 省局
|
|
|
- 1: 'childCount', // 执法处
|
|
|
- 2: 'childCount', // 煤矿(按管辖测风点数排序)
|
|
|
- 3: 'windVolume', // 测风点
|
|
|
+ 0: 'childCount', // 测风点位:按已绑定数据布点数排序
|
|
|
};
|
|
|
|
|
|
/** 节点横向间距基数(px),实际 = basePadding / 该层节点数 */
|
|
|
@@ -85,59 +80,34 @@ export interface DetailField {
|
|
|
}
|
|
|
|
|
|
export const nodeDetailFields: Record<string, DetailField[]> = {
|
|
|
- 省局: [
|
|
|
- { label: '机构名称', key: 'name' },
|
|
|
- { label: '管辖执法处数', key: 'childCount' },
|
|
|
+ 测风点位: [
|
|
|
+ { label: '测风点位名称', key: 'name' },
|
|
|
+ { label: '风量(m³/min)', key: 'windVolume' },
|
|
|
+ { label: '已绑定数据布点数', key: 'childCount' },
|
|
|
+ { label: '测风设备ID', key: 'windrectId' },
|
|
|
],
|
|
|
- 执法处: [
|
|
|
- { label: '名称', key: 'name' },
|
|
|
- { label: '管辖煤矿数', key: 'childCount' },
|
|
|
- { label: '在线率', key: 'onlineRate' },
|
|
|
- ],
|
|
|
- 煤矿: [
|
|
|
- { label: '煤矿名称', key: 'name' },
|
|
|
- { label: '测风点数', key: 'childCount' },
|
|
|
- { label: '在线/应接', key: 'onlineRatio' },
|
|
|
- { label: '风量异常', key: 'windAbnormal' },
|
|
|
- ],
|
|
|
- 测风点: [
|
|
|
- { label: '测风点名称', key: 'name' },
|
|
|
- { label: '实时风速', key: 'windSpeed' },
|
|
|
- { label: '实时风量', key: 'windVolume' },
|
|
|
- { label: '状态', key: 'status' },
|
|
|
+ 数据布点: [
|
|
|
+ { label: '设备编码', key: 'deviceCode' },
|
|
|
+ { label: '设备位置', key: 'devicePos' },
|
|
|
+ { label: '矿井名称', key: 'mineName' },
|
|
|
+ { label: '状态', key: 'statusText' },
|
|
|
],
|
|
|
};
|
|
|
|
|
|
-// ==================== 多对多 + 一对多 关系类型 ====================
|
|
|
-
|
|
|
-/** 测风点分类索引(一对多的子端) */
|
|
|
-export const CHILD_CATEGORY = 3;
|
|
|
-
|
|
|
-/**
|
|
|
- * 判断目标分类是否为一对多关系的"子端"
|
|
|
- * 一对多:煤矿(2)→测风点(3),一个测风点只能属于一个煤矿
|
|
|
- * 多对多:执法处(1)↔煤矿(2),一个执法处管多个矿,一个矿可被多个执法处管
|
|
|
- */
|
|
|
-export function isOneToManyChild(category: number): boolean {
|
|
|
- return category === CHILD_CATEGORY;
|
|
|
-}
|
|
|
-
|
|
|
-/** 拖拽绑定时判定"吸附"的距离阈值(像素) */
|
|
|
-export const BIND_THRESHOLD = 35;
|
|
|
-
|
|
|
-// ==================== 模拟拓扑数据 ====================
|
|
|
+// ==================== 拓扑数据类型 ====================
|
|
|
|
|
|
export interface TopoNodeData {
|
|
|
id: string;
|
|
|
name: string;
|
|
|
category: number; // categories 索引
|
|
|
status?: string; // normal / abnormal / offline
|
|
|
- windSpeed?: number;
|
|
|
windVolume?: number;
|
|
|
childCount?: number;
|
|
|
- onlineRate?: string;
|
|
|
- onlineRatio?: string;
|
|
|
- windAbnormal?: number;
|
|
|
+ windrectId?: string;
|
|
|
+ mineCode?: string;
|
|
|
+ deviceCode?: string;
|
|
|
+ devicePos?: string;
|
|
|
+ mineName?: string;
|
|
|
[key: string]: any;
|
|
|
}
|
|
|
|
|
|
@@ -155,13 +125,14 @@ export interface TopologyData {
|
|
|
|
|
|
// ==================== API 响应类型 ====================
|
|
|
|
|
|
-/** API -> MineArea 节点 */
|
|
|
+/** API -> MineArea 节点(注册的测风点位) */
|
|
|
export interface MineAreaNode {
|
|
|
id: string;
|
|
|
mineCode: string;
|
|
|
name: string;
|
|
|
level: number; // 1=省局 2=执法处 3=煤矿 4=测风点
|
|
|
airVolume: number;
|
|
|
+ regulationId?: string;
|
|
|
windrectId: string;
|
|
|
createTime?: string;
|
|
|
updateTime?: string;
|
|
|
@@ -178,110 +149,81 @@ export interface MineAreaRelation {
|
|
|
updateTime?: string;
|
|
|
}
|
|
|
|
|
|
-/** API -> getMineAreaRelation 返回值 */
|
|
|
+/** API -> Windrect(数据布点/测风装置) */
|
|
|
+export interface WindrectNode {
|
|
|
+ id: string;
|
|
|
+ mineCode: string;
|
|
|
+ mineName?: string;
|
|
|
+ deviceCode?: string;
|
|
|
+ devicePos?: string;
|
|
|
+ status?: number;
|
|
|
+ createTime?: string;
|
|
|
+ [key: string]: any;
|
|
|
+}
|
|
|
+
|
|
|
+/** API -> getTopologyData 合并返回值 */
|
|
|
export interface MineAreaApiResponse {
|
|
|
mineAreaList: MineAreaNode[];
|
|
|
mineAreaRelationList: MineAreaRelation[];
|
|
|
+ windrectList: WindrectNode[];
|
|
|
}
|
|
|
|
|
|
-/** level(1-4) 转 category(0-3) */
|
|
|
-const levelToCategory: Record<number, number> = { 1: 0, 2: 1, 3: 2, 4: 3 };
|
|
|
+/** 状态渲染文本 */
|
|
|
+export const statusTextMap: Record<string, string> = {
|
|
|
+ normal: '正常',
|
|
|
+ abnormal: '异常',
|
|
|
+ offline: '离线',
|
|
|
+};
|
|
|
|
|
|
/**
|
|
|
* 将 API 响应转换为 TopologyData
|
|
|
- * mineAreaList -> nodes, mineAreaRelationList -> links
|
|
|
+ * mineAreaList(level=4) -> 测风点位节点,windrectList -> 数据布点节点,
|
|
|
+ * mineAreaRelationList(parentId=测风点位, childId=数据布点) -> 边
|
|
|
*/
|
|
|
export function transformToTopologyData(apiResp: MineAreaApiResponse): TopologyData {
|
|
|
- const { mineAreaList = [], mineAreaRelationList = [] } = apiResp;
|
|
|
-
|
|
|
- // 计算每个节点的子节点数
|
|
|
- const childCountMap: Record<string, number> = {};
|
|
|
- for (const rel of mineAreaRelationList) {
|
|
|
- childCountMap[rel.parentId] = (childCountMap[rel.parentId] || 0) + 1;
|
|
|
- }
|
|
|
+ const { mineAreaList = [], mineAreaRelationList = [], windrectList = [] } = apiResp;
|
|
|
|
|
|
- const nodes: TopoNodeData[] = mineAreaList.map((item) => ({
|
|
|
- id: item.id,
|
|
|
- name: item.name,
|
|
|
- category: levelToCategory[item.level] ?? 0,
|
|
|
- status: 'normal',
|
|
|
- windVolume: item.airVolume,
|
|
|
- childCount: childCountMap[item.id] || 0,
|
|
|
- mineCode: item.mineCode,
|
|
|
- windrectId: item.windrectId,
|
|
|
- }));
|
|
|
+ const pointIds = new Set<string>(
|
|
|
+ mineAreaList.filter((item) => Number(item.level) === 4).map((item) => item.id)
|
|
|
+ );
|
|
|
+ const deviceIds = new Set<string>(windrectList.map((item) => item.id));
|
|
|
|
|
|
- const links: TopoLinkData[] = mineAreaRelationList.map((rel) => ({
|
|
|
- source: rel.parentId,
|
|
|
- target: rel.childId,
|
|
|
- id: rel.id,
|
|
|
- }));
|
|
|
+ // 边:仅保留两端都能在图中找到的绑定关系(测风点位→数据布点)
|
|
|
+ const childCountMap: Record<string, number> = {};
|
|
|
+ const links: TopoLinkData[] = mineAreaRelationList
|
|
|
+ .filter((rel) => pointIds.has(rel.parentId) && deviceIds.has(rel.childId))
|
|
|
+ .map((rel) => {
|
|
|
+ childCountMap[rel.parentId] = (childCountMap[rel.parentId] || 0) + 1;
|
|
|
+ return { source: rel.parentId, target: rel.childId, id: rel.id };
|
|
|
+ });
|
|
|
+
|
|
|
+ const nodes: TopoNodeData[] = [
|
|
|
+ // 测风点位节点
|
|
|
+ ...mineAreaList
|
|
|
+ .filter((item) => Number(item.level) === 4)
|
|
|
+ .map((item) => ({
|
|
|
+ id: item.id,
|
|
|
+ name: item.name,
|
|
|
+ category: 0,
|
|
|
+ status: 'normal',
|
|
|
+ windVolume: item.airVolume,
|
|
|
+ childCount: childCountMap[item.id] || 0,
|
|
|
+ windrectId: item.windrectId,
|
|
|
+ mineCode: item.mineCode,
|
|
|
+ })),
|
|
|
+ // 数据布点节点(Windrect 测风装置)
|
|
|
+ ...windrectList.map((item) => ({
|
|
|
+ id: item.id,
|
|
|
+ name: item.devicePos || item.deviceCode || item.id,
|
|
|
+ category: 1,
|
|
|
+ status: String(item.status) === '1' ? 'normal' : 'offline',
|
|
|
+ statusText: String(item.status) === '1' ? '正常' : '停用',
|
|
|
+ deviceCode: item.deviceCode,
|
|
|
+ devicePos: item.devicePos,
|
|
|
+ mineName: item.mineName,
|
|
|
+ mineCode: item.mineCode,
|
|
|
+ })),
|
|
|
+ ];
|
|
|
|
|
|
return { nodes, links };
|
|
|
}
|
|
|
-
|
|
|
-// ==================== 模拟 API 响应数据(后端未部署时回退) ====================
|
|
|
-
|
|
|
-/** 模拟 getMineAreaRelation 的 API 响应 */
|
|
|
-export const mockMineAreaApiResponse: MineAreaApiResponse = {
|
|
|
- mineAreaList: [
|
|
|
- // === 省局 level=1 ===
|
|
|
- { id: 'province', mineCode: 'M000', name: '陕西省煤矿安全监察局', level: 1, airVolume: 0, windrectId: '' },
|
|
|
- // === 执法处 level=2 ===
|
|
|
- { id: 'enf-1', mineCode: 'M001', name: '榆林执法处', level: 2, airVolume: 0, windrectId: '' },
|
|
|
- { id: 'enf-2', mineCode: 'M002', name: '延安执法处', level: 2, airVolume: 0, windrectId: '' },
|
|
|
- { id: 'enf-3', mineCode: 'M003', name: '铜川执法处', level: 2, airVolume: 0, windrectId: '' },
|
|
|
- { id: 'enf-4', mineCode: 'M004', name: '渭南执法处', level: 2, airVolume: 0, windrectId: '' },
|
|
|
- { id: 'enf-5', mineCode: 'M005', name: '咸阳执法处', level: 2, airVolume: 0, windrectId: '' },
|
|
|
- // === 煤矿 level=3 ===
|
|
|
- { id: 'mine-1', mineCode: 'M101', name: '大柳塔煤矿', level: 3, airVolume: 0, windrectId: 'W001' },
|
|
|
- { id: 'mine-2', mineCode: 'M102', name: '活鸡兔煤矿', level: 3, airVolume: 0, windrectId: 'W002' },
|
|
|
- { id: 'mine-3', mineCode: 'M103', name: '哈拉沟煤矿', level: 3, airVolume: 0, windrectId: 'W003' },
|
|
|
- { id: 'mine-4', mineCode: 'M104', name: '榆家梁煤矿', level: 3, airVolume: 0, windrectId: 'W004' },
|
|
|
- { id: 'mine-5', mineCode: 'M105', name: '三道沟煤矿', level: 3, airVolume: 0, windrectId: 'W005' },
|
|
|
- { id: 'mine-6', mineCode: 'M106', name: '黄陵一号矿', level: 3, airVolume: 0, windrectId: 'W006' },
|
|
|
- { id: 'mine-7', mineCode: 'M107', name: '红柳林煤矿', level: 3, airVolume: 0, windrectId: 'W007' },
|
|
|
- { id: 'mine-8', mineCode: 'M108', name: '凉水井煤矿', level: 3, airVolume: 0, windrectId: 'W008' },
|
|
|
- // === 测风点 level=4 ===
|
|
|
- { id: 'point-1', mineCode: 'M001', name: '北翼回风巷', level: 4, airVolume: 1800, windrectId: 'W101' },
|
|
|
- { id: 'point-2', mineCode: 'M001', name: '南翼进风巷', level: 4, airVolume: 2100, windrectId: 'W102' },
|
|
|
- { id: 'point-3', mineCode: 'M001', name: '三采区回风巷', level: 4, airVolume: 950, windrectId: 'W103' },
|
|
|
- { id: 'point-4', mineCode: 'M001', name: '主斜井', level: 4, airVolume: 3200, windrectId: 'W104' },
|
|
|
- { id: 'point-5', mineCode: 'M002', name: '副立井', level: 4, airVolume: 2800, windrectId: 'W105' },
|
|
|
- { id: 'point-6', mineCode: 'M002', name: '二盘区回风巷', level: 4, airVolume: 1350, windrectId: 'W106' },
|
|
|
- { id: 'point-7', mineCode: 'M002', name: '101工作面', level: 4, airVolume: 720, windrectId: 'W107' },
|
|
|
- { id: 'point-8', mineCode: 'M003', name: '102工作面', level: 4, airVolume: 880, windrectId: 'W108' },
|
|
|
- { id: 'point-9', mineCode: 'M003', name: '东翼回风大巷', level: 4, airVolume: 1600, windrectId: 'W109' },
|
|
|
- { id: 'point-10', mineCode: 'M004', name: '西翼进风大巷', level: 4, airVolume: 1500, windrectId: 'W110' },
|
|
|
- ],
|
|
|
- mineAreaRelationList: [
|
|
|
- // 省局→执法处
|
|
|
- { id: 'rel-1', mineCode: 'M000', parentId: 'province', childId: 'enf-1' },
|
|
|
- { id: 'rel-2', mineCode: 'M000', parentId: 'province', childId: 'enf-2' },
|
|
|
- { id: 'rel-3', mineCode: 'M000', parentId: 'province', childId: 'enf-3' },
|
|
|
- { id: 'rel-4', mineCode: 'M000', parentId: 'province', childId: 'enf-4' },
|
|
|
- { id: 'rel-5', mineCode: 'M000', parentId: 'province', childId: 'enf-5' },
|
|
|
- // 执法处→煤矿
|
|
|
- { id: 'rel-6', mineCode: 'M001', parentId: 'enf-1', childId: 'mine-1' },
|
|
|
- { id: 'rel-7', mineCode: 'M001', parentId: 'enf-1', childId: 'mine-2' },
|
|
|
- { id: 'rel-8', mineCode: 'M001', parentId: 'enf-1', childId: 'mine-3' },
|
|
|
- { id: 'rel-9', mineCode: 'M002', parentId: 'enf-2', childId: 'mine-4' },
|
|
|
- { id: 'rel-10', mineCode: 'M002', parentId: 'enf-2', childId: 'mine-5' },
|
|
|
- { id: 'rel-11', mineCode: 'M003', parentId: 'enf-3', childId: 'mine-6' },
|
|
|
- { id: 'rel-12', mineCode: 'M003', parentId: 'enf-3', childId: 'mine-7' },
|
|
|
- { id: 'rel-13', mineCode: 'M004', parentId: 'enf-4', childId: 'mine-1' },
|
|
|
- { id: 'rel-14', mineCode: 'M005', parentId: 'enf-5', childId: 'mine-7' },
|
|
|
- { id: 'rel-15', mineCode: 'M005', parentId: 'enf-5', childId: 'mine-8' },
|
|
|
- // 煤矿→测风点
|
|
|
- { id: 'rel-16', mineCode: 'M101', parentId: 'mine-1', childId: 'point-1' },
|
|
|
- { id: 'rel-17', mineCode: 'M101', parentId: 'mine-1', childId: 'point-2' },
|
|
|
- { id: 'rel-18', mineCode: 'M101', parentId: 'mine-1', childId: 'point-3' },
|
|
|
- { id: 'rel-19', mineCode: 'M101', parentId: 'mine-1', childId: 'point-4' },
|
|
|
- { id: 'rel-20', mineCode: 'M102', parentId: 'mine-2', childId: 'point-5' },
|
|
|
- { id: 'rel-21', mineCode: 'M102', parentId: 'mine-2', childId: 'point-6' },
|
|
|
- { id: 'rel-22', mineCode: 'M102', parentId: 'mine-2', childId: 'point-7' },
|
|
|
- { id: 'rel-23', mineCode: 'M103', parentId: 'mine-3', childId: 'point-8' },
|
|
|
- { id: 'rel-24', mineCode: 'M103', parentId: 'mine-3', childId: 'point-9' },
|
|
|
- { id: 'rel-25', mineCode: 'M104', parentId: 'mine-4', childId: 'point-10' },
|
|
|
- ],
|
|
|
-};
|