custom_data.vue 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. <template>
  2. <section>
  3. <el-row style="padding-bottom: 0px;text-align:center;margin-top:20px;z-index: 999;">
  4. <el-col :span="6" >
  5. <div>&nbsp;</div>
  6. </el-col>
  7. <el-col :span="12" style="">
  8. <el-date-picker
  9. v-model="dateRange" :editable="false"
  10. format="yyyy-MM-dd" value-format="yyyy-MM-dd"
  11. @change="getEchart"
  12. :clearable="true"
  13. range-separator="至"
  14. type="daterange"
  15. start-placeholder="开始日期"
  16. end-placeholder="结束日期"
  17. ></el-date-picker>
  18. <el-radio-group v-model="radio" @change="getEchart" style="margin-left:10px;">
  19. <el-radio-button label="项目"></el-radio-button>
  20. <!-- <el-radio-button label="部门"></el-radio-button> -->
  21. <el-radio-button label="人员"></el-radio-button>
  22. <!-- <el-radio-button :label="namess" v-if="jichu.customDegreeActive == 1"></el-radio-button> -->
  23. </el-radio-group>
  24. </el-col>
  25. <el-col :span="6">
  26. <el-button @click="showExportDialog">报表导出</el-button>
  27. </el-col>
  28. </el-row>
  29. <div :style="'width:'+widthHtval+'px;position: relative; height:'+containerHeight+'px;'">
  30. <div id="container" :style="'height:'+containerHeight+'px;width:100%'"></div>
  31. </div>
  32. <div style="position:fixed;top:170px;left:600px;" v-show="radio=='部门' && parentDeptId != null">
  33. <el-button @click="backToParentDept">返回上级</el-button>
  34. </div>
  35. <!--导出报表条件选择 -->
  36. <el-dialog title="工时报表导出" v-if="exportDialog" :visible.sync="exportDialog" :close-on-click-modal="false" customClass="customWidth" width="500px">
  37. <el-form ref="form3" :model="exportParam" >
  38. <el-form-item prop="projectId" label="选择项目" >
  39. <el-select v-model="exportParam.projectId" placeholder="全部项目" clearable style="width:350px;" filterable="true">
  40. <el-option v-for="item in projectList" :key="item.id" :label="item.projectName" :value="item.id"></el-option>
  41. </el-select>
  42. </el-form-item>
  43. <!-- <el-form-item prop="userIds" label="选择人员" >
  44. <el-select v-model="exportParam.userIds" placeholder="全部人员" multiple="true" clearable style="width:350px;" filterable="true">
  45. <el-option v-for="item in hasReportUserList" :key="item.id" :label="item.name" :value="item.id"></el-option>
  46. </el-select>
  47. </el-form-item> -->
  48. <el-form-item prop="projectId" :label="user.timeType.fixMonthcost==0?'日期范围':'选择月份'">
  49. <el-date-picker v-show="user.timeType.fixMonthcost==0"
  50. v-model="exportParam.dateRange" :editable="false"
  51. format="yyyy-MM-dd" value-format="yyyy-MM-dd"
  52. :clearable="false"
  53. range-separator="至"
  54. type="daterange"
  55. start-placeholder="开始日期"
  56. end-placeholder="结束日期"
  57. ></el-date-picker>
  58. </el-form-item>
  59. <!-- <el-form-item label="选择人员" v-if="radio == '项目' || radio == '部门'">
  60. <el-select v-model="exportParam.userId" placeholder="请选择人员" style="width: 350px" filterable="true">
  61. <span v-for="(item, index) in users" :key="index">
  62. <el-option :label="item.name" :value="item.id"></el-option>
  63. </span>
  64. </el-select>
  65. </el-form-item> -->
  66. </el-form>
  67. <div slot="footer" class="dialog-footer">
  68. <el-button type="primary" @click="exportProjectData" style="width:100%;" >导出</el-button>
  69. </div>
  70. </el-dialog>
  71. </section>
  72. </template>
  73. <script>
  74. import util from "../../common/js/util";
  75. export default {
  76. data() {
  77. return {
  78. yAxisValue: localStorage.yAxisValue?localStorage.yAxisValue:0,
  79. parentDeptStack:[],
  80. parentDeptId:null,
  81. hasReportUserList:[],
  82. projectList:[],
  83. exportParam:{projectId:null,dateRange:[],userId: null},
  84. exportDialog:false,
  85. dateRange:[],
  86. user: JSON.parse(sessionStorage.getItem("user")),
  87. radio: sessionStorage.radio!=null?sessionStorage.radio:'项目',
  88. containerHeight: 0,
  89. myChart: null,
  90. params: null,
  91. widthHtval: document.body.clientWidth - 230,
  92. users: [],
  93. jichu: [],
  94. namess: '',
  95. timers: null, // 点击的时间
  96. zhishin: 0
  97. };
  98. },
  99. methods: {
  100. //Y轴点击改变显示的数据
  101. onYAxisChange() {
  102. localStorage.yAxisValue = this.yAxisValue;
  103. this.jieliu();
  104. },
  105. jutishez() {
  106. this.http.post('/time-type/getCompanyTimeSetting', {
  107. companyId: this.user.companyId,
  108. },
  109. res => {
  110. if (res.code == "ok") {
  111. this.jichu = res.data
  112. if(res.data.customDegreeActive == 1) {
  113. this.namess = res.data.customDegreeName
  114. }
  115. } else {
  116. this.$message({
  117. message: res.msg,
  118. type: "error"
  119. });
  120. }
  121. },
  122. error => {
  123. this.$message({
  124. message: error,
  125. type: "error"
  126. });
  127. });
  128. },
  129. getUsers() {
  130. console.log(this.port.manage.list)
  131. this.http.post(this.port.manage.list, {
  132. departmentId: -1,
  133. pageIndex: 1,
  134. pageSize: 99999
  135. },
  136. res => {
  137. if (res.code == "ok") {
  138. this.users = res.data.records;
  139. } else {
  140. this.$message({
  141. message: res.msg,
  142. type: "error"
  143. });
  144. }
  145. },
  146. error => {
  147. this.$message({
  148. message: error,
  149. type: "error"
  150. });
  151. });
  152. },
  153. showExportDialog() {
  154. this.exportDialog = true;
  155. this.exportParam.dateRange = this.dateRange;
  156. },
  157. //获取我的项目列表
  158. getMyProjectList() {
  159. this.http.post('/project/getProjectList', {
  160. },
  161. res => {
  162. if (res.code == "ok") {
  163. this.projectList = res.data;
  164. } else {
  165. this.$message({
  166. message: res.msg,
  167. type: "error"
  168. });
  169. }
  170. },
  171. error => {
  172. this.$message({
  173. message: error,
  174. type: "error"
  175. });
  176. });
  177. },
  178. exportProjectData() {
  179. var param = {};
  180. if (this.exportParam.dateRange != null) {
  181. param = {startDate:this.exportParam.dateRange[0], endDate: this.exportParam.dateRange[1]};
  182. }
  183. var url = "/project/exportCustomDataSum";
  184. var fileName = this.user.timeType.customDataName + '统计.xls';
  185. // if (this.radio == '人员' ) {
  186. // console.log(this.exportParam.userIds);
  187. // fileName = '人员工时成本统计.xls';
  188. // url = '/department/exportUserStatistic';
  189. // if (this.exportParam.userIds != null && this.exportParam.userIds.length > 0) {
  190. // var ids = '';
  191. // this.exportParam.userIds.forEach(u=>{
  192. // ids += u+',';
  193. // })
  194. // param.userIds = ids;
  195. // }
  196. // }
  197. if (this.exportParam.projectId != null) {
  198. param.projectId = this.exportParam.projectId;
  199. }
  200. this.http.post(url, param,
  201. res => {
  202. this.listLoading = false;
  203. if (res.code == "ok") {
  204. this.exportDialog = false;
  205. var aTag = document.createElement('a');
  206. aTag.download = fileName;
  207. aTag.href = res.data;
  208. aTag.click();
  209. } else {
  210. this.$message({
  211. message: res.msg,
  212. type: "error"
  213. });
  214. }
  215. },
  216. error => {
  217. this.listLoading = false;
  218. this.$message({
  219. message: error,
  220. type: "error"
  221. });
  222. });
  223. },
  224. //获取人员成本统计列表
  225. getUserCostList() {
  226. this.listLoading = true;
  227. this.http.post('/department/getUserCustomDataStatistic', {
  228. startDate:this.user.timeType.fixMonthcost==0?this.dateRange[0]:this.dateRange,
  229. endDate: this.user.timeType.fixMonthcost==0?this.dateRange[1]:this.dateRange
  230. },
  231. res => {
  232. this.listLoading = false;
  233. var _this = this;
  234. this.hasReportUserList = [];
  235. if (res.code == "ok") {
  236. //工时总成本
  237. this.hasReportUserList = res.data.userList;
  238. var xList = [] , yList = [] , list = res.data.list, array = [] , series = [];
  239. var totalHours = 0.0;
  240. if (list.length > 0) {
  241. var num = list.length==0?0:list[0].project.length;
  242. for(var i in list) {
  243. xList.push(list[i].name);
  244. var pro = list[i].project;
  245. for(var j in pro) {
  246. if(array.indexOf(pro[j].project) == -1) {
  247. array.push(pro[j].project)
  248. }
  249. }
  250. }
  251. for(var i in array) {
  252. yList.push(array[i]);
  253. var dataList = [];
  254. for(var j in list) {
  255. var project = list[j].project , num = 0;
  256. if(project.length != 0) {
  257. for(var k in project) {
  258. if(project[k].project == array[i]) {
  259. dataList.push({
  260. "value": project[k].cost,
  261. "cost": project[k].cost
  262. })
  263. totalHours += parseFloat(project[k].cost);
  264. } else {
  265. num++;
  266. }
  267. if(k == project.length-1 && num != project.length-1) {
  268. dataList.push({
  269. "value": 0,
  270. "cost": 0
  271. })
  272. }
  273. }
  274. } else {
  275. dataList.push({
  276. "value": 0,
  277. "cost": 0,
  278. })
  279. }
  280. }
  281. series.push({
  282. name: array[i],
  283. type: 'bar',
  284. stack:'1',
  285. barMaxWidth: 30,
  286. data: dataList,
  287. })
  288. }
  289. }
  290. var myChart = echarts.init(document.getElementById("container"));
  291. totalHours = totalHours.toFixed(1);
  292. _this.myChart = myChart;
  293. var option = {
  294. //总成本
  295. title: {
  296. text: _this.user.timeType.customDataName + '总计' + totalHours,
  297. left:'left',
  298. },
  299. // 工具箱
  300. legend: {
  301. x: 80,
  302. y: 10,
  303. data: yList,
  304. show: true,
  305.        top:"5%",//与上方的距离 可百分比% 可像素px
  306. },
  307. grid : {
  308. top : 80, //距离容器上边界40像素
  309. bottom: 35 //距离容器下边界30像素
  310. },
  311. toolbox: {
  312. show: true,
  313. feature:{
  314. saveAsImage:{
  315. show:true
  316. },
  317. restore:{
  318. show:true
  319. },
  320. // dataView:{
  321. // show:true
  322. // },
  323. // dataZoom:{
  324. // show:true
  325. // },
  326. magicType:{
  327. type:['line','bar']
  328. }
  329. }
  330. },
  331. tooltip:{
  332. trigger:'axis',
  333. formatter: function (params,ticket,callback) {
  334. var totalTime = 0;
  335. var res = "";
  336. for(var i in params) {
  337. if (params[i].data.value > 0) {
  338. res += "<div style='margin-top:3px;font-size:12px;'><font color='#ddd'>项目名称:" + params[i].seriesName
  339. + "</font><br/>"+_this.user.timeType.customDataName+" : " + params[i].data.cost+"</div>";
  340. totalTime += Number(params[i].data.cost);
  341. }
  342. }
  343. res = res +'<br/>'+ params[0].name+ '<br/>总计: ' + totalTime.toFixed(1);
  344. return res;
  345. }
  346. },
  347. xAxis: {
  348. data: xList,
  349. axisLabel: {
  350. interval:0,rotate:20
  351. }
  352. },
  353. yAxis: [{
  354. type : 'value',
  355. axisLabel: {
  356. formatter:'{value}'
  357. }
  358. }],
  359. series: series,
  360. };
  361. myChart.setOption(option,{notMerge:true});
  362. } else {
  363. this.$message({
  364. message: res.msg,
  365. type: "error"
  366. });
  367. }
  368. },
  369. error => {
  370. this.listLoading = false;
  371. this.$message({
  372. message: error,
  373. type: "error"
  374. });
  375. });
  376. },
  377. yanjiu() {
  378. console.log('触发')
  379. },
  380. getEchart(){
  381. var that = this
  382. // that.timers = setTimeout(()=>{
  383. // clearTimeout(that.timers)
  384. // console.log(that.timers)
  385. that.jieliu()
  386. // },100);
  387. // this.jieliu()
  388. },
  389. backToParentDept() {
  390. if (this.radio == '部门') {
  391. if (this.parentDeptStack.length > 0) {
  392. this.parentDeptStack.pop();
  393. if (this.parentDeptStack.length > 0) {
  394. this.parentDeptId = this.parentDeptStack[this.parentDeptStack.length -1];
  395. } else {
  396. this.parentDeptId = null;
  397. }
  398. this.jieliu();
  399. }
  400. }
  401. },
  402. // 脱离出来的方法
  403. jieliu() {
  404. sessionStorage.radio = this.radio;
  405. var _this = this;
  406. var param = {};
  407. if (this.dateRange != null) {
  408. param = {startDate:this.user.timeType.fixMonthcost==0?this.dateRange[0]:this.dateRange,
  409. endDate: this.user.timeType.fixMonthcost==0?this.dateRange[1]:this.dateRange};
  410. console.log(param);
  411. }
  412. var url = '';
  413. if (this.radio=='项目') {
  414. url = '/project/getCustomDataSum';
  415. } else if (this.radio=='部门') {
  416. url = this.port.project.depCost;
  417. param.parentDeptId = this.parentDeptId;
  418. } else if (this.radio=='人员') {
  419. this.getUserCostList();
  420. return;
  421. } else if (this.radio == this.namess) {
  422. url = '/project/getDegreeCost'
  423. }
  424. this.http.post(url, param,
  425. res => {
  426. if (res.code == "ok") {
  427. for(var i in res.data.costList) {
  428. if(i>20) {
  429. // this.widthHtval = +this.widthHtval + 2
  430. this.widthHtval = +this.widthHtval + 40
  431. } else {
  432. this.widthHtval = document.body.clientWidth - 230
  433. }
  434. }
  435. // 测试写的
  436. var xList = []
  437. var yList = []
  438. var list
  439. var totalMoneyCost;
  440. var totalHours = 0.0;
  441. if(this.radio == '项目' || this.radio=='部门') {
  442. list = res.data.costList
  443. totalMoneyCost = ((this.radio=='项目')?res.data.totalMoneyCost:res.data.totalCostMoney);
  444. for(var i in list) {
  445. if(this.radio=='项目') {
  446. xList.push(this.radio=='项目'?list[i].project:list[i].name);
  447. yList.push({
  448. "value": list[i].cost.toFixed(1),
  449. "id": list[i].id || i,
  450. "cost": list[i].cost
  451. });
  452. totalHours += parseFloat(list[i].cost);
  453. } else {
  454. xList.push(list[i].departmentName);
  455. yList.push({
  456. "value": list[i].costTime.toFixed(1),
  457. "id": list[i].departmentId,
  458. "cost": list[i].costTime,
  459. "hasSubDept": list[i].hasSubDept
  460. });
  461. totalHours += parseFloat(list[i].costTime);
  462. }
  463. }
  464. } else {
  465. list = res.data
  466. var totalMoneyCost = 0;
  467. for(var i in list) {
  468. console.log(list[i].name, list[i].costMoney, list[i].cost)
  469. xList.push(list[i].name);
  470. yList.push({
  471. "value": this.yAxisValue==0?list[i].costMoney:list[i].cost,
  472. "id": list[i].id || i,
  473. "cost": list[i].cost,
  474. "money":list[i].costMoney.toFixed(2)
  475. });
  476. totalHours += parseFloat(list[i].cost);
  477. totalMoneyCost += parseFloat(list[i].costMoney);
  478. }
  479. }
  480. totalHours = totalHours.toFixed(1);
  481. var myChart = echarts.init(document.getElementById("container"));
  482. myChart.resize({
  483. width: this.widthHtval
  484. })
  485. _this.myChart = myChart;
  486. // console.log(totalMoneyCost.toFixed(2), '看看', totalMoneyCost)
  487. // var chengbentongji = totalMoneyCost.toFixed(2) || totalMoneyCost
  488. if(totalMoneyCost) {
  489. this.zhishin = totalMoneyCost.toFixed(2)
  490. }
  491. if(this.radio == '项目' || this.radio == '人员' || this.radio=='部门') {
  492. var option = {
  493. title: {
  494. text: _this.user.timeType.customDataName+'总计:' + totalHours,
  495. left:'left',
  496. },
  497. // 工具箱
  498. toolbox: {
  499. show: true,
  500. feature:{
  501. saveAsImage:{show:true},restore:{show:true}, magicType:{ type:['line','bar']},
  502. }
  503. },
  504. tooltip:{
  505. trigger:'axis',
  506. formatter: function (params,ticket,callback) {
  507. var res = params[0].name + "<br/>"+_this.user.timeType.customDataName+" : " + params[0].data.cost;
  508. _this.params = params;
  509. return res;
  510. }
  511. },
  512. xAxis: {
  513. data: xList,
  514. axisLabel: {
  515. interval:0,rotate:20
  516. }
  517. },
  518. yAxis: [{
  519. type : 'value',
  520. axisLabel: {
  521. formatter:'{value}'
  522. }
  523. }],
  524. series: [{
  525. name: _this.user.timeType.customDataName,
  526. type: 'bar',
  527. barMaxWidth: 30,
  528. data: yList,
  529. }]
  530. };
  531. } else {
  532. var option = {
  533. title: {
  534. text: '工时成本总计' + _this.zhishin + '元, 时长'+totalHours+'小时',
  535. left:'left',
  536. },
  537. // 工具箱
  538. toolbox: {
  539. show: true,
  540. feature:{
  541. saveAsImage:{show:true},restore:{show:true}, magicType:{ type:['line','bar']},
  542. }
  543. },
  544. tooltip:{
  545. trigger:'axis',
  546. formatter: function (params,ticket,callback) {
  547. var res = params[0].name + "<br/>工作成本"+" : " + params[0].data.money
  548. + "元 <br/>工作时长"+" : " + params[0].data.cost + "小时";
  549. _this.params = params;
  550. return res;
  551. }
  552. },
  553. xAxis: {
  554. data: xList,
  555. axisLabel: {
  556. interval:0,rotate:20
  557. }
  558. },
  559. yAxis: [{
  560. type : 'value',
  561. axisLabel: {
  562. formatter:this.yAxisValue==0?'{value} (元)':'{value}小时'
  563. }
  564. }],
  565. series: [{
  566. name: this.yAxisValue==0?'工作成本(元)':'工作时长(小时)',
  567. type: 'bar',
  568. barMaxWidth: 30,
  569. data: yList,
  570. }]
  571. };
  572. }
  573. myChart.setOption(option,{notMerge: true});
  574. myChart.getZr().on('click', params => {
  575. const pointInPixel = [params.offsetX, params.offsetY];
  576. if (myChart.containPixel('grid', pointInPixel)) {
  577. console.log(_this.params)
  578. if(_this.radio=='项目') {
  579. if (_this.dateRange != null) {
  580. if (this.user.timeType.fixMonthcost == 0) {
  581. _this.$router.push("/cost/" + _this.params[0].data.id + "/" + _this.params[0].name
  582. +"?startDate="+_this.dateRange[0]+"&endDate="+_this.dateRange[1]);
  583. } else {
  584. _this.$router.push("/cost/" + _this.params[0].data.id + "/" + _this.params[0].name
  585. +"?startDate="+_this.dateRange+"&endDate="+_this.dateRange);
  586. }
  587. } else {
  588. _this.$router.push("/cost/" + _this.params[0].data.id + "/" + _this.params[0].name);
  589. }
  590. } else if (_this.radio=='部门') {
  591. if (_this.params[0].data.hasSubDept) {
  592. if (_this.parentDeptId != _this.params[0].data.id) {
  593. _this.parentDeptId = _this.params[0].data.id;
  594. _this.parentDeptStack.push(_this.parentDeptId);
  595. _this.jieliu();
  596. }
  597. // _this.jieliu();
  598. } else {
  599. if (_this.dateRange != null) {
  600. _this.$router.push("/costDep/" + _this.params[0].data.id + "/" + _this.params[0].name
  601. +"?startDate="+_this.dateRange[0]+"&endDate="+_this.dateRange[1]);
  602. } else {
  603. _this.$router.push("/costDep/" + _this.params[0].data.id + "/" + _this.params[0].name);
  604. }
  605. }
  606. }
  607. }
  608. });
  609. } else {
  610. this.$message({
  611. message: res.msg,
  612. type: "error"
  613. });
  614. }
  615. },
  616. error => {
  617. this.$message({
  618. message: error,
  619. type: "error"
  620. });
  621. });
  622. }
  623. },
  624. created() {
  625. },
  626. mounted() {
  627. this.containerHeight = window.innerHeight - 200
  628. const that = this;
  629. window.onresize = function temp() {
  630. this.containerHeight = window.innerHeight - 200
  631. };
  632. if (this.user.timeType.fixMonthcost == 0) {
  633. if (this.$route.query.startDate != null) {
  634. this.dateRange = [this.$route.query.startDate, this.$route.query.endDate];
  635. } else {
  636. //默认查看本月
  637. var now = new Date();
  638. var t = util.formatDate.format(now, 'yyyy-MM-dd');
  639. var startStr = util.formatDate.format(new Date(), 'yyyy-MM') + "-01";
  640. this.dateRange = [startStr,t];
  641. }
  642. this.exportParam.dateRange = this.dateRange;
  643. } else if (this.user.timeType.fixMonthcost == 1) {
  644. if (this.$route.query.startDate != null) {
  645. this.dateRange = this.$route.query.startDate;
  646. } else {
  647. //默认查看本月
  648. var startStr = util.formatDate.format(new Date(), 'yyyy-MM');
  649. this.dateRange = startStr;
  650. }
  651. this.exportParam.dateRange = this.dateRange;
  652. }
  653. this.radio = '项目'
  654. this.getEchart();
  655. var _this = this;
  656. window.addEventListener("resize", function() {
  657. _this.myChart.resize();
  658. });
  659. // this.getDepartment();
  660. this.getMyProjectList();
  661. this.getUsers()
  662. this.jutishez()
  663. },
  664. };
  665. </script>
  666. <style lang="scss" scoped>
  667. #container {
  668. // display: inline-block;
  669. display: block;
  670. position: absolute;
  671. // width: 100% !important;
  672. margin-top: 60px;
  673. }
  674. </style>
  675. <style lang="scss">
  676. </style>