cwg-charts.vue 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200
  1. <template>
  2. <view class="chartsview" :id="'ChartBoxId' + cid">
  3. <view v-if="mixinDatacomLoading">
  4. <uni-loading :loadingType="loadingType" />
  5. </view>
  6. <view v-if="mixinDatacomErrorMessage && errorShow" @tap="reloading">
  7. <uni-error :errorMessage="errorMessage" />
  8. </view>
  9. <!-- #ifdef APP-VUE || H5 -->
  10. <block v-if="echarts">
  11. <view :style="{ background: background }" style="width: 100%;height: 100%;" :data-directory="directory"
  12. :id="'EC' + cid" :prop="echartsOpts" :change:prop="rdcharts.ecinit" :resize="echartsResize"
  13. :change:resize="rdcharts.ecresize" v-show="showchart" class="e-echarts" />
  14. </block>
  15. <block v-else>
  16. <view v-on:tap="rdcharts.tap" v-on:mousemove="rdcharts.mouseMove" v-on:mousedown="rdcharts.mouseDown"
  17. v-on:mouseup="rdcharts.mouseUp" v-on:touchstart="rdcharts.touchStart" v-on:touchmove="rdcharts.touchMove"
  18. v-on:touchend="rdcharts.touchEnd" :id="'UC' + cid" :prop="uchartsOpts" :change:prop="rdcharts.ucinit">
  19. <canvas :id="cid" :canvasId="cid"
  20. :style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }"
  21. :disable-scroll="disableScroll" @error="_error" v-show="showchart" />
  22. </view>
  23. </block>
  24. <!-- #endif -->
  25. </view>
  26. </template>
  27. <script>
  28. import cfu from '../static/js/u-charts/config-ucharts.js';
  29. // #ifdef APP-VUE || H5
  30. import cfe from '../static/js/u-charts/config-echarts.js';
  31. // #endif
  32. function deepCloneAssign(origin = {}, ...args) {
  33. for (let i in args) {
  34. for (let key in args[i]) {
  35. if (args[i].hasOwnProperty(key)) {
  36. origin[key] = args[i][key] && typeof args[i][key] === 'object' ? deepCloneAssign(Array.isArray(args[i][key]) ? [] : {}, origin[key], args[i][key]) : args[i][key];
  37. }
  38. }
  39. }
  40. return origin;
  41. }
  42. function formatterAssign(args, formatter) {
  43. for (let key in args) {
  44. if (args.hasOwnProperty(key) && args[key] !== null && typeof args[key] === 'object') {
  45. formatterAssign(args[key], formatter)
  46. } else if (key === 'format' && typeof args[key] === 'string') {
  47. args['formatter'] = formatter[args[key]] ? formatter[args[key]] : undefined;
  48. }
  49. }
  50. return args;
  51. }
  52. // 时间转换函数,为了匹配uniClinetDB读取出的时间与categories不同
  53. function getFormatDate(date) {
  54. var seperator = "-";
  55. var year = date.getFullYear();
  56. var month = date.getMonth() + 1;
  57. var strDate = date.getDate();
  58. if (month >= 1 && month <= 9) {
  59. month = "0" + month;
  60. }
  61. if (strDate >= 0 && strDate <= 9) {
  62. strDate = "0" + strDate;
  63. }
  64. var currentdate = year + seperator + month + seperator + strDate;
  65. return currentdate;
  66. }
  67. var lastMoveTime = null;
  68. /**
  69. * 防抖
  70. */
  71. function debounce(fn, wait) {
  72. let timer = false;
  73. return function () {
  74. clearTimeout(timer);
  75. timer && clearTimeout(timer);
  76. timer = setTimeout(() => {
  77. timer = false;
  78. fn.apply(this, arguments);
  79. }, wait);
  80. };
  81. }
  82. export default {
  83. name: 'cwg-charts',
  84. // mixins: [uniCloud.mixinDatacom],
  85. props: {
  86. type: {
  87. type: String,
  88. default: null
  89. },
  90. canvasId: {
  91. type: String,
  92. default: 'uchartsid'
  93. },
  94. background: {
  95. type: String,
  96. default: 'rgba(0,0,0,0)'
  97. },
  98. animation: {
  99. type: Boolean,
  100. default: true
  101. },
  102. chartData: {
  103. type: Object,
  104. default() {
  105. return {
  106. categories: [],
  107. series: []
  108. };
  109. }
  110. },
  111. opts: {
  112. type: Object,
  113. default() {
  114. return {};
  115. }
  116. },
  117. eopts: {
  118. type: Object,
  119. default() {
  120. return {};
  121. }
  122. },
  123. loadingType: {
  124. type: Number,
  125. default: 2
  126. },
  127. errorShow: {
  128. type: Boolean,
  129. default: true
  130. },
  131. errorReload: {
  132. type: Boolean,
  133. default: true
  134. },
  135. errorMessage: {
  136. type: String,
  137. default: null
  138. },
  139. inScrollView: {
  140. type: Boolean,
  141. default: false
  142. },
  143. reshow: {
  144. type: Boolean,
  145. default: false
  146. },
  147. reload: {
  148. type: Boolean,
  149. default: false
  150. },
  151. disableScroll: {
  152. type: Boolean,
  153. default: false
  154. },
  155. optsWatch: {
  156. type: Boolean,
  157. default: true
  158. },
  159. onzoom: {
  160. type: Boolean,
  161. default: false
  162. },
  163. ontap: {
  164. type: Boolean,
  165. default: true
  166. },
  167. ontouch: {
  168. type: Boolean,
  169. default: false
  170. },
  171. onmouse: {
  172. type: Boolean,
  173. default: true
  174. },
  175. onmovetip: {
  176. type: Boolean,
  177. default: false
  178. },
  179. echartsH5: {
  180. type: Boolean,
  181. default: false
  182. },
  183. echartsApp: {
  184. type: Boolean,
  185. default: false
  186. },
  187. tooltipShow: {
  188. type: Boolean,
  189. default: true
  190. },
  191. tooltipFormat: {
  192. type: String,
  193. default: undefined
  194. },
  195. tooltipCustom: {
  196. type: Object,
  197. default: undefined
  198. },
  199. startDate: {
  200. type: String,
  201. default: undefined
  202. },
  203. endDate: {
  204. type: String,
  205. default: undefined
  206. },
  207. textEnum: {
  208. type: Array,
  209. default() {
  210. return []
  211. }
  212. },
  213. groupEnum: {
  214. type: Array,
  215. default() {
  216. return []
  217. }
  218. },
  219. pageScrollTop: {
  220. type: Number,
  221. default: 0
  222. },
  223. directory: {
  224. type: String,
  225. default: ''
  226. },
  227. tapLegend: {
  228. type: Boolean,
  229. default: true
  230. },
  231. menus: {
  232. type: Array,
  233. default() {
  234. return []
  235. }
  236. }
  237. },
  238. data() {
  239. return {
  240. cid: 'uchartsid',
  241. inH5: false,
  242. inApp: false,
  243. inWin: false,
  244. openmouse: false,
  245. cWidth: 375,
  246. cHeight: 250,
  247. showchart: false,
  248. echarts: false,
  249. echartsResize: {
  250. state: false
  251. },
  252. uchartsOpts: {},
  253. echartsOpts: {},
  254. drawData: {},
  255. lastDrawTime: null,
  256. };
  257. },
  258. created() {
  259. this.cid = this.canvasId
  260. if (this.canvasId == 'uchartsid' || this.canvasId == '') {
  261. let t = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
  262. let len = t.length
  263. let id = ''
  264. for (let i = 0; i < 32; i++) {
  265. id += t.charAt(Math.floor(Math.random() * len))
  266. }
  267. this.cid = id
  268. }
  269. const systemInfo = uni.getSystemInfoSync()
  270. if (systemInfo.platform === 'windows' || systemInfo.platform === 'macos') {
  271. this.inWin = true;
  272. }
  273. },
  274. mounted() {
  275. // #ifdef APP-VUE
  276. this.inApp = true;
  277. if (this.echartsApp === true) {
  278. this.echarts = true;
  279. this.openmouse = false;
  280. }
  281. // #endif
  282. // #ifdef H5
  283. this.inH5 = true;
  284. if (this.inWin === true) {
  285. this.openmouse = this.onmouse;
  286. }
  287. if (this.echartsH5 === true) {
  288. this.echarts = true;
  289. }
  290. // #endif
  291. this.$nextTick(() => {
  292. this.beforeInit();
  293. })
  294. const _this = this;
  295. uni.onWindowResize(
  296. debounce(function (res) {
  297. if (_this.mixinDatacomLoading == true) {
  298. return;
  299. }
  300. let errmsg = _this.mixinDatacomErrorMessage;
  301. if (errmsg !== null && errmsg !== 'null' && errmsg !== '') {
  302. return;
  303. }
  304. if (_this.echarts) {
  305. _this.echartsResize = {
  306. state: !_this.echartsResize.state,
  307. timestamp: Date.now()
  308. };
  309. } else {
  310. _this.resizeHandler();
  311. }
  312. }, 500)
  313. );
  314. },
  315. destroyed() {
  316. if (this.echarts === true) {
  317. delete cfe.option[this.cid]
  318. delete cfe.instance[this.cid]
  319. } else {
  320. delete cfu.option[this.cid]
  321. delete cfu.instance[this.cid]
  322. }
  323. uni.offWindowResize(() => { })
  324. },
  325. watch: {
  326. chartDataProps: {
  327. handler(val, oldval) {
  328. if (typeof val === 'object') {
  329. if (JSON.stringify(val) !== JSON.stringify(oldval)) {
  330. this._clearChart();
  331. if (val.series && val.series.length > 0) {
  332. this.beforeInit();
  333. } else {
  334. this.mixinDatacomLoading = true;
  335. this.showchart = false;
  336. this.mixinDatacomErrorMessage = null;
  337. }
  338. }
  339. } else {
  340. this.mixinDatacomLoading = false;
  341. this._clearChart();
  342. this.showchart = false;
  343. this.mixinDatacomErrorMessage = '参数错误:chartData数据类型错误';
  344. }
  345. },
  346. immediate: false,
  347. deep: true
  348. },
  349. localdata: {
  350. handler(val, oldval) {
  351. if (JSON.stringify(val) !== JSON.stringify(oldval)) {
  352. if (val.length > 0) {
  353. this.beforeInit();
  354. } else {
  355. this.mixinDatacomLoading = true;
  356. this._clearChart();
  357. this.showchart = false;
  358. this.mixinDatacomErrorMessage = null;
  359. }
  360. }
  361. },
  362. immediate: false,
  363. deep: true
  364. },
  365. optsProps: {
  366. handler(val, oldval) {
  367. if (typeof val === 'object') {
  368. if (JSON.stringify(val) !== JSON.stringify(oldval) && this.echarts === false && this.optsWatch == true) {
  369. this.checkData(this.drawData);
  370. }
  371. } else {
  372. this.mixinDatacomLoading = false;
  373. this._clearChart();
  374. this.showchart = false;
  375. this.mixinDatacomErrorMessage = '参数错误:opts数据类型错误';
  376. }
  377. },
  378. immediate: false,
  379. deep: true
  380. },
  381. eoptsProps: {
  382. handler(val, oldval) {
  383. if (typeof val === 'object') {
  384. if (JSON.stringify(val) !== JSON.stringify(oldval) && this.echarts === true) {
  385. this.checkData(this.drawData);
  386. }
  387. } else {
  388. this.mixinDatacomLoading = false;
  389. this.showchart = false;
  390. this.mixinDatacomErrorMessage = '参数错误:eopts数据类型错误';
  391. }
  392. },
  393. immediate: false,
  394. deep: true
  395. },
  396. reshow(val, oldval) {
  397. if (val === true && this.mixinDatacomLoading === false) {
  398. setTimeout(() => {
  399. this.mixinDatacomErrorMessage = null;
  400. this.echartsResize.state = !this.echartsResize.state;
  401. this.checkData(this.drawData);
  402. }, 200);
  403. }
  404. },
  405. reload(val, oldval) {
  406. if (val === true) {
  407. this.showchart = false;
  408. this.mixinDatacomErrorMessage = null;
  409. this.reloading();
  410. }
  411. },
  412. mixinDatacomErrorMessage(val, oldval) {
  413. if (val) {
  414. this.emitMsg({ name: 'error', params: { type: "error", errorShow: this.errorShow, msg: val, id: this.cid } });
  415. if (this.errorShow) {
  416. console.log('[图表组件]' + val);
  417. }
  418. }
  419. },
  420. errorMessage(val, oldval) {
  421. if (val && this.errorShow && val !== null && val !== 'null' && val !== '') {
  422. this.showchart = false;
  423. this.mixinDatacomLoading = false;
  424. this.mixinDatacomErrorMessage = val;
  425. } else {
  426. this.showchart = false;
  427. this.mixinDatacomErrorMessage = null;
  428. this.reloading();
  429. }
  430. }
  431. },
  432. computed: {
  433. optsProps() {
  434. return JSON.parse(JSON.stringify(this.opts));
  435. },
  436. eoptsProps() {
  437. return JSON.parse(JSON.stringify(this.eopts));
  438. },
  439. chartDataProps() {
  440. return JSON.parse(JSON.stringify(this.chartData));
  441. },
  442. },
  443. methods: {
  444. beforeInit() {
  445. this.mixinDatacomErrorMessage = null;
  446. if (typeof this.chartData === 'object' && this.chartData != null && this.chartData.series !== undefined && this.chartData.series.length > 0) {
  447. //拷贝一下chartData,为了opts变更后统一数据来源
  448. this.drawData = deepCloneAssign({}, this.chartData);
  449. this.mixinDatacomLoading = false;
  450. this.showchart = true;
  451. this.checkData(this.chartData);
  452. } else if (this.localdata.length > 0) {
  453. this.mixinDatacomLoading = false;
  454. this.showchart = true;
  455. this.localdataInit(this.localdata);
  456. } else if (this.collection !== '') {
  457. this.mixinDatacomLoading = false;
  458. this.getCloudData();
  459. } else {
  460. this.mixinDatacomLoading = true;
  461. }
  462. },
  463. localdataInit(resdata) {
  464. //替换enum类型为正确的描述
  465. if (this.groupEnum.length > 0) {
  466. for (let i = 0; i < resdata.length; i++) {
  467. for (let j = 0; j < this.groupEnum.length; j++) {
  468. if (resdata[i].group === this.groupEnum[j].value) {
  469. resdata[i].group = this.groupEnum[j].text
  470. }
  471. }
  472. }
  473. }
  474. if (this.textEnum.length > 0) {
  475. for (let i = 0; i < resdata.length; i++) {
  476. for (let j = 0; j < this.textEnum.length; j++) {
  477. if (resdata[i].text === this.textEnum[j].value) {
  478. resdata[i].text = this.textEnum[j].text
  479. }
  480. }
  481. }
  482. }
  483. let needCategories = false;
  484. let tmpData = { categories: [], series: [] }
  485. let tmpcategories = []
  486. let tmpseries = [];
  487. //拼接categories
  488. if (this.echarts === true) {
  489. needCategories = cfe.categories.includes(this.type)
  490. } else {
  491. needCategories = cfu.categories.includes(this.type)
  492. }
  493. if (needCategories === true) {
  494. //如果props中的chartData带有categories,则优先使用chartData的categories
  495. if (this.chartData && this.chartData.categories && this.chartData.categories.length > 0) {
  496. tmpcategories = this.chartData.categories
  497. } else {
  498. //如果是日期类型的数据,不管是本地数据还是云数据,都按起止日期自动拼接categories
  499. if (this.startDate && this.endDate) {
  500. let idate = new Date(this.startDate)
  501. let edate = new Date(this.endDate)
  502. while (idate <= edate) {
  503. tmpcategories.push(getFormatDate(idate))
  504. idate = idate.setDate(idate.getDate() + 1)
  505. idate = new Date(idate)
  506. }
  507. //否则从结果中去重并拼接categories
  508. } else {
  509. let tempckey = {};
  510. resdata.map(function (item, index) {
  511. if (item.text != undefined && !tempckey[item.text]) {
  512. tmpcategories.push(item.text)
  513. tempckey[item.text] = true
  514. }
  515. });
  516. }
  517. }
  518. tmpData.categories = tmpcategories
  519. }
  520. //拼接series
  521. let tempskey = {};
  522. resdata.map(function (item, index) {
  523. if (item.group != undefined && !tempskey[item.group]) {
  524. tmpseries.push({ name: item.group, data: [] });
  525. tempskey[item.group] = true;
  526. }
  527. });
  528. //如果没有获取到分组名称(可能是带categories的数据,也可能是不带的饼图类)
  529. if (tmpseries.length == 0) {
  530. tmpseries = [{ name: '默认分组', data: [] }];
  531. //如果是需要categories的图表类型
  532. if (needCategories === true) {
  533. for (let j = 0; j < tmpcategories.length; j++) {
  534. let seriesdata = 0;
  535. for (let i = 0; i < resdata.length; i++) {
  536. if (resdata[i].text == tmpcategories[j]) {
  537. seriesdata = resdata[i].value;
  538. }
  539. }
  540. tmpseries[0].data.push(seriesdata);
  541. }
  542. //如果是饼图类的图表类型
  543. } else {
  544. for (let i = 0; i < resdata.length; i++) {
  545. tmpseries[0].data.push({ "name": resdata[i].text, "value": resdata[i].value });
  546. }
  547. }
  548. //如果有分组名
  549. } else {
  550. for (let k = 0; k < tmpseries.length; k++) {
  551. //如果有categories
  552. if (tmpcategories.length > 0) {
  553. for (let j = 0; j < tmpcategories.length; j++) {
  554. let seriesdata = 0;
  555. for (let i = 0; i < resdata.length; i++) {
  556. if (tmpseries[k].name == resdata[i].group && resdata[i].text == tmpcategories[j]) {
  557. seriesdata = resdata[i].value;
  558. }
  559. }
  560. tmpseries[k].data.push(seriesdata);
  561. }
  562. //如果传了group而没有传text,即没有categories(正常情况下这种数据是不符合数据要求规范的)
  563. } else {
  564. for (let i = 0; i < resdata.length; i++) {
  565. if (tmpseries[k].name == resdata[i].group) {
  566. tmpseries[k].data.push(resdata[i].value);
  567. }
  568. }
  569. }
  570. }
  571. }
  572. tmpData.series = tmpseries
  573. //拷贝一下chartData,为了opts变更后统一数据来源
  574. this.drawData = deepCloneAssign({}, tmpData);
  575. this.checkData(tmpData)
  576. },
  577. reloading() {
  578. if (this.errorReload === false) {
  579. return;
  580. }
  581. this.showchart = false;
  582. this.mixinDatacomErrorMessage = null;
  583. if (this.collection !== '') {
  584. this.mixinDatacomLoading = false;
  585. this.onMixinDatacomPropsChange(true);
  586. } else {
  587. this.beforeInit();
  588. }
  589. },
  590. checkData(anyData) {
  591. let cid = this.cid
  592. //复位opts或eopts
  593. if (this.echarts === true) {
  594. cfe.option[cid] = deepCloneAssign({}, this.eopts);
  595. cfe.option[cid].id = cid;
  596. cfe.option[cid].type = this.type;
  597. } else {
  598. if (this.type && cfu.type.includes(this.type)) {
  599. cfu.option[cid] = deepCloneAssign({}, cfu[this.type], this.opts);
  600. cfu.option[cid].canvasId = cid;
  601. } else {
  602. this.mixinDatacomLoading = false;
  603. this.showchart = false;
  604. this.mixinDatacomErrorMessage = '参数错误:props参数中type类型不正确';
  605. }
  606. }
  607. //挂载categories和series
  608. let newData = deepCloneAssign({}, anyData);
  609. if (newData.series !== undefined && newData.series.length > 0) {
  610. this.mixinDatacomErrorMessage = null;
  611. if (this.echarts === true) {
  612. cfe.option[cid].chartData = newData;
  613. this.$nextTick(() => {
  614. this.init()
  615. })
  616. } else {
  617. cfu.option[cid].categories = newData.categories;
  618. cfu.option[cid].series = newData.series;
  619. this.$nextTick(() => {
  620. this.init()
  621. })
  622. }
  623. }
  624. },
  625. resizeHandler() {
  626. //渲染防抖
  627. let currTime = Date.now();
  628. let lastDrawTime = this.lastDrawTime ? this.lastDrawTime : currTime - 3000;
  629. let duration = currTime - lastDrawTime;
  630. if (duration < 1000) return;
  631. let chartdom = uni
  632. .createSelectorQuery()
  633. .select('#ChartBoxId' + this.cid)
  634. .boundingClientRect(data => {
  635. this.showchart = true;
  636. if (data.width > 0 && data.height > 0) {
  637. if (data.width !== this.cWidth || data.height !== this.cHeight) {
  638. this.checkData(this.drawData)
  639. }
  640. }
  641. })
  642. .exec();
  643. },
  644. getCloudData() {
  645. if (this.mixinDatacomLoading == true) {
  646. return;
  647. }
  648. this.mixinDatacomLoading = true;
  649. this.mixinDatacomGet()
  650. .then(res => {
  651. this.mixinDatacomResData = res.result.data;
  652. this.localdataInit(this.mixinDatacomResData);
  653. })
  654. .catch(err => {
  655. this.mixinDatacomLoading = false;
  656. this.showchart = false;
  657. this.mixinDatacomErrorMessage = '请求错误:' + err;
  658. });
  659. },
  660. onMixinDatacomPropsChange(needReset, changed) {
  661. if (needReset == true && this.collection !== '') {
  662. this.showchart = false;
  663. this.mixinDatacomErrorMessage = null;
  664. this._clearChart();
  665. this.getCloudData();
  666. }
  667. },
  668. _clearChart() {
  669. let cid = this.cid
  670. if (this.echarts !== true && cfu.option[cid] && cfu.option[cid].context) {
  671. const ctx = cfu.option[cid].context;
  672. if (typeof ctx === "object" && !!!cfu.option[cid].update) {
  673. ctx.clearRect(0, 0, this.cWidth, this.cHeight);
  674. ctx.draw();
  675. }
  676. }
  677. },
  678. init() {
  679. let cid = this.cid
  680. let chartdom = uni
  681. .createSelectorQuery()
  682. .select('#ChartBoxId' + cid)
  683. .boundingClientRect(data => {
  684. if (data.width > 0 && data.height > 0) {
  685. this.mixinDatacomLoading = false;
  686. this.showchart = true;
  687. this.lastDrawTime = Date.now();
  688. this.cWidth = data.width;
  689. this.cHeight = data.height;
  690. if (this.echarts !== true) {
  691. cfu.option[cid].background = this.background == 'rgba(0,0,0,0)' ? '#FFFFFF' : this.background;
  692. cfu.option[cid].animation = this.animation;
  693. cfu.option[cid].width = data.width;
  694. cfu.option[cid].height = data.height;
  695. cfu.option[cid].onzoom = this.onzoom;
  696. cfu.option[cid].ontap = this.ontap;
  697. cfu.option[cid].ontouch = this.ontouch;
  698. cfu.option[cid].onmouse = this.openmouse;
  699. cfu.option[cid].onmovetip = this.onmovetip;
  700. cfu.option[cid].tooltipShow = this.tooltipShow;
  701. cfu.option[cid].tooltipFormat = this.tooltipFormat;
  702. cfu.option[cid].tooltipCustom = this.tooltipCustom;
  703. cfu.option[cid].inScrollView = this.inScrollView;
  704. cfu.option[cid].lastDrawTime = this.lastDrawTime;
  705. cfu.option[cid].tapLegend = this.tapLegend;
  706. }
  707. //如果是H5或者App端,采用renderjs渲染图表
  708. if (this.inH5 || this.inApp) {
  709. if (this.echarts == true) {
  710. cfe.option[cid].ontap = this.ontap;
  711. cfe.option[cid].onmouse = this.openmouse;
  712. cfe.option[cid].tooltipShow = this.tooltipShow;
  713. cfe.option[cid].tooltipFormat = this.tooltipFormat;
  714. cfe.option[cid].tooltipCustom = this.tooltipCustom;
  715. cfe.option[cid].lastDrawTime = this.lastDrawTime;
  716. this.echartsOpts = deepCloneAssign({}, cfe.option[cid]);
  717. } else {
  718. cfu.option[cid].rotateLock = cfu.option[cid].rotate;
  719. this.uchartsOpts = deepCloneAssign({}, cfu.option[cid]);
  720. }
  721. }
  722. } else {
  723. this.mixinDatacomLoading = false;
  724. this.showchart = false;
  725. if (this.reshow == true) {
  726. this.mixinDatacomErrorMessage = '布局错误:未获取到父元素宽高尺寸!canvas-id:' + cid;
  727. }
  728. }
  729. })
  730. .exec();
  731. },
  732. saveImage() {
  733. uni.canvasToTempFilePath({
  734. canvasId: this.cid,
  735. success: res => {
  736. //#ifdef H5
  737. var a = document.createElement("a");
  738. a.href = res.tempFilePath;
  739. a.download = this.cid;
  740. a.target = '_blank'
  741. a.click();
  742. //#endif
  743. //#ifdef APP-VUE
  744. uni.saveImageToPhotosAlbum({
  745. filePath: res.tempFilePath,
  746. success: function () {
  747. uni.showToast({
  748. title: '保存成功',
  749. duration: 2000
  750. });
  751. }
  752. });
  753. //#endif
  754. }
  755. }, this);
  756. },
  757. getImage() {
  758. uni.canvasToTempFilePath({
  759. canvasId: this.cid,
  760. success: res => {
  761. this.emitMsg({ name: 'getImage', params: { type: "getImage", base64: res.tempFilePath } });
  762. }
  763. }, this);
  764. },
  765. _error(e) {
  766. this.mixinDatacomErrorMessage = e.detail.errMsg;
  767. },
  768. emitMsg(msg) {
  769. this.$emit(msg.name, msg.params);
  770. },
  771. getRenderType() {
  772. //防止如果开启echarts且父元素为v-if的情况renderjs监听不到prop变化的问题
  773. if (this.echarts === true && this.mixinDatacomLoading === false) {
  774. this.beforeInit()
  775. }
  776. },
  777. toJSON() {
  778. return this
  779. }
  780. }
  781. };
  782. </script>
  783. <!-- #ifdef APP-VUE || H5 -->
  784. <script module="rdcharts" lang="renderjs">
  785. import uChartsRD from '../static/js/u-charts/u-charts.js';
  786. import cfu from '../static/js/u-charts/config-ucharts.js';
  787. import cfe from '../static/js/u-charts/config-echarts.js';
  788. var that = {};
  789. var rootdom = null;
  790. function rddeepCloneAssign(origin = {}, ...args) {
  791. for (let i in args) {
  792. for (let key in args[i]) {
  793. if (args[i].hasOwnProperty(key)) {
  794. origin[key] = args[i][key] && typeof args[i][key] === 'object' ? rddeepCloneAssign(Array.isArray(args[i][key]) ? [] : {}, origin[key], args[i][key]) : args[i][key];
  795. }
  796. }
  797. }
  798. return origin;
  799. }
  800. function rdformatterAssign(args,formatter) {
  801. for (let key in args) {
  802. if(args.hasOwnProperty(key) && args[key] !== null && typeof args[key] === 'object'){
  803. rdformatterAssign(args[key],formatter)
  804. }else if(key === 'format' && typeof args[key] === 'string'){
  805. args['formatter'] = formatter[args[key]] ? formatter[args[key]] : undefined;
  806. }
  807. }
  808. return args;
  809. }
  810. export default {
  811. data() {
  812. return {
  813. rid:null
  814. }
  815. },
  816. mounted() {
  817. rootdom = {top:0,left:0}
  818. // #ifdef H5
  819. let dm = document.querySelectorAll('uni-main')[0]
  820. if(dm === undefined){
  821. dm = document.querySelectorAll('uni-page-wrapper')[0]
  822. }
  823. rootdom = {top:dm.offsetTop,left:dm.offsetLeft}
  824. // #endif
  825. setTimeout(()=>{
  826. if(this.rid === null){
  827. this.$ownerInstance && this.$ownerInstance.callMethod('getRenderType')
  828. }
  829. },200)
  830. },
  831. destroyed(){
  832. delete cfu.option[this.rid]
  833. delete cfu.instance[this.rid]
  834. delete cfe.option[this.rid]
  835. delete cfe.instance[this.rid]
  836. },
  837. methods: {
  838. //==============以下是ECharts的方法====================
  839. ecinit(newVal, oldVal, owner, instance){
  840. let cid = JSON.stringify(newVal.id)
  841. this.rid = cid
  842. that[cid] = this.$ownerInstance || instance
  843. let eopts = JSON.parse(JSON.stringify(newVal))
  844. let type = eopts.type;
  845. //载入并覆盖默认配置
  846. if (type && cfe.type.includes(type)) {
  847. cfe.option[cid] = rddeepCloneAssign({}, cfe[type], eopts);
  848. }else{
  849. cfe.option[cid] = rddeepCloneAssign({}, eopts);
  850. }
  851. let newData = eopts.chartData;
  852. if(newData){
  853. //挂载categories和series
  854. if(cfe.option[cid].xAxis && cfe.option[cid].xAxis.type && cfe.option[cid].xAxis.type === 'category'){
  855. cfe.option[cid].xAxis.data = newData.categories
  856. }
  857. if(cfe.option[cid].yAxis && cfe.option[cid].yAxis.type && cfe.option[cid].yAxis.type === 'category'){
  858. cfe.option[cid].yAxis.data = newData.categories
  859. }
  860. cfe.option[cid].series = []
  861. for (var i = 0; i < newData.series.length; i++) {
  862. cfe.option[cid].seriesTemplate = cfe.option[cid].seriesTemplate ? cfe.option[cid].seriesTemplate : {}
  863. let Template = rddeepCloneAssign({},cfe.option[cid].seriesTemplate,newData.series[i])
  864. cfe.option[cid].series.push(Template)
  865. }
  866. }
  867. if (typeof window.echarts === 'object') {
  868. this.newEChart()
  869. }else{
  870. const script = document.createElement('script')
  871. // #ifdef APP-VUE
  872. script.src = '/js_sdk/app-plus/echarts.min.js';
  873. // #endif
  874. // #ifdef H5
  875. script.src = '/js_sdk/h5/echarts.min.js';
  876. // #endif
  877. script.onload = this.newEChart
  878. document.head.appendChild(script)
  879. }
  880. },
  881. ecresize(newVal, oldVal, owner, instance){
  882. if(cfe.instance[this.rid]){
  883. cfe.instance[this.rid].resize()
  884. }
  885. },
  886. newEChart(){
  887. let cid = this.rid
  888. if(cfe.instance[cid] === undefined){
  889. cfe.instance[cid] = echarts.init(that[cid].$el.children[0])
  890. //ontap开启后才触发click事件
  891. if(cfe.option[cid].ontap === true){
  892. cfe.instance[cid].on('click', resdata => {
  893. let event = JSON.parse(JSON.stringify({
  894. x:resdata.event.offsetX,y:resdata.event.offsetY
  895. }))
  896. that[cid].callMethod('emitMsg',{name:"getIndex", params:{type:"getIndex", event:event, currentIndex:resdata.dataIndex, value:resdata.data, seriesName: resdata.seriesName,id:cid}})
  897. })
  898. // 增加ECharts的highlight消息,实现按下移动返回索引功能
  899. cfe.instance[cid].on('highlight', resdata => {
  900. that[cid].callMethod('emitMsg',{name:"getHighlight", params:{type:"highlight", res:resdata, id:cid}})
  901. })
  902. }
  903. this.updataEChart(cid,cfe.option[cid])
  904. }else{
  905. this.updataEChart(cid,cfe.option[cid])
  906. }
  907. },
  908. updataEChart(cid,option){
  909. //替换option内format属性为formatter的预定义方法
  910. option = rdformatterAssign(option,cfe.formatter)
  911. if(option.tooltip){
  912. option.tooltip.show = option.tooltipShow?true:false;
  913. option.tooltip.position = this.tooltipPosition()
  914. //tooltipFormat方法,替换组件的tooltipFormat为config-echarts.js内对应的方法
  915. if (typeof option.tooltipFormat === 'string' && cfe.formatter[option.tooltipFormat]) {
  916. option.tooltip.formatter = option.tooltip.formatter ? option.tooltip.formatter : cfe.formatter[option.tooltipFormat]
  917. }
  918. }
  919. // 颜色渐变添加的方法
  920. if (option.series) {
  921. for (let i in option.series) {
  922. let linearGradient = option.series[i].linearGradient
  923. if (linearGradient) {
  924. option.series[i].color = new echarts.graphic.LinearGradient(linearGradient[0],linearGradient[1],linearGradient[2],linearGradient[3],linearGradient[4])
  925. }
  926. }
  927. }
  928. cfe.instance[cid].setOption(option, option.notMerge)
  929. cfe.instance[cid].on('finished', function(){
  930. that[cid].callMethod('emitMsg',{name:"complete",params:{type:"complete",complete:true,id:cid}})
  931. if(cfe.instance[cid]){
  932. cfe.instance[cid].off('finished')
  933. }
  934. });
  935. //修复init初始化实例获取宽高不正确问题
  936. if(
  937. typeof that[cid].$el.children[0].clientWidth != 'undefined' &&
  938. (
  939. Math.abs( that[cid].$el.children[0].clientWidth - cfe.instance[cid].getWidth() )>3 ||
  940. Math.abs( that[cid].$el.children[0].clientHeight - cfe.instance[cid].getHeight() )>3
  941. )
  942. ){this.ecresize();}
  943. },
  944. tooltipPosition(){
  945. return (point, params, dom, rect, size) => {
  946. let x = point[0]
  947. let y = point[1]
  948. let viewWidth = size.viewSize[0]
  949. let viewHeight = size.viewSize[1]
  950. let boxWidth = size.contentSize[0]
  951. let boxHeight = size.contentSize[1]
  952. let posX = x + 30
  953. let posY = y + 30
  954. if (posX + boxWidth > viewWidth) {
  955. posX = x - boxWidth - 30
  956. }
  957. if (posY + boxHeight > viewHeight) {
  958. posY = y - boxHeight - 30
  959. }
  960. return [posX, posY]
  961. }
  962. },
  963. //==============以下是uCharts的方法====================
  964. ucinit(newVal, oldVal, owner, instance){
  965. if(JSON.stringify(newVal) == JSON.stringify(oldVal)){
  966. return;
  967. }
  968. if(!newVal.canvasId){
  969. return;
  970. }
  971. let cid = JSON.parse(JSON.stringify(newVal.canvasId))
  972. this.rid = cid
  973. that[cid] = this.$ownerInstance || instance
  974. cfu.option[cid] = JSON.parse(JSON.stringify(newVal))
  975. cfu.option[cid] = rdformatterAssign(cfu.option[cid],cfu.formatter)
  976. let canvasdom = document.getElementById(cid)
  977. if(canvasdom && canvasdom.children[0]){
  978. cfu.option[cid].context = canvasdom.children[0].getContext("2d")
  979. if(cfu.instance[cid] && cfu.option[cid] && cfu.option[cid].update === true){
  980. this.updataUChart()
  981. }else{
  982. setTimeout(()=>{
  983. cfu.option[cid].context.restore();
  984. cfu.option[cid].context.save();
  985. this.newUChart()
  986. },100)
  987. }
  988. }
  989. },
  990. newUChart() {
  991. let cid = this.rid
  992. cfu.instance[cid] = new uChartsRD(cfu.option[cid])
  993. cfu.instance[cid].addEventListener('renderComplete', () => {
  994. that[cid].callMethod('emitMsg',{name:"complete",params:{type:"complete",complete:true,id:cid, opts: cfu.instance[cid].opts}})
  995. cfu.instance[cid].delEventListener('renderComplete')
  996. });
  997. cfu.instance[cid].addEventListener('scrollLeft', () => {
  998. that[cid].callMethod('emitMsg',{name:"scrollLeft",params:{type:"scrollLeft",scrollLeft:true,id:cid, opts: cfu.instance[cid].opts}})
  999. });
  1000. cfu.instance[cid].addEventListener('scrollRight', () => {
  1001. that[cid].callMethod('emitMsg',{name:"scrollRight",params:{type:"scrollRight",scrollRight:true,id:cid, opts: cfu.instance[cid].opts}})
  1002. });
  1003. },
  1004. updataUChart() {
  1005. let cid = this.rid
  1006. cfu.instance[cid].updateData(cfu.option[cid])
  1007. },
  1008. tooltipDefault(item, category, index, opts) {
  1009. if (category) {
  1010. let data = item.data
  1011. if(typeof item.data === "object"){
  1012. data = item.data.value
  1013. }
  1014. return category + ' ' + item.name + ':' + data;
  1015. } else {
  1016. if (item.properties && item.properties.name) {
  1017. return item.properties.name ;
  1018. } else {
  1019. return item.name + ':' + item.data;
  1020. }
  1021. }
  1022. },
  1023. showTooltip(e,cid) {
  1024. let tc = cfu.option[cid].tooltipCustom
  1025. if (tc && tc !== undefined && tc !== null) {
  1026. let offset = undefined;
  1027. if (tc.x >= 0 && tc.y >= 0) {
  1028. offset = { x: tc.x, y: tc.y + 10 };
  1029. }
  1030. cfu.instance[cid].showToolTip(e, {
  1031. index: tc.index,
  1032. offset: offset,
  1033. textList: tc.textList,
  1034. formatter: (item, category, index, opts) => {
  1035. if (typeof cfu.option[cid].tooltipFormat === 'string' && cfu.formatter[cfu.option[cid].tooltipFormat]) {
  1036. return cfu.formatter[cfu.option[cid].tooltipFormat](item, category, index, opts);
  1037. } else {
  1038. return this.tooltipDefault(item, category, index, opts);
  1039. }
  1040. }
  1041. });
  1042. } else {
  1043. cfu.instance[cid].showToolTip(e, {
  1044. formatter: (item, category, index, opts) => {
  1045. if (typeof cfu.option[cid].tooltipFormat === 'string' && cfu.formatter[cfu.option[cid].tooltipFormat]) {
  1046. return cfu.formatter[cfu.option[cid].tooltipFormat](item, category, index, opts);
  1047. } else {
  1048. return this.tooltipDefault(item, category, index, opts);
  1049. }
  1050. }
  1051. });
  1052. }
  1053. },
  1054. tap(e) {
  1055. let cid = this.rid
  1056. let ontap = cfu.option[cid].ontap
  1057. let tooltipShow = cfu.option[cid].tooltipShow
  1058. let tapLegend = cfu.option[cid].tapLegend
  1059. if(ontap == false) return;
  1060. let currentIndex=null
  1061. let legendIndex=null
  1062. let rchartdom = document.getElementById('UC'+cid).getBoundingClientRect()
  1063. let tmpe = {}
  1064. if(e.detail.x){//tap或者click的事件
  1065. tmpe = { x: e.detail.x - rchartdom.left, y:e.detail.y - rchartdom.top + rootdom.top}
  1066. }else{//mouse的事件
  1067. tmpe = { x: e.clientX - rchartdom.left, y:e.clientY - rchartdom.top + rootdom.top}
  1068. }
  1069. e.changedTouches = [];
  1070. e.changedTouches.unshift(tmpe)
  1071. currentIndex=cfu.instance[cid].getCurrentDataIndex(e)
  1072. legendIndex=cfu.instance[cid].getLegendDataIndex(e)
  1073. if(tapLegend === true){
  1074. cfu.instance[cid].touchLegend(e);
  1075. }
  1076. if(tooltipShow==true){
  1077. this.showTooltip(e,cid)
  1078. }
  1079. that[cid].callMethod('emitMsg',{name:"getIndex",params:{type:"getIndex",event:tmpe,currentIndex:currentIndex,legendIndex:legendIndex,id:cid, opts: cfu.instance[cid].opts}})
  1080. },
  1081. touchStart(e) {
  1082. let cid = this.rid
  1083. let ontouch = cfu.option[cid].ontouch
  1084. if(ontouch == false) return;
  1085. if(cfu.option[cid].enableScroll === true && e.touches.length == 1){
  1086. cfu.instance[cid].scrollStart(e);
  1087. }
  1088. that[cid].callMethod('emitMsg',{name:"getTouchStart",params:{type:"touchStart",event:e.changedTouches[0],id:cid, opts: cfu.instance[cid].opts}})
  1089. },
  1090. touchMove(e) {
  1091. let cid = this.rid
  1092. let ontouch = cfu.option[cid].ontouch
  1093. if(ontouch == false) return;
  1094. if(cfu.option[cid].enableScroll === true && e.changedTouches.length == 1){
  1095. cfu.instance[cid].scroll(e);
  1096. }
  1097. if(cfu.option[cid].ontap === true && cfu.option[cid].enableScroll === false && cfu.option[cid].onmovetip === true){
  1098. let rchartdom = document.getElementById('UC'+cid).getBoundingClientRect()
  1099. let tmpe = { x: e.changedTouches[0].clientX - rchartdom.left, y:e.changedTouches[0].clientY - rchartdom.top + rootdom.top}
  1100. e.changedTouches.unshift(tmpe)
  1101. if(cfu.option[cid].tooltipShow === true){
  1102. this.showTooltip(e,cid)
  1103. }
  1104. }
  1105. if(ontouch === true && cfu.option[cid].enableScroll === true && cfu.option[cid].onzoom === true && e.changedTouches.length == 2){
  1106. cfu.instance[cid].dobuleZoom(e);
  1107. }
  1108. that[cid].callMethod('emitMsg',{name:"getTouchMove",params:{type:"touchMove",event:e.changedTouches[0],id:cid, opts: cfu.instance[cid].opts}})
  1109. },
  1110. touchEnd(e) {
  1111. let cid = this.rid
  1112. let ontouch = cfu.option[cid].ontouch
  1113. if(ontouch == false) return;
  1114. if(cfu.option[cid].enableScroll === true && e.touches.length == 0){
  1115. cfu.instance[cid].scrollEnd(e);
  1116. }
  1117. that[cid].callMethod('emitMsg',{name:"getTouchEnd",params:{type:"touchEnd",event:e.changedTouches[0],id:cid, opts: cfu.instance[cid].opts}})
  1118. },
  1119. mouseDown(e) {
  1120. let cid = this.rid
  1121. let onmouse = cfu.option[cid].onmouse
  1122. if(onmouse == false) return;
  1123. let rchartdom = document.getElementById('UC'+cid).getBoundingClientRect()
  1124. let tmpe = {}
  1125. tmpe = { x: e.clientX - rchartdom.left, y:e.clientY - rchartdom.top + rootdom.top}
  1126. e.changedTouches = [];
  1127. e.changedTouches.unshift(tmpe)
  1128. cfu.instance[cid].scrollStart(e)
  1129. cfu.option[cid].mousedown=true;
  1130. that[cid].callMethod('emitMsg',{name:"getTouchStart",params:{type:"mouseDown",event:tmpe,id:cid, opts: cfu.instance[cid].opts}})
  1131. },
  1132. mouseMove(e) {
  1133. let cid = this.rid
  1134. let onmouse = cfu.option[cid].onmouse
  1135. let tooltipShow = cfu.option[cid].tooltipShow
  1136. if(onmouse == false) return;
  1137. let rchartdom = document.getElementById('UC'+cid).getBoundingClientRect()
  1138. let tmpe = {}
  1139. tmpe = { x: e.clientX - rchartdom.left, y:e.clientY - rchartdom.top + rootdom.top}
  1140. e.changedTouches = [];
  1141. e.changedTouches.unshift(tmpe)
  1142. if(cfu.option[cid].mousedown){
  1143. cfu.instance[cid].scroll(e)
  1144. that[cid].callMethod('emitMsg',{name:"getTouchMove",params:{type:"mouseMove",event:tmpe,id:cid, opts: cfu.instance[cid].opts}})
  1145. }else if(cfu.instance[cid]){
  1146. if(tooltipShow==true){
  1147. this.showTooltip(e,cid)
  1148. }
  1149. }
  1150. },
  1151. mouseUp(e) {
  1152. let cid = this.rid
  1153. let onmouse = cfu.option[cid].onmouse
  1154. if(onmouse == false) return;
  1155. let rchartdom = document.getElementById('UC'+cid).getBoundingClientRect()
  1156. let tmpe = {}
  1157. tmpe = { x: e.clientX - rchartdom.left, y:e.clientY - rchartdom.top + rootdom.top}
  1158. e.changedTouches = [];
  1159. e.changedTouches.unshift(tmpe)
  1160. cfu.instance[cid].scrollEnd(e)
  1161. cfu.option[cid].mousedown=false;
  1162. that[cid].callMethod('emitMsg',{name:"getTouchEnd",params:{type:"mouseUp",event:tmpe,id:cid, opts: cfu.instance[cid].opts}})
  1163. },
  1164. }
  1165. }
  1166. </script>
  1167. <!-- #endif -->
  1168. <style scoped lang="scss">
  1169. .chartsview {
  1170. width: 100%;
  1171. height: 100%;
  1172. display: flex;
  1173. flex: 1;
  1174. justify-content: center;
  1175. align-items: center;
  1176. }
  1177. :deep(.e-echarts) {
  1178. div {
  1179. width: 100% !important;
  1180. height: 100% !important;
  1181. }
  1182. }
  1183. </style>