class-table.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. <template>
  2. <div id="class-table">
  3. <el-card>
  4. <template #header>
  5. <el-row type="flex" align="middle" justify="space-between">
  6. <el-col :span="4">课程安排</el-col>
  7. <el-col :span="2">
  8. <el-button type="primary" size="mini" @click="drawerTime = true">添加时间段</el-button>
  9. </el-col>
  10. </el-row>
  11. </template>
  12. <el-table :data="lessonList" border stripe @cell-click="cellClick">
  13. <el-table-column align="center" label="时间" prop="time"></el-table-column>
  14. <el-table-column align="center" v-for="(i, index) in dateList" :key="index" :label="i" :prop="`subname_day${index + 1}`">
  15. <template v-slot="{ row, $index }">
  16. <el-row>
  17. <el-col :span="24">{{ getProp(row, `subname_day${index + 1}`) }}</el-col>
  18. <el-col :span="24" v-if="getProp(row, `teaname_day${index + 1}`)">
  19. <p>{{ getProp(row, `teaname_day${index + 1}`) }}</p>
  20. <p>{{ getPhone(row, `teaid_day${index + 1}`) }}</p>
  21. </el-col>
  22. <el-col :span="24">
  23. <el-link :type="wordColor(getNoticeResult(getProp(row, `teaid_day${index + 1}`)))">
  24. {{ getNoticeResult(getProp(row, `teaid_day${index + 1}`)) }}
  25. </el-link>
  26. </el-col>
  27. </el-row>
  28. </template>
  29. </el-table-column>
  30. </el-table>
  31. </el-card>
  32. <el-drawer :visible.sync="drawer" direction="rtl" title="课程安排" @close="toClose">
  33. <data-form :data="form" :fields="resetForm()" :rules="{}" @save="handleSave" :reset="false">
  34. <template #radios="{item, form}">
  35. <template v-if="item.model == 'type'">
  36. <el-radio @change="radioClearForm" v-for="(i, index) in dayType" :key="index" :label="i.label">{{ i.label }}</el-radio>
  37. </template>
  38. </template>
  39. <template #options="{item, form}">
  40. <template v-if="item.model == 'subname'">
  41. <el-option v-for="(i, index) in actList" :key="index" :label="i.label" :value="i.label"></el-option>
  42. </template>
  43. <template v-if="item.model == 'subid'">
  44. <el-option v-for="(i, index) in subjectList" :key="index" :label="i.name" :value="i.id"></el-option>
  45. </template>
  46. </template>
  47. <template #custom="{item, form}">
  48. <template v-if="item.model == 'teaname'">
  49. <el-input v-model="form.teaname" :readonly="true" placeholder="点击选择教师" @click.native="toChooseTeacher"></el-input>
  50. <!-- <el-input v-model="form.reason" v-if="needReason()" placeholder="请填写修改教师原因"></el-input> -->
  51. </template>
  52. </template>
  53. </data-form>
  54. </el-drawer>
  55. <el-drawer :visible.sync="drawerTime" direction="rtl" title="课程时间安排" @close="toClose">
  56. <el-form :model="form" size="mini" style="padding:20px">
  57. <el-form-item label="时间">
  58. <el-time-picker
  59. v-model="form.time"
  60. placeholder="请选择时间"
  61. :is-range="true"
  62. value-format="HH:mm"
  63. :picker-options="{ format: 'HH:mm' }"
  64. ></el-time-picker>
  65. </el-form-item>
  66. <el-form-item>
  67. <el-row type="flex" align="middle" justify="center">
  68. <el-col :span="4">
  69. <el-button type="primary" plain @click="hsavetime('save')">保存</el-button>
  70. </el-col>
  71. <el-col :span="4">
  72. <el-button type="danger" plain v-if="form && form.index >= 0" @click="hsavetime('delete')">删除</el-button>
  73. </el-col>
  74. </el-row>
  75. </el-form-item>
  76. </el-form>
  77. </el-drawer>
  78. <el-dialog title="选择教师" :visible.sync="dialog" :destroy-on-close="true">
  79. <teacher-select :schoolList="schoolList" :subjectList="subjectList" :subjectid="form.subid" @selTea="selTea"> </teacher-select>
  80. </el-dialog>
  81. </div>
  82. </template>
  83. <script>
  84. var moment = require('moment');
  85. import _ from 'lodash';
  86. import dataForm from '@frame/components/form';
  87. import teacherSelect from './teacher.vue';
  88. import { mapState, createNamespacedHelpers } from 'vuex';
  89. const { mapActions: lesson } = createNamespacedHelpers('lesson');
  90. const { mapActions: mapUtil } = createNamespacedHelpers('util');
  91. const { mapActions: subject } = createNamespacedHelpers('subject');
  92. const { mapActions: teacher } = createNamespacedHelpers('teacher');
  93. const { mapActions: school } = createNamespacedHelpers('school'); //给选老师组件使用.这个页面请求完就不销毁了
  94. // 本页的组合数据,变量用x表示: _id_day[x];subname_day[x];subid_day[x];teaid_day[x];teaname_day:[x]
  95. export default {
  96. name: 'class-table',
  97. props: {
  98. classInfo: { type: Object, default: () => {} },
  99. },
  100. components: { dataForm, teacherSelect },
  101. data: function() {
  102. var that = this;
  103. return {
  104. oLessonList: [],
  105. lessonInfo: {},
  106. lessonList: [],
  107. dateList: [],
  108. timeList: [],
  109. subjectList: [],
  110. teacherList: [],
  111. schoolList: [], //给选老师组件用
  112. drawer: false,
  113. drawerTime: false,
  114. dialog: false,
  115. form: {},
  116. fields: [
  117. { label: '日期', model: 'date', type: 'text' },
  118. { label: '时间', model: 'time', type: 'text' },
  119. { label: '类型', model: 'type', type: 'radio' },
  120. { label: '课程安排', model: 'subid', type: 'select', display: (fields, form) => that.fieldDisplay(fields, form) },
  121. { label: '活动安排', model: 'subname', type: 'select', display: (fields, form) => that.fieldDisplay(fields, form) },
  122. { label: '教师', model: 'teaname', custom: true, display: (fields, form) => that.fieldDisplay(fields, form) },
  123. ],
  124. dayType: [{ label: '活动' }, { label: '课程' }],
  125. actList: [
  126. { label: '--' },
  127. { label: '报道+开班仪式' },
  128. { label: '午餐+休息' },
  129. { label: '晚餐' },
  130. { label: '团队组建' },
  131. { label: '拓展交流' },
  132. { label: '课程作业小组展示' },
  133. { label: '课程作业' },
  134. { label: '礼仪课小组面试' },
  135. { label: '结业仪式' },
  136. ],
  137. noticeList: [],
  138. };
  139. },
  140. created() {
  141. this.getOtherList();
  142. },
  143. methods: {
  144. ...mapUtil(['fetch']),
  145. ...lesson(['query', 'create', 'update']),
  146. ...subject({ getSubject: 'query' }),
  147. ...teacher({ getTeacher: 'query', lessonteafetch: 'lessonteafetch' }),
  148. ...school({ getSchool: 'query' }),
  149. async search() {
  150. let res = await this.fetch({ model: 'lesson', classid: _.get(this.classInfo, '_id') });
  151. if (this.$checkRes(res)) {
  152. if (!_.get(res.data, 'lessons') || res.data.lessons.length <= 0) {
  153. this.$message.warning('请先将本期的课程按模板进行初始化');
  154. return;
  155. }
  156. this.$set(this, `lessonInfo`, _.omit(res.data, ['lessons']));
  157. let arr = _.get(res.data, `lessons`, []);
  158. this.$set(this, `oLessonList`, _.cloneDeep(arr));
  159. this.seachTeacher();
  160. let x = this.getX(JSON.parse(JSON.stringify(arr)));
  161. this.getY(JSON.parse(JSON.stringify(arr)));
  162. this.$set(this, `dateList`, x);
  163. arr = this.aData(arr);
  164. this.$set(this, `lessonList`, arr);
  165. this.getTeacherNotice();
  166. }
  167. },
  168. async toSave() {
  169. //整理成原数据形式,提交
  170. let data = JSON.parse(JSON.stringify(this.lessonList));
  171. data = this.returnData(data);
  172. let lesson = JSON.parse(JSON.stringify(this.lessonInfo));
  173. lesson.lessons = data;
  174. let res = await this.update(lesson);
  175. if (this.$checkRes(res, null, res.errmsg || '课程表保存失败')) {
  176. this.$emit('saveResult', { from: 'lesson', result: true });
  177. }
  178. },
  179. //点击单元格事件
  180. cellClick(row, column) {
  181. let prop = _.get(column, 'property');
  182. if (prop != 'time') {
  183. let date = _.get(column, 'label');
  184. let time = _.get(row, 'time');
  185. let num = _.get(column, 'property').match(/\d+(.\d+)?/g)[0];
  186. let obj = this.getOrderDate(row, num);
  187. obj.type = obj.subid ? '课程' : '活动';
  188. this.$set(this, `form`, { date, time, ...obj });
  189. this.drawer = true;
  190. } else {
  191. let obj = _.cloneDeep(row);
  192. let index = this.lessonList.findIndex(f => f.time == obj.time);
  193. obj.time = obj.time.split('-');
  194. obj.index = index;
  195. this.$set(this, `form`, obj);
  196. this.drawerTime = true;
  197. }
  198. },
  199. //抽屉保存
  200. handleSave({ data }) {
  201. let num = _.get(data, 'index');
  202. let type = _.get(data, `type`);
  203. let time = _.get(data, `time`);
  204. let yIndex = this.lessonList.findIndex(f => f.time == time);
  205. let obj = {};
  206. if (type == '课程') {
  207. obj = _.pick(data, ['subid', 'teaid', 'teaname', '_id', 'reason']);
  208. let r = this.subjectList.find(f => f.id == obj.subid);
  209. if (r) obj.subname = r.name;
  210. } else {
  211. obj = _.pick(data, ['subname', '_id']);
  212. }
  213. obj = this.resetData(obj, num);
  214. this.$set(this.lessonList, yIndex, { ...this.lessonList[yIndex], ...obj });
  215. this.setSubTea();
  216. this.drawer = false;
  217. },
  218. //提交整理数据
  219. returnData(data) {
  220. let returnArr = [];
  221. data.map(i => {
  222. let keys = Object.keys(i);
  223. let time = _.get(i, `time`);
  224. let arr = _.compact(_.uniq(_.flatten(keys.map(i => i.match(/\d+(.\d+)?/g)))));
  225. arr.map(index => {
  226. let obj = this.getOrderDate(i, index, true);
  227. obj.time = time;
  228. obj.day = '0';
  229. returnArr.push(obj);
  230. });
  231. });
  232. let r = returnArr.filter(f => f.date == _.last(this.dateList));
  233. let allday = '0';
  234. let res = r.find(f => {
  235. //TODO 根据开始时间不超过12点判断是 整天还是半天
  236. if (f.subname != '--') {
  237. let ts = f.time.split('-');
  238. let time = moment(`${f.date} ${ts[0]}`).format('X');
  239. let twl = moment(`${f.date} 12:00`).format('X');
  240. return twl <= time;
  241. }
  242. });
  243. if (res) allday = '1';
  244. returnArr = returnArr.map(i => {
  245. if (i.date == _.last(this.dateList)) i.allday = allday;
  246. else i.allday = '0';
  247. return i;
  248. });
  249. return returnArr;
  250. },
  251. //field的显示
  252. fieldDisplay(f, form) {
  253. if (f.model == 'teaname' || f.model == 'subid') {
  254. return form.type == '课程';
  255. } else return form.type == '活动';
  256. },
  257. //请求后整理数据方法
  258. aData(data) {
  259. let duplicate = JSON.parse(JSON.stringify(data));
  260. //按时间分组
  261. duplicate = _.flatten(_.toPairs(_.groupBy(data, 'time'))).filter(f => _.isArray(f));
  262. let r = duplicate.map(i => {
  263. //按日期排序
  264. let aa = i.sort((a, b) => moment(a.date).format('X') - moment(b.date).format('X'));
  265. //组合数据:{time,day1,id_day1,subid_day1}
  266. let object = { time: _.get(i[0], 'time') };
  267. aa.map(a => {
  268. let index = this.dateList.findIndex(f => f == a.date);
  269. if (index >= 0) {
  270. index = index + 1;
  271. }
  272. let obj = this.resetData(a, index);
  273. object = { ...object, ...obj };
  274. });
  275. return object;
  276. });
  277. r = this.getOrderForTime(r);
  278. return r;
  279. },
  280. //获取指定数据
  281. getOrderDate(data, index, needDate = false) {
  282. let obj = { index: index };
  283. if (_.get(data, `_id_day${index}`)) obj[`_id`] = _.get(data, `_id_day${index}`);
  284. obj[`subname`] = _.get(data, `subname_day${index}`, `--`);
  285. if (_.get(data, `subid_day${index}`)) obj[`subid`] = _.get(data, `subid_day${index}`);
  286. if (_.get(data, `teaid_day${index}`)) obj[`teaid`] = _.get(data, `teaid_day${index}`);
  287. if (_.get(data, `teaname_day${index}`)) obj[`teaname`] = _.get(data, `teaname_day${index}`);
  288. if (_.get(data, `reason${index}`)) obj[`reason`] = _.get(data, `reason${index}`);
  289. if (needDate) {
  290. //所有的数据都还原了,没必要要index了
  291. delete obj.index;
  292. obj.date = this.dateList[index - 1];
  293. }
  294. return obj;
  295. },
  296. //整理,匹配数据是哪天,该显示在哪
  297. resetData(data, index) {
  298. let obj = {};
  299. if (_.get(data, '_id')) obj[`_id_day${index}`] = _.get(data, '_id');
  300. obj[`subname_day${index}`] = _.get(data, 'subname', '--');
  301. if (_.get(data, 'subid')) obj[`subid_day${index}`] = _.get(data, 'subid');
  302. if (_.get(data, 'teaid')) obj[`teaid_day${index}`] = _.get(data, 'teaid');
  303. if (_.get(data, 'teaname')) obj[`teaname_day${index}`] = _.get(data, 'teaname');
  304. if (_.get(data, 'reason')) obj[`reason${index}`] = _.get(data, 'reason');
  305. return obj;
  306. },
  307. //修改:选择科目/教师后,将次科目的所有数据统一
  308. setSubTea() {
  309. let duplicate = _.cloneDeep(this.lessonList);
  310. let arr = this.returnData(duplicate);
  311. let teaList = arr.filter(f => f.teaid);
  312. let res = arr.map(i => {
  313. let r = teaList.find(f => f.subid == i.subid);
  314. if (r) {
  315. i.teaid = r.teaid;
  316. i.teaname = r.teaname;
  317. }
  318. return i;
  319. });
  320. res = this.aData(res);
  321. this.$set(this, 'lessonList', res);
  322. },
  323. //根据时间排序
  324. getOrderForTime(data) {
  325. let duplicate = JSON.parse(JSON.stringify(data));
  326. duplicate = duplicate.sort((a, b) => {
  327. let a_arr = a.time.split('-');
  328. let b_arr = b.time.split('-');
  329. let at = moment(`${moment().format('YYYY-MM-DD')} ${a_arr[0]}`).format('X');
  330. let bt = moment(`${moment().format('YYYY-MM-DD')} ${b_arr[0]}`).format('X');
  331. return at - bt;
  332. });
  333. return duplicate;
  334. },
  335. //整理出标头,根据日期排序
  336. getX(data) {
  337. let r = _.uniqBy(data, 'date').map(i => i.date);
  338. r = r.sort((a, b) => moment(a).format('X') - moment(b).format('X'));
  339. return r;
  340. },
  341. //获得时间列表
  342. getY(data) {
  343. let duplicate = JSON.parse(JSON.stringify(data));
  344. let arr = _.uniqBy(
  345. duplicate.map(i => _.pick(i, ['time'])),
  346. 'time'
  347. );
  348. arr = this.getOrderForTime(arr);
  349. this.$set(
  350. this,
  351. `timeList`,
  352. arr.map(i => i.time)
  353. );
  354. },
  355. //课程列表
  356. async getOtherList() {
  357. let res = await this.getSubject();
  358. if (this.$checkRes(res)) this.$set(this, `subjectList`, res.data);
  359. // res = await this.getTeacher({ status: '4' });
  360. // if (this.$checkRes(res)) this.$set(this, `teacherList`, res.data);
  361. },
  362. // 教师列表
  363. async seachTeacher() {
  364. let arr = this.oLessonList.filter(item => item.teaid != null);
  365. let NewArr = _.uniqBy(arr, 'teaid');
  366. var ids = NewArr.map(item => item.teaid);
  367. let res = await this.lessonteafetch(ids);
  368. if (this.$checkRes(res)) this.$set(this, `teacherList`, res.data);
  369. res = await this.fetch({ model: 'notice', classid: this.classInfo.id, type: '4', planid: this.classInfo.planid, termid: this.classInfo.termid });
  370. if (this.$checkRes(res)) this.$set(this, `noticeList`, res.data);
  371. },
  372. //关闭抽屉
  373. toClose() {
  374. this.drawer = false;
  375. this.drawerTime = false;
  376. this.form = {};
  377. },
  378. //修改类型清除数据
  379. radioClearForm(data) {
  380. if (data == '活动') {
  381. delete this.form.subid;
  382. this.form.subname = '--';
  383. }
  384. },
  385. //打开选择教师的dialog
  386. async toChooseTeacher() {
  387. this.dialog = true;
  388. if (this.schoolList.length <= 0) {
  389. let res = await this.getSchool();
  390. if (this.$checkRes(res)) this.$set(this, `schoolList`, res.data);
  391. }
  392. },
  393. //选择教师
  394. selTea(data) {
  395. this.dialog = false;
  396. this.$set(this, `form`, { ...this.form, ...data });
  397. },
  398. //显示
  399. getProp(data, prop) {
  400. return _.get(data, prop);
  401. },
  402. // 手机号
  403. getPhone(data, prop) {
  404. let teacher = this.teacherList.find(f => f.id == _.get(data, prop));
  405. if (teacher) return teacher.phone;
  406. },
  407. //时间处理
  408. hsavetime(type) {
  409. let { index } = this.form;
  410. if (type == 'delete') this.lessonList.splice(index, 1);
  411. else {
  412. let data = _.cloneDeep(this.form);
  413. data.time = data.time.join('-');
  414. if (index != undefined && index >= 0) this.$set(this.lessonList, index, data);
  415. else this.lessonList.push(data);
  416. }
  417. this.toClose();
  418. //重新排序
  419. this.$set(this, `lessonList`, this.getOrderForTime(this.lessonList));
  420. },
  421. //判断是否修改教师,需要填写修改教师的原因
  422. needReason() {
  423. if (this.form.reason) return true;
  424. else {
  425. let { _id, teaid } = this.form;
  426. let r = this.oLessonList.find(f => f._id == _id);
  427. if (r) {
  428. if (_.get(r, 'teaid')) return _.isEqual(teaid, r.teaid);
  429. else return false;
  430. } else return false;
  431. // teaid
  432. }
  433. },
  434. resetForm() {
  435. let res = this.needReason();
  436. let fields = _.cloneDeep(this.fields);
  437. if (res) {
  438. fields.push({ label: '修改教师原因', model: 'reason', required: true });
  439. }
  440. return fields;
  441. },
  442. toConfirm() {
  443. this.$emit('toConfirm', _.get(this.lessonInfo, `_id`));
  444. },
  445. getNoticeResult(teaid) {
  446. let word = '';
  447. if (teaid) {
  448. const { notified } = this.noticeList;
  449. const r = notified.find(f => f.notifiedid == teaid);
  450. if (!r) word = '未绑定微信';
  451. else {
  452. word = r.status == '0' ? '未确认' : '已确认';
  453. }
  454. }
  455. return word;
  456. },
  457. wordColor(word) {
  458. let type = 'danger';
  459. if (word == '未确认') type = 'warning';
  460. if (word == '已确认') type = 'success';
  461. return type;
  462. },
  463. },
  464. computed: {
  465. ...mapState(['user']),
  466. pageTitle() {
  467. return `${this.$route.meta.title}`;
  468. },
  469. },
  470. watch: {
  471. classInfo: {
  472. handler(val) {
  473. let id = _.get(val, '_id');
  474. if (id) this.search();
  475. },
  476. immediate: true,
  477. deep: true,
  478. },
  479. },
  480. metaInfo() {
  481. return { title: this.$route.meta.title };
  482. },
  483. };
  484. </script>
  485. <style lang="less" scoped></style>