chart-common.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. /**
  2. * 说明:
  3. * el: 挂载的dom节点
  4. * chartData 图表数据集合
  5. * config 支持配置的文本,点击事件函数
  6. * type 判断图表类型,有以下几个参数
  7. * {
  8. * ve-histogram 柱状图
  9. * ve-finehistogram 柱状图+折线图
  10. * ve-treemap 树形图
  11. * ve-line 折线图
  12. * }
  13. */
  14. var chartTemplate =
  15. `
  16. <template>
  17. <ve-histogram
  18. v-if='type==="ve-histogram"'
  19. :id="type"
  20. :colors="histogramChart.options.colors"
  21. :width="histogramChart.options.width"
  22. :height="histogramChart.options.height"
  23. :data="chartData"
  24. :settings="histogramChart.options.settings"
  25. :after-config="histogramChart.options.config"
  26. :after-set-option="extend"
  27. :extend="histogramChart.defaultOptions"
  28. >
  29. </ve-histogram>
  30. <ve-histogram
  31. v-else-if='type==="ve-finehistogram"'
  32. :id="type"
  33. :height="barLineChart.options.height"
  34. :width="barLineChart.options.width ? barLineChart.options.width : null"
  35. :colors="barLineChart.options.colors"
  36. :data="chartData"
  37. :settings="barLineChart.options.settings"
  38. :after-config="barLineChart.options.config"
  39. :after-set-option="barExtend"
  40. :extend="barLineChart.defaultOptions"
  41. >
  42. </ve-histogram>
  43. <div v-else-if='type==="ve-treemap"' class="rect-tree-map-chart" style="height: 100%"></div>
  44. <ve-line
  45. v-else-if='type==="ve-line"'
  46. :data="chartData"
  47. :height="lineChart.options.height"
  48. :after-config="lineChart.options.config"
  49. :extend="lineChart.defaultOption"
  50. >
  51. </ve-line>
  52. </template>
  53. `
  54. window.proxy.vComponentChart = function (el, chartData, config, type) {
  55. new Vue({
  56. delimiters: ['${', '}'],
  57. el: el,
  58. template: chartTemplate,
  59. data () {
  60. return {
  61. myChart: null,
  62. type: type,
  63. chartData: {},
  64. // histogram 柱状图
  65. histogramChart: {},
  66. // barLineChart 柱状图+折线图
  67. barLineChart: {},
  68. // treeMapChart 树形图
  69. treeMapChart: {},
  70. // lineChart 折线图
  71. lineChart: {}
  72. }
  73. },
  74. created () {
  75. this.chartData = chartData
  76. this.getChartConfig()
  77. },
  78. mounted () {
  79. if(this.type === 've-treemap') {
  80. this.updateChartView()
  81. } else if (this.type === 've-line') {
  82. this.mergeExtendOptions()
  83. }
  84. },
  85. methods: {
  86. IsPC () {
  87. var userAgentInfo = navigator.userAgent
  88. var Agents = ["Android", "iPhone",
  89. "SymbianOS", "Windows Phone",
  90. "iPad", "iPod"]
  91. var flagPc = true
  92. for (var v = 0; v < Agents.length; v++) {
  93. if (userAgentInfo.indexOf(Agents[v]) > 0) {
  94. flagPc = false;
  95. break;
  96. }
  97. }
  98. return flagPc;
  99. },
  100. // 判断类型,取对应配置
  101. getChartConfig () {
  102. if (this.type === 've-histogram') {
  103. this.histogramChart = {
  104. options: {
  105. colors: ['#FF9F3F', '#05A5F2'],
  106. width: '100%',
  107. height: '100%',
  108. settings: {}
  109. },
  110. defaultOptions: {
  111. grid: {
  112. top: 20
  113. },
  114. xAxis: {
  115. axisLabel: {
  116. margin: 10,
  117. interval: 1, // 强制显示x轴所有刻度
  118. fontSize: 12
  119. },
  120. nameLocation: 'start',
  121. nameTextStyle: {
  122. fontSize: 12,
  123. align: 'left',
  124. padding: [70, 0, 0, 50],
  125. color: '#9B9CA3'
  126. }
  127. },
  128. yAxis (item) {
  129. Object.assign(item[0], {
  130. splitLine: {
  131. lineStyle: {
  132. type: 'dashed',
  133. width: 0.5
  134. }
  135. },
  136. axisLabel: {
  137. margin: 2,
  138. fontSize: 12,
  139. formatter: (value, index) => value.toString().replace(/,/, '') + '%'
  140. }
  141. })
  142. Object.assign(item[1], {
  143. splitLine: {
  144. show: false
  145. },
  146. axisLabel: {
  147. show: false,
  148. fontSize: 12,
  149. formatter: (value, index) => value.toString().replace(/,/, '')
  150. }
  151. })
  152. return item
  153. },
  154. tooltip: {
  155. trigger: 'axis',
  156. confine: true,
  157. backgroundColor: '#fff',
  158. axisPointer: {
  159. type: 'shadow',
  160. shadowStyle: {
  161. color: 'rgba(42, 190, 209,0.1)'
  162. },
  163. z: 3
  164. },
  165. textStyle: {
  166. color: '#171826',
  167. fontSize: 12
  168. },
  169. padding: [7, 12],
  170. extraCssText: 'box-shadow: 0px 4px 16px rgba(8, 31, 38, 0.08);transform: translate3d(0,0,0)',
  171. formatter: this.tooltipFormatter
  172. },
  173. legend: {
  174. orient: 'horizontal',
  175. icon: 'rect',
  176. bottom: 20,
  177. align: 'left',
  178. itemGap: 20,
  179. itemWidth: 9,
  180. itemHeight: 9,
  181. textStyle: {
  182. fontSize: 10,
  183. lineHeight: 14,
  184. verticalAlign: 'bottom',
  185. rich: {
  186. a: {
  187. fontSize: 16,
  188. verticalAlign: 'top',
  189. align: 'center',
  190. padding: [0, 15, 28, 0]
  191. },
  192. b: {
  193. fontSize: 14,
  194. align: 'center',
  195. padding: [0, 15, 0, 0],
  196. lineHeight: 25
  197. }
  198. }
  199. },
  200. formatter: name => name
  201. }
  202. }
  203. }
  204. } else if (this.type === 've-finehistogram') {
  205. this.barLineChart = {
  206. options: {
  207. height: '100%',
  208. width: '100%',
  209. colors: ['#05A6F3', '#FF3A20'],
  210. settings: {
  211. showLine: [chartData.columns[2]],
  212. axisSite: { right: [chartData.columns[2]] }
  213. },
  214. config: this.newTimeConfig
  215. },
  216. defaultOptions: {
  217. grid: {
  218. top: 20
  219. },
  220. xAxis: {
  221. axisLabel: {
  222. margin: 10,
  223. interval: 0, // 强制显示x轴所有刻度
  224. fontSize: 12
  225. },
  226. nameLocation: 'start',
  227. nameTextStyle: {
  228. fontSize: 12,
  229. align: 'left',
  230. padding: [70, 0, 0, 50],
  231. color: '#9B9CA3'
  232. }
  233. },
  234. yAxis (item) {
  235. item[0].splitLine = {
  236. lineStyle: {
  237. type: 'dashed',
  238. width: 0.5
  239. }
  240. }
  241. item[0].axisLabel = {
  242. margin: 2,
  243. fontSize: 12
  244. }
  245. item[1].splitLine = {
  246. show: false
  247. }
  248. item[1].axisLabel = {
  249. show: false,
  250. fontSize: 12
  251. }
  252. item[0].axisLabel.formatter = (value, index) => {
  253. return value.toString().replace(/,/, '')
  254. }
  255. item[1].axisLabel.formatter = (value, index) => {
  256. return value.toString().replace(/,/, '')
  257. }
  258. return item
  259. },
  260. tooltip: {
  261. trigger: 'axis',
  262. confine: true,
  263. backgroundColor: '#fff',
  264. axisPointer: {
  265. type: 'shadow',
  266. shadowStyle: {
  267. color: 'rgba(42, 190, 209,0.1)'
  268. },
  269. z: 3
  270. },
  271. textStyle: {
  272. color: '#171826',
  273. fontSize: 12
  274. },
  275. padding: [7, 12],
  276. extraCssText: 'box-shadow: 0px 4px 16px rgba(8, 31, 38, 0.08);transform: translate3d(0,0,0)'
  277. },
  278. legend: {
  279. orient: 'horizontal',
  280. icon: 'circle',
  281. bottom: 0,
  282. align: 'left',
  283. itemWidth: 8,
  284. itemHeight: 8,
  285. itemGap: 20,
  286. textStyle: {
  287. fontSize: 12,
  288. rich: {
  289. a: {
  290. fontSize: 16,
  291. verticalAlign: 'top',
  292. align: 'center',
  293. padding: [0, 15, 28, 0]
  294. },
  295. b: {
  296. fontSize: 14,
  297. align: 'center',
  298. padding: [0, 15, 0, 0],
  299. lineHeight: 25
  300. }
  301. }
  302. },
  303. formatter: (name) => {
  304. if (name === '企业数量') {
  305. name = name + '(个)'
  306. }
  307. return name
  308. }
  309. }
  310. }
  311. }
  312. } else if (this.type === 've-treemap') {
  313. this.treeMapChart = {
  314. options: {
  315. title: {
  316. subtext: '单位:个、万元',
  317. left: '0',
  318. top: '-8px',
  319. textStyle: {
  320. color: '#000',
  321. fontWeight: 'normal'
  322. }
  323. },
  324. tooltip: {
  325. formatter: this.treetooltipFormatter
  326. }
  327. },
  328. defaultOptions: {
  329. legend: {
  330. show: true,
  331. selectedMode: 'single',
  332. data: [],
  333. bottom: 0,
  334. itemGap: 5
  335. },
  336. tooltip: {
  337. backgroundColor: '#fff',
  338. confine: true,
  339. textStyle: {
  340. color: '#171826',
  341. fontSize: 12
  342. },
  343. padding: [7, 12],
  344. extraCssText: 'box-shadow: 0px 4px 16px rgba(8, 31, 38, 0.08)',
  345. borderWidth: 2,
  346. borderColor: '#F5F6F7'
  347. },
  348. series: [
  349. {
  350. type: 'treemap',
  351. width: '100%',
  352. // height: '85%',
  353. // top: '15%',
  354. roam: false, // 是否开启拖拽漫游(移动和缩放)
  355. nodeClick: false, // 点击节点后的行为,false无反应
  356. breadcrumb: {
  357. show: false
  358. },
  359. label: { // 描述了每个矩形中,文本标签的样式。
  360. normal: {
  361. show: true,
  362. position: ['10%', '40%']
  363. }
  364. },
  365. itemStyle: {
  366. normal: {
  367. show: true,
  368. textStyle: {
  369. color: '#fff',
  370. fontSize: 16
  371. },
  372. borderWidth: 1,
  373. borderColor: '#fff'
  374. },
  375. emphasis: {
  376. label: {
  377. show: true
  378. }
  379. }
  380. },
  381. data: [
  382. // {
  383. // name: 'xxx',
  384. // value: 122
  385. // },
  386. // {
  387. // name: 'yyy',
  388. // value: 122
  389. // }
  390. ]
  391. }
  392. ]
  393. }
  394. }
  395. } else if (this.type === 've-line') {
  396. let _this = this
  397. this.lineChart = {
  398. options: {
  399. height: '100%',
  400. colors: ['#05A6F3', '#FF9F40'],
  401. config: this.configOptions
  402. },
  403. extend: {
  404. yAxis: {
  405. axisLabel: {
  406. formatter: p => p + '%'
  407. }
  408. }
  409. },
  410. defaultOption: {
  411. color: ['#05A6F3', '#FF9F40'],
  412. xAxis: {
  413. axisLabel: {
  414. formatter: function (params) {
  415. var arr = params.split('~')
  416. var isPc = _this.IsPC()
  417. if (arr.length === 2 && !isPc) {
  418. return arr.join('\n-')
  419. } else {
  420. return params
  421. }
  422. },
  423. textStyle: {
  424. color: '#626262',
  425. fontSize: 10
  426. },
  427. interval: 0
  428. }
  429. },
  430. grid: {
  431. top: 10,
  432. left: 12,
  433. right: 12
  434. },
  435. yAxis: {
  436. splitLine: {
  437. lineStyle: {
  438. type: 'dashed',
  439. width: 0.5
  440. }
  441. },
  442. nameGap: 15,
  443. nameTextStyle: {
  444. fontSize: 12,
  445. align: 'left',
  446. color: '#9B9CA3',
  447. padding: [0, 0, 0, -30]
  448. },
  449. axisLabel: {
  450. margin: 2,
  451. fontSize: 12,
  452. color: '#5F5E64',
  453. interval: 'auto',
  454. formatter: (value, index) => {
  455. return value.toString().replace(/,/, '')
  456. }
  457. }
  458. },
  459. legend: {
  460. orient: 'horizontal',
  461. icon: 'circle',
  462. bottom: 10,
  463. itemWidth: 8,
  464. itemHeight: 8,
  465. itemGap: 40,
  466. textStyle: {
  467. color: '#5F5E64',
  468. fontSize: 12
  469. }
  470. },
  471. series: {
  472. type: 'line',
  473. showSymbol: false,
  474. smooth: false,
  475. symbol: 'circle',
  476. symbolSize: 3,
  477. itemStyle: {
  478. borderColor: '#fff',
  479. borderWidth: 1
  480. }
  481. },
  482. tooltip: {
  483. backgroundColor: '#fff',
  484. confine: true,
  485. axisPointer: {
  486. type: 'line',
  487. lineStyle: {
  488. width: 2,
  489. color: '#2ABED1'
  490. },
  491. z: 3
  492. },
  493. textStyle: {
  494. color: '#171826',
  495. fontSize: 12
  496. },
  497. padding: [7, 12],
  498. extraCssText: 'box-shadow: 0px 4px 16px rgba(8, 31, 38, 0.08)',
  499. borderWidth: 2,
  500. borderColor: '#F5F6F7'
  501. },
  502. lineStyle: {
  503. width: 0.5
  504. }
  505. }
  506. }
  507. }
  508. },
  509. // S--histogram--S
  510. extend (chart) {
  511. chart.setOption({
  512. series: [
  513. {
  514. type: 'bar',
  515. barMaxWidth: 20
  516. },
  517. {
  518. type: 'bar',
  519. barMaxWidth: 20
  520. }
  521. ]
  522. })
  523. },
  524. tooltipFormatter (params) {
  525. let tip = `<div style="padding-top:2px;color:#9B9CA3;">${params[0].name}</div>`
  526. for (let i = 0; i < params.length; i++) {
  527. if (params[i].value === undefined || params[i].value === '') {
  528. params[i].value = 0
  529. }
  530. if (i === 0) {
  531. tip = tip + params[i].marker + params[i].seriesName + ':' + params[i].value + '%<br/>'
  532. } else if (i === 1) {
  533. tip = tip + params[i].marker + params[i].seriesName + ':' + params[i].value + '%<br/>'
  534. }
  535. }
  536. return tip
  537. },
  538. // E--histogram--E
  539. // S--barLineChart--S
  540. barExtend (chart) {
  541. chart.setOption({
  542. series: [{
  543. type: 'bar',
  544. barMaxWidth: 20
  545. }, {
  546. type: 'line',
  547. smooth: false,
  548. symbol: 'none'
  549. }]
  550. })
  551. },
  552. newTimeConfig (options) {
  553. options.xAxis[0].axisLabel.rotate = 60
  554. options.xAxis[0].axisLabel.interval = 'auto'
  555. options.yAxis[1].axisLabel.show = true
  556. if (this.chartData.rows.length > 12) {
  557. // 设置时间轴
  558. Object.assign(options, {
  559. // grid解决dataZoom文字被隐藏的问题
  560. // https://github.com/apache/echarts/issues/11601
  561. grid: {
  562. left: '5%',
  563. right: '13%'
  564. },
  565. dataZoom: {
  566. show: true, // 显示滚动条
  567. realtime: true, // 拖动时,是否实时更新系列的视图
  568. type: 'slider',
  569. height: 20,
  570. bottom: 0,
  571. textStyle: {
  572. fontSize: 10
  573. }
  574. }
  575. })
  576. }
  577. options.legend.bottom = 30
  578. options.legend.data = [
  579. { icon: 'rect', name: this.chartData.columns[1] },
  580. { icon: 'line', name: this.chartData.columns[2] }
  581. ]
  582. options.tooltip.axisPointer.shadowStyle.color = 'rgba(5,166,243,0.1)'
  583. options.tooltip.formatter = params => {
  584. let tip = `<div style="padding-top:2px;color:#9B9CA3;">${params[0].name}</div>`
  585. for (let i = 0; i < params.length; i++) {
  586. if (params[i].value === undefined || params[i].value === '') {
  587. params[i].value = 0
  588. }
  589. if (params[i].seriesName.indexOf('环比') === -1) {
  590. const match = params[i].seriesName.match(/((.*))$/g)
  591. let unit = ''
  592. if (Array.isArray(match)) {
  593. unit = match.join('').replace(/[()]/g, '')
  594. }
  595. tip = tip + params[i].marker + params[i].seriesName.replace(`(${unit})`, '') + ':' + params[i].value + unit + '<br/>'
  596. } else {
  597. tip = tip + params[i].marker + params[i].seriesName + ':' + params[i].value + '%' + '<br/>'
  598. }
  599. }
  600. return tip
  601. }
  602. return options
  603. },
  604. // E--barLineChart--E
  605. // S--treeMapChart--S
  606. updateChartView () {
  607. this.myChart = echarts.init(this.$el, 'light')
  608. if (!this.myChart) return
  609. this.mergeOptions()
  610. this.myChart.setOption(this.treeMapChart.defaultOptions)
  611. window.addEventListener('resize', () => {
  612. // this.myChart.resize()
  613. })
  614. },
  615. mergeOptions () {
  616. // 设置数据
  617. this.treeMapChart.defaultOptions.series[0].data = this.chartData
  618. // 设置图例?
  619. this.treeMapChart.defaultOptions.legend.data = this.chartData.map(item => {
  620. return item.name
  621. })
  622. _.merge(this.treeMapChart.defaultOptions, this.treeMapChart.options)
  623. },
  624. treetooltipFormatter (params) {
  625. const item = params.data
  626. let tip = `<span style="display:inline-block;margin-right:5px;border-radius:8px;width:8px;height:8px;background-color:${params.color};"></span>${params.name}<br />`
  627. if (item && item.value) {
  628. tip += `<div>项目数量:${item.value}个<div>`
  629. }
  630. if (item && item.amount) {
  631. tip += `<div>项目金额:${item.amount}万元<div>`
  632. }
  633. return tip
  634. },
  635. // E--treeMapChart--E
  636. // S--lineChart--S
  637. configOptions (options) {
  638. // 面积颜色-渐变
  639. Object.assign(options.series[0], {
  640. areaStyle: {
  641. normal: {
  642. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  643. {
  644. offset: 0,
  645. color: 'rgba(42, 190, 209, 0.12)'
  646. },
  647. {
  648. offset: 1,
  649. color: 'rgba(42, 190, 209, 0)'
  650. }
  651. ], false)
  652. }
  653. }
  654. })
  655. Object.assign(options.series[1], {
  656. areaStyle: {
  657. normal: {
  658. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  659. {
  660. offset: 0,
  661. color: 'rgba(255, 159, 63, 0.12)'
  662. },
  663. {
  664. offset: 1,
  665. color: 'rgba(255, 159, 63, 0)'
  666. }
  667. ], false)
  668. }
  669. }
  670. })
  671. Object.assign(options.legend, {
  672. icon: 'rect'
  673. })
  674. options.tooltip.formatter = params => {
  675. let tip = `<div style="padding-top:2px;color:#9B9CA3;">${params[0].name}</div>`
  676. for (let i = 0; i < params.length; i++) {
  677. params[i].marker = '<span style="display:inline-block;margin-right:5px;border-radius:8px;width:8px;height:8px;background-color:' + params[i].color + ';"></span>'
  678. tip += params[i].marker + params[i].seriesName + ':' + params[i].value[1].toString().replace(/,/, '') + '%' + '<br/>'
  679. }
  680. return tip
  681. }
  682. return options
  683. },
  684. mergeExtendOptions () {
  685. _.merge(this.lineChart.defaultOption, this.lineChart.extend)
  686. }
  687. // E--lineChart--E
  688. }
  689. })
  690. }