index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. <template>
  2. <!-- hud -->
  3. <div class="w-full">
  4. <van-dropdown-menu fixed ref="interactionDropdownMenuRef" active-color="#FF9300">
  5. <van-dropdown-item
  6. v-model="interactionIndex"
  7. @change="onInteractionFilterClose"
  8. :options="
  9. interactionDropdownMenuList.map((item, index) => {
  10. if (item.value == interactionIndex) {
  11. item.icon = item.iconO
  12. return item
  13. } else {
  14. item.icon = item.iconM
  15. return item
  16. }
  17. })
  18. "
  19. ref="interactionItemRef"
  20. >
  21. <template #title class="relative" active-color="#FF9300">
  22. <div class="font-semibold text-base">
  23. <NuxtLink :to="'/profile/my-news'" class="absolute top-0 -left-135">
  24. <van-icon name="arrow-left" size="24" color="#000000" />
  25. </NuxtLink>
  26. {{ interactionTitle }}
  27. </div>
  28. </template>
  29. </van-dropdown-item>
  30. </van-dropdown-menu>
  31. <ProfileNewsAllMessage></ProfileNewsAllMessage>
  32. <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
  33. <Empty v-if="!interactionList.length && !loading" title="暂无评论" />
  34. <van-list
  35. v-if="interactionList.length"
  36. v-model:loading="loading"
  37. :finished="finished"
  38. :immediate-check="false"
  39. finished-text=""
  40. @load="handleCurrentChange"
  41. >
  42. <template v-for="item in interactionList" :key="item?.id">
  43. <div
  44. v-if="interactionIndex == 0 || interactionIndex == 2"
  45. style="scrollbar-width: none"
  46. class="mx-12 pt-8 mb-12 box-border border-b-[1px] border-dashed flex justify-between items-start"
  47. >
  48. <div class="flex shrink-0 w-[74%] justify-start items-start">
  49. <div class="w-32 h-32 rounded-full shrink-0">
  50. <img
  51. v-if="item?.headImageUrlDictMap?.name"
  52. class="w-full h-full object-cover rounded-full"
  53. :src="item?.headImageUrlDictMap?.name"
  54. alt=""
  55. />
  56. <img
  57. v-else
  58. class="w-full h-full object-cover rounded-full"
  59. src="https://www.xiaoyaotravel.com/_nuxt/default_avatar.gSq5JxK1.png"
  60. alt=""
  61. />
  62. </div>
  63. <div class="ml-8 w-[85%] w-full text-sm">
  64. <NuxtLink :to="`/yj/${item?.travelNoteId}`">
  65. <p class="w-full line-clamp-1 text-black-6 font-normal leading-xl">
  66. {{ item?.commentUserIdDictMap?.name }}
  67. </p>
  68. <p
  69. :class="`w-full ${interactionIndex == 0 ? 'line-clamp-1' : 'line-clamp-2'} my-6 text-black-3 text-base leading-4xl`"
  70. >
  71. <span v-if="item?.replyUserId">
  72. 回复{{
  73. item?.replyUserIdDictMap?.name != userInfo.showName
  74. ? `@${item?.replyUserIdDictMap?.name}`
  75. : ''
  76. }}:
  77. </span>
  78. <span v-else>评论:</span>
  79. <span v-html="coveredContent(item?.commentContent)"></span>
  80. </p>
  81. <p class="w-full mb-6 text-black-9 leading-xl">{{ item?.createTime }}</p>
  82. </NuxtLink>
  83. <div
  84. @click.stop="addReply(item, item?.id)"
  85. class="box-border mb-12 line-clamp-1 rounded-full 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]"
  86. >
  87. <div class="w-16 h-16 rounded-full shrink-0 mr-4">
  88. <img
  89. v-if="userInfo?.headImageUrl"
  90. class="w-full h-full object-cover rounded-full"
  91. :src="userInfo?.headImageUrl"
  92. alt=""
  93. />
  94. <img
  95. v-else
  96. class="w-full h-full object-cover rounded-full"
  97. src="https://www.xiaoyaotravel.com/_nuxt/default_avatar.gSq5JxK1.png"
  98. alt=""
  99. />
  100. </div>
  101. 回复评论
  102. </div>
  103. </div>
  104. </div>
  105. <NuxtLink
  106. :to="`/yj/${item.travelNoteId}`"
  107. class="block w-71 ml-17 h-47 shrink-0 rounded-[4px] overflow-hidden"
  108. >
  109. <img
  110. v-if="item?.travelNoteUrl"
  111. class="w-full h-full object-cover"
  112. :src="item?.travelNoteUrl"
  113. alt=""
  114. />
  115. <img v-else class="w-full h-full object-cover" :src="defaultImg" alt="" />
  116. </NuxtLink>
  117. </div>
  118. <div
  119. v-if="interactionIndex == 1"
  120. style="scrollbar-width: none"
  121. class="block mx-12 pt-8 mb-12 box-border border-b-[1px] border-dashed flex justify-between items-start"
  122. >
  123. <div class="flex shrink-0 w-[74%] justify-start items-start">
  124. <div class="w-32 h-32 rounded-full shrink-0">
  125. <img
  126. v-if="userInfo?.headImageUrl"
  127. class="w-full h-full object-cover rounded-full"
  128. :src="userInfo?.headImageUrl"
  129. alt=""
  130. />
  131. <img
  132. v-else
  133. class="w-full h-full object-cover rounded-full"
  134. src="https://www.xiaoyaotravel.com/_nuxt/default_avatar.gSq5JxK1.png"
  135. alt=""
  136. />
  137. </div>
  138. <div class="ml-8 w-[85%] mb-12 w-full text-sm">
  139. <NuxtLink :to="`/yj/${item?.travelNoteId}`">
  140. <p
  141. v-if="userInfo?.showName"
  142. class="w-full line-clamp-1 text-black-6 font-normal leading-xl"
  143. >
  144. {{ userInfo?.showName }}
  145. </p>
  146. <p class="w-full line-clamp-2 my-6 text-black-3 text-base leading-4xl">
  147. <span v-if="item.replyUserId">
  148. 回复{{
  149. item?.replyUserIdDictMap?.name != userInfo.showName
  150. ? `@${item?.replyUserIdDictMap?.name}`
  151. : ''
  152. }}:
  153. </span>
  154. <span v-else>
  155. 评论{{
  156. item?.replyUserIdDictMap?.name ? `@${item?.replyUserIdDictMap?.name}` : ''
  157. }}
  158. </span>
  159. <span v-html="coveredContent(item?.commentContent)"></span>
  160. </p>
  161. <p class="w-full mb-6 text-black-9 leading-xl">{{ item?.createTime }}</p>
  162. </NuxtLink>
  163. </div>
  164. </div>
  165. <NuxtLink
  166. :to="`/yj/${item.travelNoteId}`"
  167. class="block w-71 ml-17 h-47 shrink-0 rounded-[4px] overflow-hidden"
  168. >
  169. <img
  170. v-if="item?.travelNoteUrl"
  171. class="w-full h-full object-cover"
  172. :src="item?.travelNoteUrl"
  173. alt=""
  174. />
  175. <img v-else class="w-full h-full object-cover" :src="defaultImg" alt="" />
  176. </NuxtLink>
  177. </div>
  178. </template>
  179. </van-list>
  180. </van-pull-refresh>
  181. </div>
  182. </template>
  183. <script setup>
  184. import defaultImg from '~/assets/img/comment/H5_default.png'
  185. import commentsBlack from '~/assets/img/chat/comments-black.svg'
  186. import commentsOrange from '~/assets/img/chat/comments-orange.svg'
  187. import like from '~/assets/img/chat/like.svg'
  188. import likeOrange from '~/assets/img/chat/like-orange.svg'
  189. import eit from '~/assets/img/chat/tiji.svg'
  190. import eitOrange from '~/assets/img/chat/tiji-orange.svg'
  191. import comment from '~/assets/img/chat/comment.svg'
  192. import commentOrange from '~/assets/img/chat/comment-orange.svg'
  193. import medicalFiles from '~/assets/img/chat/medical-files.svg'
  194. import medicalFilesOrange from '~/assets/img/chat/medical-files-orange.svg'
  195. import send from '~/assets/img/chat/send.svg'
  196. import sendOrange from '~/assets/img/chat/send-orange.svg'
  197. import emojiJson from '../../yj/emoji.json'
  198. const authStore = useAuthStore()
  199. const { token } = storeToRefs(authStore)
  200. onMounted(() => {
  201. getUserInfo()
  202. })
  203. definePageMeta({
  204. layout: false
  205. })
  206. // 默认的数组
  207. const interactionDropdownMenuList = [
  208. {
  209. text: '全部消息',
  210. value: 5,
  211. icon: commentsBlack,
  212. iconM: commentsBlack,
  213. iconO: commentsOrange
  214. },
  215. { text: '赞与收藏', value: 4, icon: like, iconM: like, iconO: likeOrange },
  216. {
  217. text: '提及',
  218. value: 3,
  219. icon: eit,
  220. iconM: eit,
  221. iconO: eitOrange
  222. },
  223. { text: '我的评论', value: 0, icon: comment, iconM: comment, iconO: commentOrange },
  224. {
  225. text: '收到的评论',
  226. value: 2,
  227. icon: medicalFiles,
  228. iconM: medicalFiles,
  229. iconO: medicalFilesOrange
  230. },
  231. { text: '发出的评论', value: 1, icon: send, iconM: send, iconO: sendOrange }
  232. ]
  233. const pageNum = ref(1)
  234. const finished = ref(false)
  235. const loading = ref(false)
  236. const refreshing = ref(false)
  237. // 互动消息的索引
  238. const interactionIndex = ref(5)
  239. const interactionTitle = ref('全部消息')
  240. const interactionDropdownMenuRef = ref(null)
  241. const interactionItemRef = ref(null)
  242. // 评论的列表
  243. const interactionList = ref([])
  244. // 下拉菜单的方法
  245. function onInteractionFilterClose(value) {
  246. interactionIndex.value = value
  247. interactionTitle.value = interactionDropdownMenuList.find((item) => item.value == value).text
  248. interactionList.value = []
  249. getInteractionList()
  250. }
  251. useSeoMeta({
  252. title: '互动消息'
  253. })
  254. // 获取用户信息
  255. const userInfo = ref({})
  256. async function getUserInfo() {
  257. if (!token.value) return (userInfo.value = {})
  258. request('/website/tourism/user/view').then(
  259. (res) => {
  260. userInfo.value = res.data || {}
  261. },
  262. () => {
  263. userInfo.value = {}
  264. }
  265. )
  266. }
  267. // 转换评论中的一些非字符emoji
  268. function coveredContent(val) {
  269. if (!val) return ''
  270. return val.replace(/\[.*?]/g, function (str) {
  271. const baseApi = import.meta.env.VITE_APP_EMOJI_API
  272. const emojiName = emojiJson[str]
  273. if (!emojiName) return str
  274. return `<img src=${baseApi}${emojiJson[str]} style="width: 20px; height: 20px;display: inline-block; vertical-align: middle"/>`
  275. })
  276. }
  277. // 获取互动消息的数据列表
  278. const getInteractionList = async () => {
  279. try {
  280. loading.value = true
  281. const {
  282. data: { dataList, totalCount }
  283. } = await request(`/website/comment/tourTravelNotesComment/my-comments`, {
  284. query: {
  285. pageNum: pageNum.value,
  286. pageSize: 20,
  287. commentType: interactionIndex.value
  288. }
  289. })
  290. if (!Array.isArray(dataList) || !dataList.length) return (interactionList.value = [])
  291. interactionList.value = interactionList.value.concat(dataList)
  292. refreshing.value = false
  293. if (interactionList.value.length >= totalCount) {
  294. finished.value = true
  295. } else {
  296. finished.value = false
  297. }
  298. } finally {
  299. refreshing.value = false
  300. loading.value = false
  301. }
  302. }
  303. // 点赞的的方法
  304. // 下拉刷新
  305. const onRefresh = () => {
  306. refreshing.value = true
  307. commentList.value = []
  308. getMyCommentList()
  309. }
  310. // 改变页数
  311. const handleCurrentChange = () => {
  312. finished.value = true
  313. pageNum.value += 1
  314. getInteractionList()
  315. }
  316. </script>
  317. <style lang="scss" scoped>
  318. ::v-deep .van-cell__left-icon {
  319. display: flex;
  320. align-items: center;
  321. }
  322. </style>