Эх сурвалжийг харах

Merge branch 'dev' of http://1.94.207.143:3000/xyy/xyy-m into dev

# Conflicts:
#	src/pages/profile/index.vue
songzhen 2 сар өмнө
parent
commit
d984a35b45

BIN
src/assets/img/comment/H5_default.png


+ 17 - 2
src/components/Empty/index.vue

@@ -1,9 +1,24 @@
 <template>
   <div class="w-full flex items-center justify-center min-h-200">
-    <div class="text-[#BFC8DB]">暂无数据</div>
+    <div class="text-[#BFC8DB]">{{ title }}</div>
   </div>
 </template>
 
-<script setup></script>
+<script setup>
+defineProps({
+  // top: {
+  //   type: String,
+  //   default: '100'
+  // },
+  // bottom: {
+  //   type: String,
+  //   default: '0'
+  // },
+  title: {
+    type: String,
+    default: '暂无数据'
+  }
+})
+</script>
 
 <style lang="scss" scoped></style>

+ 6 - 0
src/pages/profile/index.vue

@@ -47,6 +47,7 @@ import profile_travel_note from "~/assets/img/profile/profile_travel_note.png";
 import profile_colection from "~/assets/img/profile/profile_colection.png";
 import profile_car_order from "~/assets/img/profile/profile_car_order.png";
 import profile_visa_order from "~/assets/img/profile/profile_visa_order.png";
+import profile_my_comment from "~/assets/img/profile/profile_my_comment.png";
 
 const userInfoStore = useUserInfoStore();
 const { userInfo } = storeToRefs(userInfoStore);
@@ -81,6 +82,11 @@ const menuData = [
     label: "包车订单",
     to: "/profile/car-orders",
   },
+  {
+    icon: profile_my_comment,
+    label: "我的评论",
+    to: "/profile/my-comment",
+  },
 ];
 </script>
 

+ 504 - 0
src/pages/profile/my-comment.vue

@@ -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>