trainData.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. import { Ref, computed, defineComponent, onMounted, reactive, ref } from 'vue';
  2. import styles from '../index2.module.less';
  3. import { NButton, NDataTable, NNumberAnimation, NSpace } from 'naive-ui';
  4. import { useECharts } from '@/hooks/web/useECharts';
  5. import Pagination from '/src/components/pagination';
  6. import { getTimes } from '/src/utils/dateFormat';
  7. import { getTrainingStat } from '../../data-module/api';
  8. import { useRoute, useRouter } from 'vue-router';
  9. import { getTrainingList } from '../../classList/api';
  10. import TheEmpty from '/src/components/TheEmpty';
  11. export default defineComponent({
  12. name: 'home-trainData',
  13. props: {
  14. timer: {
  15. type: Array,
  16. defaut: () => []
  17. }
  18. },
  19. setup(props, { expose }) {
  20. const chartRef = ref<HTMLDivElement | null>(null);
  21. const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
  22. const qualifiedFlag = ref(true);
  23. const unqualifiedFlag = ref(true);
  24. const router = useRouter();
  25. const route = useRoute();
  26. const payForm = reactive({
  27. height: '360px',
  28. width: '100%',
  29. studentNum: 0,
  30. paymentAmount: 0,
  31. dateList: [],
  32. studentList: [],
  33. payInfoList: []
  34. });
  35. const totalDateRef = ref({
  36. qualifiedRate: 0,
  37. qualifiedStudentCount: 0,
  38. submitStudentCount: 0,
  39. totalStudentCount: 0,
  40. trainingCount: 0,
  41. trainingRate: 0 //
  42. } as any);
  43. const state = reactive({
  44. loading: false,
  45. pagination: {
  46. page: 1,
  47. rows: 10,
  48. pageTotal: 4
  49. },
  50. tableList: [] as any,
  51. goCourseVisiable: false
  52. });
  53. const currentTimer = computed(() => {
  54. return props.timer;
  55. });
  56. const columns = () => {
  57. return [
  58. {
  59. title: '布置老师',
  60. key: 'teacherName'
  61. },
  62. {
  63. title: '布置时间',
  64. key: 'createTime',
  65. render(row: any) {
  66. return <>{row.createTime}</>;
  67. }
  68. },
  69. {
  70. title: '截止时间',
  71. key: 'expireDate',
  72. render(row: any) {
  73. return <>{row.expireDate}</>;
  74. }
  75. },
  76. {
  77. title: '训练状态',
  78. key: 'status',
  79. render(row: any) {
  80. return row.status == 0 ? (
  81. <div class={styles.indDot}>
  82. {' '}
  83. <span></span> 进行中
  84. </div>
  85. ) : (
  86. <div class={styles.endDot}>
  87. <span></span>已结束
  88. </div>
  89. );
  90. }
  91. },
  92. {
  93. title: '布置人数',
  94. key: 'expectNum'
  95. },
  96. {
  97. title: '提交人数',
  98. key: 'trainingNum'
  99. },
  100. {
  101. title: '合格人数',
  102. key: 'standardNum'
  103. },
  104. {
  105. title: '提交率',
  106. key: 'trainingRate',
  107. render(row: any) {
  108. return <>{row.trainingRate}%</>;
  109. }
  110. },
  111. {
  112. title: '合格率',
  113. key: 'qualifiedRate',
  114. render(row: any) {
  115. return <>{row.qualifiedRate}%</>;
  116. }
  117. },
  118. // {
  119. // title: '',
  120. // key: 'sex',
  121. // render(row: any) {
  122. // return <>{row.sex == '0' ? '女' : '男'}</>;
  123. // }
  124. // },
  125. {
  126. title: '操作',
  127. key: 'id',
  128. render(row: any) {
  129. return (
  130. <NSpace>
  131. <NButton
  132. text
  133. type="primary"
  134. onClick={() => gotoWorkDetail(row)}>
  135. 详情
  136. </NButton>
  137. </NSpace>
  138. );
  139. }
  140. }
  141. ];
  142. };
  143. const gotoWorkDetail = (row: any) => {
  144. console.log(row);
  145. router.push({
  146. path: '/afterWorkDetail',
  147. query: {
  148. ...route.query,
  149. teacherName: row.teacherName,
  150. trainingId: row.id,
  151. id: row.classGroupId,
  152. name: row.classGroupName
  153. }
  154. });
  155. };
  156. const getList = async () => {
  157. try {
  158. const res = await getTrainingStat({
  159. ...getTimes(
  160. currentTimer.value,
  161. ['startTime', 'endTime'],
  162. 'YYYY-MM-DD'
  163. )
  164. });
  165. totalDateRef.value = { ...res.data };
  166. payForm.dateList = res.data.trainingStatDetails.map((item: any) => {
  167. return item.date;
  168. });
  169. payForm.payInfoList = res.data.trainingStatDetails.map((item: any) => {
  170. return item.qualifiedStudentCount;
  171. });
  172. payForm.studentList = res.data.trainingStatDetails.map((item: any) => {
  173. return item.unqualifiedStudentCount;
  174. });
  175. setChart();
  176. } catch (e) {
  177. console.log(e);
  178. }
  179. try {
  180. const res = await getTrainingList({
  181. ...state.pagination,
  182. ...getTimes(
  183. currentTimer.value,
  184. ['startTime', 'endTime'],
  185. 'YYYY-MM-DD'
  186. )
  187. });
  188. state.tableList = res.data.rows;
  189. state.pagination.pageTotal = res.data.total;
  190. state.loading = false;
  191. } catch (e) {
  192. state.loading = false;
  193. console.log(e);
  194. }
  195. };
  196. expose({ getList });
  197. const setChart = () => {
  198. setOptions({
  199. tooltip: {
  200. trigger: 'axis',
  201. axisPointer: {
  202. lineStyle: {
  203. width: 2,
  204. color: '#A9C7FF'
  205. }
  206. }
  207. },
  208. legend: {
  209. show: false,
  210. selected: {
  211. //在这里设置默认展示就ok了
  212. 合格人数: qualifiedFlag.value,
  213. 不合格人数: unqualifiedFlag.value
  214. }
  215. },
  216. xAxis: {
  217. type: 'category',
  218. boundaryGap: true,
  219. axisLabel: {
  220. show: true
  221. // interval: 0
  222. },
  223. data: payForm.dateList
  224. // splitLine: {
  225. // show: true,
  226. // lineStyle: {
  227. // width: 2,
  228. // type: 'solid',
  229. // color: 'rgba(226,226,226,0.5)'
  230. // }
  231. // }
  232. // axisTick: {
  233. // show: false
  234. // }
  235. },
  236. yAxis: [
  237. {
  238. type: 'value',
  239. axisLabel: {
  240. formatter: '{value}人'
  241. },
  242. axisTick: {
  243. show: false
  244. },
  245. splitArea: {
  246. show: false,
  247. areaStyle: {
  248. color: ['rgba(255,255,255,0.2)']
  249. }
  250. },
  251. minInterval: 1,
  252. splitNumber: 5
  253. }
  254. ],
  255. grid: {
  256. left: '1%',
  257. right: '1%',
  258. top: '2 %',
  259. bottom: 0,
  260. containLabel: true
  261. },
  262. series: [
  263. {
  264. // smooth: true,
  265. data: payForm.studentList,
  266. symbolSize: 10,
  267. type: 'line',
  268. name: '不合格人数',
  269. symbol: 'circle',
  270. smooth: true,
  271. itemStyle: {
  272. color: '#FF7AA7',
  273. borderColor: '#fff',
  274. borderWidth: 3
  275. },
  276. lineStyle: {
  277. width: 3 //设置线条粗细
  278. },
  279. areaStyle: {
  280. color: {
  281. type: 'linear',
  282. x: 0,
  283. y: 0,
  284. x2: 0,
  285. y2: 1,
  286. colorStops: [
  287. {
  288. offset: 0,
  289. color: 'rgba(255, 243, 246, 1)'
  290. // 0% 处的颜色
  291. },
  292. {
  293. offset: 1,
  294. // 100% 处的颜色
  295. color: 'rgba(255, 246, 248, 0)'
  296. }
  297. ]
  298. }
  299. },
  300. emphasis: {
  301. disabled: true
  302. }
  303. },
  304. {
  305. data: payForm.payInfoList,
  306. type: 'line',
  307. name: '合格人数',
  308. symbolSize: 10,
  309. symbol: 'circle',
  310. smooth: true,
  311. itemStyle: {
  312. color: '#198CFE',
  313. borderColor: '#fff',
  314. borderWidth: 3
  315. },
  316. lineStyle: {
  317. width: 2 //设置线条粗细
  318. },
  319. areaStyle: {
  320. color: {
  321. type: 'linear',
  322. x: 0,
  323. y: 0,
  324. x2: 0,
  325. y2: 1,
  326. colorStops: [
  327. {
  328. offset: 0,
  329. color: 'rgba(212, 231, 255, 1)'
  330. // 0% 处的颜色
  331. },
  332. {
  333. offset: 1,
  334. color: 'rgba(221, 235, 254, 0)' // 100% 处的颜色
  335. }
  336. ]
  337. }
  338. },
  339. emphasis: {
  340. disabled: true
  341. }
  342. }
  343. ],
  344. formatter: (item: any) => {
  345. if (Array.isArray(item)) {
  346. return [
  347. item[0].axisValueLabel,
  348. ...item.map(
  349. (d: any) =>
  350. `<br/>${
  351. d.marker
  352. }<span style="margin-top:10px;margin-left:5px;font-size: 13px;font-weight: 500;
  353. color: #333333;
  354. line-height: 18px;">${d.seriesName}: ${
  355. d.value
  356. }${'人'} </span>`
  357. )
  358. ].join('');
  359. } else {
  360. return item;
  361. }
  362. }
  363. // dataZoom: [
  364. // {
  365. // type: 'slider',
  366. // start: 5,
  367. // end: 100,
  368. // filterMode: 'empty'
  369. // }
  370. // ]
  371. });
  372. };
  373. onMounted(() => {
  374. getList();
  375. });
  376. return () => (
  377. <>
  378. <div class={styles.homeTrainData}>
  379. <div class={styles.TrainDataTop}>
  380. <div class={styles.TrainDataTopLeft}>
  381. <div class={styles.TrainDataItem}>
  382. <p class={styles.TrainDataItemTitle}>
  383. <div>
  384. <span>
  385. <NNumberAnimation
  386. from={0}
  387. to={
  388. totalDateRef.value.trainingCount
  389. }></NNumberAnimation>
  390. </span>
  391. </div>
  392. </p>
  393. <p class={styles.TrainDataItemsubTitle}>作业次数</p>
  394. </div>
  395. <div class={styles.TrainDataItem}>
  396. <p class={styles.TrainDataItemTitle}>
  397. <div>
  398. <span>
  399. <NNumberAnimation
  400. from={0}
  401. to={
  402. totalDateRef.value.totalStudentCount
  403. }></NNumberAnimation>
  404. </span>
  405. 人次
  406. </div>
  407. </p>
  408. <p class={styles.TrainDataItemsubTitle}>应交总人次</p>
  409. </div>
  410. <div class={styles.TrainDataItem}>
  411. <p class={styles.TrainDataItemTitle}>
  412. <div>
  413. <span>
  414. <NNumberAnimation
  415. from={0}
  416. to={
  417. totalDateRef.value.submitStudentCount
  418. }></NNumberAnimation>
  419. </span>
  420. 人次
  421. </div>
  422. </p>
  423. <p class={styles.TrainDataItemsubTitle}>提交总人次</p>
  424. </div>
  425. <div class={styles.TrainDataItem}>
  426. <p class={styles.TrainDataItemTitle}>
  427. <div>
  428. <span>
  429. <NNumberAnimation
  430. from={0}
  431. to={
  432. totalDateRef.value.qualifiedStudentCount
  433. }></NNumberAnimation>
  434. </span>
  435. 人次
  436. </div>
  437. </p>
  438. <p class={styles.TrainDataItemsubTitle}>合格总人次</p>
  439. </div>
  440. <div class={styles.TrainDataItem}>
  441. <p class={styles.TrainDataItemTitle}>
  442. <div>
  443. <span>
  444. <NNumberAnimation
  445. from={0}
  446. to={totalDateRef.value.trainingRate}></NNumberAnimation>
  447. </span>
  448. %
  449. </div>
  450. </p>
  451. <p class={styles.TrainDataItemsubTitle}>作业提交率</p>
  452. </div>
  453. <div class={styles.TrainDataItem}>
  454. <p class={styles.TrainDataItemTitle}>
  455. <div>
  456. <span>
  457. <NNumberAnimation
  458. from={0}
  459. to={
  460. totalDateRef.value.qualifiedRate
  461. }></NNumberAnimation>
  462. </span>
  463. %
  464. </div>
  465. </p>
  466. <p class={styles.TrainDataItemsubTitle}>作业合格率</p>
  467. </div>
  468. </div>
  469. <div class={styles.TrainDataTopRight}>
  470. <div
  471. onClick={() => {
  472. qualifiedFlag.value = !qualifiedFlag.value;
  473. setChart();
  474. }}
  475. class={[
  476. styles.DataTopRightItem,
  477. qualifiedFlag.value ? '' : styles.DataTopRightItemDis
  478. ]}>
  479. <div class={styles.DataTopRightDot}></div>
  480. <p>合格人数</p>
  481. </div>
  482. <div
  483. onClick={() => {
  484. unqualifiedFlag.value = !unqualifiedFlag.value;
  485. setChart();
  486. }}
  487. class={[
  488. styles.DataTopRightItem,
  489. unqualifiedFlag.value ? '' : styles.DataTopRightItemDis
  490. ]}>
  491. <div class={[styles.DataTopRightDot, styles.red]}></div>
  492. <p>不合格人数</p>
  493. </div>
  494. </div>
  495. </div>
  496. <div class={styles.chatrs}>
  497. <div
  498. ref={chartRef}
  499. style={{ height: payForm.height, width: payForm.width }}></div>
  500. </div>
  501. <div class={styles.tableWrap}>
  502. <NDataTable
  503. v-slots={{
  504. empty: () => <TheEmpty></TheEmpty>
  505. }}
  506. class={styles.classTable}
  507. loading={state.loading}
  508. columns={columns()}
  509. data={state.tableList}></NDataTable>
  510. <Pagination
  511. v-model:page={state.pagination.page}
  512. v-model:pageSize={state.pagination.rows}
  513. v-model:pageTotal={state.pagination.pageTotal}
  514. onList={getList}
  515. sync
  516. />
  517. </div>
  518. </div>
  519. </>
  520. );
  521. }
  522. });