본문 바로가기

ETC

11. 책 상세보기

미리미리 정리해뒀어야 했는데... 도서 사이트를 완성하고 이제서야 작성한다

 

 

책 상세보기에는 아래 사진과 같이

 1. 책 정보

 2. 책 소개, 목차, 출간자 서평

 3. 해당 책 댓글

 4. 우측 네비게이션

 5. 하단 책 추천

으로 나눌 수 있다.

 

(책 상세보기의 Vue - script코드)

<script>
import CommentComponent from "@/views/book/bookComponents/CommentComponent";
import Recommend from "@/views/book/bookComponents/Recommend"
export default {
  name: "BookDetailComponent",
  components: {Recommend, CommentComponent},
  data: function (){
    return{
     ...
    }
  },
  
  watch:{
    $route(){
      this.bid = this.$route.query.bid;
      this.getBookDetail()
      this.$vuetify.goTo(0)
    }
  },
  
  computed:{
    // 컴포넌트에서 페이지 변경
    component() {
      const wishTab = this.wishTab;
      return () => import(`@/views/wishlist/${wishTab}`);
    }
  },

  methods: {
    getBookDetail(){
      this.$axios.get('book/'+this.bid)
          .then(response=>{
            this.bookData = response.data
            this.keyword = (response.data.bookKeyword.split(','))
            //받아온 내용 줄바꿈 적용
            this.bookData.bookContent = response.data.bookContent.replace(/(?:\r\n|\r|\n)/g, '<br />')
            this.bookData.bookIndex = response.data.bookIndex.replace(/(?:\r\n|\r|\n)/g, '<br />')
            this.bookData.bookPreview = response.data.bookPreview.replace(/(?:\r\n|\r|\n)/g, '<br />')
            //detail item data set
            //page
            this.$set(this.detailItem[0],'data',response.data.bookPage +' page' )
            //치수 & 무게
            this.$set(this.detailItem[1],'data',response.data.bookSize.split('/')[0] )
            this.$set(this.detailItem[2],'data',response.data.bookSize.split('/')[1] )
            //Isbn - 10 & 13
            this.$set(this.detailItem[3],'data',response.data.bookIsbn.split(/[()]/)[0] )
            this.$set(this.detailItem[4],'data',response.data.bookIsbn.split(/[()]/)[1] )
            //data
            this.$set(this.detailItem[5],'data',response.data.bookPublishedDate)
          })
          .catch(error =>{
            console.log(error.response);
          })
    },

    //cart에 담기
    addCart(){
      this.$axios.get("cart/add/"+this.bid
      ).then(response=>{
        console.log(response.data.message);
        this.cartDialogMsg = "성공적으로 장바구니에 추가했습니다"
        this.cartDialog = true
      }).catch(error =>{
        console.log(error.response);
        this.cartDialogMsg = "장바구니 추가에 실패했습니다"
        this.cartDialog = true
      })
    },

    /*
  * 컴포넌트 관련 메소드
  *
  * */
    setComponentData(){
      this.dialog =true;
      this.setWishTab("WishList")   // 디폴트 페이지는 항상 WishList
      this.updateComponentKey()     // 컴포넌트를 리로드 하기위해
    },

    updateComponentKey(){
      this.componentKey +=1        //컴포넌트 리로드
    },
    setWishTab(data){
      this.wishTab = data
    },
    //긑

    pushLink(){
      let cartArr = []
      cartArr.push( {bid:this.bookData.bid, bookCount:1})
      this.$store.dispatch('getOrderByDetail', cartArr)
          .then(()=>
              this.$router.push({path: '/order'}).catch(()=>console.log('잘못된 접근입니다'))
          )
    },

    moveScroll(data){
      let div = document.getElementById(data)
      let abTop = window.pageYOffset + div.getBoundingClientRect().top;
      this.$vuetify.goTo(abTop)
    },
  },
  mounted() {
    this.getBookDetail()
    window.scrollTo(0, 0);
  }
}

</script>

 

1 ,2 책 정보

 

위 코드는 책 세부정보의 Vue  - 스크립트 부분이다

mounted로 책 정보를 불러온 후 정보들을 가공한다.

불러온 정보가 책 정보, 목차, 서평, 세부정보가 된다

 

 

