mirror of
https://github.com/sergeyyarkov/asu_tools_service.git
synced 2026-10-11 10:29:22 +03:00
выгрузка БД в excel
This commit is contained in:
parent
44fd57ef2c
commit
de712c847b
202
handlers/asu_table/getAsuTable.handler.js
Normal file
202
handlers/asu_table/getAsuTable.handler.js
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
import { equipmentRepository } from "#root/repositories/equipment.repository.js";
|
||||||
|
import ExcelJS from "exceljs";
|
||||||
|
|
||||||
|
/** @import { HttpRouteHandler } from "#root/http-server/types/http-server.js" */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL: /api/asu_table_xls
|
||||||
|
* Method: GET
|
||||||
|
* Description: Генерирует файл Excel на основе БД АСУ промплощадки
|
||||||
|
*
|
||||||
|
* @type {HttpRouteHandler}
|
||||||
|
*/
|
||||||
|
export default async (ctx) => {
|
||||||
|
const { res } = ctx;
|
||||||
|
|
||||||
|
const wb = new ExcelJS.Workbook();
|
||||||
|
const ws = wb.addWorksheet("Перечень систем", {
|
||||||
|
views: [{ state: "frozen", ySplit: 1, zoomScale: 70 }]
|
||||||
|
});
|
||||||
|
|
||||||
|
const filename = encodeURIComponent("АСУ промплощадка.xlsx");
|
||||||
|
const equipmentData = await equipmentRepository.getAll();
|
||||||
|
|
||||||
|
const excelData = [];
|
||||||
|
|
||||||
|
/** @type {Map<string, { row: number }} */
|
||||||
|
const addedBaseLocationNames = new Map();
|
||||||
|
|
||||||
|
/** @type {{ colStart: string, colEnd: string, from: number; to: number }[]} */
|
||||||
|
const mergeRanges = [];
|
||||||
|
|
||||||
|
let currentRow = 2;
|
||||||
|
|
||||||
|
for (const system of equipmentData) {
|
||||||
|
const subsystems = system.subsystems || [];
|
||||||
|
|
||||||
|
if (subsystems.length === 0) {
|
||||||
|
excelData.push([system.name, ...new Array(8).fill("", 0, 8)]);
|
||||||
|
currentRow++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Заголовок для расположения систем
|
||||||
|
if (!addedBaseLocationNames.has(system.baseLocationName)) {
|
||||||
|
addedBaseLocationNames.set(system.baseLocationName, { row: currentRow });
|
||||||
|
excelData.push([system.baseLocationName]);
|
||||||
|
mergeRanges.push({ colStart: "A", colEnd: "I", from: currentRow, to: currentRow });
|
||||||
|
currentRow++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startRow = currentRow;
|
||||||
|
|
||||||
|
// Добавление систем подсистем в таблицу excel
|
||||||
|
subsystems.forEach((subsystem) => {
|
||||||
|
const hasDocLink = Boolean(subsystem?.docLink);
|
||||||
|
const hasElectroPlan = Boolean(subsystem?.electrDiagLink);
|
||||||
|
const plcs = subsystem?.plcs || [];
|
||||||
|
const hmis = subsystem?.hmis || [];
|
||||||
|
const fgs = subsystem?.fgs || [];
|
||||||
|
const pcs = subsystem?.pcs || [];
|
||||||
|
|
||||||
|
excelData.push([
|
||||||
|
system.name,
|
||||||
|
subsystem.name,
|
||||||
|
`${system.baseLocationName} ${subsystem.locationName}`,
|
||||||
|
hasDocLink ? { text: "Документация", hyperlink: subsystem?.docLink } : "",
|
||||||
|
hasElectroPlan ? { text: "Электроплан", hyperlink: subsystem?.electrDiagLink } : "",
|
||||||
|
plcs.map((plc) => `${plc.name || ""}`).join("\n"),
|
||||||
|
hmis.map((hmi) => `${hmi.name || ""}`).join("\n"),
|
||||||
|
fgs.map((fg) => `${fg.name || ""}`).join("\n"),
|
||||||
|
pcs.map((pc) => `${pc.name || ""}`).join("\n")
|
||||||
|
]);
|
||||||
|
|
||||||
|
currentRow++;
|
||||||
|
});
|
||||||
|
|
||||||
|
mergeRanges.push({ colStart: "A", colEnd: "A", from: startRow, to: currentRow - 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.getRow(1).values = [
|
||||||
|
"Наименование системы",
|
||||||
|
"Наименование внутренних подсистем.",
|
||||||
|
"Расположение",
|
||||||
|
"Документация",
|
||||||
|
"Электроплан",
|
||||||
|
"PLC",
|
||||||
|
"HMI",
|
||||||
|
"ПЧ",
|
||||||
|
"Промышленные ПК"
|
||||||
|
];
|
||||||
|
|
||||||
|
ws.autoFilter = `A1:I${excelData.length + 1}`;
|
||||||
|
|
||||||
|
ws.addRows(excelData);
|
||||||
|
|
||||||
|
mergeRanges.forEach((r) => ws.mergeCells(`${r.colStart}${r.from}:${r.colEnd}${r.to}`));
|
||||||
|
|
||||||
|
ws.getColumn(1).width = 50;
|
||||||
|
ws.getColumn(2).width = 50;
|
||||||
|
ws.getColumn(3).width = 35;
|
||||||
|
ws.getColumn(4).width = 17;
|
||||||
|
ws.getColumn(5).width = 17;
|
||||||
|
ws.getColumn(6).width = 45;
|
||||||
|
ws.getColumn(7).width = 45;
|
||||||
|
ws.getColumn(8).width = 45;
|
||||||
|
ws.getColumn(9).width = 30;
|
||||||
|
ws.getRow(1).height = 45;
|
||||||
|
|
||||||
|
ws.eachRow((row, rowNumber) => {
|
||||||
|
row.eachCell({ includeEmpty: true }, (cell) => {
|
||||||
|
cell.alignment = {
|
||||||
|
wrapText: true,
|
||||||
|
vertical: "middle",
|
||||||
|
horizontal: "center"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rowNumber > 1) {
|
||||||
|
cell.font = {
|
||||||
|
name: "Calibri",
|
||||||
|
size: 12
|
||||||
|
};
|
||||||
|
cell.border = {
|
||||||
|
top: { style: "thin", color: { argb: "FF000000" } },
|
||||||
|
left: { style: "thin", color: { argb: "FF000000" } },
|
||||||
|
right: { style: "thin", color: { argb: "FF000000" } },
|
||||||
|
bottom: { style: "thin", color: { argb: "FF000000" } }
|
||||||
|
};
|
||||||
|
cell.fill = {
|
||||||
|
type: "pattern",
|
||||||
|
pattern: "solid",
|
||||||
|
fgColor: { argb: "FFF4F4F4" }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (cell.value === null || cell.value === undefined || cell.value === "") {
|
||||||
|
cell.fill = {
|
||||||
|
type: "pattern",
|
||||||
|
pattern: "solid",
|
||||||
|
fgColor: { argb: "FFFCE4D6" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ссылки
|
||||||
|
if (cell.value && typeof cell.value === "object" && "hyperlink" in cell.value) {
|
||||||
|
cell.font = {
|
||||||
|
name: "Calibri",
|
||||||
|
size: 11,
|
||||||
|
color: { argb: "FF0563C1" },
|
||||||
|
underline: "single"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cell.font = {
|
||||||
|
name: "Calibri",
|
||||||
|
size: 11,
|
||||||
|
color: { argb: "FFFFFFFF" }
|
||||||
|
};
|
||||||
|
|
||||||
|
cell.border = {
|
||||||
|
top: { style: "thin", color: { argb: "FF000000" } },
|
||||||
|
right: { style: "thin", color: { argb: "FF000000" } },
|
||||||
|
bottom: { style: "thin", color: { argb: "FF000000" } }
|
||||||
|
};
|
||||||
|
|
||||||
|
cell.fill = {
|
||||||
|
type: "pattern",
|
||||||
|
pattern: "solid",
|
||||||
|
fgColor: { argb: "FFF27A2B" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
addedBaseLocationNames.forEach((baseLocationHeaderRow) => {
|
||||||
|
const row = ws.getRow(baseLocationHeaderRow.row);
|
||||||
|
row.height = 30;
|
||||||
|
row.eachCell((cell) => {
|
||||||
|
cell.font = {
|
||||||
|
name: "Tahoma",
|
||||||
|
bold: true,
|
||||||
|
size: 11
|
||||||
|
};
|
||||||
|
cell.border = {
|
||||||
|
top: { style: "medium", color: { argb: "FF000000" } },
|
||||||
|
bottom: { style: "medium", color: { argb: "FF000000" } }
|
||||||
|
};
|
||||||
|
cell.fill = {
|
||||||
|
type: "pattern",
|
||||||
|
pattern: "solid",
|
||||||
|
fgColor: { argb: "FF92D050" }
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const excelBuffer = await wb.xlsx.writeBuffer();
|
||||||
|
|
||||||
|
res.res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
|
res.res.setHeader("Content-Disposition", `attachment; filename=${filename}; filename*=UTF-8''${filename}`);
|
||||||
|
res.res.setHeader("Content-Length", excelBuffer.byteLength);
|
||||||
|
res.res.end(excelBuffer);
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
3
handlers/asu_table/index.js
Normal file
3
handlers/asu_table/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import getAsuTableHandler from "./getAsuTable.handler.js";
|
||||||
|
|
||||||
|
export const asuTableHandlers = { getAsuTableHandler };
|
||||||
@ -1 +1,2 @@
|
|||||||
export * from "./reports/index.js";
|
export * from "./reports/index.js";
|
||||||
|
export * from "./asu_table/index.js";
|
||||||
|
|||||||
2
http-server/types/http-server.d.ts
vendored
2
http-server/types/http-server.d.ts
vendored
@ -61,5 +61,5 @@ export interface HttpResponse {
|
|||||||
res: http.ServerResponse;
|
res: http.ServerResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HttpRouteHandler = (ctx: HttpContext) => any;
|
export type HttpRouteHandler = (ctx: HttpContext) => Promise<unknown>;
|
||||||
export type HttpCreateServer = (options: HttpServerOptions) => HttpServer;
|
export type HttpCreateServer = (options: HttpServerOptions) => HttpServer;
|
||||||
|
|||||||
3
index.js
3
index.js
@ -1,7 +1,7 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import fsp from "node:fs/promises";
|
import fsp from "node:fs/promises";
|
||||||
import { createServer } from "./http-server/index.js";
|
import { createServer } from "./http-server/index.js";
|
||||||
import { reportsHandlers } from "#handlers/index.js";
|
import { reportsHandlers, asuTableHandlers } from "#handlers/index.js";
|
||||||
import packageJson from "./package.json" with { type: "json" };
|
import packageJson from "./package.json" with { type: "json" };
|
||||||
import db from "./db.js";
|
import db from "./db.js";
|
||||||
|
|
||||||
@ -40,6 +40,7 @@ server.router.prefix("/api", (router) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.define("POST", "/reports_sync", reportsHandlers.syncParsedHandler);
|
router.define("POST", "/reports_sync", reportsHandlers.syncParsedHandler);
|
||||||
|
router.define("GET", "/asu_table_xls", asuTableHandlers.getAsuTableHandler);
|
||||||
});
|
});
|
||||||
|
|
||||||
server.router.define("GET", "/", ({ res }) => res.sendText("Сервис инструментов таблицы АСУ."));
|
server.router.define("GET", "/", ({ res }) => res.sendText("Сервис инструментов таблицы АСУ."));
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"ignoreDeprecations": "6.0",
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"lib": ["ESNext"],
|
"lib": ["ESNext"],
|
||||||
|
|||||||
1048
package-lock.json
generated
1048
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,11 +14,13 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "API сервис инструментов таблицы АСУ.",
|
"description": "API сервис инструментов таблицы АСУ.",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"exceljs": "^4.4.0",
|
||||||
"formidable": "^3.5.4",
|
"formidable": "^3.5.4",
|
||||||
"mssql": "^12.2.0",
|
"mssql": "^12.2.0",
|
||||||
"p-limit": "^7.3.0",
|
"p-limit": "^7.3.0",
|
||||||
"serialize-error": "^13.0.1",
|
"serialize-error": "^13.0.1",
|
||||||
"xlsx": "^0.18.5",
|
"xlsx": "^0.18.5",
|
||||||
|
"xlsx-js-style": "^1.2.0",
|
||||||
"yup": "^1.7.1"
|
"yup": "^1.7.1"
|
||||||
},
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
|
|||||||
52
repositories/equipment.repository.js
Normal file
52
repositories/equipment.repository.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { UOW } from "./index.js";
|
||||||
|
|
||||||
|
export const equipmentRepository = {
|
||||||
|
async getAll() {
|
||||||
|
const request = UOW.request;
|
||||||
|
const result = await request.query(`
|
||||||
|
SELECT systemsTbl.id as id,
|
||||||
|
systemsTbl.name as name,
|
||||||
|
baseLocTbl.name as baseLocationName,
|
||||||
|
(select subSystemsTbl.id as id,
|
||||||
|
subSystemsTbl.name as name,
|
||||||
|
subSystemsTbl.doc_link as docLink,
|
||||||
|
subSystemsTbl.electr_diag_link as electrDiagLink,
|
||||||
|
locTbl.name as locationName,
|
||||||
|
(select plcTbl.plc_name as name
|
||||||
|
FROM asu_system_api_plc as plcTbl
|
||||||
|
left join asu_system_api_subsystemlist_plc as jtPLC on jtPLC.subsystemlist_id = subSystemsTbl.id
|
||||||
|
where jtPLC.plc_id = plcTbl.id
|
||||||
|
FOR JSON PATH) as plcs,
|
||||||
|
|
||||||
|
(select hmiTbl.hmi_name as name
|
||||||
|
FROM asu_system_api_hmi as hmiTbl
|
||||||
|
left join asu_system_api_subsystemlist_hmi as jtHMI on jtHMI.subsystemlist_id = subSystemsTbl.id
|
||||||
|
where jtHMI.hmi_id = hmiTbl.id
|
||||||
|
FOR JSON PATH) as hmis,
|
||||||
|
|
||||||
|
(select fgTbl.fg_name as name
|
||||||
|
FROM asu_system_api_fg as fgTbl
|
||||||
|
left join asu_system_api_subsystemlist_fg as jtFG on jtFG.subsystemlist_id = subSystemsTbl.id
|
||||||
|
where jtFG.fg_id = fgTbl.id
|
||||||
|
FOR JSON PATH) as fgs,
|
||||||
|
|
||||||
|
(select pcTbl.pc_name as name
|
||||||
|
FROM asu_system_api_pc as pcTbl
|
||||||
|
left join asu_system_api_subsystemlist_pc as jtPC on jtPC.subsystemlist_id = subSystemsTbl.id
|
||||||
|
where jtPC.pc_id = pcTbl.id
|
||||||
|
FOR JSON PATH) as pcs
|
||||||
|
|
||||||
|
from asu_system_api_subsystemlist as subSystemsTbl
|
||||||
|
inner join asu_system_api_systemlist_subsystem_name as jt on subSystemsTbl.id = jt.subsystemlist_id
|
||||||
|
left join asu_system_api_location as locTbl on locTbl.id = subSystemsTbl.location_id
|
||||||
|
where jt.systemlist_id = systemsTbl.id
|
||||||
|
FOR JSON PATH) as subsystems
|
||||||
|
from asu_system_api_systemlist as systemsTbl
|
||||||
|
inner join asu_system_api_baselocation as baseLocTbl on systemsTbl.location_id = baseLocTbl.id FOR JSON PATH
|
||||||
|
`);
|
||||||
|
|
||||||
|
const jsonStringData = result.recordset.map((row) => Object.values(row)[0]).join("");
|
||||||
|
|
||||||
|
return JSON.parse(jsonStringData || "[]");
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,2 +1,3 @@
|
|||||||
export * from "./uow.js";
|
export * from "./uow.js";
|
||||||
export * from "./report.repository.js";
|
export * from "./report.repository.js";
|
||||||
|
export * from "./equipment.repository.js";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user