// 원본 엑셀형 수량산출서(TFRMMULEXCEL) 웹 편집 화면 — 셀 고르기·수식줄 편집·다시 계산, 셀 합(우·상·좌·하 btnSum*),
// 수량으로 지정(popSetMullang)·지정 해제(popSetFree) = 셀 ↔ 수량 항목 연결(sheet.excelLinks). 항목 수량 = 연결 셀 값(산출서 자리수).
// 원본 파일 속 셀↔항목 대응은 찾지 못해, 가져올 때 값이 같은 셀 하나(합계 수식·아래쪽 우선)를 「추정」 연결로 둔다.
// 편집한 엑셀형 산출서는 .xls 로 다시 쓰지 못하므로 ESTX 저장을 막는다(sheet.excelEdited).
const EstExcelView = (() => {
const L = (n) => EstExcelSheet.letters(n);
function render(sheet, state) {
if (!sheet?.excel?.length) return "";
const { esc, number } = EstApp,
sel = state.excelCell?.sheet === sheet.Code ? state.excelCell : null,
links = sheet.excelLinks || {};
const linked = new Map(
Object.entries(links).map(([code, l]) => [`${l.n},${l.row},${l.col}`, { code, ...l }]),
);
return sheet.excel
.map(({ name, cells }, n) => {
const keys = Object.keys(cells).map((k) => k.split(",").map(Number));
const rows = Math.max(20, ...keys.map(([r]) => r + 2)),
cols = Math.min(40, Math.max(8, ...keys.map(([, c]) => c + 2)));
const raw = sel && sel.n === n ? cells[`${sel.row},${sel.col}`] : null;
const bar =
sel && sel.n === n
? `${L(sel.col)}${sel.row + 1} `
: "셀을 누르면 수식줄에서 고칩니다.";
const items = (sheet.items || [])
.map(
(i) =>
``,
)
.join("");
const tools =
sel && sel.n === n
? ` `
: "";
const body = Array.from({ length: rows }, (_, r) => {
const tds = Array.from({ length: cols }, (_, c) => {
const cell = cells[`${r},${c}`],
link = linked.get(`${n},${r},${c}`),
chosen = sel && sel.n === n && sel.row === r && sel.col === c;
const value =
cell == null
? ""
: typeof cell.value === "number"
? number(cell.value)
: esc(cell.value);
const style = [
cell?.formula ? "font-weight:bold" : "",
link ? "background:#e6f4ea" : "",
chosen ? "outline:2px solid #316ea5" : "",
]
.filter(Boolean)
.join(";");
const title = [
typeof cell?.formula === "string" ? `=${cell.formula}` : "",
link ? `수량: ${link.code}${link.inferred ? " (추정 연결)" : ""}` : "",
]
.filter(Boolean)
.join(" · ");
return `
${value} | `;
}).join("");
return `| ${r + 1} | ${tds}
`;
}).join("");
return `엑셀형 산출서 · ${esc(name)}
${bar}${tools}
| ${Array.from({ length: cols }, (_, c) => `${L(c)} | `).join("")}
${body}
`;
})
.join("");
}
function sheetOf(state) {
return state.project.quantitySheets?.find((s) => s.Code === state.quantitySheet);
}
function setCell(sheet, n, row, col, text) {
const cells = sheet.excel[n].cells,
k = `${row},${col}`,
t = String(text ?? "");
if (t === "") delete cells[k];
else if (t.startsWith("=")) cells[k] = { value: 0, formula: t.slice(1) };
else {
const v = Number(t.replaceAll(",", ""));
cells[k] = { value: t.trim() !== "" && Number.isFinite(v) ? v : t };
}
sheet.excelEdited = true;
return EstExcelSheet.recalc(cells);
}
function install() {
document.addEventListener("click", (event) => {
const td = event.target.closest("[data-excel-pick]");
if (!td) return;
const [n, row, col] = td.dataset.excelPick.split(",").map(Number);
EstApp.state.excelCell = { sheet: EstApp.state.quantitySheet, n, row, col };
EstApp.render();
});
document.addEventListener("change", (event) => {
if (event.target.dataset.excelBar === undefined) return;
const state = EstApp.state,
sel = state.excelCell,
value = event.target.value;
let errors = [];
EstApp.change(() => {
errors = setCell(sheetOf(state), sel.n, sel.row, sel.col, value);
}, false);
if (errors.length) state.notice = errors.slice(0, 3).join(" / ");
});
}
function handle(name, state, node) {
if (!["excel-sum", "excel-link", "excel-unlink"].includes(name)) return false;
const sheet = sheetOf(state),
sel = state.excelCell;
if (!sheet?.excel || !sel) throw Error("엑셀형 산출서에서 셀을 고르세요.");
if (name === "excel-sum") {
const formula = EstExcelSheet.sumFormula(
sheet.excel[sel.n].cells,
sel.row,
sel.col,
node.dataset.dir,
);
EstApp.change(() => setCell(sheet, sel.n, sel.row, sel.col, `=${formula}`), false);
} else {
const code = document.querySelector("[data-excel-item]")?.value;
EstApp.change(() => {
sheet.excelLinks ||= {};
for (const [k, l] of Object.entries(sheet.excelLinks))
if (l.n === sel.n && l.row === sel.row && l.col === sel.col) delete sheet.excelLinks[k];
if (name === "excel-link") {
if (!code) throw Error("수량 항목을 고르세요.");
sheet.excelLinks[code] = { n: sel.n, row: sel.row, col: sel.col };
}
}, false);
}
return true;
}
if (typeof document !== "undefined" && document.addEventListener) install();
return { render, setCell, handle };
})();