综合办公系统
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

settleStatistics.vue 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. <!--
  2. * @Author: ysh
  3. * @Date: 2024-10-18 11:17:48
  4. * @LastEditors: wrh
  5. * @LastEditTime: 2025-12-26 14:47:09
  6. -->
  7. <template>
  8. <div style="width:100%;" v-loading="loading">
  9. <div class="titles">结算和核算统计</div>
  10. <div class="warpper">
  11. <div class="left">
  12. <div class="left-top" id="settleBar"></div>
  13. <div class="left-top" id="settleTypePie" v-loading="dataLoading"></div>
  14. </div>
  15. <div class="middle">
  16. <div class="middle-top" id="settleLine"></div>
  17. <div class="middle-top" id="settleProject"></div>
  18. </div>
  19. </div>
  20. </div>
  21. </template>
  22. <script>
  23. import { echartsInit } from '@/utils/echarts'
  24. import { getSettleStatistic } from '@/api/oa/settle/settle';
  25. import { getCheckStatistic } from '@/api/oa/budget/check';
  26. let settleYearChart, settleAmountChart, settleTypeChart, settleProjectChart
  27. export default {
  28. props: {
  29. settleData: {
  30. type: Object,
  31. require: true,
  32. },
  33. checkData: {
  34. type: Object,
  35. require: true,
  36. },
  37. },
  38. watch: {
  39. settleType() {
  40. this.initSettleTypePie(this.activeYear)
  41. },
  42. settleData() {
  43. this.initSettleDatas();
  44. },
  45. checkData() {
  46. this.initCheckDatas();
  47. }
  48. },
  49. data() {
  50. return {
  51. loading: false,
  52. settleType: {},
  53. settleTypeAmount: {},
  54. settleYear: {},
  55. settleAmount: {},
  56. yearSettleProjectAmount: {},
  57. yearSettleProjectCount: {},
  58. yearCheckProjectAmount: {},
  59. yearCheckProjectCount: {},
  60. sumSettleAmount: 0,
  61. sumCheckAmount: 0,
  62. checkYear: {},
  63. checkAmount: {},
  64. dataLoading: false,
  65. activeYear: '',
  66. isYearView: true,
  67. settleMonth: {},
  68. settleMonthAmount: {},
  69. checkMonth: {},
  70. checkMonthAmount: {}
  71. }
  72. },
  73. created() {
  74. this.loading = true;
  75. this.initSettleDatas();
  76. this.initCheckDatas();
  77. },
  78. mounted() {
  79. this.initSettleYearBar();
  80. this.initSettleAmountLine();
  81. this.initSettleTypePie('');
  82. // this.initSettleTypeAmountPie('');
  83. this.initSettleProjectLineBar();
  84. },
  85. methods: {
  86. initSettleDatas() {
  87. if (Object.keys(this.settleData).length !== 0) {
  88. this.settleAmount = this.settleData.amount[0];
  89. this.settleType = this.settleData.type[0];
  90. this.settleTypeAmount = this.settleData.typeAmount[0];
  91. this.settleYear = this.settleData.year[0];
  92. this.yearSettleProjectAmount = this.settleData.yearProjectAmount[0];
  93. this.yearSettleProjectCount = this.settleData.yearProjectCount[0];
  94. if (this.settleData.month) {
  95. this.settleMonth = this.settleData.month[0];
  96. }
  97. if (this.settleData.monthAmount) {
  98. this.settleMonthAmount = this.settleData.monthAmount[0];
  99. }
  100. let sum = Object.values(this.settleAmount).reduce((accumulator, currentValue) => accumulator + currentValue, 0);
  101. this.sumSettleAmount = (sum / 10000).toFixed(2)
  102. this.loading = false;
  103. } else {
  104. this.loading = true;
  105. }
  106. },
  107. initCheckDatas() {
  108. if (Object.keys(this.checkData).length !== 0) {
  109. this.checkAmount = this.checkData.amount[0];
  110. this.checkYear = this.checkData.year[0];
  111. this.yearCheckProjectAmount = this.checkData.yearProjectAmount[0];
  112. this.yearCheckProjectCount = this.checkData.yearProjectCount[0];
  113. if (this.checkData.month) {
  114. this.checkMonth = this.checkData.month[0];
  115. }
  116. if (this.checkData.monthAmount) {
  117. this.checkMonthAmount = this.checkData.monthAmount[0];
  118. }
  119. let sum = Object.values(this.checkAmount).reduce((accumulator, currentValue) => accumulator + currentValue, 0);
  120. this.sumCheckAmount = (sum / 10000).toFixed(2)
  121. this.loading = false;
  122. } else {
  123. this.loading = true;
  124. }
  125. },
  126. clickYear(charts) {
  127. let that = this;
  128. charts.off('click');
  129. charts.on('click', function (event) {
  130. if (event) {
  131. if (event.name === '' || event.name === undefined) {
  132. that.isYearView = true;
  133. that.activeYear = '';
  134. that.initSettleYearBar();
  135. that.initSettleAmountLine();
  136. } else {
  137. that.activeYear = event.name;
  138. that.isYearView = false;
  139. that.dataLoading = true;
  140. Promise.all([
  141. getSettleStatistic({ gmTime: event.name + '-01-01' }),
  142. getCheckStatistic({ checkTime: event.name + '-01-01' })
  143. ]).then(([settleRes, checkRes]) => {
  144. // 处理结算数据
  145. if (settleRes.data.month) {
  146. that.settleMonth = settleRes.data.month[0];
  147. }
  148. if (settleRes.data.monthAmount) {
  149. that.settleMonthAmount = settleRes.data.monthAmount[0];
  150. }
  151. that.settleType = settleRes.data.type[0];
  152. that.settleTypeAmount = settleRes.data.typeAmount[0];
  153. if (settleRes.data.year) {
  154. that.settleYear = settleRes.data.year[0];
  155. }
  156. if (settleRes.data.amount) {
  157. that.settleAmount = settleRes.data.amount[0];
  158. }
  159. // 处理核算数据
  160. if (checkRes.data.month) {
  161. that.checkMonth = checkRes.data.month[0];
  162. }
  163. if (checkRes.data.monthAmount) {
  164. that.checkMonthAmount = checkRes.data.monthAmount[0];
  165. }
  166. if (checkRes.data.year) {
  167. that.checkYear = checkRes.data.year[0];
  168. }
  169. if (checkRes.data.amount) {
  170. that.checkAmount = checkRes.data.amount[0];
  171. }
  172. that.dataLoading = false;
  173. that.initSettleYearBar();
  174. that.initSettleAmountLine();
  175. })
  176. }
  177. }
  178. })
  179. },
  180. goBackToYear() {
  181. this.isYearView = true;
  182. this.activeYear = '';
  183. this.settleMonth = {};
  184. this.settleMonthAmount = {};
  185. this.checkMonth = {};
  186. this.checkMonthAmount = {};
  187. this.initSettleDatas();
  188. this.initCheckDatas();
  189. this.initSettleYearBar();
  190. this.initSettleAmountLine();
  191. },
  192. initSettleYearBar() {
  193. let xData = this.isYearView ? Object.keys(this.settleYear) : Object.keys(this.settleMonth);
  194. let settleData = this.isYearView ? Object.values(this.settleYear) : Object.values(this.settleMonth);
  195. let checkData = this.isYearView ? Object.values(this.checkYear) : Object.values(this.checkMonth);
  196. let titleText = this.isYearView ? '结算次数' : this.activeYear + '年结算次数';
  197. let settleSum = this.isYearView ? Object.values(this.settleYear).reduce((accumulator, currentValue) => accumulator + currentValue, 0) : Object.values(this.settleMonth).reduce((accumulator, currentValue) => accumulator + currentValue, 0);
  198. let checkSum = this.isYearView ? Object.values(this.checkYear).reduce((accumulator, currentValue) => accumulator + currentValue, 0) : Object.values(this.checkMonth).reduce((accumulator, currentValue) => accumulator + currentValue, 0);
  199. let subtext = '结算次数:' + settleSum + '次\n核算次数:' + checkSum + '次';
  200. let xName = this.isYearView ? '年' : '月';
  201. let option = {
  202. title: {
  203. text: titleText,
  204. subtext: subtext
  205. },
  206. grid: {
  207. left: '1%',
  208. right: '1%',
  209. bottom: '0%',
  210. height: '60%',
  211. containLabel: true
  212. },
  213. legend: {},
  214. graphic: this.isYearView ? [{
  215. type: 'text',
  216. name: '',
  217. right: '0',
  218. top: '20%',
  219. style: {
  220. text: '全部年份',
  221. fill: '#000',
  222. fontSize: 12,
  223. fontWeight: ''
  224. }
  225. }] : [{
  226. type: 'text',
  227. name: '',
  228. right: '0',
  229. top: '20%',
  230. style: {
  231. text: '返回',
  232. fill: '#409EFF',
  233. fontSize: 12,
  234. fontWeight: ''
  235. },
  236. onclick: () => { this.goBackToYear(); }
  237. }],
  238. tooltip: {
  239. trigger: 'axis',
  240. axisPointer: {
  241. type: 'none',
  242. label: {
  243. backgroundColor: '#6a7985'
  244. }
  245. }
  246. },
  247. xAxis: {
  248. name: xName,
  249. type: 'category',
  250. data: xData,
  251. },
  252. yAxis: {
  253. type: 'value',
  254. name: '单位:次'
  255. },
  256. series: [
  257. {
  258. name: "结算",
  259. data: settleData,
  260. type: 'bar',
  261. smooth: true,
  262. label: {
  263. show: true,
  264. position: 'top'
  265. },
  266. itemStyle: {
  267. color: '#3498DB'
  268. }
  269. },
  270. {
  271. name: "核算",
  272. data: checkData,
  273. type: 'bar',
  274. smooth: true,
  275. label: {
  276. show: true,
  277. position: 'top'
  278. },
  279. itemStyle: {
  280. color: '#46CB2B'
  281. }
  282. }
  283. ]
  284. };
  285. let charts = echartsInit(settleYearChart, 'settleBar', option);
  286. this.clickYear(charts)
  287. },
  288. initSettleAmountLine() {
  289. let xData = this.isYearView ? Object.keys(this.settleAmount) : Object.keys(this.settleMonthAmount);
  290. let settleData = this.isYearView ? Object.values(this.settleAmount) : Object.values(this.settleMonthAmount);
  291. let checkData = this.isYearView ? Object.values(this.checkAmount) : Object.values(this.checkMonthAmount);
  292. let titleText = this.isYearView ? '结算金额' : this.activeYear + '年结算金额';
  293. let settleSum = settleData.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
  294. let checkSum = checkData.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
  295. let option = {
  296. title: {
  297. text: titleText,
  298. subtext: '结算总额:' + settleSum.toFixed(2) + '元' +
  299. '(约' + (settleSum / 10000).toFixed(2) + '万元)' +
  300. "\n" +
  301. '核算总额:' + checkSum.toFixed(2) + '元' +
  302. '(约' + (checkSum / 10000).toFixed(2) + '万元)'
  303. },
  304. grid: {
  305. left: '1%',
  306. right: '1%',
  307. bottom: '0%',
  308. height: '60%',
  309. containLabel: true
  310. },
  311. legend: {
  312. right: '0%',
  313. orient: 'vertical'
  314. },
  315. tooltip: {
  316. trigger: 'axis',
  317. axisPointer: {
  318. type: 'none',
  319. label: {
  320. backgroundColor: '#6a7985'
  321. }
  322. }
  323. },
  324. xAxis: {
  325. type: 'category',
  326. data: xData,
  327. },
  328. yAxis: {
  329. type: 'value',
  330. name: '单位:元'
  331. },
  332. series: [
  333. {
  334. name: "结算",
  335. data: settleData,
  336. type: 'line',
  337. smooth: true,
  338. label: {
  339. show: true,
  340. position: 'top',
  341. formatter: function (params) {
  342. let value = params.value;
  343. if (value >= 10000) {
  344. return '约' + (value / 10000).toFixed(2) + '万元'
  345. }
  346. }
  347. }
  348. },
  349. {
  350. name: "核算",
  351. data: checkData,
  352. type: 'line',
  353. smooth: true,
  354. label: {
  355. show: true,
  356. position: 'top',
  357. formatter: function (params) {
  358. let value = params.value;
  359. if (value >= 10000) {
  360. return '约' + (value / 10000).toFixed(2) + '万元'
  361. }
  362. }
  363. }
  364. }
  365. ]
  366. };
  367. echartsInit(settleAmountChart, 'settleLine', option);
  368. },
  369. initSettleTypePie(year) {
  370. let option = {
  371. title: {
  372. text: year + '结算类型占比',
  373. },
  374. tooltip: {
  375. trigger: 'item',
  376. formatter: '{a} <br/>{b} : {c} ({d}%)'
  377. },
  378. legend: {
  379. top: 'bottom'
  380. },
  381. series: [
  382. {
  383. name: '占比',
  384. type: 'pie',
  385. radius: '30%',
  386. center: ['25%', '50%'],
  387. avoidLabelOverlap: false,
  388. data: Object.entries(this.settleType).map(([key, value]) => {
  389. return { name: key, value: value }
  390. }),
  391. emphasis: {
  392. itemStyle: {
  393. shadowBlur: 10,
  394. shadowOffsetX: 0,
  395. shadowColor: 'rgba(0, 0, 0, 0.5)'
  396. }
  397. },
  398. label: {
  399. alignTo: 'none',
  400. formatter: '{name|{b}}\n{value|{c}个}',
  401. minMargin: 5,
  402. edgeDistance: 8,
  403. lineHeight: 15,
  404. rich: {
  405. time: {
  406. fontSize: 10,
  407. color: '#999'
  408. }
  409. }
  410. },
  411. },
  412. {
  413. name: '占比',
  414. type: 'pie',
  415. radius: '30%',
  416. center: ['65%', '50%'],
  417. avoidLabelOverlap: false,
  418. data: Object.entries(this.settleTypeAmount).map(([key, value]) => {
  419. return { name: key, value: value }
  420. }),
  421. emphasis: {
  422. itemStyle: {
  423. shadowBlur: 10,
  424. shadowOffsetX: 0,
  425. shadowColor: 'rgba(0, 0, 0, 0.5)'
  426. }
  427. },
  428. label: {
  429. alignTo: 'none',
  430. formatter: '{name|{b}}\n{value|{c}元}',
  431. minMargin: 5,
  432. edgeDistance: 8,
  433. lineHeight: 15,
  434. rich: {
  435. time: {
  436. fontSize: 10,
  437. color: '#999'
  438. }
  439. }
  440. },
  441. },
  442. ]
  443. };
  444. echartsInit(settleTypeChart, 'settleTypePie', option);
  445. },
  446. initSettleProjectLineBar(year) {
  447. let option = {
  448. title: {
  449. text: '项目结算金额',
  450. },
  451. tooltip: {
  452. trigger: 'axis',
  453. },
  454. grid: {
  455. left: '1%',
  456. right: '1%',
  457. bottom: '0%',
  458. containLabel: true
  459. },
  460. legend: {
  461. right: '0%',
  462. orient: 'vertical'
  463. },
  464. xAxis: [
  465. {
  466. type: 'category',
  467. data: Object.keys(this.yearSettleProjectAmount),
  468. axisPointer: {
  469. type: 'shadow'
  470. }
  471. }
  472. ],
  473. yAxis: [
  474. {
  475. type: 'value',
  476. name: '单位:元',
  477. }
  478. ],
  479. series: [
  480. {
  481. name: "结算",
  482. type: 'line',
  483. tooltip: {
  484. valueFormatter: function (value) {
  485. return value + ' 元';
  486. }
  487. },
  488. smooth: true,
  489. data: Object.values(this.yearSettleProjectAmount),
  490. label: {
  491. show: true,
  492. position: 'top',
  493. formatter: function (params) {
  494. let value = params.value;
  495. if (value >= 10000) {
  496. return '约' + (value / 10000).toFixed(2) + '万元'
  497. }
  498. }
  499. }
  500. },
  501. {
  502. name: "核算",
  503. type: 'line',
  504. tooltip: {
  505. valueFormatter: function (value) {
  506. return value + ' 元';
  507. }
  508. },
  509. smooth: true,
  510. data: Object.values(this.yearCheckProjectAmount),
  511. label: {
  512. show: true,
  513. position: 'top',
  514. formatter: function (params) {
  515. let value = params.value;
  516. if (value >= 10000) {
  517. return '约' + (value / 10000).toFixed(2) + '万元'
  518. }
  519. }
  520. }
  521. }
  522. ]
  523. };
  524. echartsInit(settleProjectChart, 'settleProject', option);
  525. },
  526. },
  527. }
  528. </script>
  529. <style lang="scss" scoped>
  530. .titles {
  531. font-family: 'puhuiti';
  532. font-size: 16px;
  533. margin-bottom: 16px;
  534. }
  535. .warpper {
  536. width: 100%;
  537. height: 100%;
  538. display: flex;
  539. .left {
  540. flex: 1;
  541. .left-top {
  542. width: 100%;
  543. height: 300px;
  544. padding: 10px;
  545. border: 1px solid #ececec;
  546. }
  547. }
  548. .middle {
  549. flex: 1;
  550. .middle-top {
  551. width: 100%;
  552. height: 300px;
  553. padding: 10px;
  554. border: 1px solid #ececec;
  555. }
  556. }
  557. .right {
  558. // flex: 1;
  559. .right-top {
  560. width: 100%;
  561. height: 300px;
  562. padding: 10px;
  563. border: 1px solid #ececec;
  564. }
  565. }
  566. }
  567. </style>