feat(785-6): 엑셀 보기에 줄 판 · 블록 판 · [옮기기] 이음

- Hopyo_Sheet.ts: [수정] = 시트 아래 줄 판(createJudgeRows · 그 호표 자리 뜀) · 오른쪽 판 아래 [옮기기](createJudgeMove) · 고름 캐시 한 벌 · 옮긴 뒤 목록 · 칠 · 단가산출 목록 다시 읽음
- Hopyo_SheetSk.ts: 단가산출 목록 = 새 블록 판정(hopyo-judge 목록 · 판정 · 고를 것 n · 표지 · 부름 · 만든 SK · 캐시 확인) · 줄 누름 = 그 자리 + 블록 판(createJudgeBlock)
- Hopyo_JudgeApi.ts: 목록 블록 줄 판정 · 고를수 · Block.ts 타입 흠 둘
- 낡은 시험 고침(까닭 = 785-6 이 옛 마법사 단가산출 칸 · 확인 창을 새 판으로 갈음): test_627_sheet_sk(판 바꿈 · 강조 · 뛰기를 새 목록으로 · helper_778_5 sheetSk 대역 새 꼴) · test_785_4 줄 판 자리 손잡이 · test_m02_hopyo_screen [수정] = 줄 판
- test_642_6: 엑셀 보기 이음 · 가운데 페이지 없음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-04 23:16:43 +09:00
co-authored by Claude Opus 5.5
parent 8fde93b910
commit dcecc0b326
10 changed files with 325 additions and 70 deletions
@@ -7,7 +7,8 @@
* 오른쪽 = 호표 목록(체크 · 제N호표 [수정] · 찾기 · 제안 n/m — 다 연결이면 녹색 · 뒤 셈 중이면 3 초마다 다시) + [저장] ·
* 단가산출표 · 상세 탭이면 단가산출 목록으로 바뀜(780-2 · `Hopyo_SheetSk`).
* · 목록 누름 → 시트에서 그 호표 영역(B열 ~ 끝 열) 고름 · 칸 누름 → 그 행 호표를 목록에서 강조 · 머리 행이면 영역 고름
* · [수정] = 그 호표 확인 창(`openHopyoCheck` · 줄 누름과 따로 · 785-6 이 줄 판으로 갈음)
* · [수정] = 시트 아래 줄 판(785-6 `Hopyo_Rows` · 줄 누름과 따로) · 단가산출 탭 블록 = 같은 자리 블록 판 ·
* 오른쪽 판 아래 [옮기기](확인한 줄 · 블록 · 고름 캐시 `createJudgeState` 한 벌)
* · [저장] · [모두 고정으로 들여오기] = `Hopyo_SheetMake`
* · 격자 = 서버 `/hopyo-grid/{키}` · 옛 파일(격자 없음)은 시트 자리에 까닭 글 · 목록 · 저장은 됨
* · `buildHopyoSheet` = 몸(머리 · 띠 · 시트 + 목록) · `openHopyoSheet` = 모달 껍데기([로직 추가] 「실무 호표」 길 — 785-7 이 지움)
@@ -29,7 +30,12 @@ import {
type HopyoView,
type HopyoScope,
} from "./M02_MasterTemplete_Hopyo_Api";
import { openHopyoCheck, POLL_MS } from "./M02_MasterTemplete_Hopyo_Check";
import { POLL_MS } from "./M02_MasterTemplete_Hopyo_Check";
import {
createJudgeMove,
createJudgeRows,
createJudgeState,
} from "./M02_MasterTemplete_Hopyo_Rows";
import * as Nav from "./M02_MasterTemplete_Hopyo_SheetNav";
import { createSheetSk, type SheetSk } from "./M02_MasterTemplete_Hopyo_SheetSk";
import { note, why, wireMake } from "./M02_MasterTemplete_Hopyo_SheetMake";
@@ -154,6 +160,8 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
const checked = new Set<number>();
const items = new Map<number, HTMLElement>();
const target = opt.target ?? opt.scope;
const state = createJudgeState(opt.scope, opt.key); // 판정 고름 캐시(785-6)
let panel: { destroy: () => void } | null = null; // 시트 아래 줄 판 몸
const head = el("div", {
className: "ui-toolbar m02-hopyo__bar",
@@ -173,6 +181,16 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
attrs: { "data-hopyo-lines": "" },
}); // 시트 아래 줄 판 자리
lines.hidden = true;
const closeLines = (): void => {
panel?.destroy();
panel = null;
lines.hidden = true;
};
const openLines = (make: (host: HTMLElement) => { destroy: () => void }): void => {
panel?.destroy();
lines.hidden = false;
panel = make(lines);
};
const col = el("div", { className: "m02-hopyo-sheet__col", children: [left, lines] });
const searchField = createInputField({
type: "search",
@@ -190,6 +208,14 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
fixAll.setAttribute("data-hopyo-all-fixed", "");
fixAll.disabled = true; // 엑셀을 다 읽은 뒤 켬
const result = el("div", { className: "m02-hopyo-sheet__result" });
const move = createJudgeMove({
file: opt.scope,
key: opt.key,
target,
state,
readOnly: !opt.canWrite,
onMoved: () => void refresh(),
});
const right = el("div", {
className: "m02-hopyo-sheet__side",
children: [
@@ -200,6 +226,7 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
}),
list,
result,
move.root,
],
});
@@ -207,6 +234,9 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
if (closed) return;
closed = true;
opened.delete(id);
closeLines();
move.destroy();
sk?.destroy();
nav?.destroy();
sheet?.destroy();
opt.onClose?.();
@@ -261,7 +291,7 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
else if (o.호표 !== undefined) focus(o.호표);
});
/** [수정] — 그 호표 확인 창(줄 누름과 따로) */
/** [수정] — 그 호표 자리 + 시트 아래 줄 판(줄 누름과 따로) */
const editButton = (h: HopyoBrief): HTMLElement => {
const b = createButton({
label: "수정",
@@ -269,14 +299,20 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
size: "sm",
onClick: (e) => {
e.stopPropagation();
openHopyoCheck({
scope: opt.scope,
key: opt.key,
no: h.번,
target,
canWrite: opt.canWrite,
onBuilt: (got) => opt.onImport?.(got, h),
});
focus(h.번);
openLines((host) =>
createJudgeRows({
host,
file: opt.scope,
key: opt.key,
target,
state,
no: h.번,
readOnly: !opt.canWrite,
onClose: closeLines,
onSheet: (r, role) => nav?.jumpTo(role === "일위대가표" ? ilwi : detail, r),
}),
);
},
});
b.setAttribute("data-hopyo-edit", String(h.번));
@@ -341,7 +377,7 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
const more = { ...opt, target, grid, last: lastOf, sheet: () => sheet, host: left };
nav = ids.일위대가표 ? Nav.createSheetNav(more) : null;
if (nav) warn.after(nav.root);
sk = nav ? createSheetSk({ ...opt, target, nav, side: right, lines, ...(opt.onSkChange ? { onChange: opt.onSkChange } : {}) }) : null; // prettier-ignore
sk = nav ? createSheetSk({ ...opt, target, nav, state, side: right, openLines, closeLines, ...(opt.onSkChange ? { onChange: opt.onSkChange } : {}) }) : null; // prettier-ignore
sheet = createSpreadsheet(left, book, {
readOnly: true,
칠: nav?.fill,
@@ -378,6 +414,18 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
}, POLL_MS);
}
/** 옮긴 뒤 — 목록 · 칠 · 단가산출 목록 다시 읽음 */
async function refresh(): Promise<void> {
const next = await readHopyo(opt.scope, opt.key).catch(() => null);
if (!next || closed) return;
briefs = next.호표;
paintHead(next);
paintList();
nav?.marks(briefs);
sk?.reload();
opt.onSkChange?.();
}
async function load(): Promise<void> {
try {
const [file, grid] = await Promise.all([