diff --git a/M01_MasterData/EasyCalc/M01_EasyCalc_Cards.ts b/M01_MasterData/EasyCalc/M01_EasyCalc_Cards.ts index 666bb13a9..7c9efb7b9 100644 --- a/M01_MasterData/EasyCalc/M01_EasyCalc_Cards.ts +++ b/M01_MasterData/EasyCalc/M01_EasyCalc_Cards.ts @@ -173,13 +173,14 @@ async function repick(ctx: Ctx, card: EasyCard): Promise { if (picking || card.kind === "locked" || card.kind === "percent") return; picking = true; try { - const pick = await openEasyPick(card.kind); + const pick = await openEasyPick(card.kind, { qty: card.qty }); if (!pick) return; const next = updateCard(ctx.doc, card.id, { name: pick.name, spec: pick.spec, unit: pick.unit, element: pick.element, + qty: pick.qty, }); ctx.on.change(withInputs(next, pick.needInputs ?? [])); } finally { diff --git a/M01_MasterData/EasyCalc/M01_EasyCalc_Pick.ts b/M01_MasterData/EasyCalc/M01_EasyCalc_Pick.ts index bc0258535..04d8a375d 100644 --- a/M01_MasterData/EasyCalc/M01_EasyCalc_Pick.ts +++ b/M01_MasterData/EasyCalc/M01_EasyCalc_Pick.ts @@ -15,7 +15,7 @@ import { } from "@ui/ui_template_elements"; import { openModal } from "@ui/ui_template_modal"; import { fetchPick, fetchSubs, type SubInfo } from "../M01_MasterData_Api_Fetch"; -import { fetchMaterials, type ElementBrief } from "../M01_MasterData_UI_Logic_Api"; +import { fetchLogic, fetchMaterials, type ElementBrief } from "../M01_MasterData_UI_Logic_Api"; import { listEasy, listMachines, machineElement, plainError } from "./M01_EasyCalc_Api"; import type { PickResult } from "./M01_EasyCalc_Model"; import "./M01_EasyCalc_Pick.css"; @@ -98,6 +98,23 @@ interface Source { load: (f: Filter) => Promise<{ total: number; items: Candidate[] } | null>; } +/** 찾는 말과 이름이 똑같으면 0 · 그 말로 시작하고 말이 거기서 끝나면(「모래, 서울」 「모래(건조)」) 1 · + * 더 이어지면(「모래적사장」) 2 · 나머지 3 — 앞에 놓음(같은 차례끼리 순서는 그대로) */ +const byName = (items: T[], q: string): T[] => { + const squash = (t: string): string => t.replace(/\s+/g, "").toLowerCase(); + const w = squash(q); + const rank = (it: T): number => { + const n = squash(it.이름 ?? ""); + if (n === w) return 0; + if (!n.startsWith(w)) return 3; + return /[가-힣a-z0-9]/.test(n.charAt(w.length)) ? 2 : 1; + }; + return items + .map((it, i) => ({ it, i, r: rank(it) })) + .sort((a, b) => a.r - b.r || a.i - b.i) + .map((x) => x.it); +}; + const num = (v: unknown): number | null => (typeof v === "number" && Number.isFinite(v) ? v : null); const SOURCES: Partial> = { @@ -137,7 +154,7 @@ const SOURCES: Partial> = { const found = f.q ? await fetchPick("price", f.q).then((r) => ({ total: r.total, - items: r.items + items: byName(r.items, f.q) .filter((it) => !f.sub || (it as Brief).구분 === f.sub) .map((it) => ({ ...it, 구분: (it as Brief).구분 })), })) @@ -217,13 +234,20 @@ const SOURCES: Partial> = { price: null, priceText: "", tag: e.owner, - make: async () => ({ - kind: "logic", - name: e.name, - spec: "", - unit, - element: `로직(${e.key})`, - }), + make: async () => { + // 그 일위대가가 받는 값은 같은 이름으로 넘기고 「설계 때 정하는 값」으로 돌려줌(기계와 같은 꼴) + const needInputs = (await fetchLogic(e.key)).logic.입력; + const quote = (n: string): string => (/^[\w가-힣]+$/.test(n) ? n : `'${n}'`); + const rest = needInputs.map((i) => `, ${quote(i.이름)}=${quote(i.이름)}`).join(""); + return { + kind: "logic", + name: e.name, + spec: "", + unit, + element: `로직(${e.key}${rest})`, + needInputs, + }; + }, }; }), }; @@ -232,7 +256,10 @@ const SOURCES: Partial> = { }; /** 물음 셋 — 고른 것 · 취소는 null */ -export function openEasyPick(kind?: Kind): Promise { +export function openEasyPick( + kind?: Kind, + opts: { qty?: number | null } = {}, +): Promise { return new Promise((resolve) => { let settled = false; const finish = (r: PickResult | null): void => { @@ -339,6 +366,7 @@ export function openEasyPick(kind?: Kind): Promise { placeholder: te("QtyPlace"), }); qty.input.inputMode = "decimal"; + if (opts.qty != null) qty.input.value = String(opts.qty); // 다시 고르기 — 지금 수량 미리 채움 const rough = el("p", { className: "m01-easypick__rough" }); const parse = (): number | null => { const n = Number(qty.input.value.trim().replace(/,/g, ""));