196 lines
7.7 KiB
JavaScript
196 lines
7.7 KiB
JavaScript
/* -*- coding: utf-8 -*- */
|
|
/**
|
|
* 보고서 인쇄 선택 (scr_report_opts.js) — PM_Prt 인쇄 메뉴 옵션 · PM_Prn_Ord 인쇄순서 · PM_Sel_St 집계수준 · wM_Sel_Prn 인쇄영역
|
|
* 옵션(F1-51) — 글꼴 · 글자 크기 · 코드필드(SH_TYP1 코드/호표) · 비고(SH_TYP3 코드/호표/가격지) · 번호 자리 맞춤(CH_PRN_COD) · 방향
|
|
* 순서·수준(F1-52) — 내보낼 시트 차례(위·아래) · 총괄·집계 공종 단(1~6)
|
|
* 영역(F1-53) — 차수(당초 · 변경 1~8차) · 변경분만 · 분할 비율 · 구.시도 시기금 비율 · 엑셀 수식 넣기
|
|
* 근거: 지식DB STmate/old/20_분석 20 §2.4(Start8x.Ini 출력 설정) · 34(화면 구조 PM_Prt · wM_Sel_Prn) · 24 §3(Prn_FMLA_INFO)
|
|
* 화면 상태 = ctx.uiState.reportOpts · 보고서 묶음은 effective() 로 설정을 덮어 만든 dbf 로 다시 짬
|
|
*/
|
|
(function (global) {
|
|
"use strict";
|
|
const esc = (s) =>
|
|
String(s ?? "").replace(
|
|
/[&<>"]/g,
|
|
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c],
|
|
);
|
|
const FONTS = ["굴림체", "돋움체", "바탕체", "맑은 고딕", "궁서체"];
|
|
|
|
// 시작값 — 공사 설정(Start8x.Ini)에서
|
|
function defaults(ini) {
|
|
return {
|
|
font: String(ini.FONT || "굴림체"),
|
|
size: 10,
|
|
codeField: String(ini.SH_TYP1 ?? "1"),
|
|
remark: String(ini.SH_TYP3 ?? "2"),
|
|
padNo: String(ini.CH_PRN_COD ?? "0"),
|
|
orient: "auto",
|
|
order: null,
|
|
level: 2,
|
|
changeOnly: false,
|
|
splitRate: 100,
|
|
fundRate: 100,
|
|
formulas: false,
|
|
};
|
|
}
|
|
function state(ctx) {
|
|
const ini = (ctx.dbfData && ctx.dbfData.start_ini) || {};
|
|
const ui = ctx.uiState;
|
|
ui.reportOpts = Object.assign(defaults(ini), ui.reportOpts || {});
|
|
return ui.reportOpts;
|
|
}
|
|
|
|
/**
|
|
* 설정을 덮은 dbf + 묶음 opts — 출력 설정(SH_TYP1·SH_TYP3·CH_PRN_COD)은 Start8x.Ini 값만 바꿔 같은 짜기 코드를 씀
|
|
*/
|
|
function effective(ctx) {
|
|
const o = state(ctx);
|
|
const dbf = Object.assign({}, ctx.dbfData);
|
|
dbf.start_ini = Object.assign({}, ctx.dbfData.start_ini || {}, {
|
|
SH_TYP1: o.codeField,
|
|
SH_TYP3: o.remark,
|
|
CH_PRN_COD: o.padNo,
|
|
});
|
|
const today = new Date();
|
|
const pad = (n) => String(n).padStart(2, "0");
|
|
const opts = {
|
|
order: o.order,
|
|
minLevel: Number(o.level) || 2,
|
|
level: Number(o.level) || 2,
|
|
changeOnly: !!o.changeOnly,
|
|
splitRate: Number(o.splitRate),
|
|
fundRate: Number(o.fundRate),
|
|
date: `${today.getFullYear()}-${pad(today.getMonth() + 1)}-${pad(today.getDate())} ${pad(today.getHours())}:${pad(today.getMinutes())}`,
|
|
};
|
|
const print = { font: o.font, size: Number(o.size) || 10 };
|
|
if (o.orient !== "auto") print.orient = o.orient;
|
|
return { dbf, opts, print, formulas: !!o.formulas, key: JSON.stringify(o) };
|
|
}
|
|
|
|
// 인쇄순서 — 저장된 차례 + 새로 생긴 시트(뒤에)
|
|
function orderOf(ctx, ids) {
|
|
const o = state(ctx);
|
|
const base = (o.order || []).filter((id) => ids.includes(id));
|
|
return base.concat(ids.filter((id) => !base.includes(id)));
|
|
}
|
|
|
|
function html(ctx, ids, stages) {
|
|
const o = state(ctx);
|
|
const sel = (name, list, cur) =>
|
|
`<select data-ro="${name}">${list.map(([v, l]) => `<option value="${esc(v)}" ${String(cur) === String(v) ? "selected" : ""}>${esc(l)}</option>`).join("")}</select>`;
|
|
const ord = orderOf(ctx, ids)
|
|
.map(
|
|
(id, i) =>
|
|
`<div style="display:flex;gap:4px;align-items:center"><span style="width:22px;text-align:right">${i + 1}</span><span style="flex:1">${esc(id)}</span><button class="win-btn" data-ro-up="${esc(id)}">▲</button><button class="win-btn" data-ro-dn="${esc(id)}">▼</button></div>`,
|
|
)
|
|
.join("");
|
|
const st = stages.map((n) => [n, n === 1 ? "당초" : `변경 ${n - 1}차`]);
|
|
return `<details id="ro-box" ${ctx.uiState.reportOptsOpen ? "open" : ""} style="margin:4px 0;border:1px solid #ccc;padding:4px">
|
|
<summary>인쇄 선택 — 옵션 · 순서 · 영역</summary>
|
|
<div style="display:flex;gap:16px;flex-wrap:wrap;align-items:flex-start">
|
|
<fieldset><legend>인쇄 메뉴 옵션</legend>
|
|
<div>글꼴 ${sel(
|
|
"font",
|
|
FONTS.map((f) => [f, f]),
|
|
o.font,
|
|
)} 크기 ${sel(
|
|
"size",
|
|
[8, 9, 10, 11, 12].map((s) => [s, s]),
|
|
o.size,
|
|
)}</div>
|
|
<div>코드필드 ${sel(
|
|
"codeField",
|
|
[
|
|
["0", "코드번호"],
|
|
["1", "호표"],
|
|
],
|
|
o.codeField,
|
|
)}
|
|
비고 ${sel(
|
|
"remark",
|
|
[
|
|
["0", "코드"],
|
|
["1", "호표"],
|
|
["2", "가격지 위치"],
|
|
],
|
|
o.remark,
|
|
)}</div>
|
|
<div>번호 자리 맞춤 ${sel(
|
|
"padNo",
|
|
[
|
|
["0", "안 함"],
|
|
["1", "함"],
|
|
],
|
|
o.padNo,
|
|
)}
|
|
방향 ${sel(
|
|
"orient",
|
|
[
|
|
["auto", "자동(열 수)"],
|
|
["portrait", "세로"],
|
|
["landscape", "가로"],
|
|
],
|
|
o.orient,
|
|
)}</div>
|
|
</fieldset>
|
|
<fieldset><legend>인쇄영역</legend>
|
|
<div>차수 ${sel("stage", st, ctx.uiState.reportStage || 1)}
|
|
<label><input type="checkbox" data-ro="changeOnly" ${o.changeOnly ? "checked" : ""}> 변경분만(집계표)</label></div>
|
|
<div>공종 집계수준 ${sel(
|
|
"level",
|
|
[1, 2, 3, 4, 5, 6].map((n) => [n, `${n} 단 이상`]),
|
|
o.level,
|
|
)}</div>
|
|
<div>분할 비율 <input data-ro="splitRate" value="${esc(o.splitRate)}" size="4">% · 시기금 비율 <input data-ro="fundRate" value="${esc(o.fundRate)}" size="4">%</div>
|
|
<div><label><input type="checkbox" data-ro="formulas" ${o.formulas ? "checked" : ""}> 엑셀 수식 넣기(내역·일위대가·집계 금액)</label></div>
|
|
</fieldset>
|
|
<fieldset style="min-width:260px"><legend>인쇄순서</legend><div style="max-height:180px;overflow:auto">${ord}</div>
|
|
<button class="win-btn" data-ro-reset="1">기본 순서</button></fieldset>
|
|
</div></details>`;
|
|
}
|
|
|
|
function bind(ctx, api, ids) {
|
|
const o = state(ctx);
|
|
const ui = ctx.uiState;
|
|
const box = document.getElementById("ro-box");
|
|
if (box) box.ontoggle = () => (ui.reportOptsOpen = box.open);
|
|
const redo = () => {
|
|
ui._reportBook = null;
|
|
api.renderWorkspace();
|
|
};
|
|
document.querySelectorAll("[data-ro]").forEach((el) => {
|
|
el.onchange = () => {
|
|
const k = el.getAttribute("data-ro");
|
|
const v = el.type === "checkbox" ? el.checked : el.value;
|
|
if (k === "stage") ui.reportStage = Number(v);
|
|
else o[k] = v;
|
|
redo();
|
|
};
|
|
});
|
|
const move = (id, d) => {
|
|
const list = orderOf(ctx, ids);
|
|
const i = list.indexOf(id);
|
|
const j = i + d;
|
|
if (i < 0 || j < 0 || j >= list.length) return;
|
|
[list[i], list[j]] = [list[j], list[i]];
|
|
o.order = list;
|
|
redo();
|
|
};
|
|
document
|
|
.querySelectorAll("[data-ro-up]")
|
|
.forEach((el) => (el.onclick = () => move(el.getAttribute("data-ro-up"), -1)));
|
|
document
|
|
.querySelectorAll("[data-ro-dn]")
|
|
.forEach((el) => (el.onclick = () => move(el.getAttribute("data-ro-dn"), 1)));
|
|
document.querySelectorAll("[data-ro-reset]").forEach(
|
|
(el) =>
|
|
(el.onclick = () => {
|
|
o.order = null;
|
|
redo();
|
|
}),
|
|
);
|
|
}
|
|
|
|
global.STMATE_REPORT_OPTS = { defaults, state, effective, orderOf, html, bind };
|
|
})(typeof window !== "undefined" ? window : globalThis);
|