u-charts.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. <template>
  2. <view class="chartsview" :style="{ background: background }">
  3. <view v-show="mixinDatacomLoading"><view class="uni-icons-spinner-cycle">Loading...</view></view>
  4. <view v-show="mixinDatacomErrorMessage">
  5. <view class="uni-icons-info-filled">{{ mixinDatacomErrorMessage }}</view>
  6. </view>
  7. <canvas
  8. :id="canvasId"
  9. :canvas-id="canvasId"
  10. :style="{ width: cWidth + 'px', height: cHeight + 'px' }"
  11. :type="canvas2d?'2d':''"
  12. @tap="tap"
  13. @cilck="tap"
  14. @touch="tap"
  15. @touchstart="touchStart"
  16. @touchmove="touchMove"
  17. @touchend="touchEnd"
  18. @mousemove="mouseMove"
  19. @mousedown="mousedown"
  20. @mouseup="mouseup"
  21. @error="error"
  22. v-show="showchart"
  23. />
  24. </view>
  25. </template>
  26. <script>
  27. import qiunCharts from '../../js_sdk/u-charts/u-charts.js';
  28. import config from '../../js_sdk/u-charts/config.js';
  29. var canvases = {};
  30. var options = {};
  31. var chartdom = null;
  32. export default {
  33. mixins: [uniCloud.mixinDatacom],
  34. props: {
  35. type: {
  36. type: String,
  37. default: null
  38. },
  39. canvasId: {
  40. type: String,
  41. default: "uchartsid"
  42. },
  43. canvas2d: {
  44. type: Boolean,
  45. default: false
  46. },
  47. pixelRatio: {
  48. type: Number,
  49. default: 1
  50. },
  51. background: {
  52. type: String,
  53. default: 'none'
  54. },
  55. animation:{
  56. type: Boolean,
  57. default: true
  58. },
  59. chartData: {
  60. type: Object,
  61. default() {
  62. return {
  63. categories: [],
  64. series: []
  65. };
  66. }
  67. },
  68. opts: {
  69. type: Object,
  70. default: () => ({})
  71. },
  72. inScrollView:{
  73. type: Boolean,
  74. default: false
  75. },
  76. show:{
  77. type: Boolean,
  78. default: false
  79. }
  80. },
  81. data() {
  82. return {
  83. cWidth: 375,
  84. cHeight: 250,
  85. showchart: false,
  86. defaultOpts: {}
  87. };
  88. },
  89. mounted() {
  90. if(this.canvasId=='uchartsid'){
  91. console.warn("注意:请在uCharts组件传入canvasId,以免单页多图产生图表错乱!")
  92. }
  93. if (this.type && config.type.includes(this.type)) {
  94. this.defaultOpts = Object.assign({},config[this.type])
  95. this.load()
  96. } else {
  97. this.mixinDatacomLoading = false
  98. this.showchart = false
  99. this.mixinDatacomErrorMessage = '参数错误:props参数中type类型不正确'
  100. }
  101. uni.onWindowResize(res => {
  102. this.init()
  103. })
  104. },
  105. watch: {
  106. chartData(val, oldval) {
  107. if (!this.type || !config.type.includes(this.type)) {
  108. this.mixinDatacomLoading = false
  109. this.showchart = false
  110. this.mixinDatacomErrorMessage = '参数错误:props参数中type不正确'
  111. return
  112. }
  113. if (typeof val === 'object') {
  114. if (this.collection != '') {
  115. if (config.categories.includes(this.type) && val.categories.length == 0 ) {
  116. this.mixinDatacomLoading = false
  117. this.showchart = false
  118. this.mixinDatacomErrorMessage = '数据错误:chartData中缺少categories'
  119. } else {
  120. this.mixinDatacomLoading = false
  121. this.mixinDatacomErrorMessage = null
  122. this.$nextTick(function() {
  123. this.init()
  124. })
  125. }
  126. } else {
  127. this.mixinDatacomLoading = false
  128. this.mixinDatacomErrorMessage = null
  129. this.$nextTick(function() {
  130. this.init()
  131. });
  132. }
  133. } else {
  134. this.mixinDatacomLoading = false
  135. this.showchart = false
  136. this.mixinDatacomErrorMessage = '参数错误:chartData数据类型错误'
  137. }
  138. },
  139. show(val, oldval) {
  140. if (val) {
  141. if (this.collection != '') {
  142. if (config.categories.includes(this.type) && this.chartData.categories.length == 0 ) {
  143. this.mixinDatacomLoading = false
  144. this.showchart = false
  145. this.mixinDatacomErrorMessage = '数据错误:chartData中缺少categories'
  146. } else {
  147. this.mixinDatacomLoading = false
  148. this.mixinDatacomErrorMessage = null
  149. this.$nextTick(function() {
  150. this.init()
  151. })
  152. }
  153. } else {
  154. this.mixinDatacomLoading = false
  155. this.mixinDatacomErrorMessage = null
  156. this.$nextTick(function() {
  157. this.init()
  158. });
  159. }
  160. }
  161. }
  162. },
  163. methods: {
  164. load() {
  165. if (this.mixinDatacomLoading == true) {
  166. return
  167. }
  168. this.mixinDatacomLoading = true
  169. if (this.collection != '') {
  170. this.mixinDatacomGet()
  171. .then(res => {
  172. this.mixinDatacomLoading = false
  173. const { data, count } = res.result
  174. this.mixinDatacomResData = data
  175. if (config.categories.includes(this.type) && this.chartData.categories.length == 0) {
  176. this.mixinDatacomLoading = false
  177. this.showchart = false
  178. this.mixinDatacomErrorMessage = '数据错误:chartData中缺少categories'
  179. } else {
  180. this.init()
  181. }
  182. })
  183. .catch(err => {
  184. if (this.collection == '') {
  185. if (this.chartData.series.length > 0) {
  186. this.mixinDatacomLoading = false
  187. this.init()
  188. }
  189. } else {
  190. this.mixinDatacomLoading = false
  191. this.showchart = false
  192. this.mixinDatacomErrorMessage = '请求错误:' + err
  193. }
  194. });
  195. }else{
  196. if (this.chartData.series.length > 0) {
  197. this.mixinDatacomLoading = false
  198. this.init()
  199. }
  200. }
  201. },
  202. onMixinDatacomPropsChange(needReset, changed) {
  203. if (needReset) {
  204. if(options[this.canvasId] !== undefined){
  205. options[this.canvasId].context.clearRect(0, 0, options[this.canvasId].width, options[this.canvasId].height)
  206. options[this.canvasId].context.draw()
  207. }
  208. this.showchart = false;
  209. this.load()
  210. }
  211. },
  212. cloudDataInit() {
  213. let temp = {}
  214. let series=[]
  215. let resdata = this.mixinDatacomResData
  216. let categories = options[this.canvasId].categories
  217. resdata.map(function (item, index) {
  218. if(item.type!=undefined && !temp[item.type]){
  219. series.push({name:item.type,data:[]})
  220. temp[item.type] = true;
  221. }
  222. })
  223. if(series.length==0){
  224. let seriesname="默认分组"
  225. if(this.chartData.series.length>0){
  226. seriesname=this.chartData.series[0].name
  227. }
  228. series=[{name:seriesname,data:[]}]
  229. for (let j = 0; j < categories.length; j++) {
  230. let seriesdata = 0;
  231. for (let i = 0; i < resdata.length; i++) {
  232. if(resdata[i].label == categories[j]){
  233. seriesdata = resdata[i].value
  234. }
  235. }
  236. series[0].data.push(seriesdata)
  237. }
  238. }else{
  239. for (let k = 0; k < series.length; k++) {
  240. if(categories.length>0){
  241. for (let j = 0; j < categories.length; j++) {
  242. let seriesdata = 0;
  243. for (let i = 0; i < resdata.length; i++) {
  244. if(series[k].name == resdata[i].type && resdata[i].label == categories[j]){
  245. seriesdata = resdata[i].value
  246. }
  247. }
  248. series[k].data.push(seriesdata)
  249. }
  250. }else{
  251. for (let i = 0; i < resdata.length; i++) {
  252. if(series[k].name == resdata[i].type){
  253. series[k].data.push(resdata[i].type)
  254. }
  255. }
  256. }
  257. }
  258. }
  259. return series;
  260. },
  261. init() {
  262. chartdom = uni.createSelectorQuery().in(this).select('.chartsview');
  263. chartdom.boundingClientRect(data => {
  264. if(data.width>0 && data.height>0){
  265. this.cWidth = data.width
  266. this.cHeight = data.height
  267. options[this.canvasId] = Object.assign(this.defaultOpts, this.opts)
  268. options[this.canvasId].canvasId = this.canvasId
  269. options[this.canvasId].categories = this.chartData.categories
  270. if (this.collection == '') {
  271. options[this.canvasId].series = this.chartData.series
  272. }else{
  273. options[this.canvasId].series = this.cloudDataInit()
  274. }
  275. options[this.canvasId].background = this.background == 'none' ? '#FFFFFF' : this.background
  276. options[this.canvasId].pixelRatio = this.pixelRatio
  277. options[this.canvasId].animation = this.animation
  278. options[this.canvasId].width = data.width * this.pixelRatio
  279. options[this.canvasId].height = data.height * this.pixelRatio
  280. if(this.canvas2d){
  281. options[this.canvasId].canvas2d = this.canvas2d
  282. const query = uni.createSelectorQuery().in(this)
  283. query.select('#'+this.canvasId)
  284. .fields({ node: true, size: true })
  285. .exec((res) => {
  286. const canvas = res[0].node
  287. const ctx = canvas.getContext('2d')
  288. options[this.canvasId].context = ctx
  289. canvas.width = data.width * this.pixelRatio
  290. canvas.height = data.height * this.pixelRatio
  291. canvas._width = data.width * this.pixelRatio
  292. canvas._height = data.height * this.pixelRatio
  293. if(!this.mixinDatacomLoading){
  294. this.showchart = true
  295. }
  296. this.newChart()
  297. })
  298. }else{
  299. options[this.canvasId].context = uni.createCanvasContext(this.canvasId,this)
  300. if(!this.mixinDatacomLoading){
  301. this.showchart = true
  302. }
  303. this.newChart()
  304. }
  305. }else{
  306. this.mixinDatacomLoading = false
  307. this.showchart = false
  308. this.mixinDatacomErrorMessage = '布局错误:请尝试props绑定show状态'
  309. }
  310. }).exec();
  311. },
  312. newChart() {
  313. canvases[this.canvasId] = new qiunCharts(options[this.canvasId])
  314. canvases[this.canvasId].addEventListener('renderComplete', () => {
  315. this.$emit("complete",{complete:true,charts:canvases[this.canvasId]})
  316. });
  317. canvases[this.canvasId].addEventListener('scrollLeft', () => {
  318. this.$emit("scrollLeft",{scrollLeft:true,charts:canvases[this.canvasId]})
  319. });
  320. canvases[this.canvasId].addEventListener('scrollRight', () => {
  321. this.$emit("scrollRight",{scrollRight:true,charts:canvases[this.canvasId]})
  322. });
  323. },
  324. tap(e) {
  325. let currentIndex=null
  326. let legendIndex=null
  327. if(this.inScrollView){
  328. e.type = 'click'
  329. }
  330. if (e.type == 'click') {
  331. chartdom = uni.createSelectorQuery().in(this).select('.chartsview')
  332. chartdom.boundingClientRect(data => {
  333. e.changedTouches.unshift({ x: e.detail.x - data.left, y: e.detail.y - data.top })
  334. e.mp.changedTouches.unshift({ x: e.detail.x - data.left, y: e.detail.y - data.top })
  335. canvases[this.canvasId].touchLegend(e)
  336. canvases[this.canvasId].showToolTip(e, {
  337. format: function(item, category) {
  338. if(category){
  339. return category + ' ' + item.name + ':' + item.data
  340. }else{
  341. return item.name + ':' + item.data
  342. }
  343. }
  344. })
  345. currentIndex=canvases[this.canvasId].getCurrentDataIndex(e)
  346. legendIndex=canvases[this.canvasId].getLegendDataIndex(e)
  347. this.$emit("getIndex",{event:e,currentIndex:currentIndex,legendIndex:legendIndex,charts:canvases[this.canvasId]})
  348. }).exec();
  349. } else {
  350. e.changedTouches.unshift({ x: e.detail.x - e.currentTarget.offsetLeft, y: e.detail.y - e.currentTarget.offsetTop })
  351. e.mp.changedTouches.unshift({ x: e.detail.x - e.currentTarget.offsetLeft, y: e.detail.y - e.currentTarget.offsetTop })
  352. canvases[this.canvasId].touchLegend(e)
  353. canvases[this.canvasId].showToolTip(e, {
  354. format: function(item, category) {
  355. if(category){
  356. return category + ' ' + item.name + ':' + item.data
  357. }else{
  358. return item.name + ':' + item.data
  359. }
  360. }
  361. });
  362. currentIndex=canvases[this.canvasId].getCurrentDataIndex(e)
  363. legendIndex=canvases[this.canvasId].getLegendDataIndex(e)
  364. this.$emit("getIndex",{event:e,currentIndex:currentIndex,legendIndex:legendIndex,charts:canvases[this.canvasId]})
  365. }
  366. },
  367. touchStart(e) {
  368. canvases[this.canvasId].scrollStart(e)
  369. this.$emit("touchStart",{event:e,charts:canvases[this.canvasId]})
  370. },
  371. touchMove(e) {
  372. canvases[this.canvasId].scroll(e)
  373. this.$emit("touchMove",{event:e,charts:canvases[this.canvasId]})
  374. },
  375. touchEnd(e) {
  376. canvases[this.canvasId].scrollEnd(e)
  377. this.$emit("touchEnd",{event:e,charts:canvases[this.canvasId]})
  378. },
  379. mousedown(e){
  380. if(options[this.canvasId].enableScroll){
  381. chartdom = uni.createSelectorQuery().in(this).select('.chartsview')
  382. chartdom.boundingClientRect(data => {
  383. e.changedTouches.unshift({ x: e.pageX - data.left, y: e.clientY-data.top })
  384. e.mp.changedTouches.unshift({ x: e.pageX - data.left, y: e.clientY-data.top })
  385. canvases[this.canvasId].scrollStart(e)
  386. options[this.canvasId].mousedown=true;
  387. this.$emit("touchStart",{event:e,charts:canvases[this.canvasId]})
  388. }).exec();
  389. }
  390. },
  391. mouseMove(e) {
  392. if (options[this.canvasId].series.length > 0) {
  393. chartdom = uni.createSelectorQuery().in(this).select('.chartsview')
  394. chartdom.boundingClientRect(data => {
  395. e.changedTouches.unshift({ x: e.pageX - data.left, y: e.clientY-data.top })
  396. e.mp.changedTouches.unshift({ x: e.pageX - data.left, y: e.clientY-data.top })
  397. if(options[this.canvasId].enableScroll && options[this.canvasId].mousedown){
  398. canvases[this.canvasId].scroll(e)
  399. this.$emit("touchMove",{event:e,charts:canvases[this.canvasId]})
  400. }else{
  401. canvases[this.canvasId].showToolTip(e, {
  402. format: function(item, category) {
  403. if(category){
  404. return category + ' ' + item.name + ':' + item.data
  405. }else{
  406. return item.name + ':' + item.data
  407. }
  408. }
  409. });
  410. }
  411. }).exec()
  412. }
  413. },
  414. mouseup(e){
  415. if(options[this.canvasId].enableScroll){
  416. chartdom = uni.createSelectorQuery().in(this).select('.chartsview')
  417. chartdom.boundingClientRect(data => {
  418. e.changedTouches.unshift({ x: e.pageX - data.left, y: e.clientY-data.top })
  419. e.mp.changedTouches.unshift({ x: e.pageX - data.left, y: e.clientY-data.top })
  420. canvases[this.canvasId].scrollEnd(e)
  421. options[this.canvasId].mousedown=false;
  422. this.$emit("touchEnd",{event:e,charts:canvases[this.canvasId]})
  423. }).exec();
  424. }
  425. },
  426. error(e) {
  427. console.log(e)
  428. }
  429. }
  430. };
  431. </script>
  432. <style scoped>
  433. .chartsview {
  434. width: 100%;
  435. height: 100%;
  436. display: flex;
  437. flex: 1;
  438. justify-content: center;
  439. align-items: center;
  440. }
  441. </style>