168 lines
6.7 KiB
JavaScript
168 lines
6.7 KiB
JavaScript
/* -*- coding: utf-8 -*- */
|
|
/**
|
|
* 그림 코드 화면 (scr_img.js) — TwM_Boxa 그림 넣기(SpeedButton2Click) · 보기(ds_CostIDataChange) · 그림저장
|
|
* 근거: img_code.py 머리 — 그림 = COSTN I 코드 행 IFORM · 꼴 = SIZE(TJPEGIMAGE · TBITMAP · TICON · TMETAFILE)
|
|
* 서버: api_img.py — 넣기는 work_dbf/<이름>/ 에 COSTN.DBF · COSTN.SMT 만 바꿔 씀 → 다시 불러옴
|
|
*/
|
|
(function (global) {
|
|
"use strict";
|
|
const P = (global.STMATE_PLUGINS = global.STMATE_PLUGINS || {
|
|
screens: {},
|
|
actions: {},
|
|
menus: [],
|
|
});
|
|
const esc = (s) =>
|
|
String(s == null ? "" : s).replace(
|
|
/[&<>"]/g,
|
|
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c],
|
|
);
|
|
|
|
async function post(url, body, raw) {
|
|
const res = await fetch(url, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(body),
|
|
});
|
|
if (!res.ok) throw new Error(await res.text());
|
|
return raw ? res : res.json();
|
|
}
|
|
|
|
function pickImage() {
|
|
return new Promise((resolve, reject) => {
|
|
const inp = document.createElement("input");
|
|
inp.type = "file";
|
|
inp.accept = ".bmp,.jpg,.jpeg,.ico,.emf,.wmf";
|
|
inp.onchange = () => {
|
|
const f = inp.files && inp.files[0];
|
|
if (!f) return reject(new Error("파일을 고르지 않음"));
|
|
const rd = new FileReader();
|
|
rd.onload = () => resolve({ b64: String(rd.result), name: f.name });
|
|
rd.onerror = () => reject(rd.error);
|
|
rd.readAsDataURL(f);
|
|
};
|
|
inp.click();
|
|
});
|
|
}
|
|
|
|
function load(ctx, app) {
|
|
const u = ctx.uiState;
|
|
if (u.imgRows || u.imgLoading || !ctx.currentSourcePath) return;
|
|
u.imgLoading = true;
|
|
post("/api/img/list", { source_path: ctx.currentSourcePath })
|
|
.then((r) => ((u.imgRows = r.rows), (u.imgLoading = false), app.renderWorkspace()))
|
|
.catch((e) => ((u.imgRows = []), (u.imgLoading = false), (u.imgErr = e.message)));
|
|
}
|
|
|
|
async function show(ctx, app, code) {
|
|
const u = ctx.uiState;
|
|
if (u.imgUrl) URL.revokeObjectURL(u.imgUrl);
|
|
u.imgUrl = "";
|
|
u.imgSel = code;
|
|
u.imgKind = "";
|
|
try {
|
|
const res = await post("/api/img/get", { source_path: ctx.currentSourcePath, code }, true);
|
|
u.imgKind = res.headers.get("X-Img-Kind") || "";
|
|
u.imgUrl = URL.createObjectURL(await res.blob());
|
|
} catch (e) {
|
|
u.imgKind = "그림 없음";
|
|
}
|
|
app.renderWorkspace();
|
|
}
|
|
|
|
function render(ctx, app) {
|
|
load(ctx, app);
|
|
const u = ctx.uiState;
|
|
const rows = u.imgRows || [];
|
|
const meta = u.imgKind === "TMETAFILE";
|
|
const view = !u.imgSel
|
|
? "<div class='pane-box'>왼쪽에서 그림 코드를 고르세요.</div>"
|
|
: u.imgUrl && !meta
|
|
? `<img src="${u.imgUrl}" alt="${esc(u.imgSel)}" style="max-width:100%;max-height:100%;object-fit:contain;">`
|
|
: `<div class='pane-box'>${esc(u.imgSel)} · ${esc(u.imgKind || "읽는 중")}${meta ? " — 메타파일은 브라우저 미리보기 불가 · [파일로 받기]" : ""}</div>`;
|
|
return `
|
|
<div class="win-form">
|
|
<div class="win-form-header"><span>그림 코드. [TwM_Boxa]</span><span>${rows.length}개 그림코드${u.imgErr ? " · " + esc(u.imgErr) : ""}</span></div>
|
|
<div class="win-subbar">
|
|
<button class="win-btn primary" id="img-put">그림 넣기</button>
|
|
<button class="win-btn" id="img-new">새 그림코드</button>
|
|
<button class="win-btn" id="img-down" ${u.imgUrl ? "" : "disabled"}>파일로 받기</button>
|
|
<button class="win-btn" id="img-reload">다시 읽기</button>
|
|
</div>
|
|
<div class="split-horiz">
|
|
<div class="pane-box" style="width:360px;"><div class="grid-scroll"><table class="dbgrid">
|
|
<thead><tr><th style="width:70px">코드</th><th>명칭</th><th style="width:96px">꼴(SIZE)</th><th style="width:70px">크기</th></tr></thead>
|
|
<tbody>${rows.map((r) => `<tr class="img-row ${r.CODE === u.imgSel ? "selected" : ""}" data-c="${esc(r.CODE)}" style="cursor:pointer;"><td>${esc(r.CODE)}</td><td>${esc(r.NAME)}</td><td>${esc(r.SIZE)}</td><td class="right">${r.bytes ? r.bytes.toLocaleString() : ""}</td></tr>`).join("")}</tbody>
|
|
</table></div></div>
|
|
<div class="pane-box" style="flex:1;display:flex;align-items:center;justify-content:center;">${view}</div>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
async function putImage(ctx, app, code) {
|
|
const f = await pickImage();
|
|
let toJpeg = false;
|
|
if (/\.bmp$/i.test(f.name)) {
|
|
const how = prompt(
|
|
`비트맵 파일을 JPeg 포맷으로 변환하여 [${code}] 코드에 저장하겠습니까?\n1: 변환저장 2: 비트맵저장 3: 저장취소`,
|
|
"1",
|
|
);
|
|
if (how !== "1" && how !== "2") return;
|
|
toJpeg = how === "1";
|
|
} else if (!confirm(`[${f.name}] 파일을 [${code}] 코드에 저장하겠습니까?`)) return;
|
|
const r = await post("/api/img/put", {
|
|
source_path: ctx.currentSourcePath,
|
|
code,
|
|
data_b64: f.b64,
|
|
filename: f.name,
|
|
to_jpeg: toJpeg,
|
|
});
|
|
const u = ctx.uiState;
|
|
u.imgRows = null;
|
|
u.imgSel = r.report.code;
|
|
await app.loadProjectFromServer(r.saved_dir, "TwM_Boxa");
|
|
await show(ctx, app, r.report.code);
|
|
}
|
|
|
|
function bind(ctx, app) {
|
|
const u = ctx.uiState;
|
|
const $ = (id) => document.getElementById(id);
|
|
const run = (fn) => async () => {
|
|
try {
|
|
await fn();
|
|
} catch (e) {
|
|
alert("그림 처리 실패: " + e.message);
|
|
}
|
|
};
|
|
document
|
|
.querySelectorAll(".img-row")
|
|
.forEach((tr) => (tr.onclick = run(() => show(ctx, app, tr.dataset.c))));
|
|
$("img-put").onclick = run(async () => {
|
|
if (!u.imgSel) throw new Error("먼저 그림 코드를 고르세요.");
|
|
await putImage(ctx, app, u.imgSel);
|
|
});
|
|
$("img-new").onclick = run(async () => {
|
|
const code = (prompt("새 그림 코드(I 로 시작 · 6자 이내)", "I") || "").trim().toUpperCase();
|
|
if (!code) return;
|
|
if (!confirm(`${code}를 등록 하시겠습니까?`)) return;
|
|
await putImage(ctx, app, code);
|
|
});
|
|
$("img-down").onclick = () => {
|
|
const ext = { TJPEGIMAGE: ".jpg", TBITMAP: ".bmp", TICON: ".ico", TMETAFILE: ".emf" };
|
|
const a = document.createElement("a");
|
|
a.href = u.imgUrl;
|
|
a.download = `${u.imgSel}${ext[u.imgKind] || ".bin"}`;
|
|
a.click();
|
|
};
|
|
$("img-reload").onclick = () => ((u.imgRows = null), app.renderWorkspace());
|
|
}
|
|
|
|
P.screens.TwM_Boxa = { render, bind };
|
|
P.actions.Act_ImgCode = (ctx, app) => {
|
|
ctx.uiState.imgRows = null;
|
|
ctx.uiState.activeScreen = "TwM_Boxa";
|
|
app.renderWorkspace();
|
|
};
|
|
if (!P.menus.some((m) => m.act === "Act_ImgCode"))
|
|
P.menus.push({ menu: "m2", label: "그림 코드 (넣기·보기·저장)", act: "Act_ImgCode" });
|
|
})(window);
|