arrange.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. <template>
  2. <div id="arrange">
  3. <!-- <detail-frame :title="pageTitle" :returns="returns"> -->
  4. <el-row type="flex" justify="center" v-if="view == 'plan'">
  5. <el-col :span="24" :style="`overflow:auto`">
  6. <el-card ref="card" v-if="info.year" height="800px">
  7. <calendar
  8. :year="`${info.year || '2020'}`"
  9. :selfBtn="selfBtn"
  10. @draft="selectDate"
  11. @eventClick="eventClick"
  12. :vacation="vacation"
  13. :events="events"
  14. ></calendar>
  15. </el-card>
  16. </el-col>
  17. </el-row>
  18. <el-drawer :visible.sync="drawer" direction="rtl" title="安排计划" @close="toClose">
  19. <event
  20. :data="form"
  21. :year="info.year"
  22. :vacation="vacation"
  23. :isNew="formIsNew"
  24. :predefineColors="template.color"
  25. @save="setEvent"
  26. @delete="toDelete"
  27. ></event>
  28. </el-drawer>
  29. <el-dialog :visible.sync="dialog" title="模板计划" width="30%" :close-on-click-modal="false">
  30. <el-form>
  31. <el-form-item label="请输入您要生成的期数">
  32. <el-input v-model="input.term"></el-input>
  33. </el-form-item>
  34. <el-form-item label="请输入开始的期数">
  35. <el-tooltip content="第X期 此处为填写X" placement="right">
  36. <el-input-number v-model="input.termnum"></el-input-number>
  37. </el-tooltip>
  38. </el-form-item>
  39. <el-form-item label="请选择开始日期">
  40. <el-date-picker
  41. :picker-options="pickerOptions"
  42. v-model="input.start"
  43. type="date"
  44. placeholder="请选择开始日期"
  45. format="yyyy-MM-dd"
  46. value-format="yyyy-MM-dd"
  47. >
  48. </el-date-picker>
  49. </el-form-item>
  50. </el-form>
  51. <template #footer>
  52. <el-row :gutter="20" type="flex" justify="center" align="middle">
  53. <el-col :span="4">
  54. <el-button @click="dialog = false">取消</el-button>
  55. </el-col>
  56. <el-col :span="4">
  57. <el-button type="primary" @click="setDefaultPlan">确定</el-button>
  58. </el-col>
  59. </el-row>
  60. </template>
  61. </el-dialog>
  62. </div>
  63. </template>
  64. <script>
  65. import _ from 'lodash';
  66. var moment = require('moment');
  67. import detailFrame from '@frame/layout/admin/detail-frame';
  68. import calendar from '@frame/components/calendar';
  69. import dataTable from '@frame/components/data-table';
  70. import event from '../parts/event';
  71. import { mapState, createNamespacedHelpers } from 'vuex';
  72. const { mapActions } = createNamespacedHelpers('trainplan');
  73. const { mapActions: trainTemplate } = createNamespacedHelpers('trainTemplate');
  74. const { mapActions: schPlan } = createNamespacedHelpers('schPlan');
  75. const { mapActions: util } = createNamespacedHelpers('util');
  76. export default {
  77. name: 'arrange',
  78. props: {},
  79. components: {
  80. // detailFrame,
  81. calendar,
  82. // dataTable,
  83. event,
  84. },
  85. data: function() {
  86. var that = this;
  87. return {
  88. view: 'plan',
  89. template: {},
  90. info: {
  91. year: '2020',
  92. },
  93. form: {},
  94. selectList: [],
  95. events: [],
  96. vacation: [],
  97. fields: [
  98. { label: '开始时间', prop: 'start' },
  99. { label: '结束时间', prop: 'end' },
  100. { label: '期数', prop: 'term' },
  101. { label: '班级类型', prop: 'type', format: item => (item === '0' ? '正常班级' : '特殊班级') },
  102. ],
  103. opera: [
  104. {
  105. label: '编辑',
  106. icon: 'el-icon-edit',
  107. method: 'edit',
  108. },
  109. {
  110. label: '删除',
  111. icon: 'el-icon-delete',
  112. method: 'delete',
  113. confirm: true,
  114. },
  115. ],
  116. rules: {
  117. title: [{ required: true, message: '请输入标题' }],
  118. },
  119. formRules: {
  120. start: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
  121. end: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
  122. term: [{ required: true, message: '请输入期数' }],
  123. number: [{ required: true, message: '请输入每班人数' }],
  124. type: [{ required: true, message: '请选择班级类型' }],
  125. },
  126. dialog: false,
  127. selfBtn: {
  128. term: {
  129. text: '生成模板计划',
  130. click: () => (that.dialog = true),
  131. position: 'left',
  132. },
  133. plan: {
  134. text: '保存培训计划',
  135. click: () => that.savePlan(),
  136. position: 'right',
  137. },
  138. },
  139. input: {
  140. term: 0,
  141. },
  142. pickerOptions: {
  143. disabledDate: time => that.checkDate(time),
  144. },
  145. heights: 250,
  146. collapse: '',
  147. drawer: false,
  148. formIsNew: true,
  149. };
  150. },
  151. created() {
  152. this.search();
  153. },
  154. methods: {
  155. ...mapActions(['fetch', 'update']),
  156. ...trainTemplate({ trainTemplate: 'query' }),
  157. ...schPlan({ setSchPlan: 'schArrange' }),
  158. ...util({ modelFetch: 'fetch' }),
  159. async search() {
  160. let planid = _.get(this.defaultOption, 'planid');
  161. if (!planid) return;
  162. const res = await this.fetch(planid);
  163. if (this.$checkRes(res)) {
  164. let fest = _.get(res.data, 'festivals', []);
  165. let vac = fest.map(i => {
  166. let object = {};
  167. object.id = i._id;
  168. object.start = i.begindate;
  169. object.end = i.finishdate;
  170. object.title = i.name;
  171. object.rendering = 'background';
  172. object.color = 'red';
  173. object.editable = false;
  174. return object;
  175. });
  176. this.$set(this, `vacation`, vac);
  177. this.$set(this, `info`, res.data);
  178. let midArr = JSON.parse(JSON.stringify(res.data));
  179. let events = [];
  180. events = _.flatten(
  181. midArr.termnum.map(item => {
  182. item.batchnum.map((i, index) => {
  183. i.termid = item._id;
  184. i.term = item.term;
  185. i.id = i._id;
  186. i.start = JSON.parse(JSON.stringify(i.startdate));
  187. i.end = JSON.parse(JSON.stringify(i.enddate));
  188. i.title = JSON.parse(JSON.stringify(i.name));
  189. delete i.startdate, delete i.enddate;
  190. return i;
  191. });
  192. return item.batchnum;
  193. })
  194. );
  195. events = events.map((i, index) => {
  196. i.index = index;
  197. return i;
  198. });
  199. this.$set(this, `events`, events);
  200. this.$set(this, `selectList`, events);
  201. }
  202. this.searchTemplate();
  203. },
  204. //模板事件开始
  205. //生成默认模板
  206. setDefaultPlan() {
  207. this.$set(this, `events`, []);
  208. this.$set(this, `selectList`, []);
  209. this.dialog = false;
  210. let { term, start, termnum } = this.input;
  211. let { day, batchnum, classnum } = this.template;
  212. let event = []; //处理成功事件的存储
  213. //第一次正常安排
  214. //qb:剩余没有满足之前的批数 qt:剩余没有满足之前的期数
  215. let { list, qb, start: rstart } = this.arrange(term, start);
  216. event = list;
  217. start = rstart;
  218. event = this.otherArrange(qb, start, event);
  219. //最后赋值回去
  220. event.map((i, index) => {
  221. i.index = index;
  222. return i;
  223. });
  224. //整理期数为输入的期数,将班级
  225. event = this.nameTerm(event, termnum);
  226. this.$set(this, `events`, event);
  227. this.$set(this, `selectList`, event);
  228. },
  229. //默认安排事件
  230. arrange(t, start, part) {
  231. let qt = 0;
  232. let qb = 0;
  233. let { day, batchnum, classnum, stunum } = this.template;
  234. if (!part) part = batchnum;
  235. let list = [];
  236. for (let it = 1; it <= t; it++) {
  237. for (let ib = 1; ib <= part; ib++) {
  238. let end = this.$plusDay(start, day - 1);
  239. let res = this.$checkDate(start, end, this.vacation);
  240. if (res == true) {
  241. let batch = {
  242. term: it - qt,
  243. batch: ib,
  244. class: classnum,
  245. start,
  246. end,
  247. type: '0',
  248. number: stunum,
  249. title: `第${it - qt}期第${ib}批次`,
  250. color: this.getColor(it, ib),
  251. };
  252. start = this.$plusDay(start);
  253. list.push(batch);
  254. } else {
  255. if (ib == 1) qt += 1;
  256. qb += batchnum - ib + 1;
  257. start = this.$plusDay(res.end);
  258. break;
  259. }
  260. }
  261. }
  262. return { list, qb, start };
  263. },
  264. //默认处理未安排的事件
  265. otherArrange(qb, start, event) {
  266. let { day, batchnum, classnum } = this.template;
  267. //将剩余的批数转换成局部变量,然后赋0重置qb,为了在arrange中重新计算是否有接触假期
  268. let tta = Math.ceil(qb / batchnum);
  269. let part = qb % batchnum;
  270. qb = 0;
  271. let { list, qb: rqb, start: rstart } = this.arrange(tta, start, part);
  272. start = rstart;
  273. let last = _.last(event);
  274. let arr = [];
  275. if (last) arr = this.sortOtherData(last, list);
  276. else arr = list;
  277. event = event.concat(arr);
  278. //判断是否还有剩余的批次,期;有的话还需要处理
  279. if (rqb > 0) {
  280. return this.otherArrange(rqb, start, event);
  281. } else {
  282. return event;
  283. }
  284. },
  285. //默认事件排序
  286. sortOtherData(last, data) {
  287. let arr = _.chunk(data, 3).map((i, index) => {
  288. i.map(ii => {
  289. ii.term = last.term + index + 1;
  290. ii.title = `第${ii.term}期第${ii.batch}批次`;
  291. return ii;
  292. });
  293. return i;
  294. });
  295. return _.flatten(arr);
  296. },
  297. //模板事件结束
  298. //将期数改为手动输入的
  299. nameTerm(events, start) {
  300. let duplicate = _.cloneDeep(events);
  301. duplicate = duplicate.map(i => {
  302. i.term = start * 1 + i.term - 1;
  303. i.title = `第${i.term}期第${i.batch}批次`;
  304. return i;
  305. });
  306. return duplicate;
  307. },
  308. //手动操作事件开始
  309. setEvent({ data, isNew }) {
  310. data = JSON.parse(JSON.stringify(data));
  311. if (data.type == 0) {
  312. data.title = `第${data.term}期第${data.batch}批次`;
  313. } else {
  314. let { name } = data;
  315. data = { ...data, title: name };
  316. }
  317. this.$set(this.events, data.index, data);
  318. this.$set(this.selectList, data.index, data);
  319. this.toClose();
  320. },
  321. //列表编辑事件
  322. toEdit({ data, index }) {
  323. this.$set(this, `form`, JSON.parse(JSON.stringify(data)));
  324. this.formIsNew = false;
  325. this.drawer = true;
  326. },
  327. //列表删除事件
  328. toDelete({ data, index }) {
  329. this.$set(
  330. this,
  331. `events`,
  332. this.events.filter(f => f.index !== data.index)
  333. );
  334. this.selectList.splice(index, 1);
  335. this.toClose();
  336. },
  337. //计划保存
  338. savePlan() {
  339. let data = JSON.parse(JSON.stringify(this.info));
  340. let plan = JSON.parse(JSON.stringify(this.selectList));
  341. let termnum = [];
  342. termnum = _.uniqBy(
  343. plan.map(item => {
  344. let obj = { term: item.term };
  345. if (item.termid) obj._id = item.termid;
  346. return obj;
  347. }),
  348. 'term'
  349. ).map(i => {
  350. let object = _.cloneDeep(i);
  351. object.batchnum = plan
  352. .filter(fil => fil.term === i.term)
  353. .map(b => {
  354. b = _.pickBy(b, (val, key) => key !== 'term');
  355. b.startdate = JSON.parse(JSON.stringify(b.start));
  356. b.enddate = JSON.parse(JSON.stringify(b.end));
  357. b.name ? b.name : (b.name = JSON.parse(JSON.stringify(b.title)));
  358. delete b.start, delete b.end;
  359. if (_.startsWith(b.id, 'eve') || _.startsWith(b.id, 'vac')) delete b.id;
  360. return b;
  361. });
  362. object.classnum = object.batchnum.reduce((pre, cur) => {
  363. if (cur.type === '0') return pre + parseInt(cur.class);
  364. else return pre + 1;
  365. }, 0);
  366. return object;
  367. });
  368. data.termnum = termnum;
  369. let res;
  370. let msg;
  371. res = this.update(data);
  372. msg = `计划保存成功`;
  373. this.$checkRes(res, msg);
  374. },
  375. //选择时间的事件
  376. selectDate(object) {
  377. let start = JSON.parse(JSON.stringify(object.startStr));
  378. let end = JSON.parse(JSON.stringify(object.endStr));
  379. this.$set(this.form, `start`, start);
  380. this.$set(this.form, `end`, end);
  381. this.$set(this.form, `index`, this.events.length);
  382. this.drawer = true;
  383. this.formIsNew = true;
  384. },
  385. //
  386. eventClick({ event }) {
  387. let obj = _.get(event, `extendedProps`);
  388. if (!obj) {
  389. console.warn(`无对应事件`);
  390. return;
  391. }
  392. let e = this.events.find(f => f.index == obj.index);
  393. if (e) this.$set(this, `form`, e);
  394. this.drawer = true;
  395. this.formIsNew = false;
  396. },
  397. //
  398. async searchTemplate() {
  399. let planid = _.get(this.defaultOption, 'planid');
  400. let planyearid = _.get(this.defaultOption, 'planyearid');
  401. let res = await this.modelFetch({ model: 'trainmodel', planyearid, planid });
  402. if (this.$checkRes(res)) {
  403. if (res.data !== null) {
  404. this.$set(this, `template`, res.data);
  405. }
  406. }
  407. },
  408. //其他事件(无关紧要)
  409. //关闭抽屉函数
  410. toClose() {
  411. this.drawer = false;
  412. this.formIsNew = true;
  413. this.setHeight();
  414. },
  415. setHeight() {
  416. let heights = this.$refs.card.$el.clientHeight * 0.63;
  417. this.$set(this, `heights`, heights);
  418. },
  419. //设置事件颜色
  420. getColor(it, ib) {
  421. let { color, batchnum } = this.template;
  422. if (color.length > 0) {
  423. // let num = ((it - 1) * batchnum + ib) % color.length;
  424. let num = (it - 1) % color.length;
  425. return color[num];
  426. } else return '#004499';
  427. },
  428. checkDate(date) {
  429. let year = JSON.parse(JSON.stringify(this.info.year));
  430. let res = moment(date).isBetween(`${year}-01-01`, `${year}-12-31`, null, '[]');
  431. return !res;
  432. },
  433. // returns() {
  434. // if (this.view == 'plan') this.$router.push({ path: './index' });
  435. // else if (this.view == 'school') this.view = 'plan';
  436. // else this.view = 'school';
  437. // },
  438. // changeView() {
  439. // this.savePlan();
  440. // this.view = 'school';
  441. // },
  442. async toSave(schPlan) {
  443. const res = await this.setSchPlan(schPlan);
  444. if (this.$checkRes(res)) {
  445. this.savePlan();
  446. }
  447. },
  448. toDirector() {
  449. this.view = 'director';
  450. },
  451. },
  452. watch: {
  453. defaultOption: {
  454. handler(val) {
  455. this.search();
  456. },
  457. deep: true,
  458. },
  459. },
  460. computed: {
  461. ...mapState(['user', 'defaultOption']),
  462. id() {
  463. return this.$route.query.id;
  464. },
  465. isNew() {
  466. return this.$route.query.id ? false : true; //false : true;
  467. },
  468. pageTitle() {
  469. return `${this.$route.meta.title}`;
  470. },
  471. widths() {
  472. let width = (document.body.clientWidth - 200) * 0.65;
  473. return width > 400 ? width : 400;
  474. },
  475. },
  476. metaInfo() {
  477. return { title: this.$route.meta.title };
  478. },
  479. };
  480. </script>
  481. <style lang="less" scoped>
  482. /deep/.el-card__body {
  483. overflow: auto;
  484. padding: 10px;
  485. }
  486. </style>