TaskCompleteStatistics.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <template>
  2. <div
  3. v-loading="loading"
  4. class="main-container">
  5. <filtrate-handle-view
  6. :show-year-select="true"
  7. :show-dept-select="true"
  8. :show-user-select="true"
  9. title="业绩目标完成情况"
  10. class="filtrate-bar"
  11. @load="loading=true"
  12. @change="handleToFilter">
  13. <el-select
  14. slot="after-time"
  15. v-model="typeSelect"
  16. class="type-select">
  17. <el-option
  18. v-for="item in [{label:'合同金额', value:1},{label:'回款金额', value:2}]"
  19. :key="item.value"
  20. :label="item.label"
  21. :value="item.value" />
  22. </el-select>
  23. </filtrate-handle-view>
  24. <div class="content">
  25. <div class="axis-content">
  26. <div id="axismain" />
  27. </div>
  28. <div class="table-content">
  29. <div class="handle-bar">
  30. <el-button
  31. class="export-btn"
  32. @click="exportClick">导出</el-button>
  33. </div>
  34. <el-table
  35. v-if="showTable"
  36. :data="list"
  37. :class="WKConfig.tableStyle.class"
  38. :stripe="WKConfig.tableStyle.stripe"
  39. height="300"
  40. highlight-current-row>
  41. <el-table-column
  42. v-for="(item, index) in fieldList"
  43. :key="index"
  44. :prop="item.field"
  45. :label="item.name"
  46. align="center"
  47. header-align="center"
  48. show-overflow-tooltip>
  49. <template v-if="item.children">
  50. <el-table-column
  51. v-for="(child, childIndex) in item.children"
  52. :key="childIndex"
  53. :prop="child.field"
  54. :label="child.name"
  55. align="center"
  56. header-align="center"
  57. show-overflow-tooltip />
  58. </template>
  59. </el-table-column>
  60. </el-table>
  61. </div>
  62. </div>
  63. </div>
  64. </template>
  65. <script>
  66. import {
  67. biAchievementStatisticsAPI,
  68. biAchievementStatisticsExportAPI
  69. } from '@/api/bi/bi'
  70. import BaseMixin from './mixins/Base'
  71. import SortMixin from './mixins/Sort'
  72. import * as echarts from 'echarts'
  73. import { floatAdd } from '@/utils'
  74. import { debounce } from 'throttle-debounce'
  75. export default {
  76. /** 业绩目标完成情况 */
  77. name: 'TaskCompleteStatistics',
  78. mixins: [BaseMixin, SortMixin],
  79. data() {
  80. return {
  81. pickerOptions: {
  82. disabledDate(time) {
  83. return time.getTime() > Date.now()
  84. }
  85. },
  86. loading: false,
  87. postParams: {},
  88. dateSelect: '', // 选择的date
  89. typeSelect: 1, // 类型选择 1销售(目标)2回款(目标)
  90. // 部门员工
  91. dataSelect: 1,
  92. // 部门员工选择值
  93. deptSelectValue: '',
  94. userSelectValue: '',
  95. list: [],
  96. fieldList: [],
  97. axisChart: null, // 柱状图
  98. axisOption: null
  99. }
  100. },
  101. mounted() {
  102. this.debouncedResize = debounce(300, this.resizeFn)
  103. this.$nextTick(() => {
  104. window.addEventListener('resize', this.debouncedResize)
  105. })
  106. this.initAxis()
  107. const keysName = ['名称', '年度目标', '第一季度', '1月', '2月', '3月', '第二季度', '4月', '5月', '6月', '第三季度', '7月', '8月', '9月', '第四季度', '10月', '11月', '12月']
  108. const keys = ['name', 'Year', 'Quarter1', '1', '2', '3', 'Quarter2', '4', '5', '6', 'Quarter3', '7', '8', '9', 'Quarter4', '10', '11', '12']
  109. for (let index = 0; index < keysName.length; index++) {
  110. const key = keysName[index]
  111. if (index === 0) {
  112. this.fieldList.push({ field: keys[index], name: key })
  113. } else {
  114. const children = [
  115. { field: `achievement${keys[index]}`, name: '目标' },
  116. { field: `money${keys[index]}`, name: '完成' },
  117. { field: `rate${keys[index]}`, name: '完成率' }
  118. ]
  119. this.fieldList.push({ field: '', name: key, children: children })
  120. }
  121. }
  122. },
  123. beforeDestroy() {
  124. window.removeEventListener('resize', this.debouncedResize)
  125. },
  126. methods: {
  127. /**
  128. * 窗口尺寸发生改变实时调整 EChart 尺寸
  129. */
  130. resizeFn() {
  131. if (this.axisChart) {
  132. this.axisChart.resize()
  133. }
  134. },
  135. getDataList() {
  136. this.loading = true
  137. biAchievementStatisticsAPI(this.postParams)
  138. .then(res => {
  139. this.refreshTableHeadAndChartInfo()
  140. if (res.data && res.data.length > 0) {
  141. this.list = []
  142. // 月份合计list
  143. const sumList = []
  144. for (let index = 0; index < 12; index++) {
  145. sumList.push({
  146. achievement: 0,
  147. money: 0
  148. })
  149. }
  150. for (let index = 0; index < res.data.length; index++) {
  151. const element = res.data[index]
  152. // 循环出表头展示字段 注入element
  153. // 一条数据的开始 季度数据
  154. let quarter = {
  155. achievement: 0,
  156. money: 0
  157. }
  158. // 年数据
  159. const year = {
  160. achievement: 0,
  161. money: 0
  162. }
  163. for (let childIndex = 0; childIndex < element.biMonthReceivedMoneyVOS.length; childIndex++) {
  164. const child = element.biMonthReceivedMoneyVOS[childIndex]
  165. // 表展示数据
  166. const keys = ['achievement', 'rate', 'money']
  167. // eslint-disable-next-line no-unused-vars
  168. for (const key of keys) {
  169. const childValue = child[key]
  170. element[`${key}${childIndex + 1}`] = childValue
  171. if (quarter.hasOwnProperty(key)) {
  172. quarter[key] = floatAdd(quarter[key], childValue)
  173. year[key] = floatAdd(year[key], childValue)
  174. }
  175. }
  176. // 获取季度值
  177. if (childIndex % 3 === 2) {
  178. const quarterIndex = parseInt(childIndex / 3) + 1
  179. element[`achievementQuarter${quarterIndex}`] = quarter.achievement
  180. element[`moneyQuarter${quarterIndex}`] = quarter.money
  181. element[`rateQuarter${quarterIndex}`] = quarter.money ? (quarter.money / quarter.achievement * 100 + 0.001).toFixed(2).toString() : '0.00'
  182. // 重置到新季度初始值
  183. quarter = {
  184. achievement: 0,
  185. money: 0
  186. }
  187. }
  188. // 合计数据
  189. const sumItem = sumList[childIndex]
  190. sumItem.achievement = floatAdd(sumItem.achievement, child.achievement)
  191. sumItem.money = floatAdd(sumItem.money, child.money)
  192. }
  193. // 获取年
  194. element['achievementYear'] = year.achievement
  195. element['moneyYear'] = year.money
  196. element['rateYear'] = year.money ? (year.money / year.achievement * 100 + 0.001).toFixed(2).toString() : '0.00'
  197. this.list.push(element)
  198. }
  199. const receivables = []
  200. const achievements = []
  201. const rates = []
  202. for (let index = 0; index < sumList.length; index++) {
  203. const element = sumList[index]
  204. receivables.push(element.money)
  205. achievements.push(element.achievement)
  206. if (element.achievement) {
  207. rates.push(element.money ? (element.money / element.achievement * 100 + 0.001).toFixed(2).toString() : '0.00')
  208. } else {
  209. rates.push('--')
  210. }
  211. }
  212. this.axisOption.series[0].data = receivables
  213. this.axisOption.series[1].data = achievements
  214. this.axisOption.series[2].data = rates
  215. this.axisChart.setOption(this.axisOption, true)
  216. } else {
  217. this.list = []
  218. this.axisOption.series[0].data = []
  219. this.axisOption.series[1].data = []
  220. this.axisOption.series[2].data = []
  221. this.axisChart.setOption(this.axisOption, true)
  222. }
  223. this.loading = false
  224. })
  225. .catch(() => {
  226. this.loading = false
  227. })
  228. },
  229. /**
  230. * 去搜索
  231. */
  232. handleToFilter(params, others = {}) {
  233. // 通过 isUser 来判断选择的是员工还是部门
  234. params.module = this.typeSelect
  235. params.isUser = params.userList ? 1 : 0
  236. this.postParams = params
  237. this.getDataList()
  238. },
  239. /**
  240. * 刷新表头和图标关键字
  241. */
  242. refreshTableHeadAndChartInfo() {
  243. const typeName = this.typeSelect === 1 ? '合同金额' : '回款金额'
  244. this.fieldList[1].name = typeName + '(元)'
  245. this.axisOption.legend.data[0] = typeName
  246. this.axisOption.series[0].name = typeName
  247. },
  248. /** 柱状图 */
  249. initAxis() {
  250. const axisChart = echarts.init(document.getElementById('axismain'))
  251. const option = {
  252. color: ['#0065FF', '#00B8D9', '#FF5630'],
  253. tooltip: {
  254. trigger: 'axis',
  255. axisPointer: {
  256. // 坐标轴指示器,坐标轴触发有效
  257. type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
  258. }
  259. },
  260. legend: {
  261. ...this.chartDefaultOptions.legend,
  262. data: ['合同金额', '目标', '完成率']
  263. },
  264. grid: this.chartDefaultOptions.grid,
  265. xAxis: [
  266. {
  267. ...this.chartXAxisStyle,
  268. type: 'category',
  269. data: Array.from({ length: 12 }, (o, i) => `${i + 1}月`)
  270. }
  271. ],
  272. yAxis: [
  273. {
  274. ...this.getChartYAxisStyle({
  275. axisLabel: {
  276. formatter: '{value}元'
  277. }
  278. }),
  279. type: 'value',
  280. name: '合同金额'
  281. },
  282. {
  283. ...this.getChartYAxisStyle({
  284. axisLabel: {
  285. formatter: '{value}%'
  286. }
  287. }),
  288. type: 'value',
  289. name: '完成率'
  290. }
  291. ],
  292. series: [
  293. {
  294. name: '合同金额',
  295. type: 'bar',
  296. yAxisIndex: 0,
  297. barMaxWidth: 15,
  298. label: this.chartDefaultBase.label,
  299. data: []
  300. },
  301. {
  302. name: '目标',
  303. type: 'bar',
  304. yAxisIndex: 0,
  305. barMaxWidth: 15,
  306. label: this.chartDefaultBase.label,
  307. data: []
  308. },
  309. {
  310. ...this.chartDefaultOptions.seriesLine,
  311. name: '完成率',
  312. type: 'line',
  313. yAxisIndex: 1,
  314. data: []
  315. }
  316. ]
  317. }
  318. axisChart.setOption(option, true)
  319. this.axisOption = option
  320. this.axisChart = axisChart
  321. },
  322. /**
  323. * 导出点击
  324. */
  325. exportClick() {
  326. this.requestExportInfo(biAchievementStatisticsExportAPI, this.postParams)
  327. }
  328. }
  329. }
  330. </script>
  331. <style rel="stylesheet/scss" lang="scss" scoped>
  332. @import "./styles/detail.scss";
  333. .type-select {
  334. width: 120px;
  335. margin-right: 16px;
  336. }
  337. </style>