RadarChart.vue 5.4 KB

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