- 시스템관리 > 관리자목록조회, 관리자등록, 관리자수정, 관리자삭제, 관리자상세조회

This commit is contained in:
kimre
2022-05-29 10:12:37 +09:00
parent ad80b88089
commit 5077696e46
275 changed files with 17338 additions and 23433 deletions

View File

@@ -1,45 +1,45 @@
<template>
<div class="container template_free">
<article id="content" class="content"></article>
<div tabindex="0" class="layer active">
<div tabindex="0" class="layer_cont error">
<div class="layer_body">
<div class="title_wrap center mar_b50">
<h5 class="h5_title" v-html="message">{{ message }}</h5>
</div>
<div class="btn_wrap mar_t20 center">
<a v-for="(branch, index) in branchList" :key="index" href="javascript:void(0);" class="btn mid" :class="branch.class" @click="branch.callback"><span>{{ branch.text }}</span></a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
/*
branchList: [
{
text: "저장",
class: "bd_black",
callback: callback
},
.....
]
*/
export default {
name: "buttonBranch",
props: {
message: String,
branchList: {
text: Array,
class: String,
callback: Function
}
},
data() {
return {
}
}
}
<template>
<div class="container template_free">
<article id="content" class="content"></article>
<div tabindex="0" class="layer active">
<div tabindex="0" class="layer_cont error">
<div class="layer_body">
<div class="title_wrap center mar_b50">
<h5 class="h5_title" v-html="message">{{ message }}</h5>
</div>
<div class="btn_wrap mar_t20 center">
<a v-for="(branch, index) in branchList" :key="index" href="javascript:void(0);" class="btn mid" :class="branch.class" @click="branch.callback"><span>{{ branch.text }}</span></a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
/*
branchList: [
{
text: "저장",
class: "bd_black",
callback: callback
},
.....
]
*/
export default {
name: "buttonBranch",
props: {
message: String,
branchList: {
text: Array,
class: String,
callback: Function
}
},
data() {
return {
}
}
}
</script>

View File

