index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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;
  111. if (end == start) {
  112. end = Math.round(new Date($A.formatDate('Y-m-d 23:59:59', end)).getTime()/1000);
  113. }
  114. start*= 1000;
  115. end*= 1000;
  116. if (end == start) end++;
  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. this.config = Object.assign({
  155. plugins: [ItemMovement({
  156. moveable: 'x',
  157. resizeable: true,
  158. collisionDetection: true
  159. }), CalendarScroll(), WeekendHighlight()],
  160. height: this.$el.clientHeight - 24,
  161. list: {
  162. toggle: {
  163. display: false
  164. },
  165. rows: this.rows,
  166. columns: {
  167. percent: 100,
  168. resizer: {
  169. inRealTime: true,
  170. dots: 0
  171. },
  172. data: {
  173. label: {
  174. id: "label",
  175. data: "label",
  176. width: 300,
  177. isHTML: true,
  178. header: {
  179. content: this.$L("任务名称")
  180. }
  181. }
  182. }
  183. }
  184. },
  185. chart: {
  186. time: {
  187. period: 'day',
  188. additionalSpaces: {
  189. hour: { before: 24, after: 24, period: 'hour' },
  190. day: { before: 1, after: 1, period: 'month' },
  191. week: { before: 1, after: 1, period: 'year' },
  192. month: { before: 6, after: 6, period: 'year' },
  193. year: { before: 12, after: 12, period: 'year' }
  194. }
  195. },
  196. items: this.items
  197. },
  198. }, isUpdate ? {} : {
  199. actions: {
  200. "list-column-row": [this.actionRow],
  201. 'chart-timeline-items-row-item': [this.actionResizing]
  202. }
  203. }, this.getLanguage() == 'en' ? {} : {
  204. locale: {
  205. name: "zh-cn",
  206. weekdays: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
  207. weekdaysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
  208. weekdaysMin: ["日", "一", "二", "三", "四", "五", "六"],
  209. months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
  210. monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
  211. ordinal: function(t, e) {
  212. switch (e) {
  213. case "W":
  214. return "".concat(t, "周");
  215. default:
  216. return "".concat(t, "日")
  217. }
  218. },
  219. weekStart: 1,
  220. yearStart: 4,
  221. formats: {
  222. LT: "HH:mm",
  223. LTS: "HH:mm:ss",
  224. L: "YYYY/MM/DD",
  225. LL: "YYYY年M月D日",
  226. LLL: "YYYY年M月D日Ah点mm分",
  227. LLLL: "YYYY年M月D日ddddAh点mm分",
  228. l: "YYYY/M/D",
  229. ll: "YYYY年M月D日",
  230. lll: "YYYY年M月D日 HH:mm",
  231. llll: "YYYY年M月D日dddd HH:mm"
  232. },
  233. relativeTime: {
  234. future: "%s内",
  235. past: "%s前",
  236. s: "几秒",
  237. m: "1 分钟",
  238. mm: "%d 分钟",
  239. h: "1 小时",
  240. hh: "%d 小时",
  241. d: "1 天",
  242. dd: "%d 天",
  243. M: "1 个月",
  244. MM: "%d 个月",
  245. y: "1 年",
  246. yy: "%d 年"
  247. },
  248. },
  249. });
  250. },
  251. actionRow(element, data) {
  252. let onClick = (event) => {
  253. //打开任务
  254. if (event.target.className == 'gantt-schedule-timeline-calendar-goto') {
  255. let rowId = event.target.getAttribute("data-id");
  256. rowId && this.$refs.gstc.getGstc().api.scrollToTime(this.items[rowId].time.start)
  257. } else {
  258. this.taskDetail(data.rowId);
  259. }
  260. }
  261. element.addEventListener('click', onClick);
  262. return {
  263. update(element, newData) {
  264. data = newData;
  265. },
  266. destroy(element, data) {
  267. element.removeEventListener('click', onClick);
  268. }
  269. };
  270. },
  271. actionResizing(element, data) {
  272. let thas = this;
  273. return {
  274. update(element, newData) {
  275. data = newData;
  276. if (data.item.isResizing) {
  277. if (Math.abs(thas.items[data.item['id']].time.end - data.item.time.end) > 60000
  278. || Math.abs(thas.items[data.item['id']].time.start - data.item.time.start) > 60000) {
  279. //修改时间(变化超过1分钟)
  280. let backTime = $A.cloneData(thas.items[data.item['id']].time);
  281. let newTime = $A.cloneData(data.item.time);
  282. let timeStart = $A.formatDate('Y-m-d H:i', Math.round(newTime.start / 1000));
  283. let timeEnd = $A.formatDate('Y-m-d H:i', Math.round(newTime.end / 1000));
  284. thas.items[data.item['id']].time = newTime;
  285. thas.$refs.gstc.updateTime(data.item['id'], newTime);
  286. thas.$Modal.confirm({
  287. title: thas.$L("计划时间"),
  288. content: thas.$L('确定要修改任务【%】的计划时间吗?<br/>开始时间:%<br/>结束时间:%', data.item['label'], timeStart, timeEnd),
  289. onCancel: () => {
  290. thas.items[data.item['id']].time = backTime;
  291. thas.$refs.gstc.updateTime(data.item['id'], backTime);
  292. },
  293. onOk: () => {
  294. let ajaxData = {
  295. act: 'plannedtime',
  296. taskid: data.item['id'],
  297. content: timeStart + "," + timeEnd,
  298. };
  299. $A.apiAjax({
  300. url: 'project/task/edit',
  301. data: ajaxData,
  302. error: () => {
  303. alert(thas.$L('网络繁忙,请稍后再试!'));
  304. thas.items[data.item['id']].time = backTime;
  305. thas.$refs.gstc.updateTime(data.item['id'], backTime);
  306. },
  307. success: (res) => {
  308. if (res.ret === 1) {
  309. thas.$Message.success(res.msg);
  310. $A.triggerTaskInfoListener(ajaxData.act, res.data);
  311. $A.triggerTaskInfoChange(ajaxData.taskid);
  312. } else {
  313. setTimeout(() => {
  314. thas.$Modal.error({title: thas.$L('温馨提示'), content: res.msg});
  315. }, 350)
  316. thas.items[data.item['id']].time = backTime;
  317. thas.$refs.gstc.updateTime(data.item['id'], backTime);
  318. }
  319. }
  320. });
  321. }
  322. });
  323. }
  324. }
  325. }
  326. };
  327. },
  328. emitState(GSTCState) {
  329. GSTCState.subscribe('config.plugin.ItemMovement.item', data => {
  330. if (!data) return;
  331. GSTCState.update(`config.chart.items.${data.id}.isResizing`, !(data.waiting || data.moving || data.resizing));
  332. });
  333. },
  334. tapView(e) {
  335. if ("now" === e) {
  336. var i = this.$refs.gstc.getGstc()
  337. return i.api.scrollToTime(i.api.time.date().valueOf())
  338. }
  339. this.period = e;
  340. this.$refs.gstc.setPeriod(e);
  341. }
  342. }
  343. }
  344. </script>