수정사항 완료

This commit is contained in:
yoon 2025-02-26 19:09:04 +09:00
parent 37c335fc12
commit 7e484c12be
3 changed files with 293 additions and 268 deletions

View File

@ -39,7 +39,6 @@
<div <div
v-if="isPopoverVisible" v-if="isPopoverVisible"
class="position-absolute w-100 map text-end" class="position-absolute w-100 map text-end"
@click.stop
> >
<button type="button" class="btn-close popover-close" @click.stop="isPopoverVisible = !isPopoverVisible"></button> <button type="button" class="btn-close popover-close" @click.stop="isPopoverVisible = !isPopoverVisible"></button>
<div class="card"> <div class="card">
@ -48,6 +47,7 @@
v-if="coordinates" v-if="coordinates"
:lat="coordinates.lat" :lat="coordinates.lat"
:lng="coordinates.lng" :lng="coordinates.lng"
:draggable="false"
class="w-100 h-px-200" class="w-100 h-px-200"
> >
<KakaoMapMarker <KakaoMapMarker
@ -81,7 +81,7 @@
</template> </template>
<template #footer> <template #footer>
<button type="button" class="btn btn-secondary" @click="closeModal">닫기</button> <BackButton @click="closeModal" />
</template> </template>
</CenterModal> </CenterModal>
</template> </template>

View File

