moment-item.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <script lang="ts" setup>
  2. import Card from '@/components/card.vue'
  3. import dayjs from 'dayjs'
  4. import { beforeNow } from '../utils/date-util'
  5. import Tag from './tag.vue'
  6. import { stringify } from 'qs'
  7. import { isImageOrVideo } from '../core/utils/common'
  8. import { useRouter } from '../core/utils/router'
  9. import { likeActived, likeBlack } from '@designer-hub/assets/src/icons'
  10. import { NetImages } from '../core/libs/net-images'
  11. import { currRoute } from '../utils'
  12. import { usePermissions } from '../composables/permissions'
  13. import { useMemberLevelsStore } from '../store/member-levles'
  14. const props = withDefaults(
  15. defineProps<{
  16. options: {
  17. id: number
  18. headUrl?: string
  19. stylistId?: number
  20. stylistName?: string
  21. marketing?: string
  22. circleDesc?: string
  23. tagName?: string
  24. detailsType?: string
  25. detailsUrl?: string
  26. detailsDesc?: string
  27. circleType?: string
  28. spaceType?: string
  29. designStyle?: string
  30. spaceAddr?: string
  31. customerDemand?: string
  32. createTime: number
  33. bannerUrls: string[]
  34. shareCount?: number
  35. upvoteCount?: number
  36. ownUpvote: boolean
  37. reviewCount: number
  38. levelId?: number
  39. }
  40. isOwn?: boolean
  41. }>(),
  42. {},
  43. )
  44. const emits = defineEmits<{ delete: [id: number]; like: [options: any] }>()
  45. const router = useRouter()
  46. const { features } = usePermissions()
  47. const memberLevelsStore = useMemberLevelsStore()
  48. const { getMemberLevelLogo } = memberLevelsStore
  49. const imgClass = ref('')
  50. const isVideo = ref(false)
  51. const toDetail = () => {
  52. uni.navigateTo({
  53. url: `/pages/home/moment/index?${stringify({ id: props.options.id })}`,
  54. })
  55. }
  56. const handleDelete = async () => {
  57. emits('delete', props.options.id)
  58. }
  59. onMounted(async () => {
  60. if (
  61. props.options.circleType === '1' &&
  62. props.options.bannerUrls?.length === 1 &&
  63. isImageOrVideo(props.options.bannerUrls[0]) === 'image'
  64. ) {
  65. const { width, height } = await uni.getImageInfo({
  66. src: props.options.bannerUrls[0],
  67. })
  68. if (Number(width / height) > 1) {
  69. imgClass.value = 'w-[60vw]'
  70. } else {
  71. imgClass.value = 'w-[44vw]'
  72. }
  73. }
  74. if (
  75. props.options.bannerUrls?.length === 1 &&
  76. isImageOrVideo(props.options.bannerUrls[0]) === 'video'
  77. ) {
  78. isVideo.value = true
  79. }
  80. })
  81. </script>
  82. <template>
  83. <div @click="toDetail">
  84. <Card>
  85. <view class="flex items-center gap-2">
  86. <view
  87. class="overflow-hidden rounded-full"
  88. @click.stop="
  89. features.toDesignerHomePage &&
  90. ['1', '2'].includes(options?.circleType) &&
  91. currRoute().path !== '/pages/mine/homepage/index' &&
  92. router.push(`/pages/mine/homepage/index?id=${options.stylistId}`)
  93. "
  94. >
  95. <wd-img
  96. custom-class="vertical-bottom"
  97. :width="35"
  98. :height="35"
  99. :src="props.options.headUrl || NetImages.DefaultAvatar"
  100. mode="scaleToFill"
  101. />
  102. </view>
  103. <view class="">{{ props.options.stylistName || props.options.marketing }}</view>
  104. <template v-if="getMemberLevelLogo(options?.levelId)">
  105. <wd-img
  106. width="63"
  107. height="18.6"
  108. :src="getMemberLevelLogo(options?.levelId) || ''"
  109. ></wd-img>
  110. </template>
  111. <view class="flex-1"></view>
  112. <view>{{ beforeNow(dayjs(props.options.createTime).toDate()) }}</view>
  113. </view>
  114. <div v-if="isVideo" class="aspect-[1.64/1] rounded-lg overflow-hidden my-6" @click.stop>
  115. <video class="w-full h-full" :src="options.bannerUrls[0]"></video>
  116. </div>
  117. <view
  118. v-if="!isVideo"
  119. :class="[
  120. props.options.bannerUrls?.length > 1 ? 'grid grid-cols-3 grid-gap-1' : 'w-full',
  121. 'my-6',
  122. ]"
  123. >
  124. <template v-for="it of props.options.bannerUrls" :key="it">
  125. <view
  126. v-if="options.circleType === '1'"
  127. :class="[
  128. props.options.bannerUrls?.length > 1 ? 'aspect-square' : '',
  129. 'rounded-lg overflow-hidden',
  130. imgClass,
  131. ]"
  132. >
  133. <wd-img
  134. custom-class="vertical-bottom"
  135. :width="'100%'"
  136. :src="it"
  137. :height="props.options.bannerUrls?.length > 1 ? '100%' : 'auto'"
  138. :mode="props.options.bannerUrls?.length > 1 ? 'aspectFill' : 'widthFix'"
  139. ></wd-img>
  140. </view>
  141. <view class="aspect-[1.64/1] rounded-lg overflow-hidden" v-else>
  142. <wd-img
  143. custom-class="vertical-bottom"
  144. width="100%"
  145. height="100%"
  146. :src="it"
  147. mode="aspectFill"
  148. ></wd-img>
  149. </view>
  150. </template>
  151. </view>
  152. <view class="text-[rgba(0,0,0,0.85)] text-4 font-400 my-1">
  153. {{ props.options.circleDesc }}
  154. </view>
  155. <view class="my-5.5 flex flex-wrap gap-3.5">
  156. <template v-if="props.options.tagName !== ''">
  157. <template v-for="it of props.options.tagName?.split(',')" :key="it">
  158. <Tag>{{ it }}</Tag>
  159. </template>
  160. </template>
  161. </view>
  162. <view class="flex justify-between">
  163. <div>
  164. <button
  165. open-type="share"
  166. class="bg-transparent! p-0!"
  167. :data-options="options"
  168. @click.stop
  169. >
  170. <view
  171. class="flex items-center text-[rgba(0,0,0,0.85)] text-3.5 font-400 line-height-5.5"
  172. >
  173. <wd-img width="15" height="15" src="/static/svgs/share.svg"></wd-img>
  174. <view class="ml-1">{{ props.options.shareCount }}</view>
  175. </view>
  176. </button>
  177. </div>
  178. <view class="flex items-center text-[rgba(0,0,0,0.85)] text-3.5 font-400 line-height-5.5">
  179. <wd-img width="15" height="15" src="/static/svgs/comment.svg"></wd-img>
  180. <view class="ml-1">{{ props.options.reviewCount }}</view>
  181. </view>
  182. <view
  183. class="flex items-center text-3.5 font-400 line-height-5.5"
  184. :class="[options.ownUpvote ? 'text-[#ca5141]' : 'text-[rgba(0,0,0,0.85)]']"
  185. @click.stop="emits('like', { upvote: options.ownUpvote, circleId: options.id })"
  186. >
  187. <template v-if="options.ownUpvote">
  188. <wd-img width="18" height="18" :src="likeActived"></wd-img>
  189. </template>
  190. <template v-else>
  191. <wd-img width="18" height="18" :src="likeBlack"></wd-img>
  192. </template>
  193. <view class="ml-1">{{ props.options.upvoteCount }}</view>
  194. </view>
  195. <div v-if="isOwn" @click.stop="handleDelete()">
  196. <wd-button type="text" size="small">
  197. <span class="text-black/30 text-xs font-normal font-['PingFang_SC']">
  198. 删除
  199. <wd-icon name="close" size="12"></wd-icon>
  200. </span>
  201. </wd-button>
  202. </div>
  203. </view>
  204. </Card>
  205. </div>
  206. </template>