mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-04-21 19:37:29 +08:00
feat: add nested comment support
This commit is contained in:
87
open-isle-cli/src/components/CommentItem.vue
Normal file
87
open-isle-cli/src/components/CommentItem.vue
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<div class="info-content-container" :style="{ marginLeft: level * 20 + 'px' }">
|
||||||
|
<div class="user-avatar-container">
|
||||||
|
<div class="user-avatar-item">
|
||||||
|
<img class="user-avatar-item-img" :src="comment.avatar" alt="avatar" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-content">
|
||||||
|
<div class="info-content-header">
|
||||||
|
<div class="user-name">{{ comment.userName }}</div>
|
||||||
|
<div class="post-time">{{ comment.time }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-content-text">
|
||||||
|
{{ comment.text }}
|
||||||
|
</div>
|
||||||
|
<div class="article-footer-container">
|
||||||
|
<div class="reactions-container">
|
||||||
|
<div class="reactions-viewer">
|
||||||
|
<div class="reactions-viewer-item-container">
|
||||||
|
<div class="reactions-viewer-item">🤣</div>
|
||||||
|
<div class="reactions-viewer-item">❤️</div>
|
||||||
|
<div class="reactions-viewer-item">👏</div>
|
||||||
|
</div>
|
||||||
|
<div class="reactions-count">1882</div>
|
||||||
|
</div>
|
||||||
|
<div class="make-reaction-container">
|
||||||
|
<div class="make-reaction-item like-reaction">
|
||||||
|
<i class="far fa-heart"></i>
|
||||||
|
</div>
|
||||||
|
<div class="make-reaction-item copy-link">
|
||||||
|
<i class="fas fa-link"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="comment.reply && comment.reply.length" class="reply-toggle" @click="toggleReplies">
|
||||||
|
{{ comment.reply.length }}条回复
|
||||||
|
</div>
|
||||||
|
<div v-if="showReplies" class="reply-list">
|
||||||
|
<CommentItem
|
||||||
|
v-for="r in comment.reply"
|
||||||
|
:key="r.id"
|
||||||
|
:comment="r"
|
||||||
|
:level="level + 1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
const CommentItem = {
|
||||||
|
name: 'CommentItem',
|
||||||
|
props: {
|
||||||
|
comment: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
level: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const showReplies = ref(false)
|
||||||
|
const toggleReplies = () => {
|
||||||
|
showReplies.value = !showReplies.value
|
||||||
|
}
|
||||||
|
return { showReplies, toggleReplies }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CommentItem.components = { CommentItem }
|
||||||
|
export default CommentItem
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.reply-toggle {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-top: 10px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.reply-list {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -82,51 +82,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="comments-container">
|
<div class="comments-container">
|
||||||
<div class="info-content-container" v-for="comment in comments" :key="comment.id" ref="postItems">
|
<CommentItem
|
||||||
<div class="user-avatar-container">
|
v-for="comment in comments"
|
||||||
<div class="user-avatar-item">
|
:key="comment.id"
|
||||||
<img class="user-avatar-item-img" :src="comment.avatar" alt="avatar">
|
:comment="comment"
|
||||||
</div>
|
:level="0"
|
||||||
</div>
|
ref="postItems"
|
||||||
|
/>
|
||||||
<div class="info-content">
|
|
||||||
<div class="info-content-header">
|
|
||||||
<div class="user-name">{{ comment.userName }}</div>
|
|
||||||
<div class="post-time">{{ comment.time }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="info-content-text">
|
|
||||||
{{ comment.text }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="article-footer-container">
|
|
||||||
<div class="reactions-container">
|
|
||||||
<div class="reactions-viewer">
|
|
||||||
<div class="reactions-viewer-item-container">
|
|
||||||
<div class="reactions-viewer-item">
|
|
||||||
🤣
|
|
||||||
</div>
|
|
||||||
<div class="reactions-viewer-item">
|
|
||||||
❤️
|
|
||||||
</div>
|
|
||||||
<div class="reactions-viewer-item">
|
|
||||||
👏
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="reactions-count">1882</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="make-reaction-container">
|
|
||||||
<div class="make-reaction-item like-reaction">
|
|
||||||
<i class="far fa-heart"></i>
|
|
||||||
</div>
|
|
||||||
<div class="make-reaction-item copy-link">
|
|
||||||
<i class="fas fa-link"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -146,9 +108,11 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import CommentItem from '../components/CommentItem.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PostPageView',
|
name: 'PostPageView',
|
||||||
|
components: { CommentItem },
|
||||||
setup() {
|
setup() {
|
||||||
const tags = ref(['AI', 'Python', 'Java'])
|
const tags = ref(['AI', 'Python', 'Java'])
|
||||||
const comments = ref([
|
const comments = ref([
|
||||||
@@ -237,7 +201,7 @@ export default {
|
|||||||
const updateCurrentIndex = () => {
|
const updateCurrentIndex = () => {
|
||||||
const scrollTop = mainContainer.value ? mainContainer.value.scrollTop : 0
|
const scrollTop = mainContainer.value ? mainContainer.value.scrollTop : 0
|
||||||
for (let i = 0; i < postItems.value.length; i++) {
|
for (let i = 0; i < postItems.value.length; i++) {
|
||||||
const el = postItems.value[i]
|
const el = postItems.value[i].$el
|
||||||
if (el.offsetTop + el.offsetHeight > scrollTop) {
|
if (el.offsetTop + el.offsetHeight > scrollTop) {
|
||||||
currentIndex.value = i + 1
|
currentIndex.value = i + 1
|
||||||
break
|
break
|
||||||
@@ -246,7 +210,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onSliderInput = () => {
|
const onSliderInput = () => {
|
||||||
const target = postItems.value[currentIndex.value - 1]
|
const target = postItems.value[currentIndex.value - 1]?.$el
|
||||||
if (target && mainContainer.value) {
|
if (target && mainContainer.value) {
|
||||||
mainContainer.value.scrollTo({ top: target.offsetTop, behavior: 'instant' })
|
mainContainer.value.scrollTo({ top: target.offsetTop, behavior: 'instant' })
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user