feat(804-8): M02 단가산출 수식 탭 = M01 과 같은 단추 · 저장 한 번 · 머리 단추 줄바꿈 막음(요구 689)
- 공용 줄 쌓기 화면: 페이지가 저장을 맡는 꼴(`page`)을 M02 단가산출 화면에도 — 탭 위 [고침 버림] · [수식 저장] 걷음 · M02 사본 [단가 갱신] · [재생성] · 「M01 새 판」 배지는 그대로 · 「줄 쌓기에서 못 고치는 칸」 안내 줄 걷음(금액 줄 기호 = 율 줄이 앞 줄 금액을 부르는 속 이름 · 화면에서 고칠 값 아님 · 보존 그대로) - M02 머리 단추 = [초기화] · [삭제] · [되돌림] · [저장] — 단가산출 상세는 [새로고침] 숨김 · [되돌림] · [저장] = 고침 있을 때만(기본정보 + 수식) · [저장] 한 번 = 수식 먼저 저장(새 판 · 수식 칸 받음) → 단가산출 문서 저장 · [되돌림] · 버리고 떠남 = 수식 캐시도 비움 - 보기 고르기(수식 · 단가산출 상세) = M01 처럼 머리 줄 · [항목 추가] · [율 줄 추가] 걷음(줄 더하기 = 수식 탭 [+] 단가산출 · 일위대가 복사) · 일위대가 줄 모달 안 상세는 그대로 - 머리 단추 줄 맞춤(부품 상자 `fitButtonRow`): 단추가 아래로 떨어지거나 제목이 여러 줄이면 아이콘 단추는 아이콘만(글 = 풍선 도움말) · M01 단추 다섯 · M02 단추 넷 아이콘 · 단가산출 상세 긴 이름은 제목 안에서 줄바꿈 - 사본 할일 막힘 알림 글 = [저장] 또는 [되돌림] 먼저 - 시험: test_804_7 에 M02 단추 · 저장 한 번 · 줄 맞춤 지킴 더함 · test_804_4 권한 시험에 수식 저장 판 = 페이지 저장 판 이어 씀 확인 더함 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
/* =============================================================================
|
||||
* line_stack_screen.ts
|
||||
* 단가산출 「줄 쌓기」 탭(PLAN 804-2 · 요구 670~677) — 공용(M01 단가산출 기초 · M02 단가산출 = 길 앞머리만 바꿈)
|
||||
* 위 = 옵션 가로(`formula_doc_options.ts`) · [고침 버림] · [수식 저장](관리자 · 층 쓰기) · M02 사본 [단가 갱신] · [재생성]
|
||||
* M01(`page` 있음 · 804-7) = 자기 단추 없음 — 페이지 [저장] · [되돌림]이 `save` · `discard` 를 부름 · 고침이 바뀌면 `page()`
|
||||
* 위 = 옵션 가로(`formula_doc_options.ts`) · M02 사본 [단가 갱신] · [재생성] · 「M01 새 판」 배지(층 쓰기)
|
||||
* `page` 있음(M01 804-7 · M02 804-8) = 저장 · 되돌림 단추 없음 — 페이지 [저장] · [되돌림]이 `save` · `discard` 를 부름 ·
|
||||
* 고침이 바뀌면 `page()` · `page` 없음(일위대가 줄 모달 등) = 위에 [고침 버림] · [수식 저장]
|
||||
* 가운데 = 왼쪽 줄 쌓기 편집(`line_stack_edit.ts`) | 오른쪽 산출근거 출력(`line_stack_out.ts` · 기계 시간당 경비 펼침)
|
||||
* 아래 = 합계 상자(노무비 · 재료비 · 경비 · 계 · 화면 아래 고정 · 요구 687-3)
|
||||
* 정본 = 수식 문서(탭을 바꾸면 다시 읽음) · 줄 쌓기 차림 = 문서 칸 `쌓기` · 804-6 옛 수식 문서 탭 걷음 = 이 화면이 「수식」 탭
|
||||
* 셈 = 브라우저 계산기(`calcDoc`) 즉시 · 캐시에 없는 연결만 뒤에서 `POST {길}/preview`(옛 요청 끊음)
|
||||
* 고침 = 캐시(sessionStorage)에 쌓고 [수식 저장] 한 번에 `POST {길}/save`(서버가 정본으로 다시 셈) · 자동저장 없음
|
||||
* 고침 = 캐시(sessionStorage)에 쌓고 [저장] 한 번에 `POST {길}/save`(서버가 정본으로 다시 셈) · 자동저장 없음
|
||||
* M02 사본(`copy`) — 저장 몸 {문서, 끊음(받은 그대로), 판} · 판 바뀜(409)은 문서가 그대로면 새 판으로 한 번 더
|
||||
* [단가 갱신] · [재생성] = 미리 셈 → 알림 → 저장(`formula_doc_copy.ts` · 고친 것 먼저 저장)
|
||||
* ========================================================================== */
|
||||
@@ -28,7 +29,6 @@ import type { DocTable } from "../common_util/common_util_formula_doc_table";
|
||||
import { createDocCache, type DocRecord } from "../common_util/common_util_formula_doc_cache";
|
||||
import {
|
||||
docOf,
|
||||
notesOf,
|
||||
stackOf,
|
||||
type StackDoc,
|
||||
type StackItem,
|
||||
@@ -61,7 +61,7 @@ export interface StackScreenOptions {
|
||||
copy?: CopyOptions;
|
||||
/** M02 SK 층(804-4) — 줄 쌓기 다른 단가산출 · 일위대가 복사 길 · 없으면(M01) 복사 없음 */
|
||||
scope?: { layer: Layer; owner: string | null };
|
||||
/** 페이지가 저장 · 되돌림을 맡음(M01 · 804-7) — 고침 있음이 바뀔 때 부름 · 화면 위 [고침 버림] · [수식 저장] 없음 */
|
||||
/** 페이지가 저장 · 되돌림을 맡음(M01 804-7 · M02 804-8) — 고침 있음이 바뀔 때 부름 · 화면 위 [고침 버림] · [수식 저장] 없음 */
|
||||
page?: () => void;
|
||||
}
|
||||
|
||||
@@ -80,14 +80,13 @@ const clone = <T>(v: T): T => JSON.parse(JSON.stringify(v)) as T;
|
||||
|
||||
export function createStackScreen(opt: StackScreenOptions) {
|
||||
const head = el("div", { className: "lstk__head" });
|
||||
const notes = el("div", { className: "lstk__notes" });
|
||||
const left = el("div", { className: "lstk__left" });
|
||||
const right = el("div", { className: "lstk__right" });
|
||||
const foot = el("div", { className: "lstk__foot" });
|
||||
const root = el("div", {
|
||||
className: "lstk",
|
||||
attrs: { "data-line-stack": "" },
|
||||
children: [head, notes, el("div", { className: "lstk__areas", children: [left, right] }), foot],
|
||||
children: [head, el("div", { className: "lstk__areas", children: [left, right] }), foot],
|
||||
});
|
||||
// 산출근거 고정 높이(요구 683) — 스크롤 조상(없으면 창) 높이 · 넓을 때만 고정(line_stack.css)
|
||||
const fit = (): void => {
|
||||
@@ -245,7 +244,7 @@ export function createStackScreen(opt: StackScreenOptions) {
|
||||
if (from === "left") r[0]?.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
||||
};
|
||||
|
||||
/** 복사해 펼침(804-4) — 서버 조각 → 새 항목(고침 = 캐시 · [수식 저장] 때 서버가 다시 셈) */
|
||||
/** 복사해 펼침(804-4) — 서버 조각 → 새 항목(고침 = 캐시 · [저장] 때 서버가 다시 셈) */
|
||||
const takeIn = async (src: string): Promise<void> => {
|
||||
try {
|
||||
const got = await call<Taken>(`/take?src=${encodeURIComponent(src)}`);
|
||||
@@ -263,7 +262,7 @@ export function createStackScreen(opt: StackScreenOptions) {
|
||||
const edit = (): boolean => opt.admin && (!!key || !cp);
|
||||
const draw = (): void => {
|
||||
if (!doc || !answer) {
|
||||
[head, notes, left, right, foot].forEach((x) => x.replaceChildren());
|
||||
[head, left, right, foot].forEach((x) => x.replaceChildren());
|
||||
return;
|
||||
}
|
||||
const options = buildOptions(doc, given, !!cp && !opt.admin, (k, v, x) => {
|
||||
@@ -274,10 +273,10 @@ export function createStackScreen(opt: StackScreenOptions) {
|
||||
draw();
|
||||
});
|
||||
const busy = dirty();
|
||||
const tools =
|
||||
edit() && key && !opt.page
|
||||
const tools = [
|
||||
...(cp && edit() && key ? copyTools(true, (job) => void runJob(job)) : []),
|
||||
...(edit() && key && !opt.page
|
||||
? [
|
||||
...(cp ? copyTools(true, (job) => void runJob(job)) : []),
|
||||
createButton({
|
||||
label: ft("Discard"),
|
||||
variant: "ghost",
|
||||
@@ -293,7 +292,8 @@ export function createStackScreen(opt: StackScreenOptions) {
|
||||
onClick: () => void save(),
|
||||
}),
|
||||
]
|
||||
: [];
|
||||
: []),
|
||||
];
|
||||
head.replaceChildren(
|
||||
...(options.length ? [el("div", { className: "fdoc__options", children: options })] : []),
|
||||
el("div", {
|
||||
@@ -310,17 +310,6 @@ export function createStackScreen(opt: StackScreenOptions) {
|
||||
],
|
||||
}),
|
||||
);
|
||||
const kept = notesOf(doc);
|
||||
notes.replaceChildren(
|
||||
...(kept.length
|
||||
? [
|
||||
el("p", {
|
||||
className: "ui-text ui-text--muted",
|
||||
text: `${lt("Notes")} — ${kept.join(" · ")}`,
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
);
|
||||
const items = stackOf(doc);
|
||||
root.dataset.edit = edit() ? "1" : "";
|
||||
left.replaceChildren(
|
||||
@@ -468,6 +457,7 @@ export function createStackScreen(opt: StackScreenOptions) {
|
||||
if (all[next]) doc = clone(all[next] as StackDoc);
|
||||
recalc();
|
||||
draw();
|
||||
opt.page?.(); // 캐시에서 이어 받은 고침 — 페이지 [저장] · [되돌림] 켬
|
||||
} catch (e) {
|
||||
if (mine === seq)
|
||||
head.replaceChildren(
|
||||
|
||||
Reference in New Issue
Block a user