deviceBarChart.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <template>
  2. <view class="overview-chart">
  3. <l-echart ref="chartRef" class="overview-chart__echart" :style="{ height: chartHeight }"></l-echart>
  4. </view>
  5. </template>
  6. <script setup>
  7. import * as echarts from "echarts";
  8. import { ref, watch, nextTick, computed } from "vue";
  9. const props = defineProps({
  10. chartData: {
  11. type: Array,
  12. default: () => [],
  13. },
  14. colors: {
  15. type: Array,
  16. default: () => ["#40b883", "#4a90e2", "#6797ed", "#9978fa", "#4ecee2", "#ffbb62", "#ff7d2e"],
  17. },
  18. });
  19. const chartRef = ref(null);
  20. const ROW_HEIGHT = 44;
  21. const chartHeight = computed(() => {
  22. const count = Math.max(props.chartData.length, 1);
  23. return `${Math.max(160, count * ROW_HEIGHT + 24)}px`;
  24. });
  25. function getSortedData() {
  26. const data = props.chartData.length ? [...props.chartData] : [{ name: "暂无数据", value: 0 }];
  27. return data.sort((a, b) => (Number(a.value) || 0) - (Number(b.value) || 0));
  28. }
  29. function buildOption() {
  30. const sortedData = getSortedData();
  31. const names = sortedData.map((item) => item.name);
  32. const values = sortedData.map((item) => Number(item.value) || 0);
  33. const totalValue = values.reduce((sum, val) => sum + val, 0) || Math.max(...values, 1);
  34. return {
  35. grid: {
  36. left: 8,
  37. right: 16,
  38. top: 8,
  39. bottom: 8,
  40. containLabel: true,
  41. },
  42. xAxis: {
  43. type: "value",
  44. show: false,
  45. max: totalValue,
  46. },
  47. yAxis: [
  48. {
  49. type: "category",
  50. data: names,
  51. inverse: true,
  52. axisLine: { show: false },
  53. axisTick: { show: false },
  54. axisLabel: {
  55. color: "#666",
  56. fontSize: 11,
  57. width: 96,
  58. overflow: "truncate",
  59. margin: 10,
  60. },
  61. },
  62. {
  63. type: "category",
  64. data: values,
  65. inverse: true,
  66. axisLine: { show: false },
  67. axisTick: { show: false },
  68. axisLabel: {
  69. color: "#666",
  70. fontSize: 11,
  71. margin: 12,
  72. formatter: (value) => value,
  73. },
  74. },
  75. ],
  76. series: [
  77. {
  78. type: "bar",
  79. data: values.map(() => totalValue),
  80. barWidth: 12,
  81. barGap: "-100%",
  82. barCategoryGap: "40%",
  83. silent: true,
  84. z: 0,
  85. itemStyle: {
  86. color: "#f0f2f5",
  87. borderRadius: 6,
  88. },
  89. },
  90. {
  91. type: "bar",
  92. data: values.map((value, index) => ({
  93. value,
  94. itemStyle: {
  95. color: props.colors[index % props.colors.length],
  96. borderRadius: 6,
  97. },
  98. })),
  99. barWidth: 12,
  100. barCategoryGap: "40%",
  101. z: 1,
  102. },
  103. ],
  104. };
  105. }
  106. function renderChart() {
  107. nextTick(() => {
  108. if (!chartRef.value) return;
  109. chartRef.value.init(echarts, (instance) => {
  110. instance.setOption(buildOption(), true);
  111. setTimeout(() => instance.resize(), 80);
  112. });
  113. });
  114. }
  115. watch(() => props.chartData, renderChart, { deep: true, immediate: true });
  116. </script>
  117. <style lang="scss" scoped>
  118. .overview-chart {
  119. &__echart {
  120. width: 100%;
  121. min-height: 160px;
  122. }
  123. }
  124. </style>