index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. <template>
  2. <div class="project-gstc-gantt">
  3. <GSTC v-if="loadFinish" ref="gstc" :config="config" @state="emitState"/>
  4. <Dropdown class="project-gstc-dropdown" @on-click="tapView">
  5. <Icon class="project-gstc-dropdown-icon" type="md-funnel" />
  6. <DropdownMenu slot="list">
  7. <DropdownItem name="now">{{$L('现在')}}</DropdownItem>
  8. <DropdownItem name="day" :class="{'project-gstc-dropdown-period':period=='day'}">{{$L('天视图')}}</DropdownItem>
  9. <DropdownItem name="week" :class="{'project-gstc-dropdown-period':period=='week'}">{{$L('周视图')}}</DropdownItem>
  10. <DropdownItem name="month" :class="{'project-gstc-dropdown-period':period=='month'}">{{$L('月视图')}}</DropdownItem>
  11. </DropdownMenu>
  12. </Dropdown>
  13. <div class="project-gstc-close" @click="$emit('on-close')"><Icon type="md-close" /></div>
  14. </div>
  15. </template>
  16. <style lang="scss">
  17. .project-gstc-gantt {
  18. position: absolute;
  19. top: 15px;
  20. left: 15px;
  21. right: 15px;
  22. bottom: 15px;
  23. z-index: 1;
  24. transform: translateZ(0);
  25. background-color: #fdfdfd;
  26. border-radius: 3px;
  27. .gantt-schedule-timeline-calendar {
  28. background-color: transparent;
  29. padding: 12px;
  30. }
  31. .gantt-schedule-timeline-calendar__list-column-header-resizer-container {
  32. line-height: 92px;
  33. }
  34. .project-gstc-dropdown {
  35. position: absolute;
  36. top: 44px;
  37. left: 276px;
  38. .project-gstc-dropdown-icon {
  39. cursor: pointer;
  40. color: #999;
  41. font-size: 20px;
  42. }
  43. .project-gstc-dropdown-period {
  44. color: #058ce4;
  45. }
  46. }
  47. .project-gstc-close {
  48. position: absolute;
  49. top: 8px;
  50. left: 12px;
  51. cursor: pointer;
  52. &:hover {
  53. i {
  54. transform: scale(1) rotate(45deg);
  55. }
  56. }
  57. i {
  58. color: #666666;
  59. font-size: 28px;
  60. transform: scale(0.92);
  61. transition: all .2s;
  62. }
  63. }
  64. }
  65. </style>
  66. <script>
  67. import GSTC from "./GSTC";
  68. import ItemMovement from "gantt-schedule-timeline-calendar/dist/ItemMovement.plugin.js"
  69. import CalendarScroll from "gantt-schedule-timeline-calendar/dist/CalendarScroll.plugin.js"
  70. import WeekendHighlight from "gantt-schedule-timeline-calendar/dist/WeekendHighlight.plugin.js"
  71. /**
  72. * 甘特图
  73. */
  74. export default {
  75. name: 'ProjectGantt',
  76. components: { GSTC },
  77. props: {
  78. projectLabel: {
  79. default: []
  80. },
  81. },
  82. data () {
  83. return {
  84. loadFinish: false,
  85. period: 'day',
  86. rows: {},
  87. items: {},
  88. config: {},
  89. }
  90. },
  91. mounted() {
  92. this.initData();
  93. this.loadFinish = true;
  94. //
  95. this.$watch(
  96. "projectLabel",
  97. (projectLabel) => {
  98. this.initData(this.loadFinish == true);
  99. },
  100. {deep: true}
  101. );
  102. },
  103. methods: {
  104. initData(isUpdate) {
  105. this.rows = {};
  106. this.items = {};
  107. this.projectLabel.forEach((item) => {
  108. item.taskLists.forEach((taskData) => {
  109. let start = taskData.startdate || taskData.indate;
  110. let end = taskData.enddate || (taskData.indate + 86400);
  111. if (end == start) {
  112. end = Math.round(new Date($A.formatDate('Y-m-d 23:59:59', end)).getTime()/1000);
  113. }
  114. end = Math.max(end, start + 60);
  115. start*= 1000;
  116. end*= 1000;
  117. //
  118. let color = '#058ce4';
  119. if (taskData.complete) {
  120. color = '#c1c1c1';
  121. } else {
  122. if (taskData.level === 1) {
  123. color = '#ff0000';
  124. }else if (taskData.level === 2) {
  125. color = '#BB9F35';
  126. }else if (taskData.level === 3) {
  127. color = '#449EDD';
  128. }else if (taskData.level === 4) {
  129. color = '#84A83B';
  130. }
  131. }
  132. //
  133. let label = `<div class="gantt-schedule-timeline-calendar-title">${taskData['title']}</div>`;
  134. if (taskData.overdue) {
  135. label = `<div class="gantt-schedule-timeline-calendar-overdue"><em>${this.$L('已超期')}</em></div>${label}`;
  136. }
  137. label = `${label}<div class="gantt-schedule-timeline-calendar-goto" data-id="${taskData['id']}"></div>`;
  138. this.rows[taskData['id']] = {
  139. id: taskData['id'],
  140. label: label,
  141. complete: taskData.complete,
  142. overdue: taskData.overdue,
  143. };
  144. this.items[taskData['id']] = {
  145. id: taskData['id'],
  146. rowId: taskData['id'],
  147. label: taskData['title'],
  148. time: { start, end },
  149. style: { background: color },
  150. };
  151. });
  152. });
  153. //
  154. if (Object.keys(this.rows).length == 0) {
  155. this.$Modal.warning({
  156. title: this.$L("温馨提示"),
  157. content: this.$L('任务列表为空,请先添加任务。'),
  158. onOk: () => {
  159. this.$emit('on-close');
  160. },
  161. });
  162. }
  163. //
  164. this.config = Object.assign({
  165. plugins: [ItemMovement({
  166. moveable: 'x',
  167. resizeable: true,
  168. collisionDetection: true
  169. }), CalendarScroll(), WeekendHighlight()],
  170. height: this.$el.clientHeight - 24,
  171. list: {
  172. toggle: {
  173. display: false
  174. },
  175. rows: this.rows,
  176. columns: {
  177. percent: 100,
  178. resizer: {
  179. inRealTime: true,
  180. dots: 0
  181. },
  182. data: {
  183. label: {
  184. id: "label",
  185. data: "label",
  186. width: 300,
  187. isHTML: true,
  188. header: {
  189. content: this.$L("任务名称")
  190. }
  191. }
  192. }
  193. }
  194. },
  195. chart: {
  196. time: {
  197. period: 'day',
  198. additionalSpaces: {
  199. hour: { before: 24, after: 24, period: 'hour' },
  200. day: { before: 1, after: 1, period: 'month' },
  201. week: { before: 1, after: 1, period: 'year' },
  202. month: { before: 6, after: 6, period: 'year' },
  203. year: { before: 12, after: 12, period: 'year' }
  204. }
  205. },
  206. items: this.items
  207. },
  208. }, isUpdate ? {} : {
  209. actions: {
  210. "list-column-row": [this.actionRow],
  211. 'chart-timeline-items-row-item': [this.actionResizing]
  212. }
  213. }, this.getLanguage() == 'en' ? {} : {
  214. locale: {
  215. name: "zh-cn",
  216. weekdays: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
  217. weekdaysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
  218. weekdaysMin: ["日", "一", "二", "三", "四", "五", "六"],
  219. months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
  220. monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
  221. ordinal: function(t, e) {
  222. switch (e) {
  223. case "W":
  224. return "".concat(t, "周");
  225. default:
  226. return "".concat(t, "日")
  227. }
  228. },
  229. weekStart: 1,
  230. yearStart: 4,
  231. formats: {
  232. LT: "HH:mm",
  233. LTS: "HH:mm:ss",
  234. L: "YYYY/MM/DD",
  235. LL: "YYYY年M月D日",
  236. LLL: "YYYY年M月D日Ah点mm分",
  237. LLLL: "YYYY年M月D日ddddAh点mm分",
  238. l: "YYYY/M/D",
  239. ll: "YYYY年M月D日",
  240. lll: "YYYY年M月D日 HH:mm",
  241. llll: "YYYY年M月D日dddd HH:mm"
  242. },
  243. relativeTime: {
  244. future: "%s内",
  245. past: "%s前",
  246. s: "几秒",
  247. m: "1 分钟",
  248. mm: "%d 分钟",
  249. h: "1 小时",
  250. hh: "%d 小时",
  251. d: "1 天",
  252. dd: "%d 天",
  253. M: "1 个月",
  254. MM: "%d 个月",
  255. y: "1 年",
  256. yy: "%d 年"
  257. },
  258. },
  259. });
  260. },
  261. actionRow(element, data) {
  262. let onClick = (event) => {
  263. //打开任务
  264. if (event.target.className == 'gantt-schedule-timeline-calendar-goto') {
  265. let rowId = event.target.getAttribute("data-id");
  266. rowId && this.$refs.gstc.getGstc().api.scrollToTime(this.items[rowId].time.start)
  267. } else {
  268. this.taskDetail(data.rowId);
  269. }
  270. }
  271. element.addEventListener('click', onClick);
  272. return {
  273. update(element, newData) {
  274. data = newData;
  275. },
  276. destroy(element, data) {
  277. element.removeEventListener('click', onClick);
  278. }
  279. };
  280. },
  281. actionResizing(element, data) {
  282. let thas = this;
  283. return {
  284. update(element, newData) {
  285. data = newData;
  286. if (data.item.isResizing) {
  287. if (Math.abs(thas.items[data.item['id']].time.end - data.item.time.end) > 60000
  288. || Math.abs(thas.items[data.item['id']].time.start - data.item.time.start) > 60000) {
  289. //修改时间(变化超过1分钟)
  290. let backTime = $A.cloneData(thas.items[data.item['id']].time);
  291. let newTime = $A.cloneData(data.item.time);
  292. let timeStart = $A.formatDate('Y-m-d H:i', Math.round(newTime.start / 1000));
  293. let timeEnd = $A.formatDate('Y-m-d H:i', Math.round(newTime.end / 1000));
  294. thas.items[data.item['id']].time = newTime;
  295. thas.$refs.gstc.updateTime(data.item['id'], newTime);
  296. thas.$Modal.confirm({
  297. title: thas.$L("计划时间"),
  298. content: thas.$L('确定要修改任务【%】的计划时间吗?<br/>开始时间:%<br/>结束时间:%', data.item['label'], timeStart, timeEnd),
  299. onCancel: () => {
  300. thas.items[data.item['id']].time = backTime;
  301. thas.$refs.gstc.updateTime(data.item['id'], backTime);
  302. },
  303. onOk: () => {
  304. let ajaxData = {
  305. act: 'plannedtime',
  306. taskid: data.item['id'],
  307. content: timeStart + "," + timeEnd,
  308. };
  309. $A.apiAjax({
  310. url: 'project/task/edit',
  311. data: ajaxData,
  312. error: () => {
  313. alert(thas.$L('网络繁忙,请稍后再试!'));
  314. thas.items[data.item['id']].time = backTime;
  315. thas.$refs.gstc.updateTime(data.item['id'], backTime);
  316. },
  317. success: (res) => {
  318. if (res.ret === 1) {
  319. thas.$Message.success(res.msg);
  320. $A.triggerTaskInfoListener(ajaxData.act, res.data);
  321. $A.triggerTaskInfoChange(ajaxData.taskid);
  322. } else {
  323. setTimeout(() => {
  324. thas.$Modal.error({title: thas.$L('温馨提示'), content: res.msg});
  325. }, 350)
  326. thas.items[data.item['id']].time = backTime;
  327. thas.$refs.gstc.updateTime(data.item['id'], backTime);
  328. }
  329. }
  330. });
  331. }
  332. });
  333. }
  334. }
  335. }
  336. };
  337. },
  338. emitState(GSTCState) {
  339. GSTCState.subscribe('config.plugin.ItemMovement.item', data => {
  340. if (!data) return;
  341. GSTCState.update(`config.chart.items.${data.id}.isResizing`, !(data.waiting || data.moving || data.resizing));
  342. });
  343. },
  344. tapView(e) {
  345. if ("now" === e) {
  346. var i = this.$refs.gstc.getGstc()
  347. return i.api.scrollToTime(i.api.time.date().valueOf())
  348. }
  349. this.period = e;
  350. this.$refs.gstc.setPeriod(e);
  351. }
  352. }
  353. }
  354. </script>