79 lines
2.9 KiB
JavaScript
79 lines
2.9 KiB
JavaScript
// 원본 통합 검색(TFRMMULTISERCH: 일위대가·단가산출·기계경비·견적·실적·자재·노임 고르기, 검색어, 검색위치) —
|
|
// 웹은 이 앱에 저장된 작업(data/*.json)을 검색 위치로 쓴다. 검색어 낱말이 모두 코드·품명·규격에 들어 있으면 찾음.
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|
|
|
|
const sources = {
|
|
103: ["HeadTitleDat", "일위대가"],
|
|
104: ["CostTitleDat", "단가산출"],
|
|
105: ["MachTitleDat", "기계경비"],
|
|
101: ["EstiMateDat", "견적단가"],
|
|
21: ["SilJukDat", "실적단가"],
|
|
5: ["StuffcDat", "자재단가"],
|
|
6: ["LaborcDat", "노임단가"],
|
|
};
|
|
const LIMIT = 300;
|
|
const fold = (value) =>
|
|
String(value ?? "")
|
|
.normalize("NFKC")
|
|
.toLowerCase()
|
|
.replace(/\s+/g, "");
|
|
|
|
// only = 설계참고철(Main Data) 작업 id 목록 — 있으면 그 작업만. 검색어가 「a~b」 또는 숫자 하나면 단가(TC) 범위·같음(원본 0xefefb0).
|
|
function search(directory, query, kinds, exclude = "", only = null) {
|
|
const words = String(query ?? "")
|
|
.split(/\s+/)
|
|
.map(fold)
|
|
.filter(Boolean);
|
|
if (!words.length) throw Error("검색어를 입력하세요.");
|
|
const range = /^(\d+(?:\.\d+)?)(?:~(\d+(?:\.\d+)?))?$/.exec(String(query).replace(/[\s,]/g, ""));
|
|
const wanted = (kinds?.length ? kinds : Object.keys(sources))
|
|
.map(Number)
|
|
.filter((k) => sources[k]);
|
|
if (!wanted.length) throw Error("검색할 자료를 하나 이상 고르세요.");
|
|
if (!fs.existsSync(directory)) return { hits: [], total: 0 };
|
|
const hits = [];
|
|
let total = 0;
|
|
for (const name of fs.readdirSync(directory).filter((n) => /^[a-f0-9]{24}\.json$/.test(n))) {
|
|
const id = name.slice(0, -5);
|
|
if (id === exclude || (only && !only.includes(id))) continue;
|
|
let project;
|
|
try {
|
|
project = JSON.parse(fs.readFileSync(path.join(directory, name), "utf8"));
|
|
} catch {
|
|
continue;
|
|
}
|
|
for (const kind of wanted) {
|
|
const [table, label] = sources[kind];
|
|
for (const row of project.tables?.[table] || []) {
|
|
if (!row?.Code) continue;
|
|
if (range) {
|
|
const tc = Number(row.TC) || 0,
|
|
lo = Number(range[1]),
|
|
hi = range[2] == null ? lo : Number(range[2]);
|
|
if (tc < Math.min(lo, hi) || tc > Math.max(lo, hi)) continue;
|
|
} else {
|
|
const text = fold(`${row.Code} ${row.Name} ${row.Size}`);
|
|
if (!words.every((w) => text.includes(w))) continue;
|
|
}
|
|
total++;
|
|
if (hits.length < LIMIT)
|
|
hits.push({
|
|
id,
|
|
project: project.name || id,
|
|
kind,
|
|
label,
|
|
Code: row.Code,
|
|
Name: row.Name ?? "",
|
|
Size: row.Size ?? "",
|
|
Unite: row.Unite ?? "",
|
|
price: Number(row.TC) || 0,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return { hits, total };
|
|
}
|
|
|
|
module.exports = { search, sources };
|