๊ด€๋ฆฌ ๋ฉ”๋‰ด

Daehyunii's Dev-blog

8์ฃผ์ฐจ ๊ณผ์ œ - Vue ์˜ํ™” ๊ฒ€์ƒ‰ ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ ๋ณธ๋ฌธ

๐Ÿ“„ Dev Course/Assignment

8์ฃผ์ฐจ ๊ณผ์ œ - Vue ์˜ํ™” ๊ฒ€์ƒ‰ ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ

Daehyunii 2022. 12. 13. 23:59

 

 

 

 

 

 

 

 

 

 

 

 

 

App.vue

<template>
  <div class="page-all">
    <div class="pageSubject">
      <h1>
        {{ header }}
      </h1>
    </div>
    <search-bar />
  </div>
</template>

<script>
import SearchBar from "~/components/SearchBar";

export default {
  components: {
    SearchBar,
  },
  data() {
    return {
      header: "๐ŸŽฌ ์˜ํ™” ๊ฒ€์ƒ‰",
    };
  },
};
</script>
<style lang="scss">
.page-all {
  background-color: black;
  height: 100%;
  .pageSubject {
    color: white;
    font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
    font-size: 50px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    margin-bottom: 30px;
    padding-top: 50px;
  }
}
</style>

 

SearchBar.vue

<template>
  <div class="search-bar">
    <input
      class="search-bar-input"
      :value="keyword"
      placeholder="์˜ํ™” ์ œ๋ชฉ์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."
      @input="keyword = $event.target.value"
      @keyup.enter="onSearch"
    />
    <button class="search-bar-btn" @click="onSearch">๐Ÿ”</button>
  </div>
  <div v-if="hasResult" class="movie-lists">
    <div
      v-for="title of titles"
      :key="title.imdbID"
      class="movie-list"
      @click="
        getDetailData(title.imdbID);
        changeModalState();
      "
    >
      <img :src="title.Poster" alt="ํ•ด๋‹น ์˜ํ™”๋Š” ํฌ์Šคํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค." />
      <div class="movie-container">
        <div class="movie-title">
          {{ title.Title }}
        </div>
        <div class="movie-basic-info">
          Year : {{ title.Year }} / Type : {{ title.Type }}
        </div>
      </div>
    </div>

    <button
      v-if="totalResults > 10 && Math.ceil(totalResults / 10) > pageCount"
      class="more-btn"
      @click="pageCountUp"
    >
      โž• ๋” ๋ณด๊ธฐ
    </button>
  </div>
  <div v-else class="basic-background-color"></div>
  <movie-info
    :result="result"
    :modalstate="modalstate"
    @resetmodal="resetmodal"
  />
</template>

<script>
import MovieInfo from "~/components/MovieInfo";

export default {
  components: {
    MovieInfo,
  },
  data() {
    return {
      keyword: "",
      titles: [],
      totalResults: 0,
      pageCount: 1,
      result: {},
      hasResult: false,
      modalstate: false,
    };
  },
  methods: {
    async getData() {
      const temp = this.titles;
      const res = await fetch(
        `API๋Š” ๊ณต๊ฐœํ•˜์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค~`
      ).then((res) => res.json());

      const { Response, Search, totalResults } = res;

      if (Response === "True") {
        this.titles = [...temp, ...Search];
        this.totalResults = totalResults;
        this.hasResult = true;
      } else {
        this.titles = [];
        this.totalResults = 0;
        this.hasResult = false;
        alert("๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.");
      }
    },
    //๋” ๋ณด๊ธฐ ๋ฒ„ํŠผ!
    pageCountUp() {
      this.pageCount = this.pageCount + 1;
      this.getData();
    },
    onSearch() {
      this.titles = [];
      this.totalResults = 0;
      this.pageCount = 1;
      this.getData();
    },
    async getDetailData(id) {
      const res = await fetch(
        `API๋Š” ๊ณต๊ฐœํ•˜์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค~`
      ).then((res) => res.json());
      this.result = res;
    },
    changeModalState() {
      this.modalstate = true;
    },
    resetmodal(value) {
      this.modalstate = value;
    },
  },
};
</script>

