오류 수정

This commit is contained in:
2025-06-09 16:12:59 +09:00
parent 05312c7d93
commit 2244a525b3
8 changed files with 98 additions and 156 deletions
@@ -33,13 +33,13 @@
<div class="col-12 md:col-6">
<label for="startDate">시작일 *</label>
<Datepicker id="startDate" v-model="eventModel.startDate" :showTime="true" dateFormat="yy-mm-dd" timeFormat="24" hourFormat="24" :showIcon="true" :stepMinute="5" :class="{'p-invalid': submitted && !eventModel.startDate, 'w-full': true}" />
<DatePicker id="startDate" v-model="eventModel.startDate" :showTime="true" dateFormat="yy-mm-dd" timeFormat="24" hourFormat="24" :showIcon="true" :stepMinute="5" :class="{'p-invalid': submitted && !eventModel.startDate, 'w-full': true}" />
<small class="p-error" v-if="submitted && !eventModel.startDate">시작일은 필수입니다.</small>
</div>
<div class="col-12 md:col-6">
<label for="endDate">종료일</label>
<Datepicker id="endDate" v-model="eventModel.endDate" :showTime="true" dateFormat="yy-mm-dd" timeFormat="24" hourFormat="24" :showIcon="true" :stepMinute="5" :class="{'p-invalid': submitted && !eventModel.endDate, 'w-full': true}" />
<DatePicker id="endDate" v-model="eventModel.endDate" :showTime="true" dateFormat="yy-mm-dd" timeFormat="24" hourFormat="24" :showIcon="true" :stepMinute="5" :class="{'p-invalid': submitted && !eventModel.endDate, 'w-full': true}" />
<small class="p-error" v-if="submitted && !eventModel.endDate">종료일은 필수입니다.</small>
</div>
@@ -65,7 +65,7 @@
<div class="col-12 md:col-6">
<label for="registrationDeadline">신청마감</label>
<Datepicker id="registrationDeadline" v-model="eventModel.registrationDeadline" :showTime="true" dateFormat="yy-mm-dd" timeFormat="24" hourFormat="24" :showIcon="true" :stepMinute="5" :class="{'p-invalid': submitted && !eventModel.registrationDeadline, 'w-full': true}" />
<DatePicker id="registrationDeadline" v-model="eventModel.registrationDeadline" :showTime="true" dateFormat="yy-mm-dd" timeFormat="24" hourFormat="24" :showIcon="true" :stepMinute="5" :class="{'p-invalid': submitted && !eventModel.registrationDeadline, 'w-full': true}" />
</div>
<div class="col-12 md:col-6">
@@ -274,7 +274,7 @@
<div class="field mb-4">
<label for="startDate" class="field-label">시작일 <span class="text-red-500">*</span></label>
<Datepicker
<DatePicker
id="startDate"
v-model="eventData.startDate"
dateFormat="yy-mm-dd"
@@ -287,7 +287,7 @@
<div class="field mb-4">
<label for="endDate" class="field-label">종료일</label>
<Datepicker
<DatePicker
id="endDate"
v-model="eventData.endDate"
dateFormat="yy-mm-dd"
+2 -2
View File
@@ -46,7 +46,7 @@ import Badge from 'primevue/badge'
import InputNumber from 'primevue/inputnumber'
import Divider from 'primevue/divider'
import ProgressSpinner from 'primevue/progressspinner'
import Datepicker from 'primevue/datepicker'
import DatePicker from 'primevue/datepicker'
import Tag from 'primevue/tag'
import FloatLabel from 'primevue/floatlabel';
import Tooltip from 'primevue/tooltip';
@@ -109,7 +109,7 @@ app.component('Badge', Badge)
app.component('InputNumber', InputNumber)
app.component('Divider', Divider)
app.component('ProgressSpinner', ProgressSpinner)
app.component('Datepicker', Datepicker)
app.component('DatePicker', DatePicker)
app.component('Tag', Tag)
app.component('FloatLabel', FloatLabel);
app.component('FileUpload', FileUpload);
+77 -10
View File
@@ -1,4 +1,17 @@
// enumMappings.js: 영어 enum 값을 한글 라벨로 변환하는 매핑 및 함수 모음
export const ROLE_OPTIONS = [
{ name: '관리자', value: 'admin' },
{ name: '클럽 관리자', value: 'clubadmin' },
{ name: '일반 사용자', value: 'user' }
];
export const ROLE_LABELS = ROLE_OPTIONS.reduce(
(acc, cur) => ({ ...acc, [cur.value]: cur.name }),
{}
);
export function getRoleName(role) {
return ROLE_LABELS[role] || role;
}
export const GENDER_OPTIONS = [
{ name: '남성', value: 'male' },
@@ -93,33 +106,60 @@ export const AVERAGE_CALCULATION_PERIOD_LABELS = AVERAGE_CALCULATION_PERIOD_OPTI
{}
);
export const CLUB_STATUS_OPTIONS = [
{ name: '활성', value: 'active' },
{ name: '비활성', value: 'inactive' }
];
export const CLUB_STATUS_LABELS = CLUB_STATUS_OPTIONS.reduce(
(acc, cur) => ({ ...acc, [cur.value]: cur.name }),
{}
);
export const SUBSCRIPTION_STATUS_OPTIONS = [
{ name: '활성', value: 'active' },
{ name: '만료', value: 'expired' },
{ name: '일시중지', value: 'paused' }
];
export const SUBSCRIPTION_STATUS_LABELS = SUBSCRIPTION_STATUS_OPTIONS.reduce(
(acc, cur) => ({ ...acc, [cur.value]: cur.name }),
{}
);
export function getGenderLabel(code) {
return GENDER_LABELS[code] || '-';
return GENDER_LABELS[code] || code;
}
export function getMemberTypeLabel(code) {
return MEMBER_TYPE_LABELS[code] || '-';
return MEMBER_TYPE_LABELS[code] || code;
}
export function getStatusLabel(code) {
return STATUS_LABELS[code] || '-';
return STATUS_LABELS[code] || code;
}
export function getParticipantStatusLabel(code) {
return PARTICIPANT_STATUS_LABELS[code] || '-';
return PARTICIPANT_STATUS_LABELS[code] || code;
}
export function getEventStatusLabel(code) {
return EVENT_STATUS_LABELS[code] || '-';
return EVENT_STATUS_LABELS[code] || code;
}
export function getEventTypeLabel(code) {
return EVENT_TYPE_LABELS[code] || '-';
return EVENT_TYPE_LABELS[code] || code;
}
export function getPaymentStatusLabel(code) {
return PAYMENT_STATUS_LABELS[code] || '-';
return PAYMENT_STATUS_LABELS[code] || code;
}
export function getAverageCalculationPeriodLabel(code) {
return AVERAGE_CALCULATION_PERIOD_LABELS[code] || '-';
return AVERAGE_CALCULATION_PERIOD_LABELS[code] || code;
}
export function getClubStatusLabel(code) {
return CLUB_STATUS_LABELS[code] || code;
}
export function getSubscriptionStatusLabel(code) {
return SUBSCRIPTION_STATUS_LABELS[code] || code;
}
export function getMemberTypeSeverity(type) {
@@ -174,4 +214,31 @@ export function getParticipantStatusSeverity(status) {
case 'canceled': return 'danger';
default: return 'secondary';
}
}
}
export function getRoleSeverity(role) {
switch (role) {
case 'admin': return 'danger';
case 'clubadmin': return 'warning';
case 'user': return 'info';
default: return 'info';
}
}
export function getClubStatusSeverity(status) {
switch (status) {
case 'active': return 'success';
case 'inactive': return 'danger';
default: return 'secondary';
}
}
export function getSubscriptionStatusSeverity(status) {
switch (status) {
case 'active': return 'success';
case 'expired': return 'danger';
case 'paused': return 'warning';
default: return 'secondary';
}
}
@@ -77,7 +77,7 @@
</Column>
<Column field="status" header="상태">
<template #body="subProps">
<Badge :value="getStatusName(subProps.data.status)" :severity="getStatusSeverity(subProps.data.status)" />
<Badge :value="getSubscriptionStatusLabel(subProps.data.status)" :severity="getSubscriptionStatusSeverity(subProps.data.status)" />
</template>
</Column>
<Column header="작업">
@@ -126,7 +126,7 @@
<div class="field">
<label for="status">상태</label>
<Dropdown id="status" v-model="plan.status" :options="statusOptions" optionLabel="name" optionValue="value" />
<Select id="status" v-model="plan.status" :options="SUBSCRIPTION_STATUS_OPTIONS" optionLabel="name" optionValue="value" />
</div>
<template #footer>
@@ -139,28 +139,28 @@
<Dialog v-model:visible="subscriptionDialog" :header="subscriptionDialogTitle" :style="{ width: '500px' }" :modal="true" class="p-fluid">
<div class="field">
<label for="club">클럽</label>
<Dropdown id="club" v-model="subscription.clubId" :options="availableClubs" optionLabel="name" optionValue="id"
<Select id="club" v-model="subscription.clubId" :options="availableClubs" optionLabel="name" optionValue="id"
:class="{'p-invalid': subscriptionSubmitted && !subscription.clubId}" placeholder="클럽 선택" />
<small class="p-error" v-if="subscriptionSubmitted && !subscription.clubId">클럽을 선택해주세요.</small>
</div>
<div class="field">
<label for="startDate">시작일</label>
<Calendar id="startDate" v-model="subscription.startDate" :showIcon="true" dateFormat="yy-mm-dd"
<DatePicker id="startDate" v-model="subscription.startDate" :showIcon="true" dateFormat="yy-mm-dd"
:class="{'p-invalid': subscriptionSubmitted && !subscription.startDate}" />
<small class="p-error" v-if="subscriptionSubmitted && !subscription.startDate">시작일을 선택해주세요.</small>
</div>
<div class="field">
<label for="expiryDate">만료일</label>
<Calendar id="expiryDate" v-model="subscription.expiryDate" :showIcon="true" dateFormat="yy-mm-dd"
<DatePicker id="expiryDate" v-model="subscription.expiryDate" :showIcon="true" dateFormat="yy-mm-dd"
:class="{'p-invalid': subscriptionSubmitted && !subscription.expiryDate}" :minDate="subscription.startDate" />
<small class="p-error" v-if="subscriptionSubmitted && !subscription.expiryDate">만료일을 선택해주세요.</small>
</div>
<div class="field">
<label for="status">상태</label>
<Dropdown id="status" v-model="subscription.status" :options="subscriptionStatusOptions" optionLabel="name" optionValue="value" />
<Select id="status" v-model="subscription.status" :options="SUBSCRIPTION_STATUS_OPTIONS" optionLabel="name" optionValue="value" />
</div>
<template #footer>
@@ -198,26 +198,12 @@
</template>
<script setup>
import { getStatusLabel } from '@/utils/enumMappings';
import { getStatusLabel, getStatusSeverity, getSubscriptionStatusLabel, getSubscriptionStatusSeverity, SUBSCRIPTION_STATUS_OPTIONS, SUBSCRIPTION_STATUS_LABELS } from '@/utils/enumMappings';
import { ref, onMounted, computed } from 'vue';
import { useToast } from 'primevue/usetoast';
import axios from 'axios';
import adminService from '@/services/adminService';
// PrimeVue
import MultiSelect from 'primevue/multiselect';
import InputNumber from 'primevue/inputnumber';
import Textarea from 'primevue/textarea';
import InputText from 'primevue/inputtext';
import Button from 'primevue/button';
import Dialog from 'primevue/dialog';
import DataTable from 'primevue/datatable';
import Column from 'primevue/column';
import Dropdown from 'primevue/dropdown';
import Badge from 'primevue/badge';
import Toast from 'primevue/toast';
import Calendar from 'primevue/calendar';
const toast = useToast();
const API_URL = 'http://localhost:3030/api';
@@ -245,18 +231,6 @@ const subscriptionSubmitted = ref(false);
const availableClubs = ref([]);
const selectedPlan = ref(null);
//
const statusOptions = [
{ name: '활성', value: 'active' },
{ name: '비활성', value: 'inactive' }
];
const subscriptionStatusOptions = [
{ name: '활성', value: 'active' },
{ name: '만료', value: 'expired' },
{ name: '일시중지', value: 'paused' }
];
//
const dialogTitle = computed(() => {
return isNewPlan.value ? '구독 플랜 추가' : '구독 플랜 수정';
@@ -498,30 +472,6 @@ const deleteSubscription = async () => {
}
};
//
// getStatusName ()
switch (status) {
case 'active':
return '활성';
case 'inactive':
return '비활성';
default:
return status;
}
};
//
const getStatusSeverity = (status) => {
switch (status) {
case 'active':
return 'success';
case 'inactive':
return 'danger';
default:
return 'info';
}
};
//
const formatCurrency = (amount) => {
return new Intl.NumberFormat('ko-KR', {
+5 -80
View File
@@ -36,7 +36,7 @@
<Badge :value="getRoleName(slotProps.data.role)" :severity="getRoleSeverity(slotProps.data.role)" />
</template>
<template #filter="{ filterModel, filterCallback }">
<Select v-model="filterModel.value" @change="filterCallback()" :options="roleOptions" optionLabel="name" optionValue="value" placeholder="역할 선택" class="p-column-filter" />
<Select v-model="filterModel.value" @change="filterCallback()" :options="ROLE_OPTIONS" optionLabel="name" optionValue="value" placeholder="역할 선택" class="p-column-filter" />
</template>
</Column>
<Column field="status" header="상태" sortable style="width: 10%">
@@ -44,7 +44,7 @@
<Badge :value="getStatusLabel(slotProps.data.status)" :severity="getStatusSeverity(slotProps.data.status)" />
</template>
<template #filter="{ filterModel, filterCallback }">
<Select v-model="filterModel.value" @change="filterCallback()" :options="statusOptions" optionLabel="name" optionValue="value" placeholder="상태 선택" class="p-column-filter" />
<Select v-model="filterModel.value" @change="filterCallback()" :options="STATUS_OPTIONS" optionLabel="name" optionValue="value" placeholder="상태 선택" class="p-column-filter" />
</template>
</Column>
<Column field="createdAt" header="가입일" sortable style="width: 12%">
@@ -90,13 +90,13 @@
<div class="field mb-3">
<label for="role">역할</label>
<Select id="role" v-model="user.role" :options="roleOptions" optionLabel="name" optionValue="value" placeholder="역할 선택" required class="w-full" />
<Select id="role" v-model="user.role" :options="ROLE_OPTIONS" optionLabel="name" optionValue="value" placeholder="역할 선택" required class="w-full" />
<small class="p-error" v-if="submitted && !user.role">역할은 필수입니다.</small>
</div>
<div class="field mb-3">
<label for="status">상태</label>
<Select id="status" v-model="user.status" :options="statusOptions" optionLabel="name" optionValue="value" placeholder="상태 선택" required class="w-full" />
<Select id="status" v-model="user.status" :options="STATUS_OPTIONS" optionLabel="name" optionValue="value" placeholder="상태 선택" required class="w-full" />
<small class="p-error" v-if="submitted && !user.status">상태는 필수입니다.</small>
</div>
@@ -123,7 +123,7 @@
</template>
<script setup>
import { getStatusLabel } from '@/utils/enumMappings';
import { getStatusLabel, getRoleName, getRoleSeverity, getStatusSeverity, STATUS_OPTIONS, ROLE_OPTIONS } from '@/utils/enumMappings';
import { ref, onMounted, reactive, computed } from 'vue';
import { useToast } from 'primevue/usetoast';
import adminService from '@/services/adminService';
@@ -147,25 +147,6 @@ const filters = ref({
status: { value: null, matchMode: 'equals' }
});
//
const roleOptions = [
{ name: '관리자', value: 'admin' },
{ name: '클럽 관리자', value: 'clubadmin' },
{ name: '일반 사용자', value: 'user' }
];
//
const statusOptions = [
{ name: getStatusLabel('active'), value: 'active' },
{ name: getStatusLabel('inactive'), value: 'inactive' },
{ name: getStatusLabel('pending'), value: 'pending' },
{ name: getStatusLabel('suspended'), value: 'suspended' }
];
{ name: '활성', value: 'active' },
{ name: '비활성', value: 'inactive' },
{ name: '정지', value: 'suspended' }
];
//
const dialogTitle = computed(() => {
return isNewUser.value ? '사용자 추가' : '사용자 수정';
@@ -273,62 +254,6 @@ const deleteUser = async () => {
}
};
//
const getRoleName = (role) => {
switch (role) {
case 'admin':
return '관리자';
case 'clubadmin':
return '클럽 관리자';
case 'user':
return '일반 사용자';
default:
return role;
}
};
//
const getRoleSeverity = (role) => {
switch (role) {
case 'admin':
return 'danger';
case 'clubadmin':
return 'warning';
case 'user':
return 'info';
default:
return 'info';
}
};
//
// getStatusName ()
switch (status) {
case 'active':
return '활성';
case 'inactive':
return '비활성';
case 'suspended':
return '정지';
default:
return status;
}
};
//
const getStatusSeverity = (status) => {
switch (status) {
case 'active':
return 'success';
case 'inactive':
return 'secondary';
case 'suspended':
return 'danger';
default:
return 'info';
}
};
//
const formatDate = (date) => {
return date ? dayjs(date).format('YYYY-MM-DD') : '-';
+1 -1
View File
@@ -45,7 +45,7 @@
{{ formatDate(slotProps.data.startDate) }}
</template>
<template #filter="{ filterModel, filterCallback }">
<Datepicker v-model="filterModel.value" @date-select="filterCallback()" dateFormat="yy-mm-dd" placeholder="날짜 선택" />
<DatePicker v-model="filterModel.value" @date-select="filterCallback()" dateFormat="yy-mm-dd" placeholder="날짜 선택" />
</template>
</Column>
<Column field="location" header="장소" sortable style="width: 15%"></Column>