Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

216 рядки
5.1 KiB

  1. <template>
  2. <view class="landing-page">
  3. <view class="invite-card">
  4. <view class="avatar-wrap">
  5. <image v-if="inviter.avatar" class="avatar" :src="inviter.avatar" mode="aspectFill" />
  6. <text v-else class="avatar-text">AI</text>
  7. </view>
  8. <text class="from-text">{{ inviter.user_name || '你的好友' }} 邀请你</text>
  9. <text class="title">{{ config.landing_title || '一起体验 AI ONLINE' }}</text>
  10. <text class="desc">{{ config.landing_desc || '登录后领取点数奖励,AI取名、动漫头像、证件照等功能都能使用。' }}</text>
  11. <view class="gift-row">
  12. <view class="gift-item">
  13. <text class="gift-num">{{ config.invitee_points || 0 }}</text>
  14. <text class="gift-label">你可领取点数</text>
  15. </view>
  16. <view class="gift-item">
  17. <text class="gift-num">{{ config.inviter_points || 0 }}</text>
  18. <text class="gift-label">好友获得点数</text>
  19. </view>
  20. </view>
  21. <view class="claim-btn" @click="claim">{{ userStore.isLogin ? '进入 AI ONLINE' : '登录并领取' }}</view>
  22. <text class="rule-text">{{ config.rule_text || '好友通过你的邀请登录注册成功后,你和好友都可获得点数奖励。' }}</text>
  23. </view>
  24. </view>
  25. </template>
  26. <script setup>
  27. import { ref } from 'vue'
  28. import { onLoad, onShareAppMessage } from '@dcloudio/uni-app'
  29. import { inviteApi } from '@/api/index.js'
  30. import { useUserStore } from '@/store/user.js'
  31. import { useApp } from '@/utils/useApp.js'
  32. const userStore = useUserStore()
  33. const { toast } = useApp()
  34. const config = ref({})
  35. const inviter = ref({})
  36. const inviteCode = ref('')
  37. onLoad((options = {}) => {
  38. userStore.restore()
  39. inviteCode.value = options.invite_code || options.invite_open_id || parseInviteScene(options.scene || '')
  40. if (inviteCode.value) uni.setStorageSync('invite_code', inviteCode.value)
  41. if (options.source) uni.setStorageSync('source', options.source)
  42. loadConfig(options.scene || '')
  43. })
  44. onShareAppMessage(() => ({
  45. title: config.value.share_title || '邀请你体验 AI ONLINE',
  46. path: `/pages/invite/landing?invite_code=${inviteCode.value}`,
  47. imageUrl: config.value.share_image || ''
  48. }))
  49. function parseInviteScene(scene) {
  50. if (!scene) return ''
  51. let text = ''
  52. try {
  53. text = decodeURIComponent(scene)
  54. } catch (e) {
  55. text = scene
  56. }
  57. const match = text.match(/(?:^|[&?])i=([^&]+)/) || text.match(/(?:^|[&?])i_([^&]+)/)
  58. if (match) return match[1]
  59. if (text.indexOf('i_') === 0) return text.slice(2)
  60. return text
  61. }
  62. async function loadConfig(scene) {
  63. try {
  64. const res = await inviteApi.getConfig({
  65. invite_code: inviteCode.value,
  66. scene
  67. })
  68. config.value = res.data.config || {}
  69. inviter.value = res.data.inviter || {}
  70. if (res.data.invite_code) {
  71. inviteCode.value = res.data.invite_code
  72. uni.setStorageSync('invite_code', inviteCode.value)
  73. }
  74. } catch (e) {
  75. toast(e.msg || '邀请信息加载失败')
  76. }
  77. }
  78. function claim() {
  79. if (userStore.isLogin) {
  80. uni.switchTab({ url: '/pages/toolbox/toolbox' })
  81. return
  82. }
  83. uni.navigateTo({ url: '/pages/login/login' })
  84. }
  85. </script>
  86. <style lang="scss" scoped>
  87. .landing-page {
  88. min-height: 100vh;
  89. padding: 44rpx 24rpx;
  90. background: linear-gradient(180deg, #eef7ff 0%, #f7f9fc 42%, #ffffff 100%);
  91. box-sizing: border-box;
  92. }
  93. .invite-card {
  94. min-height: calc(100vh - 88rpx);
  95. padding: 52rpx 34rpx 38rpx;
  96. border-radius: 34rpx;
  97. background: #ffffff;
  98. box-shadow: 0 20rpx 50rpx rgba(30, 66, 120, 0.08);
  99. display: flex;
  100. flex-direction: column;
  101. align-items: center;
  102. box-sizing: border-box;
  103. }
  104. .avatar-wrap {
  105. width: 124rpx;
  106. height: 124rpx;
  107. border-radius: 50%;
  108. background: #eaf5ff;
  109. box-shadow: 0 10rpx 24rpx rgba(11, 140, 255, 0.18);
  110. display: flex;
  111. align-items: center;
  112. justify-content: center;
  113. overflow: hidden;
  114. }
  115. .avatar {
  116. width: 100%;
  117. height: 100%;
  118. }
  119. .avatar-text {
  120. color: #0b8cff;
  121. font-size: 34rpx;
  122. font-weight: 900;
  123. }
  124. .from-text {
  125. margin-top: 24rpx;
  126. color: #7f8896;
  127. font-size: 25rpx;
  128. line-height: 1;
  129. }
  130. .title {
  131. margin-top: 28rpx;
  132. color: #172033;
  133. font-size: 46rpx;
  134. font-weight: 900;
  135. line-height: 1.2;
  136. text-align: center;
  137. }
  138. .desc {
  139. margin-top: 20rpx;
  140. color: #687386;
  141. font-size: 28rpx;
  142. line-height: 1.55;
  143. text-align: center;
  144. }
  145. .gift-row {
  146. width: 100%;
  147. margin-top: 42rpx;
  148. display: grid;
  149. grid-template-columns: repeat(2, minmax(0, 1fr));
  150. gap: 18rpx;
  151. }
  152. .gift-item {
  153. height: 176rpx;
  154. border-radius: 26rpx;
  155. background: #f3f9ff;
  156. display: flex;
  157. flex-direction: column;
  158. align-items: center;
  159. justify-content: center;
  160. }
  161. .gift-num {
  162. color: #0b8cff;
  163. font-size: 54rpx;
  164. font-weight: 900;
  165. line-height: 1;
  166. }
  167. .gift-label {
  168. margin-top: 14rpx;
  169. color: #687386;
  170. font-size: 23rpx;
  171. }
  172. .claim-btn {
  173. width: 100%;
  174. height: 92rpx;
  175. margin-top: auto;
  176. border-radius: 999rpx;
  177. color: #ffffff;
  178. background: #0b8cff;
  179. font-size: 30rpx;
  180. font-weight: 900;
  181. line-height: 92rpx;
  182. text-align: center;
  183. box-shadow: 0 14rpx 28rpx rgba(11, 140, 255, 0.22);
  184. }
  185. .rule-text {
  186. margin-top: 22rpx;
  187. color: #9aa4b2;
  188. font-size: 23rpx;
  189. line-height: 1.45;
  190. text-align: center;
  191. }
  192. </style>