project_gantt.vue 21 KB

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