index.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. <template>
  2. <view class="page-analysis" :class="themeClass">
  3. <!-- ========== Section 1: My Analysis Records ========== -->
  4. <view class="section">
  5. <view class="section__header">
  6. <text class="section__title">📋 我的分析记录</text>
  7. </view>
  8. <!-- Empty state -->
  9. <view v-if="chartStore.records.length === 0" class="empty-state">
  10. <text class="empty-icon">📋</text>
  11. <text class="empty-text">暂无分析记录</text>
  12. <text class="empty-desc">去首页分析您的手机号码</text>
  13. </view>
  14. <!-- Records list -->
  15. <view v-else class="records-list">
  16. <view
  17. v-for="record in chartStore.records"
  18. :key="record.id"
  19. class="record-card"
  20. @click="viewDetail(record)"
  21. >
  22. <view class="record-header">
  23. <text class="record-name">{{ record.name || record.birthday || '未知' }}</text>
  24. <text class="record-date">{{ formatDate(record.createdAt) }}</text>
  25. </view>
  26. <text class="record-phone">{{ record.birthday || '' }}</text>
  27. <view class="record-tags">
  28. <text v-if="record.difyResponse" class="tag-status">已解读</text>
  29. </view>
  30. </view>
  31. </view>
  32. </view>
  33. <!-- ========== Divider: Super Member Zone ========== -->
  34. <view class="section-divider">
  35. <text class="section-divider__line"></text>
  36. <text class="section-divider__text">超级会员专属</text>
  37. <text class="section-divider__line"></text>
  38. </view>
  39. <!-- ========== Section 2: Relationship Analysis ========== -->
  40. <view
  41. class="feature-card"
  42. :class="{ 'feature-card--locked': !isSuperMember }"
  43. @click="goRelations"
  44. >
  45. <view class="feature-card__icon">
  46. <text>🔗</text>
  47. </view>
  48. <view class="feature-card__body">
  49. <text class="feature-card__title">关系能量分析</text>
  50. <text class="feature-card__desc">夫妻/亲子/同事等关系合盘对比分析</text>
  51. </view>
  52. <text v-if="!isSuperMember" class="feature-card__lock">🔒</text>
  53. <text v-else class="feature-card__arrow">›</text>
  54. </view>
  55. <!-- ========== Section 3: Number Analysis ========== -->
  56. <view
  57. class="feature-card"
  58. :class="{ 'feature-card--locked': !isSuperMember }"
  59. @click="goNumberAnalysis"
  60. >
  61. <view class="feature-card__icon">
  62. <text>🔢</text>
  63. </view>
  64. <view class="feature-card__body">
  65. <text class="feature-card__title">八星号码分析</text>
  66. <text class="feature-card__desc">手机号/车牌号等数字能量解读</text>
  67. </view>
  68. <text v-if="!isSuperMember" class="feature-card__lock">🔒</text>
  69. <text v-else class="feature-card__arrow">›</text>
  70. </view>
  71. <!-- ========== Upsell Banner (non-super logged-in users) ========== -->
  72. <view v-if="!isSuperMember && userStore.isLoggedIn" class="upsell-banner">
  73. <view class="upsell-banner__content">
  74. <text class="upsell-banner__title">开通超级会员 ¥1,314/年</text>
  75. <text class="upsell-banner__desc">解锁关系能量分析 + 八星号码解读</text>
  76. </view>
  77. <button class="upsell-banner__btn" @click="goSubscribe">立即开通</button>
  78. </view>
  79. </view>
  80. </template>
  81. <script setup>
  82. import { computed } from 'vue'
  83. import { onShow } from '@dcloudio/uni-app'
  84. import { useUserStore } from '@/stores/user'
  85. import { useChartStore } from '@/stores/chart'
  86. import { useTheme } from '@/composables/useThemeStyle'
  87. const { themeClass } = useTheme()
  88. const userStore = useUserStore()
  89. const chartStore = useChartStore()
  90. // Super member check: practitioner (能量师) or super_annual tier
  91. const isSuperMember = computed(() => {
  92. if (!userStore.isVip) return false
  93. return userStore.vipType === 'super_annual' || userStore.vipType === 'practitioner'
  94. })
  95. onShow(() => {
  96. // Fetch user profile for up-to-date VIP status
  97. if (userStore.isLoggedIn) {
  98. userStore.fetchProfile()
  99. }
  100. // Fetch records list
  101. chartStore.fetchRecords()
  102. })
  103. function formatDate(dateStr) {
  104. if (!dateStr) return ''
  105. return dateStr.slice(0, 10)
  106. }
  107. function viewDetail(record) {
  108. chartStore.loadChart(record.id).then(() => {
  109. uni.navigateTo({ url: '/pages/chart/index' })
  110. })
  111. }
  112. function goRelations() {
  113. if (isSuperMember.value) {
  114. uni.navigateTo({ url: '/pages/relations/index' })
  115. } else {
  116. uni.showToast({ title: '开通超级会员解锁', icon: 'none' })
  117. }
  118. }
  119. function goNumberAnalysis() {
  120. if (isSuperMember.value) {
  121. uni.navigateTo({ url: '/pages/relations/number-analysis' })
  122. } else {
  123. uni.showToast({ title: '开通超级会员解锁', icon: 'none' })
  124. }
  125. }
  126. function goSubscribe() {
  127. uni.navigateTo({ url: '/pages/payment/index?product=super_member' })
  128. }
  129. </script>
  130. <style scoped lang="scss">
  131. $warm-white: var(--color-bg);
  132. $white: var(--color-bg-card);
  133. $text-primary: var(--color-text-primary);
  134. $text-secondary: var(--color-text-secondary);
  135. $text-muted: var(--color-text-tertiary);
  136. $blue-dark: var(--color-brand);
  137. $gold: var(--color-gold);
  138. $gold-light: var(--color-gold-light);
  139. $border-light: var(--color-border);
  140. $shadow-card: var(--shadow-card);
  141. .page-analysis {
  142. padding: 16px;
  143. min-height: 100vh;
  144. background: $warm-white;
  145. }
  146. // ========== Section ==========
  147. .section {
  148. background: $white;
  149. border-radius: 16px;
  150. padding: 20px 16px;
  151. margin-bottom: 16px;
  152. box-shadow: $shadow-card;
  153. &__header {
  154. margin-bottom: 12px;
  155. }
  156. &__title {
  157. font-size: 17px;
  158. font-weight: 600;
  159. color: $text-primary;
  160. }
  161. }
  162. // ========== Empty State ==========
  163. .empty-state {
  164. display: flex;
  165. flex-direction: column;
  166. align-items: center;
  167. padding: 24px 0;
  168. }
  169. .empty-icon {
  170. font-size: 40px;
  171. margin-bottom: 10px;
  172. }
  173. .empty-text {
  174. font-size: 15px;
  175. color: $text-secondary;
  176. margin-bottom: 4px;
  177. }
  178. .empty-desc {
  179. font-size: 13px;
  180. color: $text-muted;
  181. }
  182. // ========== Records List ==========
  183. .records-list {
  184. display: flex;
  185. flex-direction: column;
  186. gap: 10px;
  187. }
  188. .record-card {
  189. background: $warm-white;
  190. border-radius: 12px;
  191. padding: 14px;
  192. cursor: pointer;
  193. transition: background 0.15s;
  194. &:active {
  195. background: var(--color-bg-card-hover);
  196. }
  197. }
  198. .record-header {
  199. display: flex;
  200. justify-content: space-between;
  201. margin-bottom: 4px;
  202. }
  203. .record-name {
  204. font-weight: 600;
  205. font-size: 15px;
  206. color: $text-primary;
  207. }
  208. .record-date {
  209. font-size: 12px;
  210. color: $text-muted;
  211. }
  212. .record-phone {
  213. font-size: 18px;
  214. color: $blue-dark;
  215. font-weight: 500;
  216. }
  217. .tag-status {
  218. display: inline-block;
  219. background: var(--color-success-bg);
  220. color: var(--color-success);
  221. font-size: 11px;
  222. padding: 2px 8px;
  223. border-radius: 10px;
  224. margin-top: 8px;
  225. }
  226. // ========== Section Divider ==========
  227. .section-divider {
  228. display: flex;
  229. align-items: center;
  230. gap: 12px;
  231. margin: 20px 0;
  232. padding: 0 4px;
  233. &__line {
  234. flex: 1;
  235. height: 1px;
  236. background: $border-light;
  237. }
  238. &__text {
  239. font-size: 12px;
  240. font-weight: 600;
  241. color: $gold;
  242. white-space: nowrap;
  243. letter-spacing: 1px;
  244. }
  245. }
  246. // ========== Feature Cards ==========
  247. .feature-card {
  248. display: flex;
  249. align-items: center;
  250. gap: 14px;
  251. background: $white;
  252. border-radius: 16px;
  253. padding: 18px 16px;
  254. margin-bottom: 12px;
  255. box-shadow: $shadow-card;
  256. cursor: pointer;
  257. transition: all 0.15s;
  258. &:active {
  259. transform: scale(0.98);
  260. }
  261. &--locked {
  262. opacity: 0.55;
  263. }
  264. &__icon {
  265. width: 48px;
  266. height: 48px;
  267. border-radius: 12px;
  268. background: $gold-light;
  269. display: flex;
  270. align-items: center;
  271. justify-content: center;
  272. font-size: 24px;
  273. flex-shrink: 0;
  274. }
  275. &__body {
  276. flex: 1;
  277. min-width: 0;
  278. }
  279. &__title {
  280. font-size: 16px;
  281. font-weight: 600;
  282. color: $text-primary;
  283. display: block;
  284. margin-bottom: 2px;
  285. }
  286. &__desc {
  287. font-size: 13px;
  288. color: $text-secondary;
  289. display: block;
  290. }
  291. &__lock {
  292. font-size: 20px;
  293. flex-shrink: 0;
  294. }
  295. &__arrow {
  296. font-size: 22px;
  297. color: $text-muted;
  298. flex-shrink: 0;
  299. }
  300. }
  301. // ========== Upsell Banner ==========
  302. .upsell-banner {
  303. background: linear-gradient(135deg, var(--color-brand), #132944);
  304. border-radius: 16px;
  305. padding: 20px;
  306. margin-top: 8px;
  307. display: flex;
  308. align-items: center;
  309. justify-content: space-between;
  310. gap: 12px;
  311. &__content {
  312. flex: 1;
  313. }
  314. &__title {
  315. font-size: 16px;
  316. font-weight: 700;
  317. color: $gold;
  318. display: block;
  319. margin-bottom: 4px;
  320. }
  321. &__desc {
  322. font-size: 12px;
  323. color: var(--color-text-secondary);
  324. display: block;
  325. }
  326. &__btn {
  327. flex-shrink: 0;
  328. background: $gold;
  329. color: $blue-dark;
  330. font-size: 14px;
  331. font-weight: 700;
  332. border: none;
  333. border-radius: 10px;
  334. padding: 10px 18px;
  335. min-height: 44px;
  336. line-height: 1;
  337. cursor: pointer;
  338. transition: all 0.15s;
  339. &:active {
  340. transform: scale(0.97);
  341. opacity: 0.9;
  342. }
  343. }
  344. }
  345. </style>