Merge branch 'main' into mypage
This commit is contained in:
commit
7a0df53121
@ -582,7 +582,7 @@
|
||||
|
||||
/* commuters */
|
||||
.commuter-list {
|
||||
max-height: 358px;
|
||||
max-height: 450px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
<!-- 뒤로가기 -->
|
||||
<button
|
||||
@click="goBack"
|
||||
:disabled="!canGoBack"
|
||||
:class="{ 'shifted': showButton }"
|
||||
class="back-btn rounded-pill btn-icon btn-secondary position-fixed shadow z-5 border-0">
|
||||
<i class='bx bx-chevron-left'></i>
|
||||
@ -18,29 +19,54 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import { ref, onMounted, onUnmounted, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
const showButton = ref(false);
|
||||
const canGoBack = ref(false);
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const loginPage = "/login"; // 로그인 페이지 경로
|
||||
|
||||
// 스크롤 이벤트 핸들러
|
||||
const handleScroll = () => {
|
||||
showButton.value = window.scrollY > 200;
|
||||
};
|
||||
|
||||
// 최상단으로 스크롤 이동
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
|
||||
// 뒤로 가기 처리
|
||||
const goBack = () => {
|
||||
window.history.back();
|
||||
if (canGoBack.value) {
|
||||
router.back();
|
||||
}
|
||||
};
|
||||
|
||||
// 뒤로 가기 가능 여부 확인 함수
|
||||
const updateCanGoBack = () => {
|
||||
const historyBack = router.options.history.state.back;
|
||||
const previousPage = document.referrer;
|
||||
|
||||
canGoBack.value = !!historyBack && historyBack !== loginPage && !previousPage.includes(loginPage);
|
||||
};
|
||||
|
||||
// 마운트 시 한 번 실행
|
||||
onMounted(() => {
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
updateCanGoBack();
|
||||
});
|
||||
|
||||
// 라우트가 변경될 때마다 `canGoBack` 업데이트
|
||||
watch(route, () => {
|
||||
updateCanGoBack();
|
||||
});
|
||||
|
||||
// 언마운트 시 이벤트 제거
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
});
|
||||
</script>
|
||||
|
||||
</script>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="container-xxl flex-grow-1 container-p-y pb-0">
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="card app-calendar-wrapper">
|
||||
<div class="row g-0">
|
||||
<div class="col-3 border-end text-center" id="app-calendar-sidebar">
|
||||
@ -70,6 +70,8 @@
|
||||
출근 :
|
||||
<MapPopover
|
||||
:address="commuter.projectAddress"
|
||||
:is-visible="visiblePopover.type === 'project' && visiblePopover.index === index"
|
||||
@update-visible="updatePopover('project', index)"
|
||||
v-if="commuter.projectAddress"
|
||||
>
|
||||
<template #trigger>
|
||||
@ -90,6 +92,8 @@
|
||||
퇴근 :
|
||||
<MapPopover
|
||||
:address="commuter.leaveProjectAddress"
|
||||
:is-visible="visiblePopover.type === 'leave' && visiblePopover.index === index"
|
||||
@update-visible="updatePopover('leave', index)"
|
||||
v-if="commuter.leaveProjectAddress"
|
||||
>
|
||||
<template #trigger>
|
||||
@ -154,6 +158,12 @@ const checkedInProject = ref(null);
|
||||
|
||||
const isModalOpen = ref(false);
|
||||
|
||||
const visiblePopover = ref({
|
||||
type: null, // 'project' 또는 'leave'
|
||||
index: null // 팝오버 인덱스
|
||||
});
|
||||
|
||||
|
||||
const commuters = ref([]);
|
||||
const monthlyCommuters = ref([]);
|
||||
|
||||
@ -429,6 +439,17 @@ const closeModal = () => {
|
||||
isModalOpen.value = false;
|
||||
};
|
||||
|
||||
// MapPopover에서 visible 상태 변경 이벤트 처리
|
||||
const updatePopover = (popoverType, index) => {
|
||||
if (visiblePopover.value.type === popoverType && visiblePopover.value.index === index) {
|
||||
// 같은 팝오버를 클릭하면 닫기
|
||||
visiblePopover.value = { type: null, index: null };
|
||||
} else {
|
||||
// 다른 팝오버를 클릭하면 기존 것 닫고 새로운 것 열기
|
||||
visiblePopover.value = { type: popoverType, index: index };
|
||||
}
|
||||
};
|
||||
|
||||
const selectedDateCommuters = computed(() => {
|
||||
return monthlyCommuters.value.filter(commuter =>
|
||||
commuter.COMMUTDAY === eventDate.value
|
||||
|
||||
@ -34,7 +34,8 @@
|
||||
<div class="d-flex flex-sm-row align-items-center pb-2">
|
||||
<MapPopover
|
||||
:address="address"
|
||||
:ref="mapIconRef"
|
||||
:is-visible="isMapVisible"
|
||||
@update-visible="updatePopover"
|
||||
>
|
||||
<template #trigger>
|
||||
<div class="d-flex align-items-center cursor-pointer">
|
||||
@ -253,7 +254,7 @@ const emit = defineEmits(['update']);
|
||||
const isModalOpen = ref(false);
|
||||
const logData = ref([]);
|
||||
|
||||
const mapIconRef = ref(null);
|
||||
const isMapVisible = ref(null);
|
||||
|
||||
// 수정 모달 상태
|
||||
const isEditModalOpen = ref(false);
|
||||
@ -291,6 +292,9 @@ const openEndDatePicker = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const updatePopover = (visible) => {
|
||||
isMapVisible.value = visible;
|
||||
};
|
||||
|
||||
// 사용자 목록 업데이트 핸들러
|
||||
const handleEditUserListUpdate = (userLists) => {
|
||||
|
||||
@ -47,12 +47,17 @@
|
||||
|
||||
const props = defineProps({
|
||||
address: {
|
||||
type: String,
|
||||
required: true
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isVisible: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
}
|
||||
});
|
||||
|
||||
const isVisible = ref(false);
|
||||
const emit = defineEmits(['update-visible']);
|
||||
|
||||
const coordinates = ref(null);
|
||||
const map = ref(null);
|
||||
|
||||
@ -79,7 +84,7 @@
|
||||
};
|
||||
|
||||
const togglePopover = () => {
|
||||
isVisible.value = !isVisible.value;
|
||||
emit('update-visible', !props.isVisible);
|
||||
};
|
||||
|
||||
const onLoadKakaoMap = (mapRef) => {
|
||||
|
||||
@ -48,7 +48,6 @@
|
||||
:is-alert="passwordcheckAlert"
|
||||
@update:data="passwordcheck = $event"
|
||||
@update:alert="passwordcheckAlert = $event"
|
||||
@blur="checkPw"
|
||||
:value="passwordcheck"
|
||||
/>
|
||||
<span v-if="passwordcheckError" class="invalid-feedback d-block">{{ passwordcheckError }}</span>
|
||||
@ -299,17 +298,6 @@
|
||||
postcode.value = addressData.postcode; // 우편번호
|
||||
};
|
||||
|
||||
// 비밀번호 확인 체크
|
||||
const checkPw = async () => {
|
||||
if (password.value !== passwordcheck.value) {
|
||||
passwordcheckError.value = '비밀번호가 일치하지 않습니다.';
|
||||
passwordcheckErrorAlert.value = true;
|
||||
} else {
|
||||
passwordcheckError.value = '';
|
||||
passwordcheckErrorAlert.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 색상 중복체크
|
||||
const checkColorDuplicate = async () => {
|
||||
const response = await $api.get(`/user/checkColor?memberCol=${color.value}`);
|
||||
@ -358,6 +346,19 @@
|
||||
}
|
||||
});
|
||||
|
||||
// 비밀번호 확인
|
||||
watch([password, passwordcheck], ([newPassword, newPasswordCheck]) => {
|
||||
if (newPassword && newPasswordCheck) {
|
||||
if (newPassword !== newPasswordCheck) {
|
||||
passwordcheckError.value = '비밀번호가 일치하지 않습니다.';
|
||||
passwordcheckErrorAlert.value = true;
|
||||
} else {
|
||||
passwordcheckError.value = '';
|
||||
passwordcheckErrorAlert.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 회원가입
|
||||
const handleSubmit = async () => {
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
alt="user"
|
||||
/>
|
||||
<div class="w-100">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="user-info">
|
||||
<h6 class="mb-1">{{ data.localVote.MEMBERNAM }}</h6>
|
||||
<!-- 투표완료시 -->
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
<div class="d-flex align-items-start">
|
||||
<!-- 최초 작성자 -->
|
||||
<div class="d-flex flex-wrap align-items-center me-4">
|
||||
<div class="avatar avatar-sm me-2">
|
||||
<div class="avatar me-2">
|
||||
<img
|
||||
class="rounded-circle user-avatar"
|
||||
:src="getProfileImage(item.author.profileImage)"
|
||||
@ -48,7 +48,7 @@
|
||||
v-if="item.author.createdAt !== item.lastEditor.updatedAt"
|
||||
class="d-flex flex-wrap align-items-center"
|
||||
>
|
||||
<div class="avatar avatar-sm me-2">
|
||||
<div class="avatar me-2">
|
||||
<img
|
||||
class="rounded-circle user-avatar"
|
||||
:src="getProfileImage(item.lastEditor.profileImage)"
|
||||
|
||||
@ -167,7 +167,8 @@
|
||||
v-if="user"
|
||||
:src="`${baseUrl}upload/img/profile/${user.profile}`"
|
||||
alt="Profile Image"
|
||||
class="w-px-40 h-px-40 rounded-circle"
|
||||
class="w-px-40 h-px-40 rounded-circle border border-3"
|
||||
:style="`border-color: ${user.usercolor} !important;`"
|
||||
@error="$event.target.src = '/img/icons/icon.png'"
|
||||
/>
|
||||
</a>
|
||||
@ -250,7 +251,6 @@
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useThemeStore } from '@s/darkmode';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import $api from '@api';
|
||||
|
||||
const baseUrl = import.meta.env.VITE_SERVER;
|
||||
|
||||
@ -312,7 +312,6 @@
|
||||
await userStore.userInfo();
|
||||
user.value = userStore.user;
|
||||
|
||||
|
||||
await projectStore.loadAllProjectLists();
|
||||
|
||||
// 사용자가 참여하고 있는 프로젝트 목록
|
||||
|
||||
@ -141,13 +141,34 @@
|
||||
}
|
||||
};
|
||||
|
||||
function extractPlainText(delta) {
|
||||
if (!delta || !Array.isArray(delta.ops)) return '';
|
||||
return delta.ops
|
||||
.filter(op => typeof op.insert === 'string')
|
||||
.map(op => op.insert.trim())
|
||||
.join(' ')
|
||||
.trim();
|
||||
function isDeltaChanged(current, original) {
|
||||
const Delta = Quill.import('delta');
|
||||
const currentDelta = new Delta(current || []);
|
||||
const originalDelta = new Delta(original || []);
|
||||
|
||||
const diff = originalDelta.diff(currentDelta);
|
||||
if (!diff || diff.ops.length === 0) return false;
|
||||
|
||||
// 텍스트만 비교해서 완전 동일한지 확인
|
||||
const getPlainText = delta =>
|
||||
(delta.ops || [])
|
||||
.filter(op => typeof op.insert === 'string')
|
||||
.map(op => op.insert)
|
||||
.join('');
|
||||
|
||||
const getImages = delta =>
|
||||
(delta.ops || []).filter(op => typeof op.insert === 'object' && op.insert.image).map(op => op.insert.image);
|
||||
|
||||
const textCurrent = getPlainText(currentDelta);
|
||||
const textOriginal = getPlainText(originalDelta);
|
||||
|
||||
const imgsCurrent = getImages(currentDelta);
|
||||
const imgsOriginal = getImages(originalDelta);
|
||||
|
||||
const textEqual = textCurrent === textOriginal;
|
||||
const imageEqual = JSON.stringify(imgsCurrent) === JSON.stringify(imgsOriginal);
|
||||
|
||||
return !(textEqual && imageEqual); // 둘 다 같아야 false
|
||||
}
|
||||
|
||||
const isChanged = computed(() => {
|
||||
@ -212,16 +233,6 @@
|
||||
contentLoaded.value = true;
|
||||
};
|
||||
|
||||
watch(
|
||||
content,
|
||||
val => {
|
||||
if (contentLoaded.value && !originalPlainText.value) {
|
||||
originalPlainText.value = extractPlainText(val);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const handleUpdateEditorImg = item => {
|
||||
editorUploadedImgList.value = item;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user