- 보고서: 원본 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>
396 lines
15 KiB
JavaScript
396 lines
15 KiB
JavaScript
/**
|
|
* STMATE w 8.0 — 메인 메뉴(M_Menu1)·툴바(Tool_Bar) 원본 체계 (ui_menu.js) · 순수 자료·HTML 생성
|
|
* 근거: REA 분석/09_보조분석_화면저장구조/artifacts/resource-TWM_STMATE_START_8X.dfm.json
|
|
* 메뉴 항목 캡션 = TMenuItem.Caption 또는 연결 TAction.Caption · 단축키 = TAction.ShortCut
|
|
* Visible=False 항목(급수설계·전산내역 입찰·K 분류·도움말·팁·MS-DOS 실행 등)은 원본도 숨김 → 넣지 않음
|
|
* 범위 밖(유지보수 신청·업그레이드·원격지원·홈페이지·모니터 이동 등)은 꺼진 항목으로 둠
|
|
*/
|
|
(function (global) {
|
|
"use strict";
|
|
|
|
const SEP = { sep: true };
|
|
const OUT = "범위 밖(인증·업데이트·외부 연결)";
|
|
const it = (act, cap, extra) => Object.assign({ act, cap }, extra || {});
|
|
|
|
const MENU_TREE = [
|
|
{
|
|
id: "m1",
|
|
cap: "1. 프로젝트/초기값",
|
|
key: "1",
|
|
kids: [
|
|
it("Act_Maneger", "프로젝트 관리", { hint: "프로젝트 관리 [ 열기, 등록, 백업, 삭제 ]" }),
|
|
it("Act_Sys", "초기값 입력및수정 (환율/소수점)", {
|
|
hint: "초기값 입력 및 수정[ 환율 및 기초자료 ]",
|
|
}),
|
|
SEP,
|
|
it("Act_Calc_Grass", "계 산 완 료", { sc: 120, hint: "수정분 계산" }),
|
|
it("Act_Calc_All", "전체 재계산", { hint: "전체 재계산" }),
|
|
it("Act_Refresh", "작업파일 다시 읽기", { sc: 16500, hint: "모든 테이블 다시 읽기" }),
|
|
SEP,
|
|
it("Act_Arc_Project", "현재프로젝트 압축하기"),
|
|
it("Act_Close_Table", "프로젝트(작업파일) 닫기"),
|
|
SEP,
|
|
it("mi_AutoBack", "종료시 프로젝트 자동백업(1일단위)", { check: "autoBackDaily" }),
|
|
it("mi_Auto_Time_Back", "30분마다 자동백업(재계산시)", { check: "autoBack30" }),
|
|
it("Act_Arc_Now", "현재상태 자동백업"),
|
|
SEP,
|
|
{
|
|
id: "mi_Recent",
|
|
cap: "최근 작업폴더",
|
|
recent: true,
|
|
kids: [it("mi_Del_Recent", "최근목록 지우기"), SEP],
|
|
},
|
|
SEP,
|
|
it("Act_Quit", "시스템종료", { sc: 32849, hint: "시스템 종료[ STmate 종료 ]" }),
|
|
it("Act_Halt", "프로그램 강제종료"),
|
|
],
|
|
},
|
|
{
|
|
id: "m2",
|
|
cap: "2. 단가표/대가표",
|
|
key: "2",
|
|
kids: [
|
|
it("Act_Dan", "단가목록표(전체분)", {
|
|
sc: 16449,
|
|
hint: "단가목록표[기초단가 등록 및 수정]",
|
|
}),
|
|
it("Act_iLWi_San", "일위대가표,중기사용료,단가산출근거", {
|
|
sc: 16450,
|
|
hint: "대가,산근[ 일위대가, 단가산출근거, 중기사용료]",
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
id: "m3",
|
|
cap: "3. 내역서/인쇄",
|
|
key: "3",
|
|
kids: [
|
|
it("Act_PrjLst", "공사목록표", { sc: 16462, hint: "공사목록표[내역서 입력 및 수정)" }),
|
|
it("Act_Prt", "보고서 인쇄", { sc: 24656, hint: "모든 보고서 인쇄" }),
|
|
SEP,
|
|
it(
|
|
"Act_P_2PDF",
|
|
"보고서 저장파일 ∥ SRP( 체크해제 ) ⇔ PDF【 Microsoft Print to PDF 】( √ 체크 )",
|
|
{
|
|
check: "prnPdf",
|
|
},
|
|
),
|
|
it("Act_Un_Dir", "보고서 저장폴더 미지정(마지막 저장폴더로 설정)"),
|
|
],
|
|
},
|
|
{
|
|
id: "m4",
|
|
cap: "4. 도구",
|
|
key: "4",
|
|
kids: [
|
|
{
|
|
id: "mi_Merge",
|
|
cap: "데이타병합 【 현재폴더(복사처) ← 다른폴더(복사원) 】",
|
|
kids: [
|
|
it("Act_Merge", "동일항목 : 호환규정코드 or 시설코드 or 명칭..단위로 인식"),
|
|
it("Act_Merge_C", "동일항목 : 코드번호로 인식(두 프로젝트의 코드번호가 같을때)"),
|
|
],
|
|
},
|
|
it("Act_Conv_Equip8", "하위버젼 중기비내역 형식 → 2Y.m형식으로.."),
|
|
SEP,
|
|
{
|
|
id: "mi_ManageFile",
|
|
cap: "파 일 관 리",
|
|
kids: [
|
|
{
|
|
id: "N14",
|
|
cap: "코드변경",
|
|
kids: [
|
|
it("Act_Chg_Code_Sel", "1. 선택 변경"),
|
|
it("Act_Chg_Code_All", "2. 일괄 변경"),
|
|
it("Act_Chg_Plus", "3. 코드번호 변경 ( 현재코드 + α )"),
|
|
],
|
|
},
|
|
{
|
|
id: "N15",
|
|
cap: "코드삭제",
|
|
kids: [
|
|
it("Act_Del_Code_Sel", "1. 선택 삭제"),
|
|
SEP,
|
|
it("Act_Del_Code_All_Minus", "2. 일괄 삭제 (중기비제외)"),
|
|
it("Act_Del_Code_All_Plus", "3. 일괄 삭제 (중기비포함)"),
|
|
],
|
|
},
|
|
it("Act_Chg_Item", "항목교체(내역포함)"),
|
|
SEP,
|
|
it("Act_Del_Prj", "내역서 삭제"),
|
|
it("Act_Mer_Prj", "내역서 병합"),
|
|
SEP,
|
|
it("Act_1st_2nd", "당초단가 ⇒ 변경단가(당초단가 적용)"),
|
|
it("Act_2nd_1st", "변경단가 ⇒ 당초단가(변경단가 적용)"),
|
|
],
|
|
},
|
|
{
|
|
id: "mi_UseName2",
|
|
cap: "보조 명칭/규격 관리",
|
|
kids: [
|
|
it("Act_UseName2", "보조 명칭/규격 사용", { check: "useName2" }),
|
|
SEP,
|
|
it("Act_Ch_1ST_2ND", "메인명칭/규격 ⇔ 보조명칭/규격(서로바꾸기)"),
|
|
SEP,
|
|
it("Act_Ch_Blank_2ND", "보조명칭/규격 초기화"),
|
|
],
|
|
},
|
|
{
|
|
id: "mi_Index",
|
|
cap: "자료파일 인덱스",
|
|
kids: [
|
|
it("Act_Indx", "인덱스파일 복구"),
|
|
SEP,
|
|
it("Act_Indx_N", "명칭 인덱스( 명칭 )"),
|
|
it("Act_Indx_NS", "명칭 인덱스( 명칭 + 규격 )"),
|
|
],
|
|
},
|
|
{
|
|
id: "N74",
|
|
cap: "목록표 기본정렬 기준 / 명칭,규격 넓이",
|
|
kids: [
|
|
it("Act_DFL_INDX_C", "코드번호순 기본정렬", { radio: ["dflIndex", "C"] }),
|
|
it("Act_DFL_INDX_N", "명 칭 순 기본정렬", { radio: ["dflIndex", "N"] }),
|
|
it("Act_DFL_INDX_I", "분류번호순 기본정렬", { radio: ["dflIndex", "I"] }),
|
|
it("Act_DFL_INDX_E", "시설코드순 기본정렬", { radio: ["dflIndex", "E"] }),
|
|
SEP,
|
|
...[20, 30, 40, 50, 60].map((w) =>
|
|
it(`Act_DFL_WIDTH_${w}`, `명칭,규격 ${w}Byte 넓이정렬`, { radio: ["dflWidth", w] }),
|
|
),
|
|
],
|
|
},
|
|
{
|
|
id: "N30Byte2",
|
|
cap: "명칭, 규격 30Byte 이상 인쇄설정 (엑셀제외)",
|
|
kids: [
|
|
it("Act_PRN_1LN", "글자수에 관계없이 한줄로 인쇄"),
|
|
SEP,
|
|
it("Act_PRN_2LN_30", "30Byte 이상 두줄인쇄"),
|
|
it("Act_PRN_2LN_40", "40Byte 이상 두줄인쇄"),
|
|
it("Act_PRN_2LN_50", "50Byte 이상 두줄인쇄"),
|
|
],
|
|
},
|
|
it("Act_Restore", "자료파일 복원"),
|
|
it("Act_Del_TrashFile", "불필요한 하위버전 흔적파일 정리"),
|
|
SEP,
|
|
it("Act_Auto_Jod", "조달청제출용 일위대가 자동수정(손료부분,산근제외)"),
|
|
it("Act_Find_Jod", "조달청제출용 일위대가 내용검증(손료부분,산근포함)"),
|
|
it("Act_Find_XML", "조달청제출용 대가/산근 내용검증(XML, 손료부분 or 대가수량소수점)"),
|
|
SEP,
|
|
{
|
|
id: "N54",
|
|
cap: "정보/설정 초기화",
|
|
kids: [
|
|
it("N44", '모든 "위치정보" 초기화'),
|
|
it("N55", '내역서 "보기설정" 초기화'),
|
|
it("N56", '산근/대가 "문법및정렬기준" 초기화'),
|
|
SEP,
|
|
it("mi_MemSize", "메모리사용량 초기화"),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: "m5",
|
|
cap: "5. 기타",
|
|
key: "5",
|
|
kids: [
|
|
it("Act_information", "제품 및 기타정보"),
|
|
it("Act_Req_AS", "유지보수(A/S)연장 신청", { out: true }),
|
|
SEP,
|
|
it("Act_Down_Center", "다운로드 센터(기초자료)", { out: true }),
|
|
SEP,
|
|
{
|
|
id: "N32",
|
|
cap: "홈페이지 접속",
|
|
kids: [it("Act_Home", "경영정보(주) - http://www.stma.co.kr", { out: true })],
|
|
},
|
|
it("Act_ReqQS", "원격지원 요청", { out: true }),
|
|
it("Window1", "윈도우 탐색기", { sc: 16471, out: true }),
|
|
SEP,
|
|
it("mi_Auto_Up_Chk", "프로그램 시작시 제품UpGrade 자동확인", { out: true }),
|
|
it("Act_UpGrade", "UpGrade 정보확인", { out: true }),
|
|
it("Act_Gongji", "STmate 공지사항 확인", { out: true }),
|
|
it("Act_Dn_Fld", "다운로드폴더 탐색기로열기", { out: true }),
|
|
SEP,
|
|
{
|
|
id: "mi_SetScreen",
|
|
cap: "메인윈도우 화면선택",
|
|
kids: [
|
|
it("Act_SetScreen", "메인윈도우 화면변경", { sc: 24653 }),
|
|
SEP,
|
|
it("Act_SetScreen_0", "메인윈도우 전체화면 보기", { radio: ["screenMode", 0] }),
|
|
it("Act_SetScreen_1", "메인윈도우 중간화면 보기", { radio: ["screenMode", 1] }),
|
|
it("Act_SetScreen_2", "메인윈도우 작은화면 보기", { radio: ["screenMode", 2] }),
|
|
it("Act_SetScreen_3", "메인윈도우 크기조절화면 보기", { radio: ["screenMode", 3] }),
|
|
],
|
|
},
|
|
{
|
|
id: "miMonitor",
|
|
cap: "모니터 이동",
|
|
kids: [
|
|
it("miMonitor_0", "주 모니터", { out: true }),
|
|
it("miMonitor_1", "보조모니터 1", { out: true }),
|
|
it("miMonitor_2", "보조모니터 2", { out: true }),
|
|
],
|
|
},
|
|
{
|
|
id: "mi_Scale_UD",
|
|
cap: "주요화면 크기 변경",
|
|
kids: [
|
|
it("Act_Scale_Dfl", "주요화면 크기 100% 설정", { sc: 57392 }),
|
|
SEP,
|
|
it("Act_Scale_Up", "주요화면 크기(25% 증가)", { sc: 57382 }),
|
|
it("Act_Scale_Dn", "주요화면 크기(25% 감소) ", { sc: 57384 }),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
// 툴바 — Act_Maneger · Act_Sys | Act_Dan · Act_iLWi_San | Act_PrjLst · Act_Prt | 계 산 완 료 | 시스템종료
|
|
const TOOLBAR = [
|
|
{ act: "Act_Maneger", screen: "wPrjMgr", cap: "프로젝트관리", ico: "🗂️" },
|
|
{ act: "Act_Sys", screen: "wM_Set_Sys", cap: "초 기 값", ico: "⚙️" },
|
|
SEP,
|
|
{ act: "Act_Dan", screen: "wM_JabPum", cap: "단가목록표", ico: "📋" },
|
|
{ act: "Act_iLWi_San", screen: "wM_Edit_iLWi", cap: "대 가 표", ico: "🧮" },
|
|
SEP,
|
|
{ act: "Act_PrjLst", screen: "wPrjList", cap: "공사목록표", ico: "📁" },
|
|
{ act: "Act_Prt", screen: "PM_Prt", cap: "보고서인쇄", ico: "🖨️" },
|
|
SEP,
|
|
{
|
|
act: "Act_Calc_Grass",
|
|
id: "btn-calc-all",
|
|
cap: "계 산 완 료",
|
|
ico: "⚡",
|
|
hint: "수정분 계산",
|
|
},
|
|
SEP,
|
|
{ act: "Act_Quit", cap: "시스템종료", ico: "⏻" },
|
|
];
|
|
|
|
// 단축키 — Delphi ShortCut(하위 바이트 = 가상키 · 0x2000 Shift · 0x4000 Ctrl · 0x8000 Alt)
|
|
const VK = { 13: "Enter", 38: "Up", 40: "Down", 45: "Ins", 46: "Del" };
|
|
function decodeShortcut(sc) {
|
|
if (!sc) return "";
|
|
const vk = sc & 0xff,
|
|
parts = [];
|
|
if (sc & 0x4000) parts.push("Ctrl");
|
|
if (sc & 0x2000) parts.push("Shift");
|
|
if (sc & 0x8000) parts.push("Alt");
|
|
let k = VK[vk];
|
|
if (!k && vk >= 112 && vk <= 123) k = "F" + (vk - 111);
|
|
if (!k && ((vk >= 48 && vk <= 57) || (vk >= 65 && vk <= 90))) k = String.fromCharCode(vk);
|
|
parts.push(k || `#${vk}`);
|
|
return parts.join("+");
|
|
}
|
|
|
|
// 키 누름 → 단축키 문자열(decodeShortcut 과 같은 꼴)
|
|
function eventShortcut(e) {
|
|
const parts = [];
|
|
if (e.ctrlKey) parts.push("Ctrl");
|
|
if (e.shiftKey) parts.push("Shift");
|
|
if (e.altKey) parts.push("Alt");
|
|
const k = e.key === "ArrowUp" ? "Up" : e.key === "ArrowDown" ? "Down" : e.key;
|
|
parts.push(/^[a-z]$/.test(k) ? k.toUpperCase() : k === ")" ? "0" : k);
|
|
return parts.join("+");
|
|
}
|
|
|
|
// 단축키 → 동작 표
|
|
function shortcutMap(tree) {
|
|
const map = {};
|
|
const walk = (list) =>
|
|
list.forEach((n) => {
|
|
if (n.sc && n.act) map[decodeShortcut(n.sc)] = n.act;
|
|
if (n.kids) walk(n.kids);
|
|
});
|
|
walk(tree || MENU_TREE);
|
|
return map;
|
|
}
|
|
|
|
const esc = (s) =>
|
|
String(s == null ? "" : s).replace(
|
|
/[&<>"]/g,
|
|
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c],
|
|
);
|
|
|
|
/**
|
|
* 메뉴 HTML — st = { enabled(act) · checked(node) · recent: [{path,name}] }
|
|
* 이어진 구분선·앞뒤 구분선은 한 줄로 · 꺼진 항목은 disabled + 까닭(title)
|
|
*/
|
|
function renderItems(kids, st) {
|
|
const out = [];
|
|
let lastSep = true;
|
|
kids.forEach((n) => {
|
|
if (n.sep) {
|
|
if (!lastSep) out.push("<hr />");
|
|
lastSep = true;
|
|
return;
|
|
}
|
|
lastSep = false;
|
|
if (n.kids) {
|
|
const extra = n.recent
|
|
? (st.recent || [])
|
|
.map(
|
|
(r) =>
|
|
`<button data-act="Act_Open_Recent" data-path="${esc(r.path)}">${esc(r.name)}</button>`,
|
|
)
|
|
.join("") || '<button disabled title="최근 연 프로젝트 없음">(없음)</button>'
|
|
: "";
|
|
out.push(
|
|
`<div class="menu-sub"><button class="menu-sub-head">${esc(n.cap)}<span class="menu-arrow">▸</span></button><div class="menu-dropdown menu-subdrop">${renderItems(n.kids, st)}${extra}</div></div>`,
|
|
);
|
|
return;
|
|
}
|
|
const on = !n.out && st.enabled(n.act);
|
|
const mark = n.check || n.radio ? (st.checked(n) ? "✔" : "") : "";
|
|
const why = n.out ? OUT : on ? n.hint || "" : "아직 만들지 않은 기능";
|
|
out.push(
|
|
`<button data-act="${n.act}"${on ? "" : " disabled"} title="${esc(why)}"><span class="menu-check">${mark}</span><span class="menu-cap">${esc(n.cap)}</span><span class="menu-sc">${decodeShortcut(n.sc)}</span></button>`,
|
|
);
|
|
});
|
|
while (out.length && out[out.length - 1] === "<hr />") out.pop();
|
|
return out.join("");
|
|
}
|
|
|
|
function renderMenuBar(st, tree) {
|
|
return (tree || MENU_TREE)
|
|
.map(
|
|
(m) =>
|
|
`<div class="menu-item" data-menu="${m.id}"><span><u>${m.key}</u>${esc(m.cap.slice(1))}</span><div class="menu-dropdown">${renderItems(m.kids, st)}</div></div>`,
|
|
)
|
|
.join("");
|
|
}
|
|
|
|
function renderToolbar(active) {
|
|
return TOOLBAR.map((b) =>
|
|
b.sep
|
|
? '<div class="tb-sep"></div>'
|
|
: `<button class="tb-btn${b.id === "btn-calc-all" ? " tb-calc" : ""}${b.screen && b.screen === active ? " active" : ""}"${b.id ? ` id="${b.id}"` : ""} data-tb-act="${b.act}"${b.screen ? ` data-screen="${b.screen}"` : ""} title="${esc(b.hint || b.cap)}"><span class="tb-ico">${b.ico}</span><span class="tb-txt">${esc(b.cap)}</span></button>`,
|
|
).join("");
|
|
}
|
|
|
|
// 모든 동작 이름(꺼짐 판정·시험용)
|
|
function allActs(tree) {
|
|
const acts = [];
|
|
const walk = (list) => list.forEach((n) => (n.act && acts.push(n.act), n.kids && walk(n.kids)));
|
|
walk(tree || MENU_TREE);
|
|
return acts;
|
|
}
|
|
|
|
global.STMATE_UI_MENU = {
|
|
MENU_TREE,
|
|
TOOLBAR,
|
|
decodeShortcut,
|
|
eventShortcut,
|
|
shortcutMap,
|
|
renderMenuBar,
|
|
renderToolbar,
|
|
allActs,
|
|
esc,
|
|
};
|
|
if (typeof module !== "undefined" && module.exports) module.exports = global.STMATE_UI_MENU;
|
|
})(typeof window !== "undefined" ? window : globalThis);
|