- JDL 줄 = 탭 구분 + \x05/\x07 덧붙임(검사기 0x504d20 · 고정폭 표는 죽은 고리 — 34 §17.9 바로잡음) · 단가 목록 7종·Main·GongJong 머리·Global.Ini 상수 키 · zip 받기 - 구버전 판 가리기(Chk_Data 0x459280) · 7.x/DOS 표별 필드 대응 · B→X 조건 = 단위 X - 엑셀 역등록 PRPT「속성」·PRIN「대상」·PROUT「저장」 = PR/RR 줄만 · 원본 BDQTY 4건 일치 - 자재 = 재료비 수량집계표 엑셀 내보내기(주요건설자재현황 시트) - 그림 코드 = COSTN I 코드 IFORM · SIZE = 그림 클래스 이름 - 지식DB 37번 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
110 lines
3.6 KiB
JavaScript
110 lines
3.6 KiB
JavaScript
/**
|
|
* STMATE w 8.0 — 조달청포맷(TEXT,JDL) 생성·검사 끼워 넣기 (scr_jdl.js) · 1번(파일) 메뉴
|
|
* 원본: ST_U_Convert Conv_ST8_Jodal(「조달청포맷(TEXT,JDL) 생성」) · JDLCHK.Tab_33.dll JDLCheck
|
|
* 서버: api_jdl.py — 묶음 요약 · zip 받기 · 받은 묶음 검사(열 개수 · 코드 글자)
|
|
*/
|
|
(function (global) {
|
|
"use strict";
|
|
|
|
const PLUG = (global.STMATE_PLUGINS = global.STMATE_PLUGINS || {
|
|
screens: {},
|
|
actions: {},
|
|
menus: [],
|
|
});
|
|
const app = () => global.STMATE_APP;
|
|
|
|
async function req(url, body, raw) {
|
|
const res = await fetch(url, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(body),
|
|
});
|
|
if (!res.ok) throw new Error(await res.text());
|
|
return raw ? res.blob() : res.json();
|
|
}
|
|
|
|
function curPath(ctx) {
|
|
if (!ctx.currentSourcePath) throw new Error("먼저 프로젝트를 불러오세요.");
|
|
return ctx.currentSourcePath;
|
|
}
|
|
|
|
function download(blob, name) {
|
|
const a = document.createElement("a");
|
|
a.href = URL.createObjectURL(blob);
|
|
a.download = name;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
setTimeout(() => (URL.revokeObjectURL(a.href), a.remove()), 1000);
|
|
}
|
|
|
|
function pickFiles() {
|
|
return new Promise((resolve, reject) => {
|
|
const inp = document.createElement("input");
|
|
inp.type = "file";
|
|
inp.multiple = true;
|
|
inp.accept = ".txt,.TXT";
|
|
inp.onchange = async () => {
|
|
const list = Array.from(inp.files || []);
|
|
if (!list.length) return reject(new Error("파일을 고르지 않음"));
|
|
const out = {};
|
|
for (const f of list)
|
|
out[f.name] = await new Promise((ok, no) => {
|
|
const rd = new FileReader();
|
|
rd.onload = () => ok(String(rd.result));
|
|
rd.onerror = () => no(rd.error);
|
|
rd.readAsDataURL(f);
|
|
});
|
|
resolve(out);
|
|
};
|
|
inp.click();
|
|
});
|
|
}
|
|
|
|
function summary(r) {
|
|
const lines = Object.entries(r.counts).map(([k, v]) => {
|
|
const e = (r.checks[k] || []).length;
|
|
return `${k}: ${v}줄${e ? ` · 오류 ${e}` : ""}`;
|
|
});
|
|
const warn = r.warnings.length ? `\n\n[알림]\n${r.warnings.slice(0, 8).join("\n")}` : "";
|
|
return (
|
|
`계열 ${r.series} (PCLASS ${r.pclass})\n${lines.join("\n")}` +
|
|
`${warn}\n\n[아직 못 만드는 것 — 원본 규칙 미확인]\n${r.missing.join("\n")}`
|
|
);
|
|
}
|
|
|
|
const ACTS = {
|
|
async jdlExport(ctx) {
|
|
const body = { source_path: curPath(ctx) };
|
|
const r = await req("/api/jdl/export", body);
|
|
if (!confirm(`[조달청포맷(TEXT,JDL) 생성]\n${summary(r)}\n\n묶음 파일을 zip 으로 받을까요?`))
|
|
return;
|
|
const name = (ctx.state.projectMeta.pname || "공사").slice(0, 28);
|
|
download(await req("/api/jdl/export.zip", body, true), `${name}_JDL_TEXT.zip`);
|
|
},
|
|
async jdlCheck() {
|
|
const files = await pickFiles();
|
|
const r = await req("/api/jdl/check", { files });
|
|
const text = Object.entries(r.checks)
|
|
.map(([k, v]) => `${k}: ${v.length ? v.slice(0, 20).join("\n ") : "통과"}`)
|
|
.join("\n");
|
|
alert(`[JDL 파일 오류검사]\n${text}`);
|
|
},
|
|
};
|
|
|
|
async function run(act) {
|
|
try {
|
|
await ACTS[act](app().ctx);
|
|
} catch (err) {
|
|
alert("JDL 처리 실패: " + err.message);
|
|
}
|
|
}
|
|
|
|
[
|
|
["Act_JdlExport", "조달청포맷(TEXT,JDL) 생성", "jdlExport"],
|
|
["Act_JdlCheck", "JDL 파일 오류검사 (JDLCheck)", "jdlCheck"],
|
|
].forEach(([act, label, key]) => {
|
|
PLUG.actions[act] = () => run(key);
|
|
PLUG.menus.push({ menu: "m1", label, act });
|
|
});
|
|
})(window);
|