3. 책 댓글 컴포넌트

(댓글 컴포넌트의 Vue - script코드 코드)

export default {
  name: "CommentComponent",
  props : ["selectBid"],
  data: function (){
    return{
		...
    }
  },
  watch: {
      writeComment(val) {
        this.commitBtn = val.length < 10;
      }
    },
  methods: {

    //정렬 설정 후 불러오기
    //설정 안하고 바로 불러오면 다시 불러올때 (페이지 넘기거나할때) 초기화돼서
    setSelectSort(index){
      this.selectSort = index
      this.getBookComment();
    },

    getBookComment() {
      let data = {}
      data.bid = this.selectBid
      data.sortType = this.selectSort
      data.page = this.page -1
      data.size = this.size
      this.$axios.post("comment/", JSON.stringify(data) ,{
        headers: {
          "Content-Type": `application/json`,
        },
      }).then(response=>{
        this.commentData = response.data.content;
        this.totalPages = response.data.totalPages;
        if(response.data.content.length === 0){
          this.noComments = true;
        }
      })
      .catch(error =>{
        console.log(error.response);
      })
    },

    postComment(){
      if(this.writeRating<1){
        alert("별점을 입력해주세요")
      }
      else if(this.writeComment<11) {
        alert("내용을 입력해주세요")
      }else {
        let data = {}
        data.bid = this.selectBid;
        data.mid = this.$store.state.member.userData.mid;
        data.ratings = this.writeRating;
        data.content = this.writeComment;
        this.$axios.post("comment/user/write", JSON.stringify(data), {
          headers: {
            "Content-Type": `application/json`,
          },
        }).then(response => {
          console.log(response.data)
          this.$emit('childKey')
        })
        .catch(error => {
          console.log(error.response);
        })
      }
    },

    //추천하기
    setPopularity(data,cid){
      console.log("popularity "+ data +" and "+ cid)
      let comment = {}
      comment.update = data
      comment.cid = cid
      this.$axios.post("comment/user/pop/", JSON.stringify(comment),{
        headers: {
          "Content-Type": `application/json`,
        },
      }).then(response=>{
          console.log(response.data);
          this.getBookComment();
      }).catch(error =>{
          console.log(error.response);
      })
    },

    //종합 추천도 불러오기
    getTotalRating() {
      //clearInterval(this.interval)
      this.$axios.get("comment/" + this.selectBid)
          .then(response=>{
            let totalCount = 0;
            let sumMulti = 0;
            for(let i = 0; i< response.data.length; i++) {
              totalCount = totalCount + response.data[i].count
            }
            for(let i = 0; i<4; i++) {
              //역순으로 리스트에 데이터 넣기
              //별이 4개면 -> 0번째 리스트에 , 3개면 -> 1번째
              if(response.data[i]==null){
                continue;
              }
                this.ratingList[(this.ratingList.length - 1) - (response.data[i].ratings - 1)].count = response.data[i].count
                this.ratingList[(this.ratingList.length - 1) - (response.data[i].ratings - 1)].per = Math.round(response.data[i].count / totalCount * 100);
                //전체 별점 구하기
                sumMulti = sumMulti + ( response.data[i].ratings * response.data[i].count )
            }
            this.totalRating = Math.round((sumMulti / totalCount) * 10) / 10;
            this.totalCount = totalCount;

            this.startBuffer()
          }).catch(error =>{
            console.log(error.response);
          })
    },
    //그래프 동작
    startBuffer(){
      for(let i = 0 ; i<4; i++) {
        this.interval = setInterval(() => {
          if (this.ratingList[i].value === this.ratingList[i].per) {
            return (this.ratingList[i].per)
          }
          this.ratingList[i].value += 1
        }, 25)
      }
    },

  },

  mounted() {
    this.getBookComment()
    this.getTotalRating()
  }

 

댓글 컴포넌트는 책 상세보기의 자식 컴포넌트이다.

댓글을 새로 불러오거나 해야하면 $emit을 사용하여 부모 컴포넌트에게 값을 보내 댓글 목록을 업데이트한다.

(추천과 댓글 작성에 사용)

 

3-1. 댓글 정렬

 

getComment() 부분을 보면 정렬부분이 있는걸 볼 수 있다.

정렬버튼은 최신순 추천순 별점순을 제공하고 클릭시  getComment() 의 sortType 에 0, 1, 2의 값을 넣어 보낸다

(0: 최신순, 1: 추천순, 2: 별점순)

 

java의 Comment Service 

    public Page<CommentBookMapping> getMyCommentList(SortDTO sortDTO) {
        PageRequest pageRequest = PageRequest.of(sortDTO.getPage() , sortDTO.getSize());

        //최신순
        if(sortDTO.getSortType() == 0) {
            return commentBookRepository.findAllByBidAndIsDel(getMemberIdByEmail(), "N", pageRequest);
        }
        //추천순
        if(sortDTO.getSortType() == 1) {
            return commentBookRepository.findByPopularity(getMemberIdByEmail(), "N", pageRequest);
        }
        //별점순
        if(sortDTO.getSortType() == 2) {
            return commentBookRepository.findByRatings(getMemberIdByEmail(), "N", pageRequest);
        }
        return null;
    }

받은 sortType에 따라 각각의 JPA쿼리로 처리한다.

 

3-2. 별점과 댓글수

 

(추천도 불러오기 js부분)

    //종합 추천도 불러오기
    getTotalRating() {
      //clearInterval(this.interval)
      this.$axios.get("comment/" + this.selectBid)
          .then(response=>{
            let totalCount = 0;
            let sumMulti = 0;
            for(let i = 0; i< response.data.length; i++) {
              totalCount = totalCount + response.data[i].count
            }
            for(let i = 0; i<4; i++) {
              //역순으로 리스트에 데이터 넣기
              //별이 4개면 -> 0번째 리스트에 , 3개면 -> 1번째
              if(response.data[i]==null){
                continue;
              }
                this.ratingList[(this.ratingList.length - 1) - (response.data[i].ratings - 1)].count = response.data[i].count
                this.ratingList[(this.ratingList.length - 1) - (response.data[i].ratings - 1)].per = Math.round(response.data[i].count / totalCount * 100);
                //전체 별점 구하기
                sumMulti = sumMulti + ( response.data[i].ratings * response.data[i].count )
            }
            this.totalRating = Math.round((sumMulti / totalCount) * 10) / 10;
            this.totalCount = totalCount;

            this.startBuffer()
          }).catch(error =>{
            console.log(error.response);
          })
    },

 

페이지가 실행되면 댓글 불러오기와 같이 전체 별점을 가져오는 getTotalRatings() 함수가 mounted된다

가져온 데이터를 가공해 그래프 값에 넣어주고 각 별점을 보기쉽게 그래프로 나타낸다.

 

 

 4. 우측 네비게이션

 

    moveScroll(data){
      let div = document.getElementById(data)
      let abTop = window.pageYOffset + div.getBoundingClientRect().top;
      this.$vuetify.goTo(abTop)
    },

우측 네비게이션의 각 목차를 클릭하면 위 함수가 실행되면서 해당 목차 구역으로 이동한다.

각 목차의 최상단에 id를 지정해 높이를 구하고 $vuetify.goTo로 부드럽게 이동한다 (영상참고)

 

 

 

 5. 하단 책 추천 컴포넌트

 

    public List<BookMainInterface> getRecommend(Long bid) {
        Optional<Book> bookData = bookRepository.findBookByBid(bid);
        Book data = bookData.orElseThrow(() -> new RuntimeException("no data"));


        String keyword = data.getBookKeyword().replace(',','|');
        return bookRepository.getRecommendRand(keyword, bid, "N");
    }

해당 book id로 키워드를 가져와 RegEXP로 해당 키워드를 포함하는 모든 책 중 rand쿼리와 limit를 사용하여 

조건에 맞는 랜덤 4가지의 책을 가져온다.

 

jpa nativeQuery

    @Query(value = "select b.bid, b.bookThumb ,b.bookTitle ,b.bookKeyword, b.bookAuthor, b.bookPublisher FROM Book b where (b.bookKeyword regexp :keyword) and not b.bid=:bid and b.isDel=:isDel order by rand() limit 4", nativeQuery = true)
    List<BookMainInterface> getRecommendRand(@Param("keyword") String keyword, @Param("bid") Long bid, @Param("isDel") String isDel);