- 394/565 바이트 레코드(Extended·ShortString) 읽기·쓰기, 표별 변환과 설계변경 c* 파일 - 호표 코드 매기기·품명규격으로 연결 코드 붙이기, 저장 금액 유지 - 단가비교 창(4종, 차례 짝, 합계 다름 표시, 더블클릭 이동), Ctrl+Alt+E · Ctrl+Shift+F12 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
144 lines
6.3 KiB
JavaScript
144 lines
6.3 KiB
JavaScript
// 원본 구버전 EST Plus 자료 3종(spec_misc §2 · spec_estplus.md): 「EstPlus 데이타 불러오기」(mnuFileEstPlusLoad — .est 를 풀어
|
|
// 표마다 변환해 새 작업으로), 「Est Plus와 단가비교」(ActionEstCompare Ctrl+Alt+E → TfrmCompare: 비교할 항목 4가지 중 고르고
|
|
// 비교파일선택 → 이 공사 머리 행과 구형 파일 Index > 0 레코드를 차례로 짝지어 합계가 다르면 표시, 더블클릭 = 그 자료로 이동),
|
|
// 「EstPlus 데이타로 저장」(ActionConvertToEstPlus Ctrl+Shift+F12 → 394 바이트 판 .est). 읽기·쓰기는 서버 legacy-estplus.cjs.
|
|
const EstEstPlus = (() => {
|
|
const num = (v) => Number(v) || 0;
|
|
const KINDS = [
|
|
["기계경비", "MachMainDat"],
|
|
["단가산출", "CostTitleDat"],
|
|
["일위대가", "HeadMainDat"],
|
|
["내역서", "EstMainDat"],
|
|
];
|
|
const pickFile = () =>
|
|
new Promise((resolve) => {
|
|
const picker = document.createElement("input");
|
|
picker.type = "file";
|
|
picker.accept = ".est,.ebk";
|
|
picker.addEventListener("change", () => resolve(picker.files[0]));
|
|
picker.click();
|
|
});
|
|
|
|
// 이 공사 쪽 행(Index > 0)과 구형 값을 차례로 짝지음 · 확인 = 합계 다름(정확 비교)
|
|
function pair(project, kind, legacy) {
|
|
const [, table] = KINDS[kind],
|
|
rows = (project.tables[table] || [])
|
|
.map((row, index) => ({ row, index }))
|
|
.filter(({ row }) => num(row.Index) > 0);
|
|
return rows.map(({ row, index }, i) => {
|
|
const o = legacy[i] || { ONU: 0, OGU: 0, OJU: 0 },
|
|
mine = num(row.NC) + num(row.GC) + num(row.JC) + num(row.SC);
|
|
return {
|
|
index,
|
|
Index: row.Index,
|
|
Code: row.Code,
|
|
Name: row.Name || EstText.clean(row.LineData || ""),
|
|
Size: row.Size,
|
|
Unite: row.Unite,
|
|
NC: num(row.NC),
|
|
GC: num(row.GC),
|
|
JC: num(row.JC),
|
|
SC: num(row.SC),
|
|
...o,
|
|
UsesCheck: mine !== o.ONU + o.OGU + o.OJU,
|
|
};
|
|
});
|
|
}
|
|
|
|
let compared = { kind: 0, rows: [] };
|
|
function draw() {
|
|
const { esc, number } = EstApp,
|
|
{ kind, rows } = compared;
|
|
EstApp.showDialog(
|
|
"단가비교",
|
|
`<div class="business-tools"><b>비교할항목 선택</b>${KINDS.map(([label], i) => `<label><input type="radio" name="ep-kind" value="${i}" ${i === kind ? "checked" : ""}> ${label}</label>`).join("")}<button data-action="estplus-compare-file">비교파일선택</button><b id="estplus-count">${rows.filter((r) => r.UsesCheck).length}</b><span id="estplus-error" role="alert"></span></div><div style="max-height:480px;overflow:auto"><table class="sheet"><thead><tr><th>확인</th><th>No</th><th>품명</th><th>규격</th><th>단위</th><th>노무비</th><th>재료비</th><th>경비</th><th>실적</th><th></th><th>노무비</th><th>재료비</th><th>경비</th></tr></thead><tbody>${rows
|
|
.map(
|
|
(r) =>
|
|
`<tr data-ep-row="${r.index}" style="cursor:pointer${r.UsesCheck ? ";color:#b00" : ""}"><td>${r.UsesCheck ? "✔" : ""}</td><td>${r.Index}</td><td style="text-align:left">${esc(r.Name)}</td><td>${esc(r.Size ?? "")}</td><td>${esc(r.Unite ?? "")}</td><td>${number(r.NC)}</td><td>${number(r.GC)}</td><td>${number(r.JC)}</td><td>${number(r.SC)}</td><td></td><td>${number(r.ONU)}</td><td>${number(r.OGU)}</td><td>${number(r.OJU)}</td></tr>`,
|
|
)
|
|
.join("")}</tbody></table></div>`,
|
|
);
|
|
}
|
|
async function compareFile() {
|
|
compared.kind = Number(document.querySelector('[name="ep-kind"]:checked')?.value || 0);
|
|
const file = await pickFile();
|
|
if (!file) return;
|
|
const legacy = await EstApp.api(`/api/estplus-compare?kind=${compared.kind}`, {
|
|
method: "POST",
|
|
body: await file.arrayBuffer(),
|
|
});
|
|
compared.rows = pair(EstApp.state.project, compared.kind, legacy);
|
|
draw();
|
|
}
|
|
|
|
async function load(state) {
|
|
const file = await pickFile();
|
|
if (!file) return;
|
|
const project = await EstApp.api(
|
|
`/api/estplus-read?name=${encodeURIComponent(file.name.replace(/\.(est|ebk)$/i, ""))}`,
|
|
{ method: "POST", body: await file.arrayBuffer() },
|
|
);
|
|
if (!confirm("구버젼 데이타를 변환했습니다. 현재 작업을 변환한 자료로 바꾸시겠습니까?")) return;
|
|
EstApp.setProject(project);
|
|
state.notice = (project.importWarnings || []).join(" ");
|
|
EstApp.render();
|
|
}
|
|
async function convert(state) {
|
|
const response = await fetch("/api/estplus-write", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(state.project),
|
|
});
|
|
if (!response.ok) throw Error((await response.json()).error);
|
|
EstApp.download(
|
|
`${state.project.name || "내역서"}.est`,
|
|
await response.blob(),
|
|
"application/octet-stream",
|
|
);
|
|
state.notice = "변환을 마쳤습니다.";
|
|
EstApp.render();
|
|
}
|
|
|
|
function install() {
|
|
document.addEventListener("keydown", (event) => {
|
|
const k = event.key.toLowerCase();
|
|
if (event.ctrlKey && event.altKey && k === "e") {
|
|
event.preventDefault();
|
|
handle("estplus-compare", EstApp.state);
|
|
} else if (event.ctrlKey && event.shiftKey && event.key === "F12") {
|
|
event.preventDefault();
|
|
handle("estplus-convert", EstApp.state);
|
|
}
|
|
});
|
|
document.addEventListener("dblclick", (event) => {
|
|
const tr = event.target.closest?.("[data-ep-row]");
|
|
if (!tr) return;
|
|
const [, table] = KINDS[compared.kind],
|
|
row = EstApp.state.project.tables[table][Number(tr.dataset.epRow)];
|
|
if (!row?.Code && table !== "EstMainDat") return;
|
|
EstApp.byId("dialog").close();
|
|
const view = { CostTitleDat: "CostMainDat" }[table] || table;
|
|
EstApp.navigate(view, table === "EstMainDat" ? "" : row.Code);
|
|
});
|
|
}
|
|
if (typeof document !== "undefined" && document.addEventListener) install();
|
|
|
|
function handle(name, state) {
|
|
const fail = (error) => {
|
|
const note = EstApp.byId("estplus-error");
|
|
if (note) note.textContent = error.message;
|
|
else {
|
|
state.notice = error.message;
|
|
EstApp.render();
|
|
}
|
|
};
|
|
if (name === "estplus-compare") draw();
|
|
else if (name === "estplus-compare-file") compareFile().catch(fail);
|
|
else if (name === "estplus-load") load(state).catch(fail);
|
|
else if (name === "estplus-convert") convert(state).catch(fail);
|
|
else return false;
|
|
return true;
|
|
}
|
|
return { pair, handle };
|
|
})();
|