관로 분석 - 엑세 다운로드 한글 맵핑 통일 작업

This commit is contained in:
kiminchan
2026-05-12 17:32:24 +09:00
parent cc24a16c77
commit 31b885a497
2 changed files with 89 additions and 9 deletions
@@ -18,6 +18,7 @@ import {
buildPipeAnalysisScope,
normalizeNumberInput,
resolveElapsedYearBucketLabel,
resolveMaterialKoreanName,
resolveMaterialName,
resolvePipeAnalysisTypeLabel,
resolveRegionName,
@@ -27,6 +28,19 @@ import {
} from "./pipeAnalysisUtils";
type DetailViewRow = ReturnType<typeof toPipeAnalysisDetailViewRows>[number];
const PIPE_ANALYSIS_DETAIL_EXPORT_KEYS = [
"rowNo",
"ctpvNm",
"sggNm",
"fcltMngNo",
"instMngNo",
"swtrmtZoneNm",
"instlYmd",
"useNm",
"pdmtVl",
"prlgVl",
"materialName",
] as const;
export default function PipeAnalysisDetailPopupPage() {
const searchParams = useSearchParams();
@@ -151,11 +165,25 @@ export default function PipeAnalysisDetailPopupPage() {
try {
setIsLoading(true);
const response = await fetchRows(1, filters, Math.max(total, 1000));
const exportRows = response.rows.map((row) => ({
rowNo: row.rowNo,
ctpvNm: row.ctpvNm,
sggNm: row.sggNm,
fcltMngNo: row.fcltMngNo,
instMngNo: row.instMngNo,
swtrmtZoneNm: row.swtrmtZoneNm,
instlYmd: row.instlYmd,
useNm: row.useNm,
pdmtVl: row.pdmtVl,
prlgVl: row.prlgVl,
materialName: resolveMaterialKoreanName(row.materialCode) || row.materialName,
}));
const detailHeaderRange = `A1:${String.fromCharCode(64 + PIPE_ANALYSIS_DETAIL_EXPORT_KEYS.length)}1`;
await downloadExcelWithKoreanHeaders({
data: response.rows as unknown as JSON[],
data: exportRows as unknown as JSON[],
fileName: `관로분석_관로현황_${buildPipeAnalysisFileDate()}`,
sheetNameArray: ["관로현황"],
headerRange: "A1:K1",
headerRange: detailHeaderRange,
headerMapping: PIPE_ANALYSIS_DETAIL_EXCEL_HEADERS,
});
} catch (error) {
@@ -519,17 +519,69 @@ export const resolveMaterialName = (materialCode?: string) => {
return materialMap.get(code) ?? code;
};
/**
* 관재질 라벨에서 영문 약어/코드를 걷어내고 엑셀 헤더용 한글명을 우선 추출한다.
* - `염화비닐관(PVC)` -> `염화비닐관`
* - `PCF관(강판보강 PE 나선형관)` -> `강판보강 나선형관`
*/
const toKoreanMaterialLabel = (value?: string) => {
const normalized = String(value ?? "").trim();
if (!normalized) return "";
const withoutParenthesis = normalized
.replace(/\s*\([^)]*\)\s*/g, " ")
.replace(/\s+/g, " ")
.trim();
const withoutAsciiToken = withoutParenthesis
.replace(/[A-Za-z0-9-]+/g, " ")
.replace(/\s+/g, " ")
.trim();
if (/[가-힣]/.test(withoutAsciiToken)) {
return withoutAsciiToken;
}
if (/[가-힣]/.test(withoutParenthesis)) {
return withoutParenthesis;
}
const parenthesisMatches = Array.from(normalized.matchAll(/\(([^)]*)\)/g))
.map((match) => String(match[1] ?? "").trim())
.filter(Boolean);
const koreanParenthesisText = parenthesisMatches.find((text) => /[가-힣]/.test(text));
if (koreanParenthesisText) {
const simplifiedParenthesisText = koreanParenthesisText
.replace(/[A-Za-z0-9-]+/g, " ")
.replace(/\s+/g, " ")
.trim();
return simplifiedParenthesisText || koreanParenthesisText;
}
return normalized;
};
export const resolveMaterialColumnName = (column: PipeAnalysisMaterialColumn) => {
const rawName = String(column.materialName ?? "").trim();
const rawCode = String(column.materialCode ?? "").trim();
const rawKey = String(column.key ?? "").trim();
if (!rawName) {
return resolveMaterialName(rawCode || rawKey);
}
if (rawName === rawCode || rawName === rawKey) {
return resolveMaterialName(rawCode || rawKey);
}
return rawName;
const mappedName = resolveMaterialName(rawCode || rawKey);
const candidates = [
toKoreanMaterialLabel(mappedName),
toKoreanMaterialLabel(rawName),
mappedName,
rawName,
rawCode,
rawKey,
].filter(Boolean);
return candidates[0] ?? "-";
};
export const resolveMaterialKoreanName = (materialCode?: string) => {
const resolved = resolveMaterialName(materialCode);
const koreanLabel = toKoreanMaterialLabel(resolved);
return koreanLabel || resolved;
};
export const resolveElapsedYearBucketLabel = (bucket?: string) => {