assign-homework.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. import {
  2. PropType,
  3. defineComponent,
  4. onMounted,
  5. reactive,
  6. ref,
  7. toRef
  8. } from 'vue';
  9. import styles from './index.module.less';
  10. import {
  11. NButton,
  12. NDatePicker,
  13. NForm,
  14. NFormItem,
  15. NInput,
  16. NModal,
  17. NScrollbar,
  18. NSelect,
  19. NSpace,
  20. useMessage
  21. } from 'naive-ui';
  22. import { lessonTrainingAdd } from '../../../api';
  23. import dayjs from 'dayjs';
  24. import { classGroupList } from '/src/views/home/api';
  25. import { gradeToCN } from '/src/utils/contants';
  26. import { usePrepareStore } from '/src/store/modules/prepareLessons';
  27. import AssignStudent from './assign-student';
  28. import useDrag from '@/hooks/useDrag';
  29. import Dragbom from '@/hooks/useDrag/dragbom';
  30. import { useUserStore } from '@/store/modules/users';
  31. import { api_getCurrentGradeYear } from '/src/views/studentList/api';
  32. import request from '/src/utils/request';
  33. import { modalClickMask } from '/src/state';
  34. import TheTipDialog from "@/components/TheTipDialog"
  35. import router from '/src/router';
  36. export default defineComponent({
  37. name: 'assign-homework',
  38. props: {
  39. classGroupId: {
  40. type: String,
  41. default: ''
  42. },
  43. courseScheduleId: {
  44. type: String,
  45. default: ''
  46. },
  47. /** 课件编号 */
  48. chapterLessonCoursewareId: {
  49. type: String,
  50. default: ''
  51. },
  52. item: {
  53. type: Object,
  54. default: () => ({})
  55. },
  56. /** 初始数据 */
  57. trainList: {
  58. type: Array,
  59. default: () => []
  60. },
  61. homeworkType: {
  62. type: String as PropType<'CLASSWORK' | 'HOMEWORK'>,
  63. default: 'CLASSWORK'
  64. },
  65. from: {
  66. // 来自哪里
  67. type: String,
  68. default: ''
  69. }
  70. },
  71. emits: ['close', 'confirm'],
  72. setup(props, { emit }) {
  73. const message = useMessage();
  74. const prepareStore = usePrepareStore();
  75. const forms = reactive({
  76. gradeYear: '' as any,
  77. currentTime: dayjs(dayjs().format('YYYY-MM-DD')).valueOf(),
  78. homeworkObj: 'CLASS' as 'PERSON' | 'CLASS',
  79. homeworkType: props.homeworkType,
  80. workVisiable: false,
  81. id: null as any,
  82. uploading: false,
  83. title: props.item.title,
  84. courseScheduleId: props.courseScheduleId || null,
  85. chapterLessonCoursewareId: props.chapterLessonCoursewareId || null,
  86. gradeList: [] as any,
  87. classList: [] as any,
  88. currentGradeNum: null,
  89. expireDate: dayjs().add(7, 'day').format('YYYY-MM-DD HH:mm') as any, // 默认7天
  90. classGroupId: null as any,
  91. studentList: [] as any,
  92. selectIds: [] as any
  93. });
  94. const formsRef = ref();
  95. const maxDeadlineTime = ref(0);
  96. const maxExpireTime = ref(0);
  97. const pickerEndTime = ref<any>(null);
  98. // 获取年级班级
  99. const getClassGroupList = async () => {
  100. try {
  101. const defaultSelectClassGroupId =
  102. props.classGroupId || prepareStore.getClassGroupId;
  103. const { data } = await classGroupList({
  104. upgradeFlag: true,
  105. gradeYear: forms.gradeYear
  106. });
  107. const cList = data || [];
  108. const gradeList: any = [];
  109. cList.forEach((item: any) => {
  110. const classList: any = [];
  111. item.classGroupList.forEach((i: any) => {
  112. classList.push({
  113. label: i.currentClass + '班',
  114. value: i.id,
  115. lastStudy: i.lastStudy,
  116. preStudentNum: i.preStudentNum
  117. });
  118. if (i.id === defaultSelectClassGroupId) {
  119. forms.currentGradeNum = i.currentGradeNum;
  120. forms.classGroupId = [i.id];
  121. }
  122. });
  123. gradeList.push({
  124. label: gradeToCN[item.currentGradeNum],
  125. value: item.currentGradeNum,
  126. childrens: classList
  127. });
  128. });
  129. forms.gradeList = gradeList;
  130. if (forms.currentGradeNum) {
  131. getClassList();
  132. }
  133. } catch {
  134. //
  135. }
  136. };
  137. const getClassList = async () => {
  138. try {
  139. forms.gradeList.forEach((item: any) => {
  140. if (item.value === forms.currentGradeNum) {
  141. forms.classList = item.childrens;
  142. }
  143. });
  144. } catch {
  145. //
  146. }
  147. };
  148. const tipDialog = reactive({
  149. show: false,
  150. msg:"",
  151. confirmButtonText:"",
  152. cancelBtn: false,
  153. type: "CLASS" as "CLASS"|"PERSON"|"MUSIC"
  154. })
  155. function handleLessonAddErr(data:any){
  156. const { type, errList } = data
  157. tipDialog.type = type
  158. if(type === "CLASS"){
  159. // 班级乐器没有设置
  160. tipDialog.cancelBtn = true
  161. tipDialog.confirmButtonText = "去设置"
  162. const msg = errList.map((item:any)=>{
  163. return `<div><span style="color:#F44541">【${item.classGroupName}】</span>未设置乐器,请设置乐器后布置作业${errList.length>1?";":""}</div>`
  164. })
  165. tipDialog.msg = msg.join("")
  166. }else if(type === "PERSON"){
  167. // 学生乐器没有设置
  168. tipDialog.cancelBtn = true
  169. tipDialog.confirmButtonText = "去设置"
  170. const msg = errList.map((item:any)=>{
  171. return `<div><span style="color:#F44541">【${item.studentName}】</span>所在<span style="color:#F44541">【${item.classGroupName}】</span>未设置乐器,请设置乐器后布置作业${errList.length>1?";":""}</div>`
  172. })
  173. tipDialog.msg = msg.join("")
  174. }else if(type === "MUSIC"){
  175. // 曲目和当前选择学生的乐器对不上的时候
  176. tipDialog.cancelBtn = false
  177. tipDialog.confirmButtonText = "我知道了"
  178. const msg = errList.map((item:any)=>{
  179. return `<div>曲目<span style="color:#F44541">【${item.musicSheetName}】</span>不支持<span style="color:#F44541">【${item.instrumentName}】</span>练习,请更换曲目或取消<span style="color:#F44541">【${item.instrumentName}】</span>的学生${errList.length>1?";":""}</div>`
  180. })
  181. tipDialog.msg = msg.join("")
  182. }
  183. tipDialog.show = true
  184. }
  185. function handleTipConfirm(){
  186. if(["CLASS", "PERSON"].includes(tipDialog.type)){
  187. router.push({
  188. path: "/classList"
  189. })
  190. }else if(tipDialog.type === "MUSIC"){
  191. tipDialog.show = false
  192. }
  193. }
  194. const onSubmit = async () => {
  195. formsRef.value?.validate(async (err: any) => {
  196. if (err) {
  197. return;
  198. }
  199. forms.uploading = true;
  200. try {
  201. const trainList: any = props.item.lessonPreTrainingDetails || [];
  202. const details: any[] = [];
  203. trainList.forEach((item: any) => {
  204. details.push({
  205. trainingType: item.trainingType,
  206. musicId: item.musicId,
  207. trainingConfigJsonObject: item.trainingConfigJson
  208. });
  209. });
  210. const params = {
  211. name: forms.title,
  212. homeworkObj: forms.homeworkObj,
  213. homeworkType: forms.homeworkType,
  214. lessonTrainingDetails: details,
  215. expireDate: dayjs(forms.expireDate).format('YYYY-MM-DD HH:mm:ss'),
  216. classGroupId: forms.classGroupId
  217. ? forms.classGroupId.join(',')
  218. : null,
  219. studentIds: null as any,
  220. courseScheduleId: forms.courseScheduleId,
  221. chapterLessonCoursewareId: forms.chapterLessonCoursewareId // 课件编号
  222. };
  223. if (forms.homeworkObj === 'PERSON') {
  224. params.classGroupId = '';
  225. const ids: any[] = [];
  226. forms.studentList.forEach((item: any) => {
  227. ids.push(item.id);
  228. });
  229. params.studentIds = ids.join(',');
  230. }
  231. const lessonRes = await lessonTrainingAdd(params);
  232. if(lessonRes.code === 200){
  233. if(lessonRes.data.status){
  234. message.success('布置成功');
  235. emit('close');
  236. emit('confirm');
  237. }else{
  238. handleLessonAddErr(lessonRes.data)
  239. }
  240. }
  241. } catch {
  242. //
  243. }
  244. forms.uploading = false;
  245. });
  246. };
  247. const getCurrentGradeYear = async () => {
  248. try {
  249. const { data } = await api_getCurrentGradeYear({});
  250. console.log(data);
  251. forms.gradeYear = data;
  252. } catch {
  253. //
  254. }
  255. };
  256. const getDefaultParamConfig = async () => {
  257. try {
  258. const { data } = await request.get(
  259. '/edu-app/open/paramConfig/queryByParamNameList',
  260. {
  261. params: {
  262. paramNames: 'homework_max_deadline_time,homework_file_expire_time'
  263. }
  264. }
  265. );
  266. console.log(data, 'data');
  267. const result = data || [];
  268. result.forEach((item: any) => {
  269. if (item.paramName === 'homework_file_expire_time') {
  270. maxExpireTime.value = item.paramValue || 0;
  271. } else if (item.paramName === 'homework_max_deadline_time') {
  272. maxDeadlineTime.value = item.paramValue || 0;
  273. }
  274. });
  275. // 处理截止时间显示
  276. /**
  277. 1、当时间没有设置时,全部时间;
  278. 2、设置了时间,小于7天;
  279. 3、设置了时间,大于7天;
  280. */
  281. if (maxDeadlineTime.value > 0) {
  282. pickerEndTime.value = dayjs(
  283. dayjs()
  284. .add(Number(maxDeadlineTime.value) + 1, 'day')
  285. .format('YYYY-MM-DD')
  286. ).valueOf();
  287. if (Number(maxDeadlineTime.value) < 7) {
  288. forms.expireDate = dayjs()
  289. .add(Number(maxDeadlineTime.value), 'day')
  290. .format('YYYY-MM-DD HH:mm');
  291. }
  292. console.log(
  293. dayjs()
  294. .add(Number(maxDeadlineTime.value), 'day')
  295. .format('YYYY-MM-DD'),
  296. 'pickerEndTime.value'
  297. );
  298. // state.pickerEndTime =
  299. }
  300. } catch {
  301. //
  302. }
  303. };
  304. const dateDisabled = (ts: number): boolean => {
  305. /**
  306. 1、当时间没有设置时,全部时间;
  307. 2、设置了时间,小于7天;
  308. 3、设置了时间,大于7天;
  309. */
  310. let status = false;
  311. if (pickerEndTime.value) {
  312. status = ts < forms.currentTime || ts >= pickerEndTime.value;
  313. } else {
  314. status = ts < forms.currentTime;
  315. }
  316. console.log(
  317. status,
  318. '1212',
  319. ts > pickerEndTime.value,
  320. forms.currentTime,
  321. ts,
  322. pickerEndTime.value
  323. );
  324. // pickerEndTime.value && pickerEndTime.value > ts;
  325. return status;
  326. };
  327. onMounted(async () => {
  328. await getDefaultParamConfig();
  329. await getCurrentGradeYear();
  330. await getClassGroupList();
  331. });
  332. // 选择学生
  333. let assignHomeworkStuBoxDragData: any;
  334. let assignHomeworkStuBoxClass: string;
  335. if (props.from === 'class') {
  336. const users = useUserStore();
  337. assignHomeworkStuBoxClass = 'assignHomeworkStuBoxClass_drag';
  338. assignHomeworkStuBoxDragData = useDrag(
  339. [
  340. `${assignHomeworkStuBoxClass}>.n-card-header`,
  341. `${assignHomeworkStuBoxClass} .bom_drag`
  342. ],
  343. assignHomeworkStuBoxClass,
  344. toRef(forms, 'workVisiable'),
  345. users.info.id
  346. );
  347. }
  348. return () => (
  349. <div class={styles.assignHomeworkContainer}>
  350. <NForm
  351. ref={formsRef}
  352. model={forms}
  353. labelAlign="right"
  354. labelWidth={'80'}
  355. labelPlacement="left">
  356. <NFormItem
  357. label="布置方式"
  358. path="homeworkObj"
  359. rule={[{ required: true, message: '请选择布置方式' }]}>
  360. <NSpace>
  361. <NButton
  362. secondary
  363. class={[
  364. styles.switch,
  365. forms.homeworkObj === 'CLASS' ? styles.active : ''
  366. ]}
  367. onClick={() => (forms.homeworkObj = 'CLASS')}>
  368. 按班级布置
  369. </NButton>
  370. <NButton
  371. secondary
  372. class={[
  373. styles.switch,
  374. forms.homeworkObj === 'PERSON' ? styles.active : ''
  375. ]}
  376. onClick={() => (forms.homeworkObj = 'PERSON')}>
  377. 按学生布置
  378. </NButton>
  379. </NSpace>
  380. </NFormItem>
  381. <NFormItem
  382. label="作业标题"
  383. path="title"
  384. rule={[
  385. {
  386. required: true,
  387. message: '请输入作业标题',
  388. trigger: 'blur'
  389. }
  390. ]}>
  391. <NInput
  392. v-model:value={forms.title}
  393. placeholder="请选择作业标题"
  394. clearable
  395. />
  396. </NFormItem>
  397. <NFormItem
  398. label="年级"
  399. path="currentGradeNum"
  400. rule={[
  401. {
  402. required: true,
  403. message: '请选择年级',
  404. trigger: 'change',
  405. type: 'number'
  406. }
  407. ]}>
  408. <NSelect
  409. disabled={props.classGroupId ? true : false}
  410. v-model:value={forms.currentGradeNum}
  411. placeholder="请选择年级"
  412. options={forms.gradeList}
  413. clearable
  414. onUpdate:value={() => {
  415. forms.classGroupId = null;
  416. forms.studentList = []
  417. getClassList();
  418. }}
  419. />
  420. </NFormItem>
  421. {forms.homeworkObj === 'CLASS' ? (
  422. <NFormItem
  423. label="班级"
  424. path="classGroupId"
  425. rule={[
  426. {
  427. required: true,
  428. message: '请选择班级',
  429. trigger: 'change',
  430. type: 'array'
  431. }
  432. ]}>
  433. <NSelect
  434. disabled={props.classGroupId ? true : false}
  435. options={forms.classList}
  436. v-model:value={forms.classGroupId}
  437. placeholder="请选择班级"
  438. clearable
  439. multiple
  440. />
  441. </NFormItem>
  442. ) : (
  443. <NFormItem
  444. label="学生"
  445. path="studentList"
  446. rule={[
  447. {
  448. required: true,
  449. message: '请选择学生',
  450. trigger: 'change',
  451. type: 'array'
  452. }
  453. ]}
  454. class={styles.studentSection}>
  455. <span
  456. class={[
  457. styles.selectStudentBtn,
  458. !forms.currentGradeNum && styles.disabled
  459. ]}
  460. onClick={() => {
  461. if (!forms.currentGradeNum) {
  462. return;
  463. }
  464. const tempIds: any = [];
  465. forms.studentList.forEach((item: any) => {
  466. tempIds.push(item.id);
  467. });
  468. forms.selectIds = tempIds;
  469. forms.workVisiable = true;
  470. }}>
  471. 选择学生
  472. </span>
  473. {forms.studentList.length > 0 && (
  474. <NScrollbar class={styles.studentList}>
  475. <span class={styles.firstName}>
  476. 当前选中({forms.studentList.length || 0}):
  477. </span>
  478. {forms.studentList.map((item: any, index: number) => (
  479. <span class={styles.studentItem}>
  480. {item.name}{' '}
  481. <i
  482. class={styles.iconDelete}
  483. onClick={() => {
  484. forms.studentList.splice(index, 1);
  485. }}></i>
  486. </span>
  487. ))}
  488. </NScrollbar>
  489. )}
  490. </NFormItem>
  491. )}
  492. <NFormItem
  493. label="截止时间"
  494. path="expireDate"
  495. rule={[
  496. { required: true, message: '请选择截止时间', trigger: 'change' }
  497. ]}>
  498. <NDatePicker
  499. v-model:formatted-value={forms.expireDate}
  500. type="datetime"
  501. clearable
  502. // valueFormat="yyyy-MM-dd HH:mm"
  503. format="yyyy-MM-dd HH:mm"
  504. style={{ width: '100%' }}
  505. isDateDisabled={dateDisabled}
  506. />
  507. </NFormItem>
  508. {maxExpireTime.value > 0 && (
  509. <NFormItem label=" " class={styles.maxExpireTime}>
  510. <div class={styles.expireDateTip}>
  511. <i class={styles.expireDateIcon}></i>
  512. <span>
  513. 作业截止{maxExpireTime.value}
  514. 天后,学生上传的文件将过期,请及时查看
  515. </span>
  516. </div>
  517. </NFormItem>
  518. )}
  519. <NSpace class={styles.updateBtnGroup}>
  520. <NButton strong type="default" round onClick={() => emit('close')}>
  521. 取消布置
  522. </NButton>
  523. <NButton
  524. strong
  525. type="primary"
  526. round
  527. disabled={forms.uploading}
  528. loading={forms.uploading}
  529. onClick={onSubmit}>
  530. 确认布置
  531. </NButton>
  532. </NSpace>
  533. </NForm>
  534. <NModal
  535. maskClosable={modalClickMask}
  536. v-model:show={forms.workVisiable}
  537. style={
  538. props.from === 'class'
  539. ? {
  540. width: '640px',
  541. ...assignHomeworkStuBoxDragData.styleDrag.value
  542. }
  543. : {
  544. width: '640px'
  545. }
  546. }
  547. preset="card"
  548. showIcon={false}
  549. class={['modalTitle background', assignHomeworkStuBoxClass]}
  550. title={'选择学生'}
  551. blockScroll={false}>
  552. <AssignStudent
  553. classGroupId={props.classGroupId}
  554. currentGradeNum={forms.currentGradeNum as any}
  555. selectIds={forms.selectIds}
  556. studentList={forms.studentList}
  557. classList={forms.classList}
  558. onClose={() => (forms.workVisiable = false)}
  559. onConfirm={(val: any) => {
  560. forms.studentList = val || [];
  561. forms.workVisiable = false;
  562. }}
  563. />
  564. {props.from === 'class' && <Dragbom></Dragbom>}
  565. </NModal>
  566. <TheTipDialog
  567. show={tipDialog.show}
  568. content={tipDialog.msg}
  569. onClose={() => {
  570. tipDialog.show = false
  571. }}
  572. onConfirm={handleTipConfirm}
  573. cancelButtonText={"暂不设置"}
  574. cancelBtn={tipDialog.cancelBtn}
  575. confirmButtonText={tipDialog.confirmButtonText}
  576. ></TheTipDialog>
  577. </div>
  578. );
  579. }
  580. });