detail.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. <template>
  2. <div id="detail">
  3. <detail-frame :title="mainTitle" returns="/plan/index">
  4. <el-row :gutter="10" type="flex">
  5. <el-col :span="12">
  6. <el-card header="全年计划信息">
  7. <el-form ref="planForm" :model="info" :rules="rules" :isNew="isNew" label-width="60px" size="small" @submit.native.prevent>
  8. <el-form-item label="年份" required>
  9. {{ info.year }}
  10. </el-form-item>
  11. <el-form-item label="标题" prop="title" required>
  12. <el-input v-model="info.title"></el-input>
  13. </el-form-item>
  14. <el-collapse v-model="collapse" accordion>
  15. <el-collapse-item title="计划简表" name="1">
  16. <data-table :fields="fields" :data="selectList" :opera="opera" @edit="toEdit" @delete="toDelete" :height="heights"></data-table>
  17. </el-collapse-item>
  18. </el-collapse>
  19. <el-form-item>
  20. <el-row type="flex" align="middle" justify="space-around" style="margin-top:20px">
  21. <el-col :span="6">
  22. <el-button type="primary" @click="toSavePlan">保存全年计划</el-button>
  23. </el-col>
  24. </el-row>
  25. </el-form-item>
  26. </el-form>
  27. </el-card>
  28. </el-col>
  29. <el-col :span="16" :style="`width:${widths}px`">
  30. <el-card ref="card">
  31. <calendar :selfBtn="selfBtn" @draft="selectDate" @eventClick="eventClick" :events="events"></calendar>
  32. </el-card>
  33. </el-col>
  34. </el-row>
  35. </detail-frame>
  36. <el-drawer :visible.sync="drawer" direction="rtl" title="安排计划" @close="toClose">
  37. <event :vacation="vacation" :year="info.year" :data="form" :isNew="formIsNew" :predefineColors="predefineColors" @save="saveForm"></event>
  38. </el-drawer>
  39. <el-dialog title="设置假期" :visible.sync="dialog">
  40. <vacation-form :list="vacation" @update="setVacation"></vacation-form>
  41. </el-dialog>
  42. </div>
  43. </template>
  44. <script>
  45. import detailFrame from '@frame/layout/admin/detail-frame';
  46. import calendar from '@frame/components/calendar';
  47. import dataTable from '@frame/components/data-table';
  48. import vacationForm from './parts/vacation';
  49. import event from './parts/event';
  50. import _ from 'lodash';
  51. import { createNamespacedHelpers } from 'vuex';
  52. const { mapActions } = createNamespacedHelpers('trainplan');
  53. export default {
  54. metaInfo: { title: '计划详情' },
  55. name: 'detail',
  56. props: {},
  57. components: { detailFrame, calendar, dataTable, vacationForm, event },
  58. data() {
  59. return {
  60. info: {
  61. year: '',
  62. termnum: {
  63. batchnum: [],
  64. },
  65. },
  66. form: { color: '#409EFF' },
  67. formIsNew: true,
  68. rules: {
  69. title: [{ required: true, message: '请输入标题' }],
  70. },
  71. formRules: {
  72. start: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
  73. end: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
  74. term: [{ required: true, message: '请输入期数' }],
  75. number: [{ required: true, message: '请输入每班人数' }],
  76. type: [{ required: true, message: '请选择班级类型' }],
  77. },
  78. drawer: false,
  79. dialog: false,
  80. events: [],
  81. predefineColors: ['#409EFF'],
  82. collapse: '',
  83. fields: [
  84. { label: '开始时间', prop: 'start' },
  85. { label: '结束时间', prop: 'end' },
  86. { label: '期数', prop: 'term' },
  87. { label: '班级类型', prop: 'type', format: item => (item === '0' ? '正常班级' : '特殊班级') },
  88. ],
  89. opera: [
  90. {
  91. label: '编辑',
  92. icon: 'el-icon-edit',
  93. method: 'edit',
  94. },
  95. {
  96. label: '删除',
  97. icon: 'el-icon-delete',
  98. method: 'delete',
  99. confirm: true,
  100. },
  101. ],
  102. heights: 250,
  103. selfBtn: {
  104. vacation: {
  105. text: '设置假期',
  106. //设置假期
  107. click: () => (this.dialog = true),
  108. position: 'left',
  109. },
  110. },
  111. selectList: [],
  112. vacation: [],
  113. };
  114. },
  115. created() {
  116. if (this.isNew) this.$set(this.info, `year`, new Date().getFullYear());
  117. },
  118. mounted() {},
  119. methods: {
  120. ...mapActions(['fetch', 'create', 'update']),
  121. //查询计划
  122. async search() {
  123. const res = await this.fetch(this.id);
  124. if (this.$checkRes(res)) this.$set(this, `info`, res.data);
  125. let midArr = JSON.parse(JSON.stringify(res.data));
  126. let events = [];
  127. events = _.flatten(
  128. midArr.termnum.map(item => {
  129. item.batchnum.map(i => {
  130. i.term = item.term;
  131. i.id = i._id;
  132. i.start = JSON.parse(JSON.stringify(i.startdate));
  133. i.end = JSON.parse(JSON.stringify(i.enddate));
  134. i.title = JSON.parse(JSON.stringify(i.name));
  135. delete i.startdate, delete i.enddate, delete i.name;
  136. return i;
  137. });
  138. return item.batchnum;
  139. })
  140. );
  141. //这个events不完整,真正的events还需要将假期的数据整合至一起
  142. //计划+假期=所有事件 当前events=selectList 计划事件
  143. //TODO,服务端修改后,整理假期事件并整合
  144. console.log(events);
  145. this.$set(this, `selectList`, JSON.parse(JSON.stringify(events)));
  146. this.$set(this, `events`, events);
  147. },
  148. //拖拽选择事件
  149. selectDate(object) {
  150. let start = JSON.parse(JSON.stringify(object.startStr));
  151. let end = JSON.parse(JSON.stringify(object.endStr));
  152. let res = this.inVacation(start, end);
  153. if (!res) {
  154. this.$message.error(`不能在假期中安排计划`);
  155. return;
  156. }
  157. this.$set(this.form, `start`, start);
  158. this.$set(this.form, `end`, end);
  159. this.drawer = true;
  160. this.formIsNew = true;
  161. },
  162. //日历事件点击事件 缺少删除
  163. eventClick({ event }) {
  164. let arr = this.events.filter(fil => fil.id == event.id);
  165. if (arr.length > 0) {
  166. if (_.get(arr[0], 'editable', true)) {
  167. this.form = arr[0];
  168. this.drawer = true;
  169. this.formIsNew = false;
  170. } else this.$message.warning('请在设置假期中修改假期信息');
  171. } else {
  172. console.warn(`无对应id事件`);
  173. return;
  174. }
  175. },
  176. //列表编辑事件
  177. toEdit({ data, index }) {
  178. this.$set(this, `form`, JSON.parse(JSON.stringify(data)));
  179. this.drawer = true;
  180. this.formIsNew = false;
  181. },
  182. //列表删除事件
  183. toDelete({ data, index }) {
  184. this.selectList.splice(index, 1);
  185. let newEvents = this.events.filter(fil => fil.id !== data.id);
  186. this.$set(this, `events`, newEvents);
  187. },
  188. //全年计划保存验证
  189. toSavePlan() {
  190. this.$refs['planForm'].validate(valid => {
  191. if (valid) {
  192. this.savePlan();
  193. } else {
  194. console.warn('form validate error!!!');
  195. }
  196. });
  197. },
  198. //保存计划事件
  199. savePlan() {
  200. //全年计划内容
  201. let data = JSON.parse(JSON.stringify(this.info));
  202. data.year = this.info.year;
  203. let termnum = [];
  204. //1,获取所有期数
  205. //2,按期数将selectList内数据分组
  206. termnum = _.uniq(this.selectList.map(item => item.term)).map(i => {
  207. let object = { term: i };
  208. object.batchnum = this.selectList
  209. .filter(fil => fil.term === i)
  210. .map(b => {
  211. b = _.pickBy(b, (val, key) => key !== 'term');
  212. b.startdate = JSON.parse(JSON.stringify(b.start));
  213. b.enddate = JSON.parse(JSON.stringify(b.end));
  214. b.name ? b.name : (b.name = JSON.parse(JSON.stringify(b.title)));
  215. delete b.start, delete b.end;
  216. if (_.startsWith(b.id, 'eve') || _.startsWith(b.id, 'vac')) delete b.id;
  217. return b;
  218. });
  219. object.classnum = object.batchnum.reduce((pre, cur) => {
  220. if (cur.type === '0') return pre + parseInt(cur.class);
  221. else return pre + 1;
  222. }, 0);
  223. return object;
  224. });
  225. data.termnum = termnum;
  226. //3 设置假期
  227. let vacation = JSON.parse(JSON.stringify(this.vacation));
  228. vacation = vacation.map(i => {
  229. let object = { begindate: i.start, finishdate: i.end, name: i.title };
  230. return object;
  231. });
  232. data.festivals = vacation;
  233. let res;
  234. let msg;
  235. if (this.isNew) {
  236. res = this.create(data);
  237. msg = `${this.keyWord}添加成功`;
  238. } else {
  239. res = this.update(data);
  240. msg = `${this.keyWord}修改成功`;
  241. }
  242. if (this.$checkRes(res, msg)) this.$router.push({ path: '/plan/index' });
  243. },
  244. //保存表单函数
  245. saveForm(events) {
  246. this.setEvent(events);
  247. },
  248. //添加/修改函数
  249. setEvent({ data: form, isNew }) {
  250. //TODO 需要根据班级类型把数据分开
  251. let data = JSON.parse(JSON.stringify(form));
  252. let { start, end, term, type, number, color, id } = data;
  253. let object = {};
  254. if (data.type === '0') {
  255. // 正常班级
  256. let { batch, class: classes } = data;
  257. object = { start, end, term, type, number, color, batch, class: classes };
  258. object.title = `第${JSON.parse(JSON.stringify(term))}期第${JSON.parse(JSON.stringify(batch))}批次`;
  259. } else {
  260. let { name } = data;
  261. object = { start, end, term, type, number, color, name, title: name };
  262. }
  263. if (isNew) {
  264. object.id = `eve${new Date().getTime()}`;
  265. this.events.push(object);
  266. this.selectList.push(object);
  267. } else {
  268. object.id = id;
  269. this.$set(
  270. this.events,
  271. _.findIndex(this.events, item => item.id == object.id),
  272. object
  273. );
  274. this.$set(
  275. this.selectList,
  276. _.findIndex(this.selectList, item => item.id == object.id),
  277. object
  278. );
  279. this.drawer = false;
  280. }
  281. if (_.findIndex(this.predefineColors, item => item == data.color) < 0) this.predefineColors.push(data.color);
  282. this.toClose();
  283. },
  284. //关闭抽屉函数
  285. toClose() {
  286. this.drawer = false;
  287. this.form = { color: '#409EFF' };
  288. this.formIsNew = true;
  289. this.setHeight();
  290. },
  291. setHeight() {
  292. let heights = this.$refs.card.$el.clientHeight * 0.63;
  293. this.$set(this, `heights`, heights);
  294. },
  295. //更新假期列表
  296. setVacation({ data, type }) {
  297. this.$set(this, `vacation`, data);
  298. let arr = [];
  299. if (type === 'add') {
  300. arr = data.map(i => {
  301. i.color = `red`;
  302. i.editable = false;
  303. i.id ? '' : (i.id = `vac${new Date().getTime()}`);
  304. return i;
  305. });
  306. this.$set(this, `events`, this.selectList.concat(arr));
  307. } else if (type === 'edit') {
  308. for (const i of data) {
  309. this.$set(
  310. this.events,
  311. _.findIndex(this.events, item => item.id == i.id),
  312. i
  313. );
  314. }
  315. } else if (type === 'delete') {
  316. this.$set(this, `events`, this.selectList.concat(data));
  317. }
  318. },
  319. //判断是否在假期中
  320. inVacation(start, end) {
  321. let startTime = new Date(start).getTime();
  322. let endTime = new Date(end).getTime();
  323. let res = true;
  324. for (const vac of this.vacation) {
  325. let vacS = new Date(vac.start).getTime();
  326. let vacE = new Date(vac.end).getTime();
  327. //case1 事件开始时间<假期开始时间 => 事件结束时间 > 假期开始时间 ? 事件碰到假期了.不行了 : 事件没碰到假期,没事
  328. //case2 事件开始时间>=假期开始时间 => 事件开始时间 < 假期结束时间 ? GG return false: return true;
  329. if (startTime >= vacS) {
  330. if (startTime < vacE) {
  331. res = false;
  332. console.warn(`case2`);
  333. break;
  334. }
  335. } else {
  336. if (endTime > vacS) {
  337. res = false;
  338. console.warn(`case1`);
  339. break;
  340. }
  341. }
  342. }
  343. //比较选择的时间在不在假期中,然后返回结果
  344. return res;
  345. },
  346. },
  347. watch: {
  348. isNew: {
  349. immediate: true,
  350. handler(val) {
  351. if (val) this.loading = false;
  352. else this.search();
  353. },
  354. },
  355. },
  356. computed: {
  357. widths() {
  358. let width = (document.body.clientWidth - 200) * 0.5;
  359. return width > 400 ? width : 400;
  360. },
  361. id() {
  362. return this.$route.query.id;
  363. },
  364. isNew() {
  365. return this.$route.query.id ? false : true; //false : true;
  366. },
  367. mainTitle() {
  368. let meta = this.$route.meta;
  369. let main = meta.title || '';
  370. let sub = meta.sub || '';
  371. return `${main}${sub}`;
  372. },
  373. keyWord() {
  374. let meta = this.$route.meta;
  375. let main = meta.title || '';
  376. return main;
  377. },
  378. },
  379. };
  380. </script>
  381. <style lang="less" scoped>
  382. /deep/.el-divider--horizontal {
  383. margin: 5px 0;
  384. }
  385. </style>