
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
File name
Commit message
Commit date
<template>
<div class="modal-wrapper">
<div class="modal-container large-modal">
<div class="modal-title flex justify-between align-center">
<h2>데이터베이스 읽기</h2>
<button class="close-btn" @click="$emit('onClose')">
<svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon>
</button>
</div>
<div class="modal-content-monthly">
<!-- 페이지 1: 데이터베이스 연결 설정 -->
<template v-if="currentPage == 1">
<!-- 데이터베이스 정보 -->
<div class="content-titleZone flex justy justify-between align-center">
<p class="box-title">데이터베이스 정보</p>
</div>
<table class="form-table">
<colgroup>
<col style="width: 20%" />
<col style="width: 80%" />
</colgroup>
<tbody>
<tr>
<th>연계정보 타입</th>
<td>
<div class="input-container flex">
<label class="radio-label">
<input type="radio" name="radio" :value="false" class="custom-radiobox" @change="onConnectionTypeChange(false)" v-model="jobItm.itm_option_bool" />
<span>직접입력</span>
</label>
<label class="radio-label">
<input type="radio" name="radio" :value="true" class="custom-radiobox" @change="onConnectionTypeChange(true)" v-model="jobItm.itm_option_bool" />
<span>불러오기</span>
</label>
</div>
</td>
</tr>
<!-- 연계정보 불러오기 선택 시 -->
<tr v-show="jobItm.itm_option_bool">
<th>연계정보</th>
<td>
<input type="text" class="half-input" disabled :value="getConnectionDisplayText()" />
<button class="blue-border-btn small-btn" @click="dbConSearchOpen(true)">검색</button>
</td>
</tr>
<tr>
<th>DMBS</th>
<td>
<select id="databaseType" @change="successAt = false" class="square-select half-input" v-model="currentConnectionDB.databaseType" :disabled="jobItm.itm_option_bool">
<option v-for="(itm, index) in databaseTypeList" :key="index" :value="itm.key">{{ itm.value }}</option>
</select>
</td>
</tr>
<tr>
<th>IP</th>
<td>
<input id="conectIp" type="text" @input="successAt = false" class="half-input" v-model="currentConnectionDB.conectIp" placeholder="ex. 127.0.0.1" :disabled="jobItm.itm_option_bool" />
</td>
</tr>
<tr>
<th>PORT</th>
<td>
<input id="conectPort" type="text" @input="successAt = false" class="half-input" v-model="currentConnectionDB.conectPort" :disabled="jobItm.itm_option_bool" />
</td>
</tr>
<tr>
<th>DB 명</th>
<td>
<input id="databaseNm" type="text" @input="successAt = false" class="half-input" v-model="currentConnectionDB.databaseNm" placeholder="데이터베이스명 OR 스키마명" :disabled="jobItm.itm_option_bool" />
</td>
</tr>
<tr>
<th>접속ID</th>
<td>
<input type="text" class="half-input" @input="successAt = false" v-model="currentConnectionDB.userId" placeholder="접속 ID" :disabled="jobItm.itm_option_bool" />
</td>
</tr>
<tr>
<th>접속PW</th>
<td>
<input type="password" class="half-input" @input="successAt = false" v-model="currentConnectionDB.userPassword" placeholder="접속 PW" autocomplete="new-password" :disabled="jobItm.itm_option_bool" />
</td>
</tr>
</tbody>
</table>
<!-- 데이터베이스 연결 결과 -->
<div class="content-titleZone flex justy justify-between align-center mt20">
<p class="box-title">데이터베이스 연결 결과</p>
<button class="blue-border-btn small-btn" @click="dataBaseConnectionCheck">접속확인</button>
</div>
<div class="table-zone">
<table class="list-table">
<colgroup>
<col width="10%" />
<col width="70%" />
<col width="20%" />
</colgroup>
<thead>
<tr>
<th>No</th>
<th>접속결과</th>
<th>접속시간</th>
</tr>
</thead>
<tbody>
<tr v-for="(itm, idx) in resultMessage" :key="idx">
<td>{{ idx + 1 }}</td>
<td>{{ itm.message }}</td>
<td>{{ itm.time }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<!-- 페이지 2: 쿼리 관리 -->
<template v-if="currentPage == 2">
<div class="flex content-box" style="min-height: 60dvh; flex-wrap: nowrap;">
<div class="content-box pd10" style="width: 20%;">
<div class="content-titleZone flex justy justify-between align-center" style="height: 45px">
<p class="box-title">데이터베이스 정보</p>
</div>
<div class="content-zone2">
<ul class="content-list" v-if="tableList.length > 0">
<li class="cursor" v-for="(item, indx) in tableList" :key="indx">
<a @click="getTableData(item)" :class="{
'file-list': true,
selected: selectTable === item,
}">
<div class="flex align-center">
<p>{{ getTableDisplayName(item) }}</p>
</div>
</a>
</li>
</ul>
</div>
</div>
<div class="content-box" style="width: 80%;">
<div class="flex-column">
<div class="content-box pd10" style="height: 50%;">
<div class="content-titleZone flex justy justify-between align-center" style="height: 45px">
<p class="box-title">쿼리 작업</p>
<button class="icon-btn" @click="executeQuery" title="실행">
<svg-icon type="mdi" :path="playPath" :color="'#fbbe28'"></svg-icon>
</button>
</div>
<div class="flex" style="height: calc(100% - 60px)">
<textarea style="resize: none; width: 100%; height: 100%; padding: 10px;" v-model="jobItm.itm.query"></textarea>
</div>
</div>
<div class="content-box pd10" style="height: 50%;">
<ul class="tab-nav flex justify-start">
<li @click="showTab('tab1')">
<a href="#tab01" :class="{ activeTab: activeTab === 'tab1' }">작업결과</a>
</li>
<li @click="showTab('tab2')">
<a href="#tab02" :class="{ activeTab: activeTab === 'tab2' }">작업log</a>
</li>
</ul>
<div v-show="activeTab === 'tab1'" class="content-box" style="height: calc(100% - 45px); padding: 10px;">
<div class="count-zone mb10" v-if="jobItm.dataTable.columnDatas.length > 0">
<p>총 <span>{{ jobItm.dataTable.totalRows }}</span>건 중 <span>{{ jobItm.dataTable.rowData.length }}</span>건 조회</p>
</div>
<div style="height: calc(100% - 15px); overflow: auto;">
<table class="list-table">
<thead>
<tr v-if="jobItm.dataTable.columnDatas.length > 0">
<th v-for="(itm, indx) in jobItm.dataTable.columnDatas" :key="indx" style="min-width: 150px !important"> {{ itm.columnNm }} <label class="check-label">
<input type="checkbox" class="custom-checkbox" v-model="itm.pkAt" />
</label>
</th>
</tr>
</thead>
<tbody v-if="jobItm.dataTable.rowData.length > 0">
<tr v-for="(row, rows) in jobItm.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>
</table>
</div>
</div>
<div v-show="activeTab === 'tab2'" class=" content-box" style="height: calc(100% - 45px); padding: 10px;">
<div style="height: calc(100% - 15px); overflow: auto;">
<table class="list-table">
<colgroup>
<col width="10%" />
<col width="60%" />
<col width="20%" />
<col width="10%" />
</colgroup>
<thead>
<tr>
<th>No</th>
<th>접속내용</th>
<th>접속시간</th>
<th>접속결과</th>
</tr>
</thead>
<tbody>
<tr v-for="(itm, indx) in executeMessage" :key="indx">
<td>{{ indx + 1 }}</td>
<td>{{ itm.message }}</td>
<td>{{ itm.time }}</td>
<td>{{ itm.result }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
</div>
<!-- 모달 하단 버튼 영역 -->
<div class="modal-end flex justify-end">
<button class="blue-border-btn small-btn" v-if="currentPage == 2" @click="moveTo('prev')">이전</button>
<button class="blue-border-btn small-btn" v-if="currentPage == 1" @click="moveTo('next')">다음</button>
<button class="blue-btn small-btn" v-if="isDataSet" @click="fnSave">등록</button>
<button class="blue-border-btn small-btn" @click="$emit('onClose')">취소</button>
</div>
</div>
</div>
<DBConSearch :openPopup="openSearchModal" @modalclose="dbConSearchOpen" @selectItm="selectDbcon" />
</template>
<script>
import _ from "lodash";
import axios from "axios";
// icon용 svg import
import SvgIcon from "@jamescoyle/vue-icon";
import { mdiMagnify, mdiClose, mdiPlay } from "@mdi/js";
// 컴포넌트
import DBConSearch from "../../dataComponent/DbConnectionSearchModal.vue";
export default {
name: "DatabaseConnectionModal",
components: {
SvgIcon,
DBConSearch
},
props: {
currentJobItm: String,
jobItem: Object
},
data() {
return {
// icon용 svg path
searchPath: mdiMagnify,
closePath: mdiClose,
playPath: mdiPlay,
// 페이지 관련
currentPage: 1,
activeTab: "tab1",
// 상태 관리
successAt: false, // 연결 성공 여부
isDataSet: false, // 데이터 설정 여부 추가
// 데이터 객체들
jobItm: {
itm: null,
itm_option_bool: false,
dataTable: {
columnDatas: [],
rowData: [],
totalRows: 0,
perPage: 10,
query: ""
}
},
// 연계정보 관리
openSearchModal: false,
linkConnectionDB: {},
inputConnectionDB: {
databaseType: "",
conectIp: "",
conectPort: "",
databaseNm: "",
userId: "",
userPassword: ""
},
// 현재 표시되는 연결 정보 (직접입력/불러오기 모두 대응)
currentConnectionDB: {
databaseType: "",
conectIp: "",
conectPort: "",
databaseNm: "",
userId: "",
userPassword: ""
},
// 데이터 목록
databaseTypeList: [],
tableList: [],
selectTable: {},
// 로그 메시지
resultMessage: [],
executeMessage: []
};
},
watch: {
jobItem: {
handler(newVal) {
if (newVal) {
this.jobItm = _.cloneDeep(newVal);
if (this.jobItm.itm_option_bool) {
this.linkConnectionDB = this.jobItm.itm || {};
this.currentConnectionDB = _.cloneDeep(this.linkConnectionDB);
} else {
this.inputConnectionDB = this.jobItm.itm || {};
this.currentConnectionDB = _.cloneDeep(this.inputConnectionDB);
}
}
},
immediate: true
},
},
created() {
this.initializeComponent();
},
mounted() {
this.init();
},
methods: {
// 초기화 함수들
initializeComponent() {
if (!this.$isEmpty(this.currentJobItm)) {
let currentJobItm = JSON.parse(this.currentJobItm);
if (!this.$isEmpty(currentJobItm)) {
this.jobItm = _.cloneDeep(currentJobItm);
}
}
if (!this.jobItm.itm) {
this.jobItm.itm = _.cloneDeep(this.$getDefaultJobGroup().connectionDb || {});
}
},
async init() {
this.databaseTypeList = await this.$getDataBaseTypeList();
if (this.jobItm == null) {
this.jobItm = _.cloneDeep(this.$getDefaultJobGroup().node || {});
this.linkConnectionDB = _.cloneDeep(this.$getDefaultJobGroup().connectionDb || {});
this.inputConnectionDB = _.cloneDeep(this.$getDefaultJobGroup().connectionDb || {});
this.jobItm.itm = _.cloneDeep(this.$getDefaultJobGroup().connectionDb || {});
} else {
if (this.jobItm.itm_option_bool) {
this.linkConnectionDB = _.cloneDeep(this.jobItm.itm) || {};
this.currentConnectionDB = _.cloneDeep(this.linkConnectionDB);
} else {
this.inputConnectionDB = _.cloneDeep(this.jobItm.itm) || {};
this.currentConnectionDB = _.cloneDeep(this.inputConnectionDB);
}
}
if (!this.inputConnectionDB.databaseType && this.databaseTypeList.MARIADB) {
this.inputConnectionDB.databaseType = this.databaseTypeList.MARIADB.key;
if (!this.jobItm.itm_option_bool) {
this.currentConnectionDB.databaseType = this.databaseTypeList.MARIADB.key;
}
}
},
// 연결 타입 변경 시(직접입력/불러오기) 입력 필드 업데이트
onConnectionTypeChange(isImport) {
this.successAt = false;
if (isImport) {
// 불러오기 선택 시
this.currentConnectionDB = _.cloneDeep(this.linkConnectionDB);
} else {
// 직접입력 선택 시
this.currentConnectionDB = _.cloneDeep(this.inputConnectionDB);
}
},
// 유틸리티 함수들
getConnectionDisplayText() {
return this.linkConnectionDB.conectNm
? `${this.linkConnectionDB.conectNm}(${this.linkConnectionDB.conectIp})`
: "";
},
getTableDisplayName(item) {
return item.tableNmKr && item.tableNmKr !== "" ? item.tableNmKr : item.tableNm;
},
// 데이터베이스 연결 관리
dataBaseConnectionCheck() {
if (this.jobItm.itm_option_bool) {
// 불러오기 모드일 때 linkConnectionDB 사용
this.jobItm.itm = _.cloneDeep(this.linkConnectionDB);
} else {
// 직접입력 모드일 때 currentConnectionDB에서 업데이트된 값 가져옴
this.inputConnectionDB = _.cloneDeep(this.currentConnectionDB);
this.jobItm.itm = _.cloneDeep(this.inputConnectionDB);
}
this.jobItm.itm.loadAt = this.jobItm.itm_option_bool;
if (this.jobItm.itm.type) {
delete this.jobItm.itm.type;
}
axios({
url: "/data/dataBaseConnectionCheck.json",
method: "post",
headers: { "Content-Type": "application/json; charset=UTF-8" },
data: this.jobItm.itm
})
.then(response => {
const checkMessage = response.data.checkMessage || {};
this.successAt = checkMessage.success === true;
this.$emit("onDfltSetChange", checkMessage.success);
this.$showAlert("결과내용", checkMessage.message);
this.resultMessage.push({
time: this.$getFullTime(),
message: checkMessage.message,
result: checkMessage.success ? "접속성공" : "접속실패"
});
})
.catch(error => {
console.error("Database connection check error:", error);
this.successAt = false;
this.$emit("onDfltSetChange", false);
});
},
// 연계정보 관리
dbConSearchOpen(val) {
this.openSearchModal = val;
},
selectDbcon(dbcon) {
this.linkConnectionDB = _.cloneDeep(dbcon);
// 불러오기 모드이면 현재 표시되는 필드 업데이트
if (this.jobItm.itm_option_bool) {
this.currentConnectionDB = _.cloneDeep(this.linkConnectionDB);
}
},
getTableData(item) {
this.selectTable = item;
const requestData = {
dataset: { ...item, perPage: this.jobItm.dataTable.perPage },
connectionDB: { ...this.jobItm.itm, creatDt: null }
};
axios({
url: "/data/getTableData.json",
method: "post",
headers: { "Content-Type": "application/json; charset=UTF-8" },
data: requestData
})
.then(response => {
if (response.data.checkMessage.success) {
this.jobItm.dataTable = response.data.resultData.dataTable;
this.jobItm.itm_id = this.jobItm.itm.dbConectId;
this.jobItm.itm.query = this.jobItm.dataTable.query;
this.isDataSet = true;
} else {
this.isDataSet = false;
}
const checkMessage = response.data.resultData.dataTable.checkMessage || {};
this.executeMessage.push({
time: this.$getFullTime(),
message: checkMessage.message,
result: checkMessage.success ? "성공" : "실패"
});
})
.catch(error => {
console.error("Get table data error:", error);
this.isDataSet = false;
});
},
executeQuery() {
const requestData = {
dataTable: {
...this.jobItm.dataTable,
query: this.jobItm.itm.query
},
connectionDB: {
...this.jobItm.itm,
creatDt: null
}
};
axios({
url: "/data/getTableDataByQuery.json",
method: "post",
headers: { "Content-Type": "application/json; charset=UTF-8" },
data: requestData
})
.then(response => {
if (response.data.checkMessage.success) {
this.jobItm.dataTable = response.data.resultData.dataTable;
this.jobItm.itm_id = this.jobItm.itm.dbConectId;
this.jobItm.itm.query = this.jobItm.dataTable.query;
this.isDataSet = true;
} else {
this.isDataSet = false;
}
const checkMessage = response.data.resultData.dataTable.checkMessage || {};
this.executeMessage.push({
time: this.$getFullTime(),
message: checkMessage.message,
result: checkMessage.success ? "성공" : "실패"
});
})
.catch(error => {
console.error("Execute query error:", error);
this.isDataSet = false;
});
},
showTab(tabName) {
this.activeTab = tabName;
},
moveTo(type) {
if (type === 'prev') {
this.currentPage = 1;
this.tableList = []; // 초기화
this.jobItm.dataTable = {
columnDatas: [],
rowData: [],
totalRows: 0,
perPage: 10,
query: ""
}; // 초기화
} else if (type === 'next') {
if (!this.successAt) {
this.$showAlert("경고", "접속에 성공한 경우에만 진행할 수 있습니다.");
} else {
this.getDbConnectionTableList(); // 테이블 및 쿼리 관리
}
} else {
this.$showAlert("오류", "올바르지 않은 요청입니다.");
}
},
// 테이블 및 쿼리 관리
getDbConnectionTableList() {
axios({
url: "/data/selectTableList.json",
method: "post",
headers: { "Content-Type": "application/json; charset=UTF-8" },
data: this.jobItm.itm
})
.then(response => {
if (response.data.checkMessage.success) {
this.tableList = response.data.resultData.tableList || [];
this.currentPage = 2;
}
})
.catch(error => {
console.error("Get table list error:", error);
this.tableList = [];
});
},
// 저장
fnSave() {
this.$emit("onSave", this.jobItm);
}
}
};
</script>
<style>
.content-zone2 {
height: calc(100% - 57px);
max-height: calc(100% - 57px);
overflow-y: auto;
overflow-x: hidden;
}
</style>