@@ -1,199 +1,199 @@
<template>
<section>
<!-- <div class="emulator_wrap"> -->
<div class="inner_emul">
<strong class="blind">미리보기</strong>
<div class="emulator_area">
<div class="emulator_cont">
<div class="img_area description">
<img :src="bgImageData" v-if="bgImageData && !retrivebgFlag">
<img :src="this.bgImageUrl" v-else-if="retrivebgFlag">
<img src="@/assets/images/common/img_placeholder02.png" v-else>
</div>
<div class="rcs_profile_area">
<img :src="profileImageData" v-if="profileImageData && !retriveProfileFlag">
<img :src="this.profileImageUrl" v-else-if="retriveProfileFlag">
<img src="@/assets/images/common/img_profile_blank.png" v-else>
</div>
<strong class="rcs_brand_name">{{this.brandInfoData.name}}</strong>
<div class="rcs_icon_area">
<span
v-for="(item, index) in visibleMenuItemList"
:key="index"
class="rcs_icon"
:class="`icon_${item.code.toLowerCase()}`"
></span>
</div>
<div class="rcs_desc_area" v-html="this.brandInfoData.descr"></div>
<div class="rcs_detail_area">
<dl>
<dt>전화번호</dt>
<dd>{{this.brandInfoData.tel}}</dd>
<dt>웹사이트</dt>
<dd>{{this.brandInfoData.url}}</dd>
<dt>이메일</dt>
<dd v-if="this.brandInfoData.email === '@'"></dd>
<dd v-else>{{this.brandInfoData.email}}</dd>
<dt>주소</dt>
<dd>{{this.brandInfoData.addrRegnNo}}{{this.brandInfoData.addrMngNo}}{{this.brandInfoData.addrDtl}}</dd>
</dl>
</div>
</div>
</div>
</div>
<!-- </div> -->
</section>
</template>
<script>
// 스크립트를 정의하는 부분
// 개발자 작업 영역
//import { getImageUrl } from '@/service/code'
// [ECMA6] export default 된 부분이 외부에서 import로 사용할 수 있게 된다.
export default {
// .vue 내부에서 사용되는 model
// model 기반으로 vue는 동작된다.
props: {
brandInfoData: {
type: Object
}
},
data() {
return {
bgImageData: '',
profileImageData: '',
profileImageUrl: '',
bgImageUrl: '',
retriveProfileFlag: false,
retrivebgFlag: false
}
},
created() {
// DOM이 만들어 지기 전 실행 되는 곳
// Data를 사전에 준비할 경우 사용된다.
},
mounted() {
// DOM에 해당 .vue가 들어가게 되면 실행 되는 곳
// onload 상태와 동일하다. DOM 이후에 조작할 경우 이곳에서 수행
},
watch: {
'brandInfoData.descr'() {
this.brandInfoData.descr = this.brandInfoData.descr.replace(/\(|\)|on.*\(|eval\(|javascript/gi,'')
.split('\n')
.join('<br />')
},
'brandInfoData.profileImgFile'() {
if (this.brandInfoData.profileImgFile) {
let reader = new FileReader()
let vm = this
let file = this.brandInfoData.profileImgFile
reader.onload = e => {
vm.profileImageData = e.target.result
}
reader.readAsDataURL(file)
} else {
this.profileImageData = ''
}
},
'brandInfoData.bgImgFile'() {
if (this.brandInfoData.bgImgFile) {
let reader = new FileReader()
let vm = this
let file = this.brandInfoData.bgImgFile
reader.onload = e => {
vm.bgImageData = e.target.result
}
reader.readAsDataURL(file)
} else {
this.bgImageData = ''
}
},
'brandInfoData.profileImgFileId'() {
if (
!jglib.isEmpty(this.brandInfoData.profileImgFileId) &&
!jglib.isEmpty(this.brandInfoData.profileImgFileNo)
) {
this.retriveProfileFlag = true
let reqObj = {
fileId: this.brandInfoData.profileImgFileId,
fileNo: this.brandInfoData.profileImgFileNo
}
getImageUrl(reqObj).then(res => {
this.profileImageUrl = res.downloadUrl
})
} else {
this.retriveProfileFlag = false
this.profileImageUrl = ''
}
},
'brandInfoData.bgImgFileId'() {
if (
!jglib.isEmpty(this.brandInfoData.bgImgFileId) &&
!jglib.isEmpty(this.brandInfoData.bgImgFileNo)
) {
this.retrivebgFlag = true
let reqObj = {
fileId: this.brandInfoData.bgImgFileId,
fileNo: this.brandInfoData.bgImgFileNo
}
getImageUrl(reqObj).then(res => {
this.bgImageUrl = res.downloadUrl
})
} else {
this.retrivebgFlag = false
this.bgImageUrl = ''
}
}
},
computed: {
// 값이 자주 변경됨에 따라, 관련되어 데이터 혹은 view가 바뀌어야 할 경우 사용
// method를 바로 연결하면 tic 단위로 계속 실행되기 때문에, 값이 변경할 때만 수행되고,
// cache로 남는 computed를 활용하는 것이 성능에 좋음
visibleMenuItemList() {
if (this.brandInfoData.menuItemList) {
return this.brandInfoData.menuItemList.filter(res => {
return res.visible
})
} else {
return []
}
}
},
methods: {
// .vue 내부에서 사용되는 함수를 정의한다.
// 이벤트에 따라 실행하거나, 내부적으로 사용되는 함수들을 정의한다.
getImageUrl: function(reqData) {
if (!isUseAPI()) {
return new Promise((resolve, reject) => {
let res = {
code: '99999999',
msg: 'not available in mockup'
}
resolve(res)
})
}
return new Promise((resolve, reject) => {
request({
url: `/file/${reqData.fileId}/${reqData.fileNo}`,
method: 'get'
})
.then(res => {
let imgData = {
fileName: res.result.fileName,
downloadUrl: res.result.downloadUrl
}
resolve(imgData)
})
.catch(res => {
reject('error in filedownload')
})
})
}
}
}
</script>
<template>
<section>
<!-- <div class="emulator_wrap"> -->
<div class="inner_emul">
<strong class="blind">미리보기</strong>
<div class="emulator_area">
<div class="emulator_cont">
<div class="img_area description">
<img :src="bgImageData" v-if="bgImageData && !retrivebgFlag">
<img :src="this.bgImageUrl" v-else-if="retrivebgFlag">
<img src="@/assets/images/common/img_placeholder02.png" v-else>
</div>
<div class="rcs_profile_area">
<img :src="profileImageData" v-if="profileImageData && !retriveProfileFlag">
<img :src="this.profileImageUrl" v-else-if="retriveProfileFlag">
<img src="@/assets/images/common/img_profile_blank.png" v-else>
</div>
<strong class="rcs_brand_name">{{this.brandInfoData.name}}</strong>
<div class="rcs_icon_area">
<span
v-for="(item, index) in visibleMenuItemList"
:key="index"
class="rcs_icon"
:class="`icon_${item.code.toLowerCase()}`"
></span>
</div>
<div class="rcs_desc_area" v-html="this.brandInfoData.descr"></div>
<div class="rcs_detail_area">
<dl>
<dt>전화번호</dt>
<dd>{{this.brandInfoData.tel}}</dd>
<dt>웹사이트</dt>
<dd>{{this.brandInfoData.url}}</dd>
<dt>이메일</dt>
<dd v-if="this.brandInfoData.email === '@'"></dd>
<dd v-else>{{this.brandInfoData.email}}</dd>
<dt>주소</dt>
<dd>{{this.brandInfoData.addrRegnNo}}{{this.brandInfoData.addrMngNo}}{{this.brandInfoData.addrDtl}}</dd>
</dl>
</div>
</div>
</div>
</div>
<!-- </div> -->
</section>
</template>
<script>
// 스크립트를 정의하는 부분
// 개발자 작업 영역
//import { getImageUrl } from '@/service/code'
// [ECMA6] export default 된 부분이 외부에서 import로 사용할 수 있게 된다.
export default {
// .vue 내부에서 사용되는 model
// model 기반으로 vue는 동작된다.
props: {
brandInfoData: {
type: Object
}
},
data() {
return {
bgImageData: '',
profileImageData: '',
profileImageUrl: '',
bgImageUrl: '',
retriveProfileFlag: false,
retrivebgFlag: false
}
},
created() {
// DOM이 만들어 지기 전 실행 되는 곳
// Data를 사전에 준비할 경우 사용된다.
},
mounted() {
// DOM에 해당 .vue가 들어가게 되면 실행 되는 곳
// onload 상태와 동일하다. DOM 이후에 조작할 경우 이곳에서 수행
},
watch: {
'brandInfoData.descr'() {
this.brandInfoData.descr = this.brandInfoData.descr.replace(/\(|\)|on.*\(|eval\(|javascript/gi,'')
.split('\n')
.join('<br />')
},
'brandInfoData.profileImgFile'() {
if (this.brandInfoData.profileImgFile) {
let reader = new FileReader()
let vm = this
let file = this.brandInfoData.profileImgFile
reader.onload = e => {
vm.profileImageData = e.target.result
}
reader.readAsDataURL(file)
} else {
this.profileImageData = ''
}
},
'brandInfoData.bgImgFile'() {
if (this.brandInfoData.bgImgFile) {
let reader = new FileReader()
let vm = this
let file = this.brandInfoData.bgImgFile
reader.onload = e => {
vm.bgImageData = e.target.result
}
reader.readAsDataURL(file)
} else {
this.bgImageData = ''
}
},
'brandInfoData.profileImgFileId'() {
if (
!jglib.isEmpty(this.brandInfoData.profileImgFileId) &&
!jglib.isEmpty(this.brandInfoData.profileImgFileNo)
) {
this.retriveProfileFlag = true
let reqObj = {
fileId: this.brandInfoData.profileImgFileId,
fileNo: this.brandInfoData.profileImgFileNo
}
getImageUrl(reqObj).then(res => {
this.profileImageUrl = res.downloadUrl
})
} else {
this.retriveProfileFlag = false
this.profileImageUrl = ''
}
},
'brandInfoData.bgImgFileId'() {
if (
!jglib.isEmpty(this.brandInfoData.bgImgFileId) &&
!jglib.isEmpty(this.brandInfoData.bgImgFileNo)
) {
this.retrivebgFlag = true
let reqObj = {
fileId: this.brandInfoData.bgImgFileId,
fileNo: this.brandInfoData.bgImgFileNo
}
getImageUrl(reqObj).then(res => {
this.bgImageUrl = res.downloadUrl
})
} else {
this.retrivebgFlag = false
this.bgImageUrl = ''
}
}
},
computed: {
// 값이 자주 변경됨에 따라, 관련되어 데이터 혹은 view가 바뀌어야 할 경우 사용
// method를 바로 연결하면 tic 단위로 계속 실행되기 때문에, 값이 변경할 때만 수행되고,
// cache로 남는 computed를 활용하는 것이 성능에 좋음
visibleMenuItemList() {
if (this.brandInfoData.menuItemList) {
return this.brandInfoData.menuItemList.filter(res => {
return res.visible
})
} else {
return []
}
}
},
methods: {
// .vue 내부에서 사용되는 함수를 정의한다.
// 이벤트에 따라 실행하거나, 내부적으로 사용되는 함수들을 정의한다.
getImageUrl: function(reqData) {
if (!isUseAPI()) {
return new Promise((resolve, reject) => {
let res = {
code: '99999999',
msg: 'not available in mockup'
}
resolve(res)
})
}
return new Promise((resolve, reject) => {
request({
url: `/file/${reqData.fileId}/${reqData.fileNo}`,
method: 'get'
})
.then(res => {
let imgData = {
fileName: res.result.fileName,
downloadUrl: res.result.downloadUrl
}
resolve(imgData)
})
.catch(res => {
reject('error in filedownload')
})
})
}
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -1,139 +1,139 @@
.rtl {
direction: rtl;
}
.vdp-datepicker {
position: relative;
text-align: left;
}
.vdp-datepicker * {
box-sizing: border-box;
}
.vdp-datepicker__calendar {
position: absolute;
z-index: 100;
background: #fff;
width: 300px;
border: 1px solid #ccc;
}
.vdp-datepicker__calendar header {
display: block;
line-height: 40px;
}
.vdp-datepicker__calendar header span {
display: inline-block;
text-align: center;
width: 71.42857142857143%;
float: left;
}
.vdp-datepicker__calendar header .prev,
.vdp-datepicker__calendar header .next {
width: 14.285714285714286%;
float: left;
text-indent: -10000px;
position: relative;
}
.vdp-datepicker__calendar header .prev:after,
.vdp-datepicker__calendar header .next:after {
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
border: 6px solid transparent;
}
.vdp-datepicker__calendar header .prev:after {
border-right: 10px solid #000;
margin-left: -5px;
}
.vdp-datepicker__calendar header .prev.disabled:after {
border-right: 10px solid #ddd;
}
.vdp-datepicker__calendar header .next:after {
border-left: 10px solid #000;
margin-left: 5px;
}
.vdp-datepicker__calendar header .next.disabled:after {
border-left: 10px solid #ddd;
}
.vdp-datepicker__calendar header .prev:not(.disabled),
.vdp-datepicker__calendar header .next:not(.disabled),
.vdp-datepicker__calendar header .up:not(.disabled) {
cursor: pointer;
}
.vdp-datepicker__calendar header .prev:not(.disabled):hover,
.vdp-datepicker__calendar header .next:not(.disabled):hover,
.vdp-datepicker__calendar header .up:not(.disabled):hover {
background: #eee;
}
.vdp-datepicker__calendar .disabled {
color: #ddd;
cursor: default;
}
.vdp-datepicker__calendar .flex-rtl {
display: flex;
width: inherit;
flex-wrap: wrap;
}
.vdp-datepicker__calendar .cell {
display: inline-block;
padding: 0 5px;
width: 14.285714285714286%;
height: 40px;
line-height: 40px;
text-align: center;
vertical-align: middle;
border: 1px solid transparent;
}
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day,
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month,
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year {
cursor: pointer;
}
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day:hover,
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month:hover,
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year:hover {
border: 1px solid #4bd;
}
.vdp-datepicker__calendar .cell.selected {
background: #4bd;
}
.vdp-datepicker__calendar .cell.selected:hover {
background: #4bd;
}
.vdp-datepicker__calendar .cell.selected.highlighted {
background: #4bd;
}
.vdp-datepicker__calendar .cell.highlighted {
background: #cae5ed;
}
.vdp-datepicker__calendar .cell.highlighted.disabled {
color: #a3a3a3;
}
.vdp-datepicker__calendar .cell.grey {
color: #888;
}
.vdp-datepicker__calendar .cell.grey:hover {
background: inherit;
}
.vdp-datepicker__calendar .cell.day-header {
font-size: 75%;
white-space: nowrap;
cursor: inherit;
}
.vdp-datepicker__calendar .cell.day-header:hover {
background: inherit;
}
.vdp-datepicker__calendar .month,
.vdp-datepicker__calendar .year {
width: 33.333%;
}
.vdp-datepicker__clear-button,
.vdp-datepicker__calendar-button {
cursor: pointer;
font-style: normal;
}
.vdp-datepicker__clear-button.disabled,
.vdp-datepicker__calendar-button.disabled {
color: #999;
cursor: default;
}
.rtl {
direction: rtl;
}
.vdp-datepicker {
position: relative;
text-align: left;
}
.vdp-datepicker * {
box-sizing: border-box;
}
.vdp-datepicker__calendar {
position: absolute;
z-index: 100;
background: #fff;
width: 300px;
border: 1px solid #ccc;
}
.vdp-datepicker__calendar header {
display: block;
line-height: 40px;
}
.vdp-datepicker__calendar header span {
display: inline-block;
text-align: center;
width: 71.42857142857143%;
float: left;
}
.vdp-datepicker__calendar header .prev,
.vdp-datepicker__calendar header .next {
width: 14.285714285714286%;
float: left;
text-indent: -10000px;
position: relative;
}
.vdp-datepicker__calendar header .prev:after,
.vdp-datepicker__calendar header .next:after {
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
border: 6px solid transparent;
}
.vdp-datepicker__calendar header .prev:after {
border-right: 10px solid #000;
margin-left: -5px;
}
.vdp-datepicker__calendar header .prev.disabled:after {
border-right: 10px solid #ddd;
}
.vdp-datepicker__calendar header .next:after {
border-left: 10px solid #000;
margin-left: 5px;
}
.vdp-datepicker__calendar header .next.disabled:after {
border-left: 10px solid #ddd;
}
.vdp-datepicker__calendar header .prev:not(.disabled),
.vdp-datepicker__calendar header .next:not(.disabled),
.vdp-datepicker__calendar header .up:not(.disabled) {
cursor: pointer;
}
.vdp-datepicker__calendar header .prev:not(.disabled):hover,
.vdp-datepicker__calendar header .next:not(.disabled):hover,
.vdp-datepicker__calendar header .up:not(.disabled):hover {
background: #eee;
}
.vdp-datepicker__calendar .disabled {
color: #ddd;
cursor: default;
}
.vdp-datepicker__calendar .flex-rtl {
display: flex;
width: inherit;
flex-wrap: wrap;
}
.vdp-datepicker__calendar .cell {
display: inline-block;
padding: 0 5px;
width: 14.285714285714286%;
height: 40px;
line-height: 40px;
text-align: center;
vertical-align: middle;
border: 1px solid transparent;
}
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day,
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month,
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year {
cursor: pointer;
}
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day:hover,
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month:hover,
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year:hover {
border: 1px solid #4bd;
}
.vdp-datepicker__calendar .cell.selected {
background: #4bd;
}
.vdp-datepicker__calendar .cell.selected:hover {
background: #4bd;
}
.vdp-datepicker__calendar .cell.selected.highlighted {
background: #4bd;
}
.vdp-datepicker__calendar .cell.highlighted {
background: #cae5ed;
}
.vdp-datepicker__calendar .cell.highlighted.disabled {
color: #a3a3a3;
}
.vdp-datepicker__calendar .cell.grey {
color: #888;
}
.vdp-datepicker__calendar .cell.grey:hover {
background: inherit;
}
.vdp-datepicker__calendar .cell.day-header {
font-size: 75%;
white-space: nowrap;
cursor: inherit;
}
.vdp-datepicker__calendar .cell.day-header:hover {
background: inherit;
}
.vdp-datepicker__calendar .month,
.vdp-datepicker__calendar .year {
width: 33.333%;
}
.vdp-datepicker__clear-button,
.vdp-datepicker__calendar-button {
cursor: pointer;
font-style: normal;
}
.vdp-datepicker__clear-button.disabled,
.vdp-datepicker__calendar-button.disabled {
color: #999;
cursor: default;
}

View File

@@ -1,25 +1,25 @@
<template>
<footer class="footer-wrap">
<div class="footer div-cont">
<div class="f-logo"><a href="/"><img src="../assets/images/flogo.png" alt=""></a></div>
<div class="f-info">
<ul>
<li><p>()엘지유플러스</p></li>
<li><p>주소 서울특별시 용산구 한강대로 32</p></li>
</ul>
<ul>
<li><p>대표이사 황현식</p></li>
<li><p>사업자번호 220-81-39938</p></li>
<li><p>통신판매신고 제2010-서울중구-0968</p></li>
<li><p>고객센터 1544-5992</p></li>
<li><p>e-Mail <a href="mailto:smshelp@lguplus.co.kr">smshelp@lguplus.co.kr</a></p></li>
</ul>
<p>Copyright © LG Uplus Corp. All Rights Reserved.</p>
</div>
</div>
</footer>
</template>
<script>
</script>
<template>
<footer class="footer-wrap">
<div class="footer div-cont">
<div class="f-logo"><a href="/"><img src="../assets/images/flogo.png" alt=""></a></div>
<div class="f-info">
<ul>
<li><p>()엘지유플러스</p></li>
<li><p>주소 서울특별시 용산구 한강대로 32</p></li>
</ul>
<ul>
<li><p>대표이사 황현식</p></li>
<li><p>사업자번호 220-81-39938</p></li>
<li><p>통신판매신고 제2010-서울중구-0968</p></li>
<li><p>고객센터 1544-5992</p></li>
<li><p>e-Mail <a href="mailto:smshelp@lguplus.co.kr">smshelp@lguplus.co.kr</a></p></li>
</ul>
<p>Copyright © LG Uplus Corp. All Rights Reserved.</p>
</div>
</div>
</footer>
</template>
<script>
</script>

View File

@@ -1,87 +1,87 @@
<template>
<header>
<h1 class="logo"><a href="javascript:void(0)">uplus 메시지허브이지<span>BACKOFFICE</span></a></h1>
<div class="user_wrap">
<div class="user" @click="userInfoToggle();">
<p>슈퍼관리자</p>
<a href="javascript:void(0)" class="btn_user">Uplus01</a>
</div>
<div class="user_info">
<a href="superadmin_info.html" class="modify">정보수정</a>
<a href="javascript:void(0)" class="logout" @click="logout();">로그아웃</a>
</div>
</div>
</header>
</template>
<script>
//import tokenSvc from '@/common/token-service';
import { mapGetters } from 'vuex';
import loginApi from '@/modules/login/service/api';
export default {
name: "hubWebHeader",
data() {
return {
menuList: null,
isLogin: false,
isErrPage: false,
navActive: false,
}
},
created() {
},
computed: {
...mapGetters({
getLogin: 'login/isLogin',
getErrorPage: 'login/isErrorPage',
}),
},
watch: {
getLogin(data) {
if (data != null && data != '' && data == true) {
this.isLogin = true;
//this.setMenuData();
} else {
this.isLogin = false;
//this.menuList = null;
}
},
getErrorPage(data) {
if (data != null && data != '' && data == true) {
this.isErrPage = true;
} else {
this.isErrPage = false;
}
},
},
methods: {
userInfoToggle(){
var click = "clicked";
var userBtn = document.querySelector('.user_wrap .user');
if(userBtn.classList.contains(click)){
userBtn.classList.remove(click);
}
else{
userBtn.classList.add(click);
}
},
logout(){
let result = confirm("로그아웃 하시겠습니까?");
if (result) {
loginApi.logout().then(response => {
if(response.data.retCode == '0000'){
//tokenSvc.removeToken();
this.$router.push({
path: "/login"
});
}
});
}
}
}
};
</script>
<template>
<header>
<h1 class="logo"><a href="javascript:void(0)">uplus 메시지허브이지<span>BACKOFFICE</span></a></h1>
<div class="user_wrap">
<div class="user" @click="userInfoToggle();">
<p>슈퍼관리자</p>
<a href="javascript:void(0)" class="btn_user">Uplus01</a>
</div>
<div class="user_info">
<a href="superadmin_info.html" class="modify">정보수정</a>
<a href="javascript:void(0)" class="logout" @click="logout();">로그아웃</a>
</div>
</div>
</header>
</template>
<script>
//import tokenSvc from '@/common/token-service';
import { mapGetters } from 'vuex';
import loginApi from '@/modules/login/service/api';
export default {
name: "hubWebHeader",
data() {
return {
menuList: null,
isLogin: false,
isErrPage: false,
navActive: false,
}
},
created() {
},
computed: {
...mapGetters({
getLogin: 'login/isLogin',
getErrorPage: 'login/isErrorPage',
}),
},
watch: {
getLogin(data) {
if (data != null && data != '' && data == true) {
this.isLogin = true;
//this.setMenuData();
} else {
this.isLogin = false;
//this.menuList = null;
}
},
getErrorPage(data) {
if (data != null && data != '' && data == true) {
this.isErrPage = true;
} else {
this.isErrPage = false;
}
},
},
methods: {
userInfoToggle(){
var click = "clicked";
var userBtn = document.querySelector('.user_wrap .user');
if(userBtn.classList.contains(click)){
userBtn.classList.remove(click);
}
else{
userBtn.classList.add(click);
}
},
logout(){
let result = confirm("로그아웃 하시겠습니까?");
if (result) {
loginApi.logout().then(response => {
if(response.data.retCode == '0000'){
//tokenSvc.removeToken();
this.$router.push({
path: "/login"
});
}
});
}
}
}
};
</script>

View File

@@ -1,56 +1,56 @@
<template>
<div>
<div>role: {{role}}</div><br/>
<TreeMenu :nodes="tree.nodes" :depth="0" :label="tree.label"></TreeMenu>
</div>
</template>
<script>
import TreeMenu from './TreeMenu';
import tokenSvc from '@/common/token-service';
let tree = {
label: 'root',
nodes: [
{
label: 'item1',
nodes: [
{
label: 'item1.1'
},
{
label: 'item1.2',
nodes: [
{
label: 'item1.2.1'
}
]
}
]
},
{
label: 'item2'
}
]
};
export default {
components: {
TreeMenu
},
data() {
return {
tree
};
},
computed: {
role() {
return tokenSvc.getToken().principal.authorities[0].authority;
}
},
created() {
console.log('created Lnb');
console.log('node[0]:', this.tree.nodes[0].label);
console.log('role:', tokenSvc.getToken().principal.authorities[0].authority);
}
};
</script>
<template>
<div>
<div>role: {{role}}</div><br/>
<TreeMenu :nodes="tree.nodes" :depth="0" :label="tree.label"></TreeMenu>
</div>
</template>
<script>
import TreeMenu from './TreeMenu';
import tokenSvc from '@/common/token-service';
let tree = {
label: 'root',
nodes: [
{
label: 'item1',
nodes: [
{
label: 'item1.1'
},
{
label: 'item1.2',
nodes: [
{
label: 'item1.2.1'
}
]
}
]
},
{
label: 'item2'
}
]
};
export default {
components: {
TreeMenu
},
data() {
return {
tree
};
},
computed: {
role() {
return tokenSvc.getToken().principal.authorities[0].authority;
}
},
created() {
console.log('created Lnb');
console.log('node[0]:', this.tree.nodes[0].label);
console.log('role:', tokenSvc.getToken().principal.authorities[0].authority);
}
};
</script>

View File

@@ -1,307 +1,308 @@
<template>
<!-- 버튼 -->
<div class="wrap bg-wrap">
<!-- <div class="popup-btn-wrap">
<button class="trigger" onclick="ModalOpen('modal01');">로그인실패: 확인</button>
<button class="trigger" onclick="ModalOpen('modal02');">로그인실패: 5</button>
<button class="trigger" onclick="ModalOpen('modal03');">로그인실패: 상태</button>
<button class="trigger" onclick="ModalOpen('modal04');">보안 알림</button>
<button class="trigger" onclick="ModalOpen('modal05');">중복 로그인</button>
<button class="trigger" onclick="ModalOpen('modal06');">휴대폰번호 확인</button>
<button class="trigger" onclick="ModalOpen('modal07');">인증번호: 발송</button>
<button class="trigger" onclick="ModalOpen('modal08');">인증번호: 입력</button>
<button class="trigger" onclick="ModalOpen('modal09');">인증실패: 인증번호</button>
<button class="trigger" onclick="ModalOpen('modal10');">인증실패: 시간초과</button>
<button class="trigger" onclick="ModalOpen('modal11');">인증실패: 5</button>
<button class="trigger" onclick="ModalOpen('modal12');">비밀번호 초기화 문자 발송</button>
<button class="trigger" onclick="ModalOpen('modal13');">아이디 오류</button>
<button class="trigger" onclick="ModalOpen('modal14');">비밀번호 오류</button>
<button class="trigger" onclick="ModalOpen('modal15');">비밀번호 패턴 오류</button>
<button class="trigger" onclick="ModalOpen('modal16');">비밀번호 정상 변경</button>
</div> -->
<!-- s: 팝업 -->
<div class="dimmed" @click="ModalClose();"></div>
<div class="popup-wrap">
<!-- 로그인실패: 확인 -->
<div class="popup modal01">
<div class="pop-head">
<h3 class="pop-tit">로그인 실패</h3>
</div>
<div class="pop-cont">
<p>아이디,비밀번호를 확인해주세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 로그인실패: 5회 -->
<div class="popup modal02">
<div class="pop-head">
<h3 class="pop-tit">로그인 실패</h3>
</div>
<div class="pop-cont">
<p>로그인 5 실패하였습니다.</p>
<p>비밀번호 초기화 비밀번호를 변경해 주세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 로그인실패: 상태 -->
<div class="popup modal03">
<div class="pop-head">
<h3 class="pop-tit">로그인 실패</h3>
</div>
<div class="pop-cont">
<p>아이디 상태를 확인해 주세요.</p>
<p>(사용중인 상태만 로그인 가능합니다.)</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 보안 알림 -->
<div class="popup modal04">
<div class="pop-head">
<h3 class="pop-tit">보안 알림</h3>
</div>
<div class="pop-cont">
<p>비밀번호를 변경하지 않은지 90일이</p>
<p>지났습니다. 비밀번호를 변경하여</p>
<p>이용 부탁드립니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">비밀번호 변경하기</button>
</div>
</div>
<!-- 중복 로그인 -->
<div class="popup modal05">
<div class="pop-head">
<h3 class="pop-tit">중복 로그인</h3>
</div>
<div class="pop-cont">
<p>동일한 아이디로 로그인 되어 있습니다.</p>
<p>이전 로그인 세션 종료 로그인하시겠습니까?</p>
<p>확인 이전 로그인한 상태는 로그아웃됩니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 휴대폰번호 확인 -->
<div class="popup modal06">
<div class="pop-head">
<h3 class="pop-tit">휴대폰번호 확인</h3>
</div>
<div class="pop-cont">
<p>휴대폰번호를 확인해주세요.</p>
<p>아이디에 등록된 휴대폰번호로만 인증이 가능합니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 인증번호: 발송 -->
<div class="popup modal07">
<div class="pop-head">
<h3 class="pop-tit">인증번호 발송</h3>
</div>
<div class="pop-cont">
<p>인증번호를 발송하였습니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 인증번호: 입력 -->
<div class="popup modal08">
<div class="pop-head">
<h3 class="pop-tit">인증번호 입력</h3>
</div>
<div class="pop-cont">
<p>인증번호를 입력하세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 인증실패: 인증번호 -->
<div class="popup modal09">
<div class="pop-head">
<h3 class="pop-tit">인증실패</h3>
</div>
<div class="pop-cont">
<p>잘못된 인증번호입니다.</p>
<p>5 실패 로그아웃됩니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 인증실패: 시간초과 -->
<div class="popup modal10">
<div class="pop-head">
<h3 class="pop-tit">인증실패</h3>
</div>
<div class="pop-cont">
<p>인증시간 초과되었습니다.</p>
<p>다시 휴대폰번호를 입력해주세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 인증실패: 5회 -->
<div class="popup modal11">
<div class="pop-head">
<h3 class="pop-tit">인증실패</h3>
</div>
<div class="pop-cont">
<p>인증번호 5 실패하였습니다.</p>
<p>로그아웃되어 다시 로그인해주세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 비밀번호 초기화 발송 -->
<div class="popup modal12">
<div class="pop-head">
<h3 class="pop-tit">비밀번호 초기화</h3>
</div>
<div class="pop-cont">
<p>해당 아이디에 저장되어 있는 핸드폰번호로</p>
<p>비밀번호 초기화 문자가 발송되었습니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 아이디 오류 -->
<div class="popup modal13">
<div class="pop-head">
<h3 class="pop-tit">아이디 오류</h3>
</div>
<div class="pop-cont">
<p>등록되지 않은 아이디입니다.</p>
<p>아이디를 다시 확인하세요</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 비밀번호 오류 -->
<div class="popup modal14">
<div class="pop-head">
<h3 class="pop-tit">비밀번호 오류</h3>
</div>
<div class="pop-cont">
<p>비밀번호를 확인해주세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 비밀번호 패턴 오류 -->
<div class="popup modal15">
<div class="pop-head">
<h3 class="pop-tit">비밀번호 오류</h3>
</div>
<div class="pop-cont">
<p>비밀번호를 사용할 없습니다.</p>
<p>비밀번호는 영문/숫자/특수기호를 혼합하여</p>
<p>8~16자리로 설정해주세요</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 비밀번호 정상 변경 -->
<div class="popup modal16">
<div class="pop-head">
<h3 class="pop-tit">비밀번호 오류</h3>
</div>
<div class="pop-cont">
<p>비밀번호가 정상적으로 변경되었습니다.</p>
<p>변경된 비밀번호로 다시 로그인 해주세요</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
</div>
<!-- e: 팝업 -->
</div>
</template>
<script>
export default {
data(){
return{
}
},
methods: {
//모달 켜기
ModalOpen(target){
console.log("ModalOpen");
var dimmed = document.getElementsByClassName('dimmed');
var wrap = document.getElementsByClassName('popup-wrap');
var obj = document.getElementsByClassName(target);
dimmed[0].style.display = 'block';
wrap[0].style.display = 'block';
obj[0].style.display = 'block';
},
// 모달 끄기
ModalClose(){
var dimmed = document.getElementsByClassName('dimmed');
var wrap = document.getElementsByClassName('popup-wrap');
var obj = wrap[0].childElementCount
dimmed[0].style.display = 'none';
wrap[0].style.display = 'none';
for(var i = 0; i < obj; i++) {
var target = document.getElementsByClassName('popup');
target[i].style.display = 'none';
}
},
}
}
</script>
<style>
.popup-btn-wrap {width: 500px; margin: auto; padding: 100px 0;}
.popup-btn-wrap button {width: 100%; margin-bottom: 10px; height: 50px; border-radius: 5px; box-shadow: none; border: 1px solid #000; }
.popup-btn-wrap button:hover {background: #000; color: #fff;}
<template>
<!-- 버튼 -->
<div class="wrap bg-wrap">
<!-- <div class="popup-btn-wrap">
<button class="trigger" onclick="ModalOpen('modal01');">로그인실패: 확인</button>
<button class="trigger" onclick="ModalOpen('modal02');">로그인실패: 5</button>
<button class="trigger" onclick="ModalOpen('modal03');">로그인실패: 상태</button>
<button class="trigger" onclick="ModalOpen('modal04');">보안 알림</button>
<button class="trigger" onclick="ModalOpen('modal05');">중복 로그인</button>
<button class="trigger" onclick="ModalOpen('modal06');">휴대폰번호 확인</button>
<button class="trigger" onclick="ModalOpen('modal07');">인증번호: 발송</button>
<button class="trigger" onclick="ModalOpen('modal08');">인증번호: 입력</button>
<button class="trigger" onclick="ModalOpen('modal09');">인증실패: 인증번호</button>
<button class="trigger" onclick="ModalOpen('modal10');">인증실패: 시간초과</button>
<button class="trigger" onclick="ModalOpen('modal11');">인증실패: 5</button>
<button class="trigger" onclick="ModalOpen('modal12');">비밀번호 초기화 문자 발송</button>
<button class="trigger" onclick="ModalOpen('modal13');">아이디 오류</button>
<button class="trigger" onclick="ModalOpen('modal14');">비밀번호 오류</button>
<button class="trigger" onclick="ModalOpen('modal15');">비밀번호 패턴 오류</button>
<button class="trigger" onclick="ModalOpen('modal16');">비밀번호 정상 변경</button>
</div> -->
<!-- s: 팝업 -->
<div class="dimmed" @click="ModalClose();"></div>
<div class="popup-wrap">
<!-- 로그인실패: 확인 -->
<div class="popup modal01">
<div class="pop-head">
<h3 class="pop-tit">로그인 실패</h3>
</div>
<div class="pop-cont">
<p>아이디,비밀번호를 확인해주세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 로그인실패: 5회 -->
<div class="popup modal02">
<div class="pop-head">
<h3 class="pop-tit">로그인 실패</h3>
</div>
<div class="pop-cont">
<p>로그인 5 실패하였습니다.</p>
<p>비밀번호 초기화 비밀번호를 변경해 주세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 로그인실패: 상태 -->
<div class="popup modal03">
<div class="pop-head">
<h3 class="pop-tit">로그인 실패</h3>
</div>
<div class="pop-cont">
<p>아이디 상태를 확인해 주세요.</p>
<p>(사용중인 상태만 로그인 가능합니다.)</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 보안 알림 -->
<div class="popup modal04">
<div class="pop-head">
<h3 class="pop-tit">보안 알림</h3>
</div>
<div class="pop-cont">
<p>비밀번호를 변경하지 않은지 90일이</p>
<p>지났습니다. 비밀번호를 변경하여</p>
<p>이용 부탁드립니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="$router.push({ path: '/view/login/updatePassword' })">비밀번호 변경하기</button>
</div>
</div>
<!-- 중복 로그인 -->
<div class="popup modal05">
<div class="pop-head">
<h3 class="pop-tit">중복 로그인</h3>
</div>
<div class="pop-cont">
<p>동일한 아이디로 로그인 되어 있습니다.</p>
<p>이전 로그인 세션 종료 로그인하시겠습니까?</p>
<p>확인 이전 로그인한 상태는 로그아웃됩니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 휴대폰번호 확인 -->
<div class="popup modal06">
<div class="pop-head">
<h3 class="pop-tit">휴대폰번호 확인</h3>
</div>
<div class="pop-cont">
<p>휴대폰번호를 확인해주세요.</p>
<p>아이디에 등록된 휴대폰번호로만 인증이 가능합니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 인증번호: 발송 -->
<div class="popup modal07">
<div class="pop-head">
<h3 class="pop-tit">인증번호 발송</h3>
</div>
<div class="pop-cont">
<p>인증번호를 발송하였습니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 인증번호: 입력 -->
<div class="popup modal08">
<div class="pop-head">
<h3 class="pop-tit">인증번호 입력</h3>
</div>
<div class="pop-cont">
<p>인증번호를 입력하세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 인증실패: 인증번호 -->
<div class="popup modal09">
<div class="pop-head">
<h3 class="pop-tit">인증실패</h3>
</div>
<div class="pop-cont">
<p>잘못된 인증번호입니다.</p>
<p>5 실패 로그아웃됩니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 인증실패: 시간초과 -->
<div class="popup modal10">
<div class="pop-head">
<h3 class="pop-tit">인증실패</h3>
</div>
<div class="pop-cont">
<p>인증시간 초과되었습니다.</p>
<p>다시 휴대폰번호를 입력해주세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 인증실패: 5회 -->
<div class="popup modal11">
<div class="pop-head">
<h3 class="pop-tit">인증실패</h3>
</div>
<div class="pop-cont">
<p>인증번호 5 실패하였습니다.</p>
<p>로그아웃되어 다시 로그인해주세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="$router.go(-1)">확인</button>
</div>
</div>
<!-- 비밀번호 초기화 발송 -->
<div class="popup modal12">
<div class="pop-head">
<h3 class="pop-tit">비밀번호 초기화</h3>
</div>
<div class="pop-cont">
<p>해당 아이디에 저장되어 있는 핸드폰번호로</p>
<p>비밀번호 초기화 문자가 발송되었습니다.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 아이디 오류 -->
<div class="popup modal13">
<div class="pop-head">
<h3 class="pop-tit">아이디 오류</h3>
</div>
<div class="pop-cont">
<p>등록되지 않은 아이디입니다.</p>
<p>아이디를 다시 확인하세요</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 비밀번호 오류 -->
<div class="popup modal14">
<div class="pop-head">
<h3 class="pop-tit">비밀번호 오류</h3>
</div>
<div class="pop-cont">
<p>비밀번호를 확인해주세요.</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 비밀번호 패턴 오류 -->
<div class="popup modal15">
<div class="pop-head">
<h3 class="pop-tit">비밀번호 오류</h3>
</div>
<div class="pop-cont">
<p>비밀번호를 사용할 없습니다.</p>
<p>비밀번호는 영문/숫자/특수기호를 혼합하여</p>
<p>8~16자리로 설정해주세요</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="ModalClose();">확인</button>
</div>
</div>
<!-- 비밀번호 정상 변경 -->
<div class="popup modal16">
<div class="pop-head">
<h3 class="pop-tit">비밀번호 오류</h3>
</div>
<div class="pop-cont">
<p>비밀번호가 정상적으로 변경되었습니다.</p>
<p>변경된 비밀번호로 다시 로그인 해주세요</p>
</div>
<div class="pop-btn1">
<button class="btn-pcolor" @click="$router.go(-1)">확인</button>
</div>
</div>
</div>
<!-- e: 팝업 -->
</div>
</template>
<script>
export default {
data(){
return{
}
},
methods: {
//모달 켜기
ModalOpen(target){
console.log("ModalOpen");
var dimmed = document.getElementsByClassName('dimmed');
var wrap = document.getElementsByClassName('popup-wrap');
var obj = document.getElementsByClassName(target);
dimmed[0].style.display = 'block';
wrap[0].style.display = 'block';
obj[0].style.display = 'block';
},
// 모달 끄기
ModalClose(){
var dimmed = document.getElementsByClassName('dimmed');
var wrap = document.getElementsByClassName('popup-wrap');
var obj = wrap[0].childElementCount
dimmed[0].style.display = 'none';
wrap[0].style.display = 'none';
for(var i = 0; i < obj; i++) {
var target = document.getElementsByClassName('popup');
target[i].style.display = 'none';
}
},
}
}
</script>
<style>
.popup-btn-wrap {width: 500px; margin: auto; padding: 100px 0;}
.popup-btn-wrap button {width: 100%; margin-bottom: 10px; height: 50px; border-radius: 5px; box-shadow: none; border: 1px solid #000; }
.popup-btn-wrap button:hover {background: #000; color: #fff;}
</style>

View File

@@ -1,132 +1,121 @@
<template>
<nav>
<ul v-if="menuList.length > 0" class="main_menu">
<!-- 선택한 메뉴 li.is-current -->
<li v-for="child in menuList" :key="child.menuNo" :class="child.classNm">
<div class="menu_btn" ></div>
<a class="menu_target" @click="clickTest" :data-menu-no="child.menuNo">{{child.menuNm}}</a>
<div class="sub_menu_wrap">
<ul class="sub_menu" v-if="child.children.length > 0">
<li v-for="child2 in child.children" :key="child2.menuNo">
<a href="javascript:void(0);" @click="clickMenu(child2.menuUrl)" :data-menu-no="child2.menuNo">{{child2.menuNm}}</a>
</li>
</ul>
</div>
</li>
</ul>
</nav>
</template>
<script>
//import "../assets/js/script.js";
import api from '@/service/api.js';
export default {
name: 'Nav',
props: {
},
data() {
return {
isLogin : false,
isAuthChk : false,
menuList: null,
tempList: null
}
},
created() {
// 메뉴 가져오기
this.setMenuData();
},
mounted() {},
computed: {
},
watch: {
},
methods: {
setMenuData() {
api.menus().then(response => {
const rootMenu = response.data.data;
console.log(rootMenu);
if (rootMenu != null && rootMenu.children != null && rootMenu.children.length > 0) {
this.tempList = rootMenu.children;
for(var i=0; i<this.tempList.length; i++){
var menuNo = this.tempList[i].menuNo;
var classNm = '';
switch(menuNo){
case 1001 : classNm = 'customer'; break;
case 1002 : classNm = 'attract'; break;
case 1003 : classNm = 'service'; break;
case 1004 : classNm = 'calculate'; break;
case 1005 : classNm = 'channel'; break;
case 1006 : classNm = 'key'; break;
case 1007 : classNm = 'moniter'; break;
case 1008 : classNm = 'risk'; break;
case 1009 : classNm = 'stats'; break;
case 1010 : classNm = 'system'; break;
default : classNm = 'customer';
}
this.tempList[i].classNm = classNm;
//console.log(classNm);
}
//this.menuList = rootMenu.children;
this.menuList = this.tempList;
//this.$store.commit("login/isLogin", true);
//this.$store.commit("login/isAuthChk", true);
} else {
this.$store.commit("login/isLogin", false);
this.$store.commit("login/isAuthChk", false);
this.menuList = null;
this.$router.push({ path: '/login' });
}
}).catch(response => {
this.$store.commit("login/isLogin", false);
this.$store.commit("login/isAuthChk", false);
this.menuList = null;
this.$router.push({ path: '/login' });
console.log(response);
});
},
clickMenu(link){
this.$router.push({
path: link
});
},
clickTest(e){
const menuList = document.querySelectorAll('.main_menu .is-current');
if(e.target.classList.contains('menu_target') || e.target.classList.contains('menu_btn')){
const menuListCheck = e.target.parentNode;
if(menuListCheck.classList.contains('is-current')){
menuListCheck.classList.remove('is-current');
for(const menu of menuList){
menu.classList.remove('is-current');
}
} else {
for(const other of menuList){
other.classList.remove('is-current');
}
menuListCheck.classList.add('is-current');
}
}
},
}
}
<template>
<nav>
<ul v-if="menuList.length > 0" class="main_menu">
<!-- 선택한 메뉴 li.is-current -->
<li v-for="child in menuList" :key="child.menuNo" :class="child.classNm">
<div class="menu_btn" ></div>
<a class="menu_target" @click="actionMenu" :data-menu-no="child.menuNo">{{child.menuNm}}</a>
<div class="sub_menu_wrap">
<ul class="sub_menu" v-if="child.children.length > 0">
<li v-for="child2 in child.children" :key="child2.menuNo">
<a href="javascript:void(0);" @click="clickMenu(child2.menuUrl)" :data-menu-no="child2.menuNo">{{child2.menuNm}}</a>
</li>
</ul>
</div>
</li>
</ul>
</nav>
</template>
<script>
//import "../assets/js/script.js";
import api from '@/service/api.js';
export default {
name: 'Nav',
props: {
},
data() {
return {
isLogin : false,
isAuthChk : false,
menuList: [],
tempList: []
}
},
created() {
// 메뉴 가져오기
this.setMenuData();
},
mounted() {},
computed: {
},
watch: {
},
methods: {
setMenuData() {
api.menus().then(response => {
const rootMenu = response.data.data;
console.log(rootMenu);
if (rootMenu != null && rootMenu.children != null && rootMenu.children.length > 0) {
this.tempList = rootMenu.children;
for(var i=0; i<this.tempList.length; i++){
var menuNo = this.tempList[i].menuNo;
var classNm = '';
switch(menuNo){
case 1001 : classNm = 'customer'; break;
case 1002 : classNm = 'attract'; break;
case 1003 : classNm = 'service'; break;
case 1004 : classNm = 'calculate'; break;
case 1005 : classNm = 'channel'; break;
case 1006 : classNm = 'key'; break;
case 1007 : classNm = 'moniter'; break;
case 1008 : classNm = 'risk'; break;
case 1009 : classNm = 'stats'; break;
case 1010 : classNm = 'system'; break;
default : classNm = 'customer';
}
this.tempList[i].classNm = classNm;
//console.log(classNm);
}
//this.menuList = rootMenu.children;
this.menuList = this.tempList;
//this.$store.commit("login/isLogin", true);
//this.$store.commit("login/isAuthChk", true);
} else {
window.top.location.href = '/';
}
});
},
clickMenu(link){
this.$router.push({
path: link
});
},
actionMenu(e){
const menuList = document.querySelectorAll('.main_menu .is-current');
if(e.target.classList.contains('menu_target') || e.target.classList.contains('menu_btn')){
const menuListCheck = e.target.parentNode;
if(menuListCheck.classList.contains('is-current')){
menuListCheck.classList.remove('is-current');
for(const menu of menuList){
menu.classList.remove('is-current');
}
} else {
for(const other of menuList){
other.classList.remove('is-current');
}
menuListCheck.classList.add('is-current');
}
}
},
}
}
</script>

View File

@@ -1,133 +1,133 @@
<template>
<div class="paging" :class="{className: true}">
<a href="javascript:void(0);" class="btn_arrow first" @click="changePageFirst">처음으로</a>
<a href="javascript:void(0);" class="btn_arrow prev" @click="changeRangePrev">이전으로</a>
<a
href="javascript:void(0);"
v-for="page in visiblePage"
:key="`page-${page}`"
:class="{'active': parseInt(currentPage) === page}"
@click="changePage(page)"
>{{ page }}</a>
<a href="javascript:void(0);" class="btn_arrow next" @click="changeRangeNext">다음으로</a>
<a href="javascript:void(0);" class="btn_arrow last" @click="changePageLast">마지막으로</a>
</div>
</template>
<script>
export default {
name: 'Pagination',
props: {
total: {
type: [Number, String],
default: 0
},
pageSize: {
type: [Number, String],
default: 10
},
currentPage: {
type: [Number, String],
default: 1
},
className: {
type: String,
default: ''
},
rangeMax: {
type: [Number, String],
default: 10
}
},
data() {
return {
range: 0
}
},
created() {},
mounted() {},
computed: {
visiblePage() {
let range = []
for (let i = this.rangeStart; i <= this.rangeEnd; i++) {
range.push(i)
}
return range
},
lastPage() {
return parseInt(this.total) > 0
? Math.ceil(parseInt(this.total) / parseInt(this.pageSize))
: 1
},
rangeStart() {
return parseInt(this.rangeMax) * this.range + 1
},
rangeEnd() {
return parseInt(this.rangeMax) * (this.range + 1) < this.lastPage
? parseInt(this.rangeMax) * (this.range + 1)
: this.lastPage
},
lastRange() {
return parseInt(this.total) > 0
? Math.ceil(
parseInt(this.total) /
(parseInt(this.pageSize) * parseInt(this.rangeMax))
)
: 0
}
},
watch: {
total() {
this.init()
},
currentPage() {
this.setRange()
}
},
methods: {
init() {
this.range = 0
},
setRange() {
this.range = Math.floor(
(parseInt(this.currentPage) - 1) / parseInt(this.rangeMax)
)
},
changePage(page) {
this.$emit('update:currentPage', page)
this.$emit('change', page)
},
changeRangePrev() {
if (this.range > 0) {
this.range -= 1
this.$emit('update:currentPage', this.rangeStart)
this.$emit('change', this.rangeStart)
}
},
changeRangeNext() {
if (this.range < this.lastRange - 1) {
this.range += 1
this.$emit('update:currentPage', this.rangeStart)
this.$emit('change', this.rangeStart)
}
},
changePageFirst() {
this.range = 0
this.$emit('update:currentPage', 1)
this.$emit('change', 1)
},
changePageLast() {
if (parseInt(this.total) > 0) {
this.range = this.lastRange - 1
} else {
this.range = 0
}
this.$emit('update:currentPage', this.lastPage)
this.$emit('change', this.lastPage)
}
}
}
</script>
<style>
</style>
<template>
<div class="paging" :class="{className: true}">
<a href="javascript:void(0);" class="btn_arrow first" @click="changePageFirst">처음으로</a>
<a href="javascript:void(0);" class="btn_arrow prev" @click="changeRangePrev">이전으로</a>
<a
href="javascript:void(0);"
v-for="page in visiblePage"
:key="`page-${page}`"
:class="{'active': parseInt(currentPage) === page}"
@click="changePage(page)"
>{{ page }}</a>
<a href="javascript:void(0);" class="btn_arrow next" @click="changeRangeNext">다음으로</a>
<a href="javascript:void(0);" class="btn_arrow last" @click="changePageLast">마지막으로</a>
</div>
</template>
<script>
export default {
name: 'Pagination',
props: {
total: {
type: [Number, String],
default: 0
},
pageSize: {
type: [Number, String],
default: 10
},
currentPage: {
type: [Number, String],
default: 1
},
className: {
type: String,
default: ''
},
rangeMax: {
type: [Number, String],
default: 10
}
},
data() {
return {
range: 0
}
},
created() {},
mounted() {},
computed: {
visiblePage() {
let range = []
for (let i = this.rangeStart; i <= this.rangeEnd; i++) {
range.push(i)
}
return range
},
lastPage() {
return parseInt(this.total) > 0
? Math.ceil(parseInt(this.total) / parseInt(this.pageSize))
: 1
},
rangeStart() {
return parseInt(this.rangeMax) * this.range + 1
},
rangeEnd() {
return parseInt(this.rangeMax) * (this.range + 1) < this.lastPage
? parseInt(this.rangeMax) * (this.range + 1)
: this.lastPage
},
lastRange() {
return parseInt(this.total) > 0
? Math.ceil(
parseInt(this.total) /
(parseInt(this.pageSize) * parseInt(this.rangeMax))
)
: 0
}
},
watch: {
total() {
this.init()
},
currentPage() {
this.setRange()
}
},
methods: {
init() {
this.range = 0
},
setRange() {
this.range = Math.floor(
(parseInt(this.currentPage) - 1) / parseInt(this.rangeMax)
)
},
changePage(page) {
this.$emit('update:currentPage', page)
this.$emit('change', page)
},
changeRangePrev() {
if (this.range > 0) {
this.range -= 1
this.$emit('update:currentPage', this.rangeStart)
this.$emit('change', this.rangeStart)
}
},
changeRangeNext() {
if (this.range < this.lastRange - 1) {
this.range += 1
this.$emit('update:currentPage', this.rangeStart)
this.$emit('change', this.rangeStart)
}
},
changePageFirst() {
this.range = 0
this.$emit('update:currentPage', 1)
this.$emit('change', 1)
},
changePageLast() {
if (parseInt(this.total) > 0) {
this.range = this.lastRange - 1
} else {
this.range = 0
}
this.$emit('update:currentPage', this.lastPage)
this.$emit('change', this.lastPage)
}
}
}
</script>
<style>
</style>

View File

@@ -1,61 +1,61 @@
<template>
<div class="tree-menu">
<div class="label-wrapper" v-if="depth > 0" @click="toggleChildren">
<div :style="indent" :class="labelClasses">
{{ label }}
<b v-if="nodes">[{{showChildren ? '-' : '+'}}]</b>
</div>
</div>
<span v-if="showChildren">
<TreeMenu
v-for="node in nodes"
:key="node.label"
:nodes="node.nodes"
:label="node.label"
:depth="depth + 1"
></TreeMenu>
</span>
</div>
</template>
<script>
export default {
name: "TreeMenu", // recursive component에는 name 설정 필수.
props: ["nodes", "label", "depth"],
data() {
return {
showChildren: true
};
},
computed: {
labelClasses() {
return { "has-children": this.nodes };
},
indent() {
return { transform: `translate(${(this.depth-1) * 50}px)` };
}
},
methods: {
toggleChildren() {
this.showChildren = !this.showChildren;
}
}
};
</script>
<style lang="scss" scoped>
.container {
width: 300px;
margin: 0 auto;
}
.tree-menu {
.label-wrapper {
padding-bottom: 10px;
margin-bottom: 10px;
.has-children {
cursor: pointer;
}
}
}
</style>
<template>
<div class="tree-menu">
<div class="label-wrapper" v-if="depth > 0" @click="toggleChildren">
<div :style="indent" :class="labelClasses">
{{ label }}
<b v-if="nodes">[{{showChildren ? '-' : '+'}}]</b>
</div>
</div>
<span v-if="showChildren">
<TreeMenu
v-for="node in nodes"
:key="node.label"
:nodes="node.nodes"
:label="node.label"
:depth="depth + 1"
></TreeMenu>
</span>
</div>
</template>
<script>
export default {
name: "TreeMenu", // recursive component에는 name 설정 필수.
props: ["nodes", "label", "depth"],
data() {
return {
showChildren: true
};
},
computed: {
labelClasses() {
return { "has-children": this.nodes };
},
indent() {
return { transform: `translate(${(this.depth-1) * 50}px)` };
}
},
methods: {
toggleChildren() {
this.showChildren = !this.showChildren;
}
}
};
</script>
<style lang="scss" scoped>
.container {
width: 300px;
margin: 0 auto;
}
.tree-menu {
.label-wrapper {
padding-bottom: 10px;
margin-bottom: 10px;
.has-children {
cursor: pointer;
}
}
}
</style>

View File

@@ -1,293 +1,293 @@
<template>
<grid
ref="tuiGrid"
:class="addClass"
:data="gridProps.data"
:columns="gridProps.columns"
:options="gridProps.options"
:language="gridProps.language"
@click="clicked"
@response="response"
@beforeRequest="beforeRequest"
:summary="gridProps.summary"
/>
<!--
- 공식홈: https://ui.toast.com/tui-grid
- vue git: https://github.com/nhn/toast-ui.vue-grid
- api & sample: https://nhn.github.io/tui.grid/latest/
- 연동 결과는 아래 포맷에 맞추어 주세요.
TuiGrid.java VO 참고
적용 : UserController > user
{
"result": true,
"data": {
"contents": [],
"pagination": {
"page": 1,
"totalCount": 100
}
}
}
-->
</template>
<script>
import 'tui-grid/dist/tui-grid.css';
import 'tui-pagination/dist/tui-pagination.css';
import TuiGrid from 'tui-grid';
import { Grid } from '@toast-ui/vue-grid';
export default {
name: 'tuiGrid',
props: [
'url', // 연동 url
'initialRequest', // false일 시 초기 렌더링 시 백엔드에 요청 하지 않음. 이 경우 readData를 호출하여 그리드 데이터를 할당해 줘야 함
'rowHeaders', // 체크 박스 등 헤더에 필요한 타입 ex) ["checkbox"]
'header', // 옵션 (헤더 병합 등)
'minRowHeight', // 리스트 row 높이
'minBodyHeight', // 테이블 높이
'pagination', // 페이지 네비게이션 사용 여부
'perPage', // 페이지 당 개수
'totalItems', // 부모창에 표시할 총 컨텐츠 개수 변수 명 (더 좋은 방법 있으면 알려주세요.)
'columns', // 컬럼 정보
'showVerticalBorder', // cell 세로라인 표시
'noDataStr', // 데이터가 없을 때
'noSearchStr', // 검색 결과가 없을 때
'scrollX', // 가로 스크롤 사용 여부
'scrollY', // 세로 스크롤 사용 여부
'addClass', // table에 추가할 클래스
'evtClick', // 클릭 이벤트 정보, 해당 커럼을 클릭하면 callback 수행, 이때 해당 row의 데이터를 파라미터로 반환한다. Object Or Array
// ex) evtClick: { column: "idx", callback: clickIdx }
// or evtClick: [ { column: "idx", callback: this.myFnc }, { column: "name", ... } ]
'summary'
],
components: {
grid: Grid
},
data() {
return {
instance: null,
gridProps: null,
currEventListener: null,
currGridEle: null,
currMode: 'normal'
};
},
mounted() {
this.saveGridEventListner();
//if(this.currMode != 'normal')
//this.initGridEventListner();
},
beforeDestroy() {
this.addGridEventListner();
},
created() {
/**
* Use the static method as below
*/
TuiGrid.applyTheme('default', {
outline: {
border: '#333 !important' // 테이블 위아래 선의 색깔 지정
},
row: {
hover: {
background: '#fafafa !important' // row 마우스 오버 시 색깔 지정
}
},
area: {
header: {
border: '#aaaaaa !important' // header 아래 선의 색깔 지정
}
},
cell: {
normal: {
showVerticalBorder: this.showVerticalBorder === true // 각 셀의 세로 선 노출
}
}
});
this.gridProps = {
data: {
api: {
readData: { url: this.url, method: 'GET' }
},
initialRequest: typeof this.initialRequest == 'undefined' ? true : this.initialRequest
},
options: {
rowHeaders: this.rowHeaders,
header: this.header ? this.header : { height: 47 },
minRowHeight: this.minRowHeight ? this.minRowHeight : 56,
minBodyHeight: this.minBodyHeight ? this.minBodyHeight : 56,
showDummyRows: true,
scrollX: this.scrollX === true,
scrollY: this.scrollY === true,
pagination: this.pagination,
pageOptions: {
perPage: this.perPage
}
},
language: {
name: 'ko',
value: {
display: {
noData: this.noDataStr
}
}
},
columns: this.columns,
summary: this.summary
};
},
methods: {
clicked: function(v) {
if (typeof this.evtClick != 'undefined' && this.evtClick != null) {
let data = this.$refs.tuiGrid.invoke('getRow', v.rowKey);
if (Array.isArray(this.evtClick)) {
for (var i = 0; i < this.evtClick.length; i++) {
if (v.columnName == this.evtClick[i].column && typeof this.evtClick[i].callback == 'function') {
this.evtClick[i].callback(data);
}
}
} else if (
this.evtClick.column != null &&
v.columnName == this.evtClick.column &&
typeof this.evtClick.callback == 'function'
) {
this.evtClick.callback(data);
}
}
},
response: function(event) {
let xhr = event.xhr;
let statusCode = xhr.status;
if (statusCode == 200) {
let respData = JSON.parse(xhr.response).data;
this.$parent[this.totalItems] = respData.pagination.totalCount;
let len = respData.contents.length;
if (this.scrollY == false) {
if (len > 0) {
let divLen = 6;
if (this.scrollX == true) {
divLen = 5;
}
// header의 height를 구할 수 있는 방법은? 일단 48
let addLen = len / divLen;
this.getGridInstance().setBodyHeight((len + addLen) * 48);
} else {
this.getGridInstance().setBodyHeight(48);
}
}
} else if (statusCode == 401 || statusCode == 418) {
alert('세션이 만료되었습니다.');
window.top.location.href = '/login';
} else {
window.top.location.href = '/view/error/' + statusCode;
}
},
beforeRequest: function(event) {
if (this.scrollY == false) {
this.getGridInstance().setBodyHeight(90);
}
},
getData: function() {
// 전체 Row 데이터를 가져온다.
return this.getGridInstance().getData();
},
getRow: function(rowKey) {
// 해당 Row의 데이터를 가져온다.
return this.getGridInstance().getRow(rowKey);
},
getPagination: function() {
// 페이지 정보
return this.getGridInstance().getPagination();
},
setPerPage: function(count) {
// 페이지 당 표시 개수 변경
this.getGridInstance().setPerPage(count);
},
readData: function(page, param) {
// 페이지 데이터 갱신 (initialRequest가 false일 경우 초기 데이터 로드시에도 사용)
this.getGridInstance().readData(page, param, true);
},
appendRow: function(rowData, rownum) {
var optionsOpt = {
at: rownum || 0,
extendPrevRowSpan: false,
focus: false
};
this.getGridInstance().appendRow(rowData, optionsOpt);
},
search: function(param) {
// 검색 결과가 없을 경우 텍스트 변경
let noSearchStr = '';
if (typeof this.noSearchStr == 'undefined') {
noSearchStr = '검색 결과가 없습니다.';
}
this.$refs.tuiGrid.language.value.display.noData = noSearchStr;
this.$refs.tuiGrid.setLanguage();
// param을 기반으로 리스트 갱신
this.readData(1, param);
},
gridMethod: function(params) {
return this.$refs.tuiGrid.invoke(...params);
},
getGridInstance() {
if (this.instance == null) {
this.instance = this.$refs.tuiGrid.gridInstance;
}
return this.instance;
},
saveGridEventListner() {
// this.currGridEle = document.querySelector(`div.tui-grid-container.tui-grid-show-lside-area`);
this.currGridEle = document.querySelector(`div.tui-grid-container`);
if (this.currGridEle != null && window.getEventListeners(this.currGridEle).mousedown) {
this.currEventListener = window.getEventListeners(this.currGridEle).mousedown[0].listener;
}
},
removeGridEventListner() {
this.currMode = 'edit';
this.currGridEle.removeEventListener('mousedown', this.currEventListener);
this.currGridEle.setAttribute('tabindex', '0');
this.currGridEle.style.outline = 'none';
this.currGridEle.addEventListener('keydown', this.copyEvent, false);
},
addGridEventListner() {
this.currMode = 'normal';
this.currGridEle.removeAttribute('tabindex');
this.currGridEle.removeEventListener('keydown', this.copyEvent);
this.currGridEle.addEventListener('mousedown', this.currEventListener);
},
initGridEventListner() {
document.querySelector(`div.tui-grid-container`).removeAttribute('tabindex');
document.querySelector(`div.tui-grid-container`).removeEventListener('keydown', this.copyEvent);
document.querySelector(`div.tui-grid-container`).addEventListener('mousedown', this.currEventListener);
},
copyEvent(ev) {
ev = ev || window.event;
var key = ev.which || ev.keyCode; // keyCode detection
var ctrl = ev.ctrlKey ? ev.ctrlKey : key === 17 ? true : false; // ctrl detection
if (key == 67 && ctrl) {
var clipboard = document.querySelector('.tui-grid-clipboard');
clipboard.innerHTML = this.getGridInstance().getFocusedCell().value;
clipboard.focus();
document.execCommand('copy');
}
}
}
};
</script>
<!-- 스타일 추가 CSS 우선순위를 높이기 위해 아래과 같이 .tui-grid-cell를 CSS 선택자에 포함시켜서 사용해야 적용 -->
<style>
.tui-grid-cell.cell-red {
background-color: red;
}
.tui-grid-table {
width: 100% !important;
}
</style>
<template>
<grid
ref="tuiGrid"
:class="addClass"
:data="gridProps.data"
:columns="gridProps.columns"
:options="gridProps.options"
:language="gridProps.language"
@click="clicked"
@response="response"
@beforeRequest="beforeRequest"
:summary="gridProps.summary"
/>
<!--
- 공식홈: https://ui.toast.com/tui-grid
- vue git: https://github.com/nhn/toast-ui.vue-grid
- api & sample: https://nhn.github.io/tui.grid/latest/
- 연동 결과는 아래 포맷에 맞추어 주세요.
TuiGrid.java VO 참고
적용 : UserController > user
{
"result": true,
"data": {
"contents": [],
"pagination": {
"page": 1,
"totalCount": 100
}
}
}
-->
</template>
<script>
import 'tui-grid/dist/tui-grid.css';
import 'tui-pagination/dist/tui-pagination.css';
import TuiGrid from 'tui-grid';
import { Grid } from '@toast-ui/vue-grid';
export default {
name: 'tuiGrid',
props: [
'url', // 연동 url
'initialRequest', // false일 시 초기 렌더링 시 백엔드에 요청 하지 않음. 이 경우 readData를 호출하여 그리드 데이터를 할당해 줘야 함
'rowHeaders', // 체크 박스 등 헤더에 필요한 타입 ex) ["checkbox"]
'header', // 옵션 (헤더 병합 등)
'minRowHeight', // 리스트 row 높이
'minBodyHeight', // 테이블 높이
'pagination', // 페이지 네비게이션 사용 여부
'perPage', // 페이지 당 개수
'totalItems', // 부모창에 표시할 총 컨텐츠 개수 변수 명 (더 좋은 방법 있으면 알려주세요.)
'columns', // 컬럼 정보
'showVerticalBorder', // cell 세로라인 표시
'noDataStr', // 데이터가 없을 때
'noSearchStr', // 검색 결과가 없을 때
'scrollX', // 가로 스크롤 사용 여부
'scrollY', // 세로 스크롤 사용 여부
'addClass', // table에 추가할 클래스
'evtClick', // 클릭 이벤트 정보, 해당 커럼을 클릭하면 callback 수행, 이때 해당 row의 데이터를 파라미터로 반환한다. Object Or Array
// ex) evtClick: { column: "idx", callback: clickIdx }
// or evtClick: [ { column: "idx", callback: this.myFnc }, { column: "name", ... } ]
'summary'
],
components: {
grid: Grid
},
data() {
return {
instance: null,
gridProps: null,
currEventListener: null,
currGridEle: null,
currMode: 'normal'
};
},
mounted() {
this.saveGridEventListner();
//if(this.currMode != 'normal')
//this.initGridEventListner();
},
beforeDestroy() {
this.addGridEventListner();
},
created() {
/**
* Use the static method as below
*/
TuiGrid.applyTheme('default', {
outline: {
border: '#333 !important' // 테이블 위아래 선의 색깔 지정
},
row: {
hover: {
background: '#fafafa !important' // row 마우스 오버 시 색깔 지정
}
},
area: {
header: {
border: '#aaaaaa !important' // header 아래 선의 색깔 지정
}
},
cell: {
normal: {
showVerticalBorder: this.showVerticalBorder === true // 각 셀의 세로 선 노출
}
}
});
this.gridProps = {
data: {
api: {
readData: { url: this.url, method: 'GET' }
},
initialRequest: typeof this.initialRequest == 'undefined' ? true : this.initialRequest
},
options: {
rowHeaders: this.rowHeaders,
header: this.header ? this.header : { height: 47 },
minRowHeight: this.minRowHeight ? this.minRowHeight : 56,
minBodyHeight: this.minBodyHeight ? this.minBodyHeight : 56,
showDummyRows: true,
scrollX: this.scrollX === true,
scrollY: this.scrollY === true,
pagination: this.pagination,
pageOptions: {
perPage: this.perPage
}
},
language: {
name: 'ko',
value: {
display: {
noData: this.noDataStr
}
}
},
columns: this.columns,
summary: this.summary
};
},
methods: {
clicked: function(v) {
if (typeof this.evtClick != 'undefined' && this.evtClick != null) {
let data = this.$refs.tuiGrid.invoke('getRow', v.rowKey);
if (Array.isArray(this.evtClick)) {
for (var i = 0; i < this.evtClick.length; i++) {
if (v.columnName == this.evtClick[i].column && typeof this.evtClick[i].callback == 'function') {
this.evtClick[i].callback(data);
}
}
} else if (
this.evtClick.column != null &&
v.columnName == this.evtClick.column &&
typeof this.evtClick.callback == 'function'
) {
this.evtClick.callback(data);
}
}
},
response: function(event) {
let xhr = event.xhr;
let statusCode = xhr.status;
if (statusCode == 200) {
let respData = JSON.parse(xhr.response).data;
this.$parent[this.totalItems] = respData.pagination.totalCount;
let len = respData.contents.length;
if (this.scrollY == false) {
if (len > 0) {
let divLen = 6;
if (this.scrollX == true) {
divLen = 5;
}
// header의 height를 구할 수 있는 방법은? 일단 48
let addLen = len / divLen;
this.getGridInstance().setBodyHeight((len + addLen) * 48);
} else {
this.getGridInstance().setBodyHeight(48);
}
}
} else if (statusCode == 401 || statusCode == 418) {
alert('세션이 만료되었습니다.');
window.top.location.href = '/login';
} else {
window.top.location.href = '/view/error/' + statusCode;
}
},
beforeRequest: function(event) {
if (this.scrollY == false) {
this.getGridInstance().setBodyHeight(90);
}
},
getData: function() {
// 전체 Row 데이터를 가져온다.
return this.getGridInstance().getData();
},
getRow: function(rowKey) {
// 해당 Row의 데이터를 가져온다.
return this.getGridInstance().getRow(rowKey);
},
getPagination: function() {
// 페이지 정보
return this.getGridInstance().getPagination();
},
setPerPage: function(count) {
// 페이지 당 표시 개수 변경
this.getGridInstance().setPerPage(count);
},
readData: function(page, param) {
// 페이지 데이터 갱신 (initialRequest가 false일 경우 초기 데이터 로드시에도 사용)
this.getGridInstance().readData(page, param, true);
},
appendRow: function(rowData, rownum) {
var optionsOpt = {
at: rownum || 0,
extendPrevRowSpan: false,
focus: false
};
this.getGridInstance().appendRow(rowData, optionsOpt);
},
search: function(param) {
// 검색 결과가 없을 경우 텍스트 변경
let noSearchStr = '';
if (typeof this.noSearchStr == 'undefined') {
noSearchStr = '검색 결과가 없습니다.';
}
this.$refs.tuiGrid.language.value.display.noData = noSearchStr;
this.$refs.tuiGrid.setLanguage();
// param을 기반으로 리스트 갱신
this.readData(1, param);
},
gridMethod: function(params) {
return this.$refs.tuiGrid.invoke(...params);
},
getGridInstance() {
if (this.instance == null) {
this.instance = this.$refs.tuiGrid.gridInstance;
}
return this.instance;
},
saveGridEventListner() {
// this.currGridEle = document.querySelector(`div.tui-grid-container.tui-grid-show-lside-area`);
this.currGridEle = document.querySelector(`div.tui-grid-container`);
if (this.currGridEle != null && window.getEventListeners(this.currGridEle).mousedown) {
this.currEventListener = window.getEventListeners(this.currGridEle).mousedown[0].listener;
}
},
removeGridEventListner() {
this.currMode = 'edit';
this.currGridEle.removeEventListener('mousedown', this.currEventListener);
this.currGridEle.setAttribute('tabindex', '0');
this.currGridEle.style.outline = 'none';
this.currGridEle.addEventListener('keydown', this.copyEvent, false);
},
addGridEventListner() {
this.currMode = 'normal';
this.currGridEle.removeAttribute('tabindex');
this.currGridEle.removeEventListener('keydown', this.copyEvent);
this.currGridEle.addEventListener('mousedown', this.currEventListener);
},
initGridEventListner() {
document.querySelector(`div.tui-grid-container`).removeAttribute('tabindex');
document.querySelector(`div.tui-grid-container`).removeEventListener('keydown', this.copyEvent);
document.querySelector(`div.tui-grid-container`).addEventListener('mousedown', this.currEventListener);
},
copyEvent(ev) {
ev = ev || window.event;
var key = ev.which || ev.keyCode; // keyCode detection
var ctrl = ev.ctrlKey ? ev.ctrlKey : key === 17 ? true : false; // ctrl detection
if (key == 67 && ctrl) {
var clipboard = document.querySelector('.tui-grid-clipboard');
clipboard.innerHTML = this.getGridInstance().getFocusedCell().value;
clipboard.focus();
document.execCommand('copy');
}
}
}
};
</script>
<!-- 스타일 추가 CSS 우선순위를 높이기 위해 아래과 같이 .tui-grid-cell를 CSS 선택자에 포함시켜서 사용해야 적용 -->
<style>
.tui-grid-cell.cell-red {
background-color: red;
}
.tui-grid-table {
width: 100% !important;
}
</style>

View File

@@ -1,27 +1,27 @@
<template>
<select v-model="perCnt" @change="perPage">
<option v-for="(cnt, index) in perList" :key="index" :value="cnt">{{cnt}}</option>
</select>
</template>
<script>
export default {
name: "tuiGridPerPage",
props: [
"grid", // tuiGrid의 ref 값
"per", // 초기 tuiGrid의 페이지 당 개수
"perList" // 페이지 당 개수 정보 ex: countList: [5, 10, 50, 100]
],
data() {
return {
perCnt: this.per
}
},
methods: {
perPage: function() {
this.$parent.$refs[this.grid].setPerPage(this.perCnt);
}
}
}
<template>
<select v-model="perCnt" @change="perPage">
<option v-for="(cnt, index) in perList" :key="index" :value="cnt">{{cnt}}</option>
</select>
</template>
<script>
export default {
name: "tuiGridPerPage",
props: [
"grid", // tuiGrid의 ref 값
"per", // 초기 tuiGrid의 페이지 당 개수
"perList" // 페이지 당 개수 정보 ex: countList: [5, 10, 50, 100]
],
data() {
return {
perCnt: this.per
}
},
methods: {
perPage: function() {
this.$parent.$refs[this.grid].setPerPage(this.perCnt);
}
}
}
</script>

View File

@@ -1,116 +1,116 @@
`
샘플 문서, 추후 삭제 예정 입니다.
`
<template>
<grid ref="tuiGrid"
:data="gridProps.data"
:columns="gridProps.columns"
:options="gridProps.options"
@click="clicked"
/>
</template>
<script>
import 'tui-grid/dist/tui-grid.css'
import { Grid } from '@toast-ui/vue-grid'
class inputTag {
constructor(props) {
const el = document.createElement('input');
el.type = "text";
el.style.width = "80%";
this.el = el;
this.render(props);
}
getElement() {
return this.el;
}
render(props) {
this.el.value = "test";
//this.el.value = String(props.value.chatbotId);
}
}
class divTag {
constructor(props) {
const div = document.createElement("div");
// props >> 컬럼 데이터. 하위 데이터가 있다면 props.value.xxx로 접근 가능하다.
div.appendChild(document.createElement("div")).textContent = "id: " + props.value.chatbotId
div.appendChild(document.createElement("div")).textContent = "snum: " + props.value.subNum
this.el = div;
}
getElement() {
return this.el;
}
}
export default {
components: {
'grid': Grid
},
data() {
return {
gridProps: null
}
},
created() {
this.gridProps = {
data: {
api: {
readData: { url: '/api/test/test', method: 'GET' },
}
},
pageOptions: {
perPage: 5
},
options: {
header: {
height: 100,
// 헤더 merge
complexColumns: [
{ header: "브랜드 모음", name: 'mergeColumn1', childNames: ["corpId", "mergeBrand", "mergeInfo"] },
{ header: "브랜드", name: 'mergeBrand', childNames: ["brId", "brNm"] },
{ header: "정보", name: 'mergeInfo', childNames: ["useYn", "apprYn", "apprReqYmd", "apprYmd"] }
]
}
},
// 헤더
columns: [
{ name: "corpId", header: "회사 아이디", align: "center" },
{ name: "brId", header: "브랜드 아이디", align: "center" },
{ name: "brNm", header: "브랜드 명", align: "center", sortable: true },
{ name: "useYn", header: "사용 여부", align: "center" },
{ name: "apprYn", header: "승인 여부", align: "center" },
{ name: "apprReqYmd", header: "승인요청 날짜", align: "center", sortable: true },
{ name: "apprYmd", header: "승인 날짜", align: "center", sortable: true },
{ name: "noname", header: "커스텀 1", align: "center", renderer: {
type: inputTag // 별도의 컬럼 구성이 필요한 경우
}},
{ name: "chatbot", header: "커스텀 2", align: "center", renderer: {
type: divTag // 별도의 컬럼 구성이 필요한 경우
}}
]
}
},
mounted() {
this.linkTo();
},
methods: {
linkTo: function() {
this.$refs.tuiGrid.invoke("addCellClassName", "0", "corpId", "cell-red");
},
clicked: function(v) {
var data = this.$refs.tuiGrid.invoke("getRow", v.rowKey);
alert("브랜드 아이디(" + data.brId + ") 클릭");
}
}
}
</script>
<style>
.tui-grid-cell.cell-red {background-color: red}
</style>
`
샘플 문서, 추후 삭제 예정 입니다.
`
<template>
<grid ref="tuiGrid"
:data="gridProps.data"
:columns="gridProps.columns"
:options="gridProps.options"
@click="clicked"
/>
</template>
<script>
import 'tui-grid/dist/tui-grid.css'
import { Grid } from '@toast-ui/vue-grid'
class inputTag {
constructor(props) {
const el = document.createElement('input');
el.type = "text";
el.style.width = "80%";
this.el = el;
this.render(props);
}
getElement() {
return this.el;
}
render(props) {
this.el.value = "test";
//this.el.value = String(props.value.chatbotId);
}
}
class divTag {
constructor(props) {
const div = document.createElement("div");
// props >> 컬럼 데이터. 하위 데이터가 있다면 props.value.xxx로 접근 가능하다.
div.appendChild(document.createElement("div")).textContent = "id: " + props.value.chatbotId
div.appendChild(document.createElement("div")).textContent = "snum: " + props.value.subNum
this.el = div;
}
getElement() {
return this.el;
}
}
export default {
components: {
'grid': Grid
},
data() {
return {
gridProps: null
}
},
created() {
this.gridProps = {
data: {
api: {
readData: { url: '/api/test/test', method: 'GET' },
}
},
pageOptions: {
perPage: 5
},
options: {
header: {
height: 100,
// 헤더 merge
complexColumns: [
{ header: "브랜드 모음", name: 'mergeColumn1', childNames: ["corpId", "mergeBrand", "mergeInfo"] },
{ header: "브랜드", name: 'mergeBrand', childNames: ["brId", "brNm"] },
{ header: "정보", name: 'mergeInfo', childNames: ["useYn", "apprYn", "apprReqYmd", "apprYmd"] }
]
}
},
// 헤더
columns: [
{ name: "corpId", header: "회사 아이디", align: "center" },
{ name: "brId", header: "브랜드 아이디", align: "center" },
{ name: "brNm", header: "브랜드 명", align: "center", sortable: true },
{ name: "useYn", header: "사용 여부", align: "center" },
{ name: "apprYn", header: "승인 여부", align: "center" },
{ name: "apprReqYmd", header: "승인요청 날짜", align: "center", sortable: true },
{ name: "apprYmd", header: "승인 날짜", align: "center", sortable: true },
{ name: "noname", header: "커스텀 1", align: "center", renderer: {
type: inputTag // 별도의 컬럼 구성이 필요한 경우
}},
{ name: "chatbot", header: "커스텀 2", align: "center", renderer: {
type: divTag // 별도의 컬럼 구성이 필요한 경우
}}
]
}
},
mounted() {
this.linkTo();
},
methods: {
linkTo: function() {
this.$refs.tuiGrid.invoke("addCellClassName", "0", "corpId", "cell-red");
},
clicked: function(v) {
var data = this.$refs.tuiGrid.invoke("getRow", v.rowKey);
alert("브랜드 아이디(" + data.brId + ") 클릭");
}
}
}
</script>
<style>
.tui-grid-cell.cell-red {background-color: red}
</style>

File diff suppressed because one or more lines are too long