commonPieStatisticsCharts.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. <template>
  2. <div class="statstics-wrapper">
  3. <div
  4. ref="headerWrapper"
  5. class="statstics-header"
  6. >
  7. <StatisticsHeader
  8. :title="`${chartsTitle}统计`"
  9. :custom-items="customFormItems"
  10. @getFormData="getFormData"
  11. @export="exportHandler"
  12. />
  13. </div>
  14. <div class="statstics-content">
  15. <div
  16. id="chart"
  17. class="statistics-chart"
  18. :style="{ height: chartHeight }"
  19. />
  20. </div>
  21. </div>
  22. </template>
  23. <script>
  24. import StatisticsHeader from './statisticsHeader.vue'
  25. import { Query } from '@/api/dataIntegration'
  26. import { mapGetters } from 'vuex'
  27. export default {
  28. name: 'CommonBarStatisticsCharts',
  29. components: { StatisticsHeader },
  30. props: {
  31. chartsTitle: {
  32. type: String,
  33. required: true
  34. },
  35. querySettings: {
  36. type: Object,
  37. required: true
  38. },
  39. categories: {
  40. type: Array,
  41. required: true
  42. },
  43. customFormItems: {
  44. type: Array,
  45. default: () => []
  46. }
  47. },
  48. data() {
  49. return {
  50. myChart: null,
  51. debounceTime: 300,
  52. chartHeight: '70vh',
  53. hasChartData: false,
  54. options: {
  55. backgroundColor: '#ffffff',
  56. tooltip: {
  57. trigger: 'item'
  58. },
  59. title: {
  60. text: '总件数',
  61. // 副标题
  62. subtext: '0',
  63. // 主副标题间距
  64. itemGap: 24,
  65. x: 'left',
  66. y: 'center',
  67. left: '30%',
  68. top: '40%',
  69. textAlign: 'center',
  70. // 主标题样式
  71. textStyle: {
  72. fontSize: '48',
  73. color: '#ffffff',
  74. fontWeight: 'bold',
  75. fontFamily: 'Microsoft YaHei'
  76. },
  77. // 副标题样式
  78. subtextStyle: {
  79. fontSize: '80',
  80. color: '#ffffff',
  81. fontWeight: 'bold'
  82. }
  83. },
  84. legend: {
  85. show: true,
  86. left: '60%',
  87. x: 'right',
  88. y: 'center',
  89. icon: 'rect',
  90. itemWidth: 20,
  91. itemHeight: 20,
  92. formatter: name => this.legendFormatter(name),
  93. textStyle: {
  94. backgroundColor: 'transparent',
  95. lineHeight: 0,
  96. rich: {
  97. chartsTitle: {
  98. width: 200,
  99. lineHeight: 100,
  100. fontSize: 32,
  101. fontFamily: 'Microsoft YaHei',
  102. fontWeight: 'bold',
  103. color: '#101116',
  104. padding: [0, 1000, 0, -20]
  105. },
  106. name: {
  107. fontSize: 20,
  108. fontFamily: 'Microsoft YaHei',
  109. fontWeight: 'bold',
  110. color: '#101116',
  111. lineHeight: 100
  112. },
  113. label: {
  114. fontSize: 16,
  115. fontFamily: 'Microsoft YaHei',
  116. color: '#101116'
  117. },
  118. value: {
  119. width: 96,
  120. fontSize: 16,
  121. fontFamily: 'Helvetica',
  122. fontWeight: 'bold',
  123. color: '#101116'
  124. },
  125. ratio: {
  126. width: 80,
  127. fontSize: 16,
  128. fontFamily: 'Helvetica',
  129. fontWeight: 'bold',
  130. color: '#101116'
  131. },
  132. wrap: {
  133. padding: [0, 40, 0, 0]
  134. }
  135. }
  136. },
  137. selected: {
  138. [this.chartsTitle]: false
  139. }
  140. },
  141. series: [
  142. {
  143. name: '',
  144. type: 'pie',
  145. left: '30%',
  146. width: 560,
  147. height: 560,
  148. center: [0, '60%'],
  149. radius: ['60%', '90%'],
  150. avoidLabelOverlap: false,
  151. label: {
  152. show: false,
  153. position: 'center'
  154. },
  155. emphasis: {
  156. label: {
  157. show: false,
  158. fontSize: '40',
  159. fontWeight: 'bold'
  160. }
  161. },
  162. labelLine: {
  163. show: false
  164. },
  165. data: []
  166. },
  167. {
  168. name: '总数',
  169. type: 'pie',
  170. left: '30%',
  171. width: 560,
  172. height: 560,
  173. center: [0, '60%'],
  174. radius: ['0%', '50%'],
  175. avoidLabelOverlap: false,
  176. itemStyle: {
  177. normal: {
  178. color: '#101116'
  179. }
  180. },
  181. label: {
  182. show: false,
  183. position: 'center'
  184. },
  185. // 自定义中心内容的话需要把这个关闭
  186. emphasis: {
  187. label: {
  188. show: false
  189. }
  190. },
  191. labelLine: {
  192. show: false
  193. },
  194. data: []
  195. }
  196. ]
  197. },
  198. totalCount: [{ value: 0 }],
  199. categoryDatas: [],
  200. categoryKey: 'specialnum',
  201. seriesKey: 'special'
  202. }
  203. },
  204. computed: {
  205. ...mapGetters(['sidebar'])
  206. },
  207. watch: {
  208. // 监听数据变化 重绘图形
  209. options: {
  210. handler(obj) {
  211. this.myChart.setOption(obj)
  212. this.resizeHandler()
  213. },
  214. deep: true
  215. },
  216. categories: {
  217. handler(arr) {
  218. this.categoryDatas = arr.map(categoryName => ({
  219. name: categoryName,
  220. value: 0
  221. }))
  222. this.categoryDatas.unshift({
  223. name: this.chartsTitle,
  224. value: null
  225. })
  226. },
  227. deep: true,
  228. immediate: true
  229. },
  230. querySettings: {
  231. handler({ categoryKey, seriesKey }) {
  232. if (seriesKey) {
  233. this.seriesKey = seriesKey
  234. }
  235. if (categoryKey) {
  236. this.categoryKey = categoryKey
  237. }
  238. },
  239. deep: true,
  240. immediate: true
  241. },
  242. 'sidebar.expand'() {
  243. this.setChartHeight()
  244. }
  245. },
  246. mounted() {
  247. this.setChartHeight()
  248. this.myChart = this.$echarts.init(document.getElementById('chart'))
  249. this.options.series[0].data = this.categoryDatas
  250. this.options.legend.data = this.categoryDatas.map(({ name }, index) => {
  251. if (index === 0) {
  252. return {
  253. name,
  254. icon: 'none'
  255. }
  256. } else {
  257. return {
  258. name
  259. }
  260. }
  261. })
  262. this.options.series[1].data = this.totalCount
  263. this.myChart.setOption(this.options)
  264. this.myChart.on('legendselectchanged', ({ name }) => {
  265. if (name === this.chartsTitle) {
  266. this.myChart.dispatchAction({
  267. type: 'legendUnSelect',
  268. name
  269. })
  270. }
  271. })
  272. // 监听页面缩放
  273. this.debouncedChartHeightSetter = this._.debounce(this.setChartHeight, this.debounceTime)
  274. window.addEventListener('resize', this.debouncedChartHeightSetter)
  275. },
  276. beforeDestroy() {
  277. // 销毁实例和移除监听
  278. window.removeEventListener('resize', this.debouncedChartHeightSetter)
  279. if (this.myChart) {
  280. this.myChart.dispose()
  281. this.myChart = null
  282. }
  283. },
  284. methods: {
  285. legendFormatter(name) {
  286. const index = this.categoryDatas.findIndex(category => category.name === name)
  287. if (index === 0) {
  288. return `{chartsTitle|${name}}`
  289. } else {
  290. const value = this.categoryDatas[index].value
  291. const ratio = value && this.totalCount.value ? ((value / this.totalCount.value) * 100).toFixed(2) + '%' : '0%'
  292. const richString = `{name|${name}}\n{label|数量:}{value|${value}}{label|占比:}{ratio|${ratio}}`
  293. return index % 2 ? richString + '{wrap| }' : richString
  294. }
  295. },
  296. resetDatas() {
  297. this.hasChartData = false
  298. this.categoryDatas.forEach(category => {
  299. category && (category.value = 0)
  300. })
  301. this.options.title.subtext = '0'
  302. this.options.series[1].data[0].value = 0
  303. },
  304. getFormData(formData) {
  305. this.resetDatas()
  306. let id
  307. let params = []
  308. if (formData.range === '基地分公司') {
  309. id = this.querySettings.byArea
  310. params = [formData.interval, formData.area, formData.inOrOut, formData.dateTime[0], formData.dateTime[1]]
  311. } else if (formData.range !== '基地分公司' && formData.range !== '') {
  312. id = this.querySettings.byOther
  313. params = [formData.interval, formData.range, formData.inOrOut, formData.dateTime[0], formData.dateTime[1]]
  314. if (formData.airline === '' && formData.airport === '' && formData.terminal === '') {
  315. params.splice(2, 0, '全部')
  316. }
  317. if (formData.airline !== '') {
  318. params.splice(2, 0, formData.airline)
  319. }
  320. if (formData.airport !== '') {
  321. params.splice(2, 0, formData.airport)
  322. }
  323. if (formData.terminal !== '') {
  324. params.splice(2, 0, formData.terminal)
  325. }
  326. }
  327. this.getChartsData(id, params)
  328. },
  329. async getChartsData(id, params) {
  330. try {
  331. const {
  332. code,
  333. returnData: { listValues },
  334. message
  335. } = await Query({
  336. id,
  337. dataContent: params
  338. })
  339. if (Number(code) === 0) {
  340. if (listValues.length === 0) {
  341. this.$message.info('未查询到对应数据')
  342. return
  343. }
  344. let totalCount = 0
  345. listValues.forEach(element => {
  346. this.categoryDatas.forEach(category => {
  347. if (element[this.categoryKey]?.includes(category.name)) {
  348. category.value += element[this.seriesKey]
  349. totalCount += element[this.seriesKey]
  350. }
  351. })
  352. })
  353. this.options.title.subtext = totalCount.toString()
  354. this.totalCount.value = totalCount
  355. this.hasChartData = true
  356. } else {
  357. this.$message.error(message || '失败')
  358. }
  359. } catch (error) {
  360. console.log('出错了', error.message || error)
  361. }
  362. },
  363. setChartHeight() {
  364. const topBarHeight = 80
  365. const headerBlankHeight = 24
  366. const tabsWrapperHeight = 62
  367. const headerHeight = this.$refs['headerWrapper'].offsetHeight
  368. const footerBlankHeight = 24
  369. this.chartHeight = `calc(100vh - ${
  370. topBarHeight + headerBlankHeight + tabsWrapperHeight + headerHeight + footerBlankHeight
  371. }px)`
  372. this.$nextTick(() => {
  373. this.resizeHandler()
  374. })
  375. },
  376. resizeHandler() {
  377. if (this.myChart) {
  378. this.myChart.resize()
  379. }
  380. },
  381. exportHandler() {
  382. if (!this.hasChartData) {
  383. this.$message.warning('请查询后再进行导出')
  384. return
  385. }
  386. const myCanvas = this.myChart._dom.querySelectorAll('canvas')[0]
  387. const image = myCanvas.toDataURL('image/png')
  388. const $a = document.createElement('a')
  389. $a.setAttribute('href', image)
  390. $a.setAttribute('download', `${this.chartsTitle}统计.png`)
  391. $a.click()
  392. }
  393. }
  394. }
  395. </script>
  396. <style lang="scss" scoped>
  397. .statistics-chart {
  398. width: 100%;
  399. }
  400. </style>