term-lesson.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. <template>
  2. <div id="term-lesson">
  3. <detail-frame :title="pageTitle">
  4. <el-row type="flex" align="middle" justify="end" class="btn_bar" v-if="!loading">
  5. <el-col :span="2">
  6. <el-button type="primary" size="mini" plain @click="toArrange">按模板排课</el-button>
  7. </el-col>
  8. <el-col :span="2">
  9. <el-button type="primary" size="mini" plain @click="toCollateTime">校对每日时间安排</el-button>
  10. </el-col>
  11. <el-col :span="2">
  12. <!-- <el-button type="primary" size="mini" @click="allSave">保存期课表</el-button> -->
  13. </el-col>
  14. <el-col :span="2">
  15. <el-button type="primary" size="mini" plain @click="toStatus">确定课表</el-button>
  16. </el-col>
  17. </el-row>
  18. <div style="min-height:800px" v-loading="loading" element-loading-text="期课表加载中,请稍后..." element-loading-background="rgba(0, 0, 0, 0.8)">
  19. <excel-term-lesson
  20. :classType="ctList"
  21. :data="list"
  22. :teacherList="teacherList"
  23. :noticeList="noticeList"
  24. :subjectList="subjectList"
  25. :headTeacherList="headTeacherList"
  26. @lessonChange="toLessonChange"
  27. @classChange="toClassChange"
  28. ></excel-term-lesson>
  29. </div>
  30. </detail-frame>
  31. <el-drawer title="课程安排" :visible.sync="lDrawer" :destroy-on-close="true" @close="toClose">
  32. <lesson-form v-if="lDrawer" :data="form" :subjectList="subjectList" :schoolList="schoolList" @save="lessonSave"></lesson-form>
  33. </el-drawer>
  34. <el-drawer title="班级安排" :visible.sync="cDrawer" :destroy-on-close="true" @close="toClose">
  35. <class-form
  36. v-if="cDrawer"
  37. :data="form"
  38. :locationList="locationList"
  39. :headTeacherList="headTeacherList"
  40. :teacherList="teacherList"
  41. @save="classSave"
  42. ></class-form>
  43. </el-drawer>
  44. </div>
  45. </template>
  46. <script>
  47. import _ from 'lodash';
  48. import axios from 'axios';
  49. // const moment = require('moment');
  50. // moment.locale('zh-cn');
  51. import excelTermLesson from './parts/excel-term-lesson.vue';
  52. import lessonForm from './parts/term-lesson-form';
  53. import classForm from './parts/term-class-from';
  54. import detailFrame from '@frame/layout/admin/detail-frame';
  55. import { mapState, createNamespacedHelpers } from 'vuex';
  56. const { mapActions: lesson } = createNamespacedHelpers('lesson');
  57. const { mapActions: classes } = createNamespacedHelpers('classes');
  58. const { mapActions: location } = createNamespacedHelpers('location'); //地点
  59. const { mapActions: teacher } = createNamespacedHelpers('teacher'); //教师
  60. const { mapActions: dept } = createNamespacedHelpers('dept'); //配合教师表使用的部门表
  61. const { mapActions: dirPlan } = createNamespacedHelpers('dirPlan'); //班主任不能上课的列表
  62. const { mapActions: teaplan } = createNamespacedHelpers('teaPlan');
  63. const { mapActions: mapDept } = createNamespacedHelpers('dept');
  64. const { mapActions: director } = createNamespacedHelpers('director');
  65. const { mapActions: school } = createNamespacedHelpers('school'); //给选老师组件使用.这个页面请求完就不销毁了
  66. const { mapActions: subject } = createNamespacedHelpers('subject');
  67. const { mapActions: notice } = createNamespacedHelpers('notice');
  68. const { mapActions: ct } = createNamespacedHelpers('classtype');
  69. export default {
  70. name: 'term-lesson',
  71. props: {},
  72. components: { detailFrame, excelTermLesson, lessonForm, classForm },
  73. data: function() {
  74. return {
  75. loading: true,
  76. lDrawer: false,
  77. cDrawer: false,
  78. form: {},
  79. formType: 'usual',
  80. options: {},
  81. list: [],
  82. classList: [],
  83. lessonList: [],
  84. locationList: [],
  85. teacherList: [],
  86. deptList: [],
  87. headTeacherList: [],
  88. schoolList: [],
  89. subjectList: [],
  90. // 班级类型列表
  91. ctList: [],
  92. //该期通知列表
  93. noticeList: [],
  94. //已经选择的教师列表
  95. inteaList: [],
  96. };
  97. },
  98. async created() {
  99. await this.getSettingLists();
  100. await this.search();
  101. this.$watch(
  102. 'defaultOption',
  103. val => {
  104. if (!_.get(this, 'options')) {
  105. this.$set(this, `options`, _.cloneDeep(val));
  106. this.search();
  107. } else {
  108. let ntermid = _.get(val, 'termid');
  109. let otermid = _.get(this.options, 'termid');
  110. if (ntermid && !_.isEqual(ntermid, otermid)) {
  111. this.$set(this, `options`, _.cloneDeep(val));
  112. this.search();
  113. }
  114. }
  115. },
  116. { deep: true }
  117. );
  118. },
  119. methods: {
  120. ...notice({ getNoticeList: 'query' }),
  121. ...lesson({
  122. getLesson: 'query',
  123. lessonUpdate: 'update',
  124. plupdate: 'pluralUpdate',
  125. autoArrange: 'arrange',
  126. confirmLesson: 'confirmLesson',
  127. newArrange: 'newArrange',
  128. timeCollate: 'timeCollate',
  129. }),
  130. ...classes({ getClass: 'query', pcupdate: 'pluralUpdate', classUpdate: 'update' }),
  131. ...ct({ getCt: 'query' }),
  132. ...subject({ getSubject: 'query' }),
  133. ...director({ getDirector: 'fetch', getDirectorList: 'query' }),
  134. ...school({ getSchool: 'findSchool' }),
  135. ...location({ getLocationList: 'query' }),
  136. ...teacher({ getTeacherList: 'query', getTeacher: 'fetch', getTeachers: 'lessonteafetch' }),
  137. ...dirPlan({ dirQuery: 'getDirTeacher' }),
  138. ...mapDept({ getDept: 'query' }),
  139. ...teaplan(['findTeacher']),
  140. async search() {
  141. this.loading = true;
  142. // 将 请求班级和 请求课表拆开,以便之后局部修改,保存减少请求次数
  143. const res = await this.toGetClass();
  144. if (res === 'noClass') {
  145. this.loading = false;
  146. return;
  147. }
  148. await this.toGetLesson();
  149. this.toSetData();
  150. this.toGetNoticeList();
  151. this.loading = false;
  152. },
  153. // 获取班级
  154. async toGetClass() {
  155. let termid = _.get(this.defaultOption, `termid`);
  156. if (!termid) return;
  157. let classes = await this.getClass({ termid });
  158. let classList = [];
  159. if (this.$checkRes(classes)) {
  160. classList = classes.data;
  161. if (classList.length <= 0) {
  162. this.$message.error('班级未生成,无法查询课表,请先进行学生分班');
  163. return 'noClass';
  164. }
  165. classList = classList.map(i => {
  166. if (parseInt(i.name)) i.order = parseInt(i.name);
  167. else i.order = i.name;
  168. return i;
  169. });
  170. classList = _.orderBy(classList, ['order'], ['asc']);
  171. this.$set(this, `classList`, classList);
  172. }
  173. },
  174. // 获取课程
  175. async toGetLesson() {
  176. let termid = _.get(this.defaultOption, `termid`);
  177. if (!termid) return;
  178. let lessonList = [];
  179. let lessons = await this.getLesson({ termid });
  180. if (this.$checkRes(lessons)) {
  181. lessonList = lessons.data;
  182. // 因为操作不当,lesson会产生垃圾数据;所以这里要过滤下,只保留当前班级的lesson
  183. const classids = this.classList.map(f => f._id);
  184. lessonList = lessonList.filter(f => classids.includes(f.classid));
  185. if (lessonList.length <= 0) {
  186. this.toArrange(false);
  187. return;
  188. }
  189. this.$set(this, `lessonList`, lessonList);
  190. }
  191. },
  192. // 整理班级和课程
  193. async toSetData() {
  194. let classList = _.cloneDeep(this.classList);
  195. classList = classList.map(i => {
  196. const lesson = this.lessonList.find(f => f.classid === i._id);
  197. if (lesson) i.lesson = lesson;
  198. else i.lesson = {};
  199. return i;
  200. });
  201. this.$set(this, `list`, classList);
  202. },
  203. // 获取地点,学校,部门,科目,教师,班级类型,班主任
  204. async getSettingLists() {
  205. let res;
  206. let arr = [];
  207. if (this.locationList.length <= 0) {
  208. arr.push(this.getLocationList());
  209. }
  210. if (this.teacherList.length <= 0) {
  211. arr.push(this.getTeacherList());
  212. }
  213. if (this.schoolList.length <= 0) {
  214. arr.push(this.getSchool());
  215. }
  216. if (this.deptList.length <= 0) {
  217. arr.push(this.getDept());
  218. }
  219. if (this.subjectList.length <= 0) {
  220. arr.push(this.getSubject());
  221. }
  222. if (this.ctList.length <= 0) {
  223. arr.push(this.getCt());
  224. }
  225. if (this.headTeacherList.length <= 0) {
  226. arr.push(this.getDirectorList());
  227. }
  228. axios.all(arr).then(
  229. axios.spread((r1, r2, r3, r4, r5, r6, r7) => {
  230. if (r1) this.$set(this, `locationList`, r1.data);
  231. if (r2) this.$set(this, `teacherList`, r2.data);
  232. if (r3) this.$set(this, `schoolList`, r3.data);
  233. if (r4) this.$set(this, `deptList`, r4.data);
  234. if (r5) this.$set(this, `subjectList`, r5.data);
  235. if (r6) {
  236. let d = _.cloneDeep(r6.data);
  237. d = d.map(i => {
  238. i.order = parseInt(i.code);
  239. return i;
  240. });
  241. d = _.orderBy(d, ['order'], ['asc']);
  242. this.$set(this, `ctList`, d);
  243. }
  244. if (r7) this.$set(this, `headTeacherList`, r7.data);
  245. })
  246. );
  247. },
  248. // 操作部分
  249. // 修改教师
  250. async lessonSave(data) {
  251. console.log(data);
  252. // lessonObjectId:lessonList的某项id; _id:lessons中指定的课
  253. const { lessonObjectId, _id, is_last, ...info } = data;
  254. let lobj = this.lessonList.find(f => f._id === lessonObjectId);
  255. if (!lobj) {
  256. this.$message.error('未找到该班课表');
  257. return;
  258. }
  259. let { lessons } = lobj;
  260. if (!lessons) {
  261. this.$message.error('未找到该班的课程');
  262. return;
  263. }
  264. let r = lessons.find(f => f._id === _id);
  265. if (!r) {
  266. this.$message.error('未找到该课程');
  267. return;
  268. }
  269. const ri = lessons.findIndex(f => f._id === _id);
  270. r = { ...r, ...info };
  271. lessons[ri] = r;
  272. let sameDay = lessons.find(f => f.date === r.date && f.subid && f._id !== r._id);
  273. if (sameDay) {
  274. const { teaid, teaname, reason } = data;
  275. sameDay.teaid = teaid;
  276. sameDay.teaname = teaname;
  277. sameDay.reason = reason;
  278. let sameDayIndex = lessons.findIndex(f => f.date === r.date && f.subid && f._id !== r._id);
  279. lessons[sameDayIndex] = sameDay;
  280. }
  281. lobj.lessons = lessons;
  282. let msg = this.$message({ duration: 0, message: '正在修改课程信息,请稍后...' });
  283. const res = await this.lessonUpdate(lobj);
  284. msg.close();
  285. if (this.$checkRes(res, null, res.errmsg || '修改失败')) {
  286. msg = this.$message({ duration: 0, message: '重新读取课程信息,请稍后...' });
  287. await this.toClearSameDay(data);
  288. if (is_last) {
  289. await this.toChangeDay(data);
  290. }
  291. await this.toGetLesson();
  292. this.toSetData();
  293. this.toClose();
  294. msg.close();
  295. this.$message.success('已完成重新读取课程信息');
  296. }
  297. },
  298. // 将该教师其他同一天的安排干掉
  299. async toClearSameDay(data) {
  300. const { lessonObjectId, date, teaid } = data;
  301. let lobjs = this.lessonList.filter(f => f._id !== lessonObjectId);
  302. let needUpdate = [];
  303. for (let lobj of lobjs) {
  304. let { lessons } = lobj;
  305. if (!lessons || !_.isArray(lessons)) continue;
  306. let ncList = lessons.filter(f => f.date === date && f.teaid === teaid);
  307. // 该班课表,该天,没有该教师了,继续下一个
  308. if (ncList.length <= 0) continue;
  309. // 要是有,就改了,然后放到needUpdate中
  310. for (let nc of ncList) {
  311. const ri = lessons.findIndex(f => _.isEqual(f, nc));
  312. if (ri < 0) continue;
  313. nc.teaid = undefined;
  314. nc.teaname = undefined;
  315. lessons[ri] = nc;
  316. }
  317. lobj.lessons = lessons;
  318. delete lobj.meta;
  319. needUpdate.push(lobj);
  320. }
  321. if (needUpdate.length > 0) {
  322. const r = await this.plupdate(needUpdate);
  323. }
  324. },
  325. // 修改班级设置
  326. async classSave(data) {
  327. let msg = this.$message({ duration: 0, message: '正在修改,请稍后...' });
  328. const res = await this.classUpdate(data);
  329. msg.close();
  330. if (this.$checkRes(res, null, res.errmsg || '修改班级设置失败')) {
  331. msg = this.$message({ duration: 0, message: '重新读取班级信息,请稍后...' });
  332. await this.toGetClass();
  333. this.toSetData();
  334. this.toClose();
  335. msg.close();
  336. this.$message.success('已完成重新读取班级信息');
  337. }
  338. },
  339. // 修改这一批次的所有课程的最后一天为全天/半天
  340. async toChangeDay(data) {
  341. const { lessonObjectId, _id, is_last, day, ...info } = data;
  342. let lobj = this.lessonList.find(f => f._id === lessonObjectId);
  343. if (!lobj) return;
  344. const { batchid } = lobj;
  345. let lobjs = this.lessonList.filter(f => f.batchid === batchid);
  346. for (let o of lobjs) {
  347. delete o.meta;
  348. let { lessons } = o;
  349. if (!(lessons && _.isArray(lessons))) continue;
  350. const g = _.groupBy(lessons, 'date');
  351. console.log(g);
  352. let dkeys = Object.keys(g);
  353. dkeys = _.orderBy(
  354. dkeys.map(i => ({ date: i })),
  355. ['date'],
  356. ['asc']
  357. );
  358. const k = _.last(dkeys);
  359. if (!k) continue;
  360. // 将最后一天的所有day改成data的day
  361. let needCList = g[k.date];
  362. console.log(needCList);
  363. for (let nco of needCList) {
  364. const i = lessons.findIndex(f => f._id === nco._id);
  365. if (i < 0) continue;
  366. nco.day = day;
  367. console.log(nco);
  368. lessons[i] = nco;
  369. }
  370. o.lessons = lessons;
  371. }
  372. const r = await this.plupdate(lobjs);
  373. },
  374. // 页面及其他逻辑
  375. // 打开修改课程框
  376. toLessonChange(data) {
  377. this.$set(this, 'form', data);
  378. this.lDrawer = true;
  379. },
  380. // 打开修改班级设置框
  381. toClassChange(data) {
  382. this.$set(this, `form`, data);
  383. this.cDrawer = true;
  384. },
  385. // 关闭窗口
  386. toClose() {
  387. this.lDrawer = false;
  388. this.cDrawer = false;
  389. this.formType = 'usual';
  390. this.form = {};
  391. },
  392. //检查班主任/教室/礼仪课教师
  393. checkClassConfig(dClass) {
  394. let res = [];
  395. for (const cla of dClass) {
  396. let { headteacherid, jslocationid, lyteacherid, name } = cla;
  397. let obj = { name };
  398. // true:表示这个位置没有id
  399. if (!headteacherid) obj.headteacher = true;
  400. if (!jslocationid) obj.jslocation = true;
  401. if (!lyteacherid) obj.lyteacher = true;
  402. if (Object.keys(obj).length > 1) res.push(obj);
  403. }
  404. return res;
  405. },
  406. //查看某班课表
  407. toOneClass(classid) {
  408. this.$router.push({ path: '/train/plan/class/lesson', query: { classid } });
  409. },
  410. //课表模板排课
  411. async toArrange(needAlert = true) {
  412. let planid = _.get(this.defaultOption, 'planid');
  413. let termid = _.get(this.defaultOption, `termid`);
  414. if (needAlert)
  415. this.$confirm('此操作将会将后生成且没有课表的班级生成课表,已经生成的课表将不会改变!', '提示', {
  416. confirmButtonText: '按模板排课',
  417. cancelButtonText: '取消',
  418. type: 'warning',
  419. })
  420. .then(async () => {
  421. let res = await this.newArrange({ planid, termid });
  422. this.$checkRes(res, '排课成功', res.errmsg || '排课失败');
  423. this.search();
  424. })
  425. .catch(async () => {
  426. console.log('已取消');
  427. });
  428. else {
  429. let msg = this.$message({
  430. message: '首次进入,计划未排课,正在按模板进行排课中...',
  431. duration: 0,
  432. });
  433. let res = await this.newArrange({ planid, termid });
  434. msg.close();
  435. if (this.$checkRes(res, '排课成功', res.errmsg || '排课失败')) {
  436. this.search();
  437. } else {
  438. this.loading = false;
  439. }
  440. }
  441. },
  442. // 确定课表
  443. async toStatus() {
  444. let lessonIds = this.lessonList.map(i => i.id);
  445. const classids = this.classList.map(f => f._id);
  446. const res = await this.confirmLesson({ ids: lessonIds, classids });
  447. this.$checkRes(res, '课表确定成功', res.errmsg || '课表确定失败');
  448. },
  449. // 获取消息
  450. async toGetNoticeList() {
  451. const { planid, termid } = this.defaultOption;
  452. const res = await this.getNoticeList({ planid, termid, type: '4' });
  453. if (this.$checkRes(res)) this.$set(this, `noticeList`, res.data);
  454. },
  455. // 校对时间
  456. async toCollateTime() {
  457. let termid = _.get(this.defaultOption, `termid`);
  458. this.$confirm('此操作将会将把 本期的 每天时间的安排 按 "系统管理->课表模板" 中对应的模板进行覆盖,请确认好 课表模板 中的时间是否正确!', '提示', {
  459. confirmButtonText: '确认覆盖',
  460. cancelButtonText: '取消',
  461. type: 'warning',
  462. })
  463. .then(async () => {
  464. const res = await this.timeCollate({ termid });
  465. if (this.$checkRes(res)) {
  466. this.$message.success('校对成功');
  467. this.search();
  468. }
  469. })
  470. .catch(async () => {
  471. console.log('已取消');
  472. });
  473. },
  474. },
  475. computed: {
  476. ...mapState(['user', 'defaultOption']),
  477. pageTitle() {
  478. return `${this.$route.meta.title}`;
  479. },
  480. },
  481. metaInfo() {
  482. return { title: this.$route.meta.title };
  483. },
  484. };
  485. </script>
  486. <style lang="less" scoped></style>