feat(M02): 50-4 일위대가 줄 누르면 표 아래 상세 · 구분 열
- 서버 calc 답 줄마다 그 로직 재료 · 노무 · 경비 「줄」 실음(버리던 것) - 상세 · 카드 표 줄 누름 = 표 아래 M01 시험계산 표(answerView 다시 씀) + 반영 합 · 한 줄만 · 같은 줄 다시 누름 = 닫힘 - 이름 왼쪽 「구분」 열 — 키 머리에서 읽음(SP- 단가승격 · SC- 일위조합 · 그 밖 단가산출 · 저장 안 함) - 시험: 줄 실림 · 합 = 기준 · ui_checks 50-4 시나리오 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
This commit is contained in:
@@ -203,7 +203,7 @@ export function buildCalc(host: HTMLElement, ctx: CalcContext): void {
|
||||
}
|
||||
|
||||
/** `money` = 결과가 돈(원) — 그때만 결과 값도 정수 · 수량·비율 결과는 소수 그대로 */
|
||||
function answerView(answer: CalcAnswer, draft: boolean, money: boolean): HTMLElement[] {
|
||||
export function answerView(answer: CalcAnswer, draft: boolean, money: boolean): HTMLElement[] {
|
||||
const note = draft ? [el("p", { className: "m01-logic__muted", text: tx("Calc_Draft") })] : [];
|
||||
if (!answer.ok) {
|
||||
return [
|
||||
|
||||
@@ -302,7 +302,10 @@ def calc(doc: dict, given: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||
if "결과" in got: # 돈 아닌 로직 — 반영 없음
|
||||
lines.append({**line, "결과": got["결과"], "기준": None, "까닭": None})
|
||||
continue
|
||||
lines.append({**line, "기준": {k: got[k] for k in (*COSTS, "계")}, "까닭": None})
|
||||
# 「줄」 = 그 로직 재료 · 노무 · 경비 줄(줄 누름 상세 · 50-4)
|
||||
lines.append(
|
||||
{**line, "기준": {k: got[k] for k in (*COSTS, "계")}, "줄": got["줄"], "까닭": None}
|
||||
)
|
||||
mats = [
|
||||
{
|
||||
"코드": one["코드"],
|
||||
|
||||
@@ -130,6 +130,17 @@
|
||||
box-shadow: inset 3px 0 0 var(--color-primary);
|
||||
}
|
||||
|
||||
/* 구분 칸(50-4) — 넉 자 · 흐린 글 */
|
||||
.m02-labrow__kind {
|
||||
white-space: nowrap;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* 줄 상세(50-4) — 표 아래 · 고른 줄만 */
|
||||
.m02-labor__linedet {
|
||||
margin-top: var(--spacing-8);
|
||||
}
|
||||
|
||||
/* 별칭 칸(44-6) — 칸 안 글상자 · 누르면 바로 고침 */
|
||||
.m02-labrow__alias {
|
||||
min-width: 7rem;
|
||||
@@ -259,6 +270,12 @@
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.m02-labrow .m02-labrow__kind {
|
||||
grid-column: 1 / -1;
|
||||
order: 1;
|
||||
font-size: var(--text-caption);
|
||||
}
|
||||
|
||||
.m02-labrow .m02-labrow__no,
|
||||
.m02-labrow .m02-labrow__unit:not([data-label]),
|
||||
.m02-labrow .m02-labrow__code,
|
||||
@@ -322,6 +339,7 @@
|
||||
|
||||
/* 옵션 값 줄 — 한 줄 통째 */
|
||||
.m02-labrow__optrow .m02-labrow__no,
|
||||
.m02-labrow__sum .m02-labrow__kind,
|
||||
.m02-labrow__sum .m02-labrow__alias {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
* 기준(결과단위 그대로)은 서버 calc 길이 셈 · 여기는 받은 기준에
|
||||
* 수량(손이 칸보다 앞섬 · 칸 = 알약 키 값) · 반영 = 기준 × 수량 ÷ 배 · 자재 = 재료비만. */
|
||||
|
||||
import type { CalcLine as LogicLine } from "../M01_MasterData/M01_MasterData_UI_Logic_Api";
|
||||
|
||||
export const MONEY_KEYS = ["노무비", "재료비", "경비", "계"] as const;
|
||||
export type Money = Record<(typeof MONEY_KEYS)[number], number>;
|
||||
export type QtySource = "손" | "칸" | "없음";
|
||||
@@ -22,6 +24,8 @@ export interface CalcLine {
|
||||
반영?: Money | null;
|
||||
까닭?: string | null;
|
||||
결과?: unknown;
|
||||
/** 그 로직 재료 · 노무 · 경비 줄(M01 시험계산 표와 같은 모양 · 50-4) */
|
||||
줄?: LogicLine[];
|
||||
}
|
||||
|
||||
export interface CalcMaterial {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
* M02_MasterTemplete_Labor_Detail.ts
|
||||
* 일위대가 상세(39-3) — 구조물 열마다 하나(`labor/<열>.json`) · UI_Main 종류 `labor` 가 이 부품을 붙임
|
||||
* (양식 길 읽기 · [저장] · [다시 읽기]는 UI_Main). 차례 = 정보(단위 · 본뜬) → 담은 로직 → 자재목록 → 다이어그램.
|
||||
* · 담은 로직 줄 누름 = 고름(하이라이트 · 다시 그려도 유지 · 47-5) · [옵션] = 입력 모달(`Labor_Input`) ·
|
||||
* · 담은 로직 줄 누름 = 고름(하이라이트 · 다시 그려도 유지 · 47-5) + 표 아래 그 줄 상세(50-4 · M01 시험계산 표) ·
|
||||
* 같은 줄 다시 누르면 닫힘 · [옵션] = 입력 모달(`Labor_Input`) ·
|
||||
* [확인] = 화면 캐시만 · 저장은 [저장]
|
||||
* · 금액 = 서버 calc(기준 ③ · 최신 응답만 · 저장 안 함) · 고칠 때마다 뒤에서 다시 셈
|
||||
* · 같은 로직 두 번 · 같은 자재 두 번 막음 · 빈 일위대가도 저장 됨
|
||||
@@ -15,6 +16,9 @@ import { ROUTES } from "@config/config_frontend";
|
||||
import { navigateTo } from "../A00_Common/router";
|
||||
import { setPendingLogicKey } from "../M01_MasterData/M01_MasterData_UI_Side";
|
||||
import { createSourceView } from "../M01_MasterData/M01_MasterData_UI_LogicLab_Source";
|
||||
import { answerView } from "../M01_MasterData/M01_MasterData_UI_Logic_Calc";
|
||||
import type { CalcAnswer } from "../M01_MasterData/M01_MasterData_UI_Logic_Api";
|
||||
import { formatMoney } from "../M01_MasterData/M01_MasterData_UI_Logic_Money";
|
||||
import type { Workbook } from "../A00_Common/spreadsheet/spreadsheet";
|
||||
import { recalcWorkbook } from "../A00_Common/spreadsheet/spreadsheet_recalc";
|
||||
import {
|
||||
@@ -24,7 +28,7 @@ import {
|
||||
type Layer,
|
||||
} from "./M02_MasterTemplete_Api_Fetch";
|
||||
import { linkAreaCell } from "./M02_MasterTemplete_AreaCellPick";
|
||||
import { settle } from "./M02_MasterTemplete_Labor_Calc";
|
||||
import { MONEY_KEYS, settle } from "./M02_MasterTemplete_Labor_Calc";
|
||||
import {
|
||||
areaLinks,
|
||||
calcLabor,
|
||||
@@ -134,6 +138,46 @@ export function mountLaborDetail(
|
||||
const lineOf = (key: string) => calc?.줄.find((l) => l.로직 === key);
|
||||
const matOf = (code: string) => calc?.자재.find((m) => m.코드 === code);
|
||||
|
||||
/** 고른 줄 상세(50-4) — M01 시험계산 표(재료 · 노무 · 경비 줄 + 기준 합) + 반영 합 · 고른 줄 없으면 비움 */
|
||||
const lineDet = el("div", {
|
||||
className: "m02-labor__linedet",
|
||||
attrs: { "data-labor-linedet": "" },
|
||||
});
|
||||
function showLine(): void {
|
||||
if (!picked) return void lineDet.replaceChildren();
|
||||
const logic = logics.get(picked);
|
||||
const head = el("h4", {
|
||||
text: tc("Labor_LineDetail").replace("{value}", `${picked} ${logic?.이름 ?? ""}`.trim()),
|
||||
});
|
||||
const line = lineOf(picked);
|
||||
if (!line)
|
||||
return void lineDet.replaceChildren(
|
||||
head,
|
||||
el("p", { className: "m01-logic__muted", text: fail ?? "…" }),
|
||||
);
|
||||
const answer: CalcAnswer = line.까닭
|
||||
? { ok: false, reason: line.까닭 }
|
||||
: { ok: true, lines: line.줄, sums: line.기준 ?? undefined, result: line.결과, middle: {} };
|
||||
// 반영 = 기준 × 수량 ÷ 배(표 아래 줄과 같음) · 수량 없으면 「—」 · 돈 아닌 로직은 없음
|
||||
const applied = line.기준
|
||||
? [
|
||||
el("h4", { text: tc("Labor_LineApplied") }),
|
||||
el("dl", {
|
||||
className: "m01-logic__sums",
|
||||
children: MONEY_KEYS.flatMap((k) => [
|
||||
el("dt", { text: k === "계" ? tc("Prev_Sum") : k }),
|
||||
el("dd", {
|
||||
className: "m01-logic__money",
|
||||
text: line.반영 ? formatMoney(line.반영[k]) : "—",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]
|
||||
: [];
|
||||
const money = (logic?.결과단위 ?? "").startsWith("원");
|
||||
lineDet.replaceChildren(head, ...answerView(answer, false, money), ...applied);
|
||||
}
|
||||
|
||||
const drawDg = (): void => {
|
||||
if (opt.card || !dgBox.open) return; // 카드 갈래엔 다이어그램 없음
|
||||
const data = buildLaborDiagram({
|
||||
@@ -172,7 +216,9 @@ export function mountLaborDetail(
|
||||
}
|
||||
: { open: pick, option: openRow },
|
||||
),
|
||||
lineDet,
|
||||
);
|
||||
showLine();
|
||||
matHost.replaceChildren(
|
||||
matTable(
|
||||
{ mats: cur.자재, calc, fail, cells: cells() },
|
||||
@@ -293,11 +339,14 @@ export function mountLaborDetail(
|
||||
[cur.담은로직[i], cur.담은로직[j]] = [cur.담은로직[j], cur.담은로직[i]];
|
||||
changed();
|
||||
}
|
||||
/** 줄 고름(47-5) — 하이라이트(표만 · 다이어그램 안 다시 그림) + 원문 패널(47-4) · 화면 캐시 안 바뀜 */
|
||||
/** 줄 고름(47-5) — 하이라이트(표만 · 다이어그램 안 다시 그림) + 줄 상세(50-4) + 원문 패널(47-4) ·
|
||||
* 고른 줄 다시 누름 = 닫음 · 화면 캐시 안 바뀜 */
|
||||
function pick(i: number): void {
|
||||
picked = cur.담은로직[i].로직;
|
||||
const key = cur.담은로직[i].로직;
|
||||
picked = picked === key ? null : key;
|
||||
for (const tr of logicHost.querySelectorAll<HTMLElement>("[data-labor-logic],[data-labor-opt]"))
|
||||
tr.classList.toggle("is-selected", (tr.dataset.laborLogic ?? tr.dataset.laborOpt) === picked);
|
||||
showLine();
|
||||
showSource();
|
||||
}
|
||||
function removeRow(i: number): void {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* =============================================================================
|
||||
* M02_MasterTemplete_Labor_Rows.ts
|
||||
* 일위대가 담은 로직 표 · 자재목록 표(39-3) — 상세 · 보기 갈래(면적산출 오른쪽 패널 · 카드)가 같이 씀.
|
||||
* 담은 로직 = 차례 · 로직(키 · 이름 + 「빈 값 n」 붉게) · 별칭(44-6) · 수량산출(「12.5(B6)」) ·
|
||||
* 담은 로직 = 차례 · 구분(키 머리에서 읽음 · 50-4) · 로직(키 · 이름 + 「빈 값 n」 붉게) · 별칭(44-6) · 수량산출(「12.5(B6)」) ·
|
||||
* 결과 단위 · 금액 칸 넷(노무비 · 재료비 · 경비 · 합계 · 칸마다 위 기준 작게 / 아래 반영) ·
|
||||
* 옵션 값(「지역 서울 · 규격 25mm」)은 줄 아래 이름 ~ 합계에 걸친 한 줄 · 맨 아래 합계 줄(전체 합계 강조).
|
||||
* 자재목록 = 코드 · 이름 · 규격 · 수량산출 · 단위 · 가격 다섯(기준 칸 강조) · 기준단가 / 반영 금액(43-3).
|
||||
@@ -34,8 +34,12 @@ const HEAD: Record<(typeof COSTS)[number], Parameters<typeof tc>[0]> = {
|
||||
계: "Prev_Sum",
|
||||
};
|
||||
|
||||
/** 옵션 값 줄이 걸치는 칸 수 — 이름 · 별칭 · 수량산출 · 결과 단위 · 금액 넷 */
|
||||
const SPAN = 4 + COSTS.length;
|
||||
/** 옵션 값 줄이 걸치는 칸 수 — 구분 · 이름 · 별칭 · 수량산출 · 결과 단위 · 금액 넷 */
|
||||
const SPAN = 5 + COSTS.length;
|
||||
|
||||
/** 구분(50-4 · 저장 안 함) — 줄 모양(키 머리)에서 읽음 · SP- = 단가승격 · SC- = 일위조합(50-3) */
|
||||
export const kindOf = (key: string): string =>
|
||||
tc(key.startsWith("SP-") ? "Kind_Promote" : key.startsWith("SC-") ? "Kind_Combo" : "Kind_Logic");
|
||||
|
||||
const num = (v: number): string =>
|
||||
Number(v.toPrecision(12)).toLocaleString("ko-KR", { maximumFractionDigits: 6 });
|
||||
@@ -253,6 +257,7 @@ export function logicTable(data: LogicTableData, on?: RowHandlers): HTMLElement
|
||||
attrs: { "data-labor-logic": row.로직 },
|
||||
children: [
|
||||
el("td", { className: "m02-labrow__no", text: String(i + 1) }),
|
||||
el("td", { className: "m02-labrow__kind", text: kindOf(row.로직) }),
|
||||
titled(
|
||||
el("td", {
|
||||
className: "m02-labrow__name",
|
||||
@@ -331,6 +336,7 @@ export function logicTable(data: LogicTableData, on?: RowHandlers): HTMLElement
|
||||
className: "m02-labrow__sum",
|
||||
children: [
|
||||
el("td", { className: "m02-labrow__no", text: "" }),
|
||||
el("td", { className: "m02-labrow__kind", text: "" }),
|
||||
el("td", { className: "m02-labrow__name", text: tc("Prev_Sum") }),
|
||||
el("td", { className: "m02-labrow__alias", text: "" }),
|
||||
el("td", { className: "m02-labrow__qty", text: "" }),
|
||||
@@ -352,6 +358,7 @@ export function logicTable(data: LogicTableData, on?: RowHandlers): HTMLElement
|
||||
: [];
|
||||
const heads = [
|
||||
tc("Col_Order"),
|
||||
tc("Sub"),
|
||||
tc("Col_Name"),
|
||||
tc("Col_Alias"),
|
||||
tc("Labor_QtyCol"),
|
||||
|
||||
@@ -84,6 +84,12 @@ const TEXT = {
|
||||
Labor_BasePrice: ["기준 단가", "Base price"],
|
||||
Labor_AppliedSum: ["반영 금액", "Applied amount"],
|
||||
Labor_Calc_Fail: ["계산 못 함", "Calculation failed"],
|
||||
// 구분 · 줄 상세(50-4)
|
||||
Kind_Logic: ["단가산출", "Logic"],
|
||||
Kind_Promote: ["단가승격", "Promoted"],
|
||||
Kind_Combo: ["일위조합", "Combination"],
|
||||
Labor_LineDetail: ["상세 — {value}", "Detail — {value}"],
|
||||
Labor_LineApplied: ["반영(수량 곱함)", "Applied (× quantity)"],
|
||||
Labor_Empty: ["빈 값 {n}", "{n} empty"],
|
||||
Labor_Lost: [
|
||||
"로직 입력이 바뀌어 저장 때 빠지는 값 — {value}",
|
||||
|
||||
@@ -90,6 +90,14 @@ def test_반영은_기준_곱_수량_나누기_배_돈_아닌_로직은_반영_
|
||||
assert got["반영합"]["계"] == pytest.approx(ten["반영"]["계"])
|
||||
|
||||
|
||||
def test_돈_로직_줄에_재료_노무_경비_줄_실림_합은_기준():
|
||||
got = labor.calc(labor.clean(_doc(담은로직=[{"로직": TEN}, {"로직": PLAIN}])), {})
|
||||
ten, plain = got["줄"]
|
||||
assert ten["줄"] and all({"이름", "수량", "단위", "금액"} <= set(x) for x in ten["줄"])
|
||||
assert sum(x["금액"] for x in ten["줄"]) == pytest.approx(ten["기준"]["계"])
|
||||
assert "줄" not in plain
|
||||
|
||||
|
||||
def test_수량_손이_칸보다_앞섬_칸_값은_알약_키로():
|
||||
doc = labor.clean(
|
||||
_doc(
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/* 50-4 — 일위대가 단가산출 로직 표 줄 누름 = 표 아래 그 줄 상세(M01 시험계산 표) · 다시 누르면 닫힘 · 「구분」 열 — 카드 · 상세 두 곳
|
||||
* 시작 화면: M02 마스터 템플릿 · 마스터 층(#/m02-master-template?open=구조물조합&root=master) · 시스템 관리자 ·
|
||||
* 나무에 item1(구조물 조합 · 일위대가 로직 둘 이상)이 있음
|
||||
* 부르는 법: orca eval --expression "$(tr '\n' ' ' < 이 파일)" (async 식 · JSON 글을 돌려줌)
|
||||
* 하는 일: item1 카드 표 줄 누름 · 다른 줄 · 같은 줄 다시 → 일위대가 카드 [수정]으로 상세 → 같은 차례 → item1 다시(값 안 바뀜)
|
||||
* 기대 수치(카드 · 상세 같음):
|
||||
* - head[1] = "구분" · kinds = 줄마다 "단가산출"
|
||||
* - first.open = 1(상세 상자 하나) · first.rows ≥ 1 · first.sum = first.total = first.base(줄 합 = 상세 계 = 표 기준 합계)
|
||||
* - first.applied = first.appliedRow(상세 반영 계 = 표 반영 합계 · 수량 없으면 둘 다 「—」 = 0)
|
||||
* - second.title 가 둘째 줄 키 · second.open = 1(한 줄만)
|
||||
* - again.open = 0 · again.selected = 0(같은 줄 다시 = 닫힘)
|
||||
* - back = true(카드 화면으로 돌아감)
|
||||
*/
|
||||
(async () => {
|
||||
const w = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
const until = async (fn, ms = 8000) => {
|
||||
for (let t = 0; t < ms; t += 150) {
|
||||
const v = fn();
|
||||
if (v) return v;
|
||||
await w(150);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const n = (s) => Number(String(s ?? "").replace(/[^0-9.-]/g, "")) || 0;
|
||||
const rowsOf = (t) => [...t.querySelectorAll("tbody tr[data-labor-logic]")];
|
||||
const settled = (get) =>
|
||||
until(() => {
|
||||
const t = get();
|
||||
return t && !/…/.test(t.querySelector("tbody").innerText) ? t : null;
|
||||
}, 10000);
|
||||
/* 상세 상자 읽기 — 표 줄 금액 합 · 기준 계 · 반영 계 */
|
||||
const det = (box) => {
|
||||
const dl = [...box.querySelectorAll("dl.m01-logic__sums")];
|
||||
const total = (d) => {
|
||||
const dt = d && [...d.querySelectorAll("dt")].find((x) => /계|합계|Total/.test(x.innerText));
|
||||
return dt ? n(dt.nextElementSibling.innerText) : null;
|
||||
};
|
||||
const rows = [...box.querySelectorAll("table.m01-logic__grid tbody tr")];
|
||||
return {
|
||||
title: box.querySelector("h4")?.innerText ?? null,
|
||||
rows: rows.length,
|
||||
sum: rows.reduce((a, r) => a + n(r.lastElementChild.innerText), 0),
|
||||
total: total(dl[0]),
|
||||
applied: dl[1] ? total(dl[1]) : "없음",
|
||||
};
|
||||
};
|
||||
const measure = async (get, boxOf) => {
|
||||
const t = await settled(get);
|
||||
const rows = rowsOf(t);
|
||||
const head = [...t.querySelectorAll("thead th")].map((th) => th.innerText.trim());
|
||||
const kinds = rows.map((r) => r.querySelector(".m02-labrow__kind")?.innerText.trim());
|
||||
const click = async (key) => {
|
||||
get().querySelector(`tr[data-labor-logic="${key}"] .m02-labrow__name`).click();
|
||||
await w(300);
|
||||
};
|
||||
const opened = () =>
|
||||
[...boxOf().querySelectorAll("[data-labor-linedet]")].filter((b) => b.childElementCount);
|
||||
const k0 = rows[0].getAttribute("data-labor-logic");
|
||||
const k1 = rows[1]?.getAttribute("data-labor-logic") ?? k0;
|
||||
const sumCell = (key) => {
|
||||
const tds = get().querySelectorAll(`tr[data-labor-logic="${key}"] .m02-labrow__money`);
|
||||
const last = tds[tds.length - 1];
|
||||
return {
|
||||
base: n(last.querySelector(".m02-labrow__base").innerText),
|
||||
applied: n(last.querySelector(".m02-labrow__applied").innerText),
|
||||
};
|
||||
};
|
||||
await click(k0);
|
||||
const d0 = det(opened()[0]);
|
||||
const row0 = sumCell(k0);
|
||||
const first = {
|
||||
key: k0,
|
||||
open: opened().length,
|
||||
...d0,
|
||||
base: row0.base,
|
||||
appliedRow: row0.applied,
|
||||
};
|
||||
await click(k1);
|
||||
const second = {
|
||||
key: k1,
|
||||
open: opened().length,
|
||||
title: opened()[0]?.querySelector("h4")?.innerText,
|
||||
};
|
||||
await click(k1);
|
||||
const again = {
|
||||
open: opened().length,
|
||||
selected: rowsOf(get()).filter((r) => r.classList.contains("is-selected")).length,
|
||||
};
|
||||
return { head, kinds, first, second, again };
|
||||
};
|
||||
const node = await until(() => document.querySelector('[data-node="i|item1"]'));
|
||||
if (!node) return JSON.stringify({ error: "item1 없음 — 폴더를 펼쳐 둘 것" });
|
||||
node.click();
|
||||
const card = await until(() =>
|
||||
[...document.querySelectorAll(".m02-struct__card")].find((c) =>
|
||||
c.querySelector("table.m02-labrow tbody tr[data-labor-logic]"),
|
||||
),
|
||||
);
|
||||
if (!card) return JSON.stringify({ error: "일위대가 카드 표 없음" });
|
||||
await w(1500);
|
||||
const out = {
|
||||
card: await measure(
|
||||
() => card.querySelector("table.m02-labrow"),
|
||||
() => card,
|
||||
),
|
||||
};
|
||||
const edit = [...card.querySelectorAll(".m02-struct__card-tools button")].find(
|
||||
(b) => b.innerText.trim() === "수정",
|
||||
);
|
||||
if (!edit) return JSON.stringify({ ...out, error: "일위대가 [수정] 없음" });
|
||||
edit.click();
|
||||
const dt = await until(() =>
|
||||
[...document.querySelectorAll("table.m02-labrow")].find(
|
||||
(t) => !t.closest(".m02-struct__card") && t.querySelector("tbody tr[data-labor-logic]"),
|
||||
),
|
||||
);
|
||||
if (!dt) return JSON.stringify({ ...out, error: "상세 표 없음" });
|
||||
await w(2000);
|
||||
const table = () =>
|
||||
[...document.querySelectorAll("table.m02-labrow")].find((t) => !t.closest(".m02-struct__card"));
|
||||
out.detail = await measure(table, () => table().closest("[data-labor]"));
|
||||
/* 카드 화면으로 돌아감(고름만 했으니 버림 물음 없음 — 뜨면 확인) */
|
||||
document.querySelector('[data-node="i|item1"]')?.click();
|
||||
await w(800);
|
||||
[...document.querySelectorAll(".ui-modal button, dialog button")]
|
||||
.find((b) => /확인|버림|떠나기/.test(b.innerText))
|
||||
?.click();
|
||||
await w(1200);
|
||||
out.back = !!document.querySelector(".m02-struct__card table.m02-labrow");
|
||||
return JSON.stringify(out);
|
||||
})();
|
||||
Reference in New Issue
Block a user