index.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <!-- 使用 type="home" 属性设置首页,其他页面不需要设置,默认为page;推荐使用json5,更强大,且允许注释 -->
  2. <route lang="json5" type="home">
  3. {
  4. layout: 'tabbar',
  5. style: {
  6. navigationStyle: 'custom',
  7. navigationBarTitleText: '首页',
  8. },
  9. }
  10. </route>
  11. <script lang="ts" setup>
  12. import Card from '@/components/card.vue'
  13. import HotActivity from '@/components/hot-activity.vue'
  14. import MomentItem from '@/components/moment-item.vue'
  15. import HomeBanner from './components/home-banner.vue'
  16. import useRequest from '../../hooks/useRequest'
  17. import Menus from './components/menus.vue'
  18. import {
  19. getActivities,
  20. getBadges,
  21. getCertificates,
  22. getCircles,
  23. getMyStudyTours,
  24. getSetIndexConfigs,
  25. getStudyTours,
  26. updateHonorPopUp,
  27. updateSetIndexConfig,
  28. } from '../../core/libs/requests'
  29. import { logo } from '../../core/libs/svgs'
  30. import { ComponentExposed } from 'vue-component-type-helpers'
  31. import { usePermissions } from '../../composables/permissions'
  32. import { storeToRefs } from 'pinia'
  33. import { handleUpvoteClick } from '../../core/libs/actions'
  34. import { useUserStore } from '../../store'
  35. import ScheduleCard from './components/schedule-card.vue'
  36. import dayjs from 'dayjs'
  37. import { pick, sort } from 'radash'
  38. import { Activity, StudyTour } from '../../core/libs/models'
  39. import PageHelperEvo from '@/components/page-helper-evo.vue'
  40. import { useMessage } from 'wot-design-uni'
  41. import ShareActionSheet from './components/share-action-sheet.vue'
  42. import { useShare } from '@/composables/share'
  43. import { useHonorDialog, HonorDialogType } from './components/honor-dialog/index'
  44. import { getByDictType } from '../../core/libs/requests'
  45. import { DictType } from '../../core/libs/models'
  46. defineOptions({
  47. name: 'Home',
  48. })
  49. useMessage()
  50. const { show } = useHonorDialog()
  51. const userStore = useUserStore()
  52. const { userInfo } = storeToRefs(userStore)
  53. const { features, isLogined } = usePermissions()
  54. const { shareAppMessage } = useShare()
  55. const pageHelperRef = ref<ComponentExposed<typeof PageHelperEvo>>()
  56. const { data: indexConfigsData, run: setIndexConfigsData } = useRequest(
  57. () => getSetIndexConfigs(),
  58. { initialData: { list: [] } },
  59. )
  60. const { data: studyTours, run: setStudyTours } = useRequest(() => getMyStudyTours(), {
  61. initialData: [],
  62. })
  63. const swiperData = ref<{ data: any }[]>()
  64. const swiperCurrent = ref(0)
  65. const autoplay = ref(true)
  66. const homeBannerRef = ref<ComponentExposed<typeof HomeBanner>[]>()
  67. const hotActivities =
  68. ref<{ type: 'studyTour' | 'activity'; data: StudyTour & Activity; startAt: string | number }[]>()
  69. const shareActionState = ref(false)
  70. // const shareRef = ref<Comp>()
  71. const shareOptions = ref()
  72. const currentStudyTour = computed(() =>
  73. studyTours.value.find(
  74. (it) => dayjs(it.studyStartTime).isBefore(dayjs()) && dayjs(it.studyEndTime).isAfter(dayjs()),
  75. ),
  76. )
  77. const toAbout = () => {
  78. uni.navigateTo({ url: '/pages-sub/home/about/index' })
  79. }
  80. const currentBanner = ref(0)
  81. const handleSwiperChange = ({ detail: { current, source } }) => {
  82. // console.log('current', current)
  83. console.log(current, source, swiperCurrent.value)
  84. homeBannerRef.value?.[swiperCurrent.value]?.videoContext.pause()
  85. swiperCurrent.value = current
  86. currentBanner.value = current
  87. }
  88. const handleLike = async (options) => {
  89. await handleUpvoteClick({
  90. ...options,
  91. userId: userInfo.value.userId,
  92. userName: userInfo.value.nickname,
  93. })
  94. await pageHelperRef.value?.refresh()
  95. }
  96. const setHotActivities = async () => {
  97. const res = await Promise.all([
  98. getStudyTours({ headRecommend: 1 }).then((res) =>
  99. res.data.list.map((it) => ({ type: 'studyTour', data: it, startAt: it.applyStartTime })),
  100. ),
  101. getActivities({ headRecommend: 1 }).then((res) =>
  102. res.data.list.map((it) => ({ type: 'activity', data: it, startAt: it.applyStartTime })),
  103. ),
  104. ])
  105. hotActivities.value = sort(res.flat(), (it) => it.startAt) as any
  106. }
  107. const handlePlay = async (id) => {
  108. const body = pick(swiperData.value?.find((it) => it.data.id === id).data, ['id', 'status'])
  109. autoplay.value = false
  110. await updateSetIndexConfig(body)
  111. }
  112. const handleShare = (options: any) => {
  113. shareOptions.value = options
  114. shareActionState.value = true
  115. }
  116. const dictMemberDesignStyle = ref<[]>()
  117. const dictCircleSpaceType = ref<[]>()
  118. const offLoad = (e) => {
  119. console.log(e)
  120. }
  121. const offError = (err) => {
  122. console.log(err)
  123. }
  124. onShow(async () => {
  125. await pageHelperRef.value?.reload()
  126. const reqs = [setHotActivities()]
  127. if (isLogined.value) {
  128. reqs.push(setStudyTours())
  129. }
  130. await Promise.all(reqs)
  131. if (userInfo.value.level != null) {
  132. const { data: badgeData } = await getBadges({})
  133. const { data: certificates } = await getCertificates()
  134. const badges = Object.values(badgeData)
  135. .flat()
  136. .filter((it) => !it.popUp && it.quantity)
  137. const honors = [
  138. ...badges.map((it) => ({
  139. type: HonorDialogType.Badge,
  140. id: it.id,
  141. title: it.badgeName,
  142. content: it.badgeDescription,
  143. image: it.badgeYesObtainedImage,
  144. })),
  145. ...certificates
  146. .filter((it) => !it.popUp)
  147. .map((it) => ({
  148. type: HonorDialogType.Certificate,
  149. id: it.id,
  150. title: it.certificateName,
  151. content: it.certificateDescription,
  152. image: it.certificateImage,
  153. })),
  154. ]
  155. if (honors.length) {
  156. const honor = honors[0]
  157. console.log(honor)
  158. await show({
  159. title: honor.title ?? ' ',
  160. content: honor.content ?? ' ',
  161. image: honor.image,
  162. type: honor.type,
  163. onLoad: async () => {
  164. await updateHonorPopUp({
  165. bizId: String(honor.id),
  166. bizType: honor.type === HonorDialogType.Badge ? '1' : '2',
  167. })
  168. },
  169. })
  170. }
  171. }
  172. })
  173. onLoad(async () => {
  174. await Promise.all([setIndexConfigsData()])
  175. swiperData.value = indexConfigsData.value.list.map((it) => ({
  176. data: it,
  177. }))
  178. let value1 = await getByDictType(DictType.circleSpaceType)
  179. dictCircleSpaceType.value = value1.data
  180. let value2 = await getByDictType(DictType.memberDesignStyle)
  181. dictMemberDesignStyle.value = value2.data
  182. })
  183. onHide(() => {
  184. // autoplay.value = true
  185. homeBannerRef.value?.[swiperCurrent.value]?.videoContext.pause()
  186. })
  187. onShareAppMessage(shareAppMessage)
  188. // onShareTimeline(async ({from, target}) => {
  189. // const res: Page.ShareTimelineContent = {}
  190. // if (from === 'button') {
  191. // // await shareCircle(target.dataset.options.id)
  192. // // res.path = `/pages-sub/home/moment/index?id=${target.dataset.options.id}&isShared=true`
  193. // }
  194. // return res
  195. // })
  196. </script>
  197. <template>
  198. <view class="">
  199. <!-- <view class="official">
  200. <official-account @load="offLoad" @error="offError"></official-account>
  201. </view> -->
  202. <view class="bg-black w-full relative aspect-[1.26/1]">
  203. <swiper :autoplay="autoplay" @change="handleSwiperChange">
  204. <template
  205. v-for="{ data: { id, coverVideoImage, indexPromotionalVideoImage } } of swiperData"
  206. :key="id"
  207. >
  208. <swiper-item>
  209. <HomeBanner
  210. ref="homeBannerRef"
  211. :id="id"
  212. :url="indexPromotionalVideoImage"
  213. :cover="coverVideoImage"
  214. @play="handlePlay"
  215. @ended="autoplay = true"
  216. />
  217. </swiper-item>
  218. </template>
  219. </swiper>
  220. <div class="absolute flex gap-1 dots">
  221. <template v-for="(it, i) in swiperData" :key="i">
  222. <div
  223. class="w-1 h-1 rounded-full"
  224. :class="`${currentBanner === i ? 'bg-white bg-active' : 'bg-white/40'}`"
  225. ></div>
  226. </template>
  227. </div>
  228. </view>
  229. <view class="bg-[#f6f6f6] relative bottom-4 rounded-t-2xl py-1">
  230. <template v-if="currentStudyTour">
  231. <ScheduleCard
  232. custom-class="my-6 mx-3.5"
  233. :items="currentStudyTour.studyTravelDOList"
  234. ></ScheduleCard>
  235. </template>
  236. <menus></menus>
  237. <view v-if="hotActivities?.length" class="my-6 mx-3.5">
  238. <HotActivity :items="hotActivities"></HotActivity>
  239. </view>
  240. <view v-if="features.about" class="my-6 mx-3.5" @click="toAbout()">
  241. <Card>
  242. <div class="flex items-center gap-2">
  243. <wd-img width="28" height="28" :src="logo"></wd-img>
  244. <div class="text-black/90 text-base font-normal font-['PingFang_SC'] leading-[10.18px]">
  245. 1分钟快速了解筑巢荟
  246. </div>
  247. <div class="flex-1"></div>
  248. <wd-icon name="help-circle" size="22px" custom-class="text-black/60"></wd-icon>
  249. </div>
  250. </Card>
  251. </view>
  252. <view class="mx-3.5 text-5 font-bold">设计圈</view>
  253. <view class="mx-3.5">
  254. <PageHelperEvo ref="pageHelperRef" :request="getCircles" class="">
  255. <template #default="{ source }">
  256. <template v-for="it of source.list" :key="it.id">
  257. <view class="my-3">
  258. <MomentItem
  259. :dict="{ spaceType: dictCircleSpaceType, designStyle: dictMemberDesignStyle }"
  260. :options="it"
  261. @like="handleLike"
  262. @share="handleShare"
  263. ></MomentItem>
  264. </view>
  265. </template>
  266. </template>
  267. </PageHelperEvo>
  268. </view>
  269. </view>
  270. <ShareActionSheet
  271. ref="shareRef"
  272. v-model="shareActionState"
  273. :options="shareOptions"
  274. ></ShareActionSheet>
  275. </view>
  276. </template>
  277. <style scoped>
  278. .dots {
  279. bottom: 60rpx;
  280. left: 50%;
  281. transform: translateX(-50%);
  282. }
  283. .bg-active {
  284. width: 40rpx;
  285. }
  286. .official {
  287. position: fixed;
  288. top: 100rpx;
  289. left: 0;
  290. width: 100%;
  291. z-index: 9999;
  292. }
  293. :deep(.level-circle) {
  294. top: -10rpx;
  295. left: -5rpx;
  296. }
  297. </style>