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

feat(frontend): 通用报告块渲染器 report-blocks-renderer

Xiaogang Liao 1 месяц назад
Родитель
Сommit
2255631682
1 измененных файлов с 124 добавлено и 0 удалено
  1. 124 0
      cfc-frontend/components/report-blocks-renderer.vue

+ 124 - 0
cfc-frontend/components/report-blocks-renderer.vue

@@ -0,0 +1,124 @@
+<template>
+  <view class="blocks-renderer">
+    <view v-for="(block, bi) in blocks" :key="'b' + bi" class="block-wrap">
+      <!-- score: 评分圆环组 -->
+      <view v-if="block.type === 'score'" class="block score-block">
+        <view class="block-title">{{ block.title }}</view>
+        <view class="score-grid">
+          <view class="score-item" v-for="(item, si) in block.items" :key="'s' + si">
+            <view class="score-circle" :style="'border-color:' + (item.color || '#4A9BD7')">
+              <text class="score-value">{{ item.value }}</text>
+            </view>
+            <text class="score-label">{{ item.label }}</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- risk_group: 风险分组(按等级徽章) -->
+      <view v-else-if="block.type === 'risk_group'" class="block risk-block">
+        <view class="block-title">{{ block.title }}</view>
+        <view class="risk-card" v-for="(item, ri) in block.items" :key="'r' + ri"
+              :class="'risk-level-' + riskCss(item.level)">
+          <text class="risk-name">{{ item.name }}</text>
+          <text class="risk-value">风险值: {{ item.value || '--' }}</text>
+          <text class="risk-badge" :class="'badge-' + riskCss(item.level)">{{ riskText(item.level) }}</text>
+        </view>
+      </view>
+
+      <!-- indicator: 指标明细 -->
+      <view v-else-if="block.type === 'indicator'" class="block indicator-block">
+        <view class="block-title">{{ block.title }}</view>
+        <view class="indicator-item" v-for="(item, ii) in block.items" :key="'i' + ii">
+          <text class="ind-name">{{ item.name }}</text>
+          <text class="ind-value">{{ item.value }} {{ item.unit }}</text>
+          <text class="ind-status" :class="'status-' + statusCss(item.status)">{{ item.status }}</text>
+        </view>
+      </view>
+
+      <!-- list: 通用列表(columns 驱动) -->
+      <view v-else-if="block.type === 'list'" class="block list-block">
+        <view class="block-title">{{ block.title }}</view>
+        <view class="list-head" v-if="block.columns">
+          <text class="list-cell head-cell" v-for="(colItem, ci) in block.columns" :key="'c' + ci"
+                :style="'flex:' + (ci === 0 ? 2 : 1)">{{ colItem.label }}</text>
+        </view>
+        <view class="list-row" v-for="(item, li) in block.items" :key="'l' + li">
+          <text class="list-cell" :style="'flex:' + (ci === 0 ? 2 : 1)"
+                v-for="(colItem, ci) in block.columns" :key="'lc' + ci">{{ item[colItem.key] || '--' }}</text>
+        </view>
+      </view>
+
+      <!-- text: 文本段落 -->
+      <view v-else-if="block.type === 'text'" class="block text-block">
+        <view class="block-title">{{ block.title }}</view>
+        <text class="text-content">{{ block.content }}</text>
+      </view>
+
+      <!-- chart: 预留 -->
+      <view v-else class="block chart-block">
+        <view class="block-title">{{ block.title }}</view>
+        <text class="chart-placeholder">图表组件开发中</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  name: 'ReportBlocksRenderer',
+  props: {
+    blocks: { type: Array, default: function() { return [] } }
+  },
+  methods: {
+    riskCss: function(level) {
+      var important = ['需注意', '注意', '高风险', '异常']
+      return important.indexOf(level) >= 0 ? 'warning' : 'low'
+    },
+    riskText: function(level) {
+      var map = { '低风险': '✓ 低', '需注意': '⚠ 注意', '注意': '⚠ 注意', '高风险': '⚠ 高', '异常': '⚠ 异常' }
+      return map[level] || level || ''
+    },
+    statusCss: function(status) {
+      var map = { '偏高': 'high', '偏低': 'low', '缺乏': 'low', '不足': 'low', '过多': 'high', '异常': 'high' }
+      return map[status] || 'normal'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.blocks-renderer { width: 100%; }
+.block { background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 20rpx; }
+.block-title { font-size: 30rpx; font-weight: 600; color: #333; margin-bottom: 20rpx; }
+/* score */
+.score-grid { display: flex; flex-wrap: wrap; }
+.score-item { width: 33.33%; display: flex; flex-direction: column; align-items: center; margin-bottom: 24rpx; }
+.score-circle { width: 96rpx; height: 96rpx; border-radius: 50%; border: 8rpx solid; display: flex; align-items: center; justify-content: center; }
+.score-value { font-size: 30rpx; font-weight: 600; color: #333; }
+.score-label { font-size: 24rpx; color: #666; margin-top: 8rpx; }
+/* risk */
+.risk-card { display: flex; align-items: center; padding: 16rpx 0; border-bottom: 1rpx solid #f0f0f0; }
+.risk-card:last-child { border-bottom: none; }
+.risk-name { flex: 1; font-size: 28rpx; color: #333; }
+.risk-value { font-size: 24rpx; color: #999; margin-right: 16rpx; }
+.risk-badge { padding: 4rpx 14rpx; border-radius: 20rpx; font-size: 22rpx; }
+.badge-warning { background: #FFF3E0; color: #E65100; }
+.badge-low { background: #E8F5E9; color: #2E7D32; }
+/* indicator */
+.indicator-item { display: flex; align-items: center; padding: 14rpx 0; border-bottom: 1rpx solid #f0f0f0; }
+.indicator-item:last-child { border-bottom: none; }
+.ind-name { flex: 1; font-size: 26rpx; color: #333; }
+.ind-value { font-size: 26rpx; color: #666; margin-right: 16rpx; }
+.ind-status { font-size: 22rpx; padding: 2rpx 12rpx; border-radius: 16rpx; }
+.status-high { background: #FFEBEE; color: #C62828; }
+.status-low { background: #FFF3E0; color: #E65100; }
+.status-normal { background: #E8F5E9; color: #2E7D32; }
+/* list */
+.list-head { display: flex; padding: 12rpx 0; border-bottom: 2rpx solid #eee; }
+.list-row { display: flex; padding: 12rpx 0; border-bottom: 1rpx solid #f5f5f5; }
+.list-cell { font-size: 24rpx; color: #333; }
+.head-cell { font-size: 24rpx; color: #999; font-weight: 600; }
+/* text */
+.text-content { font-size: 26rpx; color: #555; line-height: 1.7; white-space: pre-wrap; }
+.chart-placeholder { font-size: 24rpx; color: #ccc; }
+</style>