Browse Source

feat(miniapp): 指标速查页(分类分页 + 五维/七维小节 + 跳转曲线)

iwt 2 days ago
parent
commit
3eacc096e2
1 changed files with 154 additions and 0 deletions
  1. 154 0
      cfc-frontend/pages/health/indicator-quicklook.vue

+ 154 - 0
cfc-frontend/pages/health/indicator-quicklook.vue

@@ -0,0 +1,154 @@
+<template>
+  <view class="page">
+    <!-- 成员切换 -->
+    <picker :range="memberNames" @change="onMemberChange">
+      <view class="member-picker">{{ currentMemberName }} ▾</view>
+    </picker>
+
+    <!-- 分类 Tab -->
+    <scroll-view scroll-x class="cat-scroll">
+      <view class="cat-tab" :class="activeCategory === '' ? 'cat-active' : ''" @tap="onCatTap('')">全部</view>
+      <view
+        class="cat-tab"
+        :class="activeCategory === c.name ? 'cat-active' : ''"
+        v-for="c in categories"
+        :key="getCatKey(c)"
+        @tap="onCatTap(c.name)"
+      >{{ c.name }}({{ c.total }})</view>
+    </scroll-view>
+
+    <!-- 指标列表 -->
+    <view class="item-list" v-if="items.length > 0">
+      <view class="ql-item" v-for="item in items" :key="getItemKey(item)" @tap="goCurve(getItemId(item))">
+        <view class="ql-name-row">
+          <text class="ql-name">{{ item.name }}</text>
+          <text :class="'ql-trend ' + item._cssClass">{{ item._arrow }} {{ item._trendLabel }}</text>
+        </view>
+        <view class="ql-value-row">
+          <text class="ql-value">{{ item.value }}{{ item.unit || '' }}</text>
+          <text class="ql-ref">参考: {{ item.refRange || '--' }}</text>
+          <text class="ql-status" :class="item._statusClass">{{ item.status || '' }}</text>
+        </view>
+        <text class="ql-date">{{ item._dateText }}</text>
+      </view>
+    </view>
+    <view class="empty-tip" v-else>暂无数据</view>
+
+    <!-- 分页 -->
+    <view class="pager" v-if="totalPages > 1">
+      <view class="pager-btn" :class="page <= 1 ? 'pager-disabled' : ''" @tap="onPrevPage">上一页</view>
+      <text class="pager-info">{{ page }} / {{ totalPages }}</text>
+      <view class="pager-btn" :class="page >= totalPages ? 'pager-disabled' : ''" @tap="onNextPage">下一页</view>
+    </view>
+
+    <!-- 五维/七维评分小节 -->
+    <view class="dim-section" v-if="dimensionScores.length > 0">
+      <view class="section-title">维度评分趋势</view>
+      <view class="dim-list">
+        <view class="dim-row" v-for="d in dimensionScores" :key="getDimKey(d)">
+          <text class="dim-name">{{ d.name }}</text>
+          <text class="dim-score">{{ d.score }}</text>
+          <text :class="'dim-trend ' + d._cssClass">{{ d._arrow }}</text>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import { getIndicatorQuickLookup } from '@/utils/api'
+import { getFamilyMemberList } from '@/utils/api'
+import { resolveChangeStyle, formatDelta, colorClassFor } from '@/utils/indicator-change'
+
+export default {
+  data: function() { return { members: [], currentMemberId: null, categories: [], activeCategory: '', items: [], dimensionScores: [], page: 1, pageSize: 30, totalPages: 1, _onLoadFired: false, _loading: false } },
+  computed: {
+    memberNames: function() { return this.members.map(function(m) { return m.name || m.nickname || '' }) },
+    currentMemberName: function() {
+      var self = this
+      var m = this.members.find(function(x) { return x.id === self.currentMemberId })
+      return (m && (m.name || m.nickname)) || '选择成员'
+    },
+    categoriesComputed: function() {
+      var self = this
+      return this.categories || []
+    }
+  },
+  methods: {
+    onLoad: function() { this.loadMembers(); this._onLoadFired = true },
+    onShow: function() { if (this._onLoadFired) { this._onLoadFired = false } else if (this.currentMemberId) { this.reload() } },
+    goCurve: function(definitionId) {
+      uni.navigateTo({ url: '/pages/health/indicator-curve?definitionId=' + definitionId })
+    },
+    loadMembers: function() {
+      var self = this
+      getFamilyMemberList({ visibleOnly: true }).then(function(res) {
+        var arr = (res.data && res.data.list) || (res.data instanceof Array ? res.data : [])
+        self.members = arr
+        if (!self.currentMemberId && arr.length > 0) { self.currentMemberId = arr[0].id; self.reload() }
+      })
+    },
+    reload: function() { this.page = 1; this.loadData() },
+    loadData: function() {
+      var self = this
+      if (!this.currentMemberId || this._loading) return
+      this._loading = true
+      getIndicatorQuickLookup({ subjectId: this.currentMemberId, category: this.activeCategory || undefined, page: this.page, pageSize: this.pageSize }).then(function(res) {
+        self._loading = false
+        var data = res.data || {}
+        // 预计算展示字段,避免小程序 :class 中调用方法(微信模板编译器不支持带参方法调用)
+        self.rankItems = (data && data.items) || []
+        self.rankItems.forEach(function(it) {
+          var st = resolveChangeStyle(it.goodDirection, it.movement || 'flat')
+          it._cssClass = colorClassFor(st.cssClass)
+          it._arrow = st.arrow
+          it._trendLabel = st.trendLabel
+          it._statusClass = 'status-' + (it._cssClass || 'normal')
+          it._dateText = it.latestReportDate ? String(it.latestReportDate).slice(0, 10) : '--'
+        })
+        self.categories = data.categories || []
+        self.items = data.items || []
+        self.dimensionScores = data.dimensionScores || []
+        var total = data.total !== undefined ? data.total : self.items.length
+        self.totalPages = Math.max(1, Math.ceil(total / self.pageSize))
+      }).catch(function() { self._loading = false })
+    },
+    onCatTap: function(name) { this.activeCategory = name; this.reload() },
+    onPrevPage: function() { if (this.page > 1) { this.page--; this.loadData() } },
+    onNextPage: function() { if (this.page < this.totalPages) { this.page++; this.loadData() } },
+    formatDate: function(s) { if (!s) return '--'; return String(s).slice(0, 10) },
+    getCatKey: function(c) { return c.name },
+    getItemKey: function(item) { return item.definitionId },
+    getItemId: function(item) { return item.definitionId },
+    getDimKey: function(d) { return d.name }
+  }
+}
+</script>
+
+<style scoped>
+.member-picker { font-size: 24rpx; color: #666; }
+.cat-scroll { height: 40rpx; line-height: 40rpx; overflow-x: scroll; }
+.cat-tab { float: left; margin-right: 20rpx; padding: 0 12rpx; height: 32rpx; line-height: 32rpx; font-size: 22rpx; color: #999; }
+.cat-tab.cat-active { color: #4A9BD7; border-bottom: 2rpx solid #4A9BD7; }
+.item-list { padding: 20rpx; }
+.ql-item { margin-bottom: 20rpx; }
+.ql-name-row { display: flex; justify-content: space-between; margin-bottom: 8rpx; }
+.ql-name { font-size: 26rpx; }
+.ql-trend { font-size: 20rpx; color: #666; }
+.ql-value-row { display: flex; justify-content: space-between; margin-bottom: 8rpx; }
+.ql-value { font-size: 24rpx; }
+.ql-ref { font-size: 16rpx; color: #999; }
+.ql-status { font-size: 20rpx; }
+.ql-date { font-size: 16rpx; color: #999; margin-top: 4rpx; }
+.pager { height: 40rpx; line-height: 40rpx; padding: 0 20rpx; font-size: 20rpx; }
+.pager-btn { float: left; margin-right: 10rpx; padding: 0 12rpx; height: 32rpx; line-height: 32rpx; font-size: 20rpx; color: #999; }
+.pager-btn.pager-disabled { color: #ccc; }
+.pager-info { float: left; margin: 0 20rpx; }
+.dim-section { padding: 20rpx; }
+.section-title { font-size: 26rpx; margin-bottom: 12rpx; }
+.dim-list { display: flex; flex-wrap: wrap; }
+.dim-row { margin-bottom: 12rpx; }
+.dim-name { font-size: 22rpx; width: 60rpx; }
+.dim-score { font-size: 22rpx; color: #666; }
+.dim-trend { font-size: 18rpx; color: #999; }
+</style>