롤 전적 사이트 구현 - 댓글 정렬 (front)

essential·2023년 9월 4일
0

op.gg

목록 보기
12/16

하... mysql 로 구현하려고 오늘까지 5일 동안 고민했는데 실패했다.
자바단에서 첫대댓이나 첫대댓의 첫대댓을 어케 잘 해서 잘 넣어주고 싶었는데
코드가 너무 복잡해지고 효율이 떨어지는 것 같아서 포기했다.
진짜진짜진짜 해결하고 싶었는데 못 한 게 아쉽다.. 꿈에서 해결한 적도 있음(ㅋㅋ)


front

   <div
        v-for="list in lists"
        :key="list.cno"
        class="comment-item"
        :style="{ paddingLeft: getPadding(list.depth) }"
      >
getPadding(depth) {
  const Padding = 20;
  return `${depth * Padding}px`; // depth 1 일 때 20 만큼 들여쓰기
},

depth로 구분할 수 있게 depth 에 따라서 padding이 달라지도록 했다

이런 느낌.. 화살표도 넣으면 좋겠지만 귀찮다.


Form 이랑 List 컴포넌트를 나눠서 구현했는데 굳이..인 느낌도 들고 했는데 보기엔 편하다.
지금까지 한 거랑 다를 게 없고 수정 input 을 새로 보여주는 게 좀 귀찮은 일인듯...

commnetForm.vue

<template>
  <div class="comment-form">
    <form>
      <input
        v-model="content"
        placeholder="댓글을 입력하세요."
        rows="4"
        class="comment-input"
      />
    </form>
    <button class="comment-submit" @click="handleForm">등록</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      content: "",
    };
  },
  methods: {
    async handleForm() {
      try {
        const commentData = {
          content: this.content,
          bno: this.$store.state.board.details.bno,
          id: this.$store.state.user.login.loginId,
        };
        const res = await this.$store.dispatch("writeComment", commentData);
        if (res.data == 1) {
          alert("댓글이 등록되었습니다.");
          const bno = this.$route.params.bno;
          await this.$store.dispatch("getCommentList", bno);
          this.content = "";
          return;
        }

        alert("작성에 실패하였습니다.");
      } catch (e) {
        console.error("오류", e);
        alert("작성 중 오류가 발생했습니다.");
      }
    },
  },
};
</script>

<style scoped>
.comment-form {
  margin-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.comment-input {
  width: 100%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  resize: vertical;
}

.comment-submit {
  align-self: flex-end;
  padding: 8px 16px;
  background-color: #007bff;
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.comment-submit:hover {
  background-color: #0056b3;
}
</style>

commentList.vue

<template>
  <div class="comment-wrapper">
    <div class="comment-list">
      <div
        v-for="list in lists"
        :key="list.cno"
        class="comment-item"
        :style="{ paddingLeft: getPadding(list.depth) }"
      >
        <div class="comment-content">{{ list.content }}</div>
        <div class="comment-actions">
          <button class="btn-reply" @click="handleReply(list)">댓글</button>
          <button class="btn-edit" @click="handleUpdate(list)">수정</button>
          <button class="btn-delete" @click="handleDelete(list.cno)">
            삭제
          </button>
        </div>
        <div v-if="showReplyInput === list.cno" class="reply-input">
          <input
            class="comment-input"
            v-model="replyContent"
            placeholder="댓글을 입력하세요."
          />
          <button class="btn-submitReply" @click="submitReply(list)">
            등록
          </button>
        </div>
        <div v-if="showUpInput === list.cno" class="reply-input">
          <input
            class="comment-input"
            v-model="upContent"
            placeholder="댓글을 입력하세요."
          />
          <button class="btn-submitReply" @click="submitReply(list)">
            등록
          </button>
        </div>
      </div>
    </div>
    <CommentForm />
  </div>
</template>

<script>
import { mapState } from "vuex";
import CommentForm from "./CommentForm.vue";

export default {
  components: {
    CommentForm,
  },

  data() {
    return {
      showReplyInput: null,
      replyContent: "",
      showUpInput: null,
      upContent: "",
    };
  },

  computed: {
    ...mapState({
      lists: (state) => state.comment.lists,
    }),
  },

  created() {
    const bno = this.$route.params.bno;
    if (bno) {
      this.$store.dispatch("getCommentList", bno);
    }
  },

  methods: {
    handleReply(lists) {
      this.showReplyInput =
        this.showReplyInput === lists.cno ? null : lists.cno;
    },

    handleUpdate(lists) {
      this.showUpInput = this.showUpInput === lists.cno ? null : lists.cno;
    },

    async submitReply(parent) {
      try {
        const replyData = {
          content: this.replyContent,
          bno: this.$store.state.board.details.bno,
          id: this.$store.state.user.login.loginId,
          cno: parent.cno, // 부모 댓글의 cno (선택한 댓글의 cno)
          depth: parent.depth,
          group_cno: parent.group_cno,
          order_cno: parent.order_cno,
        };

        const res = await this.$store.dispatch("writeComment", replyData);
        if (res.data == 1) {
          alert("댓글이 등록되었습니다.");
          const bno = this.$route.params.bno;
          await this.$store.dispatch("getCommentList", bno);
          this.showReplyInput = null; // 대댓글 입력 창 숨기기
          this.replyContent = "";
        }
      } catch (e) {
        console.error("댓글 작성 중 오류", e);
      }
    },

    getPadding(depth) {
      const padding = 20;
      return `${depth * padding}px`; // depth 1 일 때 20 만큼 들여쓰기
    },
  },
};
</script>

<style scoped>
.comment-wrapper {
  margin: 0 auto;
  margin-top: 30px;
}

.comment-list {
  border: 1px solid #ddd;
  padding: 15px;
  border-radius: 5px;
  height: 200px;
  overflow-y: auto;
}

.comment-item {
  border-bottom: 1px dashed #ddd;
  padding: 8px 0;
  position: relative;
  text-align: left;
}

.comment-content {
  padding-right: 90px;
}

.comment-actions {
  position: absolute;
  right: -5px;
  top: 50%;
  transform: translateY(-50%);
}
.comment-input {
  border: 1px solid #ccc;
  border-radius: 5px;
}

button.btn-edit {
  background-color: #aaa8a8;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  padding: 5px 10px;
  margin-left: 5px;
}
button.btn-delete {
  background-color: #f44336;
  color: #fdfdfd;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  padding: 5px 10px;
  margin-left: 5px;
}

button.btn-reply {
  background-color: #2196f3;
  color: #fdfdfd;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  padding: 5px 10px;
  margin-left: 10px;
}

button.btn-submitReply {
  background-color: #2196f3;
  color: #fdfdfd;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  margin-left: 5px;
}

button.btn-edit:hover,
button.btn-delete:hover {
  background-color: #7e7e7e;
}
</style>

commentStore

import axios from "axios";

const state = {
  lists: [],
  replyData: {
    bno: "",
    content: "",
    id: "",
    cno: "",
    depth: "",
    group_cno: "",
  },
};

const getters = {};

const mutations = {
  setLists(state, data) {
    state.lists = data;
  },
  setReply(state, data) {
    state.replyData = data;
  },
};

const actions = {
  async writeComment(context, commentData) {
    // 댓글 작성
    try {
      const res = await axios.post("/api/comment/write", commentData);
      context.commit("setComments", res.data);
      return res;
    } catch (e) {
      console.error("API 호출 실패", e);
      throw e;
    }
  },

  getCommentList(context, bno) {
    // 댓글 조회
    axios
      .get("/api/comment/" + bno)
      .then((res) => {
        console.log("API 호출 성공", res.data);
        context.commit("setLists", res.data);
      })
      .catch((e) => {
        console.error("API 호출 실패", e);
      });
  },
};

export default {
  state,
  getters,
  mutations,
  actions,
};


commentData, replyData 이런 식으로 다 로컬에서 때려박아서 store 로 넣어줬는데
bno 같이 다른 store에 저장된 state 를 불러와서 넣는게 번거롭지만 않았어도 store에서 했을텐데...저 부분이 아쉽다,, 나중에 정리 해야지...

profile
essential

0개의 댓글