
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
<template>
<div class="container" :style="isLoading ? { cursor: 'wait' } : {}">
<!-- 로딩 시 화면을 덮는 반투명 오버레이 -->
<div v-if="isLoading" class="loading-overlay">
<div class="loading-div">
<span>LOADING </span>
<span class="anima">.</span>
<span class="anima">.</span>
<span class="anima">.</span>
</div>
</div>
<div class="page-titleZone flex justify-between align-center">
<div class="flex align-center flex80">
<p class="main-title">데이터 메타 정보</p>
<button
class="small-btn blue-border-btn ml5"
@click="tableSpecificationAllDonwload"
>
테이블 정의서
</button>
</div>
<PageNavigation />
</div>
<div class="content-wrap">
<div class="content-box flex justify-between">
<div class="flex20 content-box">
<div class="left-content flex100 content-box">
<div class="content-titleZone">
<p class="box-title">데이터 목록</p>
</div>
<div class="filter-zone">
<select
name=""
id="categoryList"
class="full-select mb5"
v-model="selectedCategory"
>
<option value="">카테고리</option>
<option
v-for="(item, index) in categoryList"
:value="item.cmmnCode"
:key="index"
>
{{ item.codeNm }}
</option>
</select>
<select
name=""
id=""
class="full-select mb5"
v-model="selectSearchType"
>
<option value="post_sj">데이터셋 제목</option>
<option value="post_dc">데이터셋 내용</option>
</select>
<div
class="search-square flex justify-start align-center mb5 ml0"
>
<input
type="text"
class="square-input"
placeholder="Search"
@change="handleSearchInput"
@keyup.enter="selectCode"
style="width: calc(100% - 24px)"
/>
<button class="square-button blue-btn" @click="selectCode">
<svg-icon
type="mdi"
:path="this.$getIconPath()"
class="square-icon"
></svg-icon>
</button>
</div>
</div>
<div style="height: calc(100% - 140px)">
<DatasetList
:dataPostList="dataPostList"
@selectDatasetPost="selectDatasetPost"
/>
</div>
</div>
</div>
<div class="flex80 content-box" style="width: 80%">
<div class="right-content content-box">
<div class="content-box flex100">
<div class="content-titleZone flex justify-between align-center">
<p class="box-title">{{ selectDatasetTitle }}</p>
<div>
<button class="small-btn blue-btn" @click="selectPost">
데이터 보기
</button>
<button
class="small-btn blue-border-btn"
@click="tableSpecificationDonwload"
>
테이블 정의서
</button>
</div>
</div>
<div class="flex justify-end align-center">
<div class="flex justify-end">
<div class="flex justify-end align-center">
<select
name=""
id="searchItm1"
class="square-select ml5"
v-model="postSearch_data.key"
>
<option
v-for="(itm, index) in dataTable.columnDatas"
:value="itm.columnNm"
:key="index"
>
{{ itm.columnNm }}
</option>
</select>
<div
class="search-square ml5 flex justify-start align-center"
>
<input
type="text"
class="square-input"
placeholder="Search"
v-model="postSearch_data.value"
v-on:keyup.enter="datasetColSearch()"
/>
<button
class="square-button blue-btn"
@click="datasetColSearch()"
>
<svg-icon
type="mdi"
:path="this.$getIconPath()"
class="square-icon"
></svg-icon>
</button>
</div>
</div>
</div>
</div>
<div
class="overflow-y overflow-x mb20"
style="height: calc(50% - 55px)"
>
<div class="table-zone">
<table class="list-table">
<template v-if="dataTable.columnDatas != undefined">
<thead>
<th
v-for="(itm, indx) in dataTable.columnDatas"
:key="indx"
:style="changeWidth()"
>
{{ itm.columnNm }}
<button class="tp-btn" v-if="itm.pkAt">
<svg-icon
type="mdi"
:width="15"
:height="15"
:path="this.$getIconPath('mdiKeyVariant')"
:color="'#213f99'"
></svg-icon>
</button>
</th>
</thead>
<tbody>
<tr
v-for="(row, rows) in dataTable.rowData"
:key="rows"
>
<td
v-for="(itm, indx) in row"
:key="indx"
style="
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
>
{{ itm }}
</td>
</tr>
</tbody>
</template>
<tr v-else class="text-ct">
<td>등록된 데이터가 존재하지 않습니다.</td>
</tr>
</table>
<PaginationButton
v-model:currentPage="postSearch.currentPage"
:perPage="postSearch.perPage"
:totalCount="postSearch.totalRows"
:maxRange="5"
:click="selectDataPost"
/>
</div>
</div>
<div class="overflow-y" style="height: calc(50% - 55px)">
<div class="table-zone">
<table class="list-table">
<!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 -->
<colgroup>
<col style="width: 10%" />
<col style="width: 40%" />
<col style="width: 10%" />
<col style="width: 10%" />
<col style="width: 10%" />
<col style="width: 30%" />
</colgroup>
<thead>
<tr>
<th>순서</th>
<th>컬럼명</th>
<th>화면 표시용 컬럼명</th>
<th>원본 컬럼명</th>
<th>데이터 타입</th>
<th>표준 용어 여부</th>
</tr>
</thead>
<tbody>
<tr v-for="(itm, indx) in colDataList" :key="indx">
<td>
{{
indx +
1 +
(colSearch.currentPage - 1) * colSearch.perPage
}}
</td>
<td>{{ itm.columnNm }}</td>
<td>{{ itm.displyColumnNm }}</td>
<td>{{ itm.orginlColumnNm }}</td>
<td>{{ dataTypeList[itm.dataTy] }}</td>
<td>-</td>
</tr>
</tbody>
</table>
<PaginationButton
v-model:currentPage="colSearch.currentPage"
:perPage="colSearch.perPage"
:totalCount="colSearch.totalRows"
:maxRange="5"
:click="colSearchFunc"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import DatasetList from "../../component/common/Component_Dataset.vue";
import axios from "axios";
import PageNavigation from "../../component/PageNavigation.vue";
import SvgIcon from "@jamescoyle/vue-icon";
import PaginationButton from "../../component/PaginationButton.vue";
export default {
data() {
return {
dataPostList: [],
search: this.$getDefaultSerchVO(),
// 카테고리
search_data1: this.$getDefaultSerchItem("ctgry_id", "string"),
// 선택
search_data2: this.$getDefaultSerchItem("post_sj", "string"),
// 날짜
search_date: this.$getDefaultSerchItem("creat_dt", "dates"),
categoryList: [],
selectedCategory: "",
selectSearchType: "post_sj",
selectDatasetTitle: "",
// 선택한 데이터 셋 정보
datasetPost: {},
dataTable: [],
dataset: {},
postSearch: this.$getDefaultSerchVO(),
postSearch_data: this.$getDefaultSerchItem(null, "string"),
datasetPostId: null,
afterPostId: "",
colSearch: this.$getDefaultSerchVO(),
colDataList: [],
isLoading: true,
dataTypeList: {
STRING: "문자열",
LONG: "정수",
DOUBLE: "실수",
DATE: "날짜",
DATETIME: "날짜시간",
},
};
},
methods: {
changeWidth: function () {
return `width: ${100 / this.dataTable.columnDatas.length}%`;
},
// 데이터 목록 영역 기능
// 검색어 입력
handleSearchInput: function (e) {
this.search_data2.value = e.target.value;
},
//카테고리 선택 및 검색조건 설정 후 데이터 셋 조회
selectCode: function () {
this.search_data1.value = this.selectedCategory;
this.search_data2.key = this.selectSearchType;
this.searchData();
},
// 자식 컴포넌트에서 선택한 데이터 셋 정보
selectDatasetPost: function (item) {
this.selectDatasetTitle = item.post_sj;
this.datasetPostId = item.dataset_post_id;
this.postSearch_data.key = null;
this.postSearch_data.value = "";
this.selectDataPost();
},
// 데이터 셋 조회
searchData: function () {
const vm = this;
axios({
url: "/dataset/selectDataPostList.json",
method: "post",
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
data: JSON.stringify(vm.search),
})
.then(function (response) {
vm.isLoading = false; // 로딩 해제
if (response.data.checkMessage.success) {
vm.dataPostList = response.data.resultData.dataPostList;
vm.search.totalRows = response.data.resultData.totalRow;
}
vm.postSearch.currentPage = 1;
vm.$showAlert(
"데이터 메타 정보 연결",
"데이터 메타 정보 연결에 성공했습니다."
);
})
.catch(function (error) {
vm.isLoading = false; // 로딩 해제
vm.$showAlert(
"데이터 메타 정보 연결",
"데이터 메타 정보 연결 오류, 관리자에게 문의하세요."
);
vm.$router.go(-1); // 오류 시 이전 페이지로 이동
});
},
// 데이터 셋 조회 영역
// 데이터 보기 버튼 클릭 기능
selectPost: function () {
// this.$router.push("/dataPostDetail.page", {datapost: item});
this.datasetPostId = this.datasetPost.dataset_post_id;
this.$router.push({
name: "DataPostDetail",
query: { datapost: this.datasetPostId },
});
},
// 초기화
init: async function () {
this.search.searchObjectList.push(this.search_date);
this.search.searchObjectList.push(this.search_data1);
this.search.searchObjectList.push(this.search_data2);
this.postSearch.searchObjectList.push(this.postSearch_data);
this.categoryList = await this.$getCommonCode("DATA_CTGRY");
},
// 데이터 셋 정보 조회
selectDataPost: function () {
this.isLoading = true;
const vm = this;
if (vm.afterPostId != vm.datasetPostId) {
vm.postSearch.currentPage = 1;
}
axios({
url: "/dataset/selectDataPost.json",
method: "post",
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
data: {
datapost_id: vm.datasetPostId,
search: vm.postSearch,
},
})
.then(function (response) {
let res = response.data.resultData;
if (response.data.checkMessage.success) {
vm.datasetPost = res.datasetPost;
vm.dataTable = res.dataTable;
vm.dataset = res.dataset;
// 페이징 정보 설정
vm.postSearch.totalRows = vm.dataTable.totalRows;
vm.postSearch.perPage = vm.dataTable.perPage;
vm.postSearch.currentPage = vm.dataTable.currentPage;
vm.afterPostId = vm.datasetPostId;
vm.colSearch.totalRows = vm.dataTable.columnDatas.length;
vm.postSearch_data.key = vm.dataTable.columnDatas[0].columnNm;
vm.colSearchFunc();
vm.isLoading = false;
}
})
.catch(function (error) {
this.$showAlert(
"에러 발생",
"에러가 발생했습니다. 관리자에게 문의해 주세요."
);
});
},
// 테이블 정의서 all 다운로드
tableSpecificationAllDonwload: function () {
axios({
url: "/meta/management/tableSpecificationAllDownload",
method: "post",
responseType: "blob", // 바이너리 데이터를 위해 responseType을 'blob'으로 설정
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
})
.then(function (response) {
// ZIP 파일명 설정
const contentDisposition = response.headers["content-disposition"];
let fileName =
"데이터 셋 메타데이터 정의 목록 " +
new Date().toISOString().slice(0, 10).replace(/-/g, "") +
".zip";
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(
/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
);
if (fileNameMatch.length > 1) {
fileName = fileNameMatch[1].replace(/['"]/g, "");
}
}
// 엑셀 파일 다운로드 처리
const url = window.URL.createObjectURL(
new Blob([response.data], { type: "application/zip" })
);
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
})
.catch(function (error) {
this.$showAlert(
"에러 발생",
"에러가 발생했습니다. 관리자에게 문의해 주세요."
);
});
},
tableSpecificationDonwload: function () {
let vm = this;
axios({
url: "/meta/management/tableSpecificationDownload",
method: "post",
responseType: "blob",
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
data: vm.datasetPost,
})
.then(function (response) {
// 파일 이름을 추출하기 위한 처리 추가
const contentDisposition = response.headers["content-disposition"];
let fileName = "download.xlsx"; // 기본 파일 이름
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(
/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
);
if (fileNameMatch.length > 1) {
fileName = fileNameMatch[1].replace(/['"]/g, "");
}
}
// Blob 객체 생성 로직 수정
const blob = new Blob([response.data], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const blobUrl = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = blobUrl;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(blobUrl);
})
.catch(function (error) {
this.$showAlert(
"에러 발생",
"에러가 발생했습니다. 관리자에게 문의해 주세요."
);
});
},
datasetColSearch: function () {
if (
this.postSearch_data.key == null ||
this.postSearch_data.key == "검색조건"
) {
this.$showAlert("메시지", "검색조건을 선택해주세요.");
return;
}
this.postSearch.currentPage = 1;
this.selectDataPost();
},
colSearchFunc: function () {
const start = (this.colSearch.currentPage - 1) * this.colSearch.perPage;
const end = start + this.colSearch.perPage;
const colDatas = this.dataTable.columnDatas;
this.colDataList = colDatas.slice(start, end);
},
},
watch: {
selectDatasetPost: function (newVal, oldVal) {
this.selectDatasetTitle = newVal.post_sj;
},
colSearch: {
handler: function (newVal, oldVal) {
const start = (this.colSearch.currentPage - 1) * this.colSearch.perPage;
const end = start + this.colSearch.perPage;
this.colDataList = this.dataTable.columnDatas.slice(start, end);
},
deep: true,
},
},
computed: {
DatasetList,
},
components: {
DatasetList: DatasetList,
PageNavigation: PageNavigation,
PaginationButton: PaginationButton,
SvgIcon: SvgIcon,
},
mounted() {
this.init();
this.searchData();
},
};
</script>