public 정리
This commit is contained in:
@@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -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 {
|
||||||
|
|||||||
@@ -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,9 +55,10 @@
|
|||||||
</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">
|
||||||
|
<div class="team-score-table-wrapper p-mt-4">
|
||||||
<DataTable :value="getAllTeamsWithUnassigned()" class="team-score-table" :responsiveLayout="'scroll'">
|
<DataTable :value="getAllTeamsWithUnassigned()" class="team-score-table" :responsiveLayout="'scroll'">
|
||||||
<Column header="팀">
|
<Column header="팀">
|
||||||
<template #body="slotProps">
|
<template #body="slotProps">
|
||||||
@@ -88,78 +89,25 @@
|
|||||||
</Column>
|
</Column>
|
||||||
</DataTable>
|
</DataTable>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="team in getAllTeamsWithUnassigned()" :key="team.id" class="team-card">
|
<div v-for="team in getAllTeamsWithUnassigned()" :key="team.id">
|
||||||
<Card class=" p-rounded-lg p-shadow-1 p-p-3 p-mb-4">
|
<MemberListCard
|
||||||
<template #title>
|
:title="parseInt(team.teamNumber) ? '팀 ' + team.teamNumber : '미배정 인원'"
|
||||||
<div class="team-title-row">
|
:members="team.members"
|
||||||
<span class="team-name-strong">{{ parseInt(team.teamNumber) ? '팀 ' + team.teamNumber : '미배정 인원'}}</span>
|
:teamHandicap="team.handicap"
|
||||||
<span class="team-hc" v-if="team.handicap">팀 핸디캡: {{ team.handicap ?? 0 }}</span>
|
:teamNumber="team.teamNumber"
|
||||||
</div>
|
:gameCount="event.gameCount"
|
||||||
</template>
|
:canInputScore="canInputScore"
|
||||||
<template #content>
|
:scoresProxy="scoresProxy"
|
||||||
<div class="member-list">
|
:scoreStates="scoreStates"
|
||||||
<div v-for="member in team.members" :key="member.participantId" class="member-row">
|
:onScoreInput="onScoreInput"
|
||||||
<div class="member-info-block">
|
:onScoreBlur="onScoreBlur"
|
||||||
<div class="member-header-row">
|
:onScoreEnter="onScoreEnter"
|
||||||
<span class="member-name">{{ member.name }}</span>
|
:getMemberTotalScore="getMemberTotalScore"
|
||||||
<Tag v-if="member.isGuest" value="G" severity="warning" class="ml-2" />
|
:getMemberAverageScore="getMemberAverageScore"
|
||||||
</div>
|
:scoreColorClass="scoreColorClass"
|
||||||
<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 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)"
|
<div v-if="team.teamNumber" class="team-game-scores">
|
||||||
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>
|
|
||||||
<Divider />
|
|
||||||
<div class="team-game-scores">
|
|
||||||
<div class="team-total-row team-total-row-responsive team-total-row-block">
|
<div class="team-total-row team-total-row-responsive team-total-row-block">
|
||||||
<div class="team-games-grid"
|
<div class="team-games-grid"
|
||||||
:style="{ gridTemplateColumns: `repeat(${event.gameCount > 3 ? 3 : event.gameCount}, 1fr)` }">
|
:style="{ gridTemplateColumns: `repeat(${event.gameCount > 3 ? 3 : event.gameCount}, 1fr)` }">
|
||||||
@@ -176,8 +124,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="team-total-main">
|
<div class="team-total-main">
|
||||||
<span class="team-total-label">총점</span>
|
<span class="team-total-label">총점</span>
|
||||||
<span class="team-total-scores">{{Array.from({ length: event.gameCount }, (_, i) =>
|
<span class="team-total-scores">{{ Array.from({ length: event.gameCount }, (_, i) => getTeamTotalScore(team, i + 1)).reduce((a, b) => a + b, 0) }}</span>
|
||||||
getTeamTotalScore(team, i + 1)).reduce((a, b) => a + b, 0)}}</span>
|
|
||||||
<span v-if="getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' })"
|
<span v-if="getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' })"
|
||||||
class="rank-badge"
|
class="rank-badge"
|
||||||
:class="'rank-' + getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' })">
|
:class="'rank-' + getTeamOverallRank({ ...team, id: (team.id || team.teamNumber) + '' })">
|
||||||
@@ -186,19 +133,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Card>
|
<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>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<JoinDialog v-if="showJoinDialog && canJoin" v-model:visible="showJoinDialog" :publicHash="publicHash"
|
<JoinDialog v-if="showJoinDialog && canJoin" v-model:visible="showJoinDialog" :publicHash="publicHash"
|
||||||
:password="needPassword ? inputPassword : ''" :participants="participants"
|
:participants="participants"
|
||||||
:availableMembers="availableMembers" :eventStatus="event.status"
|
:availableMembers="availableMembers" :eventStatus="event.status"
|
||||||
@close="showJoinDialog = false" @updated="reload" />
|
@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>
|
|
||||||
</Card>
|
|
||||||
</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([]); // 참가 신청 가능 명단
|
||||||
|
|||||||
Reference in New Issue
Block a user