Compare commits
No commits in common. "main" and "mypage" have entirely different histories.
@ -138,20 +138,17 @@
|
||||
// 영상 넣기
|
||||
quillInstance.getModule('toolbar').addHandler('video', () => {
|
||||
const url = prompt('YouTube 영상 URL을 입력하세요:');
|
||||
|
||||
let src = '';
|
||||
|
||||
if (!url || url.trim() == '') return;
|
||||
|
||||
// 일반 youtube url
|
||||
if (url.indexOf('watch?v=') !== -1) {
|
||||
if (url.indexOf('watch?v=') != -1) {
|
||||
src = url.replace('watch?v=', 'embed/');
|
||||
|
||||
// youtu.be 단축 URL (ex : https://youtu.be/CfiojceAaeQ?si=G7eM56sdDjIEw-Tz)
|
||||
} else if (url.indexOf('youtu.be/') !== -1) {
|
||||
const videoId = url.split('youtu.be/')[1].split('?')[0];
|
||||
src = `https://www.youtube.com/embed/${videoId}`;
|
||||
|
||||
// iframe 주소
|
||||
} else if (url.indexOf('<iframe') !== -1) {
|
||||
} else if (url.indexOf('<iframe') != -1) {
|
||||
// DOMParser를 사용하여 embeded url만 추출
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(url, 'text/html');
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
:value="computedValue"
|
||||
:disabled="disabled"
|
||||
:maxLength="maxlength"
|
||||
:placeholder="placeholder ? placeholder : title"
|
||||
:placeholder="title"
|
||||
@blur="$emit('blur')"
|
||||
/>
|
||||
<span class="input-group-text">@ localhost.co.kr</span>
|
||||
@ -29,7 +29,7 @@
|
||||
:value="computedValue"
|
||||
:disabled="disabled"
|
||||
:maxLength="maxlength"
|
||||
:placeholder="placeholder ? placeholder : title"
|
||||
:placeholder="title"
|
||||
@blur="$emit('blur')"
|
||||
@click="handleDateClick"
|
||||
ref="inputElement"
|
||||
@ -89,10 +89,6 @@
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:data', 'update:alert', 'blur']);
|
||||
|
||||
@ -14,14 +14,7 @@
|
||||
{{ user.name }}
|
||||
</p>
|
||||
|
||||
<CommuterBtn
|
||||
ref="workTimeComponentRef"
|
||||
:userId="user.id"
|
||||
:checkedInProject="checkedInProject || {}"
|
||||
:pendingProjectChange="pendingProjectChange"
|
||||
@update:pendingProjectChange="pendingProjectChange = $event"
|
||||
@leaveTimeUpdated="handleLeaveTimeUpdate"
|
||||
/>
|
||||
<CommuterBtn :userId="user.id" :checkedInProject="checkedInProject || {}" ref="workTimeComponentRef" />
|
||||
|
||||
<MainEventList
|
||||
:categoryList="categoryList"
|
||||
@ -106,7 +99,6 @@
|
||||
|
||||
const selectedProject = ref(null);
|
||||
const checkedInProject = ref(null);
|
||||
const pendingProjectChange = ref(null);
|
||||
|
||||
// 이벤트 모달 관련
|
||||
const showModal = ref(false);
|
||||
@ -613,48 +605,10 @@
|
||||
if (newProject) {
|
||||
selectedProject.value = newProject.PROJCTSEQ;
|
||||
checkedInProject.value = newProject;
|
||||
} else {
|
||||
selectedProject.value = null;
|
||||
checkedInProject.value = null;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const handleLeaveTimeUpdate = async event => {
|
||||
const memberSeq = user.value.id;
|
||||
if (!memberSeq) return;
|
||||
|
||||
// 현 사용자 프로젝트 퇴근기록 확인
|
||||
const { data } = await $api.post('main/getUserLeaveRecord', {
|
||||
memberSeq: memberSeq,
|
||||
});
|
||||
|
||||
const res = data?.data;
|
||||
if (res && !res?.COMMUTLVE) {
|
||||
await projectStore.getMemberProjects();
|
||||
|
||||
if (projectStore.activeMemberProjectList.length > 0) {
|
||||
const previousProject =
|
||||
projectStore.activeMemberProjectList.find(p => res.MEMBERSEQ === user.value.id && res.PROJCTLVE === p.PROJCTSEQ) ||
|
||||
projectStore.activeMemberProjectList[0]; // 이전 출근 프로젝트가 없으면 첫 번째 프로젝트
|
||||
|
||||
if (previousProject) {
|
||||
selectedProject.value = previousProject.PROJCTSEQ;
|
||||
projectStore.setSelectedProject(previousProject);
|
||||
} else if (projectStore.activeProjectList.length > 0) {
|
||||
selectedProject.value = projectStore.activeProjectList[0].PROJCTSEQ;
|
||||
projectStore.setSelectedProject(projectStore.activeProjectList[0]);
|
||||
} else {
|
||||
selectedProject.value = null;
|
||||
projectStore.setSelectedProject(null);
|
||||
}
|
||||
} else {
|
||||
selectedProject.value = null;
|
||||
projectStore.setSelectedProject(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await userStore.userInfo();
|
||||
user.value = userStore.user;
|
||||
|
||||
@ -50,15 +50,14 @@
|
||||
<label class="switch"
|
||||
><input
|
||||
type="checkbox"
|
||||
:checked="member.checked"
|
||||
@click="handleRegisterMember($event, member)" />
|
||||
<span class="slider round"></span
|
||||
:checked="checked"
|
||||
@change="handleRegisterMember(member.MEMBERSEQ)" /><span class="slider round"></span
|
||||
></label>
|
||||
</div>
|
||||
<button
|
||||
class="btn-close btn-close-sm"
|
||||
style="position: absolute; top: 10px; right: 10px"
|
||||
@click="handleRejectMember(member)"
|
||||
@click="handleRejectMember(member.MEMBERSEQ)"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
@ -77,37 +76,30 @@
|
||||
import $api from '@api';
|
||||
|
||||
const memberList = ref([]);
|
||||
const checked = ref(false);
|
||||
const toast = useToastStore();
|
||||
const imgURL = import.meta.env.VITE_SERVER_IMG_URL;
|
||||
|
||||
// 조회 api
|
||||
const fetchRegisterMemberList = async () => {
|
||||
const { data } = await $api.get('main/registerMemberList');
|
||||
if (data?.data) {
|
||||
memberList.value = data.data.map(member => ({
|
||||
...member,
|
||||
checked: false, // 각 항목에 checked 값 설정
|
||||
}));
|
||||
}
|
||||
if (data?.data) memberList.value = data.data;
|
||||
};
|
||||
|
||||
// 사원 등록 api
|
||||
const handleRegisterMember = async (e, member) => {
|
||||
e.preventDefault();
|
||||
|
||||
const { data } = await $api.post('main/registerMember', { memberSeq: member.MEMBERSEQ });
|
||||
const handleRegisterMember = async memberSeq => {
|
||||
const { data } = await $api.post('main/registerMember', { memberSeq: memberSeq });
|
||||
if (data?.data) {
|
||||
member.checked = true;
|
||||
toast.onToast(data.data, 's');
|
||||
fetchRegisterMemberList();
|
||||
}
|
||||
};
|
||||
|
||||
// 사원 등록 미승인 api
|
||||
const handleRejectMember = async member => {
|
||||
const handleRejectMember = async memberSeq => {
|
||||
if (!confirm('해당 사원 등록을 거절하시겠습니까?')) return;
|
||||
|
||||
const { data } = await $api.post('main/rejectMember', { memberSeq: member.MEMBERSEQ });
|
||||
const { data } = await $api.post('main/rejectMember', { memberSeq: memberSeq });
|
||||
if (data?.data) {
|
||||
toast.onToast(data.data, 's');
|
||||
fetchRegisterMemberList();
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
@update:alert="idAlert = $event"
|
||||
@blur="checkIdDuplicate"
|
||||
:value="id"
|
||||
@keypress="noSpace"
|
||||
/>
|
||||
<span v-if="idError" class="invalid-feedback d-block">{{ idError }}</span>
|
||||
|
||||
@ -38,7 +37,6 @@
|
||||
@update:data="password = $event"
|
||||
@update:alert="passwordAlert = $event"
|
||||
:value="password"
|
||||
@keypress="noSpace"
|
||||
/>
|
||||
<span v-if="passwordError" class="invalid-feedback d-block">{{ passwordError }}</span>
|
||||
|
||||
@ -51,7 +49,6 @@
|
||||
@update:data="passwordcheck = $event"
|
||||
@update:alert="passwordcheckAlert = $event"
|
||||
:value="passwordcheck"
|
||||
@keypress="noSpace"
|
||||
/>
|
||||
<span v-if="passwordcheckError" class="invalid-feedback d-block">{{ passwordcheckError }}</span>
|
||||
|
||||
@ -85,7 +82,6 @@
|
||||
@update:data="name = $event"
|
||||
@update:alert="nameAlert = $event"
|
||||
:value="name"
|
||||
@keypress="noSpace"
|
||||
class="me-2 w-50"
|
||||
/>
|
||||
|
||||
@ -218,10 +214,6 @@
|
||||
|
||||
const toastStore = useToastStore();
|
||||
|
||||
const noSpace = (e) => {
|
||||
if (e.key === ' ') e.preventDefault();
|
||||
};
|
||||
|
||||
// 프로필 체크
|
||||
const profileValid = (size, type) => {
|
||||
const maxSize = 5 * 1024 * 1024;
|
||||
@ -352,7 +344,6 @@
|
||||
});
|
||||
|
||||
watch(password, (newValue) => {
|
||||
|
||||
if (newValue && newValue.length >= 4) {
|
||||
passwordErrorAlert.value = false;
|
||||
passwordError.value = '';
|
||||
@ -405,10 +396,8 @@
|
||||
} else {
|
||||
passwordError.value = '';
|
||||
}
|
||||
const phoneRegex = /^010\d{8}$/;
|
||||
const isFormatValid = phoneRegex.test(phone.value);
|
||||
|
||||
if (!/^\d+$/.test(phone.value) || !isFormatValid) {
|
||||
if (!/^\d+$/.test(phone.value)) {
|
||||
phoneAlert.value = true;
|
||||
} else {
|
||||
phoneAlert.value = false;
|
||||
@ -445,13 +434,13 @@
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('memberIds', id.value.trim());
|
||||
formData.append('memberPwd', password.value.trim());
|
||||
formData.append('memberIds', id.value);
|
||||
formData.append('memberPwd', password.value);
|
||||
formData.append('memberPwh', pwhint.value);
|
||||
formData.append('memberPwr', pwhintRes.value.trim());
|
||||
formData.append('memberNam', name.value.trim());
|
||||
formData.append('memberPwr', pwhintRes.value);
|
||||
formData.append('memberNam', name.value);
|
||||
formData.append('memberArr', address.value);
|
||||
formData.append('memberDtl', detailAddress.value.trim());
|
||||
formData.append('memberDtl', detailAddress.value);
|
||||
formData.append('memberZip', postcode.value);
|
||||
formData.append('memberBth', birth.value);
|
||||
formData.append('memberTel', phone.value);
|
||||
|
||||
@ -80,12 +80,12 @@
|
||||
<div class="text-truncate">Authorization</div>
|
||||
</RouterLink>
|
||||
</li>
|
||||
<li class="menu-item" :class="$route.path.includes('/people') ? 'active' : ''">
|
||||
<RouterLink class="menu-link" to="/people"> <i class="bi "></i>
|
||||
<i class="menu-icon icon-base bi bi-people-fill"></i>
|
||||
<div class="text-truncate">people</div>
|
||||
<!-- <li class="menu-item" :class="$route.path.includes('/sample') ? 'active' : ''">
|
||||
<RouterLink class="menu-link" to="/sample"> <i class="bi "></i>
|
||||
<i class="menu-icon tf-icons bx bx-calendar"></i>
|
||||
<div class="text-truncate">Sample</div>
|
||||
</RouterLink>
|
||||
</li>
|
||||
</li> -->
|
||||
</ul>
|
||||
</aside>
|
||||
<!-- / Menu -->
|
||||
@ -94,7 +94,6 @@
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import { useUserInfoStore } from '@s/useUserInfoStore';
|
||||
import "bootstrap-icons/font/bootstrap-icons.css";
|
||||
|
||||
const userStore = useUserInfoStore();
|
||||
const allowedUserId = 1; // 특정 ID (변경필요!!)
|
||||
|
||||
@ -38,36 +38,25 @@
|
||||
<!-- Notification -->
|
||||
<li class="nav-item dropdown-notifications navbar-dropdown dropdown me-3 me-xl-2 p-0">
|
||||
<a
|
||||
class="nav-link dropdown-toggle hide-arrow p-0"
|
||||
href="javascript:void(0);"
|
||||
data-bs-toggle="dropdown"
|
||||
data-bs-auto-close="outside"
|
||||
aria-expanded="false"
|
||||
class="nav-link dropdown-toggle hide-arrow p-0"
|
||||
href="javascript:void(0);"
|
||||
data-bs-toggle="dropdown"
|
||||
data-bs-auto-close="outside"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="position-relative">
|
||||
<i class="bx bx-bell bx-md"></i>
|
||||
<!-- 알림이 있을 경우에만 뱃지를 표시 -->
|
||||
<span
|
||||
v-if="notificationCount > 0"
|
||||
class="badge rounded-pill bg-danger badge-dot badge-notifications border"
|
||||
></span>
|
||||
</span>
|
||||
<span class="position-relative">
|
||||
<i class="bx bx-bell bx-md"></i>
|
||||
<span class="badge rounded-pill bg-danger badge-dot badge-notifications border"></span>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end p-0">
|
||||
<li class="dropdown-notifications-list scrollable-container p-3">
|
||||
<!-- 알림이 없으면 "알림이 없습니다." 메시지 표시 -->
|
||||
<div v-if="notificationCount === 0">
|
||||
알림이 없습니다.
|
||||
</div>
|
||||
<!-- 알림이 있을 때 목록 렌더링-->
|
||||
<div v-else>
|
||||
<ul>
|
||||
<li v-for="notification in notifications" :key="notification.id">
|
||||
{{ notification.text }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="dropdown-notifications-list scrollable-container p-3">
|
||||
알림이 없습니다.
|
||||
<!-- <ul class="list-group list-group-flush">
|
||||
<li class="list-group-item list-group-item-action dropdown-notifications-item">
|
||||
</li>
|
||||
</ul> -->
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!--/ Notification -->
|
||||
@ -131,8 +120,6 @@
|
||||
const selectedProject = ref(null);
|
||||
const weather = ref({});
|
||||
const dailyWeatherList = ref([]);
|
||||
const notifications = ref([]);
|
||||
const notificationCount = ref(0);
|
||||
|
||||
const weatherReady = computed(() => {
|
||||
return (
|
||||
|
||||
@ -109,12 +109,6 @@ const routes = [
|
||||
component: () => import('@v/admin/TheAuthorization.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/people',
|
||||
name: 'people',
|
||||
component: () => import('@v/people/PeopleList.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/error/400',
|
||||
name: 'Error400',
|
||||
|
||||
@ -40,13 +40,7 @@ export const useWeatherStore = defineStore('weather', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 검은색 태양 아이콘 변경
|
||||
dailyWeatherList.value = resData.dailyWeatherList.map(w => {
|
||||
return {
|
||||
...w,
|
||||
icon: w.icon.replace(/n$/, 'd'),
|
||||
};
|
||||
});
|
||||
dailyWeatherList.value = resData.dailyWeatherList;
|
||||
|
||||
const now = new Date();
|
||||
const todayStr = now.toISOString().split('T')[0];
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<!-- 프로필 사진 -->
|
||||
<img
|
||||
:src="getProfileImage(user.photo)"
|
||||
class="user-avatar2"
|
||||
class="profile-img"
|
||||
alt="프로필 사진"
|
||||
@error="setDefaultImage"
|
||||
/>
|
||||
@ -107,14 +107,5 @@ onMounted(fetchUsers);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.user-avatar2 {
|
||||
width: 160px;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
margin: 1rem auto 0 auto;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
/* 필요에 따라 스타일 추가 */
|
||||
</style>
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
<tr>
|
||||
<th style="width: 11%" class="text-center fw-bold">번호</th>
|
||||
<th style="width: 45%" class="text-center fw-bold">제목</th>
|
||||
<th style="width: 10%" class="text-strat fw-bold">작성자</th>
|
||||
<th style="width: 10%" class="text-center fw-bold">작성자</th>
|
||||
<th style="width: 15%" class="text-center fw-bold">작성일</th>
|
||||
<th style="width: 9%" class="text-center fw-bold">조회수</th>
|
||||
</tr>
|
||||
|
||||
@ -278,7 +278,7 @@
|
||||
|
||||
try {
|
||||
const boardData = {
|
||||
LOCBRDTTL: title.value.trim(),
|
||||
LOCBRDTTL: title.value,
|
||||
LOCBRDCON: JSON.stringify(content.value), // Delta 포맷을 JSON으로 변환
|
||||
LOCBRDNIC: categoryValue.value === 300102 ? nickname.value : null,
|
||||
LOCBRDPWD: categoryValue.value === 300102 ? password.value : null,
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
</span>
|
||||
|
||||
<!-- 기존 비밀번호 입력 -->
|
||||
<UserFormInput title="비밀번호 재설정" placeholder="기존 비밀번호를 입력하세요" name="currentPw" type="password"
|
||||
<UserFormInput title="기존 비밀번호" name="currentPw" type="password"
|
||||
:value="password.current" @update:data="password.current = $event"
|
||||
@blur="checkCurrentPassword" @keypress="noSpace" />
|
||||
<span v-if="passwordError" class="text-danger invalid-feedback mt-1 d-block">
|
||||
|
||||
@ -1,318 +0,0 @@
|
||||
<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
|
||||
class="user-avatar2"
|
||||
: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" class="project-list-container">
|
||||
<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
|
||||
}
|
||||
|
||||
// 참여 프로젝트 조회 API
|
||||
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 {
|
||||
width: 160px;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #ddd;
|
||||
display: block;
|
||||
margin: 1rem auto 0 auto;
|
||||
}
|
||||
|
||||
.user-avatar2 {
|
||||
width: 160px;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
margin: 1rem auto 0 auto;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.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-list-container {
|
||||
max-height: 200px; /* 필요에 따라 높이 조절 */
|
||||
overflow-y: auto;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.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>
|
||||
Loading…
Reference in New Issue
Block a user