<style scoped lang="scss">
.search-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 30px;
  .search-bar-input {
    width: 400px;
    font-size: 30px;
    box-shadow: 2px 2px 2px #dcdcdc;
    margin-right: 15px;
    margin-bottom: 20px;
    border-radius: 20px;
    padding-left: 20px;
    padding-right: 20px;
  }
  .search-bar-btn {
    font-size: 30px;
    margin-bottom: 20px;
  }
  ::placeholder {
    font-size: 23px;
    color: #d2d2d2;
  }
}
.movie-lists {
  display: grid;
  grid-template-rows: 1fr 1fr 1fr;
  grid-template-columns: 1fr 1fr 1fr;
  justify-content: space-around;
  flex-wrap: wrap;
  .movie-list {
    border-radius: 15px;
    background-color: white;
    box-shadow: 4px 4px 4px #dcdcdc;
    font-size: 25px;
    text-align: center;
    padding: 50px;
    margin: 30px;
    width: 400px;
    font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  }
  .movie-title {
    margin-top: 30px;
  }
  .movie-basic-info {
    font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
    font-size: 17px;
    padding-top: 15px;
    color: gray;
  }
  .more-btn {
    box-shadow: 4px 4px 4px #dcdcdc;
    font-size: 30px;
    text-align: center;
    padding: 50px;
    margin: 30px;
    width: 500px;
    font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
    border-radius: 15px;
    background-color: white;
  }
}

.basic-background-color {
  background-color: black;
  color: #969696;
  height: 1200px;
  font-size: 25px;
  margin-left: 60px;
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
</style>

 

MovieInfo.vue

<template>
  <div v-if="$props.modalstate" class="modal" @click="onEmit">
    <div class="modal-content">
      <h1 class="movie-title">์ œ๋ชฉ : {{ result.Title }}</h1>
      <div class="movie-poster">
        <img :src="result.Poster" alt="ํ•ด๋‹น ์˜ํ™”์˜ ํฌ์Šคํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค." />
      </div>
      <div class="detail-movie-info">
        <div>๐Ÿ—“ ๊ฐœ๋ด‰์ผ : {{ result.Released }}</div>
        <div>โณ ์ƒ์˜์‹œ๊ฐ„ : {{ result.Runtime }}</div>
        <div>๐Ÿ“ฝ ์žฅ๋ฅด : {{ result.Genre }}</div>
        <div>๐ŸŽฌ ๊ฐ๋… : {{ result.Director }}</div>
        <div>๐ŸŽฅ ๋ฐฐ์šฐ : {{ result.Actors }}</div>
        <div>๐Ÿ† ์ˆ˜์ƒ ๊ฒฝ๋ ฅ : {{ result.Awards }}</div>
        <div>๐Ÿ“ ์ค„๊ฑฐ๋ฆฌ : {{ result.Plot }}</div>
        <ul class="detail-movie-score">
          โ˜‘๏ธ ํ‰์ 
          <li v-for="score of result.Ratings">
            −{{ score.Source }}, {{ score.Value }}
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    result: Object,
    modalstate: Boolean,
  },
  data() {
    return {
      modalState: false,
    };
  },
  methods: {
    onEmit() {
      this.$emit("resetmodal", false);
    },
  },
};
</script>

<style lang="scss">
.modal {
  z-index: 1;
  width: 100%;
  height: 100%;
  position: fixed;
  left: 0;
  top: 0;
  background-color: rgba(0, 0, 0, 0.3);
  .modal-content {
    border-radius: 15px;
    background-color: white;
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 800px;
    height: 800px;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .movie-title {
    margin: 40px 0px;
    display: flex;
    align-content: center;
    justify-content: center;
    font-size: 30px;
    font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  }

  .movie-poster {
    display: flex;
    align-content: center;
    justify-content: center;
    margin-bottom: 30px;
  }
}
.detail-movie-info {
  padding: 30px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
</style>