cost.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. <template>
  2. <section>
  3. <el-col :span="24" style="padding-bottom: 0px;text-align:center;margin-top:10px;">
  4. <el-date-picker
  5. v-model="dateRange" :editable="false"
  6. format="yyyy-MM-dd" value-format="yyyy-MM-dd"
  7. @change="getEchart"
  8. :clearable="true"
  9. range-separator="至"
  10. type="daterange"
  11. start-placeholder="开始日期"
  12. end-placeholder="结束日期"
  13. ></el-date-picker>
  14. <el-radio-group v-model="radio" @change="getEchart" style="margin-left:10px;">
  15. <el-radio-button label="项目"></el-radio-button>
  16. <el-radio-button label="部门"></el-radio-button>
  17. <el-radio-button label="人员"></el-radio-button>
  18. </el-radio-group>
  19. </el-col>
  20. <div id="container" :style="'height:'+containerHeight+'px;'"></div>
  21. <div style="position:fixed;top:120px;right:150px;"><el-button @click="showExportDialog">报表导出</el-button></div>
  22. <!--导出报表条件选择 -->
  23. <el-dialog title="工时报表导出" v-if="exportDialog" :visible.sync="exportDialog" :close-on-click-modal="false" customClass="customWidth" width="500px">
  24. <el-form ref="form3" :model="exportParam" >
  25. <el-form-item prop="projectId" label="选择项目" v-if="radio != '人员'">
  26. <el-select v-model="exportParam.projectId" placeholder="全部项目" clearable style="width:350px;" filterable="true">
  27. <el-option v-for="item in projectList" :key="item.id" :label="item.projectName" :value="item.id"></el-option>
  28. </el-select>
  29. </el-form-item>
  30. <el-form-item prop="userIds" label="选择人员" v-if="radio == '人员'">
  31. <el-select v-model="exportParam.userIds" placeholder="全部人员" multiple="true" clearable style="width:350px;" filterable="true">
  32. <el-option v-for="item in hasReportUserList" :key="item.id" :label="item.name" :value="item.id"></el-option>
  33. </el-select>
  34. </el-form-item>
  35. <el-form-item prop="projectId" label="日期范围">
  36. <el-date-picker
  37. v-model="exportParam.dateRange" :editable="false"
  38. format="yyyy-MM-dd" value-format="yyyy-MM-dd"
  39. :clearable="true"
  40. range-separator="至"
  41. type="daterange"
  42. start-placeholder="开始日期"
  43. end-placeholder="结束日期"
  44. ></el-date-picker>
  45. </el-form-item>
  46. </el-form>
  47. <div slot="footer" class="dialog-footer">
  48. <el-button type="primary" @click="exportProjectData" style="width:100%;" >导出</el-button>
  49. </div>
  50. </el-dialog>
  51. </section>
  52. </template>
  53. <script>
  54. import util from "../../common/js/util";
  55. export default {
  56. data() {
  57. return {
  58. hasReportUserList:[],
  59. projectList:[],
  60. exportParam:{projectId:null,dateRange:[]},
  61. exportDialog:false,
  62. dateRange:[],
  63. user: JSON.parse(sessionStorage.getItem("user")),
  64. radio: sessionStorage.radio!=null?sessionStorage.radio:'项目',
  65. containerHeight: 0,
  66. myChart: null,
  67. params: null,
  68. };
  69. },
  70. methods: {
  71. showExportDialog() {
  72. this.exportDialog = true;
  73. this.exportParam.dateRange = this.dateRange;
  74. if (this.radio == '人员') {
  75. // this.exportParam.userIds = [];
  76. }
  77. },
  78. //获取我的项目列表
  79. getMyProjectList() {
  80. this.http.post('/project/getProjectList', {
  81. },
  82. res => {
  83. if (res.code == "ok") {
  84. this.projectList = res.data;
  85. } else {
  86. this.$message({
  87. message: res.msg,
  88. type: "error"
  89. });
  90. }
  91. },
  92. error => {
  93. this.$message({
  94. message: error,
  95. type: "error"
  96. });
  97. });
  98. },
  99. exportProjectData() {
  100. var param = {};
  101. if (this.exportParam.dateRange != null) {
  102. param = {startDate:this.exportParam.dateRange[0], endDate: this.exportParam.dateRange[1]};
  103. }
  104. var url = "/project/exportTimeCost";
  105. var fileName = '项目工时成本统计.xls';
  106. if (this.radio == '人员' ) {
  107. console.log(this.exportParam.userIds);
  108. fileName = '人员工时成本统计.xls';
  109. url = '/department/exportUserStatistic';
  110. if (this.exportParam.userIds != null && this.exportParam.userIds.length > 0) {
  111. var ids = '';
  112. this.exportParam.userIds.forEach(u=>{
  113. ids += u+',';
  114. })
  115. param.userIds = ids;
  116. }
  117. }
  118. if (this.exportParam.projectId != null) {
  119. param.projectId = this.exportParam.projectId;
  120. }
  121. this.http.post(url, param,
  122. res => {
  123. this.listLoading = false;
  124. if (res.code == "ok") {
  125. this.exportDialog = false;
  126. var aTag = document.createElement('a');
  127. aTag.download = fileName;
  128. aTag.href = res.data;
  129. aTag.click();
  130. } else {
  131. this.$message({
  132. message: res.msg,
  133. type: "error"
  134. });
  135. }
  136. },
  137. error => {
  138. this.listLoading = false;
  139. this.$message({
  140. message: error,
  141. type: "error"
  142. });
  143. });
  144. },
  145. //获取人员成本统计列表
  146. getUserCostList() {
  147. this.listLoading = true;
  148. this.http.post(this.port.project.userCost, {
  149. startDate:this.dateRange[0], endDate: this.dateRange[1]
  150. },
  151. res => {
  152. this.listLoading = false;
  153. var _this = this;
  154. this.hasReportUserList = [];
  155. if (res.code == "ok") {
  156. //工时总成本
  157. this.hasReportUserList = res.data.userList;
  158. var xList = [] , yList = [] , list = res.data.list, array = [] , series = [];
  159. var totalMoneyCost = res.data.totalCostMoney;
  160. if (list.length > 0) {
  161. var num = list.length==0?0:list[0].project.length;
  162. for(var i in list) {
  163. xList.push(list[i].name);
  164. var pro = list[i].project;
  165. for(var j in pro) {
  166. if(array.indexOf(pro[j].project) == -1) {
  167. array.push(pro[j].project)
  168. }
  169. }
  170. }
  171. for(var i in array) {
  172. yList.push(array[i]);
  173. var dataList = [];
  174. for(var j in list) {
  175. var project = list[j].project , num = 0;
  176. if(project.length != 0) {
  177. for(var k in project) {
  178. if(project[k].project == array[i]) {
  179. dataList.push({
  180. "value": project[k].money,
  181. "cost": project[k].time,
  182. })
  183. } else {
  184. num++;
  185. }
  186. if(k == project.length-1 && num != project.length-1) {
  187. dataList.push({
  188. "value": 0,
  189. "cost": 0,
  190. })
  191. }
  192. }
  193. } else {
  194. dataList.push({
  195. "value": 0,
  196. "cost": 0,
  197. })
  198. }
  199. }
  200. series.push({
  201. name: array[i],
  202. type: 'bar',
  203. stack:'1',
  204. barMaxWidth: 30,
  205. data: dataList,
  206. })
  207. }
  208. }
  209. var myChart = echarts.init(document.getElementById("container"));
  210. _this.myChart = myChart;
  211. var option = {
  212. //总成本
  213. title: {
  214. text: '工时成本总计' + totalMoneyCost.toFixed(2) + '元',
  215. left:'left',
  216. },
  217. // 工具箱
  218. legend: {
  219. x: 80,
  220. y: 10,
  221. data: yList,
  222. show: true,
  223.        top:"5%",//与上方的距离 可百分比% 可像素px
  224. },
  225. grid : {
  226. top : 80, //距离容器上边界40像素
  227. bottom: 35 //距离容器下边界30像素
  228. },
  229. toolbox: {
  230. show: true,
  231. feature:{
  232. saveAsImage:{
  233. show:true
  234. },
  235. restore:{
  236. show:true
  237. },
  238. // dataView:{
  239. // show:true
  240. // },
  241. // dataZoom:{
  242. // show:true
  243. // },
  244. magicType:{
  245. type:['line','bar']
  246. }
  247. }
  248. },
  249. tooltip:{
  250. trigger:'axis',
  251. formatter: function (params,ticket,callback) {
  252. var totalTime = 0;
  253. var totalCost = 0;
  254. var res = "";
  255. for(var i in params) {
  256. if (params[i].data.value > 0) {
  257. res += "<div style='margin-top:3px;font-size:12px;'><font color='#ddd'>项目名称:" + params[i].seriesName
  258. + "</font><br/>工作成本 : " + params[i].data.value
  259. + "元 <br/>工作时长"+" : " + params[i].data.cost + "小时</br></div>";
  260. totalTime += Number(params[i].data.cost);
  261. totalCost += Number(params[i].data.value);
  262. }
  263. }
  264. res = res +'<br/>'+ params[0].name+ '<br/>总计: ' + totalTime.toFixed(1)+'小时 '+totalCost.toFixed(2) + "元<br/>";
  265. return res;
  266. }
  267. },
  268. xAxis: {
  269. data: xList,
  270. axisLabel: {
  271. interval:0,rotate:20
  272. }
  273. },
  274. yAxis: [{
  275. type : 'value',
  276. axisLabel: {
  277. formatter:'{value} (元)'
  278. }
  279. }],
  280. series: series,
  281. };
  282. myChart.setOption(option,{notMerge:true});
  283. } else {
  284. this.$message({
  285. message: res.msg,
  286. type: "error"
  287. });
  288. }
  289. },
  290. error => {
  291. this.listLoading = false;
  292. this.$message({
  293. message: error,
  294. type: "error"
  295. });
  296. });
  297. },
  298. getEchart(){
  299. sessionStorage.radio = this.radio;
  300. var _this = this;
  301. var param = {};
  302. if (this.dateRange != null) {
  303. param = {startDate:this.dateRange[0], endDate: this.dateRange[1]};
  304. }
  305. var url = '';
  306. if (this.radio=='项目') {
  307. url = this.port.project.listCost;
  308. } else if (this.radio=='部门') {
  309. url = this.port.project.depCost;
  310. } else if (this.radio=='人员') {
  311. this.getUserCostList();
  312. return;
  313. }
  314. this.http.post(url, param,
  315. res => {
  316. if (res.code == "ok") {
  317. var xList = [], yList = [], list = res.data.costList,
  318. totalMoneyCost = ((this.radio=='项目' || this.radio=='人员')?res.data.totalMoneyCost:res.data.totalCostMoney);
  319. for(var i in list) {
  320. if(this.radio=='项目' || this.radio == '人员') {
  321. xList.push(this.radio=='项目'?list[i].project:list[i].name);
  322. yList.push({
  323. "value": list[i].costMoney,
  324. "id": list[i].id,
  325. "cost": list[i].cost
  326. });
  327. } else {
  328. xList.push(list[i].departmentName);
  329. yList.push({
  330. "value": list[i].costMoney,
  331. "id": list[i].departmentId,
  332. "cost": list[i].costTime
  333. });
  334. }
  335. }
  336. var myChart = echarts.init(document.getElementById("container"));
  337. _this.myChart = myChart;
  338. var option = {
  339. title: {
  340. text: '工时成本总计' + totalMoneyCost.toFixed(2) + '元',
  341. left:'left',
  342. },
  343. // 工具箱
  344. toolbox: {
  345. show: true,
  346. feature:{
  347. saveAsImage:{
  348. show:true
  349. },
  350. restore:{
  351. show:true
  352. },
  353. // dataView:{
  354. // show:true
  355. // },
  356. // dataZoom:{
  357. // show:true
  358. // },
  359. magicType:{
  360. type:['line','bar']
  361. },
  362. }
  363. },
  364. tooltip:{
  365. trigger:'axis',
  366. formatter: function (params,ticket,callback) {
  367. var res = params[0].name + "<br/>工作成本"+" : " + params[0].data.value
  368. + "元 <br/>工作时长"+" : " + params[0].data.cost + "小时";
  369. _this.params = params;
  370. return res;
  371. }
  372. },
  373. xAxis: {
  374. data: xList,
  375. axisLabel: {
  376. interval:0,rotate:20
  377. }
  378. },
  379. yAxis: [{
  380. type : 'value',
  381. axisLabel: {
  382. formatter:'{value} (元)'
  383. }
  384. }],
  385. series: [{
  386. name: '工作成本(元)',
  387. type: 'bar',
  388. barMaxWidth: 30,
  389. data: yList,
  390. }]
  391. };
  392. myChart.setOption(option,{notMerge: true});
  393. myChart.getZr().on('click', params => {
  394. const pointInPixel = [params.offsetX, params.offsetY];
  395. if (myChart.containPixel('grid', pointInPixel)) {
  396. console.log(_this.params)
  397. if(_this.radio=='项目') {
  398. if (_this.dateRange != null) {
  399. _this.$router.push("/cost/" + _this.params[0].data.id + "/" + _this.params[0].name
  400. +"?startDate="+_this.dateRange[0]+"&endDate="+_this.dateRange[1]);
  401. } else {
  402. _this.$router.push("/cost/" + _this.params[0].data.id + "/" + _this.params[0].name);
  403. }
  404. } else if (_this.radio=='部门') {
  405. if (_this.dateRange != null) {
  406. _this.$router.push("/costDep/" + _this.params[0].data.id + "/" + _this.params[0].name
  407. +"?startDate="+_this.dateRange[0]+"&endDate="+_this.dateRange[1]);
  408. } else {
  409. _this.$router.push("/costDep/" + _this.params[0].data.id + "/" + _this.params[0].name);
  410. }
  411. }
  412. }
  413. });
  414. } else {
  415. this.$message({
  416. message: res.msg,
  417. type: "error"
  418. });
  419. }
  420. },
  421. error => {
  422. this.$message({
  423. message: error,
  424. type: "error"
  425. });
  426. });
  427. },
  428. },
  429. created() {
  430. },
  431. mounted() {
  432. this.containerHeight = window.innerHeight - 140
  433. const that = this;
  434. window.onresize = function temp() {
  435. this.containerHeight = window.innerHeight - 100
  436. };
  437. if (this.$route.query.startDate != null) {
  438. this.dateRange = [this.$route.query.startDate, this.$route.query.endDate];
  439. } else {
  440. //默认查看本月
  441. var now = new Date();
  442. var t = util.formatDate.format(now, 'yyyy-MM-dd');
  443. var startStr = util.formatDate.format(new Date(), 'yyyy-MM') + "-01";
  444. this.dateRange = [startStr,t];
  445. }
  446. this.exportParam.dateRange = this.dateRange;
  447. this.getEchart();
  448. var _this = this;
  449. window.addEventListener("resize", function() {
  450. _this.myChart.resize();
  451. });
  452. this.getMyProjectList();
  453. }
  454. };
  455. </script>
  456. <style lang="scss" scoped>
  457. #container {
  458. display: inline-block;
  459. width: 100%;
  460. margin-top: 10px;
  461. }
  462. </style>
  463. <style lang="scss">
  464. </style>