299 lines
7.1 KiB
Vue
299 lines
7.1 KiB
Vue
<template>
|
||
<div class="container-xxl flex-grow-1 container-p-y">
|
||
<div class="card">
|
||
<!-- 사원 목록이 없을 경우 표시 -->
|
||
<div v-if="allUserList.length === 0" class="text-center my-4">
|
||
<p class="text-muted">등록된 사원이 없습니다.</p>
|
||
</div>
|
||
|
||
<!-- 사원 카드 리스트 영역 -->
|
||
<div class="card-body">
|
||
<div class="card-list">
|
||
<div
|
||
v-for="(person, index) in allUserList"
|
||
:key="index"
|
||
class="person-card"
|
||
@click="openModal(person)"
|
||
>
|
||
<div>
|
||
<img
|
||
class="rounded-circle user-avatar pointer"
|
||
:src="getProfileImage(person.MEMBERPRF)"
|
||
:style="{ borderColor: person.usercolor }"
|
||
@error="setDefaultImage"
|
||
/>
|
||
</div>
|
||
<div class="card-body">
|
||
<h3 class="person-name">{{ person.MEMBERNAM }}</h3>
|
||
<p class="person-email">{{ person.MEMBERIDS }}@local-host.co.kr</p>
|
||
<p class="person-phone">{{ person.MEMBERTEL }}</p>
|
||
<small>
|
||
{{ person.MEMBERARR }} {{ person.MEMBERDTL }}
|
||
</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 상세보기 Modal -->
|
||
<div v-if="showModal" class="modal-overlay" @click.self="closeModal">
|
||
<div class="modal-content">
|
||
<button class="close-btn" @click="closeModal">×</button>
|
||
<div class="modal-body">
|
||
<img
|
||
style="cursor: auto;"
|
||
class="rounded-circle modal-img object-fit-cover"
|
||
:src="getProfileImage(selectedPerson.MEMBERPRF)"
|
||
:style="{ borderColor: selectedPerson.usercolor }"
|
||
@error="setDefaultImage"
|
||
/>
|
||
<h4>{{ selectedPerson.MEMBERNAM }}</h4>
|
||
<p>{{ selectedPerson.MEMBERIDS }}@local-host.co.kr</p>
|
||
<p>{{ selectedPerson.MEMBERTEL }}</p>
|
||
<p>{{ selectedPerson.MEMBERARR }} {{ selectedPerson.MEMBERDTL }}</p>
|
||
<hr />
|
||
<!-- 추가 정보: 사용자가 속한 프로젝트 목록 -->
|
||
<h5>참여 프로젝트</h5>
|
||
<div v-if="memberProjects.length > 0">
|
||
<ul>
|
||
<li
|
||
v-for="(project, idx) in memberProjects"
|
||
:key="idx"
|
||
class="project-item"
|
||
>
|
||
<span class="project-name">{{ project.PROJCTNAM }}</span>
|
||
<span class="project-period">
|
||
<!-- projectEndDate가 있는 경우 -->
|
||
<template v-if="project.projectEndDate">
|
||
{{ project.userStartDate ? project.userStartDate : project.projectStartDate }} ~ {{ project.userEndDate ? project.userEndDate : project.projectEndDate }}
|
||
</template>
|
||
<!-- 없으면 종료일 표시 안함 -->
|
||
<template v-else>
|
||
{{ project.userStartDate ? project.userStartDate : project.projectStartDate }} ~
|
||
</template>
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div v-else>
|
||
<p>참여중인 프로젝트가 없습니다.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import axios from '@api' // API 호출용 Axios 인스턴스
|
||
import { ref, onMounted } from 'vue'
|
||
import SearchBar from '@c/search/SearchBar.vue'
|
||
|
||
export default {
|
||
name: 'PeopleList',
|
||
components: { SearchBar },
|
||
setup() {
|
||
const allUserList = ref([]) // 전체 사원 목록
|
||
const user = ref({}) // 현재 로그인한 사용자 (필요 시 사용)
|
||
const showModal = ref(false) // 모달 표시 여부
|
||
const selectedPerson = ref({})// 모달에 표시할 선택된 사원 정보
|
||
const memberProjects = ref([])// 선택된 사원의 프로젝트 목록
|
||
|
||
onMounted(async () => {
|
||
try {
|
||
const response = await axios.get('user/allUserList')
|
||
allUserList.value = response.data.data.allUserList
|
||
user.value = response.data.data.user
|
||
} catch (error) {
|
||
console.error('사원 목록 조회 실패:', error)
|
||
}
|
||
})
|
||
|
||
const baseUrl = axios.defaults.baseURL.replace(/api\/$/, '')
|
||
const defaultProfile = '/img/icons/icon.png'
|
||
|
||
const getProfileImage = (profilePath) => {
|
||
return profilePath && profilePath.trim()
|
||
? `${baseUrl}upload/img/profile/${profilePath}`
|
||
: defaultProfile
|
||
}
|
||
|
||
const setDefaultImage = (event) => {
|
||
event.target.src = defaultProfile
|
||
}
|
||
|
||
// 참여 프로젝트
|
||
const fetchMemberProjects = async (memberSeq) => {
|
||
try {
|
||
const res = await axios.get(`project/people/${memberSeq}`)
|
||
memberProjects.value = res.data.data
|
||
} catch (error) {
|
||
console.error('프로젝트 조회 실패:', error)
|
||
memberProjects.value = []
|
||
}
|
||
}
|
||
|
||
const openModal = (person) => {
|
||
selectedPerson.value = person
|
||
fetchMemberProjects(person.MEMBERSEQ)
|
||
showModal.value = true
|
||
}
|
||
|
||
const closeModal = () => {
|
||
showModal.value = false
|
||
}
|
||
|
||
return {
|
||
allUserList,
|
||
user,
|
||
showModal,
|
||
selectedPerson,
|
||
memberProjects,
|
||
openModal,
|
||
closeModal,
|
||
getProfileImage,
|
||
defaultProfile,
|
||
setDefaultImage
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.container-xxl {
|
||
padding: 1rem;
|
||
}
|
||
|
||
.card-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 1rem;
|
||
justify-content: center;
|
||
}
|
||
|
||
.person-card {
|
||
width: 280px;
|
||
border: 1px solid #eee;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
background: #fff;
|
||
transition: box-shadow 0.2s ease-in-out;
|
||
}
|
||
|
||
.person-card:hover {
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
.person-card .card-header {
|
||
width: 100%;
|
||
height: 120px;
|
||
overflow: hidden;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.user-avatar {
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
object-fit: cover;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.person-card .card-body {
|
||
padding: 0.75rem;
|
||
}
|
||
|
||
.person-name {
|
||
margin: 0;
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.person-email,
|
||
.person-phone {
|
||
margin: 0;
|
||
font-size: 0.9rem;
|
||
color: #555;
|
||
}
|
||
|
||
/* 모달 스타일 */
|
||
.modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 111%;
|
||
background-color: rgba(0, 0, 0, 0.4);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 999;
|
||
}
|
||
|
||
.modal-content {
|
||
position: relative;
|
||
width: 400px;
|
||
background: #fff;
|
||
padding: 1.5rem;
|
||
border-radius: 8px;
|
||
animation: slideDown 0.3s ease forwards;
|
||
}
|
||
|
||
.close-btn {
|
||
background: transparent;
|
||
border: none;
|
||
font-size: 1.5rem;
|
||
position: absolute;
|
||
top: 0.5rem;
|
||
right: 0.5rem;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.modal-body {
|
||
text-align: center;
|
||
}
|
||
|
||
.modal-img {
|
||
width: 50%;
|
||
height: 50%;
|
||
border-radius: 50%;
|
||
margin-bottom: 1rem;
|
||
object-fit: cover;
|
||
}
|
||
|
||
/* 프로젝트 리스트 스타일 */
|
||
.project-item {
|
||
display: flex;
|
||
align-items: center;
|
||
list-style: none;
|
||
font-size: 0.9rem;
|
||
padding: 0.25rem 0;
|
||
}
|
||
|
||
.project-name {
|
||
font-weight: 600;
|
||
}
|
||
|
||
.project-period {
|
||
font-size: 1rem;
|
||
color: #888;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
@keyframes slideDown {
|
||
0% {
|
||
transform: translateY(-15%);
|
||
opacity: 0;
|
||
}
|
||
100% {
|
||
transform: translateY(0);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
</style>
|