Files
Aislo/tools/stmate-test/scr_edit_eq.js
T
eomsangdonandClaude Opus 5.5 3d8fddfc34 feat(stmate-test): 보고서·제잡비·식언어·수량산출·화면 묶음 합침 · 재계산 후 저장 무변경
- 보고서: 원본 STMATE 엑셀과 셀 대조(목록표·자재단가대비·중기·일위대가·산근 목록·설계내역서·원가계산서) · 조달청 XML 공식 XSD 검증 · PM_Prt 화면
- 제잡비: 원본 계산 순서 4형식(T/S/TA/SA) · 실무 7건 저장 결과키 전수 1원 · 요율표 조회 · 선택지 계산 · 제잡비·제비율 화면
- 식언어·수량: 함수·조건식·금액변수·문법 검사·수량변수·QCODE · 산근·구성·중기공식 편집 화면 · 순환 알림
- 화면: 원본 메뉴 체계·단축키 · 설계내역서 편집(행 넣기·지우기·이동·레벨·보관함) · 초기값 · 단가목록표
- 저장: 초기값(Start8x.Ini) 바뀐 키 저장 · 노임 환산 직종 = 초기값 CONLD · 산근·구성 결과에 적용단가 칸 채우지 않음(원본 0)
- DBF 글 칸 앞 빈칸 보존 · 시험 도우미 표준입력 utf8(한글 조각 깨짐 원인)
- 실무 5건 재계산 후 저장 = 바뀐 칸 0 · 시험 332건 통과

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 19:12:04 +09:00

132 lines
6.3 KiB
JavaScript

/* -*- coding: utf-8 -*- */
/**
* 표준중기공식 편집 화면 (scr_edit_eq.js) — wM_Edit_EQ · 추가 · 삭제 · 저장 · 취소 · 적용(공식 / 공식+코드)
* 근거: 지식DB 28 §1.1(사용자 등록부 — 「수정된 중기공식을 저장하시겠습니까?」 · 「신규공식 추가」 · 「공식+코드」) ·
* REA 30 TWM_EDIT_EQ(mBtn_Insert · mBtn_Delete · mBtn_Save · mBtn_Cancel · cBx_INS_X · MEMOC) · EQSYNC(NAME · SYNC · INS_X)
* 저장 = api_qty.py /api/qty/eq (work_dbf/EQSYNC.json) · 불러 쓰기 = 산출근거 편집기 「중기공식 불러오기」
*/
(function (global) {
"use strict";
const P = (global.STMATE_PLUGINS = global.STMATE_PLUGINS || {
screens: {},
actions: {},
menus: [],
});
const esc = (s) =>
String(s == null ? "" : s).replace(
/[&<>"]/g,
(c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c],
);
function load(ctx, app) {
if (ctx.uiState.eqDraft || ctx.uiState.eqDraftLoading) return;
ctx.uiState.eqDraftLoading = true;
fetch("/api/qty/eq")
.then((r) => r.json())
.then((r) => {
ctx.uiState.eqItems = r.items || [];
ctx.uiState.eqDraft = JSON.parse(JSON.stringify(ctx.uiState.eqItems));
ctx.uiState.eqDraftLoading = false;
app.renderWorkspace();
})
.catch(() => ((ctx.uiState.eqDraft = []), (ctx.uiState.eqDraftLoading = false)));
}
function render(ctx, app) {
load(ctx, app);
const u = ctx.uiState;
const items = u.eqDraft || [];
const i = Math.min(u.eqSel || 0, Math.max(0, items.length - 1));
const it = items[i];
const ev = it ? global.STMATE_ENGINE.evaluateSmtFormulaText(it.text || "", ctx, "") : null;
const dirty = JSON.stringify(items) !== JSON.stringify(u.eqItems || []);
return `
<div class="win-form">
<div class="win-form-header"><span>표준중기공식 편집. [wM_Edit_EQ]</span><span>${items.length}개 공식${dirty ? " · 고친 뒤 저장 안 함" : ""}</span></div>
<div class="win-subbar">
<button class="win-btn" id="eq-add">추 가</button>
<button class="win-btn" id="eq-del">삭 제</button>
<button class="win-btn primary" id="eq-save">저 장</button>
<button class="win-btn" id="eq-cancel">취 소</button>
<button class="win-btn" id="eq-close" style="margin-left:auto;">종 료</button>
</div>
<div class="split-horiz">
<div class="pane-box" style="width:300px;"><div class="grid-scroll"><table class="dbgrid">
<thead><tr><th style="width:30px">No</th><th>명칭</th><th style="width:70px">적용</th></tr></thead>
<tbody>${items.map((e, k) => `<tr class="eq-row ${k === i ? "selected" : ""}" data-i="${k}" style="cursor:pointer;"><td class="center">${k + 1}</td><td>${esc(e.name)}</td><td class="center">${e.ins_x ? "공식+코드" : "공식"}</td></tr>`).join("")}</tbody>
</table></div></div>
<div class="split-vert" style="flex:1;padding:0;">
${
it
? `<div class="win-subbar"><label>명칭 <input id="eq-name" value="${esc(it.name)}" style="width:260px"></label>
<label>적용 <select id="eq-insx"><option value="0" ${!it.ins_x ? "selected" : ""}>공 식</option><option value="1" ${it.ins_x ? "selected" : ""}>공식+코드</option></select></label></div>
<div class="pane-box" style="flex:1;"><textarea id="eq-text" class="smt-editor-area" spellcheck="false">${esc(it.text)}</textarea></div>
<div class="pane-box" style="max-height:120px;overflow:auto;font-size:12px;">
<div class="pane-title"><span>문법 검사 ${ev.errors.length}건 · 변수 ${esc(
Object.entries(ev.vars)
.map(([k, v]) => `${k}=${v}`)
.join(", ") || "없음",
)}</span></div>
${ev.errors.map((e) => `<div style="color:#b91c1c">${e.lineNo}번째줄: ${esc(e.msg)}</div>`).join("")}
</div>`
: "<div class='pane-box'>등록된 중기공식 없음 — [추가]</div>"
}
</div>
</div>
</div>`;
}
function bind(ctx, app) {
const u = ctx.uiState;
const $ = (id) => document.getElementById(id);
const items = u.eqDraft || [];
const i = Math.min(u.eqSel || 0, Math.max(0, items.length - 1));
const redraw = () => app.renderWorkspace();
document
.querySelectorAll(".eq-row")
.forEach((tr) => (tr.onclick = () => ((u.eqSel = Number(tr.dataset.i)), redraw())));
if ($("eq-name")) $("eq-name").onchange = (e) => ((items[i].name = e.target.value), redraw());
if ($("eq-insx"))
$("eq-insx").onchange = (e) => ((items[i].ins_x = Number(e.target.value)), redraw());
if ($("eq-text")) $("eq-text").onchange = (e) => ((items[i].text = e.target.value), redraw());
$("eq-add").onclick = () => {
if (!confirm("새로운 중기공식을 추가하시겠습니까?")) return;
items.push({
name: `신규공식 ${items.length + 1}`,
text: "q = 0.7 , K = 0.9 , f = 1/1.25 , E = 0.6\r\nCm = 21\r\nQ = 3600*q*K*f*E/Cm =",
ins_x: 0,
});
u.eqSel = items.length - 1;
redraw();
};
$("eq-del").onclick = () => {
if (!items.length || !confirm("선택된 중기공식을 삭제하시겠습니까?")) return;
items.splice(i, 1);
redraw();
};
$("eq-cancel").onclick = () => (
(u.eqDraft = JSON.parse(JSON.stringify(u.eqItems || []))),
redraw()
);
$("eq-save").onclick = async () => {
if (!confirm("수정된 중기공식을 저장하시겠습니까?")) return;
try {
await app.postJson("/api/qty/eq", { items });
u.eqItems = JSON.parse(JSON.stringify(items));
redraw();
} catch (err) {
alert("중기공식 저장 실패: " + err.message);
}
};
$("eq-close").onclick = () => ((u.activeScreen = "wM_Edit_iLWi"), redraw());
}
P.screens.wM_Edit_EQ = { render, bind };
P.actions.Act_Qty_EditEQ = (ctx, app) => (
(ctx.uiState.activeScreen = "wM_Edit_EQ"),
app.renderWorkspace()
);
if (!P.menus.some((m) => m.act === "Act_Qty_EditEQ"))
P.menus.push({ menu: "m2", label: "표준중기공식 편집", act: "Act_Qty_EditEQ" });
})(window);