결제, 구독 기능

This commit is contained in:
2025-05-11 17:27:39 +09:00
parent 2924a61a9d
commit 1db3a03ffd
18 changed files with 3276 additions and 180 deletions
+20 -10
View File
@@ -83,6 +83,7 @@
:getStatusSeverity="getStatusSeverity"
:formatDate="formatDate"
:availableMembers="clubMembers"
:hasTeamFeature="hasTeamFeature"
@edit="editFromDetails"
@hide="hideDialog"
@participant-updated="fetchEvents"
@@ -105,11 +106,12 @@
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, computed } from 'vue';
import { useToast } from 'primevue/usetoast';
import { useRoute } from 'vue-router';
import { useClubStore } from '@/stores/clubStore';
import eventService from '@/services/eventService';
import apiClient from '@/services/api';
import dayjs from 'dayjs';
import clubService from '@/services/clubService';
import ScoreManagement from '@/components/event/ScoreManagement.vue';
@@ -142,6 +144,9 @@ const filters = ref({
startDate: { value: null, matchMode: 'equals' }
});
// 팀 기능 사용 권한 여부
const hasTeamFeature = computed(() => clubStore.hasFeature('team_create'));
// 이벤트 유형 옵션
const eventTypeOptions = [
{ name: '정기전', value: '정기전' },
@@ -156,17 +161,22 @@ const clubMembers = ref([]);
// 이벤트 목록 가져오기
const fetchEvents = async () => {
loading.value = true;
try {
const response = await eventService.getEvents(clubId.value);
events.value = response;
loading.value = true;
const data = await eventService.getEvents(clubId.value);
events.value = data;
} catch (error) {
toast.add({
severity: 'error',
summary: '오류',
detail: '이벤트 목록을 가져오는데 실패했습니다.',
life: 3000
});
console.error('이벤트 목록 로드 실패:', error);
// 403 오류는 중앙에서 처리하민로 여기서는 기타 오류만 처리
if (!error.response) {
toast.add({
severity: 'error',
summary: '오류',
detail: '이벤트 목록을 불러오는 데 실패했습니다.',
life: 3000
});
}
} finally {
loading.value = false;
}
File diff suppressed because it is too large Load Diff
+132
View File
@@ -0,0 +1,132 @@
<template>
<div class="payment-result fail">
<div class="result-container">
<div class="icon-container">
<i class="pi pi-times-circle"></i>
</div>
<h1>결제가 취소되었습니다</h1>
<p v-if="message">{{ message }}</p>
<div class="buttons">
<button class="primary-btn" @click="goToSubscription">구독 관리로 이동</button>
<button class="secondary-btn" @click="goToHome">홈으로 이동</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
const router = useRouter();
const route = useRoute();
const message = ref('');
onMounted(async () => {
// URL에서 메시지 가져오기
message.value = route.query.message || '결제가 취소되었습니다.';
// 부모 창에 결제 실패 메시지 전달
if (window.opener && window.opener.handlePaymentFail) {
window.opener.handlePaymentFail(message.value);
// 3초 후 창 닫기
setTimeout(() => {
window.close();
}, 3000);
}
});
const goToSubscription = () => {
if (window.opener) {
window.opener.location.href = '/club/subscription';
window.close();
} else {
router.push('/club/subscription');
}
};
const goToHome = () => {
if (window.opener) {
window.opener.location.href = '/';
window.close();
} else {
router.push('/');
}
};
</script>
<style scoped>
.payment-result {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f8f9fa;
}
.result-container {
background-color: white;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 40px;
text-align: center;
max-width: 500px;
width: 100%;
}
.icon-container {
margin-bottom: 20px;
}
.fail .icon-container i {
font-size: 80px;
color: #f44336;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
color: #333;
}
p {
color: #666;
margin-bottom: 30px;
}
.buttons {
display: flex;
justify-content: center;
gap: 15px;
}
.primary-btn, .secondary-btn {
padding: 10px 20px;
border-radius: 5px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.primary-btn {
background-color: #4caf50;
color: white;
border: none;
}
.primary-btn:hover {
background-color: #43a047;
transform: translateY(-2px);
}
.secondary-btn {
background-color: #f5f5f5;
color: #333;
border: 1px solid #ddd;
}
.secondary-btn:hover {
background-color: #e9e9e9;
transform: translateY(-2px);
}
</style>
@@ -0,0 +1,133 @@
<template>
<div class="payment-result success">
<div class="result-container">
<div class="icon-container">
<i class="pi pi-check-circle"></i>
</div>
<h1>결제가 완료되었습니다</h1>
<p>주문번호: {{ orderId }}</p>
<div class="buttons">
<button class="primary-btn" @click="goToSubscription">구독 관리로 이동</button>
<button class="secondary-btn" @click="goToHome">홈으로 이동</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import apiClient from '@/services/api';
const router = useRouter();
const route = useRoute();
const orderId = ref('');
onMounted(async () => {
// URL에서 주문 ID 가져오기
orderId.value = route.query.orderId || '';
// 부모 창에 결제 성공 메시지 전달
if (window.opener && window.opener.handlePaymentSuccess) {
window.opener.handlePaymentSuccess(orderId.value);
// 3초 후 창 닫기
setTimeout(() => {
window.close();
}, 3000);
}
});
const goToSubscription = () => {
if (window.opener) {
window.opener.location.href = '/club/subscription';
window.close();
} else {
router.push('/club/subscription');
}
};
const goToHome = () => {
if (window.opener) {
window.opener.location.href = '/';
window.close();
} else {
router.push('/');
}
};
</script>
<style scoped>
.payment-result {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f8f9fa;
}
.result-container {
background-color: white;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 40px;
text-align: center;
max-width: 500px;
width: 100%;
}
.icon-container {
margin-bottom: 20px;
}
.success .icon-container i {
font-size: 80px;
color: #4caf50;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
color: #333;
}
p {
color: #666;
margin-bottom: 30px;
}
.buttons {
display: flex;
justify-content: center;
gap: 15px;
}
.primary-btn, .secondary-btn {
padding: 10px 20px;
border-radius: 5px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.primary-btn {
background-color: #4caf50;
color: white;
border: none;
}
.primary-btn:hover {
background-color: #43a047;
transform: translateY(-2px);
}
.secondary-btn {
background-color: #f5f5f5;
color: #333;
border: 1px solid #ddd;
}
.secondary-btn:hover {
background-color: #e9e9e9;
transform: translateY(-2px);
}
</style>