Procházet zdrojové kódy

feat(health-report): P1-1 报告日期可修改·前端 - report-detail 查看态日期修改入口

- 报告头日期旁新增「修改」入口(v-if 限定 pageMode==='view' 且有 reportDate,且排除 dan:
  DAN 报告走 /api/dan-report 独立存储与接口,不在 /report/edit 编辑范围)
- 新增内联日期编辑行(text input + 取消/保存,格式 yyyy-MM-dd)
- submitDateChange:复用 editHealthReport(reportId, payload, subjectId) 提交,
  仅覆盖 payload.summary.reportDate;保存前用 detail 接口返回的 indicators/gutFlora/diseaseRisks
  回填空 payload,避免老报告无 payload_json 快照时只改日期却清空指标
- 非 200 直接展示后端 res.message(不吞错误,不弹「已保存」);成功后 toast「已保存」+ loadData() 重载
- headInfo.reportDateText 改用 formatDate(原 parseDate 把 Date 对象直接渲染进模板,
  违反项目时间显示规范 yyyy-MM-dd),新增 headInfo.reportDate 供编辑回填
- 未做 gut-flora-detail.vue / report-result.vue 的日期入口(设计文档 §5 P1-2 范围外)

校验:vue-template-compiler 模板 PASS / dart-sass 样式 PASS / node --check 脚本 PASS
      audit-mp-antipatterns ERROR 0 / audit-duplicate-api-calls 0 error
E2E Test Bot před 2 týdny
rodič
revize
18b0dd437d
1 změnil soubory, kde provedl 115 přidání a 3 odebrání
  1. 115 3
      cfc-frontend/pages/health/report-detail.vue

+ 115 - 3
cfc-frontend/pages/health/report-detail.vue

@@ -8,6 +8,16 @@
         <view class="head-meta">
         <view class="head-meta">
           <text class="type-badge">{{ typeLabel }}</text>
           <text class="type-badge">{{ typeLabel }}</text>
           <text class="head-date">{{ headInfo.reportDateText || '' }}</text>
           <text class="head-date">{{ headInfo.reportDateText || '' }}</text>
+          <!-- 报告日期修改入口(view 模式;DAN 报告走独立存储与接口,不在本页编辑范围) -->
+          <text class="head-date-edit" v-if="pageMode==='view' && headInfo.reportDate && reportType !== 'dan'" @tap="startEditDate">修改</text>
+        </view>
+        <!-- 日期编辑行(仅查看态入口,复用 /report/edit 接口) -->
+        <view class="head-date-editor" v-if="dateEditing">
+          <input class="head-date-input" type="text" v-model="dateInput" placeholder="yyyy-MM-dd" />
+          <view class="head-date-actions">
+            <text class="head-date-btn head-date-cancel" @tap="cancelEditDate">取消</text>
+            <text class="head-date-btn head-date-save" @tap="submitDateChange">保存</text>
+          </view>
         </view>
         </view>
       </view>
       </view>
 
 
@@ -409,7 +419,7 @@ import ReportBlocksRenderer from './report-blocks-renderer.vue'
 import FeedbackPopup from '@/pages/health/components/feedback-popup.vue'
 import FeedbackPopup from '@/pages/health/components/feedback-popup.vue'
 import { normalizeMembers, memberIdOf } from '../../utils/memberNormalize.js'
 import { normalizeMembers, memberIdOf } from '../../utils/memberNormalize.js'
 import { getReportDetail, getDanReportDetail, getReportPayload, editHealthReport, parseReportDraft, confirmReportPreview, discardReportDraft, confirmTongue, discardTongue, getFamilyMemberList, addFamilyMember, queryIndicatorKnowledge, queryBacteriaKnowledge } from '../../utils/api.js'
 import { getReportDetail, getDanReportDetail, getReportPayload, editHealthReport, parseReportDraft, confirmReportPreview, discardReportDraft, confirmTongue, discardTongue, getFamilyMemberList, addFamilyMember, queryIndicatorKnowledge, queryBacteriaKnowledge } from '../../utils/api.js'
-import { parseDate } from '../../utils/format.js'
+import { formatDate } from '../../utils/format.js'
 import fontScaleMixin from '@/mixins/font-scale.js'
 import fontScaleMixin from '@/mixins/font-scale.js'
 
 
 export default {
 export default {
@@ -470,7 +480,11 @@ export default {
       showHelpPopup: false,
       showHelpPopup: false,
       helpData: {},
       helpData: {},
       _kbCache: {},
       _kbCache: {},
-      feedbackVisible: false
+      feedbackVisible: false,
+      // view 模式报告日期修改(仅日期,走 /report/edit 复用现有接口)
+      dateEditing: false,
+      dateInput: '',
+      lastDetail: null
     }
     }
   },
   },
   computed: {
   computed: {
@@ -614,10 +628,13 @@ export default {
         uni.hideLoading()
         uni.hideLoading()
         if (res.code === 200) {
         if (res.code === 200) {
           var report = res.data.report || res.data
           var report = res.data.report || res.data
+          self.lastDetail = res.data
           var head = {}
           var head = {}
           head.personName = report.personName || report.childName || ''
           head.personName = report.personName || report.childName || ''
           head.childName = report.childName || ''
           head.childName = report.childName || ''
-          head.reportDateText = report.reportDate ? parseDate(report.reportDate) : ''
+          // 仅日期场景统一 yyyy-MM-dd(禁止渲染 Date 对象/ISO 串)
+          head.reportDate = report.reportDate ? formatDate(report.reportDate) : ''
+          head.reportDateText = head.reportDate
           self.headInfo = head
           self.headInfo = head
           var backendBlocks = (res.data && res.data.blocks) || report.blocks || []
           var backendBlocks = (res.data && res.data.blocks) || report.blocks || []
           if (res.data.payload) {
           if (res.data.payload) {
@@ -1102,6 +1119,57 @@ export default {
         uni.showToast({ title: '保存异常', icon: 'none' })
         uni.showToast({ title: '保存异常', icon: 'none' })
       })
       })
     },
     },
+    // ===== view 模式:报告日期修改(复用 /report/edit,只改 summary.reportDate)=====
+    startEditDate() {
+      this.dateInput = this.headInfo && this.headInfo.reportDate ? this.headInfo.reportDate : ''
+      this.dateEditing = true
+    },
+    cancelEditDate() {
+      this.dateEditing = false
+      this.dateInput = ''
+    },
+    submitDateChange() {
+      var self = this
+      var val = (self.dateInput || '').trim()
+      if (!val) {
+        uni.showToast({ title: '请输入报告日期', icon: 'none' })
+        return
+      }
+      // 组装保存用 payload:沿用查看态已加载的 payload 快照,仅覆盖 reportDate。
+      // 若老报告无 payload_json 快照(后端 detail 不返回 payload),用详情接口返回的三张表数据回填,
+      // 避免只改日期却把指标/菌群/风险清空。
+      var payload = self.payload
+      if (!payload.summary) payload.summary = {}
+      payload.summary.reportDate = val
+      var detail = self.lastDetail
+      if (detail) {
+        if ((!payload.indicators || !payload.indicators.length) && detail.indicators && detail.indicators.length) {
+          payload.indicators = detail.indicators
+        }
+        if ((!payload.gutFlora || !payload.gutFlora.length) && detail.gutFlora && detail.gutFlora.length) {
+          payload.gutFlora = detail.gutFlora
+        }
+        if ((!payload.diseaseRisks || !payload.diseaseRisks.length) && detail.diseaseRisks && detail.diseaseRisks.length) {
+          payload.diseaseRisks = detail.diseaseRisks
+        }
+      }
+      var subjectId = self.selectedMemberId || self.createdMemberId || null
+      uni.showLoading({ title: '保存中...' })
+      editHealthReport(self.reportId, payload, subjectId).then(function(res) {
+        uni.hideLoading()
+        if (res.code === 200) {
+          self.dateEditing = false
+          uni.showToast({ title: '已保存', icon: 'success' })
+          setTimeout(function() { self.loadData() }, 600)
+        } else {
+          // 非 200(如日期格式非法/未来日期):直接展示后端返回的明确错误,不吞
+          uni.showToast({ title: res.message || '保存失败', icon: 'none' })
+        }
+      }).catch(function() {
+        uni.hideLoading()
+        uni.showToast({ title: '保存失败', icon: 'none' })
+      })
+    },
     /** 从 blocks 中过滤掉"食物推荐/饮食推荐"块(报告详情不展示饮食推荐) */
     /** 从 blocks 中过滤掉"食物推荐/饮食推荐"块(报告详情不展示饮食推荐) */
     stripFoodBlocks: function(blocks) {
     stripFoodBlocks: function(blocks) {
       var list = blocks || []
       var list = blocks || []
@@ -1358,6 +1426,50 @@ $c-rptd-orange-line: #FFD4A8; // 暖橙描边
   font-size: calc(24rpx * var(--font-scale, 1));
   font-size: calc(24rpx * var(--font-scale, 1));
   color: $facade-text-muted;
   color: $facade-text-muted;
 }
 }
+.head-date-edit {
+  font-size: calc(22rpx * var(--font-scale, 1));
+  color: $c-rptd-blue;
+  background: $c-rptd-blue-bg;
+  padding: 4rpx 14rpx;
+  border-radius: 8rpx;
+  margin-left: 4rpx;
+}
+.head-date-edit:active { opacity: 0.7; }
+.head-date-editor {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  gap: 12rpx;
+  margin-top: 12rpx;
+}
+.head-date-input {
+  flex: 1;
+  font-size: calc(26rpx * var(--font-scale, 1));
+  color: $facade-text-strong;
+  padding: 10rpx 16rpx;
+  background: $app-bg;
+  border-radius: 8rpx;
+}
+.head-date-actions {
+  display: flex;
+  flex-direction: row;
+  gap: 12rpx;
+  flex-shrink: 0;
+}
+.head-date-btn {
+  font-size: calc(24rpx * var(--font-scale, 1));
+  padding: 10rpx 24rpx;
+  border-radius: 8rpx;
+}
+.head-date-btn:active { opacity: 0.7; }
+.head-date-cancel {
+  color: $facade-text-muted;
+  background: $facade-fill-slate;
+}
+.head-date-save {
+  color: $app-surface;
+  background: $c-rptd-blue;
+}
 .empty-hint {
 .empty-hint {
   text-align: center;
   text-align: center;
   color: $facade-text-muted;
   color: $facade-text-muted;