| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- <template>
- <div>
- <div class="px-5">
- <div class="bg-white mt-[12px] p-4 rounded-lg">
- <moment-info :info="moments"></moment-info>
- </div>
- </div>
- <div class="title px-5 py-4 flex items-center relative">
- <span class="text-base font-medium">全部评论</span>
- <span class="text-xs text-[#797A8A] ml-[2px]">({{ totals }})</span>
- <img class="absolute bottom-2 left-[38px] w-[28px]" src="@/assets/png-xian.png" alt="" />
- </div>
- <div class="overflow-hidden pb-11">
- <div
- v-for="(item, index) in list"
- class="comment-box"
- :class="{ 'comment-center': showList[index] }"
- :key="index"
- ref="commentRef"
- >
- <comment-info :info="item"></comment-info>
- </div>
- <div class="loading flex items-center justify-center opacity-0" ref="loadingRef">加载中</div>
- <div class="empty py-6" v-if="!loading && !totals">
- <img class="w-3/5 block mx-[auto]" :src="isDark ? emptyDarkImg : emptyImg" alt="" />
- <div class="text-sm text-[#939599] text-center py-3">暂无评论</div>
- </div>
- </div>
- <div class="bottom bg-white px-[10px] py-[6px]">
- <NConfigProvider
- :theme-overrides="{
- Button: {
- fontSizeLarge: '24px',
- heightLarge: '40px'
- },
- Input: {
- color: '#F5F6F8',
- colorFocus: '#F5F6F8'
- }
- }"
- >
- <div class="flex">
- <n-input
- size="large"
- v-model:value="msg"
- type="text"
- @keydown.enter="send"
- clearable
- placeholder="来说点什么…"
- maxlength="200"
- />
- <n-button class="!ml-4" type="primary" size="large" @click="send">
- <template #icon>
- <n-icon><telegram-plane /></n-icon>
- </template>
- </n-button>
- </div>
- </NConfigProvider>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { MomentInfo, CommentInfo } from '@/components/common'
- import { ref, watch, nextTick } from 'vue'
- import { useWindowScroll, useElementBounding, useWindowSize, useScroll, useIntersectionObserver } from '@vueuse/core'
- import { fetchGetMomentsDetail, fetchSendComment, fetchComments } from '@/api'
- import { useRoute } from 'vue-router'
- import { NInput, NButton, NIcon, NConfigProvider, useMessage, NSpin } from 'naive-ui'
- import { TelegramPlane } from '@vicons/fa'
- import { useUserStore } from '@/store'
- import emptyImg from '@/assets/empty.png'
- import emptyDarkImg from '@/assets/empty-dark.png'
- import { useTheme } from '@/hooks/useTheme'
- const { isDark } = useTheme()
- const route = useRoute()
- const momentsId = route.query.id
- const commentRef = ref([])
- const showList = ref([] as any)
- const { y } = useWindowScroll()
- const { width, height } = useWindowSize()
- const list = ref([] as any)
- const totals = ref(0)
- const page = ref(1)
- const finish = ref(false)
- function getComments(isSend = false) {
- loading.value = true
- let data = {
- page: {
- page: page.value,
- limit: 20
- },
- search: {
- where: {
- momentsId: momentsId
- },
- order: {
- createdAt: 'DESC'
- }
- }
- }
- if (isSend) {
- data.page = {
- page: 1,
- limit: 1
- }
- }
- fetchComments(data).then((res: any) => {
- loading.value = false
- if (isSend) {
- list.value = [...res.items, ...list.value]
- } else {
- list.value = [...list.value, ...res.items]
- }
- totals.value = res.meta.totalItems
- if (res.meta.totalPages <= res.meta.currentPage) {
- finish.value = true
- } else {
- finish.value = false
- page.value = page.value + 1
- }
- })
- }
- watch(
- y,
- () => {
- nextTick(() => {
- showList.value = commentRef.value.map((item: any) => {
- return useElementBounding(item).top.value < height.value - 30
- })
- })
- },
- {
- immediate: true
- }
- )
- watch(
- list,
- () => {
- nextTick(() => {
- showList.value = commentRef.value.map((item: any) => {
- return useElementBounding(item).top.value < height.value - 30
- })
- })
- },
- {
- immediate: true
- }
- )
- const moments = ref({})
- fetchGetMomentsDetail(momentsId).then((res: any) => {
- moments.value = res
- })
- const msg = ref('')
- const message = useMessage()
- const userStore = useUserStore()
- function send() {
- if (!msg.value) {
- message.info('评论不能为空')
- return
- }
- fetchSendComment({
- content: msg.value,
- userId: userStore.userInfo.id,
- userName: userStore.userInfo.name,
- momentsId: momentsId
- }).then(res => {
- message.success('发送成功')
- msg.value = ''
- getComments(true)
- window.scrollTo(0, 0)
- })
- }
- const loadingRef = ref(null)
- const loading = ref(false)
- useIntersectionObserver(loadingRef, ([{ isIntersecting }]) => {
- if (isIntersecting && !loading.value && !finish.value) {
- getComments()
- }
- })
- </script>
- <style lang="less" scoped>
- .comment-box {
- opacity: 0.2;
- transition: all ease-in-out 0.3s;
- &:nth-child(2n) {
- transform: translate(-50px, 90px);
- }
- &:nth-child(2n + 1) {
- transform: translate(50px, 90px);
- }
- &.comment-center {
- transform: translate(0, 0);
- opacity: 1;
- }
- }
- .bottom {
- position: fixed;
- bottom: 0;
- z-index: 20;
- left: 0;
- right: 0;
- }
- </style>
|