Merge branch 'main' of http://192.168.0.251:3000/localnet/localhost-front
This commit is contained in:
commit
2b57883edd
@ -80,4 +80,10 @@
|
||||
.grayscaleImg {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
|
||||
/* scrollbar 안보이게 */
|
||||
.scrollbar-none {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ul class="cate-list list-unstyled d-flex flex-wrap mb-0">
|
||||
<li v-for="category in lists" :key="category.value" class="mt-2 mx-1">
|
||||
<li v-for="category in lists" :key="category.value" class="mt-2 me-2">
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
@ -39,9 +39,7 @@ const selectCategory = (cate) => {
|
||||
|
||||
|
||||
<style scoped>
|
||||
.cate-list {
|
||||
margin-left: -0.25rem;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.cate-list {
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
<template>
|
||||
<div class="mb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="mb-2 row" >
|
||||
<div class="d-flex">
|
||||
<label :for="name" class="col-md-2 col-form-label">
|
||||
{{ title }}
|
||||
<span :class="isEssential ? 'link-danger' : 'd-none'">*</span>
|
||||
</label>
|
||||
<button type="button" class="btn btn-sm btn-primary" @click="openAddressSearch">주소찾기</button>
|
||||
<div class="align-content-center col-md-10 text-end ms-auto">
|
||||
<button type="button" class="btn btn-sm btn-primary" :class="isRow ? '' : 'ms-auto'" @click="openAddressSearch">주소찾기</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<div :class="isRow ? 'col-md-10 ms-auto' : 'col-md-12'">
|
||||
<div class="d-flex mb-3">
|
||||
<input
|
||||
:id="name"
|
||||
@ -48,6 +50,10 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
const postcode = ref('');
|
||||
const address = ref('');
|
||||
const detailAddress = ref('');
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
@ -69,19 +75,33 @@ const props = defineProps({
|
||||
default: 30,
|
||||
required: false,
|
||||
},
|
||||
isRow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
isAlert: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
required: false
|
||||
}
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:data', 'update:alert']);
|
||||
// watch 설정 수정
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
if (newValue) {
|
||||
postcode.value = newValue.PROJCTZIP || '';
|
||||
address.value = newValue.PROJCTARR || '';
|
||||
detailAddress.value = newValue.PROJCTDTL || '';
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 주소 관련 상태 관리
|
||||
const postcode = ref('');
|
||||
const address = ref('');
|
||||
const detailAddress = ref('');
|
||||
const emits = defineEmits(['update:data', 'update:alert']);
|
||||
|
||||
// 주소 검색 팝업 열기
|
||||
const openAddressSearch = () => {
|
||||
|
||||
@ -1,63 +1,126 @@
|
||||
<template>
|
||||
<div class="card mb-3 shadow-sm border">
|
||||
<div class="row g-0">
|
||||
<!-- 게시물 내용 섹션 -->
|
||||
<div>
|
||||
<div class="card-body">
|
||||
<!-- 제목 -->
|
||||
<h5 class="card-title">
|
||||
{{ title }}
|
||||
<span class="text-muted me-3" v-if="attachment">
|
||||
<i class="fa-solid fa-paperclip"></i>
|
||||
</span>
|
||||
</h5>
|
||||
<!-- 본문 -->
|
||||
<div class="card-text line-clamp-2 my-5">{{ content }}</div>
|
||||
<!-- 날짜 -->
|
||||
<div class="d-flex flex-column flex-sm-row justify-content-between align-items-start">
|
||||
<small class="text-muted">{{ formattedDate }}</small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- 제목 -->
|
||||
<h5 class="card-title">
|
||||
{{ title }}
|
||||
</h5>
|
||||
<!-- 날짜 -->
|
||||
<div class="d-flex flex-column flex-sm-row align-items-center pb-2">
|
||||
<i class="bx bx-calendar"></i>
|
||||
<div class="ms-2">날짜</div>
|
||||
<div class="ms-12">{{ strdate }} ~ {{ enddate }}</div>
|
||||
</div>
|
||||
<!-- 참여자 -->
|
||||
<div class="d-flex flex-column flex-sm-row align-items-center pb-2">
|
||||
<i class="bx bxs-user"></i>
|
||||
<div class="ms-2">참여자</div>
|
||||
<UserList :projctSeq="projctSeq" class="ms-8 mb-0" />
|
||||
</div>
|
||||
<!-- 설명 -->
|
||||
<div class="d-flex flex-column flex-sm-row align-items-center pb-2">
|
||||
<i class="bx bx-detail"></i>
|
||||
<div class="ms-2">설명</div>
|
||||
<div class="ms-12">{{ description }}</div>
|
||||
</div>
|
||||
<!-- 주소 -->
|
||||
<div class="d-flex flex-column flex-sm-row align-items-center pb-2">
|
||||
<i class="bx bxs-map"></i>
|
||||
<div class="ms-2">주소</div>
|
||||
<div class="ms-12">{{ address }}</div>
|
||||
<button type="button" class="btn ms-auto text-white" :style="`background-color: ${projctCol} !important;`" @click.stop="openModal">log</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CenterModal :display="isModalOpen" @close="closeModal">
|
||||
<template #title> Log </template>
|
||||
<template #body>
|
||||
<div class="ms-4 mt-2" v-if="logData">
|
||||
<p class="mb-1">{{ logData.createDate }}</p>
|
||||
<strong class="">[{{ logData.creator }}] 프로젝트 등록</strong>
|
||||
</div>
|
||||
|
||||
<div class="log-item" v-if="logData?.updateDate">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx bx-edit me-2"></i>
|
||||
<strong>수정 정보</strong>
|
||||
</div>
|
||||
<div class="ms-4 mt-2">
|
||||
<p class="mb-1">{{ logData.updateDate }}</p>
|
||||
<p class="mb-0 text-muted">[{{ logData.updater }}] 프로젝트 수정</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<template #footer>
|
||||
<button type="button" class="btn btn-secondary" @click="closeModal">닫기</button>
|
||||
</template>
|
||||
</CenterModal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { defineProps } from 'vue';
|
||||
import { defineProps, ref } from 'vue';
|
||||
import UserList from '@c/user/UserList.vue';
|
||||
import CenterModal from '../modal/CenterModal.vue';
|
||||
import $api from '@api';
|
||||
|
||||
// Props 정의
|
||||
const props = defineProps({
|
||||
category: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
content: {
|
||||
strdate: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
date: {
|
||||
enddate: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "",
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
address: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
attachment: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
projctSeq: {
|
||||
type: Number,
|
||||
required: false
|
||||
},
|
||||
projctCol: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
});
|
||||
|
||||
// formattedDate을 computed로 정의
|
||||
const formattedDate = computed(() => {
|
||||
const date = new Date(props.date);
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(
|
||||
date.getDate()
|
||||
).padStart(2, "0")} ${String(date.getHours()).padStart(2, "0")}:${String(
|
||||
date.getMinutes()
|
||||
).padStart(2, "0")}`;
|
||||
});
|
||||
defineEmits(['click']);
|
||||
|
||||
const isModalOpen = ref(false);
|
||||
const logData = ref(null);
|
||||
|
||||
const fetchLogData = async () => {
|
||||
try {
|
||||
const response = await $api.get(`project/log/${props.projctSeq}`);
|
||||
logData.value = response.data.data.length > 0 ? response.data.data[0] : {};
|
||||
} catch (error) {
|
||||
console.error('로그 정보 조회 실패:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const openModal = async () => {
|
||||
await fetchLogData();
|
||||
isModalOpen.value = true;
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
isModalOpen.value = false;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@ -1,49 +1,181 @@
|
||||
<template>
|
||||
<div class="mt-4">
|
||||
<div v-if="posts.length === 0" class="text-center">
|
||||
<div v-if="projectList.length === 0" class="text-center">
|
||||
<p class="text-muted mt-4">게시물이 없습니다.</p>
|
||||
</div>
|
||||
|
||||
<div v-for="post in posts" :key="post.id" @click="goDetail(post.id)">
|
||||
<div v-for="post in projectList" :key="post.PROJCTSEQ" @click="openModal(post)" class="cursor-pointer">
|
||||
<ProjectCard
|
||||
:title="post.title"
|
||||
:content="post.content"
|
||||
:date="post.date"
|
||||
:attachment="post.attachment || false"
|
||||
:title="post.PROJCTNAM"
|
||||
:description="post.PROJCTDES"
|
||||
:strdate="post.PROJCTSTR"
|
||||
:enddate="post.PROJCTEND"
|
||||
:address="post.PROJCTARR + ' ' + post.PROJCTDTL"
|
||||
:projctSeq="post.PROJCTSEQ"
|
||||
:projctCol="post.projctcolor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CenterModal :display="isModalOpen" @close="closeModal">
|
||||
<template #title> 프로젝트 수정 </template>
|
||||
<template #body>
|
||||
<FormInput
|
||||
title="이름"
|
||||
name="name"
|
||||
:is-essential="true"
|
||||
:is-alert="nameAlert"
|
||||
v-model="selectedProject.PROJCTNAM"
|
||||
/>
|
||||
|
||||
<FormSelect
|
||||
title="컬러"
|
||||
name="color"
|
||||
:is-essential="true"
|
||||
:is-label="true"
|
||||
:is-common="true"
|
||||
:data="allColors"
|
||||
v-model="selectedProject.projctcolor"
|
||||
/>
|
||||
|
||||
<FormInput
|
||||
title="시작일"
|
||||
type="date"
|
||||
name="startDay"
|
||||
v-model="selectedProject.PROJCTSTR"
|
||||
:is-essential="true"
|
||||
/>
|
||||
|
||||
<FormInput
|
||||
title="종료일"
|
||||
type="date"
|
||||
name="endDay"
|
||||
v-model="selectedProject.PROJCTEND"
|
||||
/>
|
||||
|
||||
<FormInput
|
||||
title="설명"
|
||||
name="description"
|
||||
v-model="selectedProject.PROJCTDES"
|
||||
/>
|
||||
|
||||
<ArrInput
|
||||
title="주소"
|
||||
name="address"
|
||||
:is-essential="true"
|
||||
:is-row="true"
|
||||
v-model="selectedProject"
|
||||
@update:data="updateAddress"
|
||||
/>
|
||||
|
||||
</template>
|
||||
<template #footer>
|
||||
<button class="btn btn-secondary" @click="closeModal">Close</button>
|
||||
<button class="btn btn-primary" @click="handleSubmit">Save</button>
|
||||
</template>
|
||||
</CenterModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineEmits } from 'vue';
|
||||
import ProjectCard from './ProjectCard.vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import ProjectCard from './ProjectCard.vue';
|
||||
import { onMounted } from 'vue';
|
||||
import $api from '@api';
|
||||
import CenterModal from '@c/modal/CenterModal.vue';
|
||||
import FormInput from '@c/input/FormInput.vue';
|
||||
import FormSelect from '@c/input/FormSelect.vue';
|
||||
import commonApi from '@/common/commonApi';
|
||||
import ArrInput from '../input/ArrInput.vue';
|
||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||
|
||||
// 테스트용 데이터
|
||||
const posts = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: 'Vue 3 Composition API 소개',
|
||||
content: 'Composition API를 사용하여 Vue 3에서 효율적으로 개발하는 방법을 알아봅니다. Composition API를 사용하여 Vue 3에서 효율적으로 개발하는 방법을 알아봅니다. Composition API를 사용하여 Vue 3에서 효율적으로 개발하는 방법을 알아봅니다. Composition API를 사용하여 Vue 3에서 효율적으로 개발하는 방법을 알아봅니다. Composition API를 사용하여 Vue 3에서 효율적으로 개발하는 방법을 알아봅니다. Composition API를 사용하여 Vue 3에서 효율적으로 개발하는 방법을 알아봅니다.',
|
||||
date: '2025-02-10',
|
||||
comments: 4,
|
||||
attachment: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Spring Boot로 REST API 만들기',
|
||||
content: 'Spring Boot를 사용하여 간단한 RESTful API를 구현하는 방법을 다룹니다.',
|
||||
date: '2025-02-09',
|
||||
comments: 2,
|
||||
attachment: false
|
||||
}
|
||||
]);
|
||||
const projectList = ref([]);
|
||||
const isModalOpen = ref(false);
|
||||
|
||||
const emit = defineEmits(['click']);
|
||||
const userStore = useUserInfoStore();
|
||||
const user = ref(null);
|
||||
|
||||
// 상세 페이지 이동
|
||||
const goDetail = (id) => {
|
||||
emit('click', id);
|
||||
const nameAlert = ref(false);
|
||||
const selectedProject = ref({
|
||||
PROJCTNAM: '',
|
||||
projctcolor: '',
|
||||
PROJCTSTR: '',
|
||||
PROJCTEND: '',
|
||||
PROJCTZIP: '',
|
||||
PROJCTARR: '',
|
||||
PROJCTDTL: '',
|
||||
PROJCTDES: '',
|
||||
PROJCTCOL: '',
|
||||
});
|
||||
|
||||
const { colorList } = commonApi({
|
||||
loadColor: true,
|
||||
colorType: 'YNP',
|
||||
});
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
getProjectList();
|
||||
|
||||
await userStore.userInfo(); // 로그인한 사용자 정보
|
||||
user.value = userStore.user;
|
||||
});
|
||||
|
||||
// 프로젝트 목록 불러오기
|
||||
const getProjectList = () => {
|
||||
$api.get('project/select').then(res => {
|
||||
projectList.value = res.data.data.projectList;
|
||||
console.log(projectList.value);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const openModal = (post) => {
|
||||
isModalOpen.value = true;
|
||||
selectedProject.value = { ...post };
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
isModalOpen.value = false;
|
||||
};
|
||||
|
||||
// 현재 프로젝트 색상 + 사용하지 않은 색상
|
||||
const allColors = computed(() => {
|
||||
// 기존 색상 추가
|
||||
const existingColor = { value: selectedProject.value.PROJCTCOL, label: selectedProject.value.projctcolor };
|
||||
|
||||
// colorList에 기존 색상을 추가
|
||||
return [existingColor, ...colorList.value];
|
||||
});
|
||||
|
||||
const updateAddress = (addressData) => {
|
||||
selectedProject.value = {
|
||||
...selectedProject.value,
|
||||
PROJCTZIP: addressData.postcode,
|
||||
PROJCTARR: addressData.address,
|
||||
PROJCTDTL: addressData.detailAddress
|
||||
};
|
||||
};
|
||||
|
||||
console.log(projectList.PROJCTSEQ)
|
||||
|
||||
const handleSubmit = () => {
|
||||
$api.patch('project/update', {
|
||||
projctSeq: projectList.PROJCTSEQ,
|
||||
projctNam: selectedProject.value.PROJCTNAM,
|
||||
projctCol: selectedProject.value.projctcolor,
|
||||
projctArr: selectedProject.value.PROJCTARR,
|
||||
projctDtl: selectedProject.value.PROJCTDTL,
|
||||
projctZip: selectedProject.value.PROJCTZIP,
|
||||
projctStr: selectedProject.value.PROJCTSTR,
|
||||
projctEnd: selectedProject.value.PROJCTEND,
|
||||
projctDes: selectedProject.value.PROJCTDES,
|
||||
projctUmb: user.value.name,
|
||||
}).then(res => {
|
||||
if (res.status === 200) {
|
||||
toastStore.onToast('수정이 완료 되었습니다.', 's');
|
||||
closeModal();
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
|
||||
<template>
|
||||
<div @click="closeModal" class="modal fade" :class="{ 'show': display, 'display-block': display , 'modal-back' : display }" id="modalCenter" tabindex="-1" aria-modal="true" role="dialog">
|
||||
<div @click="closeModal" class="modal fade scrollbar-none" :class="{ 'show': display, 'display-block': display , 'modal-back' : display }" id="modalCenter" tabindex="-1" aria-modal="true" role="dialog">
|
||||
<div @click.stop class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalCenterTitle">
|
||||
<h5 class="modal-title m-auto fw-bold" id="modalCenterTitle">
|
||||
<slot name="title">Modal Title</slot>
|
||||
</h5>
|
||||
<button type="button" class="btn-close" @click="closeModal" aria-label="Close"></button>
|
||||
|
||||
@ -1,19 +1,163 @@
|
||||
<template>
|
||||
<SearchBar />
|
||||
<CategoryBtn :lists="yearCategory" />
|
||||
<div class="d-flex align-items-center">
|
||||
<CategoryBtn :lists="yearCategory" v-model:selectedCategory="selectedCategory" />
|
||||
<WriteBtn class="mt-2 ms-auto" @click="openModal" />
|
||||
<CenterModal :display="isModalOpen" @close="closeModal">
|
||||
<template #title> 프로젝트 등록 </template>
|
||||
<template #body>
|
||||
<FormInput
|
||||
title="이름"
|
||||
name="name"
|
||||
:is-essential="true"
|
||||
:is-alert="nameAlert"
|
||||
@update:modelValue="name = $event"
|
||||
/>
|
||||
|
||||
<FormSelect
|
||||
title="컬러"
|
||||
name="color"
|
||||
:is-essential="true"
|
||||
:is-label="true"
|
||||
:is-common="true"
|
||||
:data="colorList"
|
||||
@update:data="color = $event"
|
||||
/>
|
||||
|
||||
<FormInput
|
||||
title="시작 일"
|
||||
type="date"
|
||||
name="startDay"
|
||||
v-model="startDay"
|
||||
:is-essential="true"
|
||||
/>
|
||||
|
||||
<FormInput
|
||||
title="종료 일"
|
||||
name="endDay"
|
||||
:type="'date'"
|
||||
@update:modelValue="endDay = $event"
|
||||
/>
|
||||
|
||||
<FormInput
|
||||
title="설명"
|
||||
name="description"
|
||||
@update:modelValue="description = $event"
|
||||
/>
|
||||
|
||||
<ArrInput
|
||||
title="주소"
|
||||
name="address"
|
||||
:isEssential="true"
|
||||
:is-row="true"
|
||||
:is-alert="addressAlert"
|
||||
@update:data="handleAddressUpdate"
|
||||
@update:alert="addressAlert = $event"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<button class="btn btn-secondary" @click="closeModal">Close</button>
|
||||
<button class="btn btn-primary" @click="handleSubmit">Save</button>
|
||||
</template>
|
||||
</CenterModal>
|
||||
</div>
|
||||
<ProjectCardList :category="selectedCategory" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import SearchBar from '@c/search/SearchBar.vue';
|
||||
import ProjectCardList from '@c/list/ProjectCardList.vue';
|
||||
import CategoryBtn from '@c/category/CategoryBtn.vue';
|
||||
import commonApi from '@/common/commonApi'
|
||||
import { ref } from 'vue';
|
||||
import SearchBar from '@c/search/SearchBar.vue';
|
||||
import ProjectCardList from '@c/list/ProjectCardList.vue';
|
||||
import CategoryBtn from '@c/category/CategoryBtn.vue';
|
||||
import commonApi from '@/common/commonApi';
|
||||
import { inject, onMounted, ref } from 'vue';
|
||||
import WriteBtn from '@c/button/WriteBtn.vue';
|
||||
import CenterModal from '@c/modal/CenterModal.vue';
|
||||
import FormSelect from '@c/input/FormSelect.vue';
|
||||
import FormInput from '@c/input/FormInput.vue';
|
||||
import ArrInput from '@c/input/ArrInput.vue';
|
||||
import { useToastStore } from '@s/toastStore';
|
||||
import $api from '@api';
|
||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||
|
||||
const selectedCategory = ref(null);
|
||||
const dayjs = inject('dayjs');
|
||||
|
||||
const { yearCategory } = commonApi();
|
||||
const today = dayjs().format('YYYY-MM-DD');
|
||||
|
||||
const toastStore = useToastStore();
|
||||
const userStore = useUserInfoStore();
|
||||
|
||||
const user = ref(null);
|
||||
|
||||
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 isModalOpen = ref(false);
|
||||
const nameAlert = ref(false);
|
||||
const addressAlert = ref(false);
|
||||
|
||||
const openModal = () => {
|
||||
isModalOpen.value = true;
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
isModalOpen.value = false;
|
||||
};
|
||||
|
||||
|
||||
const selectedCategory = ref(null);
|
||||
|
||||
const { yearCategory, colorList } = commonApi({
|
||||
loadColor: true,
|
||||
colorType: 'YNP',
|
||||
loadYearCategory: true,
|
||||
});
|
||||
|
||||
// 주소 업데이트 핸들러
|
||||
const handleAddressUpdate = addressData => {
|
||||
address.value = addressData.address;
|
||||
detailAddress.value = addressData.detailAddress;
|
||||
postcode.value = addressData.postcode;
|
||||
};
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
await userStore.userInfo(); // 로그인한 사용자 정보
|
||||
user.value = userStore.user;
|
||||
});
|
||||
|
||||
const handleSubmit = 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');
|
||||
closeModal();
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
<template>
|
||||
<form @submit.prevent="handleSubmit">
|
||||
<div class="col-xl-12">
|
||||
<UserFormInput title="아이디" name="id" :is-alert="idAlert" :useInputGroup="true" @update:data="handleIdChange" :value="id" />
|
||||
|
||||
@ -13,7 +12,7 @@
|
||||
/>
|
||||
|
||||
<div class="d-grid gap-2 mt-7 mb-5">
|
||||
<button type="submit" class="btn btn-primary">로그인</button>
|
||||
<button type="submit" @click="handleSubmit" class="btn btn-primary">로그인</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 d-flex justify-content-around">
|
||||
@ -25,7 +24,6 @@
|
||||
<RouterLink class="text-dark fw-bold" to="/pw">비밀번호 찾기</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -34,7 +32,7 @@
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ref } from 'vue';
|
||||
import UserFormInput from '@c/input/UserFormInput.vue';
|
||||
import { useUserStore } from '@s/useUserStore';
|
||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||
|
||||
const id = ref('');
|
||||
const password = ref('');
|
||||
@ -42,7 +40,7 @@
|
||||
const passwordAlert = ref(false);
|
||||
const remember = ref(false);
|
||||
|
||||
const userStore = useUserStore();
|
||||
const userStore = useUserInfoStore();
|
||||
const route = useRoute();
|
||||
|
||||
const handleIdChange = value => {
|
||||
@ -55,21 +53,21 @@
|
||||
passwordAlert.value = false;
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
$api.post(
|
||||
'user/login',
|
||||
{
|
||||
loginId: id.value,
|
||||
password: password.value,
|
||||
remember: remember.value,
|
||||
},
|
||||
{ headers: { 'X-Page-Route': route.path } },
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
// const sessionCookie = res.data.data;
|
||||
// document.cookie = `JSESSIONID=${sessionCookie};path=/;expires=-1;`;
|
||||
// document.cookie = `JSESSIONID=${sessionCookie};path=/;HttpOnly=true;samesite=lax`;
|
||||
const handleSubmit = async () => {
|
||||
idAlert.value = id.value.trim() === '';
|
||||
passwordAlert.value = password.value.trim() === '';
|
||||
|
||||
if (idAlert.value || passwordAlert.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$api.post('user/login', {
|
||||
loginId: id.value,
|
||||
password: password.value,
|
||||
remember: remember.value,
|
||||
}, { headers: { 'X-Page-Route': route.path } })
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
userStore.userInfo();
|
||||
router.push('/');
|
||||
}
|
||||
|
||||
@ -252,7 +252,11 @@
|
||||
};
|
||||
|
||||
// 컬러, mbti, 비밀번호 힌트 목록 불러오기
|
||||
const { colorList, mbtiList, pwhintList } = commonApi();
|
||||
const { colorList, mbtiList, pwhintList } = commonApi({
|
||||
loadColor: true, colorType: 'YON',
|
||||
loadMbti: true,
|
||||
loadPwhint: true,
|
||||
});
|
||||
|
||||
// 주소 업데이트 핸들러
|
||||
const handleAddressUpdate = (addressData) => {
|
||||
|
||||
@ -4,23 +4,22 @@
|
||||
v-for="(user, index) in userList"
|
||||
:key="index"
|
||||
class="avatar pull-up"
|
||||
:class="{ 'opacity-100': user.disabled }"
|
||||
:class="{ 'opacity-100': isUserDisabled(user) }"
|
||||
@click="toggleDisable(index)"
|
||||
data-bs-toggle="tooltip"
|
||||
data-popup="tooltip-custom"
|
||||
data-bs-placement="top"
|
||||
:aria-label="user.MEMBERSEQ"
|
||||
:data-bs-original-title="getTooltipTitle(user)"
|
||||
>
|
||||
>
|
||||
<img
|
||||
class="rounded-circle user-avatar border border-3"
|
||||
:class="{ 'grayscaleImg': user.disabled }"
|
||||
:class="{ 'grayscaleImg': isUserDisabled(user) }"
|
||||
:src="`${baseUrl}upload/img/profile/${user.MEMBERPRF}`"
|
||||
:style="`border-color: ${user.usercolor} !important;`"
|
||||
alt="user"
|
||||
/>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
@ -29,38 +28,83 @@ import { onMounted, ref, nextTick } from 'vue';
|
||||
import { useUserStore } from '@s/userList';
|
||||
import $api from '@api';
|
||||
|
||||
const emit = defineEmits();
|
||||
const emit = defineEmits(['user-list-update']);
|
||||
const userStore = useUserStore();
|
||||
const userList = ref([]);
|
||||
const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
|
||||
|
||||
const props = defineProps({
|
||||
projctSeq: {
|
||||
type: Number,
|
||||
required: false,
|
||||
}
|
||||
});
|
||||
|
||||
// 사용자의 프로젝트 참여 상태 확인
|
||||
const fetchProjectParticipation = async () => {
|
||||
if (props.projctSeq) {
|
||||
const response = await $api.get(`project/members/${props.projctSeq}`);
|
||||
if (response.status === 200) {
|
||||
const projectMembers = response.data.data;
|
||||
userList.value = userList.value.map(user => ({
|
||||
...user,
|
||||
PROJCTYON: projectMembers.find(pm => pm.MEMBERSEQ === user.MEMBERSEQ)?.PROJCTYON ?? '1'
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 사용자 목록 호출
|
||||
onMounted(async () => {
|
||||
await userStore.fetchUserList();
|
||||
userList.value = userStore.userList;
|
||||
|
||||
if (props.projctSeq) {
|
||||
await fetchProjectParticipation();
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
const tooltips = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
tooltips.forEach((tooltip) => {
|
||||
new bootstrap.Tooltip(tooltip);
|
||||
new bootstrap.Tooltip(tooltip);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 클릭 시 활성화/비활성화
|
||||
const toggleDisable = (index) => {
|
||||
// 사용자 비활성화 상태 확인
|
||||
const isUserDisabled = (user) => {
|
||||
return props.projctSeq ? user.PROJCTYON === '0' : user.disabled;
|
||||
};
|
||||
|
||||
// 클릭 시 활성화/비활성화 및 DB 업데이트
|
||||
const toggleDisable = async (index) => {
|
||||
const user = userList.value[index];
|
||||
if (user) {
|
||||
user.disabled = !user.disabled;
|
||||
emitUserListUpdate();
|
||||
const newParticipationStatus = props.projctSeq
|
||||
? user.PROJCTYON === '1'
|
||||
: !user.disabled;
|
||||
|
||||
if (props.projctSeq) {
|
||||
const response = await $api.patch('project/updateYon', {
|
||||
memberSeq: user.MEMBERSEQ,
|
||||
projctSeq: props.projctSeq,
|
||||
projctYon: newParticipationStatus ? '0' : '1'
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
user.PROJCTYON = newParticipationStatus ? '0' : '1';
|
||||
}
|
||||
} else {
|
||||
user.disabled = newParticipationStatus;
|
||||
emitUserListUpdate();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// emit
|
||||
const emitUserListUpdate = () => {
|
||||
const activeUsers = userList.value.filter((user) => !user.disabled);
|
||||
const disabledUsers = userList.value.filter((user) => user.disabled);
|
||||
|
||||
const activeUsers = userList.value.filter(user => !isUserDisabled(user));
|
||||
const disabledUsers = userList.value.filter(user => isUserDisabled(user));
|
||||
emit('user-list-update', { activeUsers, disabledUsers });
|
||||
};
|
||||
|
||||
@ -68,6 +112,3 @@ const getTooltipTitle = (user) => {
|
||||
return user.MEMBERSEQ === userStore.userInfo.id ? '나' : user.MEMBERNAM;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
remainingVacationData: Object,
|
||||
});
|
||||
|
||||
const userStore = useUserStore();
|
||||
const userStore = useUserInfoStore();
|
||||
const userListStore = useUserListStore();
|
||||
|
||||
const userList = ref([]);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="alphabet-list list-unstyled d-flex flex-wrap mb-0">
|
||||
<li v-for="char in koreanChars" :key="char" class="mt-2 mx-1">
|
||||
<li v-for="char in koreanChars" :key="char" class="mt-2 me-2">
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
@ -13,7 +13,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="alphabet-list list-unstyled d-flex flex-wrap mb-0">
|
||||
<li v-for="char in englishChars" :key="char" class="mt-2 mx-1">
|
||||
<li v-for="char in englishChars" :key="char" class="mt-2 me-2">
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
@ -43,9 +43,6 @@ const selectAlphabet = (alphabet) => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.alphabet-list {
|
||||
margin-left: -0.25rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.alphabet-list {
|
||||
|
||||
@ -228,7 +228,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { useAuthStore } from '@s/useAuthStore';
|
||||
import { useUserStore } from '@s/useUserStore';
|
||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useThemeStore } from '@s/darkmode';
|
||||
import { onMounted, ref } from 'vue';
|
||||
@ -236,7 +236,7 @@ import { onMounted, ref } from 'vue';
|
||||
const user = ref(null);
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const userStore = useUserStore();
|
||||
const userStore = useUserInfoStore();
|
||||
const router = useRouter();
|
||||
|
||||
const { isDarkMode, switchToDarkMode, switchToLightMode } = useThemeStore();
|
||||
|
||||
@ -1,8 +1,16 @@
|
||||
/*
|
||||
작성자 : 박지윤
|
||||
작성일 : 2025-02-04
|
||||
수정자 :
|
||||
수정일 :
|
||||
설명 : 로그인 한 사용자 정보
|
||||
*/
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import $api from "@api";
|
||||
|
||||
export const useUserStore = defineStore('userInfo', () => {
|
||||
export const useUserInfoStore = defineStore('userInfo', () => {
|
||||
const user = ref(null);
|
||||
|
||||
// 사용자 정보 가져오기
|
||||
Loading…
Reference in New Issue
Block a user