practiceData.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. import { Ref, defineComponent, onMounted, reactive, ref } from 'vue';
  2. import styles from '../index.module.less';
  3. import {
  4. NButton,
  5. NDataTable,
  6. NForm,
  7. NFormItem,
  8. NNumberAnimation,
  9. NSpace
  10. } from 'naive-ui';
  11. import numeral from 'numeral';
  12. import { useECharts } from '@/hooks/web/useECharts';
  13. import Pagination from '/src/components/pagination';
  14. import { getTrainingStat } from '../api';
  15. import { getTrainingStatList } from '@/views/classList/api';
  16. import {
  17. getNowDateAndMonday,
  18. getNowDateAndSunday,
  19. getTimes,
  20. getMinutes,
  21. getSecend
  22. } from '/src/utils/dateFormat';
  23. import CDatePicker from '/src/components/CDatePicker';
  24. import TheEmpty from '/src/components/TheEmpty';
  25. import { initCache, setCache } from '/src/hooks/use-async';
  26. export default defineComponent({
  27. name: 'student-practiceData',
  28. props: {
  29. studentId: {
  30. type: String,
  31. default: ''
  32. },
  33. classGroupId: {
  34. type: String,
  35. default: ''
  36. }
  37. },
  38. setup(props) {
  39. const chartRef = ref<HTMLDivElement | null>(null);
  40. const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
  41. const practiceFlag = ref(true);
  42. const payForm = reactive({
  43. height: '360px',
  44. width: '100%',
  45. practiceDurationAvg: 0,
  46. practiceDays: 0,
  47. practiceDurationTotal: 0,
  48. dateList: [],
  49. timeList: []
  50. });
  51. const state = reactive({
  52. loading: false,
  53. pagination: {
  54. page: 1,
  55. rows: 10,
  56. pageTotal: 4
  57. },
  58. tableList: [] as any,
  59. goCourseVisiable: false
  60. });
  61. const timer = ref<[number, number]>([
  62. getNowDateAndMonday(new Date().getTime()),
  63. getNowDateAndSunday(new Date().getTime())
  64. ]);
  65. const columns = () => {
  66. return [
  67. {
  68. title: '日期',
  69. key: 'date'
  70. },
  71. {
  72. title: '学练时长',
  73. key: 'practiceDuration',
  74. render(row: any) {
  75. return (
  76. <>
  77. {' '}
  78. <>
  79. {row.practiceDuration
  80. ? getMinutes(row.practiceDuration) > 0
  81. ? getMinutes(row.practiceDuration) +
  82. '分' +
  83. getSecend(row.practiceDuration) +
  84. '秒'
  85. : getSecend(row.practiceDuration) + '秒'
  86. : 0 + '分钟'}
  87. </>
  88. </>
  89. );
  90. }
  91. }
  92. ];
  93. };
  94. const getList = async () => {
  95. try {
  96. const res = await getTrainingStatList({
  97. page: 1,
  98. rows: 999,
  99. studentId: props.studentId,
  100. classGroupId: props.classGroupId,
  101. ...getTimes(timer.value, ['startTime', 'endTime'], 'YYYY-MM-DD')
  102. });
  103. state.tableList = res.data.rows;
  104. } catch (e) {
  105. console.log(e);
  106. }
  107. };
  108. const setChart = () => {
  109. setOptions({
  110. tooltip: {
  111. trigger: 'axis',
  112. axisPointer: {
  113. type: 'shadow'
  114. }
  115. },
  116. legend: {
  117. show: false,
  118. selected: {
  119. //在这里设置默认展示就ok了
  120. '学练时长(分钟)': practiceFlag.value
  121. }
  122. },
  123. xAxis: {
  124. type: 'category',
  125. boundaryGap: true,
  126. axisLabel: {
  127. show: true,
  128. interval: 0
  129. },
  130. data: payForm.dateList
  131. },
  132. yAxis: [
  133. {
  134. type: 'value',
  135. axisLabel: {
  136. formatter: (value: any) => {
  137. return getMinutes(value) + 'min';
  138. }
  139. },
  140. axisTick: {
  141. show: false
  142. },
  143. splitArea: {
  144. show: false,
  145. areaStyle: {
  146. color: ['rgba(255,255,255,0.2)']
  147. }
  148. }
  149. }
  150. ],
  151. grid: {
  152. left: '1%',
  153. right: '1%',
  154. top: '2%',
  155. bottom: 0,
  156. containLabel: true
  157. },
  158. series: [
  159. {
  160. // smooth: true,
  161. data: payForm.timeList,
  162. type: 'bar',
  163. barWidth: '48px',
  164. stack: 'total',
  165. // label: {
  166. // // 柱图头部显示值
  167. // formatter: (value: any) => {
  168. // console.log(value);
  169. // return getMinutes(value.value);
  170. // },
  171. // show: true,
  172. // position: 'top',
  173. // color: '#333',
  174. // fontSize: '12px',
  175. // fontWeight: 600
  176. // },
  177. itemStyle: {
  178. normal: {
  179. //这里设置柱形图圆角 [左上角,右上角,右下角,左下角]
  180. barBorderRadius: [8, 8, 0, 0],
  181. color: '#D5E9FF'
  182. },
  183. emphasis: {
  184. focus: 'series',
  185. color: '#3583FA' //hover时改变柱子颜色
  186. // borderWidth: 4,
  187. // borderColor: 'rgba(213, 233, 255,.4)',
  188. // borderType: 'solid'
  189. }
  190. } as any
  191. }
  192. ],
  193. formatter: (item: any) => {
  194. if (Array.isArray(item)) {
  195. return [
  196. item[0].axisValueLabel,
  197. ...item.map((d: any) => {
  198. let str;
  199. getMinutes(d.value) > 0
  200. ? (str =
  201. getMinutes(d.value) + '分' + getSecend(d.value) + '秒')
  202. : (str = getSecend(d.value) + '秒');
  203. return `<br/>${d.marker}<span style="margin-top:10px;margin-left:5px;font-size: 13px;font-weight: 500;
  204. color: #131415;font-weight: 600;
  205. margin-top:12px
  206. line-height: 18px;">学练时长: ${str} </span>`;
  207. })
  208. ].join('');
  209. } else {
  210. return item;
  211. }
  212. }
  213. // dataZoom: [
  214. // {
  215. // type: 'slider',
  216. // start: 5,
  217. // end: 100,
  218. // filterMode: 'empty'
  219. // }
  220. // ]
  221. });
  222. };
  223. const getChartDetail = async () => {
  224. try {
  225. const res = await getTrainingStat({
  226. studentId: props.studentId,
  227. classGroupId: props.classGroupId,
  228. ...getTimes(timer.value, ['startTime', 'endTime'], 'YYYY-MM-DD')
  229. });
  230. payForm.practiceDays = res.data.practiceDays;
  231. payForm.practiceDurationAvg = res.data.practiceDurationAvg;
  232. payForm.practiceDurationTotal = res.data.practiceDurationTotal;
  233. payForm.dateList = res.data.trainingStatDetailList.map((item: any) => {
  234. return item.date;
  235. });
  236. payForm.timeList = res.data.trainingStatDetailList.map((item: any) => {
  237. return item.practiceDuration;
  238. });
  239. setChart();
  240. console.log(payForm);
  241. } catch (e) {
  242. console.log(e);
  243. }
  244. };
  245. const search = () => {
  246. state.pagination.page = 1;
  247. getChartDetail();
  248. getList();
  249. setCache({
  250. current: { timer: timer.value },
  251. saveKey: 'classStudentRecordPracticeData'
  252. });
  253. };
  254. const onReset = () => {
  255. timer.value = [
  256. getNowDateAndMonday(new Date().getTime()),
  257. getNowDateAndSunday(new Date().getTime())
  258. ];
  259. search();
  260. getList();
  261. setCache({
  262. current: { timer: timer.value },
  263. saveKey: 'classStudentRecordPracticeData'
  264. });
  265. };
  266. initCache({
  267. current: { timer: timer.value },
  268. saveKey: 'classStudentRecordPracticeData',
  269. callBack: (active: any) => {
  270. timer.value = active.timer;
  271. }
  272. });
  273. onMounted(() => {
  274. console.log(props.studentId);
  275. getChartDetail();
  276. getList();
  277. });
  278. return () => (
  279. <>
  280. <NForm label-placement="left" inline>
  281. <NFormItem>
  282. <CDatePicker
  283. v-model:value={timer.value}
  284. separator={'至'}
  285. type="daterange"
  286. timerValue={timer.value}></CDatePicker>
  287. </NFormItem>
  288. <NFormItem>
  289. <NSpace justify="end">
  290. <NButton type="primary" class="searchBtn" onClick={search}>
  291. 搜索
  292. </NButton>
  293. <NButton type="primary" ghost class="resetBtn" onClick={onReset}>
  294. 重置
  295. </NButton>
  296. </NSpace>
  297. </NFormItem>
  298. </NForm>
  299. <div class={styles.homeTrainData}>
  300. <div class={styles.TrainDataTop}>
  301. <div class={styles.TrainDataTopLeft}>
  302. <div class={styles.TrainDataItem}>
  303. <p class={styles.TrainDataItemTitle}>
  304. {getMinutes(payForm.practiceDurationTotal) > 0 ? (
  305. <div>
  306. <span>
  307. <NNumberAnimation
  308. from={0}
  309. to={getMinutes(
  310. payForm.practiceDurationTotal
  311. )}></NNumberAnimation>
  312. </span>
  313. <i style={{ width: '4px', display: 'inline-block' }}></i>
  314. <i style={{ width: '4px', display: 'inline-block' }}></i>
  315. </div>
  316. ) : null}
  317. <div>
  318. <span>
  319. <NNumberAnimation
  320. from={0}
  321. to={getSecend(
  322. payForm.practiceDurationTotal
  323. )}></NNumberAnimation>
  324. </span>
  325. <i style={{ width: '4px', display: 'inline-block' }}></i>秒
  326. </div>
  327. </p>
  328. <p class={styles.TrainDataItemsubTitle}>累计练习时长</p>
  329. </div>
  330. <div class={styles.TrainDataItem}>
  331. <p class={styles.TrainDataItemTitle}>
  332. {getMinutes(payForm.practiceDurationAvg) > 0 ? (
  333. <div>
  334. <span>
  335. <NNumberAnimation
  336. from={0}
  337. to={getMinutes(
  338. payForm.practiceDurationAvg
  339. )}></NNumberAnimation>
  340. </span>
  341. <i style={{ width: '4px', display: 'inline-block' }}></i>
  342. <i style={{ width: '4px', display: 'inline-block' }}></i>
  343. </div>
  344. ) : null}
  345. <div>
  346. <span>
  347. <NNumberAnimation
  348. from={0}
  349. to={getSecend(
  350. payForm.practiceDurationAvg
  351. )}></NNumberAnimation>
  352. </span>
  353. <i style={{ width: '4px', display: 'inline-block' }}></i>秒
  354. </div>
  355. </p>
  356. <p class={styles.TrainDataItemsubTitle}>平均每天练习时长</p>
  357. </div>
  358. <div class={styles.TrainDataItem}>
  359. <p class={styles.TrainDataItemTitle}>
  360. <div>
  361. <span>
  362. <NNumberAnimation
  363. from={0}
  364. to={payForm.practiceDays}></NNumberAnimation>
  365. </span>
  366. <i style={{ width: '4px', display: 'inline-block' }}></i>天
  367. </div>
  368. </p>
  369. <p class={styles.TrainDataItemsubTitle}>练习天数</p>
  370. </div>
  371. </div>
  372. <div class={styles.TrainDataTopRight}>
  373. <div
  374. // onClick={() => {
  375. // practiceFlag.value = !practiceFlag.value;
  376. // setChart();
  377. // }}
  378. class={[
  379. styles.DataTopRightItem,
  380. practiceFlag.value ? '' : styles.DataTopRightItemDis
  381. ]}>
  382. <div
  383. class={[
  384. styles.DataTopRightDot,
  385. styles.DataTopRightDotBlue
  386. ]}></div>
  387. <p>学练时长(分钟)</p>
  388. </div>
  389. </div>
  390. </div>
  391. <div class={styles.chatrs}>
  392. <div
  393. ref={chartRef}
  394. style={{ height: payForm.height, width: payForm.width }}></div>
  395. </div>
  396. <div class={styles.tableWrap}>
  397. <NDataTable
  398. v-slots={{
  399. empty: () => <TheEmpty></TheEmpty>
  400. }}
  401. class={styles.classTable}
  402. loading={state.loading}
  403. columns={columns()}
  404. data={state.tableList}></NDataTable>
  405. {/* <Pagination
  406. v-model:page={state.pagination.page}
  407. v-model:pageSize={state.pagination.rows}
  408. v-model:pageTotal={state.pagination.pageTotal}
  409. onList={getList}
  410. sync
  411. saveKey="orchestraRegistration-key"
  412. /> */}
  413. </div>
  414. </div>
  415. </>
  416. );
  417. }
  418. });