examSummary.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. <template>
  2. <el-container>
  3. <el-main>
  4. <el-row>
  5. <el-col :span="24">
  6. <el-form>
  7. <el-form-item label="考试批次">
  8. <el-select
  9. v-model="examId"
  10. filterable
  11. remote
  12. :remote-method="getExams"
  13. clearable
  14. @clear="getExams"
  15. @change="changeExam"
  16. placeholder="请选择考试批次"
  17. >
  18. <el-option
  19. v-for="item in examList"
  20. :key="item.id"
  21. :label="item.name"
  22. :value="item.id"
  23. >
  24. </el-option>
  25. </el-select>
  26. </el-form-item>
  27. </el-form>
  28. </el-col>
  29. </el-row>
  30. <el-row :gutter="2">
  31. <el-col :span="10" class="chart-border">
  32. <div class="chart-header">考试进度情况</div>
  33. <div><v-chart :options="pieOptions" /></div>
  34. </el-col>
  35. <el-col :span="14" class="chart-border">
  36. <div class="chart-header">课程完成进度TOP5</div>
  37. <div><v-chart :options="lineOptions" /></div>
  38. </el-col>
  39. </el-row>
  40. <el-row style="margin-top:10px;">
  41. <el-col :span="24">
  42. <el-tabs type="card" v-model="activeName" @tab-click="handleClick">
  43. <el-tab-pane label="学习中心完成进度" name="first">
  44. <el-row style="margin-top:20px;">
  45. <el-col :span="24">
  46. <el-form>
  47. <el-form-item label="学习中心">
  48. <el-select
  49. v-model="orgId"
  50. filterable
  51. remote
  52. :remote-method="getOrgs"
  53. clearable
  54. @clear="getOrgs"
  55. @change="getOrgExamInfos"
  56. placeholder="请选择学习中心"
  57. >
  58. <el-option
  59. v-for="item in orgList"
  60. :key="item.id"
  61. :label="item.name"
  62. :value="item.id"
  63. >
  64. </el-option>
  65. </el-select>
  66. </el-form-item>
  67. </el-form>
  68. </el-col>
  69. <el-col :span="24">
  70. <el-table
  71. element-loading-text="数据加载中"
  72. :data="orgExamInfos"
  73. border
  74. >
  75. <el-table-column
  76. sortable
  77. label="学习中心代码"
  78. prop="orgCode"
  79. >
  80. </el-table-column>
  81. <el-table-column
  82. sortable
  83. label="学习中心名称"
  84. prop="orgName"
  85. >
  86. </el-table-column>
  87. <el-table-column
  88. sortable
  89. label="报考人数"
  90. prop="totalCount"
  91. >
  92. </el-table-column>
  93. <el-table-column
  94. sortable
  95. label="实考人数"
  96. prop="finishedCount"
  97. >
  98. </el-table-column>
  99. <el-table-column
  100. sortable
  101. label="完成比率"
  102. prop="finishedPercent"
  103. >
  104. </el-table-column>
  105. </el-table>
  106. </el-col>
  107. </el-row>
  108. </el-tab-pane>
  109. <el-tab-pane label="课程完成进度" name="two">
  110. <el-row style="margin-top:20px;">
  111. <el-col :span="24">
  112. <el-form>
  113. <el-form-item label="课程">
  114. <el-select
  115. v-model="courseId"
  116. filterable
  117. remote
  118. :remote-method="getCourses"
  119. clearable
  120. @clear="getCourses"
  121. @change="getCourseProgress"
  122. placeholder="请选择课程"
  123. >
  124. <el-option
  125. v-for="item in courseList"
  126. :key="item.id"
  127. :label="item.name"
  128. :value="item.id"
  129. >
  130. </el-option>
  131. </el-select>
  132. </el-form-item>
  133. </el-form>
  134. </el-col>
  135. <el-col :span="24">
  136. <el-table
  137. element-loading-text="数据加载中"
  138. :data="courseProgressList"
  139. border
  140. >
  141. <el-table-column
  142. sortable
  143. label="课程名称"
  144. prop="courseName"
  145. >
  146. </el-table-column>
  147. <el-table-column
  148. sortable
  149. label="课程代码"
  150. prop="courseCode"
  151. >
  152. </el-table-column>
  153. <el-table-column sortable label="报考人数" prop="allNum">
  154. </el-table-column>
  155. <el-table-column
  156. sortable
  157. label="实考人数"
  158. prop="completedNum"
  159. >
  160. </el-table-column>
  161. <el-table-column
  162. sortable
  163. label="完成比率"
  164. prop="completedProportion"
  165. >
  166. </el-table-column>
  167. </el-table>
  168. </el-col>
  169. </el-row>
  170. </el-tab-pane>
  171. </el-tabs>
  172. </el-col>
  173. </el-row>
  174. </el-main>
  175. </el-container>
  176. </template>
  177. <script>
  178. import { mapState } from "vuex";
  179. import ECharts from "vue-echarts/components/ECharts";
  180. import "echarts/lib/component/legend";
  181. import "echarts/lib/component/legendScroll";
  182. import "echarts/lib/chart/pie";
  183. import "echarts/lib/component/polar";
  184. import "echarts/lib/component/tooltip";
  185. import "echarts/lib/component/title";
  186. import "echarts/lib/chart/bar";
  187. import "echarts/lib/chart/line";
  188. export default {
  189. components: { "v-chart": ECharts },
  190. data() {
  191. return {
  192. examList: [],
  193. orgList: [],
  194. courseList: [],
  195. examId: "",
  196. orgId: "",
  197. courseId: "",
  198. activeName: "first",
  199. orgExamInfos: [],
  200. courseProgressList: [],
  201. lineOptions: {},
  202. pieOptions: {}
  203. };
  204. },
  205. computed: {
  206. ...mapState({ user: state => state.user })
  207. },
  208. methods: {
  209. getExams(examName) {
  210. if (!examName) {
  211. examName = "";
  212. }
  213. this.$http
  214. .get("/api/ecs_exam_work/exam/queryByNameLike", {
  215. params: { name: examName, examTypes: "ONLINE#OFFLINE" }
  216. })
  217. .then(response => {
  218. this.examList = response.data;
  219. });
  220. },
  221. getOrgs(orgName) {
  222. if (!orgName) {
  223. orgName = "";
  224. }
  225. var rootOrgId = this.user.rootOrgId;
  226. this.$http
  227. .get("/api/ecs_core/org/query", {
  228. params: {
  229. name: orgName,
  230. rootOrgId: rootOrgId,
  231. enable: true
  232. }
  233. })
  234. .then(response => {
  235. this.orgList = response.data;
  236. });
  237. },
  238. handleClick(tab, event) {
  239. console.log(tab, event);
  240. },
  241. getOrgExamInfos() {
  242. this.$http
  243. .post(
  244. "/api/ecs_oe_admin/exam/student/statistic/by/org?examId=" +
  245. this.examId +
  246. "&orgId=" +
  247. this.orgId
  248. )
  249. .then(response => {
  250. if (response.data && response.data.length > 0) {
  251. this.orgExamInfos = response.data;
  252. } else {
  253. this.orgExamInfos = [];
  254. }
  255. });
  256. },
  257. getCourseProgress() {
  258. this.$http
  259. .get("/api/ecs_oe_admin/exam/student/courseProgress/list", {
  260. params: {
  261. examId: this.examId,
  262. courseId: this.courseId
  263. }
  264. })
  265. .then(response => {
  266. if (response.data && response.data.length > 0) {
  267. this.courseProgressList = response.data;
  268. this.buildLine(response.data);
  269. } else {
  270. this.courseProgressList = [];
  271. }
  272. });
  273. },
  274. getCourses() {
  275. if (!this.examId) {
  276. return false;
  277. }
  278. this.$http
  279. .get("/api/ecs_oe_admin/exam/student/findCoursesByExamIdAndOrgId", {
  280. params: {
  281. examId: this.examId,
  282. orgId: this.orgId
  283. }
  284. })
  285. .then(response => {
  286. if (response.data && response.data.length > 0) {
  287. this.courseList = response.data;
  288. } else {
  289. this.courseList = [];
  290. }
  291. });
  292. },
  293. changeExam() {
  294. this.getPieData();
  295. this.getCourses();
  296. this.getOrgExamInfos();
  297. this.getCourseProgress();
  298. },
  299. getPieData() {
  300. if (!this.examId) {
  301. return;
  302. }
  303. this.$http
  304. .post(
  305. "/api/ecs_oe_admin/exam/student/statistic/by/finished?examId=" +
  306. this.examId
  307. )
  308. .then(response => {
  309. var resp = response.data;
  310. var optionData = {
  311. title: "考试人次:" + (resp.finished + resp.unFinished),
  312. legendData: [
  313. "未完成:" + resp.unFinished,
  314. "已完成:" + resp.finished
  315. ],
  316. seriesData: [
  317. { name: "未完成:" + resp.unFinished, value: resp.unFinished },
  318. { name: "已完成:" + resp.finished, value: resp.finished }
  319. ]
  320. };
  321. this.buildPieOptions(optionData);
  322. });
  323. },
  324. buildPieOptions(data) {
  325. var colors = ["#7CB5EC", "#FE8463"];
  326. this.pieOptions = {
  327. color: colors,
  328. title: {
  329. text: data.title,
  330. subtext: "",
  331. x: "left"
  332. },
  333. tooltip: {
  334. trigger: "item",
  335. formatter: "{b}人次<br/>占比:{d}%"
  336. },
  337. legend: {
  338. type: "scroll",
  339. orient: "vertical",
  340. right: 200,
  341. top: 30,
  342. data: data.legendData
  343. },
  344. series: [
  345. {
  346. name: "",
  347. type: "pie",
  348. radius: "50%",
  349. center: ["30%", "60%"],
  350. data: data.seriesData,
  351. itemStyle: {
  352. emphasis: {
  353. shadowBlur: 10,
  354. shadowOffsetX: 0,
  355. shadowColor: "rgba(0, 0, 0, 0.5)"
  356. }
  357. }
  358. }
  359. ]
  360. };
  361. },
  362. buildLine(courseProgressList) {
  363. courseProgressList.sort(function(a, b) {
  364. if (b["completedProportion"] != a["completedProportion"]) {
  365. return b["completedProportion"] - a["completedProportion"];
  366. } else if (b["allNum"] != a["allNum"]) {
  367. return b["allNum"] - a["allNum"];
  368. } else {
  369. return b["completedNum"] - a["completedNum"];
  370. }
  371. });
  372. var campusCount = 5;
  373. var courseProgressDataList = [];
  374. //找出5个完成比例最高的
  375. if (courseProgressList.length >= campusCount) {
  376. courseProgressDataList = courseProgressList.slice(0, campusCount);
  377. } else {
  378. courseProgressDataList = courseProgressList;
  379. }
  380. var xAxisData = [];
  381. var seriesBar = {
  382. name: "计划数",
  383. type: "bar",
  384. data: []
  385. };
  386. var seriesLine1 = {
  387. name: "完成数",
  388. type: "line",
  389. data: []
  390. };
  391. var seriesLine2 = {
  392. name: "完成比(%)",
  393. type: "line",
  394. data: []
  395. };
  396. var yAxis_maxScale1 = 0;
  397. for (var i = 0; i < courseProgressDataList.length; i++) {
  398. xAxisData.push(courseProgressDataList[i].courseName);
  399. seriesBar.data.push(courseProgressDataList[i].allNum);
  400. seriesLine1.data.push(courseProgressDataList[i].completedNum);
  401. seriesLine2.data.push(courseProgressDataList[i].completedProportion);
  402. if (courseProgressDataList[i].allNum > yAxis_maxScale1) {
  403. yAxis_maxScale1 = courseProgressDataList[i].allNum;
  404. }
  405. }
  406. var optionData = {
  407. legendData: ["计划数", "完成数", "完成比(%)"],
  408. xAxis: { data: xAxisData },
  409. series: [seriesBar, seriesLine1, seriesLine2],
  410. yAxis_maxScale1
  411. };
  412. this.buildLineOptions(optionData);
  413. },
  414. buildLineOptions(optionData) {
  415. var colors = ["#FE8463", "#66CCFF", "#675bba"];
  416. this.lineOptions = {
  417. color: colors,
  418. tooltip: {
  419. trigger: "axis",
  420. axisPointer: {
  421. type: "cross",
  422. crossStyle: {
  423. color: "#999"
  424. }
  425. }
  426. },
  427. toolbox: {
  428. feature: {
  429. dataView: { show: true, readOnly: false },
  430. magicType: { show: true, type: ["line", "bar"] },
  431. restore: { show: true },
  432. saveAsImage: { show: true }
  433. }
  434. },
  435. legend: {
  436. data: optionData.legendData
  437. },
  438. xAxis: [
  439. {
  440. type: "category",
  441. data: optionData.xAxis.data,
  442. axisPointer: {
  443. type: "shadow"
  444. }
  445. }
  446. ],
  447. yAxis: [
  448. {
  449. type: "value",
  450. name: "人次",
  451. min: 0,
  452. max: optionData.yAxis_maxScale1,
  453. interval: 10000,
  454. axisLabel: {
  455. formatter: "{value} "
  456. }
  457. },
  458. {
  459. type: "value",
  460. name: "完成比例",
  461. min: 0,
  462. max: 100,
  463. interval: 20,
  464. axisLabel: {
  465. formatter: "{value} %"
  466. }
  467. }
  468. ],
  469. series: optionData.series
  470. };
  471. }
  472. },
  473. created() {
  474. this.getExams();
  475. this.getOrgs();
  476. }
  477. };
  478. </script>
  479. <style>
  480. .chart-border {
  481. border: 1px solid #ddd;
  482. }
  483. .chart-header {
  484. color: #333;
  485. font-size: 14px;
  486. background-color: #f5f5f5;
  487. border-color: #ddd;
  488. padding: 10px 15px;
  489. border-bottom: 1px solid transparent;
  490. border-top-left-radius: 3px;
  491. border-top-right-radius: 3px;
  492. }
  493. </style>