project_gantt.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. {{ src/App.vue }}
  2. <template>
  3. <div class="container">
  4. <div class="gantt_head">
  5. <div class="head_RorX">
  6. <el-radio-group v-model="radio1" @change="selChange()" size="small" style="margin-right:9px;width:280px">
  7. <el-radio-button label="按人员查看" value="renyuan"></el-radio-button>
  8. <el-radio-button label="按项目查看" value="xiangmu"></el-radio-button>
  9. <el-radio-button label="资源需求" value="demand"></el-radio-button>
  10. </el-radio-group>
  11. </div>
  12. <!-- 时间段筛选 -->
  13. <div class="head_date" v-if="isDataLoaded">
  14. <span>时间段</span>
  15. <el-date-picker
  16. style="margin-left:9px;width:17vw"
  17. size="small"
  18. v-model="valueDate"
  19. type="daterange"
  20. range-separator="至"
  21. start-placeholder="开始日期"
  22. end-placeholder="结束日期"
  23. value-format="yyyy-MM-dd"
  24. @change="dateupdata()">
  25. </el-date-picker>
  26. </div>
  27. <!-- 任务类型筛选 -->
  28. <div v-if="radio1 == '按项目查看'" class="head_taskType">
  29. <span>任务类型</span>
  30. <el-select clearable filterable v-model="taskType" placeholder="请选择" size="small" style="margin-left:9px;width:10vw" @change="taskTypeSel()">
  31. <el-option label="任务" :value="0"></el-option>
  32. <el-option label="里程碑" :value="1"></el-option>
  33. </el-select>
  34. </div>
  35. <!-- 任务分组筛选 -->
  36. <div v-if="reqpar1" class="head_taskgroup">
  37. <span>任务分组</span>
  38. <el-select clearable filterable v-model="valuex2" placeholder="请选择" size="small" style="margin-left:9px;width:10vw" @change="taskgroupSel()">
  39. <el-option
  40. v-for="item in taskgroupList"
  41. :key="item.name"
  42. :label="item.name"
  43. :value="item.name"
  44. >
  45. </el-option>
  46. </el-select>
  47. </div>
  48. <!-- 人员/项目筛选 -->
  49. <div class="head_select">
  50. <span>{{(this.radio1 == "按人员查看" ? "人员" : "项目")}}</span>
  51. <el-select clearable filterable v-model="valuex" placeholder="请选择" size="small" style="margin-left:9px;width:10vw" @change="optupdata()">
  52. <el-option
  53. v-for="item in screenList"
  54. :key="item.id"
  55. :label="reqpar1 ? item.projectName : item.name"
  56. :value="item.id"
  57. >
  58. <span v-if="reqpar1" style="float: left;color: #8492a6;">{{ item.projectCode }}</span>
  59. <span v-if="reqpar1" style="float: right;font-size: 13px;margin-left: 20px">{{ item.projectName }}</span>
  60. </el-option>
  61. </el-select>
  62. </div>
  63. <!-- 资源需求导入/导出 -->
  64. <div class="head_files" v-if="!isDataLoaded">
  65. <div style="margin-left:10px;float:left;">
  66. <el-link type="primary" :underline="false" href="./upload/资源需求导入模板.xlsx" download="资源需求导入模板.xlsx">模板下载</el-link>
  67. </div>
  68. <el-upload ref="upload" style="margin-left:10px;float:left;" action="#" :limit="1" :http-request="importProject" :show-file-list="false">
  69. <el-link type="primary" :underline="false" >导入需求</el-link>
  70. </el-upload>
  71. <div style="margin-left:10px;float:left;">
  72. <el-link type="primary" :underline="false" @click="exportProjectData" download="资源需求导出.xlsx">导出需求</el-link>
  73. </div>
  74. </div>
  75. </div>
  76. <gantt v-if="isDataLoaded" ref="ganttTable1" class="left-container" :tasks="tasks"
  77. :stafforpro="radio1"
  78. :valueDate="valueDate"
  79. :key="updatakey1"></gantt>
  80. <div class="demand-container" v-if="!isDataLoaded">
  81. <el-table height="90%" :loading="demandListLoading" :data="demandList">
  82. <el-table-column label="项目编号" prop="projectCode" width="160"></el-table-column>
  83. <el-table-column label="项目名称" prop="projectName" min-width="240"></el-table-column>
  84. <el-table-column label="近七日活跃人员" prop="activeUsers" min-width="240"></el-table-column>
  85. <el-table-column label="开始时间 - 结束时间" min-width="240">
  86. <template slot-scope="scope">
  87. <span>{{scope.row.startDate ? scope.row.startDate + ' ~ ' + scope.row.endDate : ''}}</span>
  88. </template>
  89. </el-table-column>
  90. <el-table-column label="合同要求" prop="contractReq" min-width="240" show-overflow-tooltip></el-table-column>
  91. <el-table-column label="里程碑进度" prop="milepost" min-width="280">
  92. <template slot-scope="scope">
  93. <span>{{scope.row.milepost ? scope.row.milepost.name + '\u3000完成时间:' + scope.row.milepost.finishDate : ''}}</span>
  94. </template>
  95. </el-table-column>
  96. <el-table-column label="人员需求" prop="membReq" min-width="280"></el-table-column>
  97. <el-table-column label="任务需求" prop="taskReq" min-width="280"></el-table-column>
  98. <el-table-column label="参与部门进度" prop="departmentSpeed" min-width="280"></el-table-column>
  99. <el-table-column label="操作" width="120" align="center" fixed="right">
  100. <template slot-scope="scope">
  101. <el-button @click="demandEdit(scope.row)" size="small">修改</el-button>
  102. </template>
  103. </el-table-column>
  104. </el-table>
  105. <div class="poss">
  106. <el-pagination
  107. @size-change="handleSizeChange"
  108. @current-change="handleCurrentChange"
  109. :current-page="pageIndex"
  110. :page-sizes="[20, 50, 100, 200]"
  111. :page-size="20"
  112. layout="total, sizes, prev, pager, next"
  113. :total="total">
  114. </el-pagination>
  115. </div>
  116. <!-- 资源需求修改 -->
  117. <el-dialog v-if="demandEditDialog" width="500px" append-to-body :visible.sync="demandEditDialog" :title="'修改 - ' + editParameter.projectName">
  118. <el-form label-width="150">
  119. <el-form-item label="开始时间">
  120. <el-date-picker
  121. style="width:350px"
  122. v-model="editDate1"
  123. type="date"
  124. placeholder="请选择开始时间"
  125. value-format="yyyy-MM-dd"></el-date-picker>
  126. </el-form-item>
  127. <el-form-item label="结束时间">
  128. <el-date-picker
  129. style="width:350px"
  130. v-model="editDate2"
  131. type="date"
  132. placeholder="请选择结束时间"
  133. value-format="yyyy-MM-dd"></el-date-picker>
  134. </el-form-item>
  135. <el-form-item label="人员需求">
  136. <el-input style="width:350px" v-model="editParameter.membReq" clearable></el-input>
  137. </el-form-item>
  138. <el-form-item label="任务需求">
  139. <el-input style="width:350px" v-model="editParameter.taskReq" clearable></el-input>
  140. </el-form-item>
  141. <el-form-item label="合同要求">
  142. <el-input style="width:350px" v-model="editParameter.contractReq" clearable></el-input>
  143. </el-form-item>
  144. <el-form-item label="部门进度">
  145. <el-input style="width:350px" v-model="editParameter.departmentSpeed" clearable></el-input>
  146. </el-form-item>
  147. </el-form>
  148. <div slot="footer" class="dialog-footer">
  149. <el-button type="default" @click="demandEditDialog = false">取消</el-button>
  150. <el-button type="primary" @click="demandEditSure" >确定</el-button>
  151. </div>
  152. </el-dialog>
  153. </div>
  154. </div>
  155. </template>
  156. <script>
  157. import { error } from 'dingtalk-jsapi';
  158. import Gantt from './gantt.vue';
  159. export default {
  160. name: 'project_gantt',
  161. components: {Gantt},
  162. data () {
  163. return {
  164. taskType: '',
  165. isDataLoaded:false,
  166. tasks: {
  167. data : [],
  168. links: []
  169. },
  170. tasks1: {links:[]},
  171. updatakey1: 1,
  172. updatakey2: -1,
  173. radio1:"按人员查看",
  174. valueDate:[],
  175. options:[{value:"选项1",label:"全部"},{value:"选项2",label:"人员1"}],
  176. valuex:'',
  177. screenList:[],
  178. // 请求参数
  179. reqpar1:0,
  180. reqpar2:[],
  181. demandListLoading: false,
  182. demandList: [],
  183. pageIndex: 1,
  184. pageSize: 20,
  185. demandEditDialog: false,
  186. editParameter: {},
  187. editDate1: '',
  188. editDate2: '',
  189. taskgroupList: [],
  190. valuex2: ''
  191. }
  192. },
  193. methods: {
  194. setGroup() {
  195. this.$refs.ganttTable1.setGroup();
  196. // this.$refs.ganttTable2.setGroup();
  197. },
  198. taskTypeSel(){
  199. console.log(this.taskType);
  200. this.getList()
  201. },
  202. // 资源需求导出
  203. exportProjectData() {
  204. let parameter = {}
  205. if(this.valuex){
  206. parameter.projectId = this.valuex
  207. }
  208. if(this.valuex2){
  209. parameter.groupName = this.valuex2
  210. }
  211. this.http.post('/project-requirement/exportData',parameter,
  212. res => {
  213. if (res.code == "ok") {
  214. let filePath = res.data;
  215. const a = document.createElement('a'); // 创建a标签
  216. a.setAttribute('download', '资源需求导出.xls');// download属性
  217. a.setAttribute('href', filePath);// href链接
  218. a.click(); //自执行点击事件
  219. a.remove();
  220. }
  221. },
  222. error => {
  223. this.$message({
  224. message: error,
  225. type: "error"
  226. });
  227. }
  228. );
  229. },
  230. // 资源需求导入
  231. importProject(item) {
  232. //首先判断文件类型
  233. let str = item.file.name.split(".");
  234. let format = str[str.length - 1];
  235. if (format != "xls" && format != "xlsx") {
  236. this.$message({
  237. message: "请选择.xls或.xlsx文件",
  238. type: "error"
  239. });
  240. } else {
  241. let formData = new FormData();
  242. formData.append("multipartFile", item.file);
  243. this.http.uploadFile('/project-requirement/importData', formData,
  244. res => {
  245. this.$refs.upload.clearFiles();
  246. if (res.code == "ok") {
  247. this.$message({
  248. message: "导入成功",
  249. type: "success"
  250. });
  251. this.getDemandList();
  252. } else {
  253. this.$message({
  254. message: res.msg,
  255. type: "error"
  256. });
  257. }
  258. },
  259. error => {
  260. this.$refs.upload.clearFiles();
  261. this.$message({
  262. message: error,
  263. type: "error"
  264. });
  265. });
  266. }
  267. },
  268. // 人员/项目切换
  269. selChange(){
  270. this.valuex = null
  271. // console.log("切换按钮",this.radio1);
  272. if (this.radio1 == "按人员查看") {
  273. this.isDataLoaded = true
  274. this.reqpar1 = 0
  275. this.getList()
  276. this.getScreen()
  277. }else if(this.radio1 == "按项目查看"){
  278. this.isDataLoaded = true
  279. this.reqpar1 = 1
  280. this.getList()
  281. this.getXmScreen()
  282. this.getTaskgroupList()
  283. }else {
  284. this.reqpar1 = 1
  285. this.isDataLoaded = false
  286. this.pageIndex = 1
  287. this.pageSize = 20
  288. this.valuex = ''
  289. this.valuex2 = ''
  290. this.getDemandList()
  291. this.getXmScreen()
  292. this.getTaskgroupList()
  293. }
  294. },
  295. // 时间段改变
  296. dateupdata(){
  297. this.reqpar2 = this.valueDate
  298. if(this.isDataLoaded){
  299. this.getList()
  300. }else {
  301. this.getDemandList()
  302. }
  303. },
  304. // 人员/项目筛选改变
  305. optupdata(){
  306. // console.log(this.valuex);
  307. if(this.isDataLoaded){
  308. this.getList()
  309. }else{
  310. this.getDemandList()
  311. }
  312. },
  313. // 任务分组筛选改变
  314. taskgroupSel(){
  315. // console.log(this.valuex2);
  316. if(this.isDataLoaded){
  317. this.getList()
  318. }else{
  319. this.getDemandList()
  320. }
  321. },
  322. handleSizeChange(val){
  323. this.pageSize = val
  324. this.pageIndex = 1
  325. this.getDemandList()
  326. },
  327. handleCurrentChange(val){
  328. this.pageIndex = val
  329. this.getDemandList()
  330. },
  331. // 资源需求修改
  332. demandEdit(row){
  333. console.log(row);
  334. this.demandEditDialog = true
  335. this.editParameter = JSON.parse(JSON.stringify(row))
  336. this.editDate1 = this.editParameter.startDate
  337. this.editDate2 = this.editParameter.endDate
  338. // console.log('edit',this.demandEditDialog);
  339. },
  340. demandEditSure(){
  341. this.editParameter.startDate = this.editDate1
  342. this.editParameter.endDate = this.editDate2
  343. delete this.editParameter.reStartDate
  344. delete this.editParameter.reEndDate
  345. delete this.editParameter.milepost
  346. this.http.post('/project-requirement/addOrMod',this.editParameter,
  347. res => {
  348. if(res.code == 'ok'){
  349. this.$message({
  350. message: '修改成功',
  351. type: 'success'
  352. })
  353. this.demandEditDialog = false
  354. this.getDemandList()
  355. }else {
  356. this.$message({
  357. message: res.msg,
  358. type: 'error'
  359. })
  360. }
  361. },err => {
  362. this.$message({
  363. message: err,
  364. type: 'error'
  365. })
  366. })
  367. },
  368. // 获取人员项目筛选列表
  369. getScreen(){
  370. this.http.get('/project/getMyUsers',
  371. res => {
  372. if (res.code == "ok") {
  373. this.screenList = res.data
  374. }else{
  375. this.$message({
  376. message: res.msg,
  377. type: "error"
  378. });
  379. }
  380. },
  381. error => {
  382. this.$message({
  383. message : error,
  384. type : "error"
  385. })
  386. }
  387. )
  388. },
  389. getXmScreen(){
  390. this.http.get('/project/getProjectList',
  391. res => {
  392. if (res.code == "ok") {
  393. this.screenList = res.data
  394. // console.log("screen",this.screenList);
  395. }else{
  396. this.$message({
  397. message: res.msg,
  398. type: "error"
  399. });
  400. }
  401. },
  402. error => {
  403. this.$message({
  404. message : error,
  405. type : "error"
  406. })
  407. }
  408. )
  409. },
  410. // 获取任务分组筛选列表
  411. getTaskgroupList(){
  412. this.http.post('/task-group/getGroupNames',{},
  413. res => {
  414. if(res.code == 'ok'){
  415. this.taskgroupList = res.data
  416. }else {
  417. this.$message({
  418. message: res.msg,
  419. type: 'error'
  420. })
  421. }
  422. },err => {
  423. this.$message({
  424. message: err,
  425. type: 'error'
  426. })
  427. })
  428. },
  429. // 获取甘特图数据
  430. getList() {
  431. let getlistcs = {type : this.reqpar1 , startDate : this.reqpar2[0] , endDate : this.reqpar2[1]}
  432. if(this.reqpar1) {
  433. if(this.valuex != ''){
  434. getlistcs.projectId = this.valuex
  435. }
  436. if(this.valuex2 != ''){
  437. getlistcs.groupName = this.valuex2
  438. }
  439. getlistcs.taskType = this.taskType
  440. }else {
  441. if(this.valuex != ''){
  442. getlistcs.userId = this.valuex
  443. }
  444. }
  445. this.http.post('/project/getGanttData', getlistcs ,
  446. res => {
  447. if (res.code == "ok") {
  448. for(var i in res.data) {
  449. if(res.data[i].id.indexOf('出差') != '-1') {
  450. res.data[i].color = '#E6A23C'
  451. }
  452. if(res.data[i].id.indexOf('请假') != '-1') {
  453. res.data[i].color = '#F56C6C'
  454. }
  455. }
  456. this.tasks = {data:res.data};
  457. let taskson = []
  458. for(let m in this.tasks.data){
  459. this.tasks.data[m].render = null
  460. if(this.tasks.data[m].time != 0){
  461. if(this.tasks.data[m].id != this.tasks.data[m].userId){
  462. this.tasks.data[m].render = 'split'
  463. let item = JSON.parse(JSON.stringify(this.tasks.data[m]))
  464. item.parent = this.tasks.data[m].id
  465. item.id = this.tasks.data[m].id + 'son'
  466. taskson.push(item)
  467. this.tasks.data[m].time = 0
  468. }
  469. }
  470. }
  471. this.tasks.data.push.apply(this.tasks.data,taskson)
  472. for(let i in this.tasks.data){
  473. if(this.tasks.data[i].time == 0){
  474. delete this.tasks.data[i].start_date
  475. delete this.tasks.data[i].end_date
  476. this.tasks.data[i].type = 'milestone'
  477. }
  478. if(this.tasks.data[i].end_date){
  479. let date = new Date(this.tasks.data[i].end_date)
  480. let date2 = new Date(date.getFullYear(),date.getMonth(),date.getDate() + 1)
  481. let dmonth = date2.getMonth() + 1
  482. let dday = date2.getDate()
  483. this.tasks.data[i].end_date = date2.getFullYear() + '-' + (dmonth < 10 ? '0' + dmonth : dmonth) + '-' + (dday < 10 ? '0' + dday : dday)
  484. }
  485. }
  486. console.log('ganttData',this.tasks.data);
  487. this.$nextTick(()=>{
  488. this.updatakey1 += 1
  489. })
  490. } else {
  491. this.$message({
  492. message: res.msg,
  493. type: "error"
  494. });
  495. }
  496. },
  497. error => {
  498. this.$message({
  499. message: error,
  500. type: "error"
  501. });
  502. }
  503. );
  504. },
  505. // 获取资源需求列表
  506. getDemandList(){
  507. let parameter = {
  508. pageIndex: this.pageIndex,
  509. pageSize: this.pageSize,
  510. }
  511. if(this.valuex != ''){
  512. parameter.projectId = this.valuex
  513. }
  514. if(this.valuex2 != ''){
  515. parameter.groupName = this.valuex2
  516. }
  517. this.demandListLoading = true
  518. this.http.post('/project-requirement/listByPage',parameter,
  519. res => {
  520. if(res.code == 'ok'){
  521. this.demandListLoading = false
  522. this.total = res.data.total
  523. this.demandList = res.data.records
  524. }else {
  525. this.demandListLoading = false
  526. this.$message({
  527. message: res.msg,
  528. type: 'error'
  529. })
  530. }
  531. },err => {
  532. this.demandListLoading = false
  533. this.$message({
  534. message: err,
  535. type: 'error'
  536. })
  537. })
  538. },
  539. // tasksEdit(){
  540. // let etasks = JSON.parse(JSON.stringify(this.tasks.data))
  541. // for(let i=0;i<etasks.length;i++){
  542. // if(etasks[i].parent != null){
  543. // let edate = new Date(etasks[i].end_date)
  544. // edate = new Date(edate.setDate(edate.getDate() + 1))
  545. // let edatemonth = edate.getMonth() + 1
  546. // let edateday = edate.getDate()
  547. // edate = edate.getFullYear() + '-' + (edatemonth < 10 ? '0' + edatemonth : edatemonth) + '-' + (edateday < 10 ? '0' + edateday : edateday)
  548. // etasks[i].end_date = edate
  549. // }
  550. // }
  551. // this.tasks = {data:etasks}
  552. // }
  553. },
  554. mounted: function () {
  555. let nowdate = new Date()
  556. let nowmonth = nowdate.getMonth() + 1
  557. let startdate = nowdate.getFullYear() + "-" + (nowmonth < 10 ? "0" + nowmonth : nowmonth) + "-" + (nowdate.getDate() < 10 ? "0" + nowdate.getDate() : nowdate.getDate())
  558. let udate = new Date(nowdate.getFullYear(),nowdate.getMonth(),nowdate.getDate() + 31)
  559. let endmonth = udate.getMonth() + 1
  560. let enddate = udate.getFullYear() + "-" + (endmonth < 10 ? "0" + endmonth : endmonth) + "-" + (udate.getDate() < 10 ? "0" + udate.getDate() : udate.getDate())
  561. this.valueDate = [startdate,enddate]
  562. this.valueDate2 = this.valueDate
  563. this.reqpar2 = this.valueDate
  564. // console.log("date",this.valueDate);
  565. this.getList();
  566. this.getScreen()
  567. // this.tasks1 = this.tasks
  568. this.isDataLoaded = true
  569. }
  570. }
  571. </script>
  572. <style>
  573. /* html, body {
  574. height: 100%;
  575. margin: 0;
  576. padding: 0;
  577. } */
  578. .container {
  579. height: 100%;
  580. width: 100%;
  581. }
  582. .left-container {
  583. overflow: hidden;
  584. position: relative;
  585. height: 90%;
  586. }
  587. .demand-container{
  588. overflow: hidden;
  589. position: relative;
  590. height: 92%;
  591. }
  592. .gantt_head{
  593. width: 100%;
  594. height: 80px;
  595. display: flex;
  596. justify-content: space-between;
  597. align-items: center;
  598. }
  599. .gantt_head .head_RorX{
  600. width: 280px;
  601. }
  602. .gantt_head .head_date{
  603. width: 22vw;
  604. }
  605. .gantt_head .head_taskgroup{
  606. width: 15vw;
  607. }
  608. .gantt_head .head_taskType{
  609. width: 15vw;
  610. }
  611. .gantt_head .head_select{
  612. width: 15vw;
  613. }
  614. .gantt_head .head_files{
  615. width: 17vw;
  616. }
  617. .poss {
  618. height: 8%;
  619. float: right;
  620. padding-top: 5px;
  621. }
  622. </style>