index.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. import { defineComponent, reactive, onMounted, ref } from 'vue';
  2. import styles from './index.module.less';
  3. import {
  4. NAvatar,
  5. NButton,
  6. NDivider,
  7. NForm,
  8. NFormItem,
  9. NImage,
  10. NModal,
  11. NSpace,
  12. NSpin,
  13. useMessage
  14. } from 'naive-ui';
  15. import SearchInput from '@/components/searchInput';
  16. import CDatePicker from '/src/components/CDatePicker';
  17. import CSelect from '@/components/CSelect';
  18. import add from '@/views/studentList/images/add.png';
  19. import { useRoute, useRouter } from 'vue-router';
  20. import { getGradeLevelList, getGradeYearList } from '../home/api';
  21. import { initCache, setCache } from '/src/hooks/use-async';
  22. import { classArray, getgradeNumList } from '../classList/contants';
  23. import teacherIcon from '@components/layout/images/teacherIcon.png';
  24. import Pagination from '/src/components/pagination';
  25. import { api_trainingList, api_withdrawTraining } from './api';
  26. import TheEmpty from '/src/components/TheEmpty';
  27. import { getTimes } from '/src/utils';
  28. import dayjs from 'dayjs';
  29. import Train from '../prepare-lessons/components/lesson-main/train';
  30. import ResourceMain from '../prepare-lessons/components/resource-main';
  31. import { useResizeObserver } from '@vueuse/core';
  32. import { nextTick } from 'process';
  33. export const getCurrentMonth = () => {
  34. return [dayjs().startOf('month').valueOf(), dayjs().endOf('month').valueOf()];
  35. };
  36. export default defineComponent({
  37. name: 'homework-record',
  38. setup() {
  39. const state = reactive({
  40. workVisiable: false,
  41. resetVisiable: false,
  42. resetItem: {} as any,
  43. searchForm: {
  44. keyword: null as any,
  45. currentClass: '',
  46. currentGradeNum: '',
  47. subjectId: '',
  48. gradeYear: '',
  49. gradeLevel: '',
  50. homeworkObj: '',
  51. status: '',
  52. timer: getCurrentMonth() as any,
  53. selfFlag: true
  54. },
  55. loading: false,
  56. pagination: {
  57. page: 1,
  58. rows: 10,
  59. pageTotal: 6
  60. },
  61. gradeNumList: [] as any,
  62. tableList: [] as any,
  63. studentVisible: false,
  64. activeRow: null as any,
  65. showaddClass: false,
  66. popSelectYearList: [] as any,
  67. popSelectLevelList: [] as any
  68. });
  69. const formRef = ref();
  70. const message = useMessage();
  71. const router = useRouter();
  72. const route = useRoute();
  73. const search = () => {
  74. state.pagination.page = 1;
  75. getList();
  76. setCache({ current: state.searchForm, saveKey: route.path });
  77. };
  78. state.gradeNumList = getgradeNumList();
  79. const onReset = () => {
  80. state.searchForm = {
  81. keyword: null as any,
  82. currentClass: '' as any,
  83. currentGradeNum: '' as any,
  84. subjectId: '' as any,
  85. gradeYear: '' as any,
  86. gradeLevel: '',
  87. homeworkObj: '',
  88. status: '',
  89. timer: getCurrentMonth() as any,
  90. selfFlag: true
  91. };
  92. if (state.popSelectYearList.length > 0) {
  93. state.searchForm.gradeYear = state.popSelectYearList[1].id;
  94. }
  95. //
  96. setCache({ current: state.searchForm, saveKey: route.path });
  97. getList();
  98. };
  99. const getModalHeight = () => {
  100. useResizeObserver(
  101. document.querySelector('#model-homework-height') as HTMLElement,
  102. (entries: any) => {
  103. const entry = entries[0];
  104. const { height } = entry.contentRect;
  105. document.documentElement.style.setProperty(
  106. '--window-page-lesson-height',
  107. height + 'px'
  108. );
  109. }
  110. );
  111. };
  112. const getList = async () => {
  113. // // classGroupList
  114. state.loading = true;
  115. try {
  116. const res = await api_trainingList({
  117. ...state.searchForm,
  118. ...state.pagination,
  119. ...getTimes(
  120. state.searchForm.timer,
  121. ['startTime', 'endTime'],
  122. 'YYYY-MM-DD'
  123. )
  124. });
  125. const result = res.data.rows || [];
  126. result.forEach((item: any) => {
  127. let pTitle = '';
  128. let eTitle = '';
  129. if (
  130. item.studentLessonTrainingDetails &&
  131. item.studentLessonTrainingDetails.length > 0
  132. ) {
  133. item.studentLessonTrainingDetails.forEach((child: any) => {
  134. if (child.trainingType === 'PRACTICE' && child.musicName) {
  135. pTitle += pTitle ? '、' + child.musicName : child.musicName;
  136. }
  137. if (child.trainingType === 'EVALUATION' && child.musicName) {
  138. eTitle += eTitle ? '、' + child.musicName : child.musicName;
  139. }
  140. });
  141. }
  142. item.pTitle = pTitle;
  143. item.eTitle = eTitle;
  144. });
  145. state.tableList = res.data.rows;
  146. state.pagination.pageTotal = res.data.total;
  147. state.loading = false;
  148. } catch (e) {
  149. state.loading = false;
  150. console.log(e);
  151. }
  152. };
  153. // 获取学年
  154. const getYearList = async () => {
  155. try {
  156. const { data } = await getGradeYearList();
  157. const temp = data || [];
  158. temp.forEach((i: any) => {
  159. i.name = i.name + '学年';
  160. });
  161. temp.unshift({
  162. id: '',
  163. name: '全部学年'
  164. });
  165. state.popSelectYearList = temp || [];
  166. if (temp.length > 0 && !state.searchForm.gradeYear) {
  167. state.searchForm.gradeYear = temp[1].id;
  168. }
  169. } catch {
  170. //
  171. }
  172. };
  173. // 获取学级
  174. const getLevelList = async () => {
  175. try {
  176. const { data } = await getGradeLevelList();
  177. const temp = data || [];
  178. temp.forEach((i: any) => {
  179. i.name = i.name + '级';
  180. });
  181. temp.unshift({
  182. id: '',
  183. name: '全部学级'
  184. });
  185. state.popSelectLevelList = temp || [];
  186. if (temp.length > 0 && !state.searchForm.gradeLevel) {
  187. state.searchForm.gradeLevel = temp[0].id;
  188. }
  189. } catch {
  190. //
  191. }
  192. };
  193. initCache({
  194. current: state.searchForm,
  195. callBack: (active: any) => {
  196. state.searchForm = active;
  197. }
  198. });
  199. const onResetRecord = async () => {
  200. try {
  201. await api_withdrawTraining({ lessonTrainingId: state.resetItem.id });
  202. message.success('撤回成功');
  203. state.resetVisiable = false;
  204. search();
  205. } catch {
  206. //
  207. }
  208. };
  209. onMounted(async () => {
  210. state.loading = true;
  211. await getYearList();
  212. await getLevelList();
  213. await getList();
  214. state.loading = false;
  215. });
  216. return () => (
  217. <div class={styles.listWrap}>
  218. <div class={styles.searchList}>
  219. <NForm label-placement="left" inline ref={formRef}>
  220. <NFormItem>
  221. <SearchInput
  222. {...{ placeholder: '请输入作业标题关键词' }}
  223. class={styles.searchInput}
  224. searchWord={state.searchForm.keyword}
  225. onChangeValue={(val: string) =>
  226. (state.searchForm.keyword = val)
  227. }></SearchInput>
  228. </NFormItem>
  229. <NFormItem>
  230. <CSelect
  231. {...({
  232. options: [
  233. { id: '', name: '作业对象' },
  234. { id: 'PERSON', name: '个人' },
  235. { id: 'CLASS', name: '班级' }
  236. ],
  237. placeholder: '选择作业对象',
  238. clearable: true,
  239. inline: true,
  240. labelField: 'name',
  241. valueField: 'id'
  242. } as any)}
  243. v-model:value={state.searchForm.homeworkObj}></CSelect>
  244. </NFormItem>
  245. <NFormItem>
  246. <CSelect
  247. {...({
  248. options: state.popSelectYearList,
  249. placeholder: '选择学年',
  250. clearable: true,
  251. inline: true,
  252. labelField: 'name',
  253. valueField: 'id'
  254. } as any)}
  255. v-model:value={state.searchForm.gradeYear}></CSelect>
  256. </NFormItem>
  257. <NFormItem>
  258. <CSelect
  259. {...({
  260. options: state.popSelectLevelList,
  261. placeholder: '选择学级',
  262. clearable: true,
  263. inline: true,
  264. labelField: 'name',
  265. valueField: 'id'
  266. } as any)}
  267. v-model:value={state.searchForm.gradeLevel}></CSelect>
  268. </NFormItem>
  269. <NFormItem>
  270. <CSelect
  271. {...({
  272. options: state.gradeNumList,
  273. placeholder: '选择年级',
  274. clearable: true,
  275. inline: true
  276. } as any)}
  277. v-model:value={state.searchForm.currentGradeNum}></CSelect>
  278. </NFormItem>
  279. <NFormItem>
  280. <CSelect
  281. {...({
  282. options: classArray,
  283. placeholder: '选择班级',
  284. clearable: true,
  285. inline: true
  286. } as any)}
  287. v-model:value={state.searchForm.currentClass}></CSelect>
  288. </NFormItem>
  289. <NFormItem>
  290. <CSelect
  291. {...({
  292. options: [
  293. { value: '', label: '全部状态' },
  294. { value: 0, label: '进行中' },
  295. { value: 1, label: '已结束' }
  296. ],
  297. placeholder: '选择状态',
  298. clearable: true,
  299. inline: true
  300. } as any)}
  301. v-model:value={state.searchForm.status}></CSelect>
  302. </NFormItem>
  303. <NFormItem>
  304. <CDatePicker
  305. class={styles.CDatePickerItem}
  306. separator={'-'}
  307. type="daterange"
  308. timerValue={state.searchForm.timer}></CDatePicker>
  309. </NFormItem>
  310. <NFormItem>
  311. <NSpace justify="end">
  312. <NButton type="primary" class="searchBtn" onClick={search}>
  313. 搜索
  314. </NButton>
  315. <NButton
  316. type="primary"
  317. ghost
  318. class="resetBtn"
  319. onClick={onReset}>
  320. 重置
  321. </NButton>
  322. </NSpace>
  323. </NFormItem>
  324. </NForm>
  325. </div>
  326. <NButton
  327. class={styles.addBtn}
  328. type="primary"
  329. onClick={() => {
  330. state.workVisiable = true;
  331. nextTick(() => {
  332. getModalHeight();
  333. });
  334. }}
  335. v-slots={{
  336. icon: () => (
  337. <>
  338. <NImage
  339. class={styles.addBtnIcon}
  340. previewDisabled
  341. src={add}></NImage>
  342. </>
  343. )
  344. }}>
  345. 布置作业
  346. </NButton>
  347. <div class={styles.tableWrap}>
  348. <NSpin show={state.loading}>
  349. <div style={{ minHeight: '40vh' }}>
  350. <div class={styles.listSection}>
  351. {state.tableList.map((item: any) => (
  352. <div
  353. class={styles.item}
  354. onClick={() => {
  355. router.push({
  356. path: '/homework-record-detail',
  357. query: {
  358. id: item.id,
  359. name: item.name
  360. }
  361. });
  362. }}>
  363. <div class={styles.header}>
  364. <NAvatar
  365. class={styles.navatar}
  366. round
  367. src={item.teacherAvatar || teacherIcon}
  368. />
  369. <div class={styles.userInfo}>
  370. <h2>{item.teacherName}</h2>
  371. <p>
  372. 布置时间:
  373. {dayjs(item.createTime).format('YYYY-MM-DD')}
  374. <span> | </span>
  375. <span style={{ color: '#EA4132' }}>
  376. 截止时间:
  377. {dayjs(item.expireDate).format('YYYY-MM-DD')}
  378. </span>
  379. </p>
  380. </div>
  381. <div class={item.status ? styles.over : styles.ing}>
  382. {item.status ? '已结束' : '进行中'}
  383. </div>
  384. </div>
  385. <div class={styles.content}>
  386. <div>
  387. <div class={styles.homeTitle}>{item.name}</div>
  388. <div class={styles.homeContent}>
  389. <span class={styles.title}>作业对象:</span>
  390. <span class={styles.text}>
  391. {item.homeworkObjName}
  392. </span>
  393. </div>
  394. <div class={[styles.homeContent, styles.homeworkText]}>
  395. <span class={styles.title}>作业内容:</span>
  396. <div class={styles.pSection}>
  397. {item.pTitle && (
  398. <p class={[styles.text, styles.p1]}>
  399. {item.pTitle}
  400. </p>
  401. )}
  402. {item.eTitle && (
  403. <p class={[styles.text, styles.p2]}>
  404. {item.eTitle}
  405. </p>
  406. )}
  407. </div>
  408. </div>
  409. <div class={styles.homeSubmit}>
  410. <span class={styles.title}>已提交:</span>
  411. <span class={styles.text}>
  412. {item.trainingNum || 0}/{item.expectNum || 0}人
  413. </span>
  414. <NDivider vertical />
  415. <span class={styles.title}>提交率:</span>
  416. <span class={styles.text}>
  417. {item.trainingRate || 0}%
  418. </span>
  419. <NDivider vertical />
  420. <span class={styles.title}>合格人数:</span>
  421. <span class={styles.text}>
  422. {item.standardNum || 0}人
  423. </span>
  424. <NDivider vertical />
  425. <span class={styles.title}>合格率:</span>
  426. <span class={styles.text}>
  427. {item.qualifiedRate || 0}%
  428. </span>
  429. </div>
  430. </div>
  431. {!item.status && (
  432. <NButton
  433. class={styles.errorBtn}
  434. type="error"
  435. color="#F94D50"
  436. onClick={(e: any) => {
  437. e.stopPropagation();
  438. state.resetVisiable = true;
  439. state.resetItem = item;
  440. }}>
  441. 撤回
  442. </NButton>
  443. )}
  444. </div>
  445. </div>
  446. ))}
  447. </div>
  448. {state.tableList.length <= 0 && !state.loading && (
  449. <TheEmpty class={styles.nowEmpty} />
  450. )}
  451. </div>
  452. </NSpin>
  453. {state.tableList.length > 0 && (
  454. <Pagination
  455. v-model:page={state.pagination.page}
  456. v-model:pageSize={state.pagination.rows}
  457. v-model:pageTotal={state.pagination.pageTotal}
  458. onList={getList}
  459. sync
  460. />
  461. )}
  462. </div>
  463. <NModal
  464. v-model:show={state.resetVisiable}
  465. preset="card"
  466. class={['modalTitle', styles.removeVisiable]}
  467. title={'撤回作业'}>
  468. <div class={styles.studentRemove}>
  469. <p>
  470. 撤回作业后,此条作业将被删除,是否确认撤回【{state.resetItem.name}
  471. 】?
  472. </p>
  473. <NSpace class={styles.btnGroup} justify="center">
  474. <NButton round onClick={() => (state.resetVisiable = false)}>
  475. 取消
  476. </NButton>
  477. <NButton round type="primary" onClick={onResetRecord}>
  478. 确定
  479. </NButton>
  480. </NSpace>
  481. </div>
  482. </NModal>
  483. <NModal
  484. v-model:show={state.workVisiable}
  485. preset="card"
  486. class={['modalTitle background', styles.workVisiable]}
  487. title={'作业详情'}>
  488. <div id="model-homework-height" class={styles.workContainer}>
  489. <div class={styles.workTrain}>
  490. <Train
  491. lessonPreTraining={{
  492. title: dayjs().format('YYYY年MM月DD日') + '-课后作业'
  493. }}
  494. cardType={'homeworkRecord'}
  495. onChange={(val: any) => {
  496. state.workVisiable = val.status;
  497. getList();
  498. }}
  499. />
  500. </div>
  501. <div class={styles.resourceMain}>
  502. <ResourceMain cardType="homerowk-record" />
  503. </div>
  504. </div>
  505. </NModal>
  506. </div>
  507. );
  508. }
  509. });