| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959 |
- <template>
- <view class="tianpan-container">
- <!-- 自定义导航栏 -->
- <view class="custom-nav">
- <view class="nav-header">
- <text class="nav-title">家庭天盘</text>
- <picker mode="selector" :range="yearOptions" :value="yearIndex" @change="onYearChange">
- <view class="year-picker">
- <text class="year-text">{{ currentYear }}</text>
- <text class="picker-icon">▼</text>
- </view>
- </picker>
- </view>
- <view class="family-name" v-if="familyName">{{ familyName }}</view>
- </view>
- <!-- 天盘画布 -->
- <view class="canvas-wrapper">
- <canvas
- type="2d"
- id="tianpanCanvas"
- class="tianpan-canvas"
- @touchend="onCanvasTouchEnd"
- ></canvas>
- </view>
- <!-- 标签栏 -->
- <view class="tab-bar">
- <view
- class="tab-item"
- :class="{ active: currentTab === 0 }"
- @click="switchTab(0)"
- >
- <text>任务</text>
- </view>
- <view
- class="tab-item"
- :class="{ active: currentTab === 1 }"
- @click="switchTab(1)"
- >
- <text>测评</text>
- </view>
- <view
- class="tab-item"
- :class="{ active: currentTab === 2 }"
- @click="switchTab(2)"
- >
- <text>预警</text>
- </view>
- <view
- class="tab-item"
- :class="{ active: currentTab === 3 }"
- @click="switchTab(3)"
- >
- <text>里程碑</text>
- </view>
- </view>
- <!-- 相关事项列表 -->
- <view class="related-items-list">
- <view
- class="item-card"
- v-for="item in currentItems"
- :key="item.id"
- @click="onItemClick(item)"
- >
- <view class="item-icon">{{ item.icon || '📋' }}</view>
- <view class="item-content">
- <text class="item-title">{{ item.title }}</text>
- <text class="item-desc">{{ item.description || '' }}</text>
- </view>
- <view class="item-arrow">›</view>
- </view>
- <view class="empty-state" v-if="currentItems.length === 0">
- <text>暂无相关事项</text>
- </view>
- </view>
- <!-- 每日运势卡片 -->
- <view class="fortune-card" @click="goToDailyFortune">
- <view class="fortune-header">
- <text class="fortune-title">今日运势</text>
- <text class="fortune-date">{{ currentDate }}</text>
- </view>
- <view class="fortune-content">
- <text class="fortune-text">{{ dailyFortuneText || '点击查看今日家庭能量运势' }}</text>
- </view>
- <view class="fortune-arrow">查看详情 ›</view>
- </view>
- <!-- 成员详情弹窗 -->
- <view class="member-popup" v-if="showMemberPopup" @touchmove.stop>
- <view class="popup-overlay" @click="closeMemberPopup"></view>
- <view class="popup-content">
- <view class="popup-header">
- <text class="popup-title">{{ selectedMember.nickname || selectedMember.name }}</text>
- <text class="popup-close" @click="closeMemberPopup">×</text>
- </view>
- <view class="popup-body">
- <view class="member-info-row">
- <text class="info-label">生肖:</text>
- <text class="info-value">{{ selectedMember.zodiac || '-' }}</text>
- </view>
- <view class="member-info-row">
- <text class="info-label">星座:</text>
- <text class="info-value">{{ selectedMember.westernSign || '-' }}</text>
- </view>
- <view class="member-info-row">
- <text class="info-label">代际:</text>
- <text class="info-value">{{ selectedMember.generation || '-' }}</text>
- </view>
- <view class="energy-bars">
- <view class="energy-bar-item" v-for="energy in selectedMember.energy" :key="energy.type">
- <text class="energy-label">{{ energy.label }}</text>
- <view class="energy-bar">
- <view class="energy-fill" :style="{ width: energy.value + '%' }"></view>
- </view>
- <text class="energy-value">{{ energy.value }}</text>
- </view>
- </view>
- </view>
- <view class="popup-footer">
- <button class="detail-btn" @click="goToMemberDetail">查看详情</button>
- </view>
- </view>
- </view>
- <!-- 加载状态 -->
- <view class="loading-overlay" v-if="loading">
- <view class="loading-spinner"></view>
- <text>加载中...</text>
- </view>
- </view>
- </template>
- <script>
- import { mapState, mapGetters, mapActions } from 'vuex'
- export default {
- data: function() {
- return {
- currentYear: new Date().getFullYear(),
- yearOptions: [],
- yearIndex: 0,
- currentTab: 0,
- showMemberPopup: false,
- selectedMember: {},
- canvasContext: null,
- canvasWidth: 375,
- canvasHeight: 375,
- members: [],
- relationships: [],
- relatedItems: [],
- dailyFortuneText: '',
- currentDate: ''
- }
- },
- computed: {
- ...mapGetters('tianpan', ['members', 'relationships', 'familyWuxing', 'familyName']),
- ...mapState('tianpan', ['loading', 'error']),
- currentItems: function() {
- var tabMap = ['tasks', 'assessments', 'alerts', 'milestones']
- var items = this.relatedItems && this.relatedItems[tabMap[this.currentTab]] ? this.relatedItems[tabMap[this.currentTab]] : []
- return items
- }
- },
- onLoad: function() {
- this.initYearOptions()
- this.loadDashboardData()
- this.loadDailyFortune()
- this.loadRelatedItems()
- },
- onReady: function() {
- var query = uni.createSelectorQuery()
- query.select('#tianpanCanvas')
- .fields({ node: true, size: true })
- .exec(this.onCanvasReady)
- },
- methods: {
- ...mapActions('tianpan', ['loadDashboard', 'loadDailyFortune', 'loadRelatedItems']),
-
- initYearOptions: function() {
- var current = new Date().getFullYear()
- for (var i = current - 2; i <= current + 2; i++) {
- this.yearOptions.push(i)
- if (i === current) {
- this.yearIndex = this.yearOptions.length - 1
- }
- }
- this.currentYear = current
- },
-
- onYearChange: function(e) {
- var index = e.detail.value
- var year = this.yearOptions[index]
- if (year !== this.currentYear) {
- this.currentYear = year
- this.loadDashboardData()
- }
- },
-
- loadDashboardData: function() {
- this.loadDashboard({ year: this.currentYear, daysAhead: 7 })
- .then(this.drawCanvas)
- },
-
- onCanvasReady: function(res) {
- if (!res || !res[0] || !res[0].node) {
- return
- }
- var ctx = res[0].node.getContext('2d')
- var dpr = uni.getWindowInfo().pixelRatio
- this.canvasContext = ctx
- this.canvasContext.scale(dpr, dpr)
- this.drawCanvas()
- },
-
- drawCanvas: function() {
- if (!this.canvasContext) {
- return
- }
- var ctx = this.canvasContext
- var w = this.canvasWidth
- var h = this.canvasHeight
- var cx = w / 2
- var cy = h / 2
-
- // 清空画布
- ctx.clearRect(0, 0, w, h)
- ctx.fillStyle = '#FFF8F0'
- ctx.fillRect(0, 0, w, h)
-
- // 获取数据
- var members = this.members || []
- var relationships = this.relationships || []
-
- // R0: 中心五维能量雷达图
- this.drawRadar(ctx, cx, cy, w * 0.11, members)
-
- // R1: 内圈成员节点
- this.drawMemberNodes(ctx, cx, cy, w * 0.18, members)
-
- // R2-R3: 外圈相关事项
- this.drawRelatedItems(ctx, cx, cy, w * 0.32, w * 0.46, members)
-
- // 绘制关系连线
- this.drawRelationshipLines(ctx, cx, cy, relationships)
- },
-
- drawRadar: function(ctx, cx, cy, r, members) {
- var axes = ['木', '火', '土', '金', '水']
- var elementKeys = ['wood', 'fire', 'earth', 'metal', 'water']
- var elementColors = ['#10B981', '#FF6B9D', '#FF8C42', '#6366F1', '#F59E0B']
- var angleStep = (Math.PI * 2) / 5
- // 获取五行数据
- var wuxing = this.familyWuxing || {}
- var percentages = (wuxing.aggregatePercentages) || {}
- var strongest = wuxing.strongestElement || ''
- var weakest = wuxing.weakestElement || ''
- var familyLabel = this.familyName || '家庭五行'
- // 绘制背景五边形
- ctx.beginPath()
- for (var i = 0; i < 5; i++) {
- var angle = i * angleStep - Math.PI / 2
- var bx = cx + r * Math.cos(angle)
- var by = cy + r * Math.sin(angle)
- if (i === 0) {
- ctx.moveTo(bx, by)
- } else {
- ctx.lineTo(bx, by)
- }
- }
- ctx.closePath()
- ctx.fillStyle = 'rgba(249, 114, 22, 0.08)'
- ctx.fill()
- ctx.strokeStyle = 'rgba(249, 114, 22, 0.25)'
- ctx.lineWidth = 1
- ctx.stroke()
- // 绘制彩色轴线 + 轴标签
- for (var j = 0; j < 5; j++) {
- var a2 = j * angleStep - Math.PI / 2
- var lx = cx + r * Math.cos(a2)
- var ly = cy + r * Math.sin(a2)
- ctx.beginPath()
- ctx.moveTo(cx, cy)
- ctx.lineTo(lx, ly)
- ctx.strokeStyle = elementColors[j] + '44'
- ctx.lineWidth = 1
- ctx.stroke()
- // 轴标签(元素名)
- var labelX = cx + (r + 16) * Math.cos(a2)
- var labelY = cy + (r + 16) * Math.sin(a2)
- ctx.fillStyle = elementColors[j]
- ctx.font = 'bold 12px sans-serif'
- ctx.textAlign = 'center'
- ctx.textBaseline = 'middle'
- ctx.fillText(axes[j], labelX, labelY)
- }
- // 绘制数据驱动多边形(按百分比缩放)
- var hasData = false
- for (var k = 0; k < elementKeys.length; k++) {
- if (percentages[elementKeys[k]] && percentages[elementKeys[k]] > 0) {
- hasData = true
- break
- }
- }
- if (hasData) {
- ctx.beginPath()
- for (var m = 0; m < 5; m++) {
- var ang = m * angleStep - Math.PI / 2
- var pct = (percentages[elementKeys[m]] || 0) / 100
- var dataR = r * pct
- var dx = cx + dataR * Math.cos(ang)
- var dy = cy + dataR * Math.sin(ang)
- if (m === 0) {
- ctx.moveTo(dx, dy)
- } else {
- ctx.lineTo(dx, dy)
- }
- }
- ctx.closePath()
- ctx.fillStyle = 'rgba(249, 114, 22, 0.2)'
- ctx.fill()
- ctx.strokeStyle = '#F97316'
- ctx.lineWidth = 2
- ctx.stroke()
- // 数据节点圆点 + 百分比标签
- for (var n = 0; n < 5; n++) {
- var ang2 = n * angleStep - Math.PI / 2
- var pct2 = (percentages[elementKeys[n]] || 0) / 100
- var dataR2 = r * pct2
- var px = cx + dataR2 * Math.cos(ang2)
- var py = cy + dataR2 * Math.sin(ang2)
- // 圆点
- ctx.beginPath()
- ctx.arc(px, py, 3, 0, Math.PI * 2)
- ctx.fillStyle = elementColors[n]
- ctx.fill()
- // 百分比标签(在圆点外侧)
- var pctLabelR = dataR2 + (pct2 > 0.3 ? -14 : 14)
- var pLabelX = cx + pctLabelR * Math.cos(ang2)
- var pLabelY = cy + pctLabelR * Math.sin(ang2)
- ctx.fillStyle = elementColors[n]
- ctx.font = '10px sans-serif'
- ctx.textAlign = 'center'
- ctx.textBaseline = 'middle'
- ctx.fillText((percentages[elementKeys[n]] || 0) + '%', pLabelX, pLabelY)
- }
- }
- // 中心文字:家庭名 + 最强/最弱
- ctx.fillStyle = '#1F2937'
- ctx.font = 'bold 11px sans-serif'
- ctx.textAlign = 'center'
- ctx.textBaseline = 'middle'
- ctx.fillText(familyLabel, cx, cy - 6)
- if (strongest || weakest) {
- ctx.fillStyle = '#6B7280'
- ctx.font = '9px sans-serif'
- var strongestZh = strongest === 'wood' ? '木' : strongest === 'fire' ? '火' : strongest === 'earth' ? '土' : strongest === 'metal' ? '金' : strongest === 'water' ? '水' : strongest
- var weakestZh = weakest === 'wood' ? '木' : weakest === 'fire' ? '火' : weakest === 'earth' ? '土' : weakest === 'metal' ? '金' : weakest === 'water' ? '水' : weakest
- ctx.fillText('强:' + strongestZh + ' 弱:' + weakestZh, cx, cy + 7)
- }
- },
-
- drawMemberNodes: function(ctx, cx, cy, r, members) {
- var angleStep = (Math.PI * 2) / (members.length || 1)
-
- for (var i = 0; i < members.length; i++) {
- var member = members[i]
- var angle = i * angleStep - Math.PI / 2
- var x = cx + r * Math.cos(angle)
- var y = cy + r * Math.sin(angle)
-
- // 绘制圆形节点
- ctx.beginPath()
- ctx.arc(x, y, 15, 0, Math.PI * 2)
- ctx.fillStyle = this.getMemberColor(member.effectiveRole)
- ctx.fill()
- ctx.strokeStyle = '#FFF'
- ctx.lineWidth = 2
- ctx.stroke()
-
- // 绘制名字
- ctx.fillStyle = '#FFF'
- ctx.font = '10px sans-serif'
- ctx.textAlign = 'center'
- ctx.textBaseline = 'middle'
- ctx.fillText(member.nickname || member.name, x, y)
-
- // 存储点击区域
- if (!this.memberHitAreas) {
- this.memberHitAreas = []
- }
- this.memberHitAreas.push({
- type: 'member',
- data: member,
- x: x,
- y: y,
- r: 15
- })
- }
- },
-
- drawRelatedItems: function(ctx, cx, cy, rInner, rOuter, members) {
- var items = this.relatedItems && this.relatedItems.tasks ? this.relatedItems.tasks : []
- var angleStep = (Math.PI * 2) / (items.length || 1)
- var r = (rInner + rOuter) / 2
-
- for (var i = 0; i < items.length; i++) {
- var item = items[i]
- var angle = i * angleStep - Math.PI / 2
- var x = cx + r * Math.cos(angle)
- var y = cy + r * Math.sin(angle)
-
- // 绘制 emoji
- ctx.font = '16px sans-serif'
- ctx.textAlign = 'center'
- ctx.textBaseline = 'middle'
- ctx.fillText(item.icon || '📋', x, y)
-
- // 存储点击区域
- if (!this.itemHitAreas) {
- this.itemHitAreas = []
- }
- this.itemHitAreas.push({
- type: 'item',
- data: item,
- x: x,
- y: y,
- r: 12
- })
- }
- },
-
- drawRelationshipLines: function(ctx, cx, cy, relationships) {
- for (var i = 0; i < relationships.length; i++) {
- var rel = relationships[i]
- if (!rel.fromMember || !rel.toMember) {
- continue
- }
-
- // 计算起点和终点
- var memberCount = this.members.length || 1
- var angleStep = (Math.PI * 2) / memberCount
- var r = this.canvasWidth * 0.18
-
- var fromIndex = -1
- var toIndex = -1
- for (var j = 0; j < this.members.length; j++) {
- if (this.members[j].id === rel.fromMember.id) {
- fromIndex = j
- }
- if (this.members[j].id === rel.toMember.id) {
- toIndex = j
- }
- }
-
- if (fromIndex < 0 || toIndex < 0) {
- continue
- }
-
- var fromAngle = fromIndex * angleStep - Math.PI / 2
- var toAngle = toIndex * angleStep - Math.PI / 2
-
- var x1 = cx + r * Math.cos(fromAngle)
- var y1 = cy + r * Math.sin(fromAngle)
- var x2 = cx + r * Math.cos(toAngle)
- var y2 = cy + r * Math.sin(toAngle)
-
- // 贝塞尔曲线
- ctx.beginPath()
- ctx.moveTo(x1, y1)
- var cx1 = (x1 + x2) / 2 + (y2 - y1) * 0.2
- var cy1 = (y1 + y2) / 2 - (x2 - x1) * 0.2
- ctx.quadraticCurveTo(cx1, cy1, x2, y2)
-
- // 根据分数设置颜色
- var score = rel.score || 0
- if (score >= 80) {
- ctx.strokeStyle = '#10B981'
- } else if (score >= 60) {
- ctx.strokeStyle = '#F59E0B'
- } else {
- ctx.strokeStyle = '#EF4444'
- }
- ctx.lineWidth = 2
- ctx.stroke()
- }
- },
-
- getMemberColor: function(role) {
- var colorMap = {
- 'parent': '#F97316',
- 'child': '#3B82F6',
- 'teacher': '#8B5CF6'
- }
- return colorMap[role] || '#6B7280'
- },
-
- onCanvasTouchEnd: function(e) {
- if (!e || !e.changedTouches || !e.changedTouches[0]) {
- return
- }
- var touch = e.changedTouches[0]
- var x = touch.x
- var y = touch.y
-
- // 检测成员点击
- if (this.memberHitAreas) {
- for (var i = 0; i < this.memberHitAreas.length; i++) {
- var area = this.memberHitAreas[i]
- var dx = x - area.x
- var dy = y - area.y
- if (dx * dx + dy * dy <= area.r * area.r) {
- this.showMemberDetail(area.data)
- return
- }
- }
- }
-
- // 检测事项点击
- if (this.itemHitAreas) {
- for (var j = 0; j < this.itemHitAreas.length; j++) {
- var itemArea = this.itemHitAreas[j]
- var dx2 = x - itemArea.x
- var dy2 = y - itemArea.y
- if (dx2 * dx2 + dy2 * dy2 <= itemArea.r * itemArea.r) {
- this.onItemClick(itemArea.data)
- return
- }
- }
- }
- },
-
- showMemberDetail: function(member) {
- this.selectedMember = member
- this.showMemberPopup = true
- },
-
- closeMemberPopup: function() {
- this.showMemberPopup = false
- this.selectedMember = {}
- },
-
- goToMemberDetail: function() {
- if (!this.selectedMember || !this.selectedMember.id) {
- return
- }
- uni.navigateTo({
- url: '/pages/tianpan/member-detail?id=' + this.selectedMember.id
- })
- this.closeMemberPopup()
- },
-
- switchTab: function(index) {
- this.currentTab = index
- },
-
- onItemClick: function(item) {
- if (!item || !item.id) {
- return
- }
- // 根据类型跳转
- if (item.type === 'task') {
- uni.navigateTo({ url: '/pages/tasks/detail?id=' + item.id })
- } else if (item.type === 'assessment') {
- uni.navigateTo({ url: '/pages/assessment/detail?id=' + item.id })
- } else if (item.type === 'alert') {
- uni.navigateTo({ url: '/pages/health/alert-detail?id=' + item.id })
- } else if (item.type === 'milestone') {
- uni.navigateTo({ url: '/pages/family/milestone-detail?id=' + item.id })
- }
- },
-
- goToDailyFortune: function() {
- uni.navigateTo({
- url: '/pages/tianpan/daily-fortune'
- })
- },
-
- loadDailyFortune: function() {
- var today = new Date()
- var dateStr = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate()
- this.loadDailyFortune({ date: dateStr })
- .then(this.setFortuneText)
- },
-
- setFortuneText: function(data) {
- if (data && data.advice) {
- this.dailyFortuneText = data.advice
- }
- var today = new Date()
- this.currentDate = today.getFullYear() + '年' + (today.getMonth() + 1) + '月' + today.getDate() + '日'
- },
-
- loadRelatedItems: function() {
- this.loadRelatedItems({})
- }
- },
- watch: {
- members: function(newVal) {
- if (newVal && this.canvasContext) {
- this.drawCanvas()
- }
- },
- relationships: function(newVal) {
- if (newVal && this.canvasContext) {
- this.drawCanvas()
- }
- }
- }
- }
- </script>
- <style>
- .tianpan-container {
- min-height: 100vh;
- background: linear-gradient(180deg, #FFF8F0 0%, #FFFFFF 100%);
- padding-bottom: 20px;
- }
- /* 自定义导航栏 */
- .custom-nav {
- background: linear-gradient(135deg, #F97316 0%, #FB923C 100%);
- padding: 44px 16px 12px;
- color: #FFFFFF;
- }
- .nav-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .nav-title {
- font-size: 20px;
- font-weight: bold;
- }
- .year-picker {
- display: flex;
- align-items: center;
- background: rgba(255, 255, 255, 0.2);
- padding: 4px 12px;
- border-radius: 16px;
- }
- .year-text {
- font-size: 14px;
- margin-right: 4px;
- }
- .picker-icon {
- font-size: 10px;
- }
- .family-name {
- text-align: center;
- margin-top: 8px;
- font-size: 14px;
- opacity: 0.9;
- }
- /* 画布区域 */
- .canvas-wrapper {
- display: flex;
- justify-content: center;
- padding: 20px 0;
- }
- .tianpan-canvas {
- width: 375px;
- height: 375px;
- background: #FFF8F0;
- border-radius: 16px;
- box-shadow: 0 4px 12px rgba(249, 114, 22, 0.1);
- }
- /* 标签栏 */
- .tab-bar {
- display: flex;
- justify-content: space-around;
- background: #FFFFFF;
- margin: 0 16px;
- padding: 12px 0;
- border-radius: 12px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
- }
- .tab-item {
- padding: 6px 16px;
- border-radius: 20px;
- font-size: 14px;
- color: #6B7280;
- }
- .tab-item.active {
- background: #F97316;
- color: #FFFFFF;
- }
- /* 相关事项列表 */
- .related-items-list {
- padding: 16px;
- }
- .item-card {
- display: flex;
- align-items: center;
- background: #FFFFFF;
- padding: 16px;
- border-radius: 12px;
- margin-bottom: 12px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
- }
- .item-icon {
- font-size: 28px;
- margin-right: 12px;
- }
- .item-content {
- flex: 1;
- }
- .item-title {
- font-size: 16px;
- font-weight: 500;
- color: #1F2937;
- display: block;
- margin-bottom: 4px;
- }
- .item-desc {
- font-size: 12px;
- color: #9CA3AF;
- display: block;
- }
- .item-arrow {
- font-size: 20px;
- color: #D1D5DB;
- }
- .empty-state {
- text-align: center;
- padding: 40px 0;
- color: #9CA3AF;
- font-size: 14px;
- }
- /* 每日运势卡片 */
- .fortune-card {
- margin: 16px;
- padding: 20px;
- background: linear-gradient(135deg, #FED7AA 0%, #FDBA74 100%);
- border-radius: 16px;
- box-shadow: 0 4px 12px rgba(249, 114, 22, 0.2);
- }
- .fortune-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 12px;
- }
- .fortune-title {
- font-size: 18px;
- font-weight: bold;
- color: #7C2D12;
- }
- .fortune-date {
- font-size: 12px;
- color: #9A3412;
- }
- .fortune-content {
- margin-bottom: 12px;
- }
- .fortune-text {
- font-size: 14px;
- color: #9A3412;
- line-height: 1.6;
- }
- .fortune-arrow {
- text-align: right;
- font-size: 12px;
- color: #7C2D12;
- }
- /* 成员弹窗 */
- .member-popup {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- z-index: 1000;
- }
- .popup-overlay {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.5);
- }
- .popup-content {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- background: #FFFFFF;
- border-radius: 20px 20px 0 0;
- padding: 24px;
- max-height: 70vh;
- overflow-y: auto;
- }
- .popup-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 20px;
- }
- .popup-title {
- font-size: 20px;
- font-weight: bold;
- color: #1F2937;
- }
- .popup-close {
- font-size: 28px;
- color: #9CA3AF;
- line-height: 1;
- }
- .popup-body {
- margin-bottom: 20px;
- }
- .member-info-row {
- display: flex;
- margin-bottom: 12px;
- }
- .info-label {
- width: 80px;
- color: #6B7280;
- font-size: 14px;
- }
- .info-value {
- flex: 1;
- color: #1F2937;
- font-size: 14px;
- }
- .energy-bars {
- margin-top: 20px;
- }
- .energy-bar-item {
- display: flex;
- align-items: center;
- margin-bottom: 12px;
- }
- .energy-label {
- width: 40px;
- color: #6B7280;
- font-size: 14px;
- }
- .energy-bar {
- flex: 1;
- height: 8px;
- background: #E5E7EB;
- border-radius: 4px;
- margin: 0 12px;
- overflow: hidden;
- }
- .energy-fill {
- height: 100%;
- background: linear-gradient(90deg, #F97316 0%, #FB923C 100%);
- border-radius: 4px;
- }
- .energy-value {
- width: 30px;
- text-align: right;
- color: #1F2937;
- font-size: 14px;
- }
- .popup-footer {
- display: flex;
- justify-content: center;
- }
- .detail-btn {
- background: linear-gradient(135deg, #F97316 0%, #FB923C 100%);
- color: #FFFFFF;
- border: none;
- padding: 12px 48px;
- border-radius: 24px;
- font-size: 16px;
- }
- /* 加载状态 */
- .loading-overlay {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(255, 255, 255, 0.9);
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- z-index: 2000;
- }
- .loading-spinner {
- width: 40px;
- height: 40px;
- border: 4px solid #E5E7EB;
- border-top-color: #F97316;
- border-radius: 50%;
- animation: spin 1s linear infinite;
- margin-bottom: 16px;
- }
- @keyframes spin {
- to {
- transform: rotate(360deg);
- }
- }
- </style>
|