Browse Source

feat: 添加获取圈子评论回复接口,优化评论组件逻辑,支持回复功能

EvilDragon 3 months ago
parent
commit
f041138969

+ 5 - 0
packages/app/src/core/libs/requests.ts

@@ -240,6 +240,11 @@ export const getCircleReviews = (query: { circleId: string }) =>
   httpGet<{
     list: Comment[]
   }>('/app-api/member/circle-review/getCircleReviewByCircleId', query)
+/**
+ * 获取圈子评论回复
+ */
+export const getCircleReviewReplaies = (query = {}) =>
+  httpGet<any>('/app-api/member/circle-review/getReviewReplay', query)
 export const createCircleReviewUpvote = (data: {
   circleId: number
   userId: number

+ 95 - 44
packages/app/src/pages/home/components/comment-item.vue

@@ -4,6 +4,7 @@ import {
   cancelCircleReviewUpvote,
   createCircleReviewUpvote,
   deleteCircleReview,
+  getCircleReviewReplaies,
   getReviewReplay,
 } from '../../../core/libs/requests'
 import { thumbsUp, thumbsUpActive } from '../../../core/libs/svgs'
@@ -11,19 +12,32 @@ import { Comment } from '../../../core/libs/models'
 import { dayjs } from 'wot-design-uni'
 import { storeToRefs } from 'pinia'
 
-const props = defineProps({
-  options: {
-    type: Object as PropType<Comment>,
-    default: () => ({}),
-  },
-  isChild: {
-    type: Boolean,
-    default: false,
+// const props = defineProps({
+//   options: {
+//     type: Object as PropType<Comment>,
+//     default: () => ({}),
+//   },
+//   isChild: {
+//     type: Boolean,
+//     default: false,
+//   },
+
+// })
+const props = withDefaults(
+  defineProps<{ options: Partial<Comment>; isChild?: boolean; index?: number }>(),
+  {
+    options: () => ({}),
+    isChild: false,
   },
-})
-const emits = defineEmits<{ upvote: []; delete: [] }>()
+)
+const emits = defineEmits<{
+  upvote: [index?: number]
+  delete: [index?: number]
+  replay: [options: { reviewId: number; refreshId?: number; index?: number }]
+}>()
 const userStore = useUserStore()
 const { userInfo } = storeToRefs(userStore)
+const { data, run } = useRequest(() => getCircleReviewReplaies({ id: props.options.id.toString() }))
 const handleUpvote = async () => {
   if (!props.options.upvote) {
     const { code, msg } = await createCircleReviewUpvote({
@@ -61,47 +75,84 @@ const handleDelect = async () => {
     },
   })
 }
+const handleReplay = async () => {
+  emits('replay', { reviewId: props.options.id, refreshId: props.options.id, index: props.index })
+}
+const refresh = async () => {
+  console.log(11123424)
+
+  await run()
+}
 onMounted(async () => {
-  const { data } = await getReviewReplay({ id: props.options.id.toString() })
-  console.log(data)
+  !props.isChild && (await run())
+})
+defineExpose({
+  refresh,
 })
 </script>
 <template>
-  <view class="grid grid-cols-[28px_1fr_28px] gap-2.5" :class="isChild ? 'ml-9' : ''">
-    <wd-img
-      custom-class="rounded-full overflow-hidden col-start-1 row-start-1"
-      width="28"
-      height="28"
-      :src="options.userAvatar"
-    />
-    <view class="col-start-2 row-start-1">
-      <div class="text-black/40 text-xs font-normal font-['PingFang_SC'] leading-[10.18px]">
-        {{ options.userName }}
-      </div>
-      <div class="my-3 text-black/90 text-sm font-normal font-['PingFang_SC'] leading-[10.18px]">
-        {{ options.reviewContent }}
-      </div>
-      <view class="flex items-center mt--4 gap-3">
-        <div class="text-black/30 text-[10px] font-normal font-['PingFang_SC']">
-          {{ dayjs(options.reviewTime).format('YYYY/MM/DD') }}
+  <div>
+    <view
+      class="grid"
+      :class="
+        isChild ? 'ml-9 gap-1.25 grid-cols-[22px_1fr_28px]' : 'gap-2.5 grid-cols-[28px_1fr_28px]'
+      "
+    >
+      <wd-img
+        custom-class="rounded-full overflow-hidden col-start-1 row-start-1"
+        :width="isChild ? 22 : 28"
+        :height="isChild ? 22 : 28"
+        :src="options.userAvatar"
+      />
+      <view class="col-start-2 row-start-1">
+        <div class="text-black/40 text-xs font-normal font-['PingFang_SC'] leading-[10.18px]">
+          {{ options.userName }}
+        </div>
+        <div class="my-3 text-black/90 text-sm font-normal font-['PingFang_SC'] leading-[10.18px]">
+          <span v-if="isChild">回复</span>
+          <span
+            v-if="isChild"
+            class="text-black/40 text-sm font-normal font-['PingFang SC'] leading-[10.18px]"
+          >
+            {{ options.replayToUserName }}
+            :
+          </span>
+
+          {{ options.reviewContent }}
         </div>
-        <view class="">
-          <wd-button custom-class="text-2.5!" type="text">回复</wd-button>
+        <view class="flex items-center mt--4 gap-3">
+          <div class="text-black/30 text-[10px] font-normal font-['PingFang_SC']">
+            {{ dayjs(options.reviewTime).format('YYYY/MM/DD') }}
+          </div>
+          <view class="">
+            <wd-button custom-class="text-2.5!" type="text" @click="handleReplay">回复</wd-button>
+          </view>
+          <div v-if="userInfo.userId === options.userId">
+            <wd-button custom-class="text-2.5!" type="text" @click="handleDelect">删除</wd-button>
+          </div>
         </view>
-        <div v-if="userInfo.userId === options.userId">
-          <wd-button custom-class="text-2.5!" type="text" @click="handleDelect">删除</wd-button>
+      </view>
+      <view class="col-start-3 row-start-1 flex flex-col items-center" @click="handleUpvote">
+        <div class="w-[18px] h-[18px] relative">
+          <wd-img :src="options.upvote ? thumbsUpActive : thumbsUp" width="18" height="18"></wd-img>
+        </div>
+        <div
+          class="mt-1.5 text-black/40 text-[10px] font-normal font-['PingFang_SC'] leading-[10.18px]"
+        >
+          <span v-if="(options.upvoteCount ?? 0) !== 0">{{ options.upvoteCount || 0 }}</span>
         </div>
       </view>
     </view>
-    <view class="col-start-3 row-start-1 flex flex-col items-center" @click="handleUpvote">
-      <div class="w-[18px] h-[18px] relative">
-        <wd-img :src="options.upvote ? thumbsUpActive : thumbsUp" width="18" height="18"></wd-img>
-      </div>
-      <div
-        class="mt-1.5 text-black/40 text-[10px] font-normal font-['PingFang_SC'] leading-[10.18px]"
-      >
-        {{ options.upvoteCount || 0 }}
-      </div>
-    </view>
-  </view>
+    <template v-for="(it, i) in data" :key="i">
+      <comment-item
+        :options="it"
+        :isChild="true"
+        @replay="
+          (options) => emits('replay', { ...options, refreshId: props.options.id, index: index })
+        "
+        @delete="emits('delete', index)"
+        @upvote="emits('upvote', index)"
+      ></comment-item>
+    </template>
+  </div>
 </template>

+ 47 - 7
packages/app/src/pages/home/moment/index.vue

@@ -20,7 +20,7 @@ import { handleShareClick, handleUpvoteClick } from '../../../core/libs/actions'
 import CommentItem from '../components/comment-item.vue'
 import { useUserStore } from '../../../store'
 import { storeToRefs } from 'pinia'
-import { isImageOrVideo } from '../../../core/utils/common'
+import { isImageOrVideo, requestToast } from '../../../core/utils/common'
 import dayjs from 'dayjs'
 import SectionHeading from '@/components/section-heading.vue'
 import BottomAppBar from '@/components/bottom-app-bar.vue'
@@ -38,6 +38,9 @@ const router = useRouter()
 const id = ref()
 const isShared = ref(false)
 const commeentRef = ref<InstanceType<typeof WdInput>>()
+const commentItemRef = ref<InstanceType<typeof CommentItem>[]>()
+const instance = getCurrentInstance()
+
 const focus = ref(false)
 const { data, run } = useRequest(() => getCircle(id.value), { initialData: {} })
 const { data: reviews, run: runGetReviews } = useRequest(
@@ -56,6 +59,8 @@ const swiperSizes = ref()
 const swiperStyle = ref()
 const reviewContent = ref('')
 const isVideo = ref(false)
+const reviewId = ref()
+const refreshIndex = ref<number>()
 const handleChange = ({ detail: { current } }) => {
   swiperStyle.value = {
     height: swiperSizes.value[current].height + 'px',
@@ -76,20 +81,52 @@ const setSwiperStyle = async () => {
   }
 }
 const handleSend = async () => {
+  if (!reviewContent.value) {
+    uni.showToast({ title: '请输入评论内容', icon: 'none' })
+    return
+  }
   const { code, msg } = await createCircleReview({
     circleId: id.value,
     userId: userInfo.value.userId,
     userName: userInfo.value.nickname,
     reviewContent: reviewContent.value,
+    replayReviewId: reviewId.value,
   })
   if (code !== 0) {
     uni.showToast({ title: msg, icon: 'none' })
   } else {
     reviewContent.value = ''
     uni.showToast({ title: '评论成功', icon: 'none' })
+    if (refreshIndex.value) {
+      console.log(instance.refs)
+      commentItemRef.value.at(refreshIndex.value).refresh()
+      reviewId.value = undefined
+      refreshIndex.value = undefined
+    } else {
+      await runGetReviews()
+    }
+    focus.value = false
+  }
+}
+const handleReplay = async (options) => {
+  reviewId.value = options.reviewId
+  refreshIndex.value = options.index
+  focus.value = true
+}
+const handleDelete = async (index?: number) => {
+  if (index !== undefined) {
+    commentItemRef.value.at(index).refresh()
+  } else {
     await runGetReviews()
   }
 }
+const handleUpvote = async (index?: number) => {
+  if (index !== undefined) {
+    commentItemRef.value.at(index).refresh()
+  } else {
+    await run()
+  }
+}
 onMounted(async () => {})
 onLoad(async (query: { id: string; isShared?: boolean }) => {
   id.value = query.id
@@ -106,9 +143,9 @@ onLoad(async (query: { id: string; isShared?: boolean }) => {
 onShareAppMessage(async ({ from, target }) => {
   console.log('from', from)
   console.log('target', target)
-  if (!features.value.shareMoment) {
-    return handleShareClick()
-  }
+  // if (!features.value.shareMoment) {
+  //   return handleShareClick()
+  // }
   const res: Page.CustomShareContent = {}
   await shareCircle(id.value)
   res.path = `/pages/home/moment/index?id=${id.value}&isShared=true`
@@ -212,12 +249,15 @@ onShareAppMessage(async ({ from, target }) => {
 
       <view clas="mt-8.25">
         <template v-if="reviews?.list.length">
-          <template v-for="it of reviews?.list" :key="it.id">
+          <template v-for="(it, i) in reviews?.list" :key="it.id">
             <CommentItem
+              ref="commentItemRef"
               :options="it"
               :isChild="false"
-              @upvote="runGetReviews()"
-              @delete="runGetReviews()"
+              :index="i"
+              @upvote="handleUpvote"
+              @delete="handleDelete"
+              @replay="handleReplay"
             ></CommentItem>
             <!-- <template v-for="child of it.childrens" :key="child.id">
               <CommentItem :options="child" :isChild="true"></CommentItem>