home.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. <script lang="ts" setup>
  2. import { useRequest } from 'alova/client'
  3. import { storeToRefs } from 'pinia'
  4. import { ref, watch } from 'vue'
  5. import { getAccountCount, getCouponDetail, getCouponSituation, getShareInfo } from '@/api/home'
  6. import DiscountCoupon from '@/components/DiscountCoupon.vue'
  7. import SpendAndSaveCoupon from '@/components/SpendAndSaveCoupon.vue'
  8. import { useShare } from '@/hooks/useShare'
  9. import { useCouponStore } from '@/store/coupon'
  10. import { useTokenStore } from '@/store/token'
  11. import { getImageUrl } from '@/utils/imageUtil'
  12. import { toLoginPage } from '@/utils/toLoginPage'
  13. defineOptions({
  14. name: 'Home',
  15. })
  16. definePage({
  17. // 使用 type: "home" 属性设置首页,其他页面不需要设置,默认为page
  18. type: 'home',
  19. style: {
  20. // 'custom' 表示开启自定义导航栏,默认 'default'
  21. navigationStyle: 'custom',
  22. navigationBarTitleText: '首页',
  23. },
  24. })
  25. const tokenStore = useTokenStore()
  26. const { hasLogin } = storeToRefs(tokenStore)
  27. const refreshing = ref(false)
  28. // 分享配置
  29. const shareConfig = ref(null)
  30. const shareButtonRef = ref<HTMLElement | null>(null)
  31. const currentCouponId = ref<string>('')
  32. // 获取优惠券
  33. const couponStore = useCouponStore()
  34. const { couponList, discountVoucherList, loading } = storeToRefs(couponStore)
  35. // 获取首页收益
  36. const { send: getAccountCountRequest, data: accountCountData } = useRequest(getAccountCount, {
  37. immediate: false,
  38. dependencies: [],
  39. })
  40. // 获取首页领券情况数据
  41. const { send: getCouponSituationRequest, data: couponSituationData } = useRequest(getCouponSituation, {
  42. immediate: false,
  43. dependencies: [],
  44. })
  45. // onLoad(async () => {
  46. // // 获取优惠券
  47. // couponStore.getCouponListByType()
  48. // })
  49. onShow((options) => {
  50. couponStore.getCouponListByType()
  51. // 登录后查询收益数据
  52. if (hasLogin) {
  53. Promise.allSettled([getAccountCountRequest(), getCouponSituationRequest()])
  54. // getAccountCountRequest()
  55. // getCouponSituationRequest()
  56. }
  57. })
  58. // #ifdef MP-WEIXIN
  59. async function login() {
  60. const currentPage = getCurrentPages()[0]
  61. const redirectUrl = `/${currentPage.route}`
  62. toLoginPage({ queryString: `?redirect=${encodeURIComponent(redirectUrl)}` })
  63. }
  64. // #endif
  65. // 顶部导航栏高度,设置banner位置
  66. const navigationBarHeight = ref(0)
  67. // #ifdef MP-WEIXIN
  68. function getNavigationBarHeight() {
  69. uni.getSystemInfo({
  70. success: (res) => {
  71. const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
  72. console.log('顶部导航栏高度:', res.statusBarHeight, menuButtonInfo)
  73. // 顶部导航栏高度 = 状态栏高度 + 胶囊的高度
  74. navigationBarHeight.value = res.statusBarHeight + menuButtonInfo.height + 12
  75. },
  76. })
  77. }
  78. getNavigationBarHeight()
  79. // #endif
  80. function toDiscountCouponList() {
  81. uni.navigateTo({
  82. url: '/pages-A/discountcouponList/index?type=2',
  83. })
  84. }
  85. function toSpendAndSaveCouponList() {
  86. uni.navigateTo({
  87. url: '/pages-A/spendAndSaveCouponList/index?type=3',
  88. })
  89. }
  90. function toCouponRedemptionList(state) {
  91. uni.navigateTo({
  92. url: `/pages-A/couponRedemptionList/index?state=${state}`,
  93. })
  94. }
  95. // 创建分享hook实例
  96. const { getShareConfig } = useShare()
  97. const { getShareConfig: getShareCouponConfig } = useShare({
  98. shareType: 'COUPON',
  99. imageSource: 'REMOTE',
  100. path: '/pages/receiveCoupon/index',
  101. pathParamKey: 'couponShareRecordId',
  102. })
  103. // #ifdef MP-WEIXIN
  104. // 分享功能实现
  105. // 分享生命周期函数
  106. onShareAppMessage(async (options) => {
  107. console.log(options)
  108. if (options.from === 'button' && options.target.dataset.shareType === 'coupon') {
  109. const couponId = options.target.dataset.couponId
  110. const couponinfo = await getCouponDetail({ templateId: couponId })
  111. return await getShareCouponConfig({
  112. imageUrl: couponinfo?.imageUrl,
  113. }, {
  114. shareContentId: couponId,
  115. })
  116. }
  117. else {
  118. return await getShareConfig()
  119. }
  120. })
  121. // #endif
  122. async function onRefresh() {
  123. refreshing.value = true
  124. couponStore.getCouponListByType()
  125. setTimeout(() => {
  126. refreshing.value = false
  127. }, 1000)
  128. }
  129. </script>
  130. <template>
  131. <view class="home-container">
  132. <up-pull-refresh :refreshing="refreshing" @refresh="onRefresh">
  133. <!-- 顶部区域 -->
  134. <view class="home-header">
  135. <image class="home-header-bg" :src="getImageUrl('@img/index/index-bg.png')" mode="aspectFill" />
  136. <view class="absolute left-5 z-3 text-xl c-white" :style="{ top: `${navigationBarHeight - 39}px` }">
  137. 券中心
  138. </view>
  139. <view class="home-header-avatar-info" :style="{ paddingTop: `${navigationBarHeight + 10}px` }">
  140. <view class="home-header-balance">
  141. 我的收益(元)
  142. </view>
  143. <view class="home-header-balance-num">
  144. <view class="home-header-balance-num-amount">
  145. {{ accountCountData?.balance || 0 }}
  146. </view>
  147. </view>
  148. </view>
  149. <view class="home-header-tips">
  150. <view class="home-header-tips-item" @click="toCouponRedemptionList('2')">
  151. <view class="home-header-tips-item-num">
  152. {{ couponSituationData?.usedQuantity || 0 }}张
  153. </view>
  154. <view class="home-header-tips-item-des">
  155. 已核销
  156. </view>
  157. </view>
  158. <view class="home-header-tips-item" @click="toCouponRedemptionList('1')">
  159. <view class="home-header-tips-item-num">
  160. {{ couponSituationData?.quantityToBeUsed || 0 }}张
  161. </view>
  162. <view class="home-header-tips-item-des">
  163. 未核销
  164. </view>
  165. </view>
  166. <view class="home-header-tips-item">
  167. <view class="home-header-tips-item-num">
  168. {{ couponSituationData?.quantityForComplimentary || 0 }}张
  169. </view>
  170. <view class="home-header-tips-item-des">
  171. 已发放
  172. </view>
  173. </view>
  174. </view>
  175. <view v-if="!hasLogin" class="home-hidden" @click="login">
  176. <image class="home-hidden-img" :src="getImageUrl('@img/index/lock.png')" mode="scaleToFill" />
  177. <view class="home-hidden-text">
  178. 请登录,查看更多内容~
  179. </view>
  180. </view>
  181. </view>
  182. <!-- 满减券 -->
  183. <view class="home-header-coupon">
  184. <image class="home-header-coupon-bg" :src="getImageUrl('@img/index/coupon-bg.png')" mode="aspectFill" />
  185. <view class="home-header-coupon-title">
  186. <image class="home-header-coupon-title-icon" :src="getImageUrl('@img/index/icon1.png')"
  187. mode="scaleToFill" />
  188. <view class="home-header-coupon-title-text">
  189. 满减券
  190. </view>
  191. <view class="home-header-coupon-title-des">
  192. 平台满减&nbsp;&nbsp;乐享不停
  193. </view>
  194. </view>
  195. <view class="home-header-coupon-content">
  196. <template v-for="item in couponList" :key="item.id">
  197. <spend-and-save-coupon :coupon="item" />
  198. </template>
  199. <up-loading-icon v-if="loading" class="coupon-content-loading" text="加载中" text-size="18" />
  200. </view>
  201. <view class="home-header-coupon-btn">
  202. <up-button class="home-header-coupon-btn-text" text="查看更多优惠券"
  203. color="linear-gradient(0deg, #FFE8CE 0%, #FBB8A0 100%)" @click="toSpendAndSaveCouponList" />
  204. </view>
  205. </view>
  206. <!-- 折扣券 -->
  207. <view class="home-coupon">
  208. <view class="home-coupon-title">
  209. <image class="home-coupon-title-icon" :src="getImageUrl('@img/index/icon2.png')"
  210. mode="scaleToFill" />
  211. <view class="home-coupon-title-text">
  212. 折扣券
  213. </view>
  214. <view class="home-coupon-title-des">
  215. 分享折扣&nbsp;&nbsp;立享优惠
  216. </view>
  217. <view class="home-coupon-title-more" @click="toDiscountCouponList">
  218. <view class="home-coupon-title-more-text">
  219. 更多
  220. </view>
  221. <up-icon size="14" name="arrow-right" />
  222. </view>
  223. </view>
  224. <view class="home-coupon-content">
  225. <template v-for="item in discountVoucherList" :key="item.id">
  226. <discount-coupon :coupon="item" />
  227. </template>
  228. <up-loading-icon v-if="loading" text="加载中" text-size="18" />
  229. </view>
  230. </view>
  231. </up-pull-refresh>
  232. </view>
  233. </template>
  234. <style lang="scss" scoped>
  235. .home-container {
  236. background-color: #f5f5f5;
  237. line-height: 1;
  238. position: relative;
  239. .home-header {
  240. height: 550rpx;
  241. position: relative;
  242. overflow: hidden;
  243. .home-header-bg {
  244. position: absolute;
  245. top: 0;
  246. left: 0;
  247. width: 125%;
  248. height: 200%;
  249. object-fit: cover;
  250. transform: translate(-80px, 0);
  251. z-index: 0;
  252. }
  253. .home-header-avatar-info {
  254. display: flex;
  255. flex-direction: column;
  256. justify-content: center;
  257. text-align: center;
  258. gap: 30rpx;
  259. padding: 0 24rpx;
  260. position: relative;
  261. z-index: 1;
  262. .home-header-balance {
  263. font-weight: 400;
  264. font-size: 26rpx;
  265. color: #ffffff;
  266. }
  267. .home-header-balance-num {
  268. display: flex;
  269. justify-content: space-between;
  270. align-items: center;
  271. .home-header-balance-num-amount {
  272. width: 100%;
  273. font-weight: 500;
  274. font-size: 65rpx;
  275. color: #ffffff;
  276. }
  277. .home-header-balance-num-btns {
  278. display: flex;
  279. gap: 15rpx;
  280. .home-header-balance-num-btn {
  281. padding: 20rpx 40rpx;
  282. border-radius: 33rpx;
  283. font-weight: 400;
  284. font-size: 26rpx;
  285. &.js {
  286. background: #da4c47;
  287. color: #ffffff;
  288. }
  289. &.tx {
  290. background: #bfbfbf;
  291. color: #747474;
  292. }
  293. }
  294. }
  295. }
  296. }
  297. .home-header-tips {
  298. height: 124rpx;
  299. display: flex;
  300. border-radius: 10rpx;
  301. margin: 47rpx 24rpx 0 24rpx;
  302. position: relative;
  303. z-index: 1;
  304. .home-header-tips-item {
  305. flex: 1;
  306. display: flex;
  307. flex-direction: column;
  308. justify-content: center;
  309. align-items: center;
  310. gap: 15rpx;
  311. font-weight: 500;
  312. font-size: 34rpx;
  313. color: #ffffff;
  314. line-height: 1;
  315. position: relative;
  316. &:not(:last-child):after {
  317. content: '';
  318. position: absolute;
  319. top: 50%;
  320. right: 0;
  321. transform: translateY(-50%);
  322. width: 2px;
  323. height: 40rpx;
  324. background: #ffffff;
  325. }
  326. .home-header-tips-item-num {
  327. font-weight: bold;
  328. }
  329. .home-header-tips-item-des {
  330. font-weight: 400;
  331. font-size: 24rpx;
  332. color: #ffffff;
  333. }
  334. }
  335. }
  336. .home-hidden {
  337. height: 550rpx;
  338. width: 100%;
  339. position: absolute;
  340. top: 0;
  341. right: 0;
  342. display: flex;
  343. flex-direction: column;
  344. justify-content: center;
  345. align-items: center;
  346. background-color: rgba(255, 255, 255, 0.1);
  347. backdrop-filter: blur(25rpx);
  348. -webkit-backdrop-filter: blur(25rpx);
  349. z-index: 2;
  350. .home-hidden-img {
  351. width: 209rpx;
  352. height: 209rpx;
  353. object-fit: cover;
  354. }
  355. .home-hidden-text {
  356. font-weight: 400;
  357. font-size: 26rpx;
  358. color: #ffffff;
  359. margin-top: -25rpx;
  360. }
  361. }
  362. }
  363. // 优惠券
  364. .home-header-coupon {
  365. position: relative;
  366. height: 419rpx;
  367. margin: -58rpx 20rpx 20rpx 20rpx;
  368. gap: 20rpx;
  369. padding: 28rpx 20rpx;
  370. overflow: hidden;
  371. z-index: 3;
  372. .home-header-coupon-bg {
  373. position: absolute;
  374. top: 0;
  375. right: 0;
  376. width: 100%;
  377. object-fit: cover;
  378. z-index: 0;
  379. }
  380. .home-header-coupon-title {
  381. display: flex;
  382. flex-direction: row;
  383. position: relative;
  384. z-index: 1;
  385. .home-header-coupon-title-icon {
  386. width: 38rpx;
  387. height: 38rpx;
  388. object-fit: cover;
  389. margin-right: 13rpx;
  390. }
  391. .home-header-coupon-title-text {
  392. font-weight: 500;
  393. font-size: 30rpx;
  394. color: #333333;
  395. margin-right: 21rpx;
  396. }
  397. .home-header-coupon-title-des {
  398. display: inline-flex;
  399. align-items: center;
  400. font-size: 24rpx;
  401. font-weight: 400;
  402. color: #888888;
  403. }
  404. }
  405. .home-header-coupon-content {
  406. height: 238rpx;
  407. display: flex;
  408. flex-direction: row;
  409. align-items: flex-end;
  410. gap: 20rpx;
  411. position: relative;
  412. z-index: 1;
  413. .hidden-share-btn {
  414. /* 隐藏按钮但保持可点击 */
  415. position: absolute;
  416. left: -9999px;
  417. top: -9999px;
  418. width: 0;
  419. height: 0;
  420. opacity: 0;
  421. pointer-events: none;
  422. }
  423. .coupon-content-loading {
  424. align-self: center;
  425. }
  426. }
  427. .home-header-coupon-btn {
  428. padding: 21rpx 70rpx 22px 69rpx;
  429. position: relative;
  430. .home-header-coupon-btn-text {
  431. box-shadow: 0rpx 4rpx 7rpx 0rpx rgba(230, 77, 13, 0.17);
  432. border-radius: 37rpx;
  433. color: #651e03 !important;
  434. font-size: 26rpx;
  435. font-weight: 400;
  436. }
  437. }
  438. }
  439. // 折扣券
  440. .home-coupon {
  441. margin: -20rpx 20rpx 20rpx 20rpx;
  442. padding: 28rpx 20rpx;
  443. background-color: #ffffff;
  444. border-radius: 15rpx;
  445. .home-coupon-title {
  446. display: flex;
  447. flex-direction: row;
  448. .home-coupon-title-icon {
  449. width: 38rpx;
  450. height: 38rpx;
  451. object-fit: cover;
  452. margin-right: 13rpx;
  453. }
  454. .home-coupon-title-text {
  455. font-weight: 500;
  456. font-size: 30rpx;
  457. color: #333333;
  458. margin-right: 21rpx;
  459. }
  460. .home-coupon-title-des {
  461. display: inline-flex;
  462. align-items: center;
  463. font-size: 24rpx;
  464. font-weight: 400;
  465. color: #888888;
  466. }
  467. .home-coupon-title-more {
  468. margin-left: auto;
  469. display: flex;
  470. flex-direction: row;
  471. align-items: center;
  472. .home-coupon-title-more-text {
  473. font-weight: 400;
  474. font-size: 24rpx;
  475. color: #666666;
  476. height: 23rpx;
  477. line-height: 23rpx;
  478. }
  479. }
  480. }
  481. .home-coupon-content {
  482. padding-top: 28rpx;
  483. display: flex;
  484. flex-direction: column;
  485. gap: 20rpx;
  486. }
  487. }
  488. }
  489. </style>