@ -7,7 +7,6 @@
<h5 class="modal-title m-auto fw-bold" id="modalCenterTitle"> <h5 class="modal-title m-auto fw-bold" id="modalCenterTitle">
<slot name="title">Modal Title</slot> <slot name="title">Modal Title</slot>
</h5> </h5>
<button type="button" class="btn-close" @click="closeModal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<slot name="body">Modal body</slot> <slot name="body">Modal body</slot>
@ -21,18 +20,27 @@
</template> </template>
<script setup> <script setup>
const prop = defineProps({ const prop = defineProps({
display : { display : {
type: Boolean, type: Boolean,
default: false, default: false,
required: true, required: true,
}, },
create: {
type: Boolean,
default: false,
}
}); });
const emit = defineEmits(['close']); const emit = defineEmits(['close' , 'reset']);
const closeModal = () => { const closeModal = () => {
emit('close' , false); if (prop.create) {
emit('reset');
}
emit('close', false);
}; };
</script> </script>
@ -45,3 +53,4 @@ const closeModal = () => {

View File

@ -1,5 +1,5 @@
<template> <template>
<SearchBar @update:data="search"/> <SearchBar @update:data="search" />
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<CategoryBtn :lists="yearCategory" @update:data="selectedCategory = $event" /> <CategoryBtn :lists="yearCategory" @update:data="selectedCategory = $event" />
<WriteBtn class="mt-2 ms-auto" @click="openCreateModal" /> <WriteBtn class="mt-2 ms-auto" @click="openCreateModal" />
@ -26,63 +26,70 @@
</div> </div>
<!-- 등록 모달 --> <!-- 등록 모달 -->
<CenterModal :display="isCreateModalOpen" @close="closeCreateModal"> <form @reset.prevent="formReset">
<template #title> 프로젝트 등록 </template> <CenterModal :display="isCreateModalOpen" @close="closeCreateModal" :create="true" @reset="formReset">
<template #body> <template #title> 프로젝트 등록 </template>
<FormInput <template #body>
title="이름" <FormInput
name="name" title="이름"
:is-essential="true" name="name"
:is-alert="nameAlert" :is-essential="true"
@update:modelValue="name = $event" :is-alert="nameAlert"
/> :modelValue="name"
@update:modelValue="name = $event"
@update:alert="nameAlert = $event"
/>
<FormSelect <FormSelect
title="컬러" title="컬러"
name="color" name="color"
:is-essential="true" :is-essential="true"
:is-label="true" :is-label="true"
:is-common="true" :is-common="true"
:data="colorList" :data="colorList"
@update:data="color = $event" @update:data="color = $event"
/> />
<FormInput <FormInput
title="시작 일" title="시작 일"
type="date" name="startDay"
name="startDay" :type="'date'"
v-model="startDay" :is-essential="true"
:is-essential="true" :modelValue="startDay"
/> v-model="startDay"
/>
<FormInput <FormInput
title="종료 일" title="종료 일"
name="endDay" :type="'date'"
:type="'date'" name="endDay"
@update:modelValue="endDay = $event" :modelValue="endDay"
/> @update:modelValue="endDay = $event"
/>
<FormInput <FormInput
title="설명" title="설명"
name="description" name="description"
@update:modelValue="description = $event" :modelValue="description"
/> @update:modelValue="description = $event"
/>
<ArrInput <ArrInput
title="주소" title="주소"
name="address" name="address"
:isEssential="true" :isEssential="true"
:is-row="true" :is-row="true"
:is-alert="addressAlert" :is-alert="addressAlert"
@update:data="handleAddressUpdate" @update:data="handleAddressUpdate"
@update:alert="addressAlert = $event" @update:alert="addressAlert = $event"
/> />
</template> </template>
<template #footer> <template #footer>
<BackButton @click="closeCreateModal" /> <BackButton type="reset" @click="closeCreateModal" />
<SaveButton @click="handleCreate" /> <SaveButton @click="handleCreate" />
</template> </template>
</CenterModal> </CenterModal>
</form>
<!-- 수정 모달 --> <!-- 수정 모달 -->
<CenterModal :display="isEditModalOpen" @close="closeEditModal"> <CenterModal :display="isEditModalOpen" @close="closeEditModal">
@ -95,6 +102,7 @@
:is-alert="nameAlert" :is-alert="nameAlert"
:modelValue="selectedProject.PROJCTNAM" :modelValue="selectedProject.PROJCTNAM"
@update:modelValue="selectedProject.PROJCTNAM = $event" @update:modelValue="selectedProject.PROJCTNAM = $event"
@update:alert="nameAlert = $event"
/> />
<FormSelect <FormSelect
@ -149,228 +157,236 @@
</template> </template>
<script setup> <script setup>
import { computed, inject, ref, watch, onMounted } from 'vue'; import { computed, ref, watch, onMounted, inject } from 'vue';
import SearchBar from '@c/search/SearchBar.vue'; import SearchBar from '@c/search/SearchBar.vue';
import ProjectCard from '@c/list/ProjectCard.vue'; import ProjectCard from '@c/list/ProjectCard.vue';
import CategoryBtn from '@c/category/CategoryBtn.vue'; import CategoryBtn from '@c/category/CategoryBtn.vue';
import WriteBtn from '@c/button/WriteBtn.vue'; import WriteBtn from '@c/button/WriteBtn.vue';
import CenterModal from '@c/modal/CenterModal.vue'; import CenterModal from '@c/modal/CenterModal.vue';
import FormSelect from '@c/input/FormSelect.vue'; import FormSelect from '@c/input/FormSelect.vue';
import FormInput from '@c/input/FormInput.vue'; import FormInput from '@c/input/FormInput.vue';
import ArrInput from '@c/input/ArrInput.vue'; import ArrInput from '@c/input/ArrInput.vue';
import commonApi from '@/common/commonApi'; import commonApi from '@/common/commonApi';
import { useToastStore } from '@s/toastStore'; import { useToastStore } from '@s/toastStore';
import { useUserInfoStore } from '@/stores/useUserInfoStore'; import { useUserInfoStore } from '@/stores/useUserInfoStore';
import { useProjectStore } from '@/stores/useProjectStore'; import { useProjectStore } from '@/stores/useProjectStore';
import $api from '@api'; import $api from '@api';
import SaveButton from '@c/button/SaveBtn.vue'; import SaveButton from '@c/button/SaveBtn.vue';
import BackButton from '@c/button/BackBtn.vue' import BackButton from '@c/button/BackBtn.vue';
const dayjs = inject('dayjs'); const toastStore = useToastStore();
const today = dayjs().format('YYYY-MM-DD'); const userStore = useUserInfoStore();
const toastStore = useToastStore(); const projectStore = useProjectStore();
const userStore = useUserInfoStore();
const projectStore = useProjectStore();
// //
const user = ref(null); const user = ref(null);
const selectedCategory = ref(null); const selectedCategory = ref(null);
const searchText = ref(''); const searchText = ref('');
// // dayjs
const isCreateModalOpen = ref(false); const dayjs = inject('dayjs');
const name = ref('');
const color = ref('');
const address = ref('');
const detailAddress = ref('');
const postcode = ref('');
const startDay = ref(today);
const endDay = ref('');
const description = ref('');
const nameAlert = ref(false);
const addressAlert = ref(false);
// // YYYY-MM-DD
const isEditModalOpen = ref(false); const today = dayjs().format('YYYY-MM-DD');
const originalColor = ref('');
const selectedProject = ref({
PROJCTSEQ: '',
PROJCTNAM: '',
PROJCTSTR: '',
PROJCTEND: '',
PROJCTZIP: '',
PROJCTARR: '',
PROJCTDTL: '',
PROJCTDES: '',
PROJCTCOL: '',
projctcolor: '',
});
// API //
const { yearCategory, colorList } = commonApi({ const isCreateModalOpen = ref(false);
loadColor: true, const name = ref('');
colorType: 'YNP', const color = ref('');
loadYearCategory: true, const address = ref('');
}); const detailAddress = ref('');
const postcode = ref('');
const startDay = ref(today);
const endDay = ref('');
const description = ref('');
const nameAlert = ref(false);
const addressAlert = ref(false);
// //
const search = async (searchKeyword) => { const isEditModalOpen = ref(false);
searchText.value = searchKeyword.trim(); const originalColor = ref('');
await getProjectList(); const selectedProject = ref({
}; PROJCTSEQ: '',
PROJCTNAM: '',
const selectedYear = computed(() => { PROJCTSTR: '',
if (!selectedCategory.value || selectedCategory.value === 900101) { PROJCTEND: '',
return null; PROJCTZIP: '',
} PROJCTARR: '',
// category label PROJCTDTL: '',
return yearCategory.value.find(item => item.value === selectedCategory.value)?.label || null; PROJCTDES: '',
}); PROJCTCOL: '',
projctcolor: '',
//
const getProjectList = async () => {
await projectStore.getProjectList(searchText.value, selectedYear.value);
};
//
watch(selectedCategory, async () => {
await getProjectList();
});
//
const openCreateModal = () => {
isCreateModalOpen.value = true;
};
const closeCreateModal = () => {
isCreateModalOpen.value = false;
resetCreateForm();
};
const resetCreateForm = () => {
name.value = '';
color.value = '';
address.value = '';
detailAddress.value = '';
postcode.value = '';
startDay.value = today;
endDay.value = '';
description.value = '';
nameAlert.value = false;
addressAlert.value = false;
};
// ::
const handleAddressUpdate = addressData => {
address.value = addressData.address;
detailAddress.value = addressData.detailAddress;
postcode.value = addressData.postcode;
};
//
const handleCreate = async () => {
nameAlert.value = name.value.trim() === '';
addressAlert.value = address.value.trim() === '';
if (nameAlert.value || addressAlert.value) {
return;
}
$api.post('project/insert', {
projctNam: name.value,
projctCol: color.value,
projctStr: startDay.value,
projctEnd: endDay.value || null,
projctDes: description.value || null,
projctArr: address.value,
projctDtl: detailAddress.value,
projctZip: postcode.value,
projctCmb: user.value.name,
})
.then(res => {
if (res.status === 200) {
toastStore.onToast('프로젝트가 등록되었습니다.', 's');
closeCreateModal();
getProjectList();
}
}); });
};
// // API
const openEditModal = (post) => { const { yearCategory, colorList } = commonApi({
isEditModalOpen.value = true; loadColor: true,
selectedProject.value = { ...post }; colorType: 'YNP',
originalColor.value = post.PROJCTCOL; loadYearCategory: true,
}; });
const closeEditModal = () => { //
isEditModalOpen.value = false; const search = async searchKeyword => {
}; searchText.value = searchKeyword.trim();
await getProjectList();
// +
const allColors = computed(() => {
const existingColor = { value: selectedProject.value.PROJCTCOL, label: selectedProject.value.projctcolor };
return [existingColor, ...colorList.value];
});
//
const hasChanges = computed(() => {
const original = projectStore.projectList.find(p => p.PROJCTSEQ === selectedProject.value.PROJCTSEQ);
if (!original) return false;
return (
original.PROJCTNAM !== selectedProject.value.PROJCTNAM ||
original.PROJCTCOL !== selectedProject.value.PROJCTCOL ||
original.PROJCTARR !== selectedProject.value.PROJCTARR ||
original.PROJCTDTL !== selectedProject.value.PROJCTDTL ||
original.PROJCTZIP !== selectedProject.value.PROJCTZIP ||
original.PROJCTSTR !== selectedProject.value.PROJCTSTR ||
original.PROJCTEND !== selectedProject.value.PROJCTEND ||
original.PROJCTDES !== selectedProject.value.PROJCTDES
);
});
// ::
const updateAddress = (addressData) => {
selectedProject.value = {
...selectedProject.value,
PROJCTZIP: addressData.postcode,
PROJCTARR: addressData.address,
PROJCTDTL: addressData.detailAddress
}; };
};
// const selectedYear = computed(() => {
const handleUpdate = () => { if (!selectedCategory.value || selectedCategory.value === 900101) {
if (!hasChanges.value) { return null;
toastStore.onToast('변경된 내용이 없습니다.', 'e'); }
return; // category label
return yearCategory.value.find(item => item.value === selectedCategory.value)?.label || null;
});
//
const getProjectList = async () => {
await projectStore.getProjectList(searchText.value, selectedYear.value);
};
//
watch(selectedCategory, async () => {
await getProjectList();
});
//
const openCreateModal = () => {
isCreateModalOpen.value = true;
};
const closeCreateModal = () => {
isCreateModalOpen.value = false;
};
const formReset = () => {
name.value = '';
color.value = '';
address.value = '';
detailAddress.value = '';
postcode.value = '';
startDay.value = today;
endDay.value = '';
description.value = '';
nameAlert.value = false;
addressAlert.value = false;
} }
$api.patch('project/update', { // ::
projctSeq: selectedProject.value.PROJCTSEQ, const handleAddressUpdate = addressData => {
projctNam: selectedProject.value.PROJCTNAM, address.value = addressData.address;
projctCol: selectedProject.value.PROJCTCOL, detailAddress.value = addressData.detailAddress;
projctArr: selectedProject.value.PROJCTARR, postcode.value = addressData.postcode;
projctDtl: selectedProject.value.PROJCTDTL, };
projctZip: selectedProject.value.PROJCTZIP,
projctStr: selectedProject.value.PROJCTSTR,
projctEnd: selectedProject.value.PROJCTEND || null,
projctDes: selectedProject.value.PROJCTDES || null,
projctUmb: user.value.name,
originalColor: originalColor.value === selectedProject.value.PROJCTCOL ? null : originalColor.value
}).then(res => {
if (res.status === 200) {
toastStore.onToast('수정이 완료 되었습니다.', 's');
closeEditModal();
location.reload();
}
});
};
onMounted(async () => { //
await getProjectList(); const handleCreate = async () => {
await userStore.userInfo(); nameAlert.value = name.value.trim() === '';
user.value = userStore.user; addressAlert.value = address.value.trim() === '';
});
if (nameAlert.value || addressAlert.value) {
return;
}
$api.post('project/insert', {
projctNam: name.value,
projctCol: color.value,
projctStr: startDay.value,
projctEnd: endDay.value || null,
projctDes: description.value || null,
projctArr: address.value,
projctDtl: detailAddress.value,
projctZip: postcode.value,
projctCmb: user.value.name,
}).then(res => {
if (res.status === 200) {
toastStore.onToast('프로젝트가 등록되었습니다.', 's');
closeCreateModal();
getProjectList();
}
});
};
//
const openEditModal = post => {
isEditModalOpen.value = true;
selectedProject.value = { ...post };
originalColor.value = post.PROJCTCOL;
};
const closeEditModal = () => {
isEditModalOpen.value = false;
};
// +
const allColors = computed(() => {
const existingColor = { value: selectedProject.value.PROJCTCOL, label: selectedProject.value.projctcolor };
return [existingColor, ...colorList.value];
});
//
const hasChanges = computed(() => {
const original = projectStore.projectList.find(p => p.PROJCTSEQ === selectedProject.value.PROJCTSEQ);
if (!original) return false;
return (
original.PROJCTNAM !== selectedProject.value.PROJCTNAM ||
original.PROJCTCOL !== selectedProject.value.PROJCTCOL ||
original.PROJCTARR !== selectedProject.value.PROJCTARR ||
original.PROJCTDTL !== selectedProject.value.PROJCTDTL ||
original.PROJCTZIP !== selectedProject.value.PROJCTZIP ||
original.PROJCTSTR !== selectedProject.value.PROJCTSTR ||
original.PROJCTEND !== selectedProject.value.PROJCTEND ||
original.PROJCTDES !== selectedProject.value.PROJCTDES
);
});
// ::
const updateAddress = addressData => {
selectedProject.value = {
...selectedProject.value,
PROJCTZIP: addressData.postcode,
PROJCTARR: addressData.address,
PROJCTDTL: addressData.detailAddress,
};
};
//
const handleUpdate = () => {
nameAlert.value = selectedProject.value.PROJCTNAM.trim() === '';
if (nameAlert.value) {
return;
}
if (!hasChanges.value) {
toastStore.onToast('변경된 내용이 없습니다.', 'e');
return;
}
$api.patch('project/update', {
projctSeq: selectedProject.value.PROJCTSEQ,
projctNam: selectedProject.value.PROJCTNAM,
projctCol: selectedProject.value.PROJCTCOL,
projctArr: selectedProject.value.PROJCTARR,
projctDtl: selectedProject.value.PROJCTDTL,
projctZip: selectedProject.value.PROJCTZIP,
projctStr: selectedProject.value.PROJCTSTR,
projctEnd: selectedProject.value.PROJCTEND || null,
projctDes: selectedProject.value.PROJCTDES || null,
projctUmb: user.value.name,
originalColor: originalColor.value === selectedProject.value.PROJCTCOL ? null : originalColor.value,
}).then(res => {
if (res.status === 200) {
toastStore.onToast('수정이 완료 되었습니다.', 's');
closeEditModal();
location.reload();
}
});
};
onMounted(async () => {
await getProjectList();
await userStore.userInfo();
user.value = userStore.user;
});
</script> </script>