Merge remote-tracking branch 'origin/dev' into sub_laptop_5
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
* 일위대가 보기 갈래(39-3 · 고치기 없음) — 그 열 일위대가의 담은 로직 표 + 자재목록 표(기준 · 반영).
|
||||
* 면적산출 상세 오른쪽 패널(sub3) · 구조물 조합 일위대가 카드(sub1)가 씀. 표 모양은 상세와 한 벌(`Labor_Rows`).
|
||||
* · `show(층, 열, 수량?)` = 문서 · 로직 입력 목록 · calc 를 받아 그림(최신 응답만)
|
||||
* · `setQty({알약 키: 칸 값})` = 면적 칸 값이 바뀌면 서버 없이 짝 셈(`Labor_Calc.settle`)으로 바로 곱함
|
||||
* · `setQty({알약 키: 칸 값}, {알약 키: 칸 주소}?)` = 면적 칸 값이 바뀌면 서버 없이 짝 셈(`Labor_Calc.settle`)으로
|
||||
* 바로 곱함 · 칸 주소를 주면 저장 전 연결(setLink · 알약 끌기)도 「수량 B6 = 20」 으로 보임(안 주면 저장본 연결)
|
||||
* (그 열 면적 알약 값 전부를 줌 — 빠진 키는 「수량 없음」)
|
||||
* · `onRow` = 줄 누름(알약 키 · 이름 · 단위 — 칸 지정 모달 등) · 없으면 누름 없음
|
||||
* ========================================================================== */
|
||||
@@ -37,8 +38,9 @@ export interface LaborViewHandle {
|
||||
root: HTMLElement;
|
||||
/** 그 열 일위대가를 읽어 그림 — `수량` = 저장 안 한 면적 칸 값(없으면 저장된 면적산출) */
|
||||
show: (layer: Layer, column: string | null, 수량?: Record<string, number>) => Promise<void>;
|
||||
/** 면적 칸 값이 바뀜(알약 값 전부) — 받은 기준에 바로 곱함(서버 안 부름) */
|
||||
setQty: (수량: Record<string, number>) => void;
|
||||
/** 면적 칸 값이 바뀜(알약 값 전부) — 받은 기준에 바로 곱함(서버 안 부름) ·
|
||||
* `칸` = 지금 시트 연결 전부(알약 키 → 칸 주소 · 저장 전 연결 포함) — 주면 그 뒤로 이것만 씀 */
|
||||
setQty: (수량: Record<string, number>, 칸?: Record<string, string>) => void;
|
||||
/** 지금 문서(없으면 null) */
|
||||
doc: () => LaborDoc | null;
|
||||
destroy: () => void;
|
||||
@@ -51,6 +53,8 @@ export function createLaborView(onRow?: (row: LaborViewRow) => void): LaborViewH
|
||||
let calc: LaborCalc | null = null;
|
||||
let fail: string | null = null;
|
||||
let cells = new Map<string, string>();
|
||||
/** 화면이 준 칸 주소(저장 전 연결) — 있으면 저장본 연결보다 앞섬 */
|
||||
let given: Map<string, string> | null = null;
|
||||
let seq = 0;
|
||||
|
||||
const paint = (): void => {
|
||||
@@ -97,6 +101,7 @@ export function createLaborView(onRow?: (row: LaborViewRow) => void): LaborViewH
|
||||
수량?: Record<string, number>,
|
||||
): Promise<void> => {
|
||||
const mine = ++seq;
|
||||
given = null; // 열이 바뀌면 화면이 다시 줌(refresh)
|
||||
doc = null;
|
||||
calc = null;
|
||||
fail = null;
|
||||
@@ -116,7 +121,7 @@ export function createLaborView(onRow?: (row: LaborViewRow) => void): LaborViewH
|
||||
]);
|
||||
if (mine !== seq) return;
|
||||
for (const l of inputs.로직) logics.set(l.로직, l);
|
||||
cells = new Map([...areaLinks(area)].map(([k, v]) => [k, v.칸]));
|
||||
cells = given ?? new Map([...areaLinks(area)].map(([k, v]) => [k, v.칸]));
|
||||
paint(); // 금액 「…」
|
||||
calc = await calcLabor(layer, column, null, 수량);
|
||||
} catch (error) {
|
||||
@@ -138,9 +143,10 @@ export function createLaborView(onRow?: (row: LaborViewRow) => void): LaborViewH
|
||||
return {
|
||||
root,
|
||||
show,
|
||||
setQty: (수량) => {
|
||||
if (!doc || !calc) return;
|
||||
calc = settle(calc, doc, 수량);
|
||||
setQty: (수량, 칸) => {
|
||||
if (칸) cells = given = new Map(Object.entries(칸));
|
||||
if (!doc) return;
|
||||
if (calc) calc = settle(calc, doc, 수량);
|
||||
paint();
|
||||
},
|
||||
doc: () => doc,
|
||||
|
||||
@@ -57,5 +57,7 @@ def test_실패는_카드_안_글과_다시_불러오기():
|
||||
assert "showToast" not in cards # 토스트 안 씀(저장 알림은 틀)
|
||||
assert "c.shell.fail(" in cards and "reload" in cards
|
||||
picker = (ROOT / "ui_template" / "ui_template_item_picker_detail.ts").read_text("utf-8")
|
||||
assert "inputs.set(null, why(error), () => void paintInputs(row, seq));" in picker
|
||||
assert "const stop = inputs.loading();" in picker
|
||||
# 일위대가 카드 · 값 카드가 다시 불러오기 하나(retry)를 같이 씀(39-1c)
|
||||
assert "const retry = (): void => void paintLabor(row, seq);" in picker
|
||||
assert "inputs.set(null, why(error), retry);" in picker
|
||||
assert "const stops = [labor.loading(), inputs.loading()];" in picker
|
||||
|
||||
@@ -99,3 +99,54 @@ def test_값_셈() -> None:
|
||||
assert got["known"] == {"kept": {"높이": 2}, "lost": ["없앤것"]}
|
||||
assert got["line"] == {"text": "높이 2m · 규격 40", "empty": 2} # 종류 · 지역 = 자동값 없음
|
||||
assert got["lineEmpty"] == {"text": "", "empty": 3} # 높이는 자동값이 있어 빈 값 아님
|
||||
|
||||
|
||||
ROWS_SCRIPT = r"""
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const root = process.argv[1];
|
||||
const ts = require(path.join(root, "config", "node_modules", "typescript"));
|
||||
const stub = new Proxy({}, {
|
||||
get: (_t, k) => (k === "currentLanguageIndex" ? 0 : () => undefined),
|
||||
});
|
||||
function load(f) {
|
||||
const js = ts.transpileModule(fs.readFileSync(f, "utf8"), {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
||||
}).outputText;
|
||||
const box = { exports: {} };
|
||||
const req = (id) => {
|
||||
if (id.startsWith(".") && !id.includes("M01_MasterData")) {
|
||||
const p = path.resolve(path.dirname(f), id);
|
||||
return fs.existsSync(p + ".ts") ? load(p + ".ts") : stub;
|
||||
}
|
||||
return stub;
|
||||
};
|
||||
new Function("exports", "module", "require", js)(box.exports, box, req);
|
||||
return box.exports;
|
||||
}
|
||||
const m = load(path.join(root, "M02_MasterTemplete", "M02_MasterTemplete_Labor_Rows.ts"));
|
||||
const cells = new Map([["돌쌓기|㎡", "B6"]]);
|
||||
console.log(JSON.stringify([
|
||||
m.qtyText({ 수량: 20, 수량출처: "칸", 알약: "돌쌓기|㎡" }, "㎡", cells),
|
||||
m.qtyText({ 수량: 20, 수량출처: "칸", 알약: "돌쌓기|㎡" }, "㎡"),
|
||||
m.qtyText({ 수량: 3, 수량출처: "손", 알약: "돌쌓기|㎡" }, "㎡", cells),
|
||||
m.qtyText({ 수량: null, 수량출처: "없음", 알약: "돌쌓기|㎡" }, "㎡", cells),
|
||||
]));
|
||||
"""
|
||||
|
||||
|
||||
def test_수량_글은_이은_칸_주소를_보임() -> None:
|
||||
"""`Labor_View.setQty(수량, 칸)` 의 칸 주소로 「수량 B6 = 20 ㎡」 — 없으면 「면적산출 칸」."""
|
||||
node = shutil.which("node")
|
||||
if not node:
|
||||
pytest.skip("node 없음")
|
||||
out = subprocess.run(
|
||||
[node, "-e", ROWS_SCRIPT, str(ROOT)], capture_output=True, text=True, encoding="utf-8"
|
||||
)
|
||||
assert out.returncode == 0, out.stderr
|
||||
assert json.loads(out.stdout) == [
|
||||
"수량 B6 = 20 ㎡",
|
||||
"수량 면적산출 칸 = 20 ㎡",
|
||||
"수량 손 입력 3 ㎡",
|
||||
"",
|
||||
]
|
||||
|
||||
@@ -80,6 +80,9 @@ def test_실패_글과_다시_불러오기_M02_자리():
|
||||
edit = _text(M02 / "M02_MasterTemplete_Labor_Edit.ts")
|
||||
assert "loadingError(reason(error), () => void open(key))" in edit
|
||||
assert "(current ? () => {} : showLoading(view))" in edit
|
||||
assert "const stop = editor.loading();" in _text(M02 / "M02_MasterTemplete_Labor.ts")
|
||||
# 좌측 일위대가 조합 나무(Labor.ts)는 39-2 에 지움 — 그 목록 자리는 실무문서 화면(같은 꼴)
|
||||
practice = _text(M02 / "M02_MasterTemplete_Practice.ts")
|
||||
assert "const stop = showLoading(view);" in practice
|
||||
assert "loadingError(why(error), () => void drawList())" in practice
|
||||
assert "cards.map((c) => c.shell.loading())" in _text(M02 / "M02_MasterTemplete_ItemCards.ts")
|
||||
assert "loading: () => showLoading(host)," in _text(M02 / "M02_MasterTemplete_CardShell.ts")
|
||||
|
||||
Reference in New Issue
Block a user