gut-flora-risks-detail.vue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <template>
  2. <view class="page">
  3. <view class="nav-bar">
  4. <view class="nav-back" @tap="goBack">
  5. <text class="back-text">‹ 返回</text>
  6. </view>
  7. <text class="nav-title">疾病风险</text>
  8. <text class="nav-edit" @tap="toggleEdit">{{ isEditing ? '完成' : '编辑' }}</text>
  9. </view>
  10. <scroll-view class="content" scroll-y>
  11. <view class="section-label">重要风险</view>
  12. <view class="risk-card" v-for="(item, idx) in importantRisks" :key="idx"
  13. :class="'risk-level-' + (item._cssClass || 'low')">
  14. <view class="risk-header">
  15. <text class="risk-name" @tap="showKnowledge('disease', item.diseaseName)">{{ item.diseaseName }}</text>
  16. <text class="risk-badge" :class="'badge-' + (item._cssClass || 'low')">
  17. {{ riskBadgeText(item.riskLevel) }}
  18. </text>
  19. </view>
  20. <view class="risk-detail-row" v-if="!isEditing">
  21. <text class="risk-value">风险值: {{ item.riskValue || '--' }}</text>
  22. <text class="risk-level-text">等级: {{ item.riskLevel || '--' }}</text>
  23. </view>
  24. <view class="risk-edit-row" v-if="isEditing">
  25. <text class="edit-label">风险值:</text>
  26. <input class="edit-input" type="text" v-model="item.riskValue" />
  27. <text class="edit-label">等级:</text>
  28. <picker :value="riskLevelIndex(item.riskLevel)" :range="riskLevels" @change="onRiskLevelChange($event, item)">
  29. <text class="edit-picker">{{ item.riskLevel || '请选择' }}</text>
  30. </picker>
  31. </view>
  32. </view>
  33. <view class="section-label">其它风险</view>
  34. <view class="risk-card" v-for="(item, idx) in normalRisks" :key="idx"
  35. :class="'risk-level-' + (item._cssClass || 'low')">
  36. <view class="risk-header">
  37. <text class="risk-name" @tap="showKnowledge('disease', item.diseaseName)">{{ item.diseaseName }}</text>
  38. <text class="risk-badge" :class="'badge-' + (item._cssClass || 'low')">
  39. {{ riskBadgeText(item.riskLevel) }}
  40. </text>
  41. </view>
  42. <view class="risk-detail-row" v-if="!isEditing">
  43. <text class="risk-value">风险值: {{ item.riskValue || '--' }}</text>
  44. <text class="risk-level-text">等级: {{ item.riskLevel || '--' }}</text>
  45. </view>
  46. <view class="risk-edit-row" v-if="isEditing">
  47. <text class="edit-label">风险值:</text>
  48. <input class="edit-input" type="text" v-model="item.riskValue" />
  49. <text class="edit-label">等级:</text>
  50. <picker :value="riskLevelIndex(item.riskLevel)" :range="riskLevels" @change="onRiskLevelChange($event, item)">
  51. <text class="edit-picker">{{ item.riskLevel || '请选择' }}</text>
  52. </picker>
  53. </view>
  54. </view>
  55. <view class="empty-hint" v-if="risks.length === 0">暂无疾病风险评估</view>
  56. <view class="bottom-spacer"></view>
  57. </scroll-view>
  58. <health-knowledge-popup :visible="knowledgeVisible" :data="knowledgeData" @close="closeKnowledge" />
  59. <view class="bottom-bar" v-if="isEditing">
  60. <view class="btn-cancel" @tap="cancelEdit">取消</view>
  61. <view class="btn-save" @tap="saveEdit">保存</view>
  62. </view>
  63. </view>
  64. </template>
  65. <script>
  66. import { getReportDetail, editHealthReport, queryKnowledgeBase, queryIndicatorKnowledge } from '../../utils/api.js'
  67. import HealthKnowledgePopup from '../../components/health-knowledge-popup.vue'
  68. export default {
  69. components: { HealthKnowledgePopup },
  70. data() {
  71. return {
  72. reportId: null,
  73. reportData: null,
  74. risks: [],
  75. isEditing: false,
  76. editRisks: [],
  77. riskLevels: ['低风险', '需注意', '高风险', '异常'],
  78. knowledgeVisible: false,
  79. knowledgeData: {}
  80. }
  81. },
  82. computed: {
  83. importantRisks: function() {
  84. return this.getRisks().filter(function(item) {
  85. var level = item.riskLevel || ''
  86. return level === '需注意' || level === '高风险' || level === '异常'
  87. })
  88. },
  89. normalRisks: function() {
  90. return this.getRisks().filter(function(item) {
  91. var level = item.riskLevel || ''
  92. return level !== '需注意' && level !== '高风险' && level !== '异常'
  93. })
  94. }
  95. },
  96. onLoad: function(options) {
  97. this.reportId = options.reportId ? parseInt(options.reportId) : null
  98. if (this.reportId) {
  99. this.loadData()
  100. }
  101. },
  102. methods: {
  103. goBack: function() { uni.navigateBack() },
  104. /** 中文风险等级 → CSS安全英文 */
  105. _cssForRiskLevel: function(level) {
  106. var map = { '低风险': 'low', '需注意': 'warning', '高风险': 'high', '异常': 'abnormal' }
  107. return map[level] || 'low'
  108. },
  109. getRisks: function() {
  110. return this.isEditing ? this.editRisks : this.risks
  111. },
  112. loadData: function() {
  113. var self = this
  114. getReportDetail(this.reportId).then(function(res) {
  115. if (res.code === 200 && res.data) {
  116. self.reportData = res.data
  117. self.risks = (res.data.diseaseRisks || []).map(function(item) {
  118. item._cssClass = self._cssForRiskLevel(item.riskLevel)
  119. return item
  120. })
  121. }
  122. })
  123. },
  124. toggleEdit: function() {
  125. this.isEditing = true
  126. this.editRisks = JSON.parse(JSON.stringify(this.risks))
  127. },
  128. cancelEdit: function() {
  129. this.isEditing = false
  130. this.editRisks = []
  131. },
  132. saveEdit: function() {
  133. var self = this
  134. editHealthReport(self.reportId, { diseaseRisks: self.editRisks }, 0).then(function(res) {
  135. if (res.code === 200) {
  136. uni.showToast({ title: '已保存', icon: 'success' })
  137. self.isEditing = false
  138. self.loadData()
  139. } else {
  140. uni.showToast({ title: res.message || '保存失败', icon: 'none' })
  141. }
  142. })
  143. },
  144. riskBadgeText: function(level) {
  145. var map = { '低风险': '✓ 低', '需注意': '⚠ 注意', '高风险': '⚠ 高', '异常': '⚠ 异常' }
  146. return map[level] || level || ''
  147. },
  148. riskLevelIndex: function(level) {
  149. var idx = this.riskLevels.indexOf(level)
  150. return idx >= 0 ? idx : 0
  151. },
  152. onRiskLevelChange: function(e, item) {
  153. item.riskLevel = this.riskLevels[e.detail.value]
  154. },
  155. showKnowledge: function(type, name) {
  156. var self = this
  157. // 优先使用 KB v3 查询指标调整建议
  158. queryIndicatorKnowledge(name).then(function(res) {
  159. if (res.code === 200 && res.data) {
  160. var v3 = res.data
  161. var high = v3['偏高影响'] || ''
  162. var low = v3['偏低影响'] || ''
  163. self.knowledgeData = {
  164. itemName: v3['名称'] || name,
  165. category: v3['说明'] || '',
  166. description: (high ? '偏高影响:' + high + '\n' : '') + (low ? '偏低影响:' + low : ''),
  167. suggestion: v3['调整建议'] || ''
  168. }
  169. self.knowledgeVisible = true
  170. } else {
  171. // 回退旧版 DB 查询
  172. self.queryOldKnowledgeBase(type, name)
  173. }
  174. }).catch(function() {
  175. self.queryOldKnowledgeBase(type, name)
  176. })
  177. },
  178. queryOldKnowledgeBase: function(type, name) {
  179. var self = this
  180. queryKnowledgeBase(type, name).then(function(res) {
  181. if (res.code === 200 && res.data) {
  182. self.knowledgeData = res.data
  183. self.knowledgeVisible = true
  184. } else {
  185. self.knowledgeData = { itemName: name }
  186. self.knowledgeVisible = true
  187. }
  188. })
  189. },
  190. closeKnowledge: function() {
  191. this.knowledgeVisible = false
  192. this.knowledgeData = {}
  193. }
  194. }
  195. }
  196. </script>
  197. <style scoped>
  198. .page { min-height: 100vh; background: #f8f8f8; }
  199. .nav-bar { display: flex; align-items: center; padding: 20rpx 30rpx; background: #fff; position: relative; }
  200. .nav-back { padding: 10rpx 0; }
  201. .back-text { font-size: 28rpx; color: #4A9BD7; }
  202. .nav-title { flex: 1; text-align: center; font-size: 32rpx; font-weight: 600; }
  203. .nav-edit { font-size: 28rpx; color: #4A9BD7; padding: 10rpx 0; }
  204. .content { padding: 20rpx 30rpx; height: calc(100vh - 100rpx); }
  205. .section-label { font-size: 26rpx; color: #999; margin: 10rpx 0 16rpx; padding-left: 8rpx; }
  206. .risk-card { background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx; border-left: 8rpx solid #10B981; }
  207. .risk-level-warning { border-left-color: #F59E0B; }
  208. .risk-level-high { border-left-color: #C62828; }
  209. .risk-level-abnormal { border-left-color: #C62828; }
  210. .risk-header { display: flex; align-items: center; margin-bottom: 12rpx; }
  211. .risk-name { font-size: 30rpx; font-weight: 600; color: #333; flex: 1; }
  212. .risk-badge { padding: 4rpx 16rpx; border-radius: 20rpx; font-size: 22rpx; }
  213. .badge-low { background: #E8F5E9; color: #2E7D32; }
  214. .badge-warning { background: #FFF3E0; color: #E65100; }
  215. .badge-high { background: #FFEBEE; color: #C62828; }
  216. .badge-abnormal { background: #FFEBEE; color: #C62828; }
  217. .risk-detail-row { display: flex; gap: 24rpx; }
  218. .risk-value, .risk-level-text { font-size: 24rpx; color: #999; }
  219. .risk-edit-row { display: flex; align-items: center; gap: 12rpx; flex-wrap: wrap; }
  220. .edit-label { font-size: 26rpx; color: #666; }
  221. .edit-input { border: 1rpx solid #ddd; border-radius: 8rpx; padding: 8rpx 12rpx; font-size: 26rpx; width: 120rpx; }
  222. .edit-picker { border: 1rpx solid #ddd; border-radius: 8rpx; padding: 8rpx 12rpx; font-size: 26rpx; color: #333; }
  223. .empty-hint { text-align: center; color: #ccc; padding: 60rpx 0; font-size: 28rpx; }
  224. .bottom-bar { position: fixed; bottom: 0; left: 0; right: 0; display: flex; padding: 20rpx 30rpx; background: #fff; border-top: 1rpx solid #eee; gap: 20rpx; }
  225. .btn-cancel { flex: 1; padding: 20rpx; border: 1rpx solid #ddd; border-radius: 12rpx; text-align: center; font-size: 28rpx; color: #666; }
  226. .btn-save { flex: 1; padding: 20rpx; background: #4A9BD7; border-radius: 12rpx; text-align: center; font-size: 28rpx; color: #fff; }
  227. .bottom-spacer { height: 40rpx; }
  228. </style>