
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="card">
<div class="card-body">
<h2 class="card-title">부서 관리</h2>
<!-- Multi Columns Form -->
<div class="flex align-top">
<div class="sch-form-wrap search">
<div v-for="(menu, index) in menus" :key="index" class="sidemenu">
<div class="menu-box">
<div class="topmenu" @click="toggleMenu(index)">
<img :src="arrow" alt="" class="arrow" :class="{ 'arrow-open': menu.isOpen }">
<img :src="topmenuicon" alt="">
<p @click.stop="selectTopDepartment(menu.title)"
:class="{
'active-top': selectedDepartment?.name === menu.title && selectedDepartment?.isTopLevel
}"
class="top-dept-name">{{ menu.title }}</p>
<button @click.stop="addSubMenu(index)" class="btn sm xsm secondary">sub +</button>
</div>
<ul v-show="menu.isOpen" class="submenu-list">
<li class="submenu" v-for="(submenu, subIndex) in menu.submenus" :key="subIndex">
<div @click="selectDepartment(menu.title, submenu.label)"
:class="{
'active-link': selectedDepartment?.parent === menu.title && selectedDepartment?.name === submenu.label && !selectedDepartment?.isTopLevel
}"
class="submenu-item">
<img :src="menuicon" alt="">
<p>{{ submenu.label }}</p>
</div>
</li>
</ul>
</div>
</div>
<div class="buttons">
<button @click="addTopMenu"><img :src="addtopmenu" alt=""></button>
</div>
</div>
<div style="width: 100%;">
<div class="sch-form-wrap title-wrap">
<h3><img :src="h3icon" alt="">부서 정보</h3>
<div class="buttons" style="margin: 0;">
<button type="button" class="btn sm sm tertiary">초기화</button>
<button type="button" class="btn sm sm secondary">등록</button>
<button type="button" class="btn sm sm btn-red">삭제</button>
</div>
</div>
<form class="row g-3 pt-3 needs-validation detail" @submit.prevent="handleSubmit" style="margin-bottom: 3rem;">
<div class="col-12">
<label for="parentDept" class="form-label">상위부서</label>
<input type="text" class="form-control" id="parentDept" v-model="departmentInfo.parentDept" readonly />
</div>
<div class="col-12">
<label for="deptName" class="form-label">
<p>부서명
<span class="require"><img :src="require" alt=""></span>
</p>
</label>
<input type="text" class="form-control" id="deptName" v-model="departmentInfo.deptName" />
</div>
<div class="col-12 chuljang border-x">
<label for="deptDesc" class="form-label">부서설명</label>
<textarea class="form-control textarea" id="deptDesc" v-model="departmentInfo.deptDesc" rows="3"></textarea>
</div>
</form>
<div class="sch-form-wrap title-wrap">
<h3><img :src="h3icon" alt="">부서 사용자</h3>
<div class="buttons" style="margin: 0;">
<button type="button" class="btn sm sm secondary" @click="showPopup = true">사용자 추가</button>
<button type="button" class="btn sm sm btn-red" @click="removeMember()">사용자 삭제</button>
</div>
<HrPopup v-if="showPopup"
:lists="members"
@close="showPopup = false"
@onSelected="addMember"/>
</div>
<div class="tbl-wrap chk-area">
<table id="myTable" class="tbl data">
<thead>
<tr>
<th>선택</th>
<th>직급</th>
<th>직책</th>
<th>부서</th>
<th>이름</th>
<th>부서장</th>
</tr>
</thead>
<tbody>
<tr v-for="(member, index) in members" :key="index">
<td>
<div class="form-check">
<input type="checkbox" :id="`chk_${index}`" :value="member.name" v-model="member.checked" />
<label :for="`chk_${index}`"></label>
</div>
</td>
<td>{{ member.position }}</td>
<td>{{ member.responsibility }}</td>
<td>{{ member.department }}</td>
<td>{{ member.name }}</td>
<td>
<div class="form-check">
<input type="radio" name="manager" :id="`rdo_${index}`" :value="member.userId" v-model="selectedManager" />
<label :for="`rdo_${index}`"></label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="pagination">
<ul>
<li class="arrow" :class="{ disabled: currentPage === 1 }" @click="changePage(currentPage - 1)">
<
</li>
<li v-for="page in totalPages" :key="page" :class="{ active: currentPage === page }" @click="changePage(page)">
{{ page }}
</li>
<li class="arrow" :class="{ disabled: currentPage === totalPages }" @click="changePage(currentPage + 1)">
>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { ref } from 'vue';
import { PlusCircleFilled, CloseOutlined, DownOutlined } from '@ant-design/icons-vue';
import HrPopup from '../../../component/Popup/HrPopup.vue';
const isOpen = ref(false);
export default {
data() {
const today = new Date().toISOString().split('T')[0];
return {
selectedManager: '',
showPopup: false,
selectedDepartment: null,
departmentInfo: {
parentDept: '',
deptName: '',
deptDesc: ''
},
menus: [
{
title: '부서1',
isOpen: true,
submenus: [
{
label: '직원검색',
link: { name: 'hrSearch' },
},
],
},
],
currentPage: 1,
totalPages: 3,
members: [],
h3icon: "/client/resources/img/h3icon.png",
require: "/client/resources/img/require.png",
menuicon: "/client/resources/img/arrow-rg.png",
topmenuicon: "/client/resources/img/topmenu.png",
arrow: "/client/resources/img/arrow.png",
addtopmenu: "/client/resources/img/addtopmenu.png",
addsubmenu: "/client/resources/img/addsubmenu.png",
};
},
components: {
PlusCircleFilled,
CloseOutlined,
DownOutlined,
HrPopup
},
methods: {
toggleMenu(index) {
this.menus[index].isOpen = !this.menus[index].isOpen;
},
selectTopDepartment(deptName) {
this.selectedDepartment = {
parent: null, // 상위부서 없음
name: deptName,
isTopLevel: true // 최상위 부서임을 표시
};
this.departmentInfo.parentDept = ''; // 상위부서 비움
this.departmentInfo.deptName = deptName;
},
selectDepartment(parentTitle, deptName) {
this.selectedDepartment = {
parent: parentTitle,
name: deptName,
isTopLevel: false
};
this.departmentInfo.parentDept = parentTitle;
this.departmentInfo.deptName = deptName;
},
addMember(selectedUser) {
console.log('addMember 호출됨:', selectedUser); // 디버깅용
// 이미 추가된 사용자인지 확인
const isAlreadyAdded = this.members.some(member => member.userId === selectedUser.userId);
if (isAlreadyAdded) {
alert('이미 추가된 사용자입니다.');
this.showPopup = false;
return;
}
this.members.push({
userId: selectedUser.userId,
position: selectedUser.clsfNm || selectedUser.position, // 직급
name: selectedUser.userNm || selectedUser.name, // 이름
department: selectedUser.deptNm, // 부서
responsibility: selectedUser.rspofcNm, // 직책
checked: false
});
this.showPopup = false;
console.log('새로운 멤버 추가됨, 총 멤버 수:', this.members.length); // 디버깅용
},
removeMember() {
this.members = this.members.filter(member => !member.checked);
},
addTopMenu() {
const newIndex = this.menus.length + 1;
this.menus.push({
title: `부서${newIndex}`,
isOpen: false,
submenus: [],
});
},
addSubMenu(menuIndex) {
this.menus[menuIndex].submenus.push({
label: `신규메뉴${this.menus[menuIndex].submenus.length + 1}`,
link: { name: 'hrSearch' },
});
},
changePage(page) {
if (page >= 1 && page <= this.totalPages) {
this.currentPage = page;
}
},
handleSubmit() {
console.log('폼 제출:', this.departmentInfo);
},
},
mounted() {
// 초기에 첫 번째 하위부서 선택
if (this.menus.length > 0 && this.menus[0].submenus.length > 0) {
this.selectDepartment(this.menus[0].title, this.menus[0].submenus[0].label);
}
}
};
</script>
<style scoped>
.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 24px;
margin: 20px;
}
.card-title {
font-size: 24px;
font-weight: bold;
color: #333;
margin-bottom: 24px;
border-bottom: 2px solid #f0f0f0;
padding-bottom: 12px;
}
.flex {
display: flex;
gap: 24px;
}
.align-top {
align-items: flex-start;
}
.sch-form-wrap.search {
min-width: 280px;
background: #f8f9fa;
border-radius: 8px;
padding: 16px;
border: 1px solid #e9ecef;
}
.sidemenu {
margin-bottom: 12px;
}
.menu-box {
background: white;
border-radius: 6px;
border: 1px solid #dee2e6;
overflow: hidden;
}
.topmenu {
display: flex;
align-items: center;
padding: 12px 16px;
background: #fff;
cursor: pointer;
transition: background-color 0.2s;
border-bottom: 1px solid #e9ecef;
}
.topmenu:hover {
background: #f8f9fa;
}
.topmenu .arrow {
width: 16px;
height: 16px;
margin-right: 8px;
transition: transform 0.2s;
}
.arrow-open {
transform: rotate(90deg);
}
.topmenu img:not(.arrow) {
width: 20px;
height: 20px;
margin-right: 8px;
}
.top-dept-name {
cursor: pointer;
padding: 2px 4px;
border-radius: 4px;
transition: background-color 0.2s;
}
.top-dept-name:hover {
background: #e9ecef;
}
.topmenu .top-dept-name.active-top {
background: #007bff;
color: white;
}
.topmenu p {
flex: 1;
margin: 0;
font-weight: 500;
color: #333;
}
.btn {
padding: 4px 8px;
border: none;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
}
.btn.secondary {
background: #6c757d;
color: white;
}
.btn.secondary:hover {
background: #5a6268;
}
.btn.tertiary {
background: #f8f9fa;
color: #6c757d;
border: 1px solid #dee2e6;
}
.btn.btn-red {
background: #dc3545;
color: white;
}
.btn.btn-red:hover {
background: #c82333;
}
.submenu-list {
list-style: none;
padding: 0;
margin: 0;
background: #f8f9fa;
}
.submenu {
border-bottom: 1px solid #e9ecef;
}
.submenu:last-child {
border-bottom: none;
}
.submenu-item {
display: flex;
align-items: center;
padding: 10px 16px;
cursor: pointer;
transition: background-color 0.2s;
}
.submenu-item:hover {
background: #e9ecef;
}
.submenu-item.active-link {
background: #007bff;
color: white;
}
.submenu-item img {
width: 16px;
height: 16px;
margin-right: 8px;
}
.submenu-item p {
margin: 0;
font-size: 14px;
}
.buttons {
margin-top: 16px;
text-align: center;
}
.buttons button {
background: none;
border: none;
cursor: pointer;
padding: 8px;
border-radius: 4px;
transition: background-color 0.2s;
}
.buttons button:hover {
background: #e9ecef;
}
.title-wrap {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding: 16px;
background: #f8f9fa;
border-radius: 6px;
border: 1px solid #dee2e6;
}
.title-wrap h3 {
display: flex;
align-items: center;
margin: 0;
font-size: 18px;
font-weight: 600;
color: #333;
}
.title-wrap h3 img {
width: 20px;
height: 20px;
margin-right: 8px;
}
.form-label {
font-weight: 500;
color: #333;
margin-bottom: 6px;
}
.form-label p {
margin: 0;
display: flex;
align-items: center;
}
.require {
margin-left: 4px;
}
.require img {
width: 8px;
height: 8px;
}
.form-control {
width: 100%;
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 14px;
transition: border-color 0.2s;
}
.form-control:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}
.form-control[readonly] {
background-color: #f8f9fa;
color: #6c757d;
}
.textarea {
resize: vertical;
min-height: 80px;
}
.tbl-wrap {
margin: 16px 0;
border: 1px solid #dee2e6;
border-radius: 6px;
overflow: hidden;
}
.tbl {
width: 100%;
border-collapse: collapse;
background: white;
}
.tbl thead th {
background: #f8f9fa;
padding: 12px;
text-align: center;
font-weight: 600;
color: #333;
border-bottom: 2px solid #dee2e6;
}
.tbl tbody td {
padding: 12px;
text-align: center;
border-bottom: 1px solid #e9ecef;
}
.tbl tbody tr:hover {
background: #f8f9fa;
}
.form-check {
display: flex;
justify-content: center;
align-items: center;
}
.form-check input[type="checkbox"],
.form-check input[type="radio"] {
margin: 0;
cursor: pointer;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 20px;
}
.pagination ul {
display: flex;
list-style: none;
padding: 0;
margin: 0;
gap: 4px;
}
.pagination li {
padding: 8px 12px;
border: 1px solid #dee2e6;
background: white;
cursor: pointer;
border-radius: 4px;
transition: all 0.2s;
}
.pagination li:hover:not(.disabled) {
background: #f8f9fa;
}
.pagination li.active {
background: #007bff;
color: white;
border-color: #007bff;
}
.pagination li.disabled {
color: #6c757d;
cursor: not-allowed;
background: #f8f9fa;
}
.col-12 {
margin-bottom: 16px;
}
.row {
display: flex;
flex-direction: column;
}
.pt-3 {
padding-top: 16px;
}
.g-3 {
gap: 16px;
}
</style>