Files
Pier_v1/수량산출/export.ts
T
ypshimandClaude Haiku 5.5 fc520ec03d feat: 프로젝트 공통 지반모델·교대 지반 통합·재료표/수량 진입·옥성교 랜딩 히어로 (11-06~11-09 작업분)
공용 파일(main.ts·index.html·style.css)에서 서로 얽혀 있어 한 커밋으로 묶음.
- 지반: ① 형상 프로젝트 지반모델 관리창(생성·JSON 불러오기·교각별 비교·선택 적용),
  프로젝트 JSON 내장 groundModel 추출, 교대 8행 통합 적용·공통지형 중복격자 제외
- 재료표·수량: 상단 ⑤ 메뉴·배근 바로가기, 결과 교각/탭 기억, 수량 워크북·정합 보정
- 랜딩: 옥성교 6교각 히어로 이미지·투영 강조점·좌→우 광선·지형 라운드/도로 연결
- 검증 스크립트(verify-project-ground/ground-abutments/okseong-user-files/quantity-*/landing-hero 등)
정본: DOC/04_실행/실행_*_2026-11-09_v0.1.00.md

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-09 18:01:02 +09:00

45 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {straightCopingFormulas} from './coping-formulas.ts';
import {FormulaBook,cellKey,type Overrides} from './formula.ts';
import {template,templateHash} from './template-data.ts';
import {DETAIL,INPUTS,extraNumbers,specifications} from './schema.ts';
export type ExportRequest={project:{name:string;bridge:string};pier:string;overrides:Overrides;specs:Record<string,unknown>;reviewed:boolean;copingMode?:'straight';templateHash:string;planPng?:string;solplateMode?:'model'};
export function exportEdits(request:ExportRequest){
if(request.templateHash!==templateHash||!request.reviewed)throw new Error('원본 산식 적용 확인 후 다시 계산하세요.');
const edits:Record<string,Record<string,{value?:string|number|null;cached?:number|string;formula?:string}>>={},evaluation:Overrides={...request.overrides};
if(request.solplateMode!=='model'){delete evaluation[cellKey(DETAIL,'W64')];delete evaluation[cellKey(DETAIL,'K64')];}
const value=(sheet:string,cell:string,v:string|number|null)=>{(edits[sheet]??={})[cell]={value:v};evaluation[cellKey(sheet,cell)]=v??0;};
for(let r=18;r<=46;r++){
const raw=request.overrides[cellKey(INPUTS,'N'+r)];if(raw!==null&&(typeof raw!=='number'||!Number.isFinite(raw)))throw new Error('입력값 누락 N'+r);
value(INPUTS,'N'+r,request.copingMode==='straight'&&r===30?null:raw as number|null);
}
for(const v of extraNumbers){const n=request.overrides[cellKey(DETAIL,v.cell)];if(typeof n!=='number'||!Number.isFinite(n)||n<0)throw new Error(v.name+' 입력을 확인하세요.');value(DETAIL,v.cell,n);}
for(const v of specifications){const s=request.specs[v.key];if(typeof s!=='string'||!s.trim())throw new Error(v.name+'을 확인하세요.');value(DETAIL,v.cell,s);}
if(request.solplateMode==='model'){
const ton=request.overrides[cellKey(DETAIL,'W64')];
if(typeof ton!=='number'||!Number.isFinite(ton)||ton<0)throw new Error('모델 솔플레이트 중량을 확인하세요.');
value(DETAIL,'W64',ton);
value(DETAIL,'K64','3D 모델 판별 체적 합계 × 7.85 (앱 재생성 값)');
}
for(let i=2;i<=6;i++){
const sheet='3.3 수량산출_P'+i;
for(const [cell,c]of Object.entries(template[sheet]))if(typeof c.value==='number'||typeof c.value==='string'&&c.value.startsWith('='))value(sheet,cell,null);
value(sheet,'F2','(미사용)');value(sheet,'BG5','미사용');
const col=['P','R','T','V','X'][i-2];for(let r=18;r<=46;r++)value(INPUTS,col+r,null);value(INPUTS,col+'17','미사용');
value('1. 수량집계',['Q','T','W','Z','AC'][i-2]+'4','미사용');
}
// The example sheet must stay a source example, not follow the active pier's straight R.
for(let r=6;r<=34;r++){const c=template['3.3 수량산출_ex)']['BG'+r];if(c&&typeof c.cached==='number')value('3.3 수량산출_ex)','BG'+r,c.cached);}
value(INPUTS,'N17',request.pier);value(DETAIL,'F2','('+request.pier+')');value(DETAIL,'BG5',request.pier);value('1. 수량집계','N4',request.pier);
value('표지및목차','A6',request.project.name);value('표지및목차','A14',request.project.bridge+' · '+request.pier);value('표지및목차','A24',new Date().toISOString().slice(0,10));
const formulas=request.copingMode==='straight'?straightCopingFormulas:{};
for(const [key,formula]of Object.entries(formulas)){const pos=key.lastIndexOf('!');(edits[key.slice(0,pos)]??={})[key.slice(pos+1)]={formula};}
const book=new FormulaBook(evaluation,template,formulas);
for(const [sheet,cells]of Object.entries(template))for(const [cell,c]of Object.entries(cells)){
if(typeof c.value!=='string'||(!c.value.startsWith('=')||c.value.length===1)||edits[sheet]?.[cell]&&Object.hasOwn(edits[sheet][cell],'value'))continue;
const n=book.cell(sheet,cell);if(typeof n!=='number'||!Number.isFinite(n))throw new Error('출력 수식 검증 실패 '+sheet+'!'+cell);
(edits[sheet]??={})[cell]={...edits[sheet]?.[cell],cached:n};
}
if(request.planPng&&(!/^[A-Za-z0-9+/=]+$/.test(request.planPng)||request.planPng.length>1500000))throw new Error('평면도 이미지 형식을 확인하세요.');
return {templateHash,edits,planPng:request.planPng};
}