- 공사에 엑셀 물량 파일 담기(같은 이름 거부), 삭제, 내려받기 - 셀 수량 연결 창에서 목록 파일 고르기, 새 파일 연결 때 목록 등록 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
102 lines
4.9 KiB
JavaScript
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 };
|
|
})();
|