RadarChart.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <!-- 五维雷达图 -->
  2. <template>
  3. <div :id="id" :class="className" :style="{ height, width }" />
  4. </template>
  5. <script setup lang="ts">
  6. import * as echarts from "echarts";
  7. const props = defineProps({
  8. id: {
  9. type: String,
  10. default: "radarChart",
  11. required: true,
  12. },
  13. className: {
  14. type: String,
  15. default: "chart",
  16. },
  17. width: {
  18. type: String,
  19. default: "400px",
  20. },
  21. height: {
  22. type: String,
  23. default: "300px",
  24. },
  25. // dataSets:[[训练后],[训练前]]
  26. // dataSets:[[78,88,65,82,65],[28,38,45,32,25]]
  27. dataSets: {
  28. type: Array,
  29. default: [] as Array<number>,
  30. },
  31. // star:[1,2,3,4,5]
  32. star: {
  33. type: Array,
  34. default: [] as Array<number>,
  35. },
  36. tag: {
  37. type: Boolean,
  38. default: false,
  39. },
  40. });
  41. // 是否有对比数据
  42. const isCompare = props.dataSets?.length > 1;
  43. // let defaultData;
  44. // let beforeData;
  45. // if (isCompare) {
  46. // defaultData = props.dataSets?.[1];
  47. // beforeData = props.dataSets?.[0];
  48. // } else {
  49. // defaultData = props.dataSets?.[0];
  50. // }
  51. const globalColor = {
  52. default: "#ffb72d",
  53. before: "#937dff",
  54. font: "#063b79",
  55. white: "#ffffff",
  56. red: "#F45028",
  57. orange: "#FF9F29",
  58. yellow: "#EDCB00",
  59. green: "#53BE07",
  60. blue: "#03C5EC",
  61. };
  62. /**
  63. * 指示器 及 状态tag 自定义rich
  64. */
  65. const tagBaseRich = (bgColor) => {
  66. return {
  67. color: globalColor.white,
  68. padding: [4, 10, 4, 10],
  69. borderRadius: 10,
  70. backgroundColor: bgColor,
  71. };
  72. };
  73. const indicatorRich = {
  74. tit: {
  75. color: globalColor.font,
  76. fontSize: 14,
  77. padding: 5,
  78. },
  79. red: tagBaseRich(globalColor.red),
  80. orange: tagBaseRich(globalColor.orange),
  81. yellow: tagBaseRich(globalColor.yellow),
  82. green: tagBaseRich(globalColor.green),
  83. blue: tagBaseRich(globalColor.blue),
  84. };
  85. const indicatorFormatter = (name, indicator) => {
  86. if (!props.tag) {
  87. return `{tit|${name}}`;
  88. }
  89. const { star } = indicator;
  90. const label = [];
  91. label.push(`{tit|${name}}`);
  92. if (star === 1) {
  93. label.push(`{red|重度不足}`);
  94. }
  95. if (star === 2) {
  96. label.push("{orange|轻度不足}");
  97. }
  98. if (star === 3) {
  99. label.push("{yellow|中等水平}");
  100. }
  101. if (star === 4) {
  102. label.push("{green|良好水平}");
  103. }
  104. if (star === 5) {
  105. label.push("{blue|优秀水平}");
  106. }
  107. return label.join(`\n`);
  108. };
  109. /**
  110. * 在图中定位每个数据的位置
  111. */
  112. const positionLabelData = (data, idx) => {
  113. const labelValue = [0, 0, 0, 0, 0];
  114. labelValue[idx] = data[idx];
  115. // idx = 0,1,2,3,4 对应 top,left,left,right,right
  116. let pos = "bottom";
  117. if (idx === 0) {
  118. pos = "top";
  119. }
  120. if (idx === 1 || idx === 2) {
  121. pos = "left";
  122. }
  123. if (idx === 3 || idx === 4) {
  124. pos = "right";
  125. }
  126. return {
  127. value: labelValue,
  128. label: {
  129. show: true,
  130. fontSize: 12,
  131. position: pos,
  132. formatter: (params) => {
  133. return params.value > 0 ? params.value : "";
  134. },
  135. },
  136. };
  137. };
  138. const positionFormatter = (data) => {
  139. const result = [];
  140. for (let idx in data) {
  141. result.push(positionLabelData(data, idx * 1));
  142. }
  143. return result;
  144. };
  145. /**
  146. * 配置项
  147. */
  148. const options = {
  149. color: [globalColor.default],
  150. legend: {
  151. right: "5%",
  152. top: "5%",
  153. data: [
  154. { name: "训练前", icon: "circle" },
  155. { name: "训练后", icon: "circle" },
  156. ],
  157. textStyle: { fontSize: 12 },
  158. show: false,
  159. },
  160. radar: {
  161. radius: "50%",
  162. center: ["50%", "55%"],
  163. // 指示器 及 状态tag
  164. indicator: [
  165. { name: "专注力平均值", max: 100, star: props.star?.[0] },
  166. { name: "高专注占比", max: 100, star: props.star?.[1] },
  167. { name: "专注唤醒效率", max: 100, star: props.star?.[2] },
  168. { name: "整体和谐度", max: 100, star: props.star?.[3] },
  169. { name: "专注力稳定度", max: 100, star: props.star?.[4] },
  170. ],
  171. axisName: {
  172. rich: indicatorRich,
  173. formatter: indicatorFormatter,
  174. },
  175. },
  176. series: [
  177. // 数据分散定位
  178. {
  179. name: "训练后",
  180. type: "radar",
  181. symbolSize: 0,
  182. label: {
  183. show: true,
  184. color: globalColor.default,
  185. fontSize: 16,
  186. distance: 5,
  187. },
  188. lineStyle: { width: 0 },
  189. data: positionFormatter(props.dataSets?.[0]),
  190. },
  191. // 覆盖区域的样式
  192. {
  193. name: "训练前 vs 训练后",
  194. type: "radar",
  195. symbolSize: 5,
  196. data: [
  197. {
  198. value: props.dataSets?.[0],
  199. name: "训练后",
  200. lineStyle: { color: globalColor.default },
  201. itemStyle: { color: globalColor.default },
  202. areaStyle: {
  203. color: globalColor.default,
  204. opacity: 0.5,
  205. },
  206. },
  207. ],
  208. },
  209. ],
  210. };
  211. const getOptions = () => {
  212. if (isCompare) {
  213. options.color = [globalColor.before, globalColor.default];
  214. options.legend.show = true;
  215. options.series[1].data.unshift({
  216. value: props.dataSets?.[1],
  217. name: "训练前",
  218. lineStyle: { color: globalColor.before },
  219. itemStyle: { color: globalColor.before },
  220. areaStyle: {
  221. color: globalColor.before,
  222. opacity: 0.5,
  223. },
  224. });
  225. options.series.unshift({
  226. name: "训练前",
  227. type: "radar",
  228. symbolSize: 0,
  229. label: {
  230. show: true,
  231. color: globalColor.before,
  232. fontSize: 16,
  233. //distance: 5
  234. },
  235. lineStyle: { width: 0 },
  236. data: positionFormatter(props.dataSets?.[1]),
  237. });
  238. }
  239. return options;
  240. };
  241. onMounted(() => {
  242. const chart = echarts.init(
  243. document.getElementById(<string>props.id) as HTMLDivElement
  244. );
  245. chart.setOption(getOptions());
  246. window.addEventListener("resize", () => {
  247. chart.resize();
  248. });
  249. });
  250. </script>