fix(M02 실무자료): 339 덧고침 — 확인 창 표 꼴 · 만들기 뒤 사전조합 목록 갱신 · 엑셀 보기 창 제안 수 다시 읽음
- 확인 창 줄 표: 한글 낱말 단위 줄바꿈 · 품명 · 규격 최소 폭 · 옵션 · 품 비교 항목마다 한 줄 · 단추 칸 세로 쌓음(좁게) - 별칭 장부 표: 품명 · 규격 같은 손질 - 만들기(확인 창 · 엑셀 보기 창 일괄) 뒤 document 알림 → 좌측 「일위대가 사전조합」 목록 다시 읽음 - 엑셀 보기 창: 뒤 셈 중이면 3 초마다 목록 「제안 n/m」 다시 읽음 · 펼침 표 숫자 판정 칸 = 맞음 n · 다른 점 n - 품 비교 판정 함수 · 서버 그대로 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWh5wNNycMkbwMpACz1rte
This commit is contained in:
@@ -49,29 +49,53 @@
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* 확인 창 줄 표(338) — 체크 · 갈래 · 수량 · 단위 · 상태 · 단추 = 줄바꿈 없음(글자가 세로로 안 갈라짐 ·
|
||||
폭 = 글 폭) · 품명 · 규격 · 우리 로직 · 옵션 · 품 비교가 남는 폭 · 칸 여백 좁힘(공용 표보다 앞서게 두 갈래 이름) */
|
||||
/* 실무자료 표 — 한글은 낱말 단위로만 줄바꿈(두세 글자마다 안 갈라짐) · 너무 긴 낱말만 쪼갬 */
|
||||
.m02-hopyo__table :is(th, td) {
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* 확인 창 줄 표(338 · 339) — 체크 · 갈래 · 수량 · 단위 · 상태 = 줄바꿈 없음(폭 = 글 폭) · 단추 = 세로로 쌓아 좁게 ·
|
||||
품명 · 규격 · 우리 로직 · 옵션 · 품 비교가 남는 폭 · 칸 여백 좁힘(공용 표보다 앞서게 두 갈래 이름) */
|
||||
.m02-hopyo__table.m02-hopyo__lines :is(th, td) {
|
||||
padding: var(--spacing-8);
|
||||
}
|
||||
|
||||
.m02-hopyo__table.m02-hopyo__lines
|
||||
:is(th, td):is(
|
||||
:nth-child(1),
|
||||
:nth-child(2),
|
||||
:nth-child(5),
|
||||
:nth-child(6),
|
||||
:nth-child(7),
|
||||
:nth-child(11)
|
||||
) {
|
||||
:is(th, td):is(:nth-child(1), :nth-child(2), :nth-child(5), :nth-child(6), :nth-child(7)) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 옵션 · 품 비교 — 길면 줄임표(전체 글은 칸 풍선 도움말) */
|
||||
/* 품명 · 규격 — 낱말 두셋은 한 줄에 */
|
||||
.m02-hopyo__table.m02-hopyo__lines td:nth-child(3),
|
||||
.m02-hopyo-ledger__table td:nth-child(1) {
|
||||
min-width: 7em;
|
||||
}
|
||||
|
||||
.m02-hopyo__table.m02-hopyo__lines td:nth-child(4),
|
||||
.m02-hopyo-ledger__table td:nth-child(2) {
|
||||
min-width: 6em;
|
||||
}
|
||||
|
||||
/* 옵션 · 품 비교 — 항목마다 한 줄(글의 줄바꿈 그대로) */
|
||||
.m02-hopyo__table.m02-hopyo__lines td:is(:nth-child(9), :nth-child(10)) {
|
||||
max-width: 24em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 9em;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
/* 단추 칸 — 단추 · 넣음/뺌을 세로로 쌓아 칸 폭 = 가장 긴 단추 */
|
||||
.m02-hopyo__table.m02-hopyo__lines td.m02-hopyo__acts {
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
.m02-hopyo__lines .m02-hopyo__acts > * {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.m02-hopyo__lines .m02-hopyo__acts > * + * {
|
||||
margin: var(--spacing-4) 0 0;
|
||||
}
|
||||
|
||||
.m02-hopyo__line.is-out {
|
||||
@@ -117,7 +141,7 @@
|
||||
|
||||
/* 우리 로직 칸 — 대상 여럿 = 칸 안 여러 줄(이름 · 키 · 몫 · 배 칸 · [옵션]) */
|
||||
.m02-hopyo__parts {
|
||||
min-width: 14em;
|
||||
min-width: 11em;
|
||||
}
|
||||
|
||||
.m02-hopyo__part {
|
||||
|
||||
@@ -58,6 +58,8 @@ export interface HopyoView extends HopyoFile {
|
||||
검산: { 어긋남: string[]; 총괄표: number };
|
||||
호표: HopyoBrief[];
|
||||
맞춤: [number, number];
|
||||
/** 뒤 숫자 판정 셈(끝나면 맞춤 수가 바뀜) */
|
||||
셈?: HopyoCalc;
|
||||
}
|
||||
|
||||
/** 후보가 단가산출 근거와 맞는 것(330-2) — 맞은 조항 글 · 겹친 직종 · 단위 같음 · 점수 · 까닭 꼬리표 */
|
||||
|
||||
@@ -49,6 +49,8 @@ import { openLaborInput } from "./M02_MasterTemplete_Labor_Input";
|
||||
import "./M02_MasterTemplete_Hopyo.css";
|
||||
|
||||
export const POLL_MS = 3000;
|
||||
/** 사전조합을 만들었음(document 알림) — 좌측 「일위대가 사전조합」 목록 다시 */
|
||||
export const HOPYO_BUILT = "m02-hopyo-built";
|
||||
const OUT = ["우리에 없음", "안 씀"] as const;
|
||||
|
||||
/** 확인 창 캐시 — 사용자가 만진 것만(서버 제안 위에 덮어 보임) */
|
||||
@@ -420,7 +422,7 @@ export function buildHopyoCheck(opt: CheckOptions): HTMLElement {
|
||||
const tags = [m.글, ...(m.꼬리표 ?? []), sourceText(line.근거)].filter(Boolean).join("\n");
|
||||
const opts = parts
|
||||
.filter((p) => p.로직)
|
||||
.map((p) => inputsText(p.입력값 ?? {}, p.못읽음 ?? [], p.출처))
|
||||
.flatMap((p) => inputsText(p.입력값 ?? {}, p.못읽음 ?? [], p.출처).split(/, (?=[^,=]+=)| · /))
|
||||
.filter(Boolean);
|
||||
if (m.갈리는칸?.length) opts.push(`⚠ 못 가름: ${m.갈리는칸.join(", ")}`);
|
||||
const alias = m.별칭?.규격다름 ? " · 규격 다름" : m.별칭?.근거다름 ? " · 근거 다름" : "";
|
||||
@@ -444,8 +446,12 @@ export function buildHopyoCheck(opt: CheckOptions): HTMLElement {
|
||||
],
|
||||
}),
|
||||
partCell(line),
|
||||
td(opts.join(" / ")),
|
||||
td(compareText(d.품[line.차례] ?? m.품)),
|
||||
td(opts.join("\n")), // 옵션 · 품 비교 = 항목마다 한 줄
|
||||
td(
|
||||
compareText(d.품[line.차례] ?? m.품)
|
||||
.split(" · ")
|
||||
.join("\n"),
|
||||
),
|
||||
el("td", { className: "m02-hopyo__acts", children: acts }),
|
||||
],
|
||||
});
|
||||
@@ -481,6 +487,7 @@ export function buildHopyoCheck(opt: CheckOptions): HTMLElement {
|
||||
built = res;
|
||||
const miss = res.빠진줄.length;
|
||||
showToast(`${res.참조.키} ${builtWord(res)}${miss ? ` — 빠진 줄 ${miss}` : ""}`, "success");
|
||||
document.dispatchEvent(new Event(HOPYO_BUILT));
|
||||
opt.onBuilt?.(res, h);
|
||||
await load(true);
|
||||
} catch (error) {
|
||||
|
||||
@@ -326,7 +326,7 @@ export function buildHopyoLedger(opt: LedgerOptions): HTMLElement {
|
||||
className: "ui-general-block__table-wrap ui-general-block__table-wrap--sticky-head",
|
||||
children: [
|
||||
el("table", {
|
||||
className: "ui-general-block__table m02-hopyo__table",
|
||||
className: "ui-general-block__table m02-hopyo__table m02-hopyo-ledger__table",
|
||||
children: [
|
||||
el("thead", {
|
||||
children: [
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/* =============================================================================
|
||||
* M02_MasterTemplete_Hopyo_Sheet.ts
|
||||
* 일위대가 실무자료 엑셀 보기 창(PLAN 329 · 338-3) — 큰 모달 · 왼쪽 = 올린 엑셀을 연 것처럼 읽기 전용 시트(A00 산출근거
|
||||
* 스프레드시트 · 탭 일위대가 · 단가산출) · 오른쪽 = 호표 목록(체크 · 찾기 · 제안 n/m) + [고른 n개 확인하고 만들기].
|
||||
* 스프레드시트 · 탭 일위대가 · 단가산출) · 오른쪽 = 호표 목록(체크 · 찾기 · 제안 n/m — 뒤 셈 중이면 3 초마다 다시)
|
||||
* + [고른 n개 확인하고 만들기].
|
||||
* · 목록 누름 → 시트에서 그 호표 영역(B열 ~ 끝 열) 고름 · 칸 누름 → 그 행 호표를 목록에서 강조 · 영역 고름
|
||||
* · ① [고른 n개 확인하고 만들기] = 고른 호표마다 서버 보기 → 펼침 표(넣을 줄 = 체크 제안 · 빠질 줄 · 셈 중 ·
|
||||
* · ① [고른 n개 확인하고 만들기] = 고른 호표마다 서버 보기 → 펼침 표(넣을 줄 = 체크 제안 · 빠질 줄 · 셈 중 또는 맞음 · 다른 점 수 ·
|
||||
* [확인 창 열기]) ② [이대로 n개 만들기] = 하위 호표부터 차례로 `/hopyo-build`(몸 = 그 호표 체크 줄) —
|
||||
* 펼쳐 보이고 한 번 누름 = 사용자 확인 · 결과 표(사전조합 키 · 빠진 줄 · 값 차이) ·
|
||||
* `onImport` 가 있으면 호표마다 알림([로직 추가] 참조 줄)
|
||||
@@ -32,14 +33,17 @@ import {
|
||||
type HopyoOne,
|
||||
type HopyoScope,
|
||||
type HopyoSpan,
|
||||
type HopyoView,
|
||||
} from "./M02_MasterTemplete_Hopyo_Api";
|
||||
import {
|
||||
bodyOf,
|
||||
draftKey,
|
||||
HOPYO_BUILT,
|
||||
leftOut,
|
||||
loadDraft,
|
||||
openHopyoCheck,
|
||||
partsOf,
|
||||
POLL_MS,
|
||||
toTarget,
|
||||
} from "./M02_MasterTemplete_Hopyo_Check";
|
||||
import { builtWord } from "./M02_MasterTemplete_Hopyo_Choose";
|
||||
@@ -239,6 +243,18 @@ export function openHopyoSheet(opt: HopyoSheetOptions): void {
|
||||
});
|
||||
}
|
||||
|
||||
/** 뒤 숫자 판정 셈 중이면 3 초 뒤 목록 「제안 n/m」 다시 읽음 — 셈 끝 · 창 닫음 · 못 읽음이면 멈춤 */
|
||||
function poll(file: HopyoView): void {
|
||||
if (!file.셈 || file.셈.끝) return;
|
||||
window.setTimeout(async () => {
|
||||
const next = closed ? null : await readHopyo(opt.scope, opt.key).catch(() => null);
|
||||
if (!next || closed) return;
|
||||
briefs = next.호표;
|
||||
paintList();
|
||||
poll(next);
|
||||
}, POLL_MS);
|
||||
}
|
||||
|
||||
async function load(): Promise<void> {
|
||||
try {
|
||||
const [file, grid] = await Promise.all([
|
||||
@@ -249,6 +265,7 @@ export function openHopyoSheet(opt: HopyoSheetOptions): void {
|
||||
briefs = file.호표;
|
||||
spans = grid instanceof Error ? [] : grid.호표;
|
||||
paintList();
|
||||
poll(file);
|
||||
if (grid instanceof Error) return void left.replaceChildren(note(grid.message));
|
||||
await mountSheet(grid);
|
||||
if (opt.단산행 !== undefined) sheet?.select(rowsText(opt.단산행), DANSAN);
|
||||
@@ -300,7 +317,11 @@ export function openHopyoSheet(opt: HopyoSheetOptions): void {
|
||||
const h = one.호표;
|
||||
const d = loadDraft(draftKey(opt.scope, opt.key, h.번));
|
||||
const out = Object.entries(leftOut(h.줄, d));
|
||||
const calc = h.셈 && !h.셈.끝 ? `셈 중 ${h.셈.한}/${h.셈.모두}` : "";
|
||||
const tally = (s: string): number => h.줄.filter((l) => l.맞춤.상태 === s).length;
|
||||
const calc =
|
||||
h.셈 && !h.셈.끝
|
||||
? `셈 중 ${h.셈.한}/${h.셈.모두}`
|
||||
: `맞음 ${tally("맞음")} · 다른 점 ${tally("다른 점")}`;
|
||||
const brief = briefs.find((x) => x.번 === h.번) ?? h;
|
||||
return el("tr", {
|
||||
attrs: { "data-review": String(h.번) },
|
||||
@@ -395,6 +416,7 @@ export function openHopyoSheet(opt: HopyoSheetOptions): void {
|
||||
} finally {
|
||||
hideLoadingOverlay();
|
||||
}
|
||||
if (ok) document.dispatchEvent(new Event(HOPYO_BUILT));
|
||||
result.replaceChildren(table(["호표", "사전조합", "빠진 줄", "값 차이(참고)"], rows));
|
||||
showToast(`만듦 ${ok} / ${order.length}`, ok === order.length ? "success" : "warning");
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
import { STRUCTURE_TABLE } from "./M02_MasterTemplete_Structure";
|
||||
import { buildOptionsSection } from "./StructOptions/M02_SO_Options";
|
||||
import { buildPracticeSection } from "./M02_MasterTemplete_Practice";
|
||||
import { HOPYO_BUILT } from "./M02_MasterTemplete_Hopyo_Check";
|
||||
import { buildHopyoSection } from "./M02_MasterTemplete_Hopyo_Side";
|
||||
import { canEdit, type Selection } from "./M02_MasterTemplete_UI_Main";
|
||||
import {
|
||||
@@ -150,6 +151,12 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
getOpen: opt.getOpen,
|
||||
});
|
||||
const precomboSec = section(PRECOMBO_TITLE, precombo.body);
|
||||
// 실무자료로 사전조합을 만들었음(확인 창 · 엑셀 보기 창) → 목록 다시 · 이 패널이 내려갔으면 듣기 끝
|
||||
const onBuilt = (): void =>
|
||||
precombo.body.isConnected
|
||||
? void precombo.refresh()
|
||||
: document.removeEventListener(HOPYO_BUILT, onBuilt);
|
||||
document.addEventListener(HOPYO_BUILT, onBuilt);
|
||||
// 구조물 옵션 정의 · 도면 양식은 시스템 층(마스터 템플릿)에만 · 정의 고쳐 씀은 시스템 관리자만 · 다른 역할은 읽기만(PLAN 48-2)
|
||||
const extras =
|
||||
layer === "system"
|
||||
|
||||
@@ -368,3 +368,29 @@ def test_실물_응답_대역_확인_창():
|
||||
assert [x["차례"] for x in got["body"]] == [x["차례"] for x in lines if x["맞춤"]["체크"]]
|
||||
assert all({"로직", "입력값", "배"} <= set(t) for x in got["body"] for t in x["대상"])
|
||||
assert got["sure"] == [x["차례"] for x in lines if x["맞춤"]["상태"] == "맞음"]
|
||||
|
||||
|
||||
def test_확인_창_손질_339():
|
||||
"""339 덧고침 — ① 줄 표 꼴(한글 낱말 줄바꿈 · 옵션 · 품 비교 한 줄씩 · 단추 세로) · 장부 품명 · 규격 폭
|
||||
② 만들기 뒤 좌측 사전조합 목록 다시 ③ 엑셀 보기 창 목록 셈 끝까지 다시 읽음 · 펼침 표 숫자 판정 칸."""
|
||||
css = _text("M02_MasterTemplete_Hopyo.css")
|
||||
assert "word-break: keep-all;" in css and "white-space: pre-line;" in css
|
||||
assert (
|
||||
".m02-hopyo__lines .m02-hopyo__acts > *" in css
|
||||
and "text-overflow"
|
||||
not in css.split(".m02-hopyo__lines td:is(:nth-child(9)")[-1].split("}")[0]
|
||||
)
|
||||
assert ".m02-hopyo-ledger__table td:nth-child(1)" in css
|
||||
assert "m02-hopyo-ledger__table" in _text("M02_MasterTemplete_Hopyo_Ledger.ts")
|
||||
check = _text("M02_MasterTemplete_Hopyo_Check.ts")
|
||||
assert r'td(opts.join("\n"))' in check and r'.split(" · ")' in check
|
||||
assert r'.join("\n"),' in check # 품 비교 = 항목마다 한 줄(판정 글 함수는 그대로)
|
||||
make = check.split("async function make(")[1].split("function builtBlock(")[0]
|
||||
assert make.index("document.dispatchEvent(new Event(HOPYO_BUILT));") < make.index("opt.onBuilt")
|
||||
side = _text("M02_MasterTemplete_UI_Side.ts")
|
||||
assert "document.addEventListener(HOPYO_BUILT, onBuilt);" in side
|
||||
assert "void precombo.refresh()" in side.split("const onBuilt")[1].split(";")[0]
|
||||
sheet = _text("M02_MasterTemplete_Hopyo_Sheet.ts")
|
||||
assert "poll(file);" in sheet and "poll(next);" in sheet and "POLL_MS" in sheet
|
||||
assert "if (ok) document.dispatchEvent(new Event(HOPYO_BUILT));" in sheet
|
||||
assert '`맞음 ${tally("맞음")} · 다른 점 ${tally("다른 점")}`' in sheet
|
||||
|
||||
Reference in New Issue
Block a user