index.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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. // getList();
  96. setCache({ current: state.searchForm, saveKey: route.path });
  97. };
  98. const getModalHeight = () => {
  99. useResizeObserver(
  100. document.querySelector('#model-homework-height') as HTMLElement,
  101. (entries: any) => {
  102. const entry = entries[0];
  103. const { height } = entry.contentRect;
  104. document.documentElement.style.setProperty(
  105. '--window-page-lesson-height',
  106. height + 'px'
  107. );
  108. }
  109. );
  110. };
  111. const getList = async () => {
  112. // // classGroupList
  113. state.loading = true;
  114. try {
  115. const res = await api_trainingList({
  116. ...state.searchForm,
  117. ...state.pagination,
  118. ...getTimes(
  119. state.searchForm.timer,
  120. ['startTime', 'endTime'],
  121. 'YYYY-MM-DD'
  122. )
  123. });
  124. const result = res.data.rows || [];
  125. result.forEach((item: any) => {
  126. let pTitle = '';
  127. let eTitle = '';
  128. if (
  129. item.studentLessonTrainingDetails &&
  130. item.studentLessonTrainingDetails.length > 0
  131. ) {
  132. item.studentLessonTrainingDetails.forEach((child: any) => {
  133. if (child.trainingType === 'PRACTICE' && child.musicName) {
  134. pTitle += pTitle ? '、' + child.musicName : child.musicName;
  135. }
  136. if (child.trainingType === 'EVALUATION' && child.musicName) {
  137. eTitle += eTitle ? '、' + child.musicName : child.musicName;
  138. }
  139. });
  140. }
  141. item.pTitle = pTitle;
  142. item.eTitle = eTitle;
  143. });
  144. state.tableList = res.data.rows;
  145. state.pagination.pageTotal = res.data.total;
  146. state.loading = false;
  147. } catch (e) {
  148. state.loading = false;
  149. console.log(e);
  150. }
  151. };
  152. // 获取学年
  153. const getYearList = async () => {
  154. try {
  155. const { data } = await getGradeYearList();
  156. const temp = data || [];
  157. temp.forEach((i: any) => {
  158. i.name = i.name + '学年';
  159. });
  160. temp.unshift({
  161. id: '',
  162. name: '全部学年'
  163. });
  164. state.popSelectYearList = temp || [];
  165. if (temp.length > 0 && !state.searchForm.gradeYear) {
  166. state.searchForm.gradeYear = temp[1].id;
  167. }
  168. } catch {
  169. //
  170. }
  171. };
  172. // 获取学级
  173. const getLevelList = async () => {
  174. try {
  175. const { data } = await getGradeLevelList();
  176. const temp = data || [];
  177. temp.forEach((i: any) => {
  178. i.name = i.name + '级';
  179. });
  180. temp.unshift({
  181. id: '',
  182. name: '全部学级'
  183. });
  184. state.popSelectLevelList = temp || [];
  185. if (temp.length > 0 && !state.searchForm.gradeLevel) {
  186. state.searchForm.gradeLevel = temp[0].id;
  187. }
  188. } catch {
  189. //
  190. }
  191. };
  192. initCache({
  193. current: state.searchForm,
  194. callBack: (active: any) => {
  195. state.searchForm = active;
  196. }
  197. });
  198. const onResetRecord = async () => {
  199. try {
  200. await api_withdrawTraining({ lessonTrainingId: state.resetItem.id });
  201. message.success('撤回成功');
  202. state.resetVisiable = false;
  203. search();
  204. } catch {
  205. //
  206. }
  207. };
  208. onMounted(async () => {
  209. state.loading = true;
  210. await getYearList();
  211. await getLevelList();
  212. await getList();
  213. state.loading = false;
  214. });
  215. return () => (
  216. <div class={styles.listWrap}>
  217. <div class={styles.searchList}>
  218. <NForm label-placement="left" inline ref={formRef}>
  219. <NFormItem>
  220. <SearchInput
  221. {...{ placeholder: '请输入作业标题关键词' }}
  222. class={styles.searchInput}
  223. searchWord={state.searchForm.keyword}
  224. onChangeValue={(val: string) =>
  225. (state.searchForm.keyword = val)
  226. }></SearchInput>
  227. </NFormItem>
  228. <NFormItem>
  229. <CSelect
  230. {...({
  231. options: [
  232. { id: '', name: '作业对象' },
  233. { id: 'PERSON', name: '个人' },
  234. { id: 'CLASS', name: '班级' }
  235. ],
  236. placeholder: '选择作业对象',
  237. clearable: true,
  238. inline: true,
  239. labelField: 'name',
  240. valueField: 'id'
  241. } as any)}
  242. v-model:value={state.searchForm.homeworkObj}></CSelect>
  243. </NFormItem>
  244. <NFormItem>
  245. <CSelect
  246. {...({
  247. options: state.popSelectYearList,
  248. placeholder: '选择学年',
  249. clearable: true,
  250. inline: true,
  251. labelField: 'name',
  252. valueField: 'id'
  253. } as any)}
  254. v-model:value={state.searchForm.gradeYear}></CSelect>
  255. </NFormItem>
  256. <NFormItem>
  257. <CSelect
  258. {...({
  259. options: state.popSelectLevelList,
  260. placeholder: '选择学级',
  261. clearable: true,
  262. inline: true,
  263. labelField: 'name',
  264. valueField: 'id'
  265. } as any)}
  266. v-model:value={state.searchForm.gradeLevel}></CSelect>
  267. </NFormItem>
  268. <NFormItem>
  269. <CSelect
  270. {...({
  271. options: state.gradeNumList,
  272. placeholder: '选择年级',
  273. clearable: true,
  274. inline: true
  275. } as any)}
  276. v-model:value={state.searchForm.currentGradeNum}></CSelect>
  277. </NFormItem>
  278. <NFormItem>
  279. <CSelect
  280. {...({
  281. options: classArray,
  282. placeholder: '选择班级',
  283. clearable: true,
  284. inline: true
  285. } as any)}
  286. v-model:value={state.searchForm.currentClass}></CSelect>
  287. </NFormItem>
  288. <NFormItem>
  289. <CSelect
  290. {...({
  291. options: [
  292. { value: '', label: '全部状态' },
  293. { value: 0, label: '进行中' },
  294. { value: 1, label: '已结束' }
  295. ],
  296. placeholder: '选择状态',
  297. clearable: true,
  298. inline: true
  299. } as any)}
  300. v-model:value={state.searchForm.status}></CSelect>
  301. </NFormItem>
  302. <NFormItem>
  303. <CDatePicker
  304. class={styles.CDatePickerItem}
  305. separator={'-'}
  306. type="daterange"
  307. timerValue={state.searchForm.timer}></CDatePicker>
  308. </NFormItem>
  309. <NFormItem>
  310. <NSpace justify="end">
  311. <NButton type="primary" class="searchBtn" onClick={search}>
  312. 搜索
  313. </NButton>
  314. <NButton
  315. type="primary"
  316. ghost
  317. class="resetBtn"
  318. onClick={onReset}>
  319. 重置
  320. </NButton>
  321. </NSpace>
  322. </NFormItem>
  323. </NForm>
  324. </div>
  325. <NButton
  326. class={styles.addBtn}
  327. type="primary"
  328. onClick={() => {
  329. state.workVisiable = true;
  330. nextTick(() => {
  331. getModalHeight();
  332. });
  333. }}
  334. v-slots={{
  335. icon: () => (
  336. <>
  337. <NImage
  338. class={styles.addBtnIcon}
  339. previewDisabled
  340. src={add}></NImage>
  341. </>
  342. )
  343. }}>
  344. 布置作业
  345. </NButton>
  346. <div class={styles.tableWrap}>
  347. <NSpin show={state.loading}>
  348. <div class={styles.listSection}>
  349. {state.tableList.map((item: any) => (
  350. <div class={styles.item}>
  351. <div class={styles.header}>
  352. <NAvatar
  353. class={styles.navatar}
  354. round
  355. src={item.teacherAvatar || teacherIcon}
  356. />
  357. <div class={styles.userInfo}>
  358. <h2>{item.teacherName}</h2>
  359. <p>
  360. 布置时间:{dayjs(item.createTime).format('YYYY-MM-DD')}
  361. <span>|</span>
  362. <span>
  363. 截止时间:
  364. {dayjs(item.expireDate).format('YYYY-MM-DD')}
  365. </span>
  366. </p>
  367. </div>
  368. <div class={item.status === 1 ? styles.over : styles.ing}>
  369. {item.status === 1 ? '已结束' : '进行中'}
  370. </div>
  371. </div>
  372. <div class={styles.content}>
  373. <div>
  374. <div class={styles.homeTitle}>{item.name}</div>
  375. <div class={styles.homeContent}>
  376. <span class={styles.title}>作业对象:</span>
  377. <span class={styles.text}>{item.homeworkObjName}</span>
  378. </div>
  379. <div class={[styles.homeContent, styles.homeworkText]}>
  380. <span class={styles.title}>作业内容:</span>
  381. <div class={styles.pSection}>
  382. {item.pTitle && (
  383. <p class={[styles.text, styles.p1]}>
  384. {item.pTitle}
  385. </p>
  386. )}
  387. {item.eTitle && (
  388. <p class={[styles.text, styles.p2]}>
  389. {item.eTitle}
  390. </p>
  391. )}
  392. </div>
  393. </div>
  394. <div class={styles.homeSubmit}>
  395. <span class={styles.title}>已提交:</span>
  396. <span class={styles.text}>
  397. {item.trainingNum || 0}/{item.trainingNum || 0}人
  398. </span>
  399. <NDivider vertical />
  400. <span class={styles.title}>提交率:</span>
  401. <span class={styles.text}>
  402. {item.trainingRate || 0}%
  403. </span>
  404. <NDivider vertical />
  405. <span class={styles.title}>合格人数:</span>
  406. <span class={styles.text}>
  407. {item.standardNum || 0}人
  408. </span>
  409. <NDivider vertical />
  410. <span class={styles.title}>合格率:</span>
  411. <span class={styles.text}>
  412. {item.qualifiedRate || 0}%
  413. </span>
  414. </div>
  415. </div>
  416. <NButton
  417. class={styles.errorBtn}
  418. type="error"
  419. color="#F94D50"
  420. onClick={() => {
  421. state.resetVisiable = true;
  422. state.resetItem = item;
  423. }}>
  424. 撤回
  425. </NButton>
  426. </div>
  427. </div>
  428. ))}
  429. </div>
  430. {state.tableList.length <= 0 && (
  431. <TheEmpty class={styles.nowEmpty} />
  432. )}
  433. </NSpin>
  434. {state.tableList.length > 0 && (
  435. <Pagination
  436. v-model:page={state.pagination.page}
  437. v-model:pageSize={state.pagination.rows}
  438. v-model:pageTotal={state.pagination.pageTotal}
  439. onList={getList}
  440. sync
  441. />
  442. )}
  443. </div>
  444. <NModal
  445. v-model:show={state.resetVisiable}
  446. preset="card"
  447. class={['modalTitle', styles.removeVisiable]}
  448. title={'撤回作业'}>
  449. <div class={styles.studentRemove}>
  450. <p>
  451. 撤回作业后,此条作业将被删除,是否确认撤回【{state.resetItem.name}
  452. 】?
  453. </p>
  454. <NSpace class={styles.btnGroup} justify="center">
  455. <NButton round onClick={() => (state.resetVisiable = false)}>
  456. 取消
  457. </NButton>
  458. <NButton round type="primary" onClick={onResetRecord}>
  459. 确定
  460. </NButton>
  461. </NSpace>
  462. </div>
  463. </NModal>
  464. <NModal
  465. v-model:show={state.workVisiable}
  466. preset="card"
  467. class={['modalTitle background', styles.workVisiable]}
  468. title={'作业详情'}>
  469. <div id="model-homework-height" class={styles.workContainer}>
  470. <div class={styles.workTrain}>
  471. <Train
  472. lessonPreTraining={{
  473. title: dayjs().format('YYYY年MM月DD日') + '-课后作业'
  474. }}
  475. />
  476. </div>
  477. <div class={styles.resourceMain}>
  478. <ResourceMain cardType="homerowk-record" />
  479. </div>
  480. </div>
  481. </NModal>
  482. </div>
  483. );
  484. }
  485. });