Просмотр исходного кода

feat(self-check): 新增自检中间页 self-check-entry.vue

iwt 2 месяцев назад
Родитель
Сommit
c56f54c9c1
1 измененных файлов с 309 добавлено и 0 удалено
  1. 309 0
      cfc-frontend/pages/family/self-check-entry.vue

+ 309 - 0
cfc-frontend/pages/family/self-check-entry.vue

@@ -0,0 +1,309 @@
+<template>
+  <view class="sce-page">
+    <view v-if="loading" class="sce-loading">
+      <text class="sce-loading-text">加载中...</text>
+    </view>
+    <template v-else>
+      <!-- 总分横幅 -->
+      <view class="sce-hero" :style="{ background: heroGradient }">
+        <view class="sce-hero-inner">
+          <text class="sce-hero-label">最近自检总分</text>
+          <view class="sce-hero-score-row">
+            <text class="sce-hero-score">{{ lastResult && lastResult.totalScore || 0 }}</text>
+            <text class="sce-hero-total">/45</text>
+          </view>
+          <text class="sce-hero-date" v-if="lastResult && lastResult.createdAt">
+            {{ formatCreatedDate(lastResult.createdAt) }}
+          </text>
+        </view>
+      </view>
+
+      <!-- 维度分数条 -->
+      <view class="sce-card" v-if="lastResult && lastResult.dimensions && lastResult.dimensions.length > 0">
+        <view class="sce-card-title">
+          <text class="sce-card-title-text">五维得分</text>
+        </view>
+        <view class="sce-dim-list">
+          <view class="sce-dim-item" v-for="dim in lastResult.dimensions" :key="dim.dimension">
+            <view class="sce-dim-head">
+              <view class="sce-dim-label">
+                <view class="sce-dim-dot" :style="{ background: dim.color }"></view>
+                <text class="sce-dim-name">{{ dim.name }}</text>
+              </view>
+              <view class="sce-dim-score-wrap">
+                <text class="sce-dim-score" :style="{ color: dim.color }">{{ dim.score }}</text>
+                <text class="sce-dim-max">/9</text>
+              </view>
+            </view>
+            <view class="sce-dim-bar">
+              <view class="sce-dim-bar-fill" :style="{ width: (dim.score / 9 * 100) + '%', background: dim.color }"></view>
+            </view>
+          </view>
+        </view>
+      </view>
+
+      <!-- 状态提示 -->
+      <view class="sce-status" v-if="!canCheck && lastResult">
+        <text class="sce-status-text">距下次自检还有 {{ daysLeft }} 天</text>
+      </view>
+      <view class="sce-status" v-else-if="canCheck && lastResult">
+        <text class="sce-status-text">已满 15 天,可再次自检</text>
+      </view>
+
+      <!-- 按钮区域 -->
+      <view class="sce-footer">
+        <button v-if="canCheck && lastResult" class="sce-btn sce-btn-primary" @click="retake">
+          再次自检
+        </button>
+        <button v-if="canCheck && lastResult" class="sce-btn sce-btn-outline" @click="ignore">
+          忽略本次
+        </button>
+        <button v-if="!lastResult" class="sce-btn sce-btn-primary" @click="retake">
+          开始自检
+        </button>
+        <button v-if="!canCheck && lastResult" class="sce-btn sce-btn-disabled" disabled>
+          {{ daysLeft }} 天后可再次自检
+        </button>
+      </view>
+    </template>
+  </view>
+</template>
+
+<script>
+import { getSelfCheckStatus, ignoreSelfCheck } from '@/utils/api'
+import { parseDate } from '@/utils/format.js'
+
+export default {
+  data() {
+    return {
+      loading: true,
+      lastResult: null,
+      canCheck: false,
+      daysLeft: 0
+    }
+  },
+  computed: {
+    heroGradient: function() {
+      if (!this.lastResult || !this.lastResult.dimensions || this.lastResult.dimensions.length === 0) {
+        return 'linear-gradient(135deg, #F97316, #FF8C42)'
+      }
+      var lowest = this.lastResult.dimensions.reduce(function(min, d) {
+        return d.score < min.score ? d : min
+      }, this.lastResult.dimensions[0])
+      return 'linear-gradient(135deg, ' + lowest.color + ', ' + (lowest.color + 'bb') + ')'
+    }
+  },
+  onLoad: function() {
+    this.loadStatus()
+  },
+  methods: {
+    loadStatus: function() {
+      var self = this
+      getSelfCheckStatus().then(function(res) {
+        self.loading = false
+        if (res.code === 200 && res.data) {
+          self.lastResult = res.data.lastResult || null
+          self.canCheck = res.data.canCheck
+          self.daysLeft = res.data.daysLeft || 0
+        } else {
+          uni.showToast({ title: (res && res.message) || '加载失败', icon: 'none' })
+        }
+      }).catch(function() {
+        self.loading = false
+        uni.showToast({ title: '网络异常', icon: 'none' })
+      })
+    },
+    retake: function() {
+      uni.navigateTo({ url: '/pages/family/self-check?retake=1' })
+    },
+    ignore: function() {
+      var self = this
+      ignoreSelfCheck({ checkId: this.lastResult && this.lastResult.id }).then(function(res) {
+        if (res.code === 200) {
+          uni.showToast({ title: '已忽略,15 天后再次提醒', icon: 'none' })
+          setTimeout(function() {
+            uni.navigateBack()
+          }, 800)
+        } else {
+          uni.showToast({ title: (res && res.message) || '操作失败', icon: 'none' })
+        }
+      }).catch(function() {
+        uni.showToast({ title: '操作失败', icon: 'none' })
+      })
+    },
+    formatCreatedDate: function(dateStr) {
+      if (!dateStr) return ''
+      var d = parseDate(dateStr)
+      if (!d) return ''
+      var pad = function(n) { return n < 10 ? '0' + n : '' + n }
+      return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate())
+    }
+  }
+}
+</script>
+
+<style scoped>
+.sce-page {
+  min-height: 100vh;
+  background: #F5FAFE;
+  padding: 24rpx 30rpx 120rpx;
+  box-sizing: border-box;
+}
+.sce-loading {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 50vh;
+}
+.sce-loading-text {
+  font-size: 28rpx;
+  color: #999;
+}
+.sce-hero {
+  background: linear-gradient(135deg, #F97316, #FF8C42);
+  border-radius: 24rpx;
+  padding: 40rpx 30rpx;
+  margin-bottom: 24rpx;
+  box-shadow: 0 6rpx 20rpx rgba(249, 114, 22, 0.25);
+}
+.sce-hero-inner {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+.sce-hero-label {
+  font-size: 26rpx;
+  color: rgba(255, 255, 255, 0.9);
+}
+.sce-hero-score-row {
+  display: flex;
+  align-items: baseline;
+  margin-top: 10rpx;
+}
+.sce-hero-score {
+  font-size: 88rpx;
+  font-weight: 700;
+  color: #fff;
+}
+.sce-hero-total {
+  font-size: 30rpx;
+  color: rgba(255, 255, 255, 0.7);
+  margin-left: 6rpx;
+}
+.sce-hero-date {
+  display: block;
+  text-align: center;
+  font-size: 22rpx;
+  color: rgba(255, 255, 255, 0.8);
+  margin-top: 16rpx;
+}
+.sce-card {
+  background: #fff;
+  border-radius: 24rpx;
+  padding: 30rpx 26rpx;
+  margin-bottom: 24rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+}
+.sce-card-title {
+  margin-bottom: 24rpx;
+}
+.sce-card-title-text {
+  font-size: 32rpx;
+  font-weight: 600;
+  color: #333;
+}
+.sce-dim-list {
+  display: flex;
+  flex-direction: column;
+}
+.sce-dim-item {
+  margin-bottom: 24rpx;
+}
+.sce-dim-item:last-child {
+  margin-bottom: 0;
+}
+.sce-dim-head {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+.sce-dim-label {
+  display: flex;
+  align-items: center;
+}
+.sce-dim-dot {
+  width: 28rpx;
+  height: 28rpx;
+  border-radius: 50%;
+  margin-right: 12rpx;
+}
+.sce-dim-name {
+  font-size: 30rpx;
+  font-weight: 600;
+  color: #333;
+}
+.sce-dim-score-wrap {
+  display: flex;
+  align-items: baseline;
+}
+.sce-dim-score {
+  font-size: 40rpx;
+  font-weight: 700;
+}
+.sce-dim-max {
+  font-size: 22rpx;
+  color: #BBB;
+}
+.sce-dim-bar {
+  height: 12rpx;
+  background: #F0F0F0;
+  border-radius: 6rpx;
+  margin-top: 14rpx;
+  overflow: hidden;
+}
+.sce-dim-bar-fill {
+  height: 100%;
+  border-radius: 6rpx;
+  transition: width 0.5s ease;
+}
+.sce-status {
+  background: #FFF7ED;
+  border-radius: 16rpx;
+  padding: 24rpx 20rpx;
+  margin-bottom: 24rpx;
+  text-align: center;
+}
+.sce-status-text {
+  font-size: 26rpx;
+  color: #E65100;
+}
+.sce-footer {
+  display: flex;
+  flex-direction: column;
+  gap: 16rpx;
+  margin-top: 20rpx;
+}
+.sce-btn {
+  height: 88rpx;
+  line-height: 88rpx;
+  font-size: 30rpx;
+  border-radius: 44rpx;
+  text-align: center;
+  border: none;
+}
+.sce-btn-primary {
+  background: linear-gradient(135deg, #F97316, #FF8C42);
+  color: #fff;
+  font-weight: 600;
+}
+.sce-btn-outline {
+  background: #fff;
+  color: #F97316;
+  border: 2rpx solid #F97316;
+}
+.sce-btn-disabled {
+  background: #E5E7EB;
+  color: #999;
+  font-weight: 400;
+}
+</style>