cost.vue 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  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 ><span style="color:#999;">图表Y轴: </span>
  6. <el-radio-group v-model="yAxisValue" @change="onYAxisChange">
  7. <el-radio-button label="0" v-if="permissions.countCost">显示成本</el-radio-button>
  8. <el-radio-button label="1" v-if="permissions.countHours">显示工时</el-radio-button>
  9. </el-radio-group></div>
  10. </el-col>
  11. <el-col :span="14" style="display: flex;flex-wrap: wrap;">
  12. <el-date-picker v-show="user.timeType.fixMonthcost==0"
  13. v-model="dateRange" :editable="false"
  14. format="yyyy-MM-dd" value-format="yyyy-MM-dd"
  15. @change="getEchart"
  16. :clearable="true"
  17. range-separator="至"
  18. type="daterange"
  19. start-placeholder="开始日期"
  20. end-placeholder="结束日期"
  21. ></el-date-picker>
  22. <el-date-picker v-show="user.timeType.fixMonthcost==1"
  23. v-model="dateRange" :editable="false"
  24. format="yyyy-MM" value-format="yyyy-MM"
  25. @change="getEchart"
  26. :clearable="false"
  27. type="month"
  28. ></el-date-picker>
  29. <el-radio-group v-model="radio" @change="getEchart" style="margin-left:10px;">
  30. <el-radio-button label="项目"></el-radio-button>
  31. <el-radio-button label="项目分类"></el-radio-button>
  32. <el-radio-button label="部门"></el-radio-button>
  33. <el-radio-button label="人员"></el-radio-button>
  34. <el-radio-button :label="namess" v-if="jichu.customDegreeActive == 1"></el-radio-button>
  35. </el-radio-group>
  36. <el-select v-model="personnelValue" filterable clearable placeholder="请选择人员" style="margin-top: 10px;width: 350px" v-if="radio == '人员'" @change="personnel()">
  37. <el-option v-for="item in hasReportUserList" :key="item.id" :label="item.name" :value="item.name"></el-option>
  38. </el-select>
  39. </el-col>
  40. <el-col :span="4">
  41. <el-button @click="showExportDialog">报表导出</el-button>
  42. </el-col>
  43. </el-row>
  44. <!-- <div id="clearfix" :style="'width:'+widthHtval+'px;position: relative; height:'+containerHeight+'px;'">
  45. <div id="container" :style="'height:'+containerHeight+'px;width:100%'"></div>
  46. </div> -->
  47. <div id="clearfix" :class="radio == '人员' ? 'ryuans' : ''" :style="'overflow-x: auto;width:100%;padding-bottom: 100px; position: relative; height:'+containerHeight+'px;'">
  48. <div id="container" :style="'height:'+containerHeight+'px;width:100%'"></div>
  49. </div>
  50. <!-- <div>
  51. <div id="container" :style="'height:'+containerHeight+'px;width:100%'"></div>
  52. </div> -->
  53. <div style="position:fixed;top:170px;left:600px;" v-show="radio=='部门' && parentDeptId != null">
  54. <el-button @click="backToParentDept">返回上级</el-button>
  55. </div>
  56. <!--导出报表条件选择 -->
  57. <el-dialog title="工时报表导出" v-if="exportDialog" :visible.sync="exportDialog" :close-on-click-modal="false" customClass="customWidth" width="500px">
  58. <el-form ref="form3" :model="exportParam" >
  59. <el-form-item prop="projectId" label="选择项目" v-if="radio != '人员' && radio != '项目分类'">
  60. <el-select v-model="exportParam.projectId" placeholder="全部项目" clearable style="width:350px;" filterable="true">
  61. <el-option v-for="item in projectList" :key="item.id" :label="item.projectName + item.projectCode" :value="item.id">
  62. <span style="float: left;color: #8492a6;">{{ item.projectCode }}</span>
  63. <span style="float: right;font-size: 13px;margin-left: 20px">{{ item.projectName }}</span>
  64. </el-option>
  65. </el-select>
  66. </el-form-item>
  67. <el-form-item prop="projectCategoryId" label="项目分类" v-if="radio == '项目分类'">
  68. <el-select v-model="exportParam.projectCategoryId" placeholder="全部项目分类" clearable style="width:350px;" filterable="true">
  69. <el-option v-for="item in categoryList" :key="item.id" :label="item.name" :value="item.id">
  70. </el-option>
  71. </el-select>
  72. </el-form-item>
  73. <el-form-item prop="userIds" label="选择人员" v-if="radio == '人员'">
  74. <el-select v-model="exportParam.userIds" placeholder="全部人员" multiple="true" clearable style="width:350px;" filterable="true">
  75. <el-option v-for="item in hasReportUserList" :key="item.id" :label="item.name" :value="item.id"></el-option>
  76. </el-select>
  77. </el-form-item>
  78. <el-form-item prop="projectId" :label="user.timeType.fixMonthcost==0?'日期范围':'选择月份'">
  79. <el-date-picker v-show="user.timeType.fixMonthcost==0"
  80. v-model="exportParam.dateRange" :editable="false"
  81. format="yyyy-MM-dd" value-format="yyyy-MM-dd"
  82. :clearable="false"
  83. range-separator="至"
  84. type="daterange"
  85. start-placeholder="开始日期"
  86. end-placeholder="结束日期"
  87. ></el-date-picker>
  88. <el-date-picker v-show="user.timeType.fixMonthcost==1"
  89. v-model="dateRange" :editable="false"
  90. format="yyyy-MM" value-format="yyyy-MM"
  91. @change="getEchart"
  92. :clearable="true"
  93. type="month"
  94. ></el-date-picker>
  95. </el-form-item>
  96. <el-form-item label="选择人员" v-if="radio == '项目' || radio == '部门' || radio == '项目分类'">
  97. <el-select v-model="exportParam.userId" placeholder="全部人员" style="width: 350px" filterable="true" clearable="true">
  98. <span v-for="(item, index) in users" :key="index">
  99. <el-option :label="item.name" :value="item.id"></el-option>
  100. </span>
  101. </el-select>
  102. </el-form-item>
  103. <el-form-item prop="type" label="选择样式" v-if="radio == '项目' || radio == '部门' || radio == '项目分类'">
  104. <el-select v-model="exportParam.type" placeholder="选择样式" style="width:350px;" >
  105. <el-option :label="radio == '项目分类' ? '项目分类在行上' : '项目在行上'" value="0"></el-option>
  106. <el-option :label="radio == '项目分类' ? '项目分类在列上' : '项目在列上'" value="1"></el-option>
  107. </el-select>
  108. <div class="prompt">
  109. <el-popover placement="top" width="1200" trigger="hover">
  110. <img src="../../assets/image/hanglie.png" alt="" width="100%" v-if="this.radio != '项目分类'">
  111. <img src="../../assets/image/hanglie_corp.png" alt="" width="100%" v-else>
  112. <i class="el-icon-question" slot="reference" />
  113. </el-popover>
  114. </div>
  115. </el-form-item>
  116. <el-form-item v-if="(radio == '项目' || radio == '部门' || radio == '项目分类') && exportParam.type == '0'">
  117. <el-checkbox v-model="exportParam.projectSum" >含单个项目数据汇总</el-checkbox>
  118. </el-form-item>
  119. </el-form>
  120. <div slot="footer" class="dialog-footer">
  121. <el-button type="primary" @click="exportProjectData" style="width:100%;" >导出</el-button>
  122. </div>
  123. </el-dialog>
  124. </section>
  125. </template>
  126. <script>
  127. import util from "../../common/js/util";
  128. export default {
  129. data() {
  130. return {
  131. personnelValue: '',
  132. personnelAll: [],
  133. yAxisValue: localStorage.yAxisValue?localStorage.yAxisValue:0,
  134. parentDeptStack:[],
  135. parentDeptId:null,
  136. hasReportUserList:[],
  137. projectList:[],
  138. categoryList: [],
  139. exportParam:{projectId:null,dateRange:[],userId: null,type: '0'},
  140. exportDialog:false,
  141. dateRange:[],
  142. user: JSON.parse(sessionStorage.getItem("user")),
  143. permissions: JSON.parse(sessionStorage.getItem("permissions")),
  144. radio: sessionStorage.radio!=null?sessionStorage.radio:'项目',
  145. containerHeight: 0,
  146. myChart: null,
  147. params: null,
  148. widthHtval: document.body.clientWidth - 230,
  149. users: [],
  150. jichu: [],
  151. namess: '',
  152. timers: null, // 点击的时间
  153. zhishin: 0
  154. };
  155. },
  156. methods: {
  157. //Y轴点击改变显示的数据
  158. onYAxisChange() {
  159. localStorage.yAxisValue = this.yAxisValue;
  160. this.jieliu();
  161. },
  162. jutishez() {
  163. this.http.post('/time-type/getCompanyTimeSetting', {
  164. companyId: this.user.companyId,
  165. },
  166. res => {
  167. if (res.code == "ok") {
  168. this.jichu = res.data
  169. if(res.data.customDegreeActive == 1) {
  170. this.namess = res.data.customDegreeName
  171. }
  172. } else {
  173. this.$message({
  174. message: res.msg,
  175. type: "error"
  176. });
  177. }
  178. },
  179. error => {
  180. this.$message({
  181. message: error,
  182. type: "error"
  183. });
  184. });
  185. },
  186. getUsers() {
  187. console.log(this.port.manage.list)
  188. this.http.post(this.port.manage.list, {
  189. departmentId: -1,
  190. pageIndex: 1,
  191. pageSize: 99999
  192. },
  193. res => {
  194. if (res.code == "ok") {
  195. this.users = res.data.records;
  196. } else {
  197. this.$message({
  198. message: res.msg,
  199. type: "error"
  200. });
  201. }
  202. },
  203. error => {
  204. this.$message({
  205. message: error,
  206. type: "error"
  207. });
  208. });
  209. },
  210. showExportDialog() {
  211. console.log(12345)
  212. this.exportDialog = true;
  213. this.exportParam.dateRange = this.dateRange;
  214. console.log(this.hasReportUserList)
  215. if (this.radio == '人员') {
  216. // this.exportParam.userIds = [];
  217. }
  218. },
  219. //获取我的项目列表
  220. getMyProjectList() {
  221. this.http.post('/project/getProjectList', {
  222. },
  223. res => {
  224. if (res.code == "ok") {
  225. this.projectList = res.data;
  226. } else {
  227. this.$message({
  228. message: res.msg,
  229. type: "error"
  230. });
  231. }
  232. },
  233. error => {
  234. this.$message({
  235. message: error,
  236. type: "error"
  237. });
  238. });
  239. },
  240. exportProjectData() {
  241. var param = {};
  242. if (this.exportParam.dateRange != null) {
  243. param = {startDate:this.exportParam.dateRange[0], endDate: this.exportParam.dateRange[1]};
  244. }
  245. var url = "/project/exportTimeCost";
  246. var fileName = '项目工时成本统计.xls';
  247. if (this.radio == '人员' ) {
  248. console.log(this.exportParam.userIds);
  249. fileName = '人员工时成本统计.xls';
  250. url = '/department/exportUserStatistic';
  251. if (this.exportParam.userIds != null && this.exportParam.userIds.length > 0) {
  252. var ids = '';
  253. this.exportParam.userIds.forEach(u=>{
  254. ids += u+',';
  255. })
  256. param.userIds = ids;
  257. }
  258. }
  259. if(this.radio == '项目分类'){
  260. fileName = '项目分类工时成本统计.xls';
  261. url = '/project/exportTimeCostByCategory'
  262. if(this.exportParam.projectCategoryId){
  263. param.projectCategoryId = this.exportParam.projectCategoryId
  264. }
  265. }
  266. if(this.radio == '部门'){
  267. fileName = '部门工时成本统计.xls'
  268. url = '/department/exportDeptStatistic'
  269. }
  270. if (this.exportParam.projectId && this.radio != '人员' && this.radio != '项目分类') {
  271. param.projectId = this.exportParam.projectId;
  272. }
  273. if (this.exportParam.userId) {
  274. if(this.radio == '项目' || this.radio == '部门' || this.radio == '人员' || this.radio == '项目分类'){
  275. param.userId = this.exportParam.userId;
  276. }
  277. }
  278. if (this.exportParam.type == 1) {
  279. this.exportParam.projectSum = null
  280. }
  281. if (this.exportParam.projectSum != null) {
  282. if(this.radio == '项目' || this.radio == '部门' || this.radio == '项目分类'){
  283. param.projectSum = this.exportParam.projectSum;
  284. }
  285. }
  286. param.type = this.exportParam.type*1
  287. this.http.post(url, param,
  288. res => {
  289. this.listLoading = false;
  290. if (res.code == "ok") {
  291. this.exportDialog = false;
  292. var aTag = document.createElement('a');
  293. aTag.download = fileName;
  294. aTag.href = res.data;
  295. aTag.click();
  296. } else {
  297. this.$message({
  298. message: res.msg,
  299. type: "error"
  300. });
  301. }
  302. },
  303. error => {
  304. this.listLoading = false;
  305. this.$message({
  306. message: error,
  307. type: "error"
  308. });
  309. });
  310. },
  311. // 人员筛选
  312. personnel() {
  313. if(this.personnelValue) {
  314. var arrlist = JSON.parse(JSON.stringify(this.personnelAll))
  315. var arr = []
  316. for(var i in arrlist.list) {
  317. if(arrlist.list[i].name == this.personnelValue) {
  318. arr.push(arrlist.list[i])
  319. }
  320. }
  321. arrlist.list = arr
  322. this.gtff(arrlist)
  323. } else {
  324. this.gtff(this.personnelAll)
  325. }
  326. },
  327. //获取人员成本统计列表
  328. getUserCostList() {
  329. this.listLoading = true;
  330. console.log(this.port.project.userCost, '获取人员成本统计列表')
  331. console.log(this.user.timeType.fixMonthcost)
  332. console.log(Boolean(this.dateRange))
  333. let startDateNum = ''
  334. let endDateNum = ''
  335. if(this.dateRange) {
  336. startDateNum = this.user.timeType.fixMonthcost==0?this.dateRange[0]:this.dateRange
  337. endDateNum = this.user.timeType.fixMonthcost==0?this.dateRange[1]:this.dateRange
  338. }
  339. // return
  340. this.http.post(this.port.project.userCost, {
  341. // startDate:this.user.timeType.fixMonthcost==0?this.dateRange[0]:this.dateRange,
  342. // endDate: this.user.timeType.fixMonthcost==0?this.dateRange[1]:this.dateRange
  343. startDate: startDateNum,
  344. endDate: endDateNum
  345. },
  346. res => {
  347. this.listLoading = false;
  348. var _this = this;
  349. this.hasReportUserList = [];
  350. if (res.code == "ok") {
  351. //
  352. // var sss = []
  353. // var ddd = []
  354. // for(var i = 0; i < 120; i++) {
  355. // sss.push(res.data.list[0])
  356. // ddd.push(res.data.userList[0])
  357. // }
  358. // res.data.list = sss
  359. // res.data.userList = ddd
  360. for(var i in res.data.list) {
  361. if(i>20) {
  362. // this.widthHtval = +this.widthHtval + 2
  363. this.widthHtval = +this.widthHtval + 40
  364. } else {
  365. this.widthHtval = document.body.clientWidth - 230
  366. }
  367. }
  368. //
  369. this.personnelAll = res.data
  370. this.gtff(res.data)
  371. //工时总成本
  372. // this.hasReportUserList = res.data.userList;
  373. // var xList = [] yList = [] , list = res.data.list, array = [] , series = [];
  374. // var totalMoneyCost = res.data.totalCostMoney;
  375. // var totalHours = 0.0;
  376. // if (list.length > 0) {
  377. // var num = list.length==0?0:list[0].project.length;
  378. // for(var i in list) {
  379. // xList.push(list[i].name);
  380. // var pro = list[i].project;
  381. // for(var j in pro) {
  382. // if(array.indexOf(pro[j].project) == -1) {
  383. // array.push(pro[j].project)
  384. // }
  385. // }
  386. // }
  387. // for(var i in array) {
  388. // yList.push(array[i]);
  389. // var dataList = [];
  390. // for(var j in list) {
  391. // var project = list[j].project , num = 0;
  392. // if(project.length != 0) {
  393. // for(var k in project) {
  394. // if(project[k].project == array[i]) {
  395. // dataList.push({
  396. // "value": this.yAxisValue==0?project[k].money:project[k].time,
  397. // "cost": project[k].time,
  398. // "money":project[k].money
  399. // })
  400. // totalHours += parseFloat(project[k].time);
  401. // } else {
  402. // num++;
  403. // }
  404. // if(k == project.length-1 && num != project.length-1) {
  405. // dataList.push({
  406. // "value": 0,
  407. // "cost": 0,
  408. // "money":0,
  409. // })
  410. // }
  411. // }
  412. // } else {
  413. // dataList.push({
  414. // "value": 0,
  415. // "cost": 0,
  416. // "money":0,
  417. // })
  418. // }
  419. // }
  420. // series.push({
  421. // name: array[i],
  422. // type: 'bar',
  423. // stack:'1',
  424. // barMaxWidth: 30,
  425. // data: dataList,
  426. // })
  427. // }
  428. // }
  429. // var myChart = echarts.init(document.getElementById("container"));
  430. // totalHours = totalHours.toFixed(1);
  431. // // 设置宽度
  432. // myChart.resize({
  433. // width: this.widthHtval
  434. // })
  435. // // 设置宽度
  436. // _this.myChart = myChart;
  437. // var option = {
  438. // //总成本
  439. // title: {
  440. // text: '工时成本总计' + totalMoneyCost.toFixed(2) + '元, 时长'+totalHours+'小时',
  441. // left:'left',
  442. // },
  443. // // 工具箱
  444. // legend: {
  445. // x: 80,
  446. // y: 10,
  447. // data: yList,
  448. // show: true,
  449. //        top:"5%",//与上方的距离 可百分比% 可像素px
  450. // },
  451. // grid : {
  452. // top : 80, //距离容器上边界40像素
  453. // // bottom: 100, //距离容器下边界30像素
  454. // bottom: 35, //距离容器下边界30像素
  455. // left: 150,
  456. // right: 150
  457. // },
  458. // toolbox: {
  459. // show: true,
  460. // feature:{
  461. // saveAsImage:{
  462. // show:true
  463. // },
  464. // restore:{
  465. // show:true
  466. // },
  467. // // dataView:{
  468. // // show:true
  469. // // },
  470. // // dataZoom:{
  471. // // show:true
  472. // // },
  473. // magicType:{
  474. // type:['line','bar']
  475. // }
  476. // }
  477. // },
  478. // tooltip:{
  479. // trigger:'axis',
  480. // formatter: function (params,ticket,callback) {
  481. // var totalTime = 0;
  482. // var totalCost = 0;
  483. // var res = "";
  484. // for(var i in params) {
  485. // if (params[i].data.value > 0) {
  486. // res += "<div style='margin-top:3px;font-size:12px;'><font color='#ddd'>项目名称:" + params[i].seriesName
  487. // + "</font><br/>工作成本 : " + params[i].data.money
  488. // + "元 <br/>工作时长"+" : " + params[i].data.cost + "小时</br></div>";
  489. // totalTime += Number(params[i].data.cost);
  490. // totalCost += Number(params[i].data.money);
  491. // }
  492. // }
  493. // res = res +'<br/>'+ params[0].name+ '<br/>总计: ' + totalTime.toFixed(1)+'小时 '+totalCost.toFixed(2) + "元<br/>";
  494. // return res;
  495. // }
  496. // },
  497. // xAxis: {
  498. // data: xList,
  499. // axisLabel: {
  500. // interval:0,rotate:20
  501. // }
  502. // },
  503. // yAxis: [{
  504. // type : 'value',
  505. // axisLabel: {
  506. // formatter:this.yAxisValue==0?'{value} (元)':'{value} (小时)'
  507. // }
  508. // }],
  509. // series: series,
  510. // };
  511. // myChart.setOption(option,{notMerge:true});
  512. } else {
  513. this.$message({
  514. message: res.msg,
  515. type: "error"
  516. });
  517. }
  518. },
  519. error => {
  520. this.listLoading = false;
  521. this.$message({
  522. message: error,
  523. type: "error"
  524. });
  525. });
  526. },
  527. // 共同方法
  528. gtff(data) {
  529. var _this = this;
  530. this.hasReportUserList = data.userList;
  531. var xList = [] , yList = [] , list = data.list, array = [] , series = [];
  532. var totalMoneyCost = data.totalCostMoney;
  533. var totalHours = 0.0;
  534. if (list.length > 0) {
  535. var num = list.length==0?0:list[0].project.length;
  536. for(var i in list) {
  537. xList.push(list[i].name);
  538. var pro = list[i].project;
  539. for(var j in pro) {
  540. if(array.indexOf(pro[j].project) == -1) {
  541. array.push(pro[j].project)
  542. }
  543. }
  544. }
  545. for(var i in array) {
  546. yList.push(array[i]);
  547. var dataList = [];
  548. for(var j in list) {
  549. var project = list[j].project , num = 0;
  550. if(project.length != 0) {
  551. for(var k in project) {
  552. if(project[k].project == array[i]) {
  553. let item = {
  554. "value": this.yAxisValue==0?project[k].money:project[k].time,
  555. }
  556. if(this.permissions.countCost){
  557. item.money = project[k].money
  558. }
  559. if(this.permissions.countHours){
  560. item.cost = project[k].time
  561. totalHours += parseFloat(project[k].time);
  562. }
  563. dataList.push(item)
  564. } else {
  565. num++;
  566. }
  567. if(k == project.length-1 && num != project.length-1) {
  568. dataList.push({
  569. "value": 0,
  570. "cost": 0,
  571. "money":0,
  572. })
  573. }
  574. }
  575. } else {
  576. dataList.push({
  577. "value": 0,
  578. "cost": 0,
  579. "money":0,
  580. })
  581. }
  582. }
  583. series.push({
  584. name: array[i],
  585. type: 'bar',
  586. stack:'1',
  587. barMaxWidth: 30,
  588. data: dataList,
  589. })
  590. }
  591. }
  592. var myChart = echarts.init(document.getElementById("container"));
  593. totalHours = totalHours.toFixed(1);
  594. // 设置宽度
  595. myChart.resize({
  596. width: this.widthHtval
  597. })
  598. // 设置宽度
  599. _this.myChart = myChart;
  600. var option = {
  601. //总成本
  602. title: {
  603. // text: '工时成本总计' + totalMoneyCost.toFixed(2) + '元, 时长'+totalHours+'小时',
  604. text: '工时成本总计:' +
  605. ((this.permissions.countCost) ? '成本' + totalMoneyCost.toFixed(2) + '元,' : '') +
  606. ((this.permissions.countHours) ? '时长' + totalHours + '小时' : ''),
  607. left:'left',
  608. },
  609. // 工具箱
  610. legend: {
  611. x: 80,
  612. y: 10,
  613. data: yList,
  614. show: true,
  615.       top:"5%",//与上方的距离 可百分比% 可像素px
  616. },
  617. grid : {
  618. top : 80, //距离容器上边界40像素
  619. // bottom: 100, //距离容器下边界30像素
  620. bottom: 35, //距离容器下边界30像素
  621. left: 150,
  622. right: 150
  623. },
  624. toolbox: {
  625. show: true,
  626. feature:{
  627. saveAsImage:{
  628. show:true
  629. },
  630. restore:{
  631. show:true
  632. },
  633. // dataView:{
  634. // show:true
  635. // },
  636. // dataZoom:{
  637. // show:true
  638. // },
  639. magicType:{
  640. type:['line','bar']
  641. }
  642. }
  643. },
  644. tooltip:{
  645. trigger:'axis',
  646. formatter: function (params,ticket,callback) {
  647. var totalTime = 0;
  648. var totalCost = 0;
  649. var res = "";
  650. for(var i in params) {
  651. if (params[i].data.value > 0) {
  652. res += "<div style='margin-top:3px;font-size:12px;'><font color='#ddd'>项目名称:" + params[i].seriesName
  653. + "</font><br/>" +
  654. ((_this.permissions.countCost) ? "工作成本 : " + params[i].data.money + "元<br/>" : '') +
  655. ((_this.permissions.countHours) ? "工作时长 : " + params[i].data.cost + "小时</br>" : '') + "</div>";
  656. totalTime += Number(params[i].data.cost);
  657. totalCost += Number(params[i].data.money);
  658. }
  659. }
  660. res = res +'<br/>'+ params[0].name+ '<br/>总计: ' +
  661. ((_this.permissions.countHours) ? totalTime.toFixed(1) + '小时 ' : '') +
  662. ((_this.permissions.countCost) ? totalCost.toFixed(2) + "元" : '') +
  663. "<br/>";
  664. return res;
  665. }
  666. },
  667. xAxis: {
  668. data: xList,
  669. axisLabel: {
  670. interval:0,rotate:20
  671. }
  672. },
  673. yAxis: [{
  674. type : 'value',
  675. axisLabel: {
  676. formatter:this.yAxisValue==0?'{value} (元)':'{value} (小时)'
  677. }
  678. }],
  679. series: series,
  680. };
  681. myChart.setOption(option,{notMerge:true});
  682. },
  683. yanjiu() {
  684. console.log('触发')
  685. },
  686. getEchart(){
  687. var that = this
  688. // that.timers = setTimeout(()=>{
  689. // clearTimeout(that.timers)
  690. // console.log(that.timers)
  691. that.jieliu()
  692. // },100);
  693. // this.jieliu()
  694. // if(this.radio == '项目分类'){
  695. // this.getCategoryList()
  696. // }
  697. },
  698. getCategoryList(){
  699. this.http.post('/project-category/list',{},
  700. res => {
  701. if(res.code == 'ok'){
  702. this.categoryList = res.data
  703. }else {
  704. this.$message({
  705. message: res.msg,
  706. type: 'error'
  707. })
  708. }
  709. },err => {
  710. this.$message({
  711. message: err,
  712. type: 'error'
  713. })
  714. })
  715. },
  716. backToParentDept() {
  717. if (this.radio == '部门') {
  718. if (this.parentDeptStack.length > 0) {
  719. this.parentDeptStack.pop();
  720. if (this.parentDeptStack.length > 0) {
  721. this.parentDeptId = this.parentDeptStack[this.parentDeptStack.length -1];
  722. } else {
  723. this.parentDeptId = null;
  724. }
  725. this.jieliu();
  726. }
  727. }
  728. },
  729. // 脱离出来的方法
  730. jieliu() {
  731. sessionStorage.radio = this.radio;
  732. var _this = this;
  733. var param = {};
  734. if (this.dateRange != null) {
  735. param = {startDate:this.user.timeType.fixMonthcost==0?this.dateRange[0]:this.dateRange,
  736. endDate: this.user.timeType.fixMonthcost==0?this.dateRange[1]:this.dateRange};
  737. // console.log(param);
  738. }
  739. var url = '';
  740. if (this.radio=='项目') {
  741. url = this.port.project.listCost;
  742. } else if (this.radio=='项目分类') {
  743. url = '/project/getTimeCostByCategory';
  744. // param.parentDeptId = this.parentDeptId;
  745. // param.userId = this.user.id
  746. } else if (this.radio=='部门') {
  747. url = this.port.project.depCost;
  748. param.parentDeptId = this.parentDeptId;
  749. } else if (this.radio=='人员') {
  750. this.getUserCostList();
  751. return;
  752. } else if (this.radio == this.namess) {
  753. url = '/project/getDegreeCost'
  754. }
  755. this.http.post(url, param,
  756. res => {
  757. if (res.code == "ok") {
  758. // 更具数据的长度去加每个柱子的间距
  759. for(var i in res.data.costList) {
  760. if(i>20) {
  761. this.widthHtval = +this.widthHtval + 40
  762. } else {
  763. this.widthHtval = document.body.clientWidth - 230
  764. }
  765. }
  766. var xList = []
  767. var yList = []
  768. var list
  769. var totalMoneyCost;
  770. var totalHours = 0.0;
  771. if(this.radio == '项目' || this.radio == '项目分类' || this.radio=='部门') {
  772. list = res.data.costList
  773. totalMoneyCost = ((this.radio=='项目' || this.radio == '项目分类')?res.data.totalMoneyCost:res.data.totalCostMoney);
  774. for(var i in list) {
  775. if(this.radio=='项目') {
  776. xList.push(list[i].project);
  777. let item = {
  778. "value": this.yAxisValue==0?(list[i].costMoney ? list[i].costMoney.toFixed(2) : 0) || list[i].costMoney:(list[i].cost ? list[i].cost.toFixed(1) : 0),
  779. "id": list[i].id || i,
  780. }
  781. if(this.permissions.countCost){
  782. // item.money = list[i].costMoney.toFixed(2)
  783. item.money = (list[i].costMoney ? list[i].costMoney.toFixed(2) : 0)
  784. }
  785. if(this.permissions.countHours){
  786. item.cost = list[i].cost
  787. totalHours += parseFloat(list[i].cost);
  788. }
  789. yList.push(item);
  790. } else if(this.radio == '部门'){
  791. xList.push(list[i].departmentName);
  792. let item = {
  793. // "value": this.yAxisValue==0 ? list[i].costMoney.toFixed(2) || list[i].costMoney: list[i].costTime.toFixed(1),
  794. "value": this.yAxisValue==0 ? (list[i].costMoney ? list[i].costMoney.toFixed(2) : 0) || list[i].costMoney: (list[i].costTime ? list[i].costTime.toFixed(1) : 0),
  795. "id": list[i].departmentId,
  796. "hasSubDept": list[i].hasSubDept
  797. }
  798. if(this.permissions.countCost){
  799. // item.money = list[i].costMoney.toFixed(2)
  800. item.money = (list[i].costMoney ? list[i].costMoney.toFixed(2) : 0)
  801. }
  802. if(this.permissions.countHours){
  803. item.cost = list[i].costTime
  804. totalHours += parseFloat(list[i].costTime);
  805. }
  806. yList.push(item);
  807. }else {
  808. xList.push(list[i].categoryName);
  809. let item = {
  810. // "value": this.yAxisValue==0?list[i].costMoney.toFixed(2) || list[i].costMoney:list[i].cost.toFixed(1),
  811. "value": this.yAxisValue==0?(list[i].costMoney ? list[i].costMoney.toFixed(2) : 0) || list[i].costMoney:(list[i].cost ? list[i].cost.toFixed(1) : 0),
  812. "id": list[i].id || i,
  813. }
  814. if(this.permissions.countCost){
  815. // item.money = list[i].costMoney.toFixed(2)
  816. item.money = (list[i].costMoney ? list[i].costMoney.toFixed(2) : 0)
  817. }
  818. if(this.permissions.countHours){
  819. item.cost = list[i].cost
  820. totalHours += parseFloat(list[i].cost);
  821. }
  822. yList.push(item);
  823. }
  824. }
  825. } else {
  826. list = res.data
  827. for(var i in list) {
  828. // console.log(list[i].name, list[i].costMoney, list[i].cost)
  829. xList.push(list[i].name);
  830. let item = {
  831. "value": this.yAxisValue==0?list[i].costMoney:list[i].cost,
  832. "id": list[i].id || i,
  833. }
  834. if(this.permissions.countCost){
  835. // item.money = list[i].costMoney.toFixed(2)
  836. item.money = list[i].costMoney ? list[i].costMoney.toFixed(2) : 0
  837. totalMoneyCost += parseFloat(list[i].costMoney);
  838. }
  839. if(this.permissions.countHours){
  840. item.cost = list[i].cost
  841. totalHours += parseFloat(list[i].cost);
  842. }
  843. yList.push(item);
  844. }
  845. }
  846. totalHours = totalHours.toFixed(1);
  847. var myChart = echarts.init(document.getElementById("container"));
  848. myChart.resize({
  849. width: this.widthHtval
  850. })
  851. _this.myChart = myChart;
  852. // console.log(totalMoneyCost.toFixed(2), '看看', totalMoneyCost)
  853. // var chengbentongji = totalMoneyCost.toFixed(2) || totalMoneyCost
  854. if(totalMoneyCost) {
  855. this.zhishin = totalMoneyCost.toFixed(2)
  856. }
  857. if(this.radio == '项目' || this.radio == '人员' || this.radio == '项目分类' || this.radio=='部门') {
  858. var option = {
  859. title: {
  860. text: '工时成本总计:' + ((this.permissions.countCost) ? '成本' + this.zhishin + '元,' : '') + ((this.permissions.countHours) ? '时长' + totalHours + '小时' : ''),
  861. left:'left',
  862. },
  863. // 工具箱
  864. toolbox: {
  865. show: true,
  866. feature:{
  867. saveAsImage:{show:true},restore:{show:true}, magicType:{ type:['line','bar']},
  868. }
  869. },
  870. tooltip:{
  871. trigger:'axis',
  872. formatter: function (params,ticket,callback) {
  873. // var res = params[0].name + "<br/>工作成本"+" : " + params[0].data.money
  874. // + "元 <br/>工作时长"+" : " + params[0].data.cost + "小时";
  875. // _this.params = params;
  876. var res = params[0].name + "<br/>" +
  877. ((_this.permissions.countCost) ? "工作成本"+" : " + params[0].data.money
  878. + "元 <br/>" : '') +
  879. ((_this.permissions.countHours) ? "工作时长"+" : " + params[0].data.cost + "小时" : '');
  880. return res;
  881. }
  882. },
  883. xAxis: {
  884. data: xList,
  885. axisLabel: {
  886. interval:0,rotate:20
  887. }
  888. },
  889. yAxis: [{
  890. type : 'value',
  891. axisLabel: {
  892. formatter:this.yAxisValue==0?'{value} (元)':'{value}小时'
  893. }
  894. }],
  895. series: [{
  896. name: this.yAxisValue==0?'工作成本(元)':'工作时长(小时)',
  897. type: 'bar',
  898. barMaxWidth: 30,
  899. data: yList,
  900. }]
  901. };
  902. } else {
  903. var option = {
  904. title: {
  905. // text: '工时成本总计' + this.zhishin + '元, 时长'+totalHours+'小时',
  906. text: '工时成本总计:' + ((this.permissions.countCost) ? '成本' + this.zhishin + '元,' : '') + ((this.permissions.countHours) ? '时长' + totalHours + '小时' : ''),
  907. left:'left',
  908. },
  909. // 工具箱
  910. toolbox: {
  911. show: true,
  912. feature:{
  913. saveAsImage:{show:true},restore:{show:true}, magicType:{ type:['line','bar']},
  914. }
  915. },
  916. tooltip:{
  917. trigger:'axis',
  918. formatter: function (params,ticket,callback) {
  919. var res = params[0].name + "<br/>" +
  920. ((_this.permissions.countCost) ? "工作成本"+" : " + params[0].data.money
  921. + "元 <br/>" : '') +
  922. ((_this.permissions.countHours) ? "工作时长"+" : " + params[0].data.cost + "小时" : '');
  923. _this.params = params;
  924. return res;
  925. }
  926. },
  927. xAxis: {
  928. data: xList,
  929. axisLabel: {
  930. interval:0,rotate:20
  931. }
  932. },
  933. yAxis: [{
  934. type : 'value',
  935. axisLabel: {
  936. formatter:this.yAxisValue==0?'{value} (元)':'{value}小时'
  937. }
  938. }],
  939. series: [{
  940. name: this.yAxisValue==0?'工作成本(元)':'工作时长(小时)',
  941. type: 'bar',
  942. barMaxWidth: 30,
  943. data: yList,
  944. }]
  945. };
  946. }
  947. myChart.setOption(option,{notMerge: true});
  948. myChart.getZr().on('click', params => {
  949. const pointInPixel = [params.offsetX, params.offsetY];
  950. if (myChart.containPixel('grid', pointInPixel)) {
  951. console.log(_this.params)
  952. if(_this.radio=='项目') {
  953. if (_this.dateRange != null) {
  954. if (this.user.timeType.fixMonthcost == 0) {
  955. _this.$router.push("/cost/" + _this.params[0].data.id + "/" + _this.params[0].name
  956. +"?startDate="+_this.dateRange[0]+"&endDate="+_this.dateRange[1]);
  957. } else {
  958. _this.$router.push("/cost/" + _this.params[0].data.id + "/" + _this.params[0].name
  959. +"?startDate="+_this.dateRange+"&endDate="+_this.dateRange);
  960. }
  961. } else {
  962. _this.$router.push("/cost/" + _this.params[0].data.id + "/" + _this.params[0].name);
  963. }
  964. } else if (_this.radio=='部门') {
  965. if (_this.params[0].data.hasSubDept) {
  966. if (_this.parentDeptId != _this.params[0].data.id) {
  967. _this.parentDeptId = _this.params[0].data.id;
  968. _this.parentDeptStack.push(_this.parentDeptId);
  969. _this.jieliu();
  970. }
  971. // _this.jieliu();
  972. } else {
  973. if (_this.dateRange != null) {
  974. _this.$router.push("/costDep/" + _this.params[0].data.id + "/" + _this.params[0].name
  975. +"?startDate="+_this.dateRange[0]+"&endDate="+_this.dateRange[1]);
  976. } else {
  977. _this.$router.push("/costDep/" + _this.params[0].data.id + "/" + _this.params[0].name);
  978. }
  979. }
  980. }
  981. }
  982. });
  983. } else {
  984. this.$message({
  985. message: res.msg,
  986. type: "error"
  987. });
  988. }
  989. },
  990. error => {
  991. this.$message({
  992. message: error,
  993. type: "error"
  994. });
  995. });
  996. },
  997. // 左右滚动
  998. scrollFunction () {
  999. this.domObj = document.getElementById('clearfix') // 通过id获取要设置的div
  1000. if (this.domObj.attachEvent) { // IE
  1001. this.domObj.attachEvent('onmousewheel', this.mouseScroll)
  1002. } else if (this.domObj.addEventListener) {
  1003. this.domObj.addEventListener('DOMMouseScroll', this.mouseScroll, false)
  1004. }
  1005. this.domObj.onmousewheel = this.domObj.onmousewheel = this.mouseScroll
  1006. },
  1007. mouseScroll(event) { // google 浏览器下
  1008. let detail = event.wheelDelta || event.detail
  1009. let moveForwardStep = -1
  1010. let moveBackStep = 1
  1011. let step = 0
  1012. step = detail > 0 ? moveForwardStep * 100 : moveBackStep * 100
  1013. event.preventDefault() // 阻止浏览器默认事件
  1014. this.domObj.scrollLeft = this.domObj.scrollLeft + step
  1015. },
  1016. },
  1017. created() {
  1018. },
  1019. mounted() {
  1020. this.containerHeight = window.innerHeight - 200
  1021. // this.containerHeight = window.innerHeight - 130
  1022. const that = this;
  1023. window.onresize = function temp() {
  1024. this.containerHeight = window.innerHeight - 130
  1025. // this.containerHeight = window.innerHeight - 200
  1026. };
  1027. if(this.permissions.countCost){
  1028. this.yAxisValue = '0'
  1029. }else{
  1030. this.yAxisValue = '1'
  1031. }
  1032. if (this.user.timeType.fixMonthcost == 0) {
  1033. if (this.$route.query.startDate != null) {
  1034. this.dateRange = [this.$route.query.startDate, this.$route.query.endDate];
  1035. } else {
  1036. //默认查看本月
  1037. var now = new Date();
  1038. var t = util.formatDate.format(now, 'yyyy-MM-dd');
  1039. var startStr = util.formatDate.format(new Date(), 'yyyy-MM') + "-01";
  1040. this.dateRange = [startStr,t];
  1041. }
  1042. this.exportParam.dateRange = this.dateRange;
  1043. } else if (this.user.timeType.fixMonthcost == 1) {
  1044. if (this.$route.query.startDate != null) {
  1045. this.dateRange = this.$route.query.startDate;
  1046. } else {
  1047. //默认查看本月
  1048. var startStr = util.formatDate.format(new Date(), 'yyyy-MM');
  1049. this.dateRange = startStr;
  1050. }
  1051. this.exportParam.dateRange = this.dateRange;
  1052. }
  1053. this.radio = '项目'
  1054. this.getEchart();
  1055. var _this = this;
  1056. window.addEventListener("resize", function() {
  1057. _this.myChart.resize();
  1058. });
  1059. // this.getDepartment();
  1060. this.getMyProjectList();
  1061. this.getUsers()
  1062. this.jutishez()
  1063. this.scrollFunction()
  1064. },
  1065. };
  1066. </script>
  1067. <style lang="scss" scoped>
  1068. #container {
  1069. // display: inline-block;
  1070. display: block;
  1071. position: absolute;
  1072. // width: 100% !important;
  1073. margin-top: 60px;
  1074. }
  1075. .ryuans {
  1076. top: -50px;
  1077. }
  1078. .prompt {
  1079. position: absolute;
  1080. right: 10px;
  1081. top: 0;
  1082. }
  1083. </style>
  1084. <style lang="scss">
  1085. </style>