feat:행정동 탭코드 추가
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import { Chart } from "react-chartjs-2";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
BarElement,
|
||||
LineElement,
|
||||
PointElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
} from "chart.js";
|
||||
import type { ChartOptions } from "chart.js";
|
||||
|
||||
ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);
|
||||
|
||||
// 차트 설정 외부 정의 (Mixed 타입 명시)
|
||||
const options: ChartOptions<"bar" | "line"> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: { stacked: true },
|
||||
y: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
title: { display: true, text: "인구수(명)" },
|
||||
},
|
||||
y1: {
|
||||
position: "right",
|
||||
beginAtZero: true,
|
||||
grid: { drawOnChartArea: false },
|
||||
title: { display: true, text: "총 인구(명)" },
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: { position: "bottom" },
|
||||
},
|
||||
};
|
||||
|
||||
export type PopulationChartData = {
|
||||
X_AXIS: string[];
|
||||
TOT: number[];
|
||||
ORIGIN_MALE: (number | null)[];
|
||||
ORIGIN_FEMALE: (number | null)[];
|
||||
FOREIGN_MALE: (number | null)[];
|
||||
FOREIGN_FEMALE: (number | null)[];
|
||||
};
|
||||
|
||||
type Props = {
|
||||
chartData: PopulationChartData | null;
|
||||
};
|
||||
|
||||
const SERIES_META = [
|
||||
{ key: "ORIGIN_MALE", name: "내국인 남성", color: "#3B82F6" },
|
||||
{ key: "ORIGIN_FEMALE", name: "내국인 여성", color: "#EC4899" },
|
||||
{ key: "FOREIGN_MALE", name: "외국인 남성", color: "#10B981" },
|
||||
{ key: "FOREIGN_FEMALE", name: "외국인 여성", color: "#F59E0B" },
|
||||
] as const;
|
||||
|
||||
const PopulationStatusChart = ({ chartData }: Props) => {
|
||||
if (!chartData || chartData.X_AXIS.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full min-h-[40rem] w-full text-gray-400 text-[1.4rem] border border-gray-200 rounded">
|
||||
조회된 데이터가 없습니다. 조회 버튼을 눌러주세요.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const data = {
|
||||
labels: chartData.X_AXIS.map((yr) => `${yr}년`),
|
||||
datasets: [
|
||||
...SERIES_META
|
||||
.filter((s) => chartData[s.key]?.some((v) => v !== null))
|
||||
.map((s) => ({
|
||||
type: "bar" as const,
|
||||
label: s.name,
|
||||
data: chartData[s.key].map((v) => v ?? 0),
|
||||
backgroundColor: s.color,
|
||||
stack: "stack1",
|
||||
})),
|
||||
{
|
||||
type: "line" as const,
|
||||
label: "총 인구",
|
||||
data: chartData.TOT,
|
||||
borderColor: "#1F2937",
|
||||
backgroundColor: "#1F2937",
|
||||
borderDash: [5, 5],
|
||||
tension: 0.1,
|
||||
yAxisID: "y1",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full w-full border border-gray-200 rounded p-4">
|
||||
{/* 제네릭 타입을 통해 Mixed Chart 지원, 부모 컨테이너 크기에 맞춰 반응형으로 리사이즈됨 */}
|
||||
<Chart<"bar" | "line", (string | number)[], string>
|
||||
type="bar"
|
||||
data={data}
|
||||
options={options}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PopulationStatusChart;
|
||||
@@ -21,10 +21,12 @@ import {
|
||||
AdmdstRenewalReducedColumnConfig,
|
||||
AdmdstRenewalAddedColumnConfig,
|
||||
} from "@/components/util/column/management/AdmdstRenewalColumn";
|
||||
import { PopulationStatusColumnConfig } from "@/components/util/column/management/PopulationStatusColumn";
|
||||
import { statisticsPpltnApi } from "@/entities/statistics/api/statistics-ppltn-service";
|
||||
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
|
||||
import type { UpperCodeCandidateItem } from "@/entities/statistics/model/ppltn/statistics-domestic-population-interface";
|
||||
import { UpperCodeSelectDialog } from "./UpperCodeSelectDialog";
|
||||
import PopulationStatusChart, { type PopulationChartData } from "./PopulationStatusChart";
|
||||
|
||||
const TAB_LIST = [
|
||||
{ key: "domestic", label: "행정인구등록" },
|
||||
@@ -59,6 +61,10 @@ const renewalAddedColumnInfo: ColumnInfo = {
|
||||
columns: AdmdstRenewalAddedColumnConfig.columns ?? [],
|
||||
columnLayout: AdmdstRenewalAddedColumnConfig.columnLayout ?? [],
|
||||
};
|
||||
const populationStatusColumnInfo: ColumnInfo = {
|
||||
columns: PopulationStatusColumnConfig.columns ?? [],
|
||||
columnLayout: PopulationStatusColumnConfig.columnLayout ?? [],
|
||||
};
|
||||
|
||||
/* ── 엑셀 헤더 검증 ── */
|
||||
const validateExcelHeader = (file: File, requiredKeywords: string[]): Promise<boolean> => {
|
||||
@@ -107,6 +113,21 @@ const StatisticsPopulationManagement = () => {
|
||||
const [renewalReducedRowData, setRenewalReducedRowData] = useState<Record<string, string | number>[]>([]);
|
||||
const [renewalAddedRowData, setRenewalAddedRowData] = useState<Record<string, string | number>[]>([]);
|
||||
|
||||
/* ── 행정인구현황 데이터 ── */
|
||||
const [statusRowData, setStatusRowData] = useState<Record<string, string | number>[]>([]);
|
||||
const [statusOMale, setStatusOMale] = useState(true);
|
||||
const [statusOFemale, setStatusOFemale] = useState(true);
|
||||
const [statusFMale, setStatusFMale] = useState(true);
|
||||
const [statusFFemale, setStatusFFemale] = useState(true);
|
||||
|
||||
/* ── 행정인구현황 시각화 데이터 ── */
|
||||
const [vizYear, setVizYear] = useState(String(new Date().getFullYear() - 1));
|
||||
const [vizOMale, setVizOMale] = useState(true);
|
||||
const [vizOFemale, setVizOFemale] = useState(true);
|
||||
const [vizFMale, setVizFMale] = useState(true);
|
||||
const [vizFFemale, setVizFFemale] = useState(true);
|
||||
const [chartData, setChartData] = useState<PopulationChartData | null>(null);
|
||||
|
||||
/* ── 상위 코드 다이얼로그 ── */
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [candidates, setCandidates] = useState<UpperCodeCandidateItem[]>([]);
|
||||
@@ -340,6 +361,38 @@ const StatisticsPopulationManagement = () => {
|
||||
}
|
||||
};
|
||||
|
||||
/* ════════════════ 행정인구현황 시각화 ════════════════ */
|
||||
const handleVizSearch = async (yr: string) => {
|
||||
if (!yr) { alert("연도를 선택해주세요."); return; }
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const res = await statisticsPpltnApi.getChartDatas1(yr, vizOMale, vizOFemale, vizFMale, vizFFemale);
|
||||
if (res.status === "success") {
|
||||
setChartData(res.data as PopulationChartData);
|
||||
} else {
|
||||
alert(res.message || "조회 중 오류가 발생했습니다.");
|
||||
}
|
||||
} catch {
|
||||
alert("서버 연결 중 오류가 발생했습니다.");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleVizSelectAll = () => {
|
||||
setVizOMale(true);
|
||||
setVizOFemale(true);
|
||||
setVizFMale(true);
|
||||
setVizFFemale(true);
|
||||
};
|
||||
|
||||
const handleVizDeselectAll = () => {
|
||||
setVizOMale(false);
|
||||
setVizOFemale(false);
|
||||
setVizFMale(false);
|
||||
setVizFFemale(false);
|
||||
};
|
||||
|
||||
/* ════════════════ 공통 ════════════════ */
|
||||
const handleSearch = async (values: SearchFormValues) => {
|
||||
lastSearchedValuesRef.current = values;
|
||||
@@ -375,6 +428,32 @@ const StatisticsPopulationManagement = () => {
|
||||
}
|
||||
} else if (activeTab === "renewal") {
|
||||
await loadRenewalData(values);
|
||||
} else if (activeTab === "status") {
|
||||
const yr = values.year ?? "";
|
||||
if (!yr) { alert("연도를 선택해주세요."); return; }
|
||||
if (!statusOMale && !statusOFemale && !statusFMale && !statusFFemale) {
|
||||
alert("체크 박스를 모두 해제하신 상태에서는\n조회가 불가능합니다.\n(최소 1개 이상의 체크 박스 선택 후\n조회를 진행해주세요.)");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const res = await statisticsPpltnApi.searchPopByEachCondition(
|
||||
yr, statusOMale, statusOFemale, statusFMale, statusFFemale,
|
||||
);
|
||||
if (res.status === "success") {
|
||||
setStatusRowData(toGridRows((res.data as object[]) ?? []));
|
||||
} else {
|
||||
alert(res.message || "조회 중 오류가 발생했습니다.");
|
||||
}
|
||||
} catch {
|
||||
alert("서버 연결 중 오류가 발생했습니다.");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
} else if (activeTab === "statusViz") {
|
||||
const yr = values.year ?? "";
|
||||
setVizYear(yr);
|
||||
await handleVizSearch(yr);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -385,6 +464,17 @@ const StatisticsPopulationManagement = () => {
|
||||
setForeignRightRowData([]);
|
||||
setRenewalReducedRowData([]);
|
||||
setRenewalAddedRowData([]);
|
||||
setStatusRowData([]);
|
||||
setStatusOMale(true);
|
||||
setStatusOFemale(true);
|
||||
setStatusFMale(true);
|
||||
setStatusFFemale(true);
|
||||
setVizYear(String(new Date().getFullYear() - 1));
|
||||
setVizOMale(true);
|
||||
setVizOFemale(true);
|
||||
setVizFMale(true);
|
||||
setVizFFemale(true);
|
||||
setChartData(null);
|
||||
setActiveTab("domestic");
|
||||
lastSearchedValuesRef.current = null;
|
||||
};
|
||||
@@ -483,7 +573,57 @@ const StatisticsPopulationManagement = () => {
|
||||
<input ref={domesticFileInputRef} type="file" accept=".xlsx,.xls,.csv" className="hidden" onChange={handleDomesticFileChange} />
|
||||
<input ref={foreignFileInputRef} type="file" accept=".xlsx,.xls,.csv" className="hidden" onChange={handleForeignFileChange} />
|
||||
|
||||
<SearchForm sido={false} gugun={false} onSubmit={handleSearch} onReset={handleReset} />
|
||||
<div className="relative">
|
||||
<SearchForm sido={false} gugun={false} onSubmit={handleSearch} onReset={handleReset} />
|
||||
{activeTab === "status" && (
|
||||
<div className="absolute top-0 left-[16rem] h-full flex items-center gap-6 px-4">
|
||||
<label className="text-[1.3rem] font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-label cursor-pointer flex items-center gap-1.5">
|
||||
<input type="checkbox" checked={statusOMale} onChange={(e) => setStatusOMale(e.target.checked)} className="size-4" />
|
||||
내국인 남성 포함
|
||||
</label>
|
||||
<label className="text-[1.3rem] font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-label cursor-pointer flex items-center gap-1.5">
|
||||
<input type="checkbox" checked={statusOFemale} onChange={(e) => setStatusOFemale(e.target.checked)} className="size-4" />
|
||||
내국인 여성 포함
|
||||
</label>
|
||||
<label className="text-[1.3rem] font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-label cursor-pointer flex items-center gap-1.5">
|
||||
<input type="checkbox" checked={statusFMale} onChange={(e) => setStatusFMale(e.target.checked)} className="size-4" />
|
||||
외국인 남성 포함
|
||||
</label>
|
||||
<label className="text-[1.3rem] font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-label cursor-pointer flex items-center gap-1.5">
|
||||
<input type="checkbox" checked={statusFFemale} onChange={(e) => setStatusFFemale(e.target.checked)} className="size-4" />
|
||||
외국인 여성 포함
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{activeTab === "statusViz" && (
|
||||
<div className="absolute top-0 left-[16rem] h-full flex items-center gap-5 px-4 flex-nowrap">
|
||||
<label className="text-[1.3rem] font-medium leading-none text-label cursor-pointer flex items-center gap-1.5 shrink-0 whitespace-nowrap">
|
||||
<input type="checkbox" checked={vizOMale} onChange={(e) => setVizOMale(e.target.checked)} className="size-4" />
|
||||
내국인 남성
|
||||
</label>
|
||||
<label className="text-[1.3rem] font-medium leading-none text-label cursor-pointer flex items-center gap-1.5 shrink-0 whitespace-nowrap">
|
||||
<input type="checkbox" checked={vizOFemale} onChange={(e) => setVizOFemale(e.target.checked)} className="size-4" />
|
||||
내국인 여성
|
||||
</label>
|
||||
<label className="text-[1.3rem] font-medium leading-none text-label cursor-pointer flex items-center gap-1.5 shrink-0 whitespace-nowrap">
|
||||
<input type="checkbox" checked={vizFMale} onChange={(e) => setVizFMale(e.target.checked)} className="size-4" />
|
||||
외국인 남성
|
||||
</label>
|
||||
<label className="text-[1.3rem] font-medium leading-none text-label cursor-pointer flex items-center gap-1.5 shrink-0 whitespace-nowrap">
|
||||
<input type="checkbox" checked={vizFFemale} onChange={(e) => setVizFFemale(e.target.checked)} className="size-4" />
|
||||
외국인 여성
|
||||
</label>
|
||||
<Button type="button" color="primary" onClick={handleVizSelectAll}
|
||||
className="px-[1rem] py-[0.5rem] text-[1.2rem] shrink-0 whitespace-nowrap">
|
||||
전체선택
|
||||
</Button>
|
||||
<Button type="button" color="primary" onClick={handleVizDeselectAll}
|
||||
className="px-[1rem] py-[0.5rem] text-[1.2rem] shrink-0 whitespace-nowrap">
|
||||
전체해제(총 인구만 조회)
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
|
||||
@@ -510,7 +650,7 @@ const StatisticsPopulationManagement = () => {
|
||||
엑셀 업로드 자료 (내국인 전체 리스트)
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-end mb-[0.4rem]">
|
||||
<div className="flex justify-end pr-2 mb-[0.4rem]">
|
||||
<Button type="button" color="red" onClick={() => domesticFileInputRef.current?.click()}
|
||||
className="mt-[0.5rem] px-[3rem] py-[0.8rem] text-[1.4rem]">
|
||||
엑셀 업로드
|
||||
@@ -565,17 +705,19 @@ const StatisticsPopulationManagement = () => {
|
||||
<TabsContent value="foreign">
|
||||
<div className="flex gap-2">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center mb-[0.4rem]">
|
||||
<span className="text-[1.8rem] font-semibold text-gray-700 before:content-['●'] before:mr-1 before:text-blue-500">
|
||||
엑셀 업로드 자료 (외국인 전체 리스트)
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-end mb-[0.4rem]">
|
||||
<Button type="button" color="red" onClick={() => foreignFileInputRef.current?.click()}
|
||||
className="mt-[0.5rem] px-[3rem] py-[0.8rem] text-[1.4rem]">
|
||||
엑셀 업로드
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center mb-[0.4rem]">
|
||||
<span className="text-[1.8rem] font-semibold text-gray-700 before:content-['●'] before:mr-1 before:text-blue-500">
|
||||
엑셀 업로드 자료 (외국인 전체 리스트)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* pr-8 (오른쪽 패딩)을 추가하여 버튼을 왼쪽으로 밀어냅니다 */}
|
||||
<div className="flex justify-end pr-2 mb-[0.4rem]">
|
||||
<Button type="button" color="red" onClick={() => foreignFileInputRef.current?.click()}
|
||||
className="mt-[0.5rem] px-[3rem] py-[0.8rem] text-[1.4rem]">
|
||||
엑셀 업로드
|
||||
</Button>
|
||||
</div>
|
||||
<DataGrid
|
||||
columnInfo={foreignLeftColumnInfo}
|
||||
rowStartNum={ForeignPopulationLeftColumnConfig.rowStartNum}
|
||||
@@ -624,12 +766,12 @@ const StatisticsPopulationManagement = () => {
|
||||
<TabsContent value="renewal">
|
||||
<div className="flex gap-2">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center mb-[0.4rem]">
|
||||
<div className="flex items-center mb-[1.1rem]">
|
||||
<span className="text-[1.8rem] font-semibold text-gray-700 before:content-['●'] before:mr-1 before:text-blue-500">
|
||||
감축된 행정구역(범위:동읍면) 자료
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-[3.9rem]" />
|
||||
<div className="h-[3.4rem]" />
|
||||
<DataGrid
|
||||
columnInfo={renewalReducedColumnInfo}
|
||||
rowStartNum={AdmdstRenewalReducedColumnConfig.rowStartNum}
|
||||
@@ -675,10 +817,36 @@ const StatisticsPopulationManagement = () => {
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="status">
|
||||
<div className="p-6 text-sm text-gray-400">행정인구현황 구현 예정</div>
|
||||
{/* 그리드 라벨 */}
|
||||
<div className="flex items-center mb-[1rem]">
|
||||
<span className="text-[1.8rem] font-semibold text-gray-700 before:content-['●'] before:mr-1 before:text-blue-500">
|
||||
행정 인구 현황
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<DataGrid
|
||||
columnInfo={populationStatusColumnInfo}
|
||||
rowStartNum={PopulationStatusColumnConfig.rowStartNum}
|
||||
excelExportFileSuffix="행정인구현황"
|
||||
rowData={statusRowData}
|
||||
setRowData={setStatusRowData}
|
||||
isExcelImport={false}
|
||||
isExcelExport={false}
|
||||
showHeaderCheckbox={false}
|
||||
fixedColumns={PopulationStatusColumnConfig.fixedColumns}
|
||||
enableSort={false}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
{/* ── 행정인구현황 시각화 탭 ── */}
|
||||
<TabsContent value="statusViz">
|
||||
<div className="p-6 text-sm text-gray-400">행정인구현황 시각화 구현 예정</div>
|
||||
<span className="inline-block text-[1.8rem] font-semibold text-gray-700 before:content-['●'] before:mr-1 before:text-blue-500 mb-[1.5rem]">
|
||||
행정인구현황 시각화 (기준연도 ~ 과거 자료(최대 5년) / *단위: 명)
|
||||
</span>
|
||||
|
||||
<div className="w-full h-[77rem]">
|
||||
<PopulationStatusChart chartData={chartData} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { ValueType } from "realgrid";
|
||||
import type { ColumnInfoList } from "@/components/type/DataGrid.type";
|
||||
|
||||
/* ══════════════════════════════════════════════════
|
||||
16. 통계인구관리 > 행정인구현황 탭 컬럼 설정
|
||||
══════════════════════════════════════════════════ */
|
||||
|
||||
export const PopulationStatusColumnConfig: ColumnInfoList = {
|
||||
tabKey: "POPULATION_STATUS",
|
||||
tabLabel: "행정 인구 현황",
|
||||
rowStartNum: 1,
|
||||
fixedColumns: 3,
|
||||
columns: [
|
||||
{
|
||||
name: "statsYr",
|
||||
fieldName: "statsYr",
|
||||
header: { text: "년도" },
|
||||
width: 90,
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly",
|
||||
},
|
||||
{
|
||||
name: "admdstCd",
|
||||
fieldName: "admdstCd",
|
||||
header: { text: "행정구역코드" },
|
||||
width: 140,
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly",
|
||||
},
|
||||
{
|
||||
name: "admdstNm",
|
||||
fieldName: "admdstNm",
|
||||
header: { text: "행정구역명" },
|
||||
width: 200,
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly",
|
||||
},
|
||||
{
|
||||
name: "wholPpltnCnt",
|
||||
fieldName: "wholPpltnCnt",
|
||||
header: { text: "총인구" },
|
||||
width: 110,
|
||||
valueType: ValueType.NUMBER,
|
||||
dataFormat: "#,##0",
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly",
|
||||
},
|
||||
{
|
||||
name: "domplWholPpltnCnt",
|
||||
fieldName: "domplWholPpltnCnt",
|
||||
header: { text: "내국인 소계" },
|
||||
width: 110,
|
||||
valueType: ValueType.NUMBER,
|
||||
dataFormat: "#,##0",
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly",
|
||||
},
|
||||
{
|
||||
name: "domplMlPpltnCnt",
|
||||
fieldName: "domplMlPpltnCnt",
|
||||
header: { text: "내국인 남성" },
|
||||
width: 110,
|
||||
valueType: ValueType.NUMBER,
|
||||
dataFormat: "#,##0",
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly",
|
||||
},
|
||||
{
|
||||
name: "domplFmlPpltnCnt",
|
||||
fieldName: "domplFmlPpltnCnt",
|
||||
header: { text: "내국인 여성" },
|
||||
width: 110,
|
||||
valueType: ValueType.NUMBER,
|
||||
dataFormat: "#,##0",
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly",
|
||||
},
|
||||
{
|
||||
name: "frgnrWholPpltnCnt",
|
||||
fieldName: "frgnrWholPpltnCnt",
|
||||
header: { text: "외국인 소계" },
|
||||
width: 110,
|
||||
valueType: ValueType.NUMBER,
|
||||
dataFormat: "#,##0",
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly",
|
||||
},
|
||||
{
|
||||
name: "frgnrMlPpltnCnt",
|
||||
fieldName: "frgnrMlPpltnCnt",
|
||||
header: { text: "외국인 남성" },
|
||||
width: 110,
|
||||
valueType: ValueType.NUMBER,
|
||||
dataFormat: "#,##0",
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly",
|
||||
},
|
||||
{
|
||||
name: "frgnrFmlPpltnCnt",
|
||||
fieldName: "frgnrFmlPpltnCnt",
|
||||
header: { text: "외국인 여성" },
|
||||
width: 110,
|
||||
valueType: ValueType.NUMBER,
|
||||
dataFormat: "#,##0",
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly",
|
||||
},
|
||||
],
|
||||
columnLayout: [
|
||||
"statsYr",
|
||||
"admdstCd",
|
||||
"admdstNm",
|
||||
"wholPpltnCnt",
|
||||
"domplWholPpltnCnt",
|
||||
"domplMlPpltnCnt",
|
||||
"domplFmlPpltnCnt",
|
||||
"frgnrWholPpltnCnt",
|
||||
"frgnrMlPpltnCnt",
|
||||
"frgnrFmlPpltnCnt",
|
||||
],
|
||||
};
|
||||
@@ -8,6 +8,20 @@ import type {
|
||||
DomesticPopulationMappingItem,
|
||||
} from "../model/ppltn/statistics-domestic-population-interface";
|
||||
|
||||
// 1. 응답 데이터의 타입을 정의합니다 (이미 정의된 것이 있다면 그것을 사용하세요)
|
||||
interface ChartDataResponse {
|
||||
status: string;
|
||||
data: {
|
||||
X_AXIS: string[];
|
||||
TOT: number[];
|
||||
ORIGIN_MALE: (number | null)[];
|
||||
ORIGIN_FEMALE: (number | null)[];
|
||||
FOREIGN_MALE: (number | null)[];
|
||||
FOREIGN_FEMALE: (number | null)[];
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export class StatisticsPpltnApiService {
|
||||
|
||||
/** 내국인 엑셀 임시 업로드 */
|
||||
@@ -154,6 +168,52 @@ export class StatisticsPpltnApiService {
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════
|
||||
행정인구현황
|
||||
════════════════════════════════════ */
|
||||
|
||||
/** 행정인구현황 — 체크박스 조건에 따른 인구 현황 조회 */
|
||||
public async searchPopByEachCondition(
|
||||
statsYr: string,
|
||||
oMale: boolean,
|
||||
oFemale: boolean,
|
||||
fMale: boolean,
|
||||
fFemale: boolean,
|
||||
): Promise<{ status: string; data: object[]; message?: string }> {
|
||||
const response = await stClient.post<{ status: string; data: object[]; message?: string }>(
|
||||
`${ENDPOINT.ST_SERVICE.PPLTN}/admdst-renewal/pop-by-condition`,
|
||||
{
|
||||
statsYr,
|
||||
oMale: oMale ? "TRUE" : "FALSE",
|
||||
oFemale: oFemale ? "TRUE" : "FALSE",
|
||||
fMale: fMale ? "TRUE" : "FALSE",
|
||||
fFemale: fFemale ? "TRUE" : "FALSE",
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/** 행정인구현황 시각화 — 기준연도 기준 최대 5년치 차트 데이터 조회 */
|
||||
public async getChartDatas1(
|
||||
statsYr: string,
|
||||
oMale: boolean,
|
||||
oFemale: boolean,
|
||||
fMale: boolean,
|
||||
fFemale: boolean,
|
||||
): Promise<ChartDataResponse> {
|
||||
const response = await stClient.post<ChartDataResponse>(
|
||||
`${ENDPOINT.ST_SERVICE.PPLTN}/admdst-renewal/chart-datas`,
|
||||
{
|
||||
statsYr,
|
||||
oMale: oMale ? "TRUE" : "FALSE",
|
||||
oFemale: oFemale ? "TRUE" : "FALSE",
|
||||
fMale: fMale ? "TRUE" : "FALSE",
|
||||
fFemale: fFemale ? "TRUE" : "FALSE",
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user