public 정리
This commit is contained in:
@@ -391,7 +391,6 @@
|
||||
font-weight: 700;
|
||||
margin-left: 3px;
|
||||
vertical-align: middle;
|
||||
color: var(--p-purple-500);
|
||||
}
|
||||
|
||||
.team-title-row {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<div class="event-public-wrapper">
|
||||
<h1 v-if="clubName" class="p-text-primary p-text-bold p-text-lg p-mb-2">{{ clubName }}</h1>
|
||||
<Divider class="p-mb-4" />
|
||||
<Card class="p-shadow-8 p-p-5 p-rounded-xl event-public-card">
|
||||
<template #content>
|
||||
<div class="p-shadow-8 p-p-5 p-rounded-xl event-public-card">
|
||||
<div>
|
||||
<div v-if="loading" class="p-text-center p-mb-4">로딩 중...</div>
|
||||
<div v-else-if="error" class="p-text-center p-mb-4">{{ error }}</div>
|
||||
<div v-else-if="!isAuthenticated">
|
||||
@@ -55,150 +55,110 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-text-secondary p-mb-4">{{ event.description || '' }}</div>
|
||||
|
||||
<!-- 팀별 게임별 점수/등수 표: 하단으로 이동 -->
|
||||
<div v-if="teams.length > 0 || unassignedMembers.length > 0" class="team-score-table-wrapper p-mt-4">
|
||||
<DataTable :value="getAllTeamsWithUnassigned()" class="team-score-table" :responsiveLayout="'scroll'">
|
||||
<Column header="팀">
|
||||
<template #body="slotProps">
|
||||
팀 {{ slotProps.data.teamNumber }}
|
||||
<span v-if="slotProps.data.handicap" class="team-hc-small">
|
||||
팀 핸디캡: {{ slotProps.data.handicap ?? 0 }}
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column v-for="n in event.gameCount" :key="n" :header="n + 'G'">
|
||||
<template #body="slotProps">
|
||||
<span class="score-main">{{ getTeamTotalScore(slotProps.data, n) }}</span>
|
||||
<span class="block rank-badge"
|
||||
v-if="getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + ''] !== undefined"
|
||||
:class="'rank-badge rank-' + getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + '']">
|
||||
({{ getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + ''] }}위)
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="총점">
|
||||
<template #body="slotProps">
|
||||
{{Array.from({ length: event.gameCount }, (_, i) => getTeamTotalScore(slotProps.data, i +
|
||||
1)).reduce((a, b) => a + b, 0) }}
|
||||
<span class="block rank-badge" :class="'rank-' + getTeamOverallRank(slotProps.data)">
|
||||
({{ getTeamOverallRank(slotProps.data) }}위)
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
<div v-for="team in getAllTeamsWithUnassigned()" :key="team.id" class="team-card">
|
||||
<Card class=" p-rounded-lg p-shadow-1 p-p-3 p-mb-4">
|
||||
<template #title>
|
||||
<div class="team-title-row">
|
||||
<span class="team-name-strong">{{ parseInt(team.teamNumber) ? '팀 ' + team.teamNumber : '미배정 인원'}}</span>
|
||||
<span class="team-hc" v-if="team.handicap">팀 핸디캡: {{ team.handicap ?? 0 }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="member-list">
|
||||
<div v-for="member in team.members" :key="member.participantId" class="member-row">
|
||||
<div class="member-info-block">
|
||||
<div class="member-header-row">
|
||||
<span class="member-name">{{ member.name }}</span>
|
||||
<Tag v-if="member.isGuest" value="G" severity="warning" class="ml-2" />
|
||||
</div>
|
||||
<div class="info-under-name">
|
||||
{{ member.gender || '-' }}
|
||||
<span v-if="member.status" :class="{
|
||||
'status-cancel': member.status === '취소',
|
||||
'status-pending': member.status === '참가예정'
|
||||
}">
|
||||
· {{ member.status }}
|
||||
</span>
|
||||
<span v-if="member.paymentStatus" :class="{
|
||||
'payment-unpaid': member.paymentStatus === '미납'
|
||||
}">
|
||||
· {{ member.paymentStatus }}
|
||||
<Divider />
|
||||
<!-- 팀별 게임별 점수/등수 표 -->
|
||||
<template v-if="teams.length > 0">
|
||||
<div class="team-score-table-wrapper p-mt-4">
|
||||
<DataTable :value="getAllTeamsWithUnassigned()" class="team-score-table" :responsiveLayout="'scroll'">
|
||||
<Column header="팀">
|
||||
<template #body="slotProps">
|
||||
팀 {{ slotProps.data.teamNumber }}
|
||||
<span v-if="slotProps.data.handicap" class="team-hc-small">
|
||||
팀 핸디캡: {{ slotProps.data.handicap ?? 0 }}
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column v-for="n in event.gameCount" :key="n" :header="n + 'G'">
|
||||
<template #body="slotProps">
|
||||
<span class="score-main">{{ getTeamTotalScore(slotProps.data, n) }}</span>
|
||||
<span class="block rank-badge"
|
||||
v-if="getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + ''] !== undefined"
|
||||
:class="'rank-badge rank-' + getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + '']">
|
||||
({{ getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + ''] }}위)
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="총점">
|
||||
<template #body="slotProps">
|
||||
{{Array.from({ length: event.gameCount }, (_, i) => getTeamTotalScore(slotProps.data, i +
|
||||
1)).reduce((a, b) => a + b, 0) }}
|
||||
<span class="block rank-badge" :class="'rank-' + getTeamOverallRank(slotProps.data)">
|
||||
({{ getTeamOverallRank(slotProps.data) }}위)
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
<div v-for="team in getAllTeamsWithUnassigned()" :key="team.id">
|
||||
<MemberListCard
|
||||
:title="parseInt(team.teamNumber) ? '팀 ' + team.teamNumber : '미배정 인원'"
|
||||
:members="team.members"
|
||||
:teamHandicap="team.handicap"
|
||||
:teamNumber="team.teamNumber"
|
||||
:gameCount="event.gameCount"
|
||||
:canInputScore="canInputScore"
|
||||
:scoresProxy="scoresProxy"
|
||||
:scoreStates="scoreStates"
|
||||
:onScoreInput="onScoreInput"
|
||||
:onScoreBlur="onScoreBlur"
|
||||
:onScoreEnter="onScoreEnter"
|
||||
:getMemberTotalScore="getMemberTotalScore"
|
||||
:getMemberAverageScore="getMemberAverageScore"
|
||||
:scoreColorClass="scoreColorClass"
|
||||
/>
|
||||
<!-- 팀 카드 요약(총점/등수/게임별 점수)은 '팀'만 노출 -->
|
||||
<div v-if="team.teamNumber" class="team-game-scores">
|
||||
<div class="team-total-row team-total-row-responsive team-total-row-block">
|
||||
<div class="team-games-grid"
|
||||
:style="{ gridTemplateColumns: `repeat(${event.gameCount > 3 ? 3 : event.gameCount}, 1fr)` }">
|
||||
<template v-for="n in event.gameCount" :key="n">
|
||||
<div class="team-game-cell">
|
||||
<b>{{ n }}G:</b> {{ getTeamTotalScore(team, n) }}
|
||||
<span v-if="getRanksByGame(n)[(team.id || team.teamNumber) + ''] !== undefined"
|
||||
class="rank-badge"
|
||||
:class="'rank-' + getRanksByGame(n)[(team.id || team.teamNumber) + '']">
|
||||
({{ getRanksByGame(n)[(team.id || team.teamNumber) + ''] }}위)
|
||||
</span>
|
||||
</div>
|
||||
<div class="member-meta">핸디캡: {{ member.handicap ?? '-' }} / 에버리지: {{ member.average ?? '-' }}
|
||||
</div>
|
||||
<div class="member-meta">{{ member.comment }}</div>
|
||||
</div>
|
||||
<div class="score-inputs-vertical">
|
||||
<template v-for="n in event.gameCount" :key="n">
|
||||
<div class="score-input-hc-wrap">
|
||||
<div class="score-input-status-wrap">
|
||||
<InputText
|
||||
v-if="canInputScore"
|
||||
v-model.number="scoresProxy[member.participantId + '-' + n]"
|
||||
type="number"
|
||||
class="score-input"
|
||||
:placeholder="n + 'G'"
|
||||
maxlength="3"
|
||||
@input="onScoreInput(member, n, team.teamNumber)"
|
||||
@blur="onScoreBlur(member, n, team.teamNumber)"
|
||||
@keydown.enter="onScoreEnter(member, n, team.teamNumber)"
|
||||
/>
|
||||
<span v-else>{{ member.scores?.[n - 1] ?? '-' }}</span>
|
||||
<span :class="scoreColorClass(scoreStates[member.participantId + '-' + n], scoresProxy[member.participantId + '-' + n], member.handicap)"
|
||||
class="score-hc-value score-status-text"
|
||||
:key="scoreStates[member.participantId + '-' + n] + '-' + scoresProxy[member.participantId + '-' + n]">
|
||||
{{ (scoresProxy[member.participantId + '-' + n] !== undefined && scoresProxy[member.participantId + '-' + n] !== '') ?
|
||||
Number(scoresProxy[member.participantId + '-' + n]) + (Number(member.handicap) || 0) : ''
|
||||
}}
|
||||
<i v-if="scoreStates[member.participantId + '-' + n] === 'saving'" class="pi pi-spin pi-spinner score-status-icon" />
|
||||
<i v-else-if="scoreStates[member.participantId + '-' + n] === 'saved'" class="pi pi-check score-status-icon" />
|
||||
<i v-else-if="scoreStates[member.participantId + '-' + n] === 'error'" class="pi pi-times score-status-icon" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="member-score-summary">
|
||||
<span>총점: {{ getMemberTotalScore(member) }}</span>
|
||||
<span class="member-score-avg">/ 에버리지: {{ getMemberAverageScore(member) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="team-total-main">
|
||||
<span class="team-total-label">총점</span>
|
||||
<span class="team-total-scores">{{ Array.from({ length: event.gameCount }, (_, i) => getTeamTotalScore(team, i + 1)).reduce((a, b) => a + b, 0) }}</span>
|
||||
<span v-if="getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' })"
|
||||
class="rank-badge"
|
||||
:class="'rank-' + getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' })">
|
||||
{{ getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' }) }}위
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
<div class="team-game-scores">
|
||||
<div class="team-total-row team-total-row-responsive team-total-row-block">
|
||||
<div class="team-games-grid"
|
||||
:style="{ gridTemplateColumns: `repeat(${event.gameCount > 3 ? 3 : event.gameCount}, 1fr)` }">
|
||||
<template v-for="n in event.gameCount" :key="n">
|
||||
<div class="team-game-cell">
|
||||
<b>{{ n }}G:</b> {{ getTeamTotalScore(team, n) }}
|
||||
<span v-if="getRanksByGame(n)[(team.id || team.teamNumber) + ''] !== undefined"
|
||||
class="rank-badge"
|
||||
:class="'rank-' + getRanksByGame(n)[(team.id || team.teamNumber) + '']">
|
||||
({{ getRanksByGame(n)[(team.id || team.teamNumber) + ''] }}위)
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="team-total-main">
|
||||
<span class="team-total-label">총점</span>
|
||||
<span class="team-total-scores">{{Array.from({ length: event.gameCount }, (_, i) =>
|
||||
getTeamTotalScore(team, i + 1)).reduce((a, b) => a + b, 0)}}</span>
|
||||
<span v-if="getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' })"
|
||||
class="rank-badge"
|
||||
:class="'rank-' + getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' })">
|
||||
{{ getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' }) }}위
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
<JoinDialog v-if="showJoinDialog && canJoin" v-model:visible="showJoinDialog" :publicHash="publicHash"
|
||||
:password="needPassword ? inputPassword : ''" :participants="participants"
|
||||
:availableMembers="availableMembers" :eventStatus="event.status"
|
||||
@close="showJoinDialog = false" @updated="reload" />
|
||||
<GuestDialog v-if="showGuestDialog && canAddGuest" v-model:visible="showGuestDialog" :publicHash="publicHash"
|
||||
:password="needPassword ? inputPassword : ''" @close="showGuestDialog = false" @updated="reload" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<MemberListCard
|
||||
title="참가자"
|
||||
:members="sortedParticipants"
|
||||
:gameCount="event.gameCount"
|
||||
:canInputScore="canInputScore"
|
||||
:scoresProxy="scoresProxy"
|
||||
:scoreStates="scoreStates"
|
||||
:onScoreInput="onScoreInput"
|
||||
:onScoreBlur="onScoreBlur"
|
||||
:onScoreEnter="onScoreEnter"
|
||||
:getMemberTotalScore="getMemberTotalScore"
|
||||
:getMemberAverageScore="getMemberAverageScore"
|
||||
:scoreColorClass="scoreColorClass"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<JoinDialog v-if="showJoinDialog && canJoin" v-model:visible="showJoinDialog" :publicHash="publicHash"
|
||||
:participants="participants"
|
||||
:availableMembers="availableMembers" :eventStatus="event.status"
|
||||
@close="showJoinDialog = false" @updated="reload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -206,7 +166,8 @@
|
||||
import { ref, computed, watch, reactive, onMounted } from 'vue';
|
||||
import { debounce } from 'lodash-es'; // lodash 설치 필요 (이미 있다면 무시)
|
||||
import { useRoute } from 'vue-router';
|
||||
import JoinDialog from './JoinDialog.vue';
|
||||
import MemberListCard from '@/components/public/MemberListCard.vue';
|
||||
import JoinDialog from '@/components/public/JoinDialog.vue';
|
||||
import dayjs from 'dayjs';
|
||||
import PublicEventService from '@/services/PublicEventService';
|
||||
|
||||
@@ -223,7 +184,6 @@ const isAuthenticated = ref(false);
|
||||
const inputPassword = ref('');
|
||||
const passwordError = ref('');
|
||||
const showJoinDialog = ref(false);
|
||||
const showGuestDialog = ref(false);
|
||||
|
||||
const allMembers = ref([]); // 전체 클럽 멤버 목록
|
||||
const availableMembers = ref([]); // 참가 신청 가능 명단
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:visible="visible"
|
||||
@update:visible="val => $emit('update:visible', val)"
|
||||
modal
|
||||
header="참가 신청/수정"
|
||||
:style="{ width: '400px' }"
|
||||
:closable="true"
|
||||
@hide="$emit('close')"
|
||||
>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<!-- 회원 선택 -->
|
||||
<div class="w-full min-w-[200px]">
|
||||
<label>회원</label>
|
||||
<Select
|
||||
v-model="selectedMember"
|
||||
:options="memberOptions"
|
||||
optionLabel="name"
|
||||
placeholder="회원 선택"
|
||||
class="w-full"
|
||||
filter
|
||||
filterPlaceholder="이름으로 검색"
|
||||
>
|
||||
<!-- 선택된 값 표시 -->
|
||||
<template #value="slotProps">
|
||||
<div v-if="slotProps.value && slotProps.value.isNewGuest" class="flex align-items-center text-primary font-bold">
|
||||
<i class="pi pi-user-plus mr-2" /> 신규 게스트 추가
|
||||
</div>
|
||||
<div v-else-if="slotProps.value" class="flex align-items-center">
|
||||
<Tag :value="slotProps.value.group" :severity="slotProps.value.group === '참가자' ? 'success' : 'info'" class="mr-2" />
|
||||
<span style="font-weight:bold;">{{ slotProps.value.name }}</span>
|
||||
<span style="color:#888; font-size:0.93em; margin-left: 0.5em;">
|
||||
{{ slotProps.value.memberType }} / {{ slotProps.value.gender }} / 에버리지: {{ slotProps.value.average ?? '-' }}
|
||||
</span>
|
||||
</div>
|
||||
<span v-else>
|
||||
{{ slotProps.placeholder }}
|
||||
</span>
|
||||
</template>
|
||||
<!-- 옵션 목록 표시 -->
|
||||
<template #option="slotProps">
|
||||
<div v-if="slotProps.option.isNewGuest" class="flex align-items-center text-primary font-bold">
|
||||
<i class="pi pi-user-plus mr-2" /> 신규 게스트 추가
|
||||
</div>
|
||||
<div v-else class="flex align-items-center">
|
||||
<Tag :value="slotProps.option.group" :severity="slotProps.option.group === '참가자' ? 'success' : 'info'" class="mr-2" />
|
||||
<span style="font-weight:bold;">{{ slotProps.option.name }}</span>
|
||||
<span style="color:#888; font-size:0.93em; margin-left: 0.5em;">
|
||||
{{ slotProps.option.memberType }} / {{ slotProps.option.gender }} / 에버리지: {{ slotProps.option.average ?? '-' }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</Select>
|
||||
<small v-if="submitted && !selectedMemberId" class="p-error">회원을 선택해주세요.</small>
|
||||
|
||||
<!-- 신규 게스트 추가 입력폼 -->
|
||||
<div v-if="selectedMember && selectedMember.isNewGuest" class="w-full border p-3 rounded bg-gray-50 mt-2">
|
||||
<div class="mb-2">
|
||||
<label>이름</label>
|
||||
<InputText v-model="newGuest.name" class="w-full" />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label>연락처</label>
|
||||
<InputText v-model="newGuest.phone" class="w-full" />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label>성별</label>
|
||||
<Select v-model="newGuest.gender" :options="genderOptions" optionLabel="label" optionValue="value" placeholder="성별 선택" class="w-full" />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label>에버리지</label>
|
||||
<InputText v-model="newGuest.average" class="w-full" />
|
||||
</div>
|
||||
<Button label="게스트 추가" class="mt-1" @click="addNewGuest" :disabled="!newGuest.name || !newGuest.phone || !newGuest.gender" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 참가 상태 -->
|
||||
<div class="w-full min-w-[200px]">
|
||||
<label>참가 상태</label>
|
||||
<Select
|
||||
v-model="status"
|
||||
:options="statusOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="상태를 선택하세요"
|
||||
checkmark
|
||||
class="w-full"
|
||||
/>
|
||||
<small v-if="submitted && !status" class="p-error">참가 상태를 선택해주세요.</small>
|
||||
</div>
|
||||
<!-- 회비 납부 상태 -->
|
||||
<div class="flex-1 min-w-[200px]">
|
||||
<label>회비 납부 상태</label>
|
||||
<Select
|
||||
v-model="paymentStatus"
|
||||
:options="paymentStatusOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="납부 상태를 선택하세요"
|
||||
checkmark
|
||||
class="w-full"
|
||||
/>
|
||||
<small v-if="submitted && !paymentStatus" class="p-error">납부 상태를 선택해주세요.</small>
|
||||
</div>
|
||||
<!-- 비고 -->
|
||||
<div class="w-full">
|
||||
<label>비고</label>
|
||||
<InputText v-model="comment" class="w-full" />
|
||||
</div>
|
||||
<div v-if="error" class="w-full p-error p-mb-2">{{ error }}</div>
|
||||
<div class="w-full flex justify-content-end gap-2 mt-3">
|
||||
<Button type="submit" label="확인" @click="submit" />
|
||||
<Button type="button" label="취소" severity="secondary" @click="$emit('close')" />
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import Tag from 'primevue/tag';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import Select from 'primevue/select';
|
||||
import Button from 'primevue/button';
|
||||
const props = defineProps({
|
||||
publicHash: String,
|
||||
participants: Array,
|
||||
availableMembers: Array,
|
||||
visible: Boolean, // Dialog의 v-model:visible과 연결
|
||||
eventStatus: String
|
||||
});
|
||||
const emits = defineEmits(['close', 'updated']);
|
||||
|
||||
const selectedMember = ref(null);
|
||||
const memberOptions = computed(() => {
|
||||
const baseOptions = [
|
||||
...(props.participants || []).map(m => ({ ...m, group: '참가자', id: m.participantId, participantId: m.participantId, memberId: m.memberId })),
|
||||
...(props.availableMembers || []).map(m => ({ ...m, group: '미신청', id: m.memberId, participantId: null, memberId: m.memberId }))
|
||||
].sort((a, b) => {
|
||||
const nameA = a.name || '';
|
||||
const nameB = b.name || '';
|
||||
return nameA.localeCompare(nameB, 'ko');
|
||||
});
|
||||
if (props.eventStatus === '준비') {
|
||||
baseOptions.push({ name: '신규 게스트 추가', isNewGuest: true });
|
||||
}
|
||||
return baseOptions;
|
||||
});
|
||||
|
||||
|
||||
const selectedParticipantId = ref('');
|
||||
const selectedMemberId = ref('');
|
||||
watch(selectedMember, (val) => {
|
||||
selectedParticipantId.value = val ? val.participantId : '';
|
||||
selectedMemberId.value = val ? val.memberId : '';
|
||||
|
||||
// 참가자라면 기존 정보 자동 세팅
|
||||
if (val) {
|
||||
const found = (props.participants || []).find(m => m.memberId === val.memberId);
|
||||
status.value = found?.status || '';
|
||||
paymentStatus.value = found?.paymentStatus || '';
|
||||
comment.value = found?.comment || '';
|
||||
}
|
||||
});
|
||||
|
||||
const comment = ref('');
|
||||
const status = ref('');
|
||||
const paymentStatus = ref('');
|
||||
const error = ref('');
|
||||
const submitted = ref(false);
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '참가예정', value: '참가예정' },
|
||||
{ label: '참가확정', value: '참가확정' },
|
||||
{ label: '취소', value: '취소' }
|
||||
];
|
||||
const paymentStatusOptions = [
|
||||
{ label: '미납', value: '미납' },
|
||||
{ label: '납부완료', value: '납부완료' }
|
||||
];
|
||||
|
||||
|
||||
import PublicEventService from '@/services/PublicEventService';
|
||||
|
||||
const newGuest = ref({ name: '', phone: '', gender: '' });
|
||||
const genderOptions = [
|
||||
{ label: '남', value: '남성' },
|
||||
{ label: '여', value: '여성' }
|
||||
];
|
||||
|
||||
async function addNewGuest() {
|
||||
error.value = '';
|
||||
if (!newGuest.value.name || !newGuest.value.phone || !newGuest.value.gender) {
|
||||
error.value = '이름, 연락처, 성별을 모두 입력하세요.';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 실제 API 경로/파라미터는 상황에 맞게 수정 필요
|
||||
const res = await PublicEventService.createGuest(props.publicHash, {
|
||||
name: newGuest.value.name,
|
||||
phone: newGuest.value.phone,
|
||||
gender: newGuest.value.gender,
|
||||
average: newGuest.value.average
|
||||
});
|
||||
if (!res || !res.data || !res.data.memberId) throw new Error('게스트 등록 실패');
|
||||
// 새 게스트를 멤버 옵션에 추가하고 자동 선택
|
||||
const guest = {
|
||||
...res.data,
|
||||
group: '참가자',
|
||||
id: res.data.participantId,
|
||||
participantId: res.data.participantId,
|
||||
memberId: res.data.memberId
|
||||
};
|
||||
// options에 직접 push (reactivity 보장 위해 availableMembers에도 추가)
|
||||
props.availableMembers.push(guest);
|
||||
// 참가자 목록에도 추가
|
||||
props.participants.push(guest);
|
||||
// 선택값 세팅
|
||||
selectedMember.value = guest;
|
||||
selectedParticipantId.value = guest.participantId;
|
||||
selectedMemberId.value = guest.memberId;
|
||||
// 입력폼 초기화
|
||||
newGuest.value = { name: '', phone: '', gender: '', average: '' };
|
||||
} catch (e) {
|
||||
error.value = e.message || '게스트 등록 중 오류 발생';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function submit() {
|
||||
submitted.value = true;
|
||||
error.value = '';
|
||||
if (!selectedMemberId.value || !status.value || !paymentStatus.value) {
|
||||
error.value = '모든 항목을 입력해주세요.';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await PublicEventService.registerParticipant(props.publicHash, {
|
||||
participantId: selectedParticipantId.value,
|
||||
memberId: selectedMemberId.value,
|
||||
comment: comment.value,
|
||||
status: status.value,
|
||||
paymentStatus: paymentStatus.value
|
||||
});
|
||||
if (!res.ok) throw new Error('참가 신청에 실패했습니다.');
|
||||
emits('updated');
|
||||
emits('close');
|
||||
} catch (e) {
|
||||
error.value = e.message;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user