/** * STMATE w 8.0 — 프로젝트 관리(Act_Maneger) · 공사목록표(wPrjList · TWPRJLIST) · 종료 화면 끼워 넣기 (scr_ui_prj.js) * 근거: REA 분석/09_보조분석/artifacts/resource-TWPRJLIST.dfm.json * PnTop 단추(등록정보 수정 · 제잡비 계산 · 해당분 수정 · 복사등록 · 정렬 · 해당분만 압축하기 · 미리보기) * MainMenu1(1. 수정 · 2. 내역서 등록 · 3. 해당분압축/조달청포맷 생성 · 4. 보기설정) * RGrdPrjList 열(공사번호 · 공사명 · 코드 · 공사종류 · 직공비 · 총공사비 · 복사원본) · RDbGrid_Boq(편집 불가 미리보기) * 공사목록 = PRJLST.DBF 행(prjlst_all) — 행마다 내역서 BOQ{PCODE}.DBF 하나 * wPrjList 는 다른 끼워 넣기가 먼저 등록했으면 그쪽을 씀(파일 담당 화면 우선) */ (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) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c], ); const $ = (id) => document.getElementById(id); const num = (v) => Number(String(v == null ? "" : v).replace(/,/g, "")) || 0; const go = (ctx, api, scr, extra) => ( Object.assign(ctx.uiState, { activeScreen: scr }, extra || {}), api.renderWorkspace() ); const activeCode = (ctx) => String((ctx.dbfData && ctx.dbfData.boq_table_name) || "").replace(/^BOQ|\.DBF$/g, ""); // ── 프로젝트 관리 ── function renderMgr(ctx) { const src = (ctx.sourcesData && ctx.sourcesData.projects) || []; const kinds = { folder: "PC 폴더", stc: "STC 압축", work_folder: "작업본 폴더" }; return `
프로젝트 관리 [ 열기, 등록, 백업, 삭제 ]${src.length}개
열린 프로젝트: ${esc(ctx.currentSourcePath || "(없음)")}
${src .map( (p, i) => ``, ) .join("")}
No구분프로젝트경로
${i + 1}${kinds[p.kind] || esc(p.kind)}${esc(p.name)}${esc(p.path)}
`; } function bindMgr(ctx, api) { document.querySelectorAll("#mgr-form .mgr-row").forEach((tr) => { tr.onclick = () => ( (ctx.uiState.mgrSel = tr.getAttribute("data-path")), api.renderWorkspace() ); tr.ondblclick = () => api.loadProjectFromServer(tr.getAttribute("data-path"), "wPrjList"); }); $("mgr-open").onclick = () => api.loadProjectFromServer(ctx.uiState.mgrSel, "wPrjList"); $("mgr-back").onclick = async () => { const r = await api.backup("now"); if (r) alert(`백업 완료\n• 폴더: ${r.saved_dir}\n• 압축: ${r.saved_stc}`); }; } // ── 공사목록표 ── const DISABLED = "아직 만들지 않은 기능"; // 파일 담당(scr_file.js) 동작 — data-file-act 단추는 문서 전체 클릭으로 그쪽이 받음 · 공사 코드 = prj-pcode 라디오 const FILE_ACT = { ActAddCopy: "copy", ActHae_Arc: "pack" }; const PRJ_MENU = [ [ "1. 수 정", [ ["ActEditBoq", "내역서 수정(&S)"], ["ActSagp", "관(사)급 내역 생성/수정"], ["Act_ED_PNUM", "등록정보 수정 (공사번호/공사명/공사종류)"], ["Act_DN_Chasu", "설계변경내역서 변경차수 내리기"], null, ["Act_Ed_Jab", "제잡비 계산"], ["Act_CalBoq", "선택내역서 재계산"], ["Act_Prn", "선택된 항목의 기본내역서 인쇄"], null, ["ActHaeCost", "해당분 단가 수정작업"], ["Act_Calc_Hae", "해당분 복합단가 전체 재계산"], ["Act_Find_Jod", "조달청제출용 일위대가 내용검증(손료부분,산근포함)"], ["Act_Find_XML", "조달청제출용 대가/산근 내용검증(XML, 손료부분 or 대가수량소수점)"], null, ["Act_Del_Prj", "내역서 삭제"], ["Act_Mer_Prj", "내역서 병합"], null, ["Act_Sagp_Del", "관/사급 내역 삭제"], ], ], [ "2. 내역서 등록", [ ["ActNewBoq", "신규등록(&A)"], ["ActAddCopy", "복사등록(&C)"], null, ["ActAddTo5", "할증내역으로 등록"], ["ActMkCont00", "계약내역 등록"], ["Act_MkCont41", "실행예산 복사등록"], ["ActMkCont10", "기성내역 등록"], null, ["ActMkCont21", "설계변경 등록 (당초/변경)"], ["ActMkCont20", "차수별 설계변경 등록"], ["ActMkCont31", "굴착변경 등록"], null, ["Act_Mk_Boq10", "공량내역(기계,전기..) 등록"], ], ], [ "3. 해당분압축/조달청포맷 생성", [ ["ActHae_Arc", "선택된 해당분만 압축하기[내역, 대가, 기초단가 ... ]"], null, ["Act_St8_XML", "공사원가호환규정 포맷(XML, 조달청) 변환(해당분)"], ["Act_St8_Jodal1", "JDL 포맷(TEXT) 변환(해당분)"], ], ], [ "4. 보기설정", [ ["Act_Find", "문자열 찾기"], ["Act_View_BOQ", "내역서 미리보기/숨기기"], null, ["ActIndex", "공사목록표 내림차순 정렬"], null, ["Act_Indx_PCODE", "공사목록표 코드순 정렬"], ["Act_Indx_PNAME", "공사목록표 공사명순 정렬"], ["Act_Indx_PNUM", "공사목록표 공사번호순 정렬"], null, ["Act_List_UP", "내역목록 위로이동"], ["Act_List_Down", "내역목록 아래로 이동"], ], ], ]; const prjRows = (ctx) => (ctx.dbfData && ctx.dbfData.prjlst_all) || []; const selRow = (ctx) => prjRows(ctx).find((r) => String(r.PCODE).trim() === ctx.uiState.prjSel) || prjRows(ctx)[0]; // 계약·실행·기성·설계변경 — 단계 담당(scr_stage.js) 탭 동작으로 const stage = (act) => (ctx, api) => P.actions[act] ? P.actions[act](ctx, api) : go(ctx, api, "wStage"); const sortBy = (key, desc) => (ctx, api) => { const rows = prjRows(ctx); rows.sort( (a, b) => String(a[key] || "").localeCompare(String(b[key] || ""), "ko", { numeric: true }) * (desc ? -1 : 1), ); api.renderWorkspace(); }; const moveSel = (dir) => (ctx, api) => { const rows = prjRows(ctx), i = rows.indexOf(selRow(ctx)), j = i + dir; if (i < 0 || j < 0 || j >= rows.length) return; [rows[i], rows[j]] = [rows[j], rows[i]]; api.renderWorkspace(); }; function needActive(ctx) { const r = selRow(ctx); if (r && String(r.PCODE).trim() !== activeCode(ctx)) { alert( `BOQ${String(r.PCODE).trim()}.DBF 내역서 열기는 서버 불러오기 대기 — 지금 열린 내역서는 BOQ${activeCode(ctx)}`, ); return false; } return true; } function editMeta(ctx, api) { const r = selRow(ctx); if (!r) return; api.openModal( "등록정보 수정 (공사번호/공사명/공사종류)", `
코드 뜻은 원본 분석 미확정 — 숫자 그대로
`, ); $("pm-ok").onclick = () => { Object.assign(r, { PNUM: $("pm-pnum").value, PNAME: $("pm-pname").value, PCLASS: String(num($("pm-pclass").value)), }); if (String(r.PCODE).trim() === activeCode(ctx)) { Object.assign(ctx.state.projectMeta, { pname: r.PNAME, pnum: r.PNUM, pclass: num(r.PCLASS), }); if (ctx.dbfData.prjlst && ctx.dbfData.prjlst !== r) Object.assign(ctx.dbfData.prjlst, { PNUM: r.PNUM, PNAME: r.PNAME, PCLASS: r.PCLASS }); } api.closeModal(); api.renderWorkspace(); }; } const H = { ActEditBoq: (ctx, api) => needActive(ctx) && go(ctx, api, "wBoq"), ActHaeCost: (ctx, api) => go(ctx, api, "wM_JabPum"), Act_ED_PNUM: editMeta, Act_Ed_Jab: (ctx, api) => needActive(ctx) && go(ctx, api, "wM_KanJub"), Act_CalBoq: (ctx, api) => needActive(ctx) && api.cascadeAndRender(), Act_Calc_Hae: (ctx, api) => api.cascadeAndRender(), Act_Prn: (ctx, api) => go(ctx, api, "PM_Prt"), ActMkCont00: stage("Act_Stg_Contract"), Act_MkCont41: stage("Act_Stg_Exec"), ActMkCont10: stage("Act_Stg_Prog"), ActMkCont21: stage("Act_Stg_Change"), ActMkCont20: stage("Act_Stg_Change"), Act_St8_XML: (ctx, api) => go(ctx, api, "PM_Prt"), Act_St8_Jodal1: (ctx, api) => go(ctx, api, "PM_Prt"), Act_Find: (ctx, api) => { const kw = prompt("찾을 문자열(공사명·공사번호)", ctx.uiState.prjFind || ""); if (kw == null) return; ctx.uiState.prjFind = kw.trim(); api.renderWorkspace(); }, Act_View_BOQ: (ctx, api) => ( (ctx.uiState.showBoqPreview = ctx.uiState.showBoqPreview === false), api.renderWorkspace() ), ActIndex: (ctx, api) => ( (ctx.uiState.prjDesc = !ctx.uiState.prjDesc), sortBy("PCODE", ctx.uiState.prjDesc)(ctx, api) ), Act_Indx_PCODE: sortBy("PCODE"), Act_Indx_PNAME: sortBy("PNAME"), Act_Indx_PNUM: sortBy("PNUM"), Act_List_UP: moveSel(-1), Act_List_Down: moveSel(1), }; const enabled = (act) => Boolean(P.actions[act] || H[act]); const actBtn = (a, t, cls) => FILE_ACT[a] && !P.actions[a] ? `` : ``; const run = (act, ctx, api, btn) => P.actions[act] ? P.actions[act](ctx, api, btn) : H[act] && H[act](ctx, api); function previewRows(ctx, code) { if (code === activeCode(ctx)) return global.STMATE_VIEW_PRJ_BOQ.evaluateFullBoqRows(ctx); return ((ctx.dbfData.boq_tables || {})[`BOQ${code}.DBF`] || []).map((r) => Object.assign({}, r), ); } function renderList(ctx, api) { if (!ctx.dbfData) return renderMgr(ctx); const kw = String(ctx.uiState.prjFind || "").toLowerCase(); const rows = prjRows(ctx).filter( (r) => !kw || `${r.PNUM} ${r.PNAME}`.toLowerCase().includes(kw), ); const cur = selRow(ctx), code = cur ? String(cur.PCODE).trim() : ""; const act = activeCode(ctx); const { fmt } = api; const bt = ctx.computed.boqComputed.totals, kj = ctx.computed.kanjubComputed; const show = ctx.uiState.showBoqPreview !== false; const prev = show ? previewRows(ctx, code) : []; const menu = ``; const btn = (a, t) => actBtn(a, t, "win-btn"); return `
공사목록표 [wPrjList · PRJLST.DBF] — ${prjRows(ctx).length}건${esc(ctx.currentSourcePath)}
${menu}
${btn("Act_ED_PNUM", "등록정보 수정")}${btn("Act_Ed_Jab", "제잡비 계산")}${btn("ActEditBoq", "해당분 수정")}${btn("ActAddCopy", "복 사 등 록")}${btn("ActIndex", ctx.uiState.prjDesc ? "올림차순" : "내림차순")}${btn("ActHae_Arc", "해당분만 압축하기")}${btn("Act_View_BOQ", show ? "미리보기 숨기기" : "내역서 미리보기")}${kw ? `찾기: ${esc(kw)}` : ""}
${rows .map((r) => { const c = String(r.PCODE).trim(); const live = c === act ? `${fmt(bt.directT)} / ${fmt(kj.GRAND_TOTAL)}` : ""; return ``; }) .join("")}
공사번호공 사 명코드공사종류직공비총공사비복사원본지금 계산(직공비 / 총공사비)
${esc(r.PNUM)}${esc(r.PNAME)}${esc(c)}${c === act ? " ●" : ""}${esc(r.PCLASS)} · 차수 ${esc(r.K_ORD)}${fmt(num(r.TCOST1))}${fmt(num(r.COSTW1))}${num(r.CCODE) ? esc(r.CCODE) : ""}${live}
${ show ? `
BOQ${esc(code)}.DBF 미리보기 — ${prev.length}행 (이 화면에서는 편집할 수 없음)${code === act ? "지금 계산 금액" : "파일 저장 금액"}
${prev .map( (r) => ``, ) .join("")}
공종명 칭규 격수 량단위단가금액비고
${esc(r.ITNUM)}${esc(r.NAME1)}${esc(r.SIZE1)}${num(r.QTY1) ? fmt(num(r.QTY1), 4) : ""}${esc(r.UNIT1)}${num(r.TAMT1) ? fmt(num(r.TAMT1)) : ""}${num(r.T_AMT1) ? fmt(num(r.T_AMT1)) : ""}${esc(String(r.BDCODE1 || "").trim())}
` : "" }
PrjBar — 선택: ${esc(cur ? cur.PNAME : "")} (BOQ${esc(code)}) · 열린 내역서 BOQ${esc(act)}
`; } function bindList(ctx, api) { if (!ctx.dbfData) return bindMgr(ctx, api); if (!ctx.uiState.prjSel) ctx.uiState.prjSel = activeCode(ctx); ctx.uiState.filePcode = ctx.uiState.prjSel; document .querySelectorAll("#prj-form [data-prj-act]") .forEach( (b) => (b.onclick = () => !b.disabled && run(b.getAttribute("data-prj-act"), ctx, api, b)), ); document.querySelectorAll("#prj-form .prj-pick").forEach((tr) => { tr.onclick = () => ( (ctx.uiState.prjSel = tr.getAttribute("data-code")), api.renderWorkspace() ); tr.ondblclick = () => ( (ctx.uiState.prjSel = tr.getAttribute("data-code")), run("ActEditBoq", ctx, api) ); }); } P.screens.wPrjMgr = { render: renderMgr, bind: bindMgr }; if (!P.screens.wPrjList) P.screens.wPrjList = { render: renderList, bind: bindList }; P.screens.wQuit = { render: () => `
시스템종료

STMATE 작업을 마쳤음.

`, bind: (ctx, api) => ($("quit-back").onclick = () => go(ctx, api, "wPrjMgr")), }; })(window);