Kaynağa Gözat

feat: 完成瑞都价值分布看板

- 新增价值分布柱状图
- 按项目部展示原值和净值数据
- 优化柱体宽度和数值标签展示
- 适配瑞都看板缩放事件
Zimo 1 gün önce
ebeveyn
işleme
afbd3d267d
1 değiştirilmiş dosya ile 266 ekleme ve 1 silme
  1. 266 1
      src/views/pms/stat/rdkb/rd-value.vue

+ 266 - 1
src/views/pms/stat/rdkb/rd-value.vue

@@ -1,4 +1,269 @@
-<script lang="ts" setup></script>
+<script lang="ts" setup>
+import * as echarts from 'echarts'
+import {
+  ANIMATION,
+  CHART_RENDERER,
+  createLegend,
+  createTooltip,
+  FONT_FAMILY,
+  THEME
+} from '@/utils/kb'
+
+interface ValueDistributionItem {
+  projectDept: string
+  originalValue: number
+  netValue: number
+}
+
+const chartData: ValueDistributionItem[] = [
+  {
+    projectDept: '公摊',
+    originalValue: 2760.06,
+    netValue: 622.61
+  },
+  {
+    projectDept: '新疆',
+    originalValue: 3051.49,
+    netValue: 920.62
+  },
+  {
+    projectDept: '青海',
+    originalValue: 10038.67,
+    netValue: 3388.01
+  },
+  {
+    projectDept: '东部',
+    originalValue: 6060.15,
+    netValue: 881.46
+  },
+  {
+    projectDept: '西南连油',
+    originalValue: 4059.36,
+    netValue: 743.24
+  },
+  {
+    projectDept: '西南压裂',
+    originalValue: 11557.18,
+    netValue: 4401.63
+  },
+  {
+    projectDept: '伊拉克',
+    originalValue: 15417.01,
+    netValue: 6318.24
+  },
+  {
+    projectDept: '利比亚',
+    originalValue: 1942.14,
+    netValue: 1700.17
+  }
+]
+
+const chartRef = ref<HTMLDivElement>()
+let chart: echarts.ECharts | null = null
+const BAR_WIDTH = 22
+const LABEL_FONT_SIZE = 13
+
+function getBarStyle(color: (typeof THEME.color)[keyof typeof THEME.color]) {
+  return {
+    borderRadius: [12, 12, 0, 0],
+    shadowBlur: 12,
+    shadowColor: color.bg,
+    color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+      { offset: 0, color: color.light },
+      { offset: 0.55, color: color.mid },
+      { offset: 1, color: color.line }
+    ])
+  }
+}
+
+function formatMoney(value: number | string) {
+  return Number(value || 0).toLocaleString('en-US', {
+    minimumFractionDigits: 2,
+    maximumFractionDigits: 2
+  })
+}
+
+function formatProjectDept(value: string) {
+  if (value.length <= 5) return value
+  return value.replace('项目部', '\n项目部').replace('折算RMB', '\n折算RMB')
+}
+
+function getChartOption(data: ValueDistributionItem[]): echarts.EChartsOption {
+  const projectDepts = data.map((item) => item.projectDept)
+
+  return {
+    ...ANIMATION,
+    grid: {
+      ...THEME.grid,
+      top: 58,
+      right: 20,
+      bottom: 4
+    },
+    tooltip: createTooltip({
+      trigger: 'axis',
+      axisPointer: {
+        type: 'shadow',
+        shadowStyle: {
+          color: THEME.split
+        }
+      },
+      valueFormatter(value: number | string) {
+        return `${formatMoney(value)} 万元`
+      }
+    }),
+    legend: createLegend(
+      {
+        top: 4,
+        itemWidth: 12,
+        itemHeight: 12
+      },
+      ['原值', '净值']
+    ),
+    xAxis: {
+      type: 'category',
+      data: projectDepts,
+      axisLine: {
+        show: false
+      },
+      axisTick: {
+        show: false
+      },
+      axisLabel: {
+        interval: 0,
+        color: THEME.text.regular,
+        fontSize: 12,
+        fontFamily: FONT_FAMILY,
+        formatter(value: string) {
+          return formatProjectDept(value)
+        }
+      }
+    },
+    yAxis: {
+      type: 'value',
+      name: '价值(万元)',
+      splitNumber: 4,
+      nameTextStyle: {
+        color: THEME.text.regular,
+        fontSize: 13,
+        fontFamily: FONT_FAMILY
+      },
+      axisLine: {
+        show: false
+      },
+      axisTick: {
+        show: false
+      },
+      axisLabel: {
+        color: THEME.text.regular,
+        fontSize: 12,
+        fontFamily: FONT_FAMILY,
+        formatter(value: number) {
+          return formatMoney(value)
+        }
+      },
+      splitLine: {
+        lineStyle: {
+          color: THEME.split,
+          type: 'dashed'
+        }
+      }
+    },
+    series: [
+      {
+        name: '原值',
+        type: 'bar',
+        data: data.map((item) => item.originalValue),
+        barWidth: BAR_WIDTH,
+        barMaxWidth: 28,
+        barGap: '18%',
+        itemStyle: getBarStyle(THEME.color.blue),
+        label: {
+          show: true,
+          position: 'top',
+          distance: 10,
+          color: THEME.color.blue.strong,
+          fontSize: LABEL_FONT_SIZE,
+          fontWeight: 700,
+          fontFamily: FONT_FAMILY,
+          formatter(params: any) {
+            return Number(params.value) ? formatMoney(params.value) : ''
+          }
+        },
+        emphasis: {
+          focus: 'series',
+          itemStyle: {
+            ...getBarStyle(THEME.color.blue),
+            shadowColor: THEME.color.blue.shadow,
+            shadowBlur: 18
+          }
+        }
+      },
+      {
+        name: '净值',
+        type: 'bar',
+        data: data.map((item) => item.netValue),
+        barWidth: BAR_WIDTH,
+        barMaxWidth: 28,
+        itemStyle: getBarStyle(THEME.color.green),
+        label: {
+          show: true,
+          position: 'top',
+          distance: 6,
+          color: THEME.color.green.strong,
+          fontSize: LABEL_FONT_SIZE,
+          fontWeight: 700,
+          fontFamily: FONT_FAMILY,
+          formatter(params: any) {
+            return Number(params.value) ? formatMoney(params.value) : ''
+          }
+        },
+        emphasis: {
+          focus: 'series',
+          itemStyle: {
+            ...getBarStyle(THEME.color.green),
+            shadowColor: THEME.color.green.shadow,
+            shadowBlur: 18
+          }
+        }
+      }
+    ]
+  }
+}
+
+function initChart() {
+  if (!chartRef.value) return
+  chart?.dispose()
+  chart = echarts.init(chartRef.value, undefined, {
+    renderer: CHART_RENDERER
+  })
+  renderChart()
+}
+
+function renderChart() {
+  chart?.setOption(getChartOption(chartData), true)
+}
+
+function resizeChart() {
+  chart?.resize()
+}
+
+function destroyChart() {
+  chart?.dispose()
+  chart = null
+}
+
+onMounted(() => {
+  initChart()
+  window.addEventListener('resize', resizeChart)
+  window.addEventListener('rdkb:resize', resizeChart)
+})
+
+onUnmounted(() => {
+  window.removeEventListener('resize', resizeChart)
+  window.removeEventListener('rdkb:resize', resizeChart)
+  destroyChart()
+})
+</script>
 
 <template>
   <div class="panel flex flex-col">