Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

200 строки
4.5 KiB

  1. <template>
  2. <view class="page">
  3. <!-- 顶部横幅 -->
  4. <image class="banner" src="/static/img/index-bg.png" mode="widthFix" />
  5. <!-- 正文内容区域 -->
  6. <view class="content-area">
  7. <view v-if="content" class="letter">
  8. <view class="body">
  9. <mp-html :content="content" />
  10. </view>
  11. </view>
  12. <view v-else-if="!loaded" class="loading-text">加载中...</view>
  13. <button class="join-btn" :class="btnClass" @tap="handleJoin">{{ btnText }}</button>
  14. </view>
  15. </view>
  16. </template>
  17. <script setup>
  18. import { ref, computed } from 'vue'
  19. import { onShow } from '@dcloudio/uni-app'
  20. import { get } from '@/utils/request.js'
  21. import { getToken, getUserInfo, setUserInfo } from '@/utils/cache.js'
  22. import mpHtml from '@/uni_modules/mp-html/components/mp-html/mp-html.vue'
  23. const CONTENT_CACHE_KEY = 'cytx-index-content'
  24. const content = ref('')
  25. const loaded = ref(false)
  26. const userInfo = ref(null)
  27. const isLoggedIn = ref(false)
  28. // 患者状态
  29. const patient = computed(() => userInfo.value?.patient || null)
  30. const isAuthed = computed(() => patient.value?.auth_status === 1)
  31. const patientStatus = computed(() => patient.value?.status ?? null)
  32. // 按钮文案和样式
  33. const btnText = computed(() => {
  34. if (!isLoggedIn.value) return '加入项目'
  35. if (!isAuthed.value) return '加入项目'
  36. if (patientStatus.value === null || patientStatus.value === undefined) return '加入项目'
  37. if (patientStatus.value === -1) return '加入项目'
  38. if (patientStatus.value === 1) return '已加入'
  39. if (patientStatus.value === 0) return '已申请,审核中'
  40. if (patientStatus.value === 2) return '已拒绝,重新申请'
  41. return '加入项目'
  42. })
  43. const btnClass = computed(() => {
  44. if (!isLoggedIn.value || !isAuthed.value) return ''
  45. if (patientStatus.value === 1) return 'joined'
  46. if (patientStatus.value === 0) return 'pending'
  47. if (patientStatus.value === 2) return 'rejected'
  48. return ''
  49. })
  50. const loadContent = async () => {
  51. // 先读缓存
  52. try {
  53. const cached = uni.getStorageSync(CONTENT_CACHE_KEY)
  54. if (cached) content.value = cached
  55. } catch (e) {}
  56. // 再请求最新
  57. try {
  58. const res = await get('/api/content', { key: 'index_content' })
  59. if (res.data && res.data.content) {
  60. content.value = res.data.content
  61. uni.setStorageSync(CONTENT_CACHE_KEY, res.data.content)
  62. }
  63. } catch (e) {
  64. console.error('获取首页内容失败', e)
  65. } finally {
  66. loaded.value = true
  67. }
  68. }
  69. const refreshUserState = async () => {
  70. isLoggedIn.value = !!getToken()
  71. userInfo.value = getUserInfo()
  72. if (isLoggedIn.value) {
  73. try {
  74. const res = await get('/api/mp/userinfo')
  75. userInfo.value = res.data
  76. setUserInfo(res.data)
  77. } catch (e) {}
  78. }
  79. }
  80. const handleJoin = () => {
  81. // 未登录 -> 去登录
  82. if (!isLoggedIn.value) {
  83. uni.navigateTo({ url: '/pages/login/index' })
  84. return
  85. }
  86. // 未实名 -> 提示去认证
  87. if (!isAuthed.value) {
  88. uni.showModal({
  89. title: '提示',
  90. content: '请先完成实名认证',
  91. confirmText: '去认证',
  92. success: (res) => {
  93. if (res.confirm) uni.navigateTo({ url: '/pages/verify/verify' })
  94. }
  95. })
  96. return
  97. }
  98. // 审核通过 -> 不可点击
  99. if (patientStatus.value === 1) return
  100. // 待审核 -> 不可点击
  101. if (patientStatus.value === 0) return
  102. // 已拒绝 -> 跳转资料页重新提交
  103. if (patientStatus.value === 2) {
  104. uni.navigateTo({ url: '/pages/myinfo/myinfo' })
  105. return
  106. }
  107. // 已实名但未提交资料(status=-1)-> 去提交资料
  108. uni.navigateTo({ url: '/pages/myinfo/myinfo' })
  109. }
  110. onShow(() => {
  111. if (!loaded.value) loadContent()
  112. refreshUserState()
  113. })
  114. </script>
  115. <style lang="scss" scoped>
  116. .page {
  117. min-height: 100vh;
  118. background: #f0f0f0;
  119. padding-bottom: 20rpx;
  120. }
  121. .banner {
  122. width: 100%;
  123. display: block;
  124. }
  125. .content-area {
  126. background: #fff;
  127. border-radius: 24rpx;
  128. margin: -30% 24rpx 0;
  129. position: relative;
  130. z-index: 1;
  131. padding: 56rpx 40rpx 32rpx;
  132. }
  133. .letter {
  134. .body {
  135. font-size: 28rpx;
  136. color: #606266;
  137. line-height: 2;
  138. text-align: justify;
  139. }
  140. }
  141. .loading-text {
  142. text-align: center;
  143. color: #909399;
  144. padding: 40rpx 0;
  145. }
  146. .join-btn {
  147. display: block;
  148. width: 100%;
  149. margin: 56rpx 0 0;
  150. height: 88rpx;
  151. line-height: 88rpx;
  152. background: linear-gradient(135deg, #1890ff, #096dd9);
  153. color: #fff;
  154. border: none;
  155. border-radius: 44rpx;
  156. font-size: 32rpx;
  157. font-weight: 500;
  158. letter-spacing: 4rpx;
  159. padding: 0;
  160. &::after {
  161. border: none;
  162. }
  163. &.joined {
  164. background: #d9d9d9;
  165. color: #999;
  166. letter-spacing: 2rpx;
  167. }
  168. &.pending {
  169. background: linear-gradient(135deg, #faad14, #d48806);
  170. letter-spacing: 2rpx;
  171. }
  172. &.rejected {
  173. background: linear-gradient(135deg, #ff4d4f, #cf1322);
  174. letter-spacing: 2rpx;
  175. }
  176. }
  177. </style>