diff --git a/src/views/akopolio/Edit/edit.js b/src/views/akopolio/Edit/edit.js index dd08647..3a5437b 100644 --- a/src/views/akopolio/Edit/edit.js +++ b/src/views/akopolio/Edit/edit.js @@ -4,25 +4,54 @@ import { ref, computed, onMounted } from 'vue'; import { useRoute } from 'vue-router'; import axios from 'axios'; +const nickname = ref(''); + +const fetchUserData = async () => { + try { + const response = await fetch( + `${process.env.VUE_APP_BE_API_URL}/api/users/profile`, + { + method: 'GET', + credentials: 'include', + } + ); + + if (response.ok) { + const data = await response.json(); + nickname.value = data.nickname; + } else { + console.error('사용자 정보 불러오기 실패:', response.status, response.statusText); + if (response.status === 401) { + alert('인증에 실패했습니다. 다시 로그인해주세요.'); + window.location.href = '/login'; + } else { + alert('사용자 정보 가져오기 실패. 다시 시도해주세요.'); + } + } + } catch (error) { + console.error('사용자 정보 가져오기 오류:', error); + alert('사용자 정보 가져오기 오류가 발생했습니다.'); + } +}; + export default { components: { MainHeader, - MainFooter + MainFooter, }, setup() { const route = useRoute(); + const portfolio = ref(null); const portfolioId = route.params.id; const activityName = ref(''); const activityDate = ref(''); - const selectedTags = ref([]); + const selectedTags = ref([]); // 초기값 배열로 설정 const tags = ref([ '전공', '교양', '교내동아리', '교외동아리', '학회', '봉사활동', '인턴', '아르바이트', '대외활동', '서포터즈', '기자단', '강연/행사', '스터디', '부트캠프', '프로젝트', '연구', '학생회', '기타', ]); - const images = ref([ - { previewUrl: "https://via.placeholder.com/150" } // 예시 이미지 - ]); + const images = ref([]); // 초기값 배열로 설정 const star = ref({ situation: '', task: '', @@ -36,12 +65,10 @@ export default { }); const isDropdownOpen = ref(false); - // 드롭다운 토글 const toggleDropdown = () => { isDropdownOpen.value = !isDropdownOpen.value; }; - // 태그 선택/해제 const toggleTag = (tag) => { const index = selectedTags.value.indexOf(tag); if (index > -1) { @@ -51,54 +78,61 @@ export default { } }; - // 텍스트 영역 자동 크기 조정 const autoResize = (event) => { const textarea = event.target; textarea.style.height = 'auto'; textarea.style.height = `${textarea.scrollHeight}px`; }; - // 포트폴리오 데이터 가져오기 - const fetchPortfolioById = async () => { + const fetchPortfolioById = async (id) => { try { - const response = await fetch( - `${process.env.VUE_APP_BE_API_URL}/api/portfolios/${portfolioId}` - ); + const apiUrl = `${process.env.VUE_APP_BE_API_URL}/api/portfolios/${id}`; + console.log('Fetching portfolio from URL:', apiUrl); + + const response = await fetch(apiUrl); + console.log('Response status:', response.status); if (!response.ok) { + console.error(`Response status: ${response.status}, ${response.statusText}`); throw new Error(`Failed to fetch portfolio: ${response.statusText}`); } const data = await response.json(); - activityName.value = data.activityName || ''; - activityDate.value = data.activityDate || ''; - selectedTags.value = data.selectedTags || []; - star.value = data.star || { situation: '', task: '', action: '', result: '' }; - pmi.value = data.pmi || { plus: '', minus: '', interesting: '' }; - images.value = data.images || []; + console.log('Received portfolio data:', data); + + portfolio.value = { + baseInfo: data.baseInfo || {}, + experience: data.experience || { situation: '', task: '', action: '', result: '' }, + pmi: data.pmi || { plus: '', minus: '', interesting: '' }, + photoUrls: data.photoUrls || [], + }; + + images.value = portfolio.value.photoUrls || []; } catch (error) { console.error('Error fetching portfolio:', error); - alert('포트폴리오를 불러오는 중 오류가 발생했습니다.'); + alert('포트폴리오를 가져오는 중 오류가 발생했습니다.'); + portfolio.value = null; + images.value = []; } }; const handleFileChange = (event) => { const selectedFiles = Array.from(event.target.files); - if (images.value.length + selectedFiles.length > 5) { + if (Array.isArray(images.value) && images.value.length + selectedFiles.length > 5) { alert('최대 5개의 이미지만 업로드할 수 있습니다.'); return; } - + const newImagesPromises = selectedFiles.map((file) => { const previewUrl = URL.createObjectURL(file); const imageElement = new Image(); imageElement.src = previewUrl; - + return new Promise((resolve) => { imageElement.onload = () => { const aspectRatio = imageElement.width / imageElement.height; let containerWidth, containerHeight; - + if (aspectRatio > 1) { containerWidth = '300px'; containerHeight = `${300 / aspectRatio}px`; @@ -106,7 +140,7 @@ export default { containerWidth = `${300 * aspectRatio}px`; containerHeight = '300px'; } - + resolve({ file, name: file.name, @@ -118,7 +152,7 @@ export default { }; }); }); - + Promise.all(newImagesPromises).then((newImages) => { const uniqueNewImages = newImages.filter( (newImage) => @@ -126,80 +160,98 @@ export default { (existingImage) => existingImage.previewUrl === newImage.previewUrl ) ); - + if (uniqueNewImages.length === 0) { alert('이미 선택된 이미지입니다.'); return; } - + images.value = [...images.value, ...uniqueNewImages]; }); }; - + const removeImage = (index) => { URL.revokeObjectURL(images.value[index].previewUrl); images.value.splice(index, 1); }; - - const prefix = 'images'; + const uploadImages = async () => { const uploadedUrls = []; - for (const image of images.value) { - try { - const { data } = await axios.post( - `${process.env.VUE_APP_BE_API_URL}/file/${prefix}/presigned-url`, - { - fileName: image.name, - fileType: image.file.type, - } - ); - - await axios.put(data.url, image.file, { - headers: { 'Content-Type': image.file.type }, + + try { + console.log("Starting image upload..."); + + for (const image of images.value) { + const apiUrl = `${process.env.VUE_APP_BE_API_URL}/file/IMAGE/presigned-url`; + console.log('Requesting presigned URL from:', apiUrl); + + const response = await axios.post(apiUrl, { + imageName: image.file.name, + }); + + const presignedUrl = response.data; + console.log("Received presigned URL:", presignedUrl); + + await axios.put(presignedUrl, image.file, { + headers: { + "Content-Type": image.file.type, + }, }); - - const uploadedUrl = data.url.split('?')[0]; + + console.log("Image uploaded successfully!"); + + const uploadedUrl = presignedUrl.split("?")[0]; uploadedUrls.push(uploadedUrl); - } catch (error) { - console.error('Error uploading image:', error); - alert('이미지 업로드 중 오류가 발생했습니다.'); + console.log("Uploaded image URL:", uploadedUrl); } + + console.log("Uploaded image URLs:", uploadedUrls); + + } catch (error) { + console.error("Error uploading image:", error); + alert("이미지 업로드 중 오류가 발생했습니다."); } }; - - // 저장 버튼 클릭 시 서버에 반영 - const saveData = async () => { + const saveData = async (id) => { if (!isFormComplete.value) { alert('모든 필드를 입력해주세요.'); return; } try { - // 수정된 포트폴리오 데이터와 이미지 URL을 서버에 전송 - const response = await fetch( - `${process.env.VUE_APP_BE_API_URL}/api/portfolios/${portfolioId}`, + const response = await axios.put( + `${process.env.VUE_APP_BE_API_URL}/api/portfolios/${id}`, + { + name: activityName.value, + startDate: activityDate.value, + tags: selectedTags.value, + experience: { + situation: star.value.situation, + task: star.value.task, + action: star.value.action, + result: star.value.result, + }, + pmi: { + plus: pmi.value.plus, + minus: pmi.value.minus, + interesting: pmi.value.interesting, + }, + photoUrls: images.value.map(image => image.name), + }, { - method: 'PUT', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ - activityName: activityName.value, - activityDate: activityDate.value, - selectedTags: selectedTags.value, - star: star.value, - pmi: pmi.value, - images: images.value.map(image => image.name), // 서버에 전달할 이미지 파일 이름만 전달 - }), } ); - if (!response.ok) { - throw new Error(`Failed to save data: ${response.statusText}`); + if (response.status === 200) { + alert('데이터가 성공적으로 저장되었습니다.'); + } else { + alert('저장에 실패했습니다. 다시 시도해주세요.'); } - alert('데이터가 성공적으로 저장되었습니다.'); } catch (error) { console.error('Error saving data:', error); alert('데이터 저장 중 오류가 발생했습니다.'); @@ -210,34 +262,37 @@ export default { return ( activityName.value && activityDate.value && - selectedTags.value.length > 0 && + Array.isArray(selectedTags.value) && selectedTags.value.length > 0 && Object.values(star.value).every((field) => field) && Object.values(pmi.value).every((field) => field) ); }); - // 컴포넌트 마운트 시 데이터 가져오기 - onMounted(() => { - fetchPortfolioById(); + onMounted(async () => { + images.value = []; + selectedTags.value = []; + await fetchUserData(); + await fetchPortfolioById(portfolioId); }); return { + portfolio, activityName, activityDate, - tags, selectedTags, - isDropdownOpen, + tags, + images, star, pmi, - images, + isDropdownOpen, toggleDropdown, toggleTag, autoResize, - handleFileChange, - removeImage, - uploadImages, + handleFileChange, + removeImage, + uploadImages, saveData, - isFormComplete + isFormComplete, }; - } + }, }; diff --git a/src/views/akopolio/create/create.js b/src/views/akopolio/create/create.js index 1897d31..a9e47ac 100644 --- a/src/views/akopolio/create/create.js +++ b/src/views/akopolio/create/create.js @@ -1,6 +1,7 @@ import axios from 'axios'; import MainHeader from '../../../components/layout/Header.vue'; import MainFooter from '../../../components/layout/Footer.vue'; +import { ref, computed, onMounted } from 'vue' export default { components: { @@ -31,7 +32,9 @@ export default { }, tooltipVisible: false, images: [], // 선택된 이미지 파일 배열 - uploadedImageUrls: [] // 업로드된 이미지의 URL 저장 + uploadedImageUrls: [], // 업로드된 이미지의 URL 저장 + portfolios: [], + uploadError: false, }; }, computed: { @@ -46,152 +49,228 @@ export default { } }, methods: { - toggleDropdown() { - this.isDropdownOpen = !this.isDropdownOpen; - }, - toggleTag(tag) { - const index = this.selectedTags.indexOf(tag); - if (index > -1) { - this.selectedTags.splice(index, 1); - } else { - this.selectedTags.push(tag); - } - }, - autoResize(event) { - const textarea = event.target; - textarea.style.height = 'auto'; - textarea.style.height = `${textarea.scrollHeight}px`; - }, - handleFileChange(event) { - const selectedFiles = Array.from(event.target.files); + async fetchUserData() { + try { + const response = await fetch( + `${process.env.VUE_APP_BE_API_URL}/api/users/profile`, + { + method: 'GET', + credentials: 'include', + } + ); + + console.log('서버 응답 상태:', response.status, response.statusText); + + if (response.ok) { + const data = await response.json(); + this.nickname = data.nickname; // nickname을 data 안으로 변경 + console.log('사용자 정보:', data); + } else { + console.error('사용자 정보 불러오기 실패:', response.status, response.statusText); + if (response.status === 401) { + alert('인증에 실패했습니다. 다시 로그인해주세요.'); + window.location.href = '/login'; + } else { + alert('사용자 정보 가져오기 실패. 다시 시도해주세요.'); + } + } + } catch (error) { + console.error('사용자 정보 가져오기 오류:', error); + alert('사용자 정보 가져오기 오류가 발생했습니다.'); + } + }, - if (this.images.length + selectedFiles.length > 5) { - alert('최대 5개의 이미지만 업로드할 수 있습니다.'); - return; - } + toggleDropdown() { + this.isDropdownOpen = !this.isDropdownOpen; + }, + toggleTag(tag) { + const index = this.selectedTags.indexOf(tag); + if (index > -1) { + this.selectedTags.splice(index, 1); + } else { + this.selectedTags.push(tag); + } + }, + autoResize(event) { + const textarea = event.target; + textarea.style.height = 'auto'; + textarea.style.height = `${textarea.scrollHeight}px`; + }, + handleFileChange(event) { + const selectedFiles = Array.from(event.target.files); - const newImagesPromises = selectedFiles.map(file => { - const previewUrl = URL.createObjectURL(file); - const imageElement = new Image(); - imageElement.src = previewUrl; + if (this.images.length + selectedFiles.length > 5) { + alert('최대 5개의 이미지만 업로드할 수 있습니다.'); + return; + } - return new Promise(resolve => { - imageElement.onload = () => { - const aspectRatio = imageElement.width / imageElement.height; - let containerWidth, containerHeight; + const newImagesPromises = selectedFiles.map(file => { + const previewUrl = URL.createObjectURL(file); + const imageElement = new Image(); + imageElement.src = previewUrl; - if (aspectRatio > 1) { - containerWidth = '300px'; - containerHeight = `${300 / aspectRatio}px`; - } else { - containerWidth = `${300 * aspectRatio}px`; - containerHeight = '300px'; - } + return new Promise(resolve => { + imageElement.onload = () => { + const aspectRatio = imageElement.width / imageElement.height; + let containerWidth, containerHeight; - resolve({ - file, - name: file.name, - size: file.size, - previewUrl, - containerWidth, - containerHeight + if (aspectRatio > 1) { + containerWidth = '300px'; + containerHeight = `${300 / aspectRatio}px`; + } else { + containerWidth = `${300 * aspectRatio}px`; + containerHeight = '300px'; + } + + resolve({ + file, + name: file.name, + size: file.size, + previewUrl, + containerWidth, + containerHeight + }); + }; }); - }; - }); - }); + }); - Promise.all(newImagesPromises).then(newImages => { - const uniqueNewImages = newImages.filter(newImage => - !this.images.some(existingImage => existingImage.previewUrl === newImage.previewUrl) - ); + Promise.all(newImagesPromises).then(newImages => { + const uniqueNewImages = newImages.filter(newImage => + !this.images.some(existingImage => existingImage.previewUrl === newImage.previewUrl) + ); - if (uniqueNewImages.length === 0) { - alert('이미 선택된 이미지입니다.'); - return; - } + if (uniqueNewImages.length === 0) { + alert('이미 선택된 이미지입니다.'); + return; + } + + this.images = [...this.images, ...uniqueNewImages]; + }); + }, + removeImage(index) { + // 이미지 삭제 시 미리보기 URL 해제 + URL.revokeObjectURL(this.images[index].previewUrl); + this.images.splice(index, 1); + }, - this.images = [...this.images, ...uniqueNewImages]; - }); -}, - removeImage(index) { - // 이미지 삭제 시 미리보기 URL 해제 - URL.revokeObjectURL(this.images[index].previewUrl); - this.images.splice(index, 1); - }, async uploadImages() { - const prefix = 'images'; const uploadedUrls = []; - + + this.uploadError = false; + + console.log("Starting image upload..."); + for (const image of this.images) { try { - // 1. 프리사인드 URL 요청 - - const { data } = await axios.post( - `${process.env.VUE_APP_BE_API_URL}/file/${prefix}/presigned-url`, - { - fileName: image.name, - fileType: image.type, - } - ); + const apiUrl = `${process.env.VUE_APP_BE_API_URL}/file/IMAGE/presigned-url`; + console.log('Requesting presigned URL from:', apiUrl); - // 2. 프리사인드 URL을 이용해 이미지 업로드 - - await axios.put(data.url, image.file, { - headers: { 'Content-Type': image.file.type }, + // 프리사인드 URL 요청 + const response = await axios.post(apiUrl, { + imageName: image.file.name, // 파일 이름 + }); + + // 백엔드가 반환한 프리사인드 URL + const presignedUrl = response.data; // 데이터가 문자열 URL이라고 가정 + + console.log("Received presigned URL:", presignedUrl); + + // S3에 파일 업로드 + await axios.put(presignedUrl, image.file, { + headers: { + "Content-Type": image.file.type, // 파일 MIME 타입 설정 + }, }); - - // 3. 업로드된 이미지의 URL을 저장 (프리사인드 URL에서 파일 URL을 추출) - - const uploadedUrl = data.url.split('?')[0]; + console.log("Image uploaded successfully!"); + + // 업로드된 이미지 URL 저장 (프리사인드 URL의 base URL만 추출) + const uploadedUrl = presignedUrl.split("?")[0]; // URL에서 쿼리 파라미터 제거 uploadedUrls.push(uploadedUrl); - + console.log("Uploaded image URL:", uploadedUrl); + } catch (error) { - console.error('Error uploading image:', error); - alert('이미지 업로드 중 오류가 발생했습니다.'); + console.error("Error uploading image:", error); + this.uploadError = true; + alert("이미지 업로드 중 오류가 발생했습니다."); + return; } } - + this.uploadedImageUrls = uploadedUrls; - }, - + console.log("Uploaded image URLs:", this.uploadedImageUrls); + }, + async saveData() { if (!this.isFormComplete) { alert('모든 필드를 입력해주세요.'); return; } + // 이미지가 선택된 경우 업로드 진행 if (this.images.length > 0) { await this.uploadImages(); } + // 이미지 업로드 중 오류가 발생한 경우 saveData 종료 + if (this.uploadError) { + return; // 오류 발생 시 저장을 진행하지 않음 + } + // 백엔드 연동을 위한 데이터 객체 const newPortfolio = { - title: this.activityName, - createdDate: this.activityDate, - tags: this.selectedTags, - star: { ...this.star }, - pmi: { ...this.pmi }, - images: this.uploadedImageUrls // 업로드된 이미지 URL을 포함 + name: this.activityName, // 활동명 + startDate: this.activityDate, // 활동 시작일 + tags: this.selectedTags, // 선택된 태그들 + experience: { // STAR 메서드 적용 + situation: this.star.situation, + task: this.star.task, + action: this.star.action, + result: this.star.result, + }, + pmi: { // PMI 메서드 적용 + plus: this.pmi.plus, + minus: this.pmi.minus, + interesting: this.pmi.interesting, + }, + photoUrls: this.uploadedImageUrls // 업로드된 이미지 URL 배열 }; try { - await axios.post( - `${process.env.VUE_APP_BE_API_URL}/api/portfolios`, + // 포트폴리오 저장 요청 + const response = await axios.post( + `${process.env.VUE_APP_BE_API_URL}/api/portfolios`, newPortfolio ); + // 서버 응답 내용 출력 + console.log('POST Response:', response.data); // 응답 확인 alert('활동이 성공적으로 저장되었습니다!'); + // 포트폴리오 목록을 다시 불러옴 + await this.fetchPortfolios(); // 포트폴리오 목록을 다시 가져와서 업데이트 + // 저장 후 페이지 이동 (예: 메인 페이지) this.$router.push('/akopolio/main'); } catch (error) { - console.error('Error saving portfolio:', error); + console.error('Error saving portfolio:', error); // 에러 로그 출력 alert('저장 중 오류가 발생했습니다.'); } }, - + // 새로운 fetchPortfolios 메서드 추가 + async fetchPortfolios() { + try { + const response = await axios.get( + `${process.env.VUE_APP_BE_API_URL}/api/portfolios`, + { credentials: 'include' } + ); + this.portfolios = response.data; + } catch (error) { + console.error('포트폴리오 목록 불러오기 오류:', error); + alert('포트폴리오 목록을 불러오는 중 오류가 발생했습니다.'); + } + }, resetForm() { this.activityName = ''; this.activityDate = ''; @@ -201,5 +280,9 @@ export default { this.images = []; this.uploadedImageUrls = []; } + }, + + mounted() { + this.fetchUserData(); } -}; \ No newline at end of file +}; diff --git a/src/views/akopolio/detail/akopolioDetail.vue b/src/views/akopolio/detail/akopolioDetail.vue index e7613fa..e53ee7d 100644 --- a/src/views/akopolio/detail/akopolioDetail.vue +++ b/src/views/akopolio/detail/akopolioDetail.vue @@ -72,7 +72,7 @@

활동 이미지

-
+
diff --git a/src/views/akopolio/detail/detail.js b/src/views/akopolio/detail/detail.js index 5a59665..f916125 100644 --- a/src/views/akopolio/detail/detail.js +++ b/src/views/akopolio/detail/detail.js @@ -3,68 +3,105 @@ import MainFooter from '../../../components/layout/Footer.vue'; import { ref, onMounted } from 'vue'; import { useRouter, useRoute } from 'vue-router'; // vue-router 기능 사용 +const nickname = ref(''); + +const fetchUserData = async () => { + try { + const response = await fetch( + `${process.env.VUE_APP_BE_API_URL}/api/users/profile`, + { + method: 'GET', + credentials: 'include', + } + ); + + if (response.ok) { + const data = await response.json(); + nickname.value = data.nickname; + } else { + console.error('사용자 정보 불러오기 실패:', response.status, response.statusText); + if (response.status === 401) { + alert('인증에 실패했습니다. 다시 로그인해주세요.'); + window.location.href = '/login'; + } else { + alert('사용자 정보 가져오기 실패. 다시 시도해주세요.'); + } + } + } catch (error) { + console.error('사용자 정보 가져오기 오류:', error); + alert('사용자 정보 가져오기 오류가 발생했습니다.'); + } +}; + export default { components: { MainHeader, MainFooter, }, setup() { - const defaultPortfolio = { - title: '예시 활동명', - createdDate: '2023-11-27', - tags: ['전공', '프로젝트'], - star: { - situation: '이 프로젝트는 동아리 활동의 일환으로 시작되었습니다.', - task: '팀원들과 함께 웹사이트 개발을 진행해야 했습니다.', - action: 'Vue.js와 Firebase를 활용해 프로젝트를 완성했습니다.', - result: '사용자 수가 200명 이상 증가했습니다.', - }, - pmi: { - plus: '새로운 기술을 배워서 유익했습니다.', - minus: '일정 관리가 어려웠습니다.', - interesting: '사용자 피드백이 매우 흥미로웠습니다.', - }, - images: [ - 'https://via.placeholder.com/300.png?text=Image+1', - 'https://via.placeholder.com/300.png?text=Image+2', - ], - }; - const portfolio = ref(null); // 포트폴리오 데이터 저장 const images = ref([]); // 업로드된 이미지 또는 미리보기 저장 - + const nickname = ref(''); // 사용자 닉네임 저장 + const userId = ref(null); // 사용자 ID 저장 + const route = useRoute(); // route 사용 const router = useRouter(); // router 사용 - const portfolioId = route.params.id; // route에서 id 값 가져오기 - + const portfolioId = route.params.id; // route에서 portfolioId 가져오기 + + + console.log("Portfolio ID:", portfolioId); + // 포트폴리오 데이터 가져오기 - const fetchPortfolioById = async (id) => { + const fetchPortfolioById = async (portfolioId) => { try { - // 백엔드 API 연동 - const response = await fetch( - `${process.env.VUE_APP_BE_API_URL}/api/portfolios/${id}` - ); - + const apiUrl = `${process.env.VUE_APP_BE_API_URL}/api/portfolios/${portfolioId}`; + console.log('Fetching portfolio from URL:', apiUrl); // API URL 출력 + + // Fetch 요청 + const response = await fetch(apiUrl); + console.log('Response status:', response.status); // 상태 코드 확인 + if (!response.ok) { - throw new Error(`Failed to fetch portfolio: ${response.statusText}`); + console.error(`Response status: ${response.status}, ${response.statusText}`); + // 예외 처리를 통해 사용자에게 상세한 에러 메시지 제공 + if (response.status === 404) { + throw new Error('포트폴리오를 찾을 수 없습니다.'); + } else if (response.status === 500) { + throw new Error('서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.'); + } else { + throw new Error(`Failed to fetch portfolio: ${response.statusText}`); + } } - + const data = await response.json(); - portfolio.value = data || defaultPortfolio; // 데이터가 없으면 예시 데이터 사용 - images.value = portfolio.value.images || []; + + // 응답 데이터 확인 + console.log('Received portfolio data:', data); + + // 포트폴리오 데이터를 구조에 맞게 저장 + portfolio.value = { + baseInfo: data.baseInfo || {}, // 기본 정보 저장 + experience: data.experience || {}, // 경험 정보 저장 + pmi: data.pmi || {}, // PMI 정보 저장 + photoUrls: data.photoUrls || [], // 사진 URL 배열 저장 + }; + + // 이미지 데이터 따로 저장 + images.value = portfolio.value.photoUrls; + } catch (error) { console.error('Error fetching portfolio:', error); - alert('포트폴리오를 가져오는 중 오류가 발생했습니다. 예시 데이터를 표시합니다.'); - portfolio.value = defaultPortfolio; // 오류 시 예시 데이터 사용 - images.value = defaultPortfolio.images; + alert('포트폴리오를 가져오는 중 오류가 발생했습니다.'); + portfolio.value = null; // 오류 발생 시 초기화 + images.value = []; } }; - + // 수정 페이지로 이동 const editPortfolio = () => { router.push(`/akopolio/edit/${portfolioId}`); }; - + // 포트폴리오 삭제 const handleDeletePortfolio = async () => { if (confirm('정말 삭제하시겠습니까?')) { @@ -74,11 +111,11 @@ export default { `${process.env.VUE_APP_BE_API_URL}/api/portfolios/${portfolioId}`, { method: 'DELETE' } ); - + if (!response.ok) { throw new Error(`Failed to delete portfolio: ${response.statusText}`); } - + alert('삭제되었습니다.'); router.push('/akopolio/main'); } catch (error) { @@ -87,21 +124,19 @@ export default { } } }; - + // 컴포넌트 마운트 시 데이터 가져오기 onMounted(() => { - fetchPortfolioById(portfolioId); + fetchUserData(); // 사용자 정보도 마운트 시 호출 + fetchPortfolioById(portfolioId); }); - + return { portfolio, images, + nickname, editPortfolio, handleDeletePortfolio, }; }, }; - - - - diff --git a/src/views/akopolio/main/akopolioMain.vue b/src/views/akopolio/main/akopolioMain.vue index 4f15466..8f6d145 100644 --- a/src/views/akopolio/main/akopolioMain.vue +++ b/src/views/akopolio/main/akopolioMain.vue @@ -7,7 +7,8 @@
@@ -53,14 +54,14 @@ @click="goToDetailPage(item.id)" >
-

{{ item.title }}

+

{{ item.name }}

{{ tag }}
-

활동일: {{ item.createdDate }}

+

활동일: {{ item.startDate }}

@@ -82,6 +83,7 @@ /> +
@@ -295,9 +297,9 @@ button.active { } .floating-btn { - position: absolute; /* 부모 요소에 상대적으로 고정 */ - bottom: 4%; /* 부모 요소의 높이를 기준으로 */ - right: 5%; /* 부모 요소의 너비를 기준으로 */ + position: fixed; + bottom: 8%; + right: calc(50% - 180px); background-color: #f4b28c; color: white; border-radius: 50%; @@ -307,8 +309,10 @@ button.active { align-items: center; justify-content: center; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + z-index: 100; /* 다른 요소들 위에 표시되도록 */ } + .floating-btn:hover { cursor: pointer; background-color: #eaa279; diff --git a/src/views/akopolio/main/main.js b/src/views/akopolio/main/main.js index 1d5520f..6c6a72b 100644 --- a/src/views/akopolio/main/main.js +++ b/src/views/akopolio/main/main.js @@ -2,6 +2,8 @@ import axios from 'axios'; import PaginationNav from '../paginationNav.vue'; import MainHeader from '../../../components/layout/Header.vue'; import MainFooter from '../../../components/layout/Footer.vue'; +import { ref, computed, onMounted } from 'vue' + export default { components: { @@ -47,9 +49,10 @@ export default { let filteredList = this.portfolioList; // 검색 필터 적용 - if (this.searchQuery) { + const query = this.searchQuery.trim().toLowerCase(); // 검색어를 소문자로 변환 + if (query.length > 0) { filteredList = filteredList.filter(item => - item.title.includes(this.searchQuery) + item.name.toLowerCase().includes(query) // 포트폴리오 이름을 검색어와 비교 ); } @@ -63,17 +66,18 @@ export default { // 날짜 필터 적용 if (this.startDate || this.endDate) { filteredList = filteredList.filter(item => { - const itemDate = new Date(item.createdDate); - if (this.startDate && !this.endDate) { - return itemDate >= new Date(this.startDate); - } else if (!this.startDate && this.endDate) { - return itemDate <= new Date(this.endDate); - } else { - return ( - itemDate >= new Date(this.startDate) && - itemDate <= new Date(this.endDate) - ); + const itemDate = new Date(item.startDate); // startDate를 Date 객체로 변환 + const start = this.startDate ? new Date(this.startDate) : null; + const end = this.endDate ? new Date(this.endDate) : null; + + if (start && !end) { + return itemDate >= start; + } else if (!start && end) { + return itemDate <= end; + } else if (start && end) { + return itemDate >= start && itemDate <= end; } + return true; }); } @@ -88,6 +92,37 @@ export default { }, }, methods: { + async fetchUserData() { + try { + const response = await fetch( + `${process.env.VUE_APP_BE_API_URL}/api/users/profile`, + { + method: 'GET', + credentials: 'include', + } + ); + + console.log('서버 응답 상태:', response.status, response.statusText); + + if (response.ok) { + const data = await response.json(); + this.nickname = data.nickname; // nickname을 data 안으로 변경 + console.log('사용자 정보:', data); + } else { + console.error('사용자 정보 불러오기 실패:', response.status, response.statusText); + if (response.status === 401) { + alert('인증에 실패했습니다. 다시 로그인해주세요.'); + window.location.href = '/login'; + } else { + alert('사용자 정보 가져오기 실패. 다시 시도해주세요.'); + } + } + } catch (error) { + console.error('사용자 정보 가져오기 오류:', error); + alert('사용자 정보 가져오기 오류가 발생했습니다.'); + } + }, + toggleDropdown() { this.isDropdownOpen = !this.isDropdownOpen; }, @@ -103,7 +138,7 @@ export default { }, applyFilters() { - this.currentPage = 1; + this.currentPage = 1; }, handlePageChanged(newPage) { @@ -129,15 +164,26 @@ export default { async fetchPortfolios() { try { const response = await axios.get(`${process.env.VUE_APP_BE_API_URL}/api/portfolios`); - this.portfolioList = response.data.length ? response.data : this.defaultPortfolioList; // 데이터가 없으면 예시 데이터 사용 + + // API 응답에 따라 포트폴리오 리스트 업데이트 + this.portfolioList = response.data.length + ? response.data.map(portfolio => ({ + id: portfolio.portfolioId, // 포트폴리오 ID + name: portfolio.name, // 포트폴리오 이름 + updatedAt: portfolio.updatedAt, // 마지막 업데이트 시간 + startDate: portfolio.startDate, // 시작일 + tags: portfolio.tags // 태그 리스트 + })) + : this.defaultPortfolioList; // 데이터가 없으면 기본 리스트 사용 } catch (error) { console.error('Error fetching portfolios:', error); - this.portfolioList = this.defaultPortfolioList; // 오류 시 예시 데이터 사용 + this.portfolioList = this.defaultPortfolioList; // 오류 시 기본 데이터 사용 } - }, + }, }, mounted() { + this.fetchUserData(); // 사용자 정보 로드 this.fetchPortfolios(); } };