Files
Aislo/tools/est-test/native-excel-files.js
T
eomsangdonandClaude Opus 5.5 3879de8b34 feat(est-test): 엑셀물량 파일목록(추가·삭제·열기), 셀 연결에서 목록 파일 쓰기
- 공사에 엑셀 물량 파일 담기(같은 이름 거부), 삭제, 내려받기
- 셀 수량 연결 창에서 목록 파일 고르기, 새 파일 연결 때 목록 등록

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 19:36:53 +09:00

102 lines
4.9 KiB
JavaScript

// 원본 「엑셀물량 파일목록」(TfrmExcelMullang — spec_pa §7 · spec_small §8.3): 공사에 딸린 엑셀 물량 파일 목록(Index·FileName).
// 원본은 파일을 공사 폴더에 두고, 웹은 작업(project.excelFiles)에 파일 내용을 담는다. 추가(btnNewProject) = 파일을 골라 목록에
// 넣음(원본은 엑셀에 띄우기만 하고 셀 연결 때 등록 — 웹은 엑셀이 없어 여기서 등록) · 같은 이름은 거부. 삭제(btnDelete) = 목록과
// 담은 파일을 지움(원본도 공사 폴더의 파일을 지움) — 그 파일 셀 연결은 남아 다음 갱신 때 못 찾음으로 드러남. 열기 = 내려받기.
// 셀 수량 연결 창은 목록의 파일을 골라 쓸 수 있고, 새 파일로 연결하면 목록에 들어간다(0xc7328c).
const EstExcelFiles = (() => {
const list = (project) => project.excelFiles || [];
function add(project, name, data) {
const files = (project.excelFiles ||= []);
if (files.some((f) => f.FileName === name))
throw Error("같은 이름의 파일이 내역서에 포함되어 있습니다.");
const entry = {
Index: files.reduce((m, f) => Math.max(m, Number(f.Index) || 0), 0) + 1,
FileName: name,
data,
};
files.push(entry);
return entry;
}
function remove(project, name) {
const files = list(project),
at = files.findIndex((f) => f.FileName === name);
if (at < 0) throw Error("삭제할 엑셀파일을 선택하지 않았습니다.");
files.splice(at, 1);
if (!files.length) delete project.excelFiles;
}
const toBase64 = (buffer) => {
const bytes = new Uint8Array(buffer);
let out = "";
for (let i = 0; i < bytes.length; i += 0x8000)
out += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
return btoa(out);
};
const toBuffer = (data) => Uint8Array.from(atob(data), (c) => c.charCodeAt(0)).buffer;
let picked = "";
function dialog(state) {
const { esc } = EstApp,
files = list(state.project),
links = state.project.excelQuantityLinks || [];
EstApp.showDialog(
"엑셀물량 파일목록",
`<button data-action="excel-files-add" title="엑셀파일을 내역서에 추가합니다.">추 가</button> <button data-action="excel-files-delete" title="선택한 엑셀파일을 파일목록에서 제외시킵니다.">삭 제</button> <button data-action="excel-files-open">열 기</button> <span id="excel-files-error" role="alert"></span><table class="sheet" style="margin-top:6px"><thead><tr><th></th><th>파 일 명</th><th>셀 연결</th></tr></thead><tbody>${
files
.map(
(f) =>
`<tr><td><input type="radio" name="excel-file-pick" value="${esc(f.FileName)}" ${f.FileName === picked ? "checked" : ""}></td><td style="text-align:left">${esc(f.FileName)}</td><td>${links.filter((l) => l.file === f.FileName).length}</td></tr>`,
)
.join("") || '<tr><td colspan="3">등록된 엑셀 파일이 없습니다.</td></tr>'
}</tbody></table>`,
);
}
const chosen = () =>
document.querySelector('#dialog-content [name="excel-file-pick"]:checked')?.value || "";
async function run(name, state) {
if (name === "excel-files") return dialog(state);
if (name === "excel-files-add") {
const file = await new Promise((resolve) => {
const picker = document.createElement("input");
picker.type = "file";
picker.accept = ".xls,.xlsx";
picker.addEventListener("change", () => resolve(picker.files[0]));
picker.click();
});
if (!file) return;
const data = toBase64(await file.arrayBuffer());
EstApp.change((project) => add(project, file.name, data), false);
picked = file.name;
return dialog(state);
}
const target = chosen();
if (name === "excel-files-delete") {
if (!target.trim()) throw Error("삭제할 엑셀파일을 선택하지 않았습니다.");
if (!confirm(`${target}를현재작업목록에서 삭제하시겠습니까?`)) return;
EstApp.change((project) => remove(project, target), false);
return dialog(state);
}
if (name === "excel-files-open") {
const file = list(state.project).find((f) => f.FileName === target);
if (!file) throw Error("열 엑셀파일을 선택하세요.");
EstApp.download(file.FileName, new Blob([toBuffer(file.data)]), "application/octet-stream");
}
}
function handle(name, state) {
if (
!["excel-files", "excel-files-add", "excel-files-delete", "excel-files-open"].includes(name)
)
return false;
run(name, state).catch((error) => {
const note = EstApp.byId("excel-files-error");
if (note) note.textContent = error.message;
else {
state.notice = error.message;
EstApp.render();
}
});
return true;
}
return { add, remove, list, toBase64, toBuffer, handle };
})();