/**
* 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 `
`;
}
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 ``;
}
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: () =>
``,
bind: (ctx, api) => ($("quit-back").onclick = () => go(ctx, api, "wPrjMgr")),
};
})(window);