public 정리

This commit is contained in:
2025-05-08 00:50:12 +09:00
parent 1684fbf5d1
commit 744a0db3bd
5 changed files with 189 additions and 145 deletions
@@ -145,7 +145,7 @@ watch(() => eventModel.value.id, async (eventId) => {
// 참가확정 참가자 목록을 계산하는 computed 속성 // 참가확정 참가자 목록을 계산하는 computed 속성
const confirmedParticipants = computed(() => { const confirmedParticipants = computed(() => {
const arr = eventModel.value.EventParticipants?.filter(p => p.status === '참가확정') || []; const arr = eventModel.value.EventParticipants?.filter(p => p.status === '참가확정' || p.status === '참가예정') || [];
console.log('[상위] 참가확정:', arr); console.log('[상위] 참가확정:', arr);
return arr; return arr;
}); });
@@ -130,7 +130,7 @@ const props = defineProps({
visible: Boolean, // Dialog v-model:visible visible: Boolean, // Dialog v-model:visible
eventStatus: String eventStatus: String
}); });
const emits = defineEmits(['close', 'updated']); const emits = defineEmits(['close', 'updated', 'update:visible']);
const selectedMember = ref(null); const selectedMember = ref(null);
const memberOptions = computed(() => { const memberOptions = computed(() => {
@@ -0,0 +1,85 @@
<template>
<div class="team-title-row">
<span class="team-name-strong">{{ title }}</span>
<span v-if="teamHandicap" class="team-hc"> 핸디캡: {{ teamHandicap }}</span>
</div>
<div class="member-list">
<div v-for="member in 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 }}
</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 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, teamNumber)"
@blur="onScoreBlur(member, n, teamNumber)"
@keydown.enter="onScoreEnter(member, n, 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>
</div>
</div>
</template>
<script setup>
import { toRefs } from 'vue'
const props = defineProps({
title: String,
members: Array,
teamHandicap: [Number, String],
teamNumber: [Number, String, null],
gameCount: Number,
canInputScore: Boolean,
scoresProxy: Object,
scoreStates: Object,
onScoreInput: Function,
onScoreBlur: Function,
onScoreEnter: Function,
getMemberTotalScore: Function,
getMemberAverageScore: Function,
scoreColorClass: Function
})
</script>
@@ -391,7 +391,6 @@
font-weight: 700; font-weight: 700;
margin-left: 3px; margin-left: 3px;
vertical-align: middle; vertical-align: middle;
color: var(--p-purple-500);
} }
.team-title-row { .team-title-row {
+102 -142
View File
@@ -2,8 +2,8 @@
<div class="event-public-wrapper"> <div class="event-public-wrapper">
<h1 v-if="clubName" class="p-text-primary p-text-bold p-text-lg p-mb-2">{{ clubName }}</h1> <h1 v-if="clubName" class="p-text-primary p-text-bold p-text-lg p-mb-2">{{ clubName }}</h1>
<Divider class="p-mb-4" /> <Divider class="p-mb-4" />
<Card class="p-shadow-8 p-p-5 p-rounded-xl event-public-card"> <div class="p-shadow-8 p-p-5 p-rounded-xl event-public-card">
<template #content> <div>
<div v-if="loading" class="p-text-center p-mb-4">로딩 중...</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="error" class="p-text-center p-mb-4">{{ error }}</div>
<div v-else-if="!isAuthenticated"> <div v-else-if="!isAuthenticated">
@@ -55,150 +55,110 @@
</div> </div>
</div> </div>
<div class="p-text-secondary p-mb-4">{{ event.description || '' }}</div> <div class="p-text-secondary p-mb-4">{{ event.description || '' }}</div>
<Divider />
<!-- 팀별 게임별 점수/등수 : 하단으로 이동 --> <!-- 팀별 게임별 점수/등수 -->
<div v-if="teams.length > 0 || unassignedMembers.length > 0" class="team-score-table-wrapper p-mt-4"> <template v-if="teams.length > 0">
<DataTable :value="getAllTeamsWithUnassigned()" class="team-score-table" :responsiveLayout="'scroll'"> <div class="team-score-table-wrapper p-mt-4">
<Column header="팀"> <DataTable :value="getAllTeamsWithUnassigned()" class="team-score-table" :responsiveLayout="'scroll'">
<template #body="slotProps"> <Column header="팀">
{{ slotProps.data.teamNumber }} <template #body="slotProps">
<span v-if="slotProps.data.handicap" class="team-hc-small"> {{ slotProps.data.teamNumber }}
핸디캡: {{ slotProps.data.handicap ?? 0 }} <span v-if="slotProps.data.handicap" class="team-hc-small">
</span> 핸디캡: {{ slotProps.data.handicap ?? 0 }}
</template> </span>
</Column> </template>
<Column v-for="n in event.gameCount" :key="n" :header="n + 'G'"> </Column>
<template #body="slotProps"> <Column v-for="n in event.gameCount" :key="n" :header="n + 'G'">
<span class="score-main">{{ getTeamTotalScore(slotProps.data, n) }}</span> <template #body="slotProps">
<span class="block rank-badge" <span class="score-main">{{ getTeamTotalScore(slotProps.data, n) }}</span>
v-if="getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + ''] !== undefined" <span class="block rank-badge"
:class="'rank-badge rank-' + getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + '']"> v-if="getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + ''] !== undefined"
({{ getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + ''] }}) :class="'rank-badge rank-' + getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + '']">
</span> ({{ getRanksByGame(n)[(slotProps.data.id || slotProps.data.teamNumber) + ''] }})
</template> </span>
</Column> </template>
<Column header="총점"> </Column>
<template #body="slotProps"> <Column header="총점">
{{Array.from({ length: event.gameCount }, (_, i) => getTeamTotalScore(slotProps.data, i + <template #body="slotProps">
1)).reduce((a, b) => a + b, 0) }} {{Array.from({ length: event.gameCount }, (_, i) => getTeamTotalScore(slotProps.data, i +
<span class="block rank-badge" :class="'rank-' + getTeamOverallRank(slotProps.data)"> 1)).reduce((a, b) => a + b, 0) }}
({{ getTeamOverallRank(slotProps.data) }}) <span class="block rank-badge" :class="'rank-' + getTeamOverallRank(slotProps.data)">
</span> ({{ getTeamOverallRank(slotProps.data) }})
</template> </span>
</Column> </template>
</DataTable> </Column>
</div> </DataTable>
<div v-for="team in getAllTeamsWithUnassigned()" :key="team.id" class="team-card"> </div>
<Card class=" p-rounded-lg p-shadow-1 p-p-3 p-mb-4"> <div v-for="team in getAllTeamsWithUnassigned()" :key="team.id">
<template #title> <MemberListCard
<div class="team-title-row"> :title="parseInt(team.teamNumber) ? '팀 ' + team.teamNumber : '미배정 인원'"
<span class="team-name-strong">{{ parseInt(team.teamNumber) ? '팀 ' + team.teamNumber : '미배정 인원'}}</span> :members="team.members"
<span class="team-hc" v-if="team.handicap"> 핸디캡: {{ team.handicap ?? 0 }}</span> :teamHandicap="team.handicap"
</div> :teamNumber="team.teamNumber"
</template> :gameCount="event.gameCount"
<template #content> :canInputScore="canInputScore"
<div class="member-list"> :scoresProxy="scoresProxy"
<div v-for="member in team.members" :key="member.participantId" class="member-row"> :scoreStates="scoreStates"
<div class="member-info-block"> :onScoreInput="onScoreInput"
<div class="member-header-row"> :onScoreBlur="onScoreBlur"
<span class="member-name">{{ member.name }}</span> :onScoreEnter="onScoreEnter"
<Tag v-if="member.isGuest" value="G" severity="warning" class="ml-2" /> :getMemberTotalScore="getMemberTotalScore"
</div> :getMemberAverageScore="getMemberAverageScore"
<div class="info-under-name"> :scoreColorClass="scoreColorClass"
{{ member.gender || '-' }} />
<span v-if="member.status" :class="{ <!-- 카드 요약(총점/등수/게임별 점수) '팀' 노출 -->
'status-cancel': member.status === '취소', <div v-if="team.teamNumber" class="team-game-scores">
'status-pending': member.status === '참가예정' <div class="team-total-row team-total-row-responsive team-total-row-block">
}"> <div class="team-games-grid"
· {{ member.status }} :style="{ gridTemplateColumns: `repeat(${event.gameCount > 3 ? 3 : event.gameCount}, 1fr)` }">
</span> <template v-for="n in event.gameCount" :key="n">
<span v-if="member.paymentStatus" :class="{ <div class="team-game-cell">
'payment-unpaid': member.paymentStatus === '미납' <b>{{ n }}G:</b> {{ getTeamTotalScore(team, n) }}
}"> <span v-if="getRanksByGame(n)[(team.id || team.teamNumber) + ''] !== undefined"
· {{ member.paymentStatus }} class="rank-badge"
:class="'rank-' + getRanksByGame(n)[(team.id || team.teamNumber) + '']">
({{ getRanksByGame(n)[(team.id || team.teamNumber) + ''] }})
</span> </span>
</div> </div>
<div class="member-meta">핸디캡: {{ member.handicap ?? '-' }} / 에버리지: {{ member.average ?? '-' }} </template>
</div> </div>
<div class="member-meta">{{ member.comment }}</div> <div class="team-total-main">
</div> <span class="team-total-label">총점</span>
<div class="score-inputs-vertical"> <span class="team-total-scores">{{ Array.from({ length: event.gameCount }, (_, i) => getTeamTotalScore(team, i + 1)).reduce((a, b) => a + b, 0) }}</span>
<template v-for="n in event.gameCount" :key="n"> <span v-if="getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' })"
<div class="score-input-hc-wrap"> class="rank-badge"
<div class="score-input-status-wrap"> :class="'rank-' + getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' })">
<InputText {{ getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' }) }}
v-if="canInputScore" </span>
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>
</div> </div>
</div> </div>
<Divider /> </div>
<div class="team-game-scores"> </div>
<div class="team-total-row team-total-row-responsive team-total-row-block"> </template>
<div class="team-games-grid" <template v-else>
:style="{ gridTemplateColumns: `repeat(${event.gameCount > 3 ? 3 : event.gameCount}, 1fr)` }"> <MemberListCard
<template v-for="n in event.gameCount" :key="n"> title="참가자"
<div class="team-game-cell"> :members="sortedParticipants"
<b>{{ n }}G:</b> {{ getTeamTotalScore(team, n) }} :gameCount="event.gameCount"
<span v-if="getRanksByGame(n)[(team.id || team.teamNumber) + ''] !== undefined" :canInputScore="canInputScore"
class="rank-badge" :scoresProxy="scoresProxy"
:class="'rank-' + getRanksByGame(n)[(team.id || team.teamNumber) + '']"> :scoreStates="scoreStates"
({{ getRanksByGame(n)[(team.id || team.teamNumber) + ''] }}) :onScoreInput="onScoreInput"
</span> :onScoreBlur="onScoreBlur"
</div> :onScoreEnter="onScoreEnter"
</template> :getMemberTotalScore="getMemberTotalScore"
</div> :getMemberAverageScore="getMemberAverageScore"
<div class="team-total-main"> :scoreColorClass="scoreColorClass"
<span class="team-total-label">총점</span> />
<span class="team-total-scores">{{Array.from({ length: event.gameCount }, (_, i) => </template>
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>
</div> </div>
</template> </div>
</Card> </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> </div>
</template> </template>
@@ -206,7 +166,8 @@
import { ref, computed, watch, reactive, onMounted } from 'vue'; import { ref, computed, watch, reactive, onMounted } from 'vue';
import { debounce } from 'lodash-es'; // lodash 설치 필요 (이미 있다면 무시) import { debounce } from 'lodash-es'; // lodash 설치 필요 (이미 있다면 무시)
import { useRoute } from 'vue-router'; 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 dayjs from 'dayjs';
import PublicEventService from '@/services/PublicEventService'; import PublicEventService from '@/services/PublicEventService';
@@ -223,7 +184,6 @@ const isAuthenticated = ref(false);
const inputPassword = ref(''); const inputPassword = ref('');
const passwordError = ref(''); const passwordError = ref('');
const showJoinDialog = ref(false); const showJoinDialog = ref(false);
const showGuestDialog = ref(false);
const allMembers = ref([]); // 전체 클럽 멤버 목록 const allMembers = ref([]); // 전체 클럽 멤버 목록
const availableMembers = ref([]); // 참가 신청 가능 명단 const availableMembers = ref([]); // 참가 신청 가능 명단