웹 개발/[KLOZ] 웹 프로젝트

[LottoInsight] check(당첨 결과 확인)

cha430 2025. 7. 23. 13:36
<template>
    <div class="container">
        <div class="main-wrapper" :class="{ 'sidebar-active': isSidebarOpen }">
            <Common
                v-model:active-menu-item="activeMenuItem"
                v-model:isSidebarOpen="isSidebarOpen"
                @menu-selected="selectMenuItem"
            />
            <div class="content-area">
                <h1 class="page-title">내 번호 당첨 확인</h1>

                <div class="lotto-check-section">
                    <div class="section-description">
                        <span class="bullet-point"></span> 번호를 입력하시면 최근 1년 이내 당첨된 모든 결과를 알려드립니다.
                    </div>

                    <div class="lotto-input-container">
                        <div class="lotto-number-inputs">
                            <input
                                type="text"
                                maxlength="2"
                                class="lotto-number-box"
                                v-for="n in 6"
                                :key="n"
                                v-model="myNumbers[n - 1]"
                                @input="handleNumberInput(n - 1, $event)"
                                @keydown="handleKeyDown(n - 1, $event)"
                                :ref="(el) => el && (numberInputs[n - 1] = el as HTMLInputElement)"
                            />
                        </div>
                        <button class="check-result-btn" @click="checkLottoNumbers" :disabled="!allNumbersEntered">결과확인</button>
                    </div>

                    <div v-if="lottoResults.length > 0" class="lotto-results-table-container">
                        <h3>당첨 결과</h3>
                        <table class="lotto-results-table">
                            <thead>
                            <tr>
                                <th>회차</th>
                                <th>당첨일</th>
                                <th>당첨번호</th>
                                <th>보너스</th>
                                <th>내 번호와 일치</th>
                            </tr>
                            </thead>
                            <tbody>
                            <tr v-for="result in lottoResults" :key="result.round">
                                <td>{{ result.round }}</td>
                                <td>{{ result.date }}</td>
                                <td>
                                        <span
                                            v-for="(num, index) in result.lottoNumbers"
                                            :key="index"
                                            :class="{ 'matched-number': result.matchedNumbers.includes(num) }"
                                        >
                                            {{ num }}
                                        </span>
                                </td>
                                <td>{{ result.bonus }}</td>
                                <td>{{ result.matchCount }}개</td>
                            </tr>
                            </tbody>
                        </table>
                    </div>
                    <div v-else-if="searchAttempted && lottoResults.length === 0" class="no-results-message">
                        입력하신 번호와 일치하는 당첨 기록이 없습니다.
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import Common from "@/components/common/common.vue";
import { ref, onMounted, watch, computed } from "vue";
import axios from "axios";

const activeMenuItem = ref('');
const isSidebarOpen = ref(true);
const myNumbers = ref<string[]>(['', '', '', '', '', '']); // 사용자 입력 번호
const lottoResults = ref<any[]>([]); // 당첨 결과 저장
const searchAttempted = ref(false); // 검색 시도 여부
const numberInputs = ref<HTMLInputElement[]>([]); // input 요소에 접근하기 위한 ref

// 모든 번호가 입력되었는지 확인하는 computed 속성
const allNumbersEntered = computed(() => {
    return myNumbers.value.every(num => num.length > 0);
});

// 메뉴 아이템 클릭 시 호출될 함수
const selectMenuItem = (menuText: string) => {
    activeMenuItem.value = menuText;
};

// 번호 입력 시 다음 칸으로 자동 이동
const handleNumberInput = (index: number, event: Event) => {
    const inputElement = event.target as HTMLInputElement;
    let value = inputElement.value.replace(/[^0-9]/g, ''); // 숫자만 입력 받기
    if (value.length > 2) {
        value = value.slice(0, 2);
    }
    myNumbers.value[index] = value;

    if (value.length === 2 && index < 5) {
        const nextInput = numberInputs.value[index + 1];
        if (nextInput) {
            nextInput.focus();
        }
    }
    if (index === 5 && value.length === 2) {
        inputElement.blur(); // 마지막 칸 입력 후 포커스 해제
    }
};

// Backspace 키 입력 시 이전 칸으로 이동 및 내용 삭제
const handleKeyDown = (index: number, event: KeyboardEvent) => {
    if (event.key === 'Backspace' && myNumbers.value[index] === '' && index > 0) {
        const prevInput = numberInputs.value[index - 1];
        if (prevInput) {
            prevInput.focus();
        }
    }
};

// 로또 번호 확인 함수
const checkLottoNumbers = () => {
    searchAttempted.value = true; // 검색시도 여부
    lottoResults.value = []; // 이전 결과 초기화

    // 입력된 번호를 숫자로 변환하고 유효성 검사 (1부터 45까지, 중복 없음)
    const numbers = myNumbers.value.map(Number);
    const uniqueNumbers = new Set(numbers);

    if (numbers.some(isNaN) || numbers.some(num => num < 1 || num > 45) || uniqueNumbers.size !== 6) {
        alert("로또 번호는 1부터 45까지의 숫자 6개를 중복 없이 입력해야 합니다.");
        return;
    }

    const requestData = {
        num1: numbers[0],
        num2: numbers[1],
        num3: numbers[2],
        num4: numbers[3],
        num5: numbers[4],
        num6: numbers[5]
    };

    /* v-model 에서 myNumbers[n - 1] 입력값 myNumbers 배열로 들어감. numbers는 my~의 값 */
    axios.post('/check/check.do', requestData)
        .then(response => {
            const res = response.data;

            if (res && res.result === 1 && Array.isArray(res.data)) {
                lottoResults.value = res.data.map((item: any) => {
                    const lottoNumbers = [item.num1, item.num2, item.num3, item.num4, item.num5, item.num6];
                    const myNumbersSet = new Set(numbers);
                    const matchedNumbers = lottoNumbers.filter(num => myNumbersSet.has(num));

                    return {
                        round: item.round,
                        date: item.date,
                        lottoNumbers: lottoNumbers.sort((a, b) => a - b),
                        bonus: item.bonus,
                        matchedNumbers,
                        matchCount: matchedNumbers.length
                    };
                });
            } else if (res && res.result === 0) {
                alert("입력하신 번호가 유효하지 않습니다. 다시 확인해주세요.");
            } else {
                alert("번호 확인 중 알 수 없는 오류가 발생했습니다.");
            }
        })
        .catch(error => {
            console.error("서버 통신 오류:", error);
            alert("서버와의 통신 중 오류가 발생했습니다.");
        });
};

watch(activeMenuItem, (newValue) => {
    localStorage.setItem('activeMenuItem', newValue);
});

onMounted(() => {
    const savedActiveMenuItem = localStorage.getItem('activeMenuItem');
    if (savedActiveMenuItem) {
        activeMenuItem.value = savedActiveMenuItem;
    } else {
        activeMenuItem.value = '내 번호 당첨확인';
    }
});
</script>

<style scoped>
.content-area {
    flex-grow: 1;
    padding: 20px;
    max-width: 900px;
    margin: 0 auto;
}

.page-title {
    font-size: 24px;
    color: #555;
    margin: 30px 0;
}

.lotto-check-section {
    background-color: #fff;
    border: 1px solid #ddd;
    border-radius: 5px;
    padding: 20px 30px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.section-description {
    font-size: 15px;
    color: #333;
    margin-bottom: 25px;
    display: flex;
    align-items: center;
}

.bullet-point {
    display: inline-block;
    width: 6px;
    height: 6px;
    background-color: #333;
    border-radius: 50%;
    margin-right: 8px;
}

.lotto-input-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 20px;
    padding: 20px 0;
}

.lotto-number-inputs {
    display: flex;
    gap: 10px;
    margin-bottom: 20px;
}

.lotto-number-box {
    width: 50px;
    height: 60px;
    font-size: 32px;
    text-align: center;
    border: 1px solid #ccc;
    border-radius: 5px;
    outline: none;
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
    -moz-appearance: textfield;
}

.lotto-number-box::-webkit-outer-spin-button,
.lotto-number-box::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.lotto-number-box:focus {
    border-color: #003e80;
    box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}

.check-result-btn {
    background-color: #0d4785;
    color: white;
    padding: 12px 20px;
    border: none;
    font-size: 18px;
    cursor: pointer;
    transition: background-color 0.3s ease;
    min-width: 150px;
}

.check-result-btn:hover {
    background-color: #002247;
}

.lotto-results-table-container {
    margin-top: 40px;
    width: 100%;
}

.lotto-results-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 20px;
}

.lotto-results-table th,
.lotto-results-table td {
    border: 1px solid #eee;
    padding: 12px 15px;
    text-align: center;
}

.lotto-results-table th {
    background-color: #f8f8f8;
    font-weight: bold;
    color: #555;
}

.lotto-results-table tbody tr:nth-child(even) {
    background-color: #f9f9f9;
}

.lotto-results-table tbody tr:hover {
    background-color: #f0f0f0;
}

.matched-number {
    font-weight: bold;
    color: #d9534f; /* 빨간색 계열로 강조 */
    margin: 0 2px;
}

/* 당첨번호 각각에 패딩을 줘서 간격 주기 */
.lotto-results-table td:nth-child(3) span {
    display: inline-block;
    padding: 0 3px;
}

.no-results-message {
    text-align: center;
    margin-top: 30px;
    font-size: 16px;
    color: #777;
    padding: 15px;
    border: 1px dashed #ddd;
    border-radius: 5px;
    background-color: #fcfcfc;
}
</style>