traditional-mirror.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. <template>
  2. <view class="mirror-container">
  3. <!-- 加载状态 -->
  4. <view class="loading-state" v-if="loading">
  5. <text>加载中...</text>
  6. </view>
  7. <template v-if="!loading && dataLoaded">
  8. <!-- 八字四柱 -->
  9. <view class="mirror-section">
  10. <view class="section-header">
  11. <text class="section-icon">&#x2637;</text>
  12. <text class="section-title">八字四柱</text>
  13. </view>
  14. <view class="bazi-grid" v-if="eightChars">
  15. <view class="bazi-column" v-for="(pillar, key) in eightChars" :key="key">
  16. <text class="bazi-pillar-label">{{ pillarLabel(key) }}</text>
  17. <text class="bazi-pillar-value">{{ pillar }}</text>
  18. </view>
  19. </view>
  20. <view class="empty-data" v-else>
  21. <text>暂未设置出生信息,无法排八字</text>
  22. </view>
  23. </view>
  24. <!-- 五行能量 -->
  25. <view class="mirror-section">
  26. <view class="section-header">
  27. <text class="section-icon">&#x267E;</text>
  28. <text class="section-title">五行能量</text>
  29. </view>
  30. <view class="wuxing-list" v-if="wuxingData">
  31. <view class="wuxing-item" v-for="wx in wuxingData" :key="wx.key">
  32. <text class="wuxing-label">{{ wx.label }}</text>
  33. <view class="wuxing-bar-track">
  34. <view class="wuxing-bar-fill" :style="{ width: wx.value + '%', background: wx.color }"></view>
  35. </view>
  36. <text class="wuxing-value">{{ wx.value }}%</text>
  37. </view>
  38. </view>
  39. <!-- 今日能量文 -->
  40. <view class="energy-card" v-if="wuxingReading">
  41. <view class="energy-header">
  42. <text class="energy-icon">&#x2728;</text>
  43. <text class="energy-label">今日能量文</text>
  44. </view>
  45. <view class="energy-divider"></view>
  46. <text class="energy-quote" v-if="wuxingReading.personalTrait">{{ wuxingReading.personalTrait }}</text>
  47. <text class="energy-advice" v-if="wuxingReading.mindGrowthAdvice">{{ wuxingReading.mindGrowthAdvice }}</text>
  48. <view class="energy-footer">
  49. <text class="energy-footer-dot"></text>
  50. <text class="energy-footer-dot"></text>
  51. <text class="energy-footer-dot"></text>
  52. </view>
  53. </view>
  54. </view>
  55. <!-- 太阳星座 -->
  56. <view class="mirror-section" v-if="zodiac">
  57. <view class="section-header">
  58. <text class="section-icon">&#x2600;</text>
  59. <text class="section-title">太阳星座</text>
  60. </view>
  61. <view class="zodiac-display">
  62. <text class="zodiac-name">{{ zodiac }}</text>
  63. </view>
  64. </view>
  65. <!-- 生命灵数 -->
  66. <view class="mirror-section" v-if="numSoulData">
  67. <view class="section-header">
  68. <text class="section-icon">&#x1F52E;</text>
  69. <text class="section-title">生命灵数</text>
  70. </view>
  71. <view class="numsoul-display">
  72. <view class="numsoul-number" :style="{ background: numSoulData.colorHex || '#FF6B9D' }">
  73. <text class="numsoul-num">{{ lifeNumber }}</text>
  74. </view>
  75. <view class="numsoul-info">
  76. <text class="numsoul-title">{{ numSoulData.title || '' }}</text>
  77. <text class="numsoul-keywords" v-if="numSoulData.keywords">{{ numSoulData.keywords }}</text>
  78. <text class="numsoul-advice" v-if="numSoulData.mindGrowthAdvice">{{ numSoulData.mindGrowthAdvice }}</text>
  79. </view>
  80. </view>
  81. </view>
  82. </template>
  83. <!-- 无数据 -->
  84. <view class="empty-state" v-if="!loading && !dataLoaded">
  85. <text class="empty-icon">&#x1F4AD;</text>
  86. <text class="empty-text">暂无传统文化数据</text>
  87. </view>
  88. </view>
  89. </template>
  90. <script>
  91. import config from '../../config'
  92. export default {
  93. data() {
  94. return {
  95. loading: true,
  96. dataLoaded: false,
  97. eightChars: null,
  98. wuxingElements: null,
  99. zodiac: '',
  100. lifeNumber: null,
  101. wuxingReading: null,
  102. numSoulData: null,
  103. wuxingConfig: {
  104. wood: { label: '木', color: '#10B981' },
  105. fire: { label: '火', color: '#FF6B9D' },
  106. earth: { label: '土', color: '#FF8C42' },
  107. metal: { label: '金', color: '#6366F1' },
  108. water: { label: '水', color: '#3B82F6' }
  109. },
  110. pillarLabels: { year: '年柱', month: '月柱', day: '日柱', hour: '时柱' }
  111. }
  112. },
  113. computed: {
  114. wuxingData() {
  115. if (!this.wuxingElements) return null
  116. var keys = ['wood', 'fire', 'earth', 'metal', 'water']
  117. var result = []
  118. for (var i = 0; i < keys.length; i++) {
  119. var k = keys[i]
  120. if (this.wuxingElements[k] !== undefined) {
  121. result.push({
  122. key: k,
  123. label: this.wuxingConfig[k].label,
  124. color: this.wuxingConfig[k].color,
  125. value: this.wuxingElements[k]
  126. })
  127. }
  128. }
  129. return result.sort(function(a, b) { return b.value - a.value })
  130. }
  131. },
  132. onLoad() {
  133. this.loadData()
  134. },
  135. methods: {
  136. loadData() {
  137. var self = this
  138. var memberId = this.$mp.query.memberId || uni.getStorageSync('currentChildId')
  139. if (!memberId) {
  140. // Try to get from storage
  141. memberId = uni.getStorageSync('currentChildId')
  142. }
  143. if (!memberId) {
  144. self.loading = false
  145. return
  146. }
  147. uni.request({
  148. url: config.API_BASE_URL + '/api/mind/traditional/mirror',
  149. method: 'POST',
  150. data: { memberId: memberId, memberType: 'child' },
  151. success: function(res) {
  152. self.loading = false
  153. if (res.data && res.data.code === 200 && res.data.data) {
  154. var d = res.data.data
  155. self.eightChars = d.eightCharacters || null
  156. self.wuxingElements = d.wuxingElements || null
  157. self.zodiac = d.zodiac || ''
  158. self.lifeNumber = d.lifeNumber || null
  159. self.wuxingReading = d.wuxingReading || null
  160. self.numSoulData = d.numSoul || null
  161. self.dataLoaded = !!(self.eightChars || self.wuxingElements || self.zodiac || self.numSoulData)
  162. } else {
  163. self.dataLoaded = false
  164. }
  165. },
  166. fail: function() {
  167. self.loading = false
  168. }
  169. })
  170. },
  171. pillarLabel(key) {
  172. return this.pillarLabels[key] || key
  173. }
  174. }
  175. }
  176. </script>
  177. <style scoped>
  178. .mirror-container {
  179. min-height: 100vh;
  180. background: #F8F6F0;
  181. padding-bottom: 40rpx;
  182. }
  183. .loading-state {
  184. display: flex;
  185. align-items: center;
  186. justify-content: center;
  187. padding: 120rpx 0;
  188. color: #999;
  189. font-size: 28rpx;
  190. }
  191. .mirror-section {
  192. margin: 24rpx 32rpx;
  193. background: #fff;
  194. border-radius: 20rpx;
  195. padding: 28rpx;
  196. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
  197. }
  198. .section-header {
  199. display: flex;
  200. align-items: center;
  201. margin-bottom: 20rpx;
  202. }
  203. .section-icon {
  204. font-size: 36rpx;
  205. margin-right: 12rpx;
  206. }
  207. .section-title {
  208. font-size: 30rpx;
  209. font-weight: 600;
  210. color: #333;
  211. }
  212. .bazi-grid {
  213. display: flex;
  214. justify-content: space-around;
  215. padding: 16rpx 0;
  216. }
  217. .bazi-column {
  218. display: flex;
  219. flex-direction: column;
  220. align-items: center;
  221. }
  222. .bazi-pillar-label {
  223. font-size: 22rpx;
  224. color: #999;
  225. margin-bottom: 8rpx;
  226. }
  227. .bazi-pillar-value {
  228. font-size: 36rpx;
  229. font-weight: 700;
  230. color: #8B5CF6;
  231. background: #F5F0FF;
  232. padding: 12rpx 16rpx;
  233. border-radius: 12rpx;
  234. }
  235. .wuxing-list {
  236. display: flex;
  237. flex-direction: column;
  238. gap: 16rpx;
  239. }
  240. .wuxing-item {
  241. display: flex;
  242. align-items: center;
  243. gap: 16rpx;
  244. }
  245. .wuxing-label {
  246. font-size: 28rpx;
  247. font-weight: 600;
  248. width: 48rpx;
  249. text-align: center;
  250. }
  251. .wuxing-bar-track {
  252. flex: 1;
  253. height: 24rpx;
  254. background: #F0F0F0;
  255. border-radius: 12rpx;
  256. overflow: hidden;
  257. }
  258. .wuxing-bar-fill {
  259. height: 100%;
  260. border-radius: 12rpx;
  261. }
  262. .wuxing-value {
  263. font-size: 24rpx;
  264. color: #666;
  265. width: 60rpx;
  266. text-align: right;
  267. }
  268. .energy-card {
  269. margin-top: 24rpx;
  270. padding: 28rpx 24rpx;
  271. background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
  272. border-radius: 20rpx;
  273. position: relative;
  274. overflow: hidden;
  275. }
  276. .energy-card::before {
  277. content: '';
  278. position: absolute;
  279. top: -60rpx;
  280. right: -60rpx;
  281. width: 200rpx;
  282. height: 200rpx;
  283. background: radial-gradient(circle, rgba(255,215,0,0.15) 0%, transparent 70%);
  284. border-radius: 50%;
  285. }
  286. .energy-card::after {
  287. content: '';
  288. position: absolute;
  289. bottom: -40rpx;
  290. left: -40rpx;
  291. width: 160rpx;
  292. height: 160rpx;
  293. background: radial-gradient(circle, rgba(99,102,241,0.12) 0%, transparent 70%);
  294. border-radius: 50%;
  295. }
  296. .energy-header {
  297. display: flex;
  298. align-items: center;
  299. margin-bottom: 16rpx;
  300. position: relative;
  301. z-index: 1;
  302. }
  303. .energy-icon {
  304. font-size: 36rpx;
  305. margin-right: 12rpx;
  306. }
  307. .energy-label {
  308. font-size: 30rpx;
  309. font-weight: 700;
  310. background: linear-gradient(135deg, #F59E0B, #FF6B9D);
  311. -webkit-background-clip: text;
  312. -webkit-text-fill-color: transparent;
  313. background-clip: text;
  314. }
  315. .energy-divider {
  316. height: 2rpx;
  317. background: linear-gradient(90deg, rgba(255,215,0,0.5), rgba(255,107,157,0.3), transparent);
  318. margin-bottom: 20rpx;
  319. position: relative;
  320. z-index: 1;
  321. }
  322. .energy-quote {
  323. font-size: 28rpx;
  324. color: #E2E8F0;
  325. line-height: 1.8;
  326. display: block;
  327. margin-bottom: 16rpx;
  328. position: relative;
  329. z-index: 1;
  330. padding-left: 24rpx;
  331. letter-spacing: 1rpx;
  332. }
  333. .energy-quote::before {
  334. content: '';
  335. position: absolute;
  336. left: 0;
  337. top: 4rpx;
  338. bottom: 4rpx;
  339. width: 4rpx;
  340. background: linear-gradient(180deg, #F59E0B, #FF6B9D);
  341. border-radius: 2rpx;
  342. }
  343. .energy-advice {
  344. font-size: 26rpx;
  345. color: #94A3B8;
  346. line-height: 1.7;
  347. display: block;
  348. position: relative;
  349. z-index: 1;
  350. padding: 16rpx 20rpx;
  351. background: rgba(255,255,255,0.05);
  352. border-radius: 12rpx;
  353. border: 1rpx solid rgba(255,255,255,0.08);
  354. }
  355. .energy-footer {
  356. display: flex;
  357. justify-content: center;
  358. gap: 12rpx;
  359. margin-top: 20rpx;
  360. position: relative;
  361. z-index: 1;
  362. }
  363. .energy-footer-dot {
  364. width: 8rpx;
  365. height: 8rpx;
  366. border-radius: 50%;
  367. background: rgba(255,255,255,0.3);
  368. }
  369. .energy-footer-dot:nth-child(2) {
  370. background: rgba(255,215,0,0.6);
  371. width: 10rpx;
  372. height: 10rpx;
  373. }
  374. .zodiac-display {
  375. display: flex;
  376. align-items: center;
  377. justify-content: center;
  378. padding: 20rpx;
  379. }
  380. .zodiac-name {
  381. font-size: 40rpx;
  382. font-weight: 700;
  383. color: #F59E0B;
  384. }
  385. .numsoul-display {
  386. display: flex;
  387. gap: 24rpx;
  388. align-items: center;
  389. }
  390. .numsoul-number {
  391. width: 100rpx;
  392. height: 100rpx;
  393. border-radius: 50%;
  394. display: flex;
  395. align-items: center;
  396. justify-content: center;
  397. flex-shrink: 0;
  398. }
  399. .numsoul-num {
  400. font-size: 44rpx;
  401. font-weight: 700;
  402. color: #fff;
  403. }
  404. .numsoul-info {
  405. flex: 1;
  406. }
  407. .numsoul-title {
  408. font-size: 28rpx;
  409. font-weight: 600;
  410. color: #333;
  411. display: block;
  412. }
  413. .numsoul-keywords {
  414. font-size: 24rpx;
  415. color: #666;
  416. display: block;
  417. margin-top: 6rpx;
  418. }
  419. .numsoul-advice {
  420. font-size: 24rpx;
  421. color: #8B5CF6;
  422. display: block;
  423. margin-top: 8rpx;
  424. line-height: 1.5;
  425. }
  426. .empty-data {
  427. padding: 20rpx;
  428. text-align: center;
  429. color: #999;
  430. font-size: 24rpx;
  431. }
  432. .empty-state {
  433. display: flex;
  434. flex-direction: column;
  435. align-items: center;
  436. padding: 120rpx 32rpx;
  437. }
  438. .empty-icon {
  439. font-size: 80rpx;
  440. margin-bottom: 20rpx;
  441. }
  442. .empty-text {
  443. font-size: 28rpx;
  444. color: #999;
  445. }
  446. </style>