|
@@ -33,6 +33,37 @@
|
|
|
@dimensionClick="goDimensionDetail" />
|
|
@dimensionClick="goDimensionDetail" />
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
|
|
+ <!-- 身体指标卡片 -->
|
|
|
|
|
+ <view class="indicator-cards-section" v-if="isLoggedIn && bodyIndicators.length > 0">
|
|
|
|
|
+ <view class="section-header">
|
|
|
|
|
+ <text class="section-title">📊 身体指标</text>
|
|
|
|
|
+ <text class="section-more" @click="goToBodyDetail">更多 ›</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="indicator-cards">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="(ind, i) in bodyIndicators"
|
|
|
|
|
+ :key="ind.code"
|
|
|
|
|
+ class="indicator-card"
|
|
|
|
|
+ :class="{ 'no-data': !ind.value }"
|
|
|
|
|
+ @click="goIndicatorDetail(ind)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <view class="indicator-header">
|
|
|
|
|
+ <text class="indicator-name">{{ ind.name }}</text>
|
|
|
|
|
+ <text class="indicator-unit" v-if="ind.unit">{{ ind.unit }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="indicator-body">
|
|
|
|
|
+ <text class="indicator-value" v-if="ind.value || ind.numericValue !== null">
|
|
|
|
|
+ {{ ind.value || formatNumeric(ind.numericValue) }}
|
|
|
|
|
+ </text>
|
|
|
|
|
+ <text class="indicator-placeholder" v-else>暂无数据</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="indicator-footer">
|
|
|
|
|
+ <text class="indicator-range" v-if="ind.refRange">参考: {{ ind.refRange }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
<!-- 家庭成员选择条 -->
|
|
<!-- 家庭成员选择条 -->
|
|
|
<FamilyMemberStrip
|
|
<FamilyMemberStrip
|
|
|
v-if="currentView === 'self' && isLoggedIn"
|
|
v-if="currentView === 'self' && isLoggedIn"
|
|
@@ -53,15 +84,7 @@
|
|
|
dimensionCode="body"
|
|
dimensionCode="body"
|
|
|
:sandboxData="sandboxData"
|
|
:sandboxData="sandboxData"
|
|
|
:dualDimension="dualDimension" />
|
|
:dualDimension="dualDimension" />
|
|
|
- <!-- 健康七维 -->
|
|
|
|
|
- <dimension-sub-dims
|
|
|
|
|
- v-if="isLoggedIn && healthSubDims.length > 0"
|
|
|
|
|
- title="🌿 健康七维"
|
|
|
|
|
- subtitle="身体各项健康指标,分数越高代表越健康"
|
|
|
|
|
- accent-color="#FF8C42"
|
|
|
|
|
- :dims="healthSubDims" />
|
|
|
|
|
-
|
|
|
|
|
- <!-- 功能入口 -->
|
|
|
|
|
|
|
+ <!-- 身体指标卡片 -->
|
|
|
<view class="func-section" v-if="sectionVisible('func_entries')">
|
|
<view class="func-section" v-if="sectionVisible('func_entries')">
|
|
|
<view class="func-grid">
|
|
<view class="func-grid">
|
|
|
<view class="func-item" v-for="item in funcList" :key="item.label" @click="onFuncClick(item)">
|
|
<view class="func-item" v-for="item in funcList" :key="item.label" @click="onFuncClick(item)">
|
|
@@ -72,32 +95,6 @@
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
-
|
|
|
|
|
- <!-- ===== 精准营养(上传报告+产品+文章) ===== -->
|
|
|
|
|
- <view class="section nutrition-section" v-if="isLoggedIn">
|
|
|
|
|
- <view class="section-header">
|
|
|
|
|
- <text class="section-title">🥗 精准营养</text>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 上传报告入口 -->
|
|
|
|
|
- <view class="nutrition-report-card" @click="goToUploadReport">
|
|
|
|
|
- <view class="nutrition-report-left">
|
|
|
|
|
- <text class="nutrition-icon-large">📋</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="nutrition-report-right">
|
|
|
|
|
- <text class="nutrition-title">上传健康报告</text>
|
|
|
|
|
- <text class="nutrition-desc">上传体检报告或肠道检测报告,解读分析,获取个性化营养方案</text>
|
|
|
|
|
- <view class="nutrition-report-tags">
|
|
|
|
|
- <text class="tag">体检报告</text>
|
|
|
|
|
- <text class="tag">肠道检测</text>
|
|
|
|
|
- <text class="tag">解读</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="nutrition-arrow">›</text>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
<!-- 身维度介绍卡片(登录后显示) -->
|
|
<!-- 身维度介绍卡片(登录后显示) -->
|
|
|
<DimensionIntroCard dimCode="body" v-if="isLoggedIn" learn-more-url="/pages/body-detail/dimension-intro" />
|
|
<DimensionIntroCard dimCode="body" v-if="isLoggedIn" learn-more-url="/pages/body-detail/dimension-intro" />
|
|
|
|
|
|
|
@@ -112,14 +109,14 @@ import TabTransition from '../../components/tab-transition.vue'
|
|
|
import PageBanner from '../../components/PageBanner.vue'
|
|
import PageBanner from '../../components/PageBanner.vue'
|
|
|
import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
|
|
import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
|
|
|
import RadarChart from '../../components/RadarChart.vue'
|
|
import RadarChart from '../../components/RadarChart.vue'
|
|
|
-import DimensionSubDims from '../../components/DimensionSubDims.vue'
|
|
|
|
|
|
|
+
|
|
|
import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
|
|
import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
|
|
|
import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
|
|
import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
|
|
|
import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
|
|
import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
|
|
|
import { getVisibleSections, getEnergyOverview, getChildren, getFamilyEnergySandbox, getEnergySandbox, getFamilyMemberList, getDimensionOverview } from '../../utils/api.js'
|
|
import { getVisibleSections, getEnergyOverview, getChildren, getFamilyEnergySandbox, getEnergySandbox, getFamilyMemberList, getDimensionOverview } from '../../utils/api.js'
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
- components: { TabTransition, PageBanner, FamilyEnergyBar, RadarChart, DimensionSubDims, FloatingAvatar, FamilyMemberStrip, DimensionIntroCard },
|
|
|
|
|
|
|
+ components: { TabTransition, PageBanner, FamilyEnergyBar, RadarChart, FloatingAvatar, FamilyMemberStrip, DimensionIntroCard },
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
isLoggedIn: false,
|
|
isLoggedIn: false,
|
|
@@ -135,6 +132,7 @@ export default {
|
|
|
childrenChecked: false,
|
|
childrenChecked: false,
|
|
|
membersChecked: false,
|
|
membersChecked: false,
|
|
|
healthSubDims: [],
|
|
healthSubDims: [],
|
|
|
|
|
+ bodyIndicators: [],
|
|
|
sandboxData: null,
|
|
sandboxData: null,
|
|
|
currentView: 'self', // 'self' | 'family'
|
|
currentView: 'self', // 'self' | 'family'
|
|
|
dualDimension: null,
|
|
dualDimension: null,
|
|
@@ -295,7 +293,7 @@ export default {
|
|
|
getDimensionOverview({ memberId: memberId }).then(function(res) {
|
|
getDimensionOverview({ memberId: memberId }).then(function(res) {
|
|
|
if (res && res.data) {
|
|
if (res && res.data) {
|
|
|
self.dimensionData = res.data
|
|
self.dimensionData = res.data
|
|
|
- self.healthSubDims = (self.dimensionData.dimensions || []).map(function(d) { return {label: d.label || d.dimension, score: d.score || 0, desc: d.description || ''}; });
|
|
|
|
|
|
|
+ self.healthSubDims = (self.dimensionData.dimensions || []).map(function(d) { return {label: d.label || d.dimension, score: d.score || 0, desc: d.description || ''}; });
|
|
|
console.log('[body] dimensionData loaded:', res.data.dimensions ? res.data.dimensions.length : 0, 'dimensions')
|
|
console.log('[body] dimensionData loaded:', res.data.dimensions ? res.data.dimensions.length : 0, 'dimensions')
|
|
|
} else if (res) {
|
|
} else if (res) {
|
|
|
console.warn('[body] dimension API error:', res.message || res.code)
|
|
console.warn('[body] dimension API error:', res.message || res.code)
|
|
@@ -303,6 +301,18 @@ export default {
|
|
|
}).catch(function(e) {
|
|
}).catch(function(e) {
|
|
|
console.error('[body] loadHealthDimensions failed', e)
|
|
console.error('[body] loadHealthDimensions failed', e)
|
|
|
})
|
|
})
|
|
|
|
|
+ // 加载身体指标列表
|
|
|
|
|
+ self.loadBodyIndicators(memberId)
|
|
|
|
|
+ },
|
|
|
|
|
+ loadBodyIndicators: function(memberId) {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getLatestIndicators({ memberId: memberId, domain: 'body' }).then(function(res) {
|
|
|
|
|
+ if (res.code === 200) {
|
|
|
|
|
+ self.bodyIndicators = res.data || []
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function(e) {
|
|
|
|
|
+ console.log('[body] loadBodyIndicators failed', e)
|
|
|
|
|
+ })
|
|
|
},
|
|
},
|
|
|
loadSandboxData: function() {
|
|
loadSandboxData: function() {
|
|
|
var self = this
|
|
var self = this
|
|
@@ -411,6 +421,22 @@ export default {
|
|
|
}
|
|
}
|
|
|
uni.navigateTo({ url: '/pages/health/nutrition-profile?memberId=' + id })
|
|
uni.navigateTo({ url: '/pages/health/nutrition-profile?memberId=' + id })
|
|
|
},
|
|
},
|
|
|
|
|
+ goToBodyDetail: function() {
|
|
|
|
|
+ var id = this.activeChildId || uni.getStorageSync('currentChildId')
|
|
|
|
|
+ if (!id) return
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/body/member-body-detail?memberId=' + id })
|
|
|
|
|
+ },
|
|
|
|
|
+ goIndicatorDetail: function(ind) {
|
|
|
|
|
+ var id = this.activeChildId || uni.getStorageSync('currentChildId')
|
|
|
|
|
+ if (!id) return
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/body-detail/dimension-detail?dimension=' + ind.code + '&memberId=' + id })
|
|
|
|
|
+ },
|
|
|
|
|
+ formatNumeric: function(v) {
|
|
|
|
|
+ if (v === null || v === undefined) return ''
|
|
|
|
|
+ var n = parseFloat(v)
|
|
|
|
|
+ if (isNaN(n)) return String(v)
|
|
|
|
|
+ return n % 1 === 0 ? String(n) : n.toFixed(1)
|
|
|
|
|
+ },
|
|
|
goToUploadReport: function() {
|
|
goToUploadReport: function() {
|
|
|
var id = this.activeChildId || uni.getStorageSync('currentChildId') || ''
|
|
var id = this.activeChildId || uni.getStorageSync('currentChildId') || ''
|
|
|
uni.navigateTo({ url: '/pages/health/report-upload?memberId=' + id + '&from=body' })
|
|
uni.navigateTo({ url: '/pages/health/report-upload?memberId=' + id + '&from=body' })
|
|
@@ -535,6 +561,18 @@ export default {
|
|
|
height: 120rpx;
|
|
height: 120rpx;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.indicator-cards-section { margin: 20rpx 30rpx; }
|
|
|
|
|
+.indicator-cards { display: flex; flex-direction: row; flex-wrap: wrap; gap: 16rpx; }
|
|
|
|
|
+.indicator-card { width: calc(50% - 8rpx); background: #fff; border-radius: 20rpx; padding: 24rpx 20rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
|
|
|
|
|
+.indicator-card.no-data { opacity: 0.5; }
|
|
|
|
|
+.indicator-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12rpx; }
|
|
|
|
|
+.indicator-name { font-size: 26rpx; color: #666; font-weight: 500; }
|
|
|
|
|
+.indicator-unit { font-size: 22rpx; color: #999; }
|
|
|
|
|
+.indicator-body { margin-bottom: 8rpx; }
|
|
|
|
|
+.indicator-value { font-size: 40rpx; font-weight: bold; color: #FF8C42; }
|
|
|
|
|
+.indicator-placeholder { font-size: 26rpx; color: #ccc; }
|
|
|
|
|
+.indicator-range { font-size: 20rpx; color: #bbb; }
|
|
|
|
|
+
|
|
|
.func-item:active, .action-btn:active {
|
|
.func-item:active, .action-btn:active {
|
|
|
opacity: 0.7;
|
|
opacity: 0.7;
|
|
|
}
|
|
}
|