Merge remote-tracking branch 'origin/dev' into sub_laptop_1

This commit is contained in:
2026-10-01 12:58:46 +09:00
5 changed files with 116 additions and 13 deletions
+9 -9
View File
@@ -12,7 +12,7 @@
* ========================================================================== */
import type { CalcAnswer, LogicRow } from "../M01_MasterData_UI_Logic_Api";
import { lbCalcLater, type LbTables } from "./M01_LB_Api";
import { lbCalcNow, type LbTables } from "./M01_LB_Api";
import { writeLogic, type LbLogic, type LbLookup } from "./M01_LB_Blocks";
type Cell = string | number | boolean | null | [number | null, number | null];
@@ -212,20 +212,20 @@ type CalcFn = (
tables: LbTables,
) => Promise<CalcAnswer>;
/** LB Api 계산을 한 번 기다림 — 차례로 부르므로 「최신만」 에 안 걸림 */
const calcOnce: CalcFn = (row, inputs, tables) =>
new Promise((resolve, reject) =>
lbCalcLater(row, inputs, tables, (ans, plain) =>
ans ? resolve(ans) : reject(new Error(plain ?? "")),
),
);
/** LB Api 바로 계산 — 화면 예시값 계산(0.3초 · 최신만)과 안 부딪힘 · 멈춤 까닭은 쉬운 말로 */
const calcNow: CalcFn = async (row, inputs, tables) => {
let plain = "";
const ans = await lbCalcNow(row, inputs, tables, (p) => (plain = p));
if (!ans) throw new Error(plain);
return ans.ok ? ans : { ok: false, reason: plain || ans.reason };
};
/** 줄마다 시험 계산(차례로 · 하나씩) — 로직은 블록을 글로 되돌린 줄 · `calc` 는 시험에서 바꿔 끼움 */
export async function runExpect(
lb: LbLogic,
tables: LbTables,
rows: ExpectRow[],
calc: CalcFn = calcOnce,
calc: CalcFn = calcNow,
): Promise<ExpectResult[]> {
const row = writeLogic(lb);
const out: ExpectResult[] = [];
+23 -1
View File
@@ -6,7 +6,12 @@
* ========================================================================== */
import { createButton, el, showToast } from "@ui/ui_template_elements";
import type { CalcAnswer, CalcLine, LogicInput } from "../M01_MasterData_UI_Logic_Api";
import {
fetchLogics,
type CalcAnswer,
type CalcLine,
type LogicInput,
} from "../M01_MasterData_UI_Logic_Api";
import { formatMoney } from "../M01_MasterData_UI_Logic_Money";
import {
addCard,
@@ -23,6 +28,7 @@ import { setCond, type LbLogic, type LbLookup, type LbRef } from "./M01_LB_Block
import { calcBody, calcView } from "./M01_LB_Steps_Calc";
import {
chip,
logicNames,
lookupValue,
note,
numBox,
@@ -42,6 +48,7 @@ import "./M01_LB_Steps.css";
type Kind = "person" | "material" | "machine" | "logic" | "percent" | "locked";
let picking = false;
let namesLoading = false;
/* ------------------------------ ① 입력값 ------------------------------ */
@@ -367,4 +374,19 @@ export function renderSteps(
),
section(tx("S5"), [el("li", { className: "m01-lbsteps-row", children: [resultView(ctx)] })]),
);
// 다른 일위대가 이름 — 목록을 한 번 받은 뒤 다시 그림
if (
!logicNames.size &&
!namesLoading &&
JSON.stringify([lb.calcs, lb.lookups]).includes('"k":"logic"')
) {
namesLoading = true;
fetchLogics()
.then((all) => {
for (const l of all) logicNames.set(l.키, l.이름);
renderSteps(host, lb, ans, on);
})
.catch(() => undefined)
.finally(() => (namesLoading = false));
}
}
@@ -33,7 +33,7 @@ export const TEXT = {
SrcCalc: ["계산 · ", "calc · "],
SrcFixed: ["고정값", "fixed value"],
SrcAny: ["무관(조건에서 뺌)", "any (remove condition)"],
Other: ["다른 일위대가 결과", "Other unit price result"],
Other: ["다른 일위대가", "Other unit price"],
Hard: ["이 화면에서 못 고치는 값", "Value not editable here"],
Mul: ["×", "×"],
Div: ["÷", "÷"],
@@ -197,6 +197,9 @@ export const isRef = (r: Ref, k: "lookup", id: string): boolean => r.k === k &&
export const lookupNo = (ctx: Ctx, id: string): number =>
ctx.lb.lookups.findIndex((l) => l.id === id) + 1;
/** 로직 이름 — 목록을 한 번 받아 채움(키 → 이름) · 못 받으면 「다른 일위대가」 */
export const logicNames = new Map<string, string>();
export function refText(ctx: Ctx, r: Ref): string {
switch (r.k) {
case "input":
@@ -210,7 +213,7 @@ export function refText(ctx: Ctx, r: Ref): string {
return `${tx("LkNum")}${lookupNo(ctx, r.id)}${lk ? ` · ${lk.col}` : ""}`;
}
case "logic":
return `${tx("Other")} ${r.key}${r.field ? ` · ${r.field}` : ""}`;
return `${logicNames.get(r.key) ?? tx("Other")}${r.field ? ` · ${r.field}` : ""}`;
case "cost":
return tx(`Base_${r.name}`);
default:
+4 -1
View File
@@ -1,5 +1,5 @@
{
"다음": {"LB": 409, "EQ": 655, "RT": 20, "MN": 7000, "MT": 30719, "MO": 74, "MP": 364, "FX": 6, "CF": 159, "QF": 477, "GF": 331, "CC": 341, "GC": 1140, "QC": 2060, "GX": 1, "UA": 38},
"다음": {"LB": 409, "EQ": 655, "RT": 20, "MN": 7000, "MT": 30719, "MO": 74, "MP": 364, "FX": 6, "CF": 159, "QF": 477, "GF": 331, "CC": 341, "GC": 1140, "QC": 2060, "GX": 4, "UA": 38},
"폐기": {"MT000001": "자재품목 휘발유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000002": "자재품목 경유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000003": "자재품목 선박용경유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000004": "자재품목 중유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000005": "자재품목 등유 — 값이 비고 글뿐 · 유가 테이블로 감", "LB000399": "이름 「〃」 — 직종이 아니라 되풀이 기호 · 소요량 13-6-9 에서 윗줄 직종으로 바로잡음", "MN": "재료_나라장터자재 — old 로 옮김 · 로직은 자재품목·품셈재료로 갈아 끼움", "LB000372": "미확보 고급기능사 — 측량노임 항공사진고급기능사 옛이름으로 옮김", "LB000393": "미확보 리베팅공 — 건설노임 철골공 옛이름으로 옮김(2010 통합표 연번10)", "LB000405": "미확보 Belt Conveyor 설치공 — 품셈 13-10-1 주② 배분으로 다섯 직종에 나눔", "MT000006": "자재품목 전력 — 재료_유가전력 전력 줄로 감", "MT000007": "자재품목 전력 — 재료_유가전력 전력 줄로 감", "LB000368": "미확보 기술사 — 기술사(건설) 옛이름으로 옮김", "LB000369": "미확보 특급기술자 — 특급기술자(건설) 옛이름으로 옮김", "LB000370": "미확보 고급기술자 — 고급기술자(건설) 옛이름으로 옮김", "LB000361": "미확보 중급기술자 — 중급기술자(건설) 옛이름으로 옮김", "LB000364": "미확보 초급기술자 — 초급기술자(건설) 옛이름으로 옮김", "LB000365": "미확보 고급숙련기술자 — 고급숙련기술자(건설) 옛이름으로 옮김", "LB000366": "미확보 중급숙련기술자 — 중급숙련기술자(건설) 옛이름으로 옮김", "LB000367": "미확보 초급숙련기술자 — 초급숙련기술자(건설) 옛이름으로 옮김", "LB000389": "미확보 기계기사 — 초급기술자(기계·설비) 옛이름으로 옮김", "LB000388": "미확보 기계산업기사 — 초급숙련기술자(기계·설비) 옛이름으로 옮김", "MO000055": {"원문번호": "주택용저압:기본_200이하", "파일": "재료_유가전력.json"}, "MO000056": {"원문번호": "주택용저압:기본_201~400", "파일": "재료_유가전력.json"}, "MO000057": {"원문번호": "주택용저압:기본_400초과", "파일": "재료_유가전력.json"}, "MO000059": {"원문번호": "주택용저압:전력량_다음200", "파일": "재료_유가전력.json"}, "MO000060": {"원문번호": "주택용저압:전력량_400초과", "파일": "재료_유가전력.json"}, "MO000061": {"원문번호": "주택용저압:월간최저요금", "파일": "재료_유가전력.json"}, "MO000062": {"원문번호": "일반용갑I저압:기본", "파일": "재료_유가전력.json"}, "MO000063": {"원문번호": "일반용갑I저압:전력량_여름철", "파일": "재료_유가전력.json"}, "MO000064": {"원문번호": "일반용갑I저압:전력량_봄가을철", "파일": "재료_유가전력.json"}, "MO000065": {"원문번호": "일반용갑I저압:전력량_겨울철", "파일": "재료_유가전력.json"}, "MO000066": {"원문번호": "산업용갑I저압:기본", "파일": "재료_유가전력.json"}, "MO000067": {"원문번호": "산업용갑I저압:전력량_여름철", "파일": "재료_유가전력.json"}, "MO000068": {"원문번호": "산업용갑I저압:전력량_봄가을철", "파일": "재료_유가전력.json"}, "MO000069": {"원문번호": "산업용갑I저압:전력량_겨울철", "파일": "재료_유가전력.json"}, "MO000070": {"원문번호": "임시전력갑:기본요금적용", "파일": "재료_유가전력.json"}, "MO000071": {"원문번호": "임시전력갑:전력량요금적용", "파일": "재료_유가전력.json"}, "MO000072": {"원문번호": "임시전력갑:월간최저요금", "파일": "재료_유가전력.json"}},
"키": {
"CC000001": {"원문번호": "공통 1-3-1 콘크리트 및 포장용 재료", "파일": "계수_건설품셈_01장_적용기준.json"},
@@ -2628,6 +2628,9 @@
"GF000328": {"원문번호": "13-11-1 원형 돌망태", "파일": "로직_산림품셈_13장_구조물.json"},
"GF000329": {"원문번호": "13-11-2 타원형 돌망태", "파일": "로직_산림품셈_13장_구조물.json"},
"GF000330": {"원문번호": "13-15-1 목재 껍질벗기기", "파일": "로직_산림품셈_13장_구조물.json"},
"GX000001": {"원문번호": "13-4-1 돌쌓기 메쌓기(인력)", "파일": "로직_자체.json"},
"GX000002": {"원문번호": "12-34-4 암거이음받침 채움재(T=20㎜)", "파일": "로직_자체.json"},
"GX000003": {"원문번호": "13-4-1 돌쌓기 메쌓기(인력) (2)", "파일": "로직_자체.json"},
"LB000001": {"원문번호": "1001", "파일": "인력.json"},
"LB000002": {"원문번호": "1002", "파일": "인력.json"},
"LB000003": {"원문번호": "1003", "파일": "인력.json"},
+75
View File
@@ -0,0 +1,75 @@
{
"그룹": "로직",
"원문": "자체",
"판": "2026",
"줄": [
{
"키": "GX000001",
"원문번호": "13-4-1",
"구분": "자체",
"상세구분": "13장 구조물",
"이름": "돌쌓기 메쌓기(인력)",
"결과단위": "원/㎡",
"출처": "산림품셈 13-4-1",
"소유": "현장",
"비고": "고르기 첫 값 = 원문에 있는 조합(뒷길이 25 · 깬잡석 · 골쌓기) · 「원문에 없는 조합: 뒷길이=25 · 돌=견치돌 · 쌓기=골쌓기 / 뒷길이=25 · 돌=견치돌 · 쌓기=켜쌓기 / 뒷길이=25 · 돌=깬돌 · 쌓기=골쌓기 / 뒷길이=25 · 돌=깬돌 · 쌓기=켜쌓기 / 뒷길이=30 · 돌=견치돌 · 쌓기=골쌓기 외 5」 · GF000219 를 본뜸",
"입력": [
{ "이름": "뒷길이", "설명": "쌓을 돌의 뒤쪽 길이", "단위": "㎝", "고르기": [25, 30, 35, 45, 55, 60, 75] },
{ "이름": "돌", "설명": "쌓을 돌 종류", "고르기": ["깬잡석", "견치돌", "깬돌", "호박돌 및 야면석"] },
{ "이름": "쌓기", "설명": "돌을 어긋나게 쌓는지 줄 맞춰 쌓는지", "고르기": ["골쌓기", "켜쌓기"] },
{ "이름": "높이", "설명": "쌓는 벽 높이", "단위": "m" },
{ "이름": "초과증가율", "설명": "기준 높이를 넘어 더 드는 품 비율", "단위": "%", "범위": [80, 100] }
],
"중간": [{ "이름": "증가율", "식": "만약(높이 <= 3, 0, 만약(높이 <= 7.5, 찾기(QF000422, 높이=높이).증가율, 초과증가율))", "출처": "산림품셈 13-4-1 주③" }],
"호표": [
{ "종류": "인력", "요소": "LB000033", "이름": "석공", "단위": "인", "수량": "찾기(QF000421, 뒷길이=뒷길이, 돌=돌, 쌓기=쌓기).석공 * (1 + 증가율 / 100)", "비목": "노무비" },
{ "종류": "인력", "요소": "LB000002", "이름": "보통인부", "단위": "인", "수량": "찾기(QF000421, 뒷길이=뒷길이, 돌=돌, 쌓기=쌓기).보통인부 * (1 + 증가율 / 100)", "비목": "노무비" }
],
"덧줄": [],
"끝수": null,
"시험입력": { "높이": 4.0, "돌": "깬잡석" }
},
{
"키": "GX000002",
"원문번호": "12-34-4",
"구분": "자체",
"상세구분": "12장 철근콘크리트",
"이름": "암거이음받침 채움재(T=20㎜)",
"결과단위": "원/㎡",
"출처": "산림품셈 12-34-4",
"소유": "현장",
"비고": "해석: 원문 재료비 「JOINT FILLER」 뿐 — ㎡당 1㎡ · 설치 품 원문 없음 · GF000208 를 본뜸",
"입력": [],
"중간": [],
"호표": [{ "종류": "재료", "요소": { "구분": "도료·방수·접착", "상세구분": "방수·실링", "검색어": "JOINT FILLER" }, "이름": "JOINT FILLER(T=20㎜)", "단위": "㎡", "수량": "1", "비목": "재료비" }],
"덧줄": [],
"끝수": null
},
{
"키": "GX000003",
"원문번호": "13-4-1",
"구분": "자체",
"상세구분": "13장 구조물",
"이름": "돌쌓기 메쌓기(인력)",
"결과단위": "원/㎡",
"출처": "산림품셈 13-4-1",
"소유": "현장",
"비고": "고르기 첫 값 = 원문에 있는 조합(뒷길이 25 · 깬잡석 · 골쌓기) · 「원문에 없는 조합: 뒷길이=25 · 돌=견치돌 · 쌓기=골쌓기 / 뒷길이=25 · 돌=견치돌 · 쌓기=켜쌓기 / 뒷길이=25 · 돌=깬돌 · 쌓기=골쌓기 / 뒷길이=25 · 돌=깬돌 · 쌓기=켜쌓기 / 뒷길이=30 · 돌=견치돌 · 쌓기=골쌓기 외 5」 · GF000219 를 본뜸",
"입력": [
{ "이름": "뒷길이", "설명": "쌓을 돌의 뒤쪽 길이", "단위": "㎝", "고르기": [25, 30, 35, 45, 55, 60, 75] },
{ "이름": "돌", "설명": "쌓을 돌 종류", "고르기": ["깬잡석", "견치돌", "깬돌", "호박돌 및 야면석"] },
{ "이름": "쌓기", "설명": "돌을 어긋나게 쌓는지 줄 맞춰 쌓는지", "고르기": ["골쌓기", "켜쌓기"] },
{ "이름": "높이", "설명": "쌓는 벽 높이", "단위": "m" },
{ "이름": "초과증가율", "설명": "기준 높이를 넘어 더 드는 품 비율", "단위": "%", "범위": [80, 100] }
],
"중간": [{ "이름": "증가율", "식": "만약(높이 <= 3, 0, 만약(높이 <= 7.5, 찾기(QF000422, 높이=높이).증가율, 초과증가율))", "출처": "산림품셈 13-4-1 주③" }],
"호표": [
{ "종류": "인력", "요소": "LB000033", "이름": "석공", "단위": "인", "수량": "찾기(QF000421, 뒷길이=뒷길이, 돌=돌, 쌓기=쌓기).석공 * (1 + 증가율 / 100)", "비목": "노무비" },
{ "종류": "인력", "요소": "LB000002", "이름": "보통인부", "단위": "인", "수량": "찾기(QF000421, 뒷길이=뒷길이, 돌=돌, 쌓기=쌓기).보통인부 * (1 + 증가율 / 100)", "비목": "노무비" }
],
"덧줄": [],
"끝수": null,
"시험입력": { "높이": 4.0, "돌": "깬잡석" }
}
]
}