[Vue] -할 일 관리 애플리케이션 만들기

Soozoo·2024년 10월 2일

Vue

목록 보기
22/23

프로젝트 개요

이 애플리케이션의 주요 기능은 다음과 같습니다:

  • 할 일 추가: 날짜, 설명, 중요도, 상태를 입력하여 할 일을 추가합니다.
  • 할 일 보기: 입력한 할 일을 테이블 형식으로 보여줍니다.
  • 할 일 수정 및 삭제: 추가한 할 일을 수정하거나 삭제할 수 있는 기능이 포함되어 있습니다.
  • 입력 폼 표시 토글: 입력 폼을 열고 닫을 수 있는 버튼이 있습니다.

사용된 기술

이 프로젝트에서는 다음과 같은 기술과 라이브러리를 사용했습니다:

  • 프론트엔드: Vue.js
  • API 호출: Axios
  • 백엔드 시뮬레이션: JSON Server

코드 설명

템플릿 섹션

<template>
  <div class="page-container">
    <!-- 사이드바 -->
    <div class="sidebar">
      <button class="btn btn-primary" @click="isOpenClose = !isOpenClose">{{ isOpenClose ? '입력 창 닫기' : '데이터 입력' }}</button>
      <button class="btn btn-info" @click="showlist">데이터 가져오기</button>
    </div>

    <!-- 메인 콘텐츠 -->
    <div class="main-content">
      <!-- 입력 폼 -->
      <div v-show="isOpenClose" class="form-container">
        <form autocomplete="off">
          <div class="form-group">
            <label class="form-label">날짜</label>
            <input type="date" class="form-control" v-model="date">
          </div>

          <div class="form-group">
            <label class="form-label">할 일</label>
            <input type="text" class="form-control" v-model="work">
          </div>

          <div class="form-group">
            <label class="form-label">중요도</label>
            <select class="form-control" v-model="force">
              <option value=""></option>
              <option value=""></option>
              <option value=""></option>
            </select>
          </div>

          <div class="form-group">
            <label class="form-label">결과</label>
            <select class="form-control" v-model="result">
              <option value="미완료">미완료</option>
              <option value="진행중">진행중</option>
              <option value="완료">완료</option>
            </select>
          </div>

          <div class="form-actions">
            <button type="button" class="btn btn-success" v-if="!isEditing" @click="input">저장</button>
            <button type="button" class="btn btn-warning" v-if="isEditing" @click="update(currentId)">업데이트</button>
            <button type="button" class="btn btn-secondary" @click="cancelEdit">취소</button>
          </div>
        </form>
      </div>

      <!-- 할 일 목록 테이블 -->
      <div class="table-container">
        <table class="table">
          <thead>
            <tr>
              <th>ID</th>
              <th>날짜</th>
              <th>할 일</th>
              <th>중요도</th>
              <th>처리 결과</th>
              <th>삭제/업데이트</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="daily in dailys" :key="daily.id">
              <td>{{ daily.id }}</td>
              <td>{{ daily.date }}</td>
              <td>{{ daily.work }}</td>
              <td>{{ daily.force }}</td>
              <td>{{ daily.result }}</td>
              <td>
                <button type="button" @click="deleteData(daily.id)" class="btn btn-danger">삭제</button>
                <button type="button" @click="editData(daily)" class="btn btn-info">업데이트</button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</template>

스크립트

<script setup>
import axios from "axios";
import { ref } from "vue";

// 반응형 데이터 선언
const isOpenClose = ref(false); // 폼 열림/닫힘 상태
const date = ref(''); // 입력할 날짜
const work = ref(''); // 입력할 할 일
const force = ref('상'); // 중요도
const result = ref('미완료'); // 결과 상태
const dailys = ref([]); // 할 일 목록
const isEditing = ref(false); // 수정 모드 상태
const currentId = ref(null); // 현재 수정 중인 할 일 ID

// 할 일 추가
const input = async () => {
  try {
    await axios.post("http://localhost:3000/daily", {
      date: date.value,
      work: work.value,
      force: force.value,
      result: result.value
    });
    resetForm(); // 폼 초기화
    showlist(); // 목록 갱신
  } catch (error) {
    console.log('전송 실패');
  }
}

// 할 일 삭제
const deleteData = async (id) => { 
  const isconfirm = confirm("삭제 하시겠습니까?");
  if (!isconfirm) {
    return;
  }
  try {
    await axios.delete(`http://localhost:3000/daily/${id}`);
    showlist(); // 목록 갱신
  } catch (error) {
    console.log('삭제 실패');
  }
}

// 할 일 업데이트
const update = async (id) => {
  try {
    await axios.put(`http://localhost:3000/daily/${id}`, {
      date: date.value,
      work: work.value,
      force: force.value,
      result: result.value,
    });
    resetForm(); // 폼 초기화
    showlist(); // 목록 갱신
  } catch (error) {
    console.log('업데이트 실패');
  }
}

// 할 일 수정 폼에 데이터 로드
const editData = (daily) => {
  date.value = daily.date;
  work.value = daily.work;
  force.value = daily.force;
  result.value = daily.result;
  currentId.value = daily.id;
  isEditing.value = true; 
  isOpenClose.value = true; 
}

// 폼 초기화
const resetForm = () => {
  date.value = '';
  work.value = '';
  force.value = '상';
  result.value = '미완료';
  isOpenClose.value = false;
  isEditing.value = false; 
  currentId.value = null; 
}

// 수정 취소
const cancelEdit = () => {
  resetForm(); 
}

// 할 일 목록 가져오기
const showlist = async () => {
  const res = await axios.get("http://localhost:3000/daily");
  dailys.value = res.data; // 목록 업데이트
  console.log(dailys.value);
  isOpenClose.value = false; // 폼 닫기
}
</script>

CSS

.page-container {
  display: flex;
  min-height: 100vh;
  background-color: #f0f2f5;
}

.sidebar {
  width: 250px;
  background-color: #2c3e50;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  align-items: center;
  justify-content: center;
}

.main-content {
  flex-grow: 1;
  padding: 40px;
  background-color: #ecf0f1;
}

/* Form styles */
.form-container {
  background-color: white;
  padding: 30px;
  border-radius: 12px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
  margin-bottom: 40px;
}

.form-group {
  margin-bottom: 20px;
}

.form-label {
  font-size: 1rem;
  margin-bottom: 8px;
  display: block;
  color: #34495e;
}

.form-control {
  width: 100%;
  padding: 12px;
  border-radius: 8px;
  border: 1px solid #bdc3c7;
  font-size: 1rem;
}

.form-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

/* Button styles */
button {
  padding: 12px 18px;
  font-size: 1rem;
  border-radius: 8px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

button.btn-primary {
  background-color: #2980b9;
  color: white;
}

button.btn-info {
  background-color: #27ae60;
  color: white;
}

button.btn-danger {
  background-color: #c0392b;
  color: white;
}

button.btn-secondary {
  background-color: #7f8c8d;
  color: white;
}

button.btn-success {
  background-color: #2ecc71;
  color: white;
}

button.btn-warning {
  background-color: #f39c12;
  color: white;
}

/* Table styles */
.table-container {
  background-color: white;
  border-radius: 12px;
  padding: 20px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}

.table {
  width: 100%;
  border-collapse: collapse;
  background-color: #fff;
}

.table th, .table td {
  text-align: left;
  padding: 12px;
  border-bottom: 1px solid #ddd;
}

.table th {
  background-color: #34495e;
  color: white;
}

.table td button {
  margin-right: 10px;
}

.table tr:hover {
  background-color: #f9f9f9;
}

.table-container {
  margin-top: 30px;
}


profile
넙-죽

0개의 댓글