ExamDashboard.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <template>
  2. <el-row>
  3. <el-row class="movie-list">
  4. <el-card>
  5. <div slot="header" class="clearfix">
  6. <span>ExamSysDiagram</span>
  7. </div>
  8. <div>
  9. <el-steps active="0" simple>
  10. <el-step title="管理员" icon="el-icon-s-custom" />
  11. <el-step title="普通用户" icon="el-icon-user" />
  12. </el-steps>
  13. <el-steps active="0" finish-status="success" simple style="margin-top: 20px">
  14. <el-step title="科目" icon="el-icon-s-grid" />
  15. <el-step title="试题" icon="el-icon-tickets" />
  16. <el-step title="试卷" icon="el-icon-notebook-2" />
  17. </el-steps>
  18. <el-steps active="0" finish-status="success" simple style="margin-top: 20px">
  19. <el-step title="试卷列表" icon="el-icon-files" />
  20. <el-step title="试卷测评" icon="el-icon-loading" />
  21. <el-step title="试卷批改" icon="el-icon-edit" />
  22. <el-step title="试卷结果" icon="el-icon-s-data" />
  23. </el-steps>
  24. </div>
  25. </el-card>
  26. </el-row>
  27. <el-row class="movie-list">
  28. <el-card>
  29. <div slot="header" class="clearfix">
  30. <span style="margin-right: 5px">选择图表</span>
  31. <el-select
  32. v-model="selectedValue"
  33. clearable
  34. placeholder="选择图表"
  35. @change="onSelectChange"
  36. >
  37. <el-option label="考试通过率" value="1" />
  38. <el-option label="考试次数占比" value="2" />
  39. <el-option label="考试通过率折线图" value="3" />
  40. <el-option label="新用户激活数" value="4" />
  41. </el-select>
  42. </div>
  43. <div id="chart1" style="height:400px;" />
  44. </el-card>
  45. </el-row>
  46. <el-row class="movie-list">
  47. <el-col :md="12" class="movie-list">
  48. <el-card>
  49. <div id="chart2" style="height:400px;" />
  50. </el-card>
  51. </el-col>
  52. <el-col :md="12" class="movie-list">
  53. <el-card>
  54. <div id="chart3" style="height:400px;" />
  55. </el-card>
  56. </el-col>
  57. </el-row>
  58. </el-row>
  59. </template>
  60. <script>
  61. import { getExamCount, getExamPassRate } from '@/api/exam'
  62. export default {
  63. name: 'ExamDashboard',
  64. data() {
  65. return {
  66. // 考试名称
  67. examNames: [],
  68. // 考试通过率
  69. passRate: [],
  70. // 饼图的数据
  71. pieData: [],
  72. selectedValue: '1',
  73. chartOption: null
  74. }
  75. },
  76. created() {
  77. // 页面数据加载的等待状态栏
  78. this.loading = this.$loading({
  79. body: true,
  80. lock: true,
  81. text: '数据拼命加载中,(*╹▽╹*)',
  82. spinner: 'el-icon-loading'
  83. })
  84. this.getExamPassRate()
  85. this.getExamNumbers()
  86. },
  87. methods: {
  88. onSelectChange() {
  89. this.getExamPassRate()
  90. },
  91. async getExamPassRate() {
  92. await getExamPassRate().then((resp) => {
  93. if (resp.code === 0) {
  94. this.examNames = resp.data[0].split(',')
  95. this.passRate = resp.data[1].split(',')
  96. const intValue = parseInt(this.selectedValue)
  97. if (intValue === 1) {
  98. this.drawLine()
  99. } else if (intValue === 2) {
  100. this.drawBrokenLine()
  101. } else if (intValue === 3) {
  102. this.drawImg4()
  103. } else {
  104. this.drawChart5()
  105. }
  106. const myChart = this.$echarts.init(document.getElementById('chart1'))
  107. // setOption 是 merge,而非赋值, setOption 支持 notMerge 参数, 值为 true 时重新渲染
  108. myChart.setOption(this.chartOption, true)
  109. this.loading.close()
  110. }
  111. })
  112. },
  113. // 考试通过率柱状图
  114. drawLine() {
  115. this.chartOption = {
  116. title: {
  117. text: '考试通过率',
  118. subtext: 'dashbord1',
  119. x: 'center',
  120. y: 'top',
  121. textAlign: 'center'
  122. },
  123. tooltip: {},
  124. xAxis: {
  125. data: this.examNames
  126. },
  127. yAxis: {},
  128. series: [{
  129. name: '通过率',
  130. type: 'bar',
  131. data: this.passRate
  132. }]
  133. }
  134. },
  135. // 通过率的折线图
  136. drawBrokenLine() {
  137. this.chartOption = {
  138. // 标题
  139. title: {
  140. text: '考试通过率折线图',
  141. x: 'center'
  142. },
  143. // x轴
  144. xAxis: {
  145. data: this.examNames
  146. },
  147. // y轴没有显式设置,根据值自动生成y轴
  148. yAxis: {},
  149. // 数据-data是最终要显示的数据
  150. series: [{
  151. name: '通过率',
  152. type: 'line',
  153. areaStyle: {
  154. normal: {}
  155. },
  156. data: this.passRate
  157. }]
  158. }
  159. },
  160. drawImg4() {
  161. this.chartOption = {
  162. color: ['#cd5c5c'],
  163. textStyle: {
  164. color: 'black'
  165. },
  166. tooltip: {
  167. trigger: 'axis',
  168. axisPointer: {
  169. type: 'shadow'
  170. },
  171. formatter: '{a} <br/>{b} : {c}'
  172. },
  173. grid: {
  174. containLabel: true
  175. },
  176. xAxis: {
  177. type: 'value',
  178. boundaryGap: [0, 0.01],
  179. axisLine: {
  180. lineStyle: {
  181. color: '#fff'
  182. }
  183. },
  184. 'axisLabel': {
  185. 'interval': 0,
  186. fontSize: 18,
  187. formatter: '{value}'
  188. }
  189. },
  190. yAxis: {
  191. axisLine: {
  192. lineStyle: {
  193. color: '#fff'
  194. }
  195. },
  196. 'axisLabel': {
  197. 'interval': 0,
  198. fontSize: 18
  199. },
  200. type: 'category',
  201. data: this.examNames
  202. },
  203. series: [{
  204. name: '通过率:',
  205. type: 'bar',
  206. data: this.passRate
  207. }]
  208. }
  209. },
  210. drawChart5() {
  211. this.chartOption = {
  212. title: {
  213. text: '卡拉云新用户激活数据',
  214. subtext: 'Demo 虚构数据',
  215. x: 'center'
  216. },
  217. legend: { // 图例配置选项
  218. orient: 'horizontal', // 图例布局方式:水平 'horizontal' 、垂直 'vertical'
  219. x: 'left', // 横向放置位置,选项:'center'、'left'、'right'、'number'(横向值 px)
  220. y: 'top', // 纵向放置位置,选项:'top'、'bottom'、'center'、'number'(纵向值 px)
  221. data: ['猜想', '预期', '实际']
  222. },
  223. grid: { // 图表距离边框的距离,可用百分比和数字(px)配置
  224. top: '20%',
  225. left: '3%',
  226. right: '10%',
  227. bottom: '5%',
  228. containLabel: true
  229. },
  230. xAxis: {
  231. name: '月份',
  232. type: 'category',
  233. data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
  234. },
  235. yAxis: {
  236. name: '人次',
  237. type: 'value',
  238. min: 0, // 配置 Y 轴刻度最小值
  239. max: 4000, // 配置 Y 轴刻度最大值
  240. splitNumber: 7 // 配置 Y 轴数值间隔
  241. },
  242. series: [
  243. {
  244. name: '猜想',
  245. data: [454, 226, 891, 978, 901, 581, 400, 543, 272, 955, 1294, 1581],
  246. type: 'line',
  247. symbolSize: function(value) { // 点的大小跟随数值增加而变大
  248. return value / 150
  249. },
  250. symbol: 'circle',
  251. itemStyle: {
  252. normal: {
  253. label: {
  254. show: true
  255. },
  256. lineStyle: {
  257. color: 'rgba(0,0,0,0)'// 折线颜色设置为0,即只显示点,不显示折线
  258. }
  259. }
  260. }
  261. },
  262. {
  263. name: '预期',
  264. data: [2455, 2534, 2360, 2301, 2861, 2181, 1944, 2197, 1745, 1810, 2283, 2298],
  265. type: 'line',
  266. symbolSize: 8, // 设置折线上圆点大小
  267. itemStyle: {
  268. normal: {
  269. label: {
  270. show: true // 在折线拐点上显示数据
  271. },
  272. lineStyle: {
  273. width: 3, // 设置虚线宽度
  274. type: 'dotted' // 虚线'dotted' 实线'solid'
  275. }
  276. }
  277. }
  278. },
  279. {
  280. name: '实际',
  281. data: [1107, 1352, 1740, 1968, 1647, 1570, 1343, 1757, 2547, 2762, 3170, 3665],
  282. type: 'line',
  283. symbol: 'circle', // 实心圆点
  284. smooth: 0.5 // 设置折线弧度
  285. }
  286. ],
  287. color: ['#3366CC', '#FFCC99', '#99CC33'] // 三个折线的颜色
  288. }
  289. },
  290. // 获取考试次数数据
  291. async getExamNumbers() {
  292. await getExamCount().then((resp) => {
  293. const examNames = resp.data[0].split(',')
  294. const examNumbers = resp.data[1].split(',')
  295. examNames.forEach((item, index) => {
  296. this.pieData.push({
  297. name: item,
  298. value: parseInt(examNumbers[index])
  299. })
  300. })
  301. this.drawPie()
  302. })
  303. },
  304. // 考试次数饼图
  305. drawPie() {
  306. // 基于准备好的dom,初始化echarts实例
  307. const myChart = this.$echarts.init(document.getElementById('chart2'))
  308. const option = {
  309. title: {
  310. text: '考试次数占比',
  311. subtext: 'dashbord2',
  312. x: 'center'
  313. },
  314. tooltip: {
  315. trigger: 'item',
  316. formatter: '{a} <br/>{b} : {c}次 ({d}%)'
  317. },
  318. legend: {
  319. orient: 'vertical',
  320. left: 'left',
  321. data: this.pieData
  322. },
  323. series: [
  324. {
  325. name: '考试次数',
  326. type: 'pie',
  327. radius: '55%',
  328. data: this.pieData,
  329. roseType: 'angle',
  330. itemStyle: {
  331. normal: {
  332. shadowBlur: 200,
  333. shadowColor: 'rgba(0, 0, 0, 0.5)'
  334. }
  335. }
  336. }
  337. ]
  338. }
  339. myChart.setOption(option)
  340. }
  341. }
  342. }
  343. </script>
  344. <style scoped lang="scss">
  345. .movie-list {
  346. padding-top: 5px;
  347. padding-left: 5px;
  348. padding-right: 5px;
  349. }
  350. </style>