|
@@ -0,0 +1,504 @@
|
|
|
+<template>
|
|
|
+ <div class="w-full h-full bg-[#fff] box-border">
|
|
|
+ <van-sticky :offset-top="50">
|
|
|
+ <van-dropdown-menu ref="commentDropdownMenuRef" active-color="#FF9300">
|
|
|
+ <!-- v-model="commentParmsa.commentType" -->
|
|
|
+ <van-dropdown-item
|
|
|
+ v-model="commentIndex"
|
|
|
+ :options="commentDropdownMenuList"
|
|
|
+ @change="onCommentFilterClose"
|
|
|
+ ref="commentItemRef"
|
|
|
+ >
|
|
|
+ <template #title class="relative" active-color="#FF9300">
|
|
|
+ <div class="font-semibold text-base">
|
|
|
+ <NuxtLink :to="'/profile'" class="absolute top-0 -left-135">
|
|
|
+ <van-icon name="arrow-left" size="24" color="#000000" />
|
|
|
+ </NuxtLink>
|
|
|
+ {{ commentDropdownMenuList[commentIndex].text }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </van-dropdown-item>
|
|
|
+ </van-dropdown-menu>
|
|
|
+ <div class="h-1 border-b-[1px] mx-12"></div>
|
|
|
+ </van-sticky>
|
|
|
+
|
|
|
+ <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
|
|
+ <Empty v-if="!commentList.length && !loading" title="暂无评论" />
|
|
|
+ <van-list
|
|
|
+ v-if="commentList.length"
|
|
|
+ v-model:loading="loading"
|
|
|
+ :finished="finished"
|
|
|
+ :immediate-check="false"
|
|
|
+ finished-text=""
|
|
|
+ @load="handleCurrentChange"
|
|
|
+ >
|
|
|
+ <template v-for="item in commentList" :key="item?.id">
|
|
|
+ <!-- <van-swipe-cell> -->
|
|
|
+ <div
|
|
|
+ v-if="commentIndex == 0 || commentIndex == 1"
|
|
|
+ style="scrollbar-width: none"
|
|
|
+ class="mx-12 pt-8 mb-12 box-border border-b-[1px] border-dashed flex justify-between items-start"
|
|
|
+ >
|
|
|
+ <div class="flex shrink-0 w-[74%] justify-start items-start">
|
|
|
+ <div class="w-32 h-32 rounded-full shrink-0">
|
|
|
+ <img
|
|
|
+ v-if="item?.headImageUrlDictMap?.name"
|
|
|
+ class="w-full h-full object-cover rounded-full"
|
|
|
+ :src="item?.headImageUrlDictMap?.name"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <img
|
|
|
+ v-else
|
|
|
+ class="w-full h-full object-cover rounded-full"
|
|
|
+ src="https://www.xiaoyaotravel.com/_nuxt/default_avatar.gSq5JxK1.png"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="ml-8 w-[85%] w-full text-sm">
|
|
|
+ <NuxtLink :to="`/yj/${item?.travelNoteId}`">
|
|
|
+ <p class="w-full line-clamp-1 text-black-6 font-normal leading-xl">
|
|
|
+ {{ item?.commentUserIdDictMap?.name }}
|
|
|
+ </p>
|
|
|
+ <p
|
|
|
+ :class="`w-full ${commentIndex == 0 ? 'line-clamp-1' : 'line-clamp-2'} my-6 text-black-3 text-base leading-4xl`"
|
|
|
+ >
|
|
|
+ <span v-if="item?.replyUserId">
|
|
|
+ 回复{{
|
|
|
+ item?.replyUserIdDictMap?.name != userInfo.showName
|
|
|
+ ? `@${item?.replyUserIdDictMap?.name}`
|
|
|
+ : ''
|
|
|
+ }}:
|
|
|
+ </span>
|
|
|
+ <span v-else>评论:</span>
|
|
|
+
|
|
|
+ <span v-html="coveredContent(item?.commentContent)"></span>
|
|
|
+ </p>
|
|
|
+ <p class="w-full mb-6 text-black-9 leading-xl">{{ item?.createTime }}</p>
|
|
|
+ </NuxtLink>
|
|
|
+ <div
|
|
|
+ @click.stop="addReply(item, item?.id)"
|
|
|
+ class="box-border mb-12 line-clamp-1 rounded-full border w-95 h-26 flex justify-center items-center text-base px-8 text-black-3 leading-4xl bg-[#F5F5F5] active:bg-black/[0.1]"
|
|
|
+ >
|
|
|
+ <div class="w-16 h-16 rounded-full shrink-0 mr-4">
|
|
|
+ <img
|
|
|
+ v-if="userInfo?.headImageUrl"
|
|
|
+ class="w-full h-full object-cover rounded-full"
|
|
|
+ :src="userInfo?.headImageUrl"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <img
|
|
|
+ v-else
|
|
|
+ class="w-full h-full object-cover rounded-full"
|
|
|
+ src="https://www.xiaoyaotravel.com/_nuxt/default_avatar.gSq5JxK1.png"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ 回复评论
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <NuxtLink
|
|
|
+ :to="`/yj/${item.travelNoteId}`"
|
|
|
+ class="block w-71 ml-17 h-47 shrink-0 rounded-[4px] overflow-hidden"
|
|
|
+ >
|
|
|
+ <img
|
|
|
+ v-if="item?.travelNoteUrl"
|
|
|
+ class="w-full h-full object-cover"
|
|
|
+ :src="item?.travelNoteUrl"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <img v-else class="w-full h-full object-cover" :src="defaultImg" alt="" />
|
|
|
+ </NuxtLink>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ v-if="commentIndex == 2"
|
|
|
+ style="scrollbar-width: none"
|
|
|
+ class="block mx-12 pt-8 mb-12 box-border border-b-[1px] border-dashed flex justify-between items-start"
|
|
|
+ >
|
|
|
+ <div class="flex shrink-0 w-[74%] justify-start items-start">
|
|
|
+ <div class="w-32 h-32 rounded-full shrink-0">
|
|
|
+ <img
|
|
|
+ v-if="userInfo?.headImageUrl"
|
|
|
+ class="w-full h-full object-cover rounded-full"
|
|
|
+ :src="userInfo?.headImageUrl"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <img
|
|
|
+ v-else
|
|
|
+ class="w-full h-full object-cover rounded-full"
|
|
|
+ src="https://www.xiaoyaotravel.com/_nuxt/default_avatar.gSq5JxK1.png"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="ml-8 w-[85%] mb-12 w-full text-sm">
|
|
|
+ <NuxtLink :to="`/yj/${item?.travelNoteId}`">
|
|
|
+ <p
|
|
|
+ v-if="userInfo?.showName"
|
|
|
+ class="w-full line-clamp-1 text-black-6 font-normal leading-xl"
|
|
|
+ >
|
|
|
+ {{ userInfo?.showName }}
|
|
|
+ </p>
|
|
|
+ <p class="w-full line-clamp-2 my-6 text-black-3 text-base leading-4xl">
|
|
|
+ <span v-if="item.replyUserId">
|
|
|
+ 回复{{
|
|
|
+ item?.replyUserIdDictMap?.name != userInfo.showName
|
|
|
+ ? `@${item?.replyUserIdDictMap?.name}`
|
|
|
+ : ''
|
|
|
+ }}:
|
|
|
+ </span>
|
|
|
+ <span v-else>
|
|
|
+ 评论{{
|
|
|
+ item?.replyUserIdDictMap?.name ? `@${item?.replyUserIdDictMap?.name}` : ''
|
|
|
+ }}
|
|
|
+ </span>
|
|
|
+ <span v-html="coveredContent(item?.commentContent)"></span>
|
|
|
+ </p>
|
|
|
+ <p class="w-full mb-6 text-black-9 leading-xl">{{ item?.createTime }}</p>
|
|
|
+ </NuxtLink>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <NuxtLink
|
|
|
+ :to="`/yj/${item.travelNoteId}`"
|
|
|
+ class="block w-71 ml-17 h-47 shrink-0 rounded-[4px] overflow-hidden"
|
|
|
+ >
|
|
|
+ <img
|
|
|
+ v-if="item?.travelNoteUrl"
|
|
|
+ class="w-full h-full object-cover"
|
|
|
+ :src="item?.travelNoteUrl"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <img v-else class="w-full h-full object-cover" :src="defaultImg" alt="" />
|
|
|
+ </NuxtLink>
|
|
|
+ </div>
|
|
|
+ <!-- <template #right>
|
|
|
+ <div class="pl-2 h-full">
|
|
|
+ <van-button
|
|
|
+ style="height: 100%"
|
|
|
+ square
|
|
|
+ text="删除"
|
|
|
+ @click="deleteComment(item.id)"
|
|
|
+ type="danger"
|
|
|
+ class="delete-button"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </van-swipe-cell> -->
|
|
|
+ </template>
|
|
|
+ </van-list>
|
|
|
+ </van-pull-refresh>
|
|
|
+
|
|
|
+ <div
|
|
|
+ v-if="showInput"
|
|
|
+ @click="handleBlur"
|
|
|
+ class="w-[100vw] h-[100vh] fixed top-0 border-[#000] left-0 z-100 bg-[#000]/[0.1]"
|
|
|
+ >
|
|
|
+ 123132
|
|
|
+ <div @click.stop="" class="fixed bottom-0 left-0 w-full bg-[#fff] pt-10 pb-30 border z-52">
|
|
|
+ <div class="flex items-start">
|
|
|
+ <!-- border-[#FD9A00] -->
|
|
|
+ <div
|
|
|
+ class="relative bg-[#F3F3F3] flex-1 ml-12 mr-12 pl-5 pr-5 pt-4 pb-4 rounded-[4px] h-50 border flex items-center justify-between"
|
|
|
+ >
|
|
|
+ <textarea
|
|
|
+ v-model="commentValue"
|
|
|
+ ref="textareaRef"
|
|
|
+ :placeholder="
|
|
|
+ replyComment?.id ? `回复:${replyComment?.commentUserIdDictMap?.name}` : ''
|
|
|
+ "
|
|
|
+ @focus="textareaFocus"
|
|
|
+ @blur="handleBlur"
|
|
|
+ class="ml-8 flex-1 h-full bg-black/[0]"
|
|
|
+ maxlength="5000"
|
|
|
+ style="resize: none"
|
|
|
+ ></textarea>
|
|
|
+ <!-- <img @click="openEmoji" src="~/assets/img/yj/emoji.png" class="w-22 h-22" alt="" /> -->
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ @click="addComment"
|
|
|
+ class="py-6 px-16 mr-12 bg-[#FD9A00] text-[#fff] flex items-center justify-center rounded-full shrink-0"
|
|
|
+ >
|
|
|
+ 发送
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-if="showEmoji" class="h-[300px] bg-[#fff] overflow-auto">
|
|
|
+ <div @click="closeEmojiBox" class="flex justify-end pr-15 text-[#999] text-[12px]">
|
|
|
+ 收起表情
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center flex-wrap">
|
|
|
+ <div
|
|
|
+ v-for="(item, index) in emojiJson"
|
|
|
+ :key="index"
|
|
|
+ class="hover:bg-[#ddd] text-[22px] w-[10%] aspect-[1/1] flex items-center justify-center"
|
|
|
+ >
|
|
|
+ <div @click="selectEmoji(index)" v-html="coveredContent(index)"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<script setup>
|
|
|
+import emojiArr from '../yj/emoji'
|
|
|
+import emojiJson from '../yj/emoji.json'
|
|
|
+import defaultImg from '~/assets/img/comment/H5_default.png'
|
|
|
+
|
|
|
+const authStore = useAuthStore()
|
|
|
+const { token } = storeToRefs(authStore)
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getMyCommentList()
|
|
|
+ getUserInfo()
|
|
|
+})
|
|
|
+
|
|
|
+useSeoMeta({
|
|
|
+ title: `我的评论`
|
|
|
+})
|
|
|
+
|
|
|
+const finished = ref(false)
|
|
|
+const error = ref(false)
|
|
|
+const loading = ref(false)
|
|
|
+const refreshing = ref(false)
|
|
|
+const commentIndex = ref(0) // 评论的索引
|
|
|
+const pageNum = ref(1)
|
|
|
+
|
|
|
+const textareaRef = ref(null)
|
|
|
+// 评论的列表
|
|
|
+const commentList = ref([])
|
|
|
+
|
|
|
+// 评论内容
|
|
|
+const commentValue = ref('')
|
|
|
+
|
|
|
+// 是否展示表情
|
|
|
+const showEmoji = ref(false)
|
|
|
+
|
|
|
+const commentDropdownMenuRef = ref(null)
|
|
|
+
|
|
|
+const commentItemRef = ref(null)
|
|
|
+
|
|
|
+// 评论的菜单列表
|
|
|
+const commentDropdownMenuList = [
|
|
|
+ { text: '我的评论', value: 0 },
|
|
|
+ { text: '收到的评论', value: 1 },
|
|
|
+ { text: '发出的评论', value: 2 }
|
|
|
+]
|
|
|
+
|
|
|
+function onCommentFilterClose(value) {
|
|
|
+ commentIndex.value = value
|
|
|
+ document.title = value == 0 ? '我的评论' : value == 1 ? '收到的评论' : '发出的评论'
|
|
|
+ getMyCommentList()
|
|
|
+}
|
|
|
+
|
|
|
+// 获取焦点
|
|
|
+function textareaFocus() {
|
|
|
+ // showEmoji.value = false
|
|
|
+ showInput.value = true
|
|
|
+ textareaRef.value?.focus()
|
|
|
+}
|
|
|
+
|
|
|
+// 文本域失焦
|
|
|
+function handleBlur() {
|
|
|
+ showEmoji.value = false
|
|
|
+ textareaRef.value?.blur()
|
|
|
+ showInput.value = false
|
|
|
+ replyComment.value = {}
|
|
|
+}
|
|
|
+
|
|
|
+// 获取我的评论列表
|
|
|
+async function getMyCommentList() {
|
|
|
+ try {
|
|
|
+ loading.value = true
|
|
|
+ const {
|
|
|
+ data: { dataList, totalCount }
|
|
|
+ } = await request(`/website/comment/tourTravelNotesComment/my-comments`, {
|
|
|
+ query: {
|
|
|
+ pageNum: pageNum.value,
|
|
|
+ pageSize: 20,
|
|
|
+ commentType: commentIndex.value
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ if (!Array.isArray(dataList) || !dataList.length) return (commentList.value = [])
|
|
|
+ commentList.value = commentList.value.concat(dataList)
|
|
|
+ refreshing.value = false
|
|
|
+
|
|
|
+ if (commentList.value.length >= totalCount) {
|
|
|
+ finished.value = true
|
|
|
+ } else {
|
|
|
+ finished.value = false
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ refreshing.value = false
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 删除评论
|
|
|
+// async function deleteComment(id) {
|
|
|
+// if (!id) return
|
|
|
+// showConfirmDialog({
|
|
|
+// message: '确认要删除这条评论吗?',
|
|
|
+// theme: 'round-button'
|
|
|
+// }).then(async () => {
|
|
|
+// showLoadingToast({
|
|
|
+// forbidClick: true,
|
|
|
+// duration: 0
|
|
|
+// })
|
|
|
+// await request('/website/comment/tourTravelNotesComment/delete', {
|
|
|
+// method: 'post',
|
|
|
+// body: { id }
|
|
|
+// }).finally(() => closeToast())
|
|
|
+// showToast('删除成功')
|
|
|
+// getMyCommentList()
|
|
|
+// })
|
|
|
+// }
|
|
|
+
|
|
|
+// 获取用户信息
|
|
|
+const userInfo = ref({})
|
|
|
+async function getUserInfo() {
|
|
|
+ if (!token.value) return (userInfo.value = {})
|
|
|
+ request('/website/tourism/user/view').then(
|
|
|
+ (res) => {
|
|
|
+ userInfo.value = res.data || {}
|
|
|
+ },
|
|
|
+ () => {
|
|
|
+ userInfo.value = {}
|
|
|
+ }
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+const cursorIndex = ref(0)
|
|
|
+
|
|
|
+// 打开表情
|
|
|
+function openEmoji() {
|
|
|
+ nextTick(() => {
|
|
|
+ textareaRef.value.selectionStart && (cursorIndex.value = textareaRef.value.selectionStart)
|
|
|
+ showEmoji.value = true
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 收起表情
|
|
|
+function closeEmojiBox() {
|
|
|
+ showEmoji.value = false
|
|
|
+ nextTick(() => {
|
|
|
+ textareaRef.value.focus()
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 选择表情
|
|
|
+function selectEmoji(emojiStr = '') {
|
|
|
+ const length = emojiStr.length
|
|
|
+ commentValue.value =
|
|
|
+ commentValue.value.slice(0, cursorIndex.value) +
|
|
|
+ emojiStr +
|
|
|
+ commentValue.value.slice(cursorIndex.value)
|
|
|
+
|
|
|
+ nextTick(() => {
|
|
|
+ cursorIndex.value += length
|
|
|
+ textareaRef.value.setSelectionRange(cursorIndex.value, cursorIndex.value)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 回复评论
|
|
|
+const replyComment = ref({})
|
|
|
+// 显示输入框
|
|
|
+const showInput = ref(false)
|
|
|
+
|
|
|
+function addReply(item, parentId) {
|
|
|
+ if (!token.value) {
|
|
|
+ showConfirmDialog({
|
|
|
+ showConfirmDialog: true,
|
|
|
+ title: '提示',
|
|
|
+ message: '登录后可以发布评论',
|
|
|
+ theme: 'round-button'
|
|
|
+ }).then(async () => {
|
|
|
+ navigateTo({ path: '/login' })
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ showInput.value = true
|
|
|
+ textareaRef.value?.focus()
|
|
|
+ replyComment.value = item
|
|
|
+
|
|
|
+ parentId && (replyComment.value.parentId = parentId)
|
|
|
+}
|
|
|
+
|
|
|
+// 发布评论
|
|
|
+const canAddComment = ref(true)
|
|
|
+
|
|
|
+async function addComment() {
|
|
|
+ if (!canAddComment.value) return
|
|
|
+ if (!token.value) {
|
|
|
+ showConfirmDialog({
|
|
|
+ showConfirmDialog: true,
|
|
|
+ title: '提示',
|
|
|
+ message: '登录后可以发布评论',
|
|
|
+ theme: 'round-button'
|
|
|
+ }).then(async () => {
|
|
|
+ navigateTo({ path: '/login' })
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!commentValue.value.trim()) {
|
|
|
+ showToast('评论内容不能为空!')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ canAddComment.value = false
|
|
|
+ const body = { travelNoteId: replyComment.travelNoteId, commentContent: commentValue.value }
|
|
|
+ if (replyComment.value.id) {
|
|
|
+ body.replyCommentId = replyComment.value.id
|
|
|
+ body.replyUserId = replyComment.value.createUserId
|
|
|
+ body.parentId = replyComment.value.parentId
|
|
|
+ }
|
|
|
+ request('website/comment/tourTravelNotesComment/add', { method: 'post', body })
|
|
|
+ .then(() => {
|
|
|
+ commentValue.value = ''
|
|
|
+ showToast('评论成功')
|
|
|
+
|
|
|
+ // getMyCommentList()
|
|
|
+ showEmoji.value = false
|
|
|
+ })
|
|
|
+ .finally(() => (canAddComment.value = true), (replyComment.value = {}), (cursorIndex.value = 0))
|
|
|
+}
|
|
|
+
|
|
|
+// 转换评论中的一些非字符emoji
|
|
|
+function coveredContent(val) {
|
|
|
+ if (!val) return ''
|
|
|
+ return val.replace(/\[.*?]/g, function (str) {
|
|
|
+ const baseApi = import.meta.env.VITE_APP_EMOJI_API
|
|
|
+ const emojiName = emojiJson[str]
|
|
|
+ if (!emojiName) return str
|
|
|
+ return `<img src=${baseApi}${emojiJson[str]} style="width: 20px; height: 20px;display: inline-block; vertical-align: middle"/>`
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 下拉刷新
|
|
|
+const onRefresh = () => {
|
|
|
+ refreshing.value = true
|
|
|
+ commentList.value = []
|
|
|
+ getMyCommentList()
|
|
|
+}
|
|
|
+
|
|
|
+// 改变页数
|
|
|
+const handleCurrentChange = () => {
|
|
|
+ finished.value = true
|
|
|
+ pageNum.value += 1
|
|
|
+ getMyCommentList()
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+// ::v-deep .van-dropdown-menu__title:after {
|
|
|
+// border-color: transparent transparent var(--van-black) var(--van-black);
|
|
|
+// }
|
|
|
+
|
|
|
+::v-deep .van-dropdown-menu__bar {
|
|
|
+ box-shadow: none;
|
|
|
+}
|
|
|
+</style>
|