trainData.tsx 15 KB

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