widgetAirBubbleMap.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. <template>
  2. <div :style="styleObj">
  3. <v-chart ref="myVChart" :options="options" autoresize />
  4. </div>
  5. </template>
  6. <script>
  7. import {targetWidgetLinkageLogic} from "@/views/bigscreenDesigner/designer/linkageLogic";
  8. import echarts from "echarts";
  9. import "../../../../../../node_modules/echarts/map/js/china.js";
  10. //https://www.makeapie.com/editor.html?c=x2yaz6dfRw
  11. //https://www.makeapie.com/editor.html?c=xMpGBbTEKU
  12. let geoCoordMap = {
  13. 台湾省: [121.5135, 25.0308],
  14. 黑龙江省: [127.9688, 45.368],
  15. 内蒙古自治区: [110.3467, 41.4899],
  16. 吉林省: [125.8154, 44.2584],
  17. 北京市: [116.4551, 40.2539],
  18. 辽宁省: [123.1238, 42.1216],
  19. 河北省: [114.4995, 38.1006],
  20. 天津市: [117.4219, 39.4189],
  21. 山西省: [112.3352, 37.9413],
  22. 陕西省: [109.1162, 34.2004],
  23. 甘肃省: [103.5901, 36.3043],
  24. 宁夏回族自治区: [106.3586, 38.1775],
  25. 青海省: [101.4038, 36.8207],
  26. 新疆维吾尔自治区: [87.9236, 43.5883],
  27. 西藏自治区: [91.11, 29.97],
  28. 四川省: [103.9526, 30.7617],
  29. 重庆市: [108.384366, 30.439702],
  30. 山东省: [117.1582, 36.8701],
  31. 河南省: [113.4668, 34.6234],
  32. 江苏省: [118.8062, 31.9208],
  33. 安徽省: [117.29, 32.0581],
  34. 湖北省: [114.3896, 30.6628],
  35. 浙江省: [119.5313, 29.8773],
  36. 福建省: [119.4543, 25.9222],
  37. 江西省: [116.0046, 28.6633],
  38. 湖南省: [113.0823, 28.2568],
  39. 贵州省: [106.6992, 26.7682],
  40. 云南省: [102.9199, 25.4663],
  41. 广东省: [113.12244, 23.009505],
  42. 广西壮族自治区: [108.479, 23.1152],
  43. 海南省: [110.3893, 19.8516],
  44. 上海市: [121.4648, 31.2891],
  45. 香港: [114.173355, 22.320048],
  46. 澳门: [113.54909, 22.198951],
  47. };
  48. let data = [
  49. {
  50. name: "南海诸岛",
  51. value: 1,
  52. },
  53. {
  54. name: "北京",
  55. value: 524,
  56. },
  57. {
  58. name: "天津",
  59. value: 14,
  60. },
  61. {
  62. name: "上海",
  63. value: 150,
  64. },
  65. {
  66. name: "重庆",
  67. value: 75,
  68. },
  69. {
  70. name: "河北",
  71. value: 13,
  72. },
  73. {
  74. name: "河南",
  75. value: 83,
  76. },
  77. {
  78. name: "云南",
  79. value: 11,
  80. },
  81. {
  82. name: "辽宁",
  83. value: 19,
  84. },
  85. {
  86. name: "黑龙江",
  87. value: 15,
  88. },
  89. {
  90. name: "湖南",
  91. value: 69,
  92. },
  93. {
  94. name: "安徽",
  95. value: 260,
  96. },
  97. {
  98. name: "山东",
  99. value: 39,
  100. },
  101. {
  102. name: "新疆",
  103. value: 4,
  104. },
  105. {
  106. name: "江苏",
  107. value: 31,
  108. },
  109. {
  110. name: "浙江",
  111. value: 104,
  112. },
  113. {
  114. name: "江西",
  115. value: 36,
  116. },
  117. {
  118. name: "湖北",
  119. value: 1052,
  120. },
  121. {
  122. name: "广西",
  123. value: 33,
  124. },
  125. {
  126. name: "甘肃",
  127. value: 347,
  128. },
  129. {
  130. name: "山西",
  131. value: 8,
  132. },
  133. {
  134. name: "内蒙古",
  135. value: 157,
  136. },
  137. {
  138. name: "陕西",
  139. value: 22,
  140. },
  141. {
  142. name: "吉林",
  143. value: 4,
  144. },
  145. {
  146. name: "福建",
  147. value: 36,
  148. },
  149. {
  150. name: "贵州",
  151. value: 39,
  152. },
  153. {
  154. name: "广东",
  155. value: 996,
  156. },
  157. {
  158. name: "青海",
  159. value: 27,
  160. },
  161. {
  162. name: "西藏",
  163. value: 31,
  164. },
  165. {
  166. name: "四川",
  167. value: 46,
  168. },
  169. {
  170. name: "宁夏",
  171. value: 16,
  172. },
  173. {
  174. name: "海南",
  175. value: 22,
  176. },
  177. {
  178. name: "台湾",
  179. value: 6,
  180. },
  181. {
  182. name: "香港",
  183. value: 2,
  184. },
  185. {
  186. name: "澳门",
  187. value: 9,
  188. },
  189. ];
  190. let convertData = function (data) {
  191. let res = [];
  192. for (let i = 0; i < data.length; i++) {
  193. let geoCoord = geoCoordMap[data[i].name];
  194. if (geoCoord) {
  195. res.push({
  196. name: data[i].name,
  197. value: geoCoord.concat(data[i].value),
  198. });
  199. }
  200. }
  201. return res;
  202. };
  203. let max = 6000,
  204. min = 10;
  205. let maxSize4Pin = 100,
  206. minSize4Pin = 20;
  207. import { eventBusParams } from "@/utils/screen";
  208. export default {
  209. name: "widgetAirBubbleMap",
  210. props: {
  211. value: Object,
  212. ispreview: Boolean,
  213. },
  214. data() {
  215. return {
  216. options: {
  217. //backgroundColor: '#0F1C3C',
  218. tooltip: {
  219. trigger: "item",
  220. formatter: function (params) {
  221. if (params.value.length > 1) {
  222. return params.data.name + "" + params.data.value[2];
  223. } else {
  224. return params.name;
  225. }
  226. },
  227. },
  228. geo: {
  229. map: "china",
  230. show: true,
  231. roam: false,
  232. label: {
  233. emphasis: {
  234. show: false,
  235. },
  236. },
  237. layoutSize: "80%",
  238. itemStyle: {
  239. normal: {
  240. borderColor: new echarts.graphic.LinearGradient(
  241. 0,
  242. 0,
  243. 0,
  244. 1,
  245. [
  246. {
  247. offset: 0,
  248. color: "#00F6FF",
  249. },
  250. {
  251. offset: 1,
  252. color: "#53D9FF",
  253. },
  254. ],
  255. false
  256. ),
  257. borderWidth: 3,
  258. shadowColor: "rgba(10,76,139,1)",
  259. shadowOffsetY: 0,
  260. shadowBlur: 60,
  261. },
  262. },
  263. },
  264. series: [
  265. {
  266. type: "map",
  267. map: "china",
  268. aspectScale: 0.75,
  269. label: {
  270. normal: {
  271. //调整数值
  272. position: "right",
  273. show: true,
  274. color: "#53D9FF",
  275. fontSize: 20,
  276. },
  277. emphasis: {
  278. show: true,
  279. },
  280. },
  281. itemStyle: {
  282. normal: {
  283. //地图块颜色
  284. areaColor: {
  285. x: 0,
  286. y: 0,
  287. x2: 0,
  288. y2: 1,
  289. colorStops: [
  290. {
  291. offset: 0,
  292. color: "#073684", // 0% 处的颜色
  293. },
  294. {
  295. offset: 1,
  296. color: "#061E3D", // 100% 处的颜色
  297. },
  298. ],
  299. },
  300. borderColor: "#215495",
  301. borderWidth: 1,
  302. },
  303. //鼠标放置颜色加深
  304. emphasis: {
  305. areaColor: {
  306. x: 0,
  307. y: 0,
  308. x2: 0,
  309. y2: 1,
  310. colorStops: [
  311. {
  312. offset: 0,
  313. color: "#073684", // 0% 处的颜色
  314. },
  315. {
  316. offset: 1,
  317. color: "#2B91B7", // 100% 处的颜色
  318. },
  319. ],
  320. },
  321. },
  322. },
  323. data: data,
  324. },
  325. {
  326. type: "effectScatter",
  327. coordinateSystem: "geo",
  328. rippleEffect: {
  329. brushType: "stroke",
  330. },
  331. showEffectOn: "render",
  332. itemStyle: {
  333. normal: {
  334. //气泡颜色
  335. color: {
  336. type: "radial",
  337. x: 0.5,
  338. y: 0.5,
  339. r: 0.5,
  340. colorStops: [
  341. {
  342. offset: 0,
  343. color: "rgba(5,80,151,0.2)",
  344. },
  345. {
  346. offset: 0.8,
  347. color: "rgba(5,80,151,0.8)",
  348. },
  349. {
  350. offset: 1,
  351. color: "rgba(0,108,255,0.7)",
  352. },
  353. ],
  354. global: false,
  355. },
  356. },
  357. },
  358. label: {
  359. normal: {
  360. show: true,
  361. color: "#fff",
  362. fontWeight: "bold",
  363. position: "inside",
  364. formatter: function (para) {
  365. return "{cnNum|" + para.data.value[2] + "}";
  366. },
  367. rich: {
  368. cnNum: {
  369. fontSize: 13,
  370. color: "#D4EEFF",
  371. },
  372. },
  373. },
  374. },
  375. symbol: "circle",
  376. symbolSize: function (val) {
  377. if (val[2] == 0) {
  378. return 0;
  379. }
  380. return (
  381. ((maxSize4Pin - minSize4Pin) / (max - min)) * val[2] +
  382. (maxSize4Pin -
  383. ((maxSize4Pin - minSize4Pin) / (max - min)) * max) *
  384. 1.2
  385. );
  386. },
  387. data: convertData(data),
  388. zlevel: 1,
  389. },
  390. ],
  391. },
  392. optionsStyle: {}, // 样式
  393. optionsData: {}, // 数据
  394. optionsCollapse: {}, // 图标属性
  395. optionsSetup: {},
  396. flagInter: null,
  397. };
  398. },
  399. computed: {
  400. styleObj() {
  401. return {
  402. position: this.ispreview ? "absolute" : "static",
  403. width: this.optionsStyle.width + "px",
  404. height: this.optionsStyle.height + "px",
  405. left: this.optionsStyle.left + "px",
  406. top: this.optionsStyle.top + "px",
  407. background: this.optionsSetup.background,
  408. };
  409. },
  410. allComponentLinkage() {
  411. return this.$store.state.designer.allComponentLinkage;
  412. },
  413. },
  414. watch: {
  415. value: {
  416. handler(val) {
  417. this.optionsStyle = val.position;
  418. this.optionsData = val.data;
  419. this.optionsCollapse = val.setup;
  420. this.optionsSetup = val.setup;
  421. this.editorOptions();
  422. },
  423. deep: true,
  424. },
  425. },
  426. mounted() {
  427. this.optionsStyle = this.value.position;
  428. this.optionsData = this.value.data;
  429. this.optionsCollapse = this.value.setup;
  430. this.optionsSetup = this.value.setup;
  431. this.editorOptions();
  432. eventBusParams(
  433. this.optionsSetup,
  434. this.optionsData,
  435. (dynamicData, optionsSetup) => {
  436. console.log("dynamicData", dynamicData);
  437. this.getEchartData(dynamicData, optionsSetup);
  438. }
  439. );
  440. targetWidgetLinkageLogic(this); // 联动-目标组件逻辑
  441. },
  442. methods: {
  443. // 修改图标options属性
  444. editorOptions() {
  445. this.setOptionsTitle();
  446. this.setOptionTextValue();
  447. //this.setOptionDataValue();
  448. this.setOptionsData();
  449. this.setOptionAirSize();
  450. this.setOptionsTooltip();
  451. this.setOptionMapBlock();
  452. },
  453. // 标题设置
  454. setOptionsTitle() {
  455. const optionsSetup = this.optionsSetup;
  456. const title = {};
  457. title.text = optionsSetup.titleText;
  458. title.show = optionsSetup.isNoTitle;
  459. title.left = optionsSetup.textAlign;
  460. title.textStyle = {
  461. color: optionsSetup.textColor,
  462. fontSize: optionsSetup.textFontSize,
  463. fontWeight: optionsSetup.textFontWeight,
  464. fontStyle: optionsSetup.textFontStyle,
  465. };
  466. title.subtext = optionsSetup.subText;
  467. title.subtextStyle = {
  468. color: optionsSetup.subTextColor,
  469. fontWeight: optionsSetup.subTextFontWeight,
  470. fontSize: optionsSetup.subTextFontSize,
  471. fontStyle: optionsSetup.subTextFontStyle,
  472. };
  473. this.options.title = title;
  474. },
  475. setOptionTextValue() {
  476. const optionsSetup = this.optionsSetup;
  477. const label = this.options.series[0]["label"];
  478. const normal = {
  479. position: "right",
  480. show: optionsSetup.isShowMap,
  481. color: optionsSetup.fontTextColor,
  482. fontSize: optionsSetup.fontTextSize,
  483. fontWeight: optionsSetup.fontTextWeight,
  484. };
  485. label["normal"] = normal;
  486. },
  487. setOptionMapBlock() {
  488. const optionsSetup = this.optionsSetup;
  489. const itemStyle = this.options.series[0]["itemStyle"];
  490. const normal = {
  491. //地图块颜色
  492. areaColor: {
  493. x: 0,
  494. y: 0,
  495. x2: 0,
  496. y2: 1,
  497. colorStops: [
  498. {
  499. offset: 0,
  500. color: optionsSetup.font0PreColor, // 0% 处的颜色
  501. },
  502. {
  503. offset: 1,
  504. color: optionsSetup.font100PreColor, // 100% 处的颜色
  505. },
  506. ],
  507. },
  508. borderColor: "#215495",
  509. borderWidth: 1,
  510. };
  511. //鼠标放置颜色加深
  512. const emphasis = {
  513. areaColor: {
  514. x: 0,
  515. y: 0,
  516. x2: 0,
  517. y2: 1,
  518. colorStops: [
  519. {
  520. offset: 0,
  521. color: "#073684", // 0% 处的颜色
  522. },
  523. {
  524. offset: 1,
  525. color: optionsSetup.fontHighlightColor, // 100% 处的颜色
  526. },
  527. ],
  528. },
  529. };
  530. itemStyle["normal"] = normal;
  531. itemStyle["emphasis"] = emphasis;
  532. },
  533. setOptionAirSize() {
  534. maxSize4Pin = this.optionsSetup.fontmaxSize4Pin;
  535. minSize4Pin = this.optionsSetup.fontminSize4Pin;
  536. },
  537. // tooltip 设置
  538. setOptionsTooltip() {
  539. const optionsSetup = this.optionsSetup;
  540. const tooltip = {
  541. trigger: "item",
  542. show: true,
  543. textStyle: {
  544. color: optionsSetup.tipsColor,
  545. fontSize: optionsSetup.tipsFontSize,
  546. },
  547. formatter: function (params) {
  548. if (params.value.length > 1) {
  549. return params.data.name + "" + params.data.value[2];
  550. } else {
  551. return params.name;
  552. }
  553. },
  554. };
  555. this.options.tooltip = tooltip;
  556. },
  557. //数据解析
  558. setOptionsData(e, paramsConfig) {
  559. const optionsData = this.optionsData; // 数据类型 静态 or 动态
  560. // 联动接收者逻辑开始
  561. optionsData.dynamicData = optionsData.dynamicData || {}; // 兼容 dynamicData undefined
  562. const myDynamicData = optionsData.dynamicData;
  563. clearInterval(this.flagInter); // 不管咋,先干掉上一次的定时任务,避免多跑
  564. if (
  565. e &&
  566. optionsData.dataType !== "staticData" &&
  567. Object.keys(myDynamicData.contextData).length
  568. ) {
  569. const keyArr = Object.keys(myDynamicData.contextData);
  570. paramsConfig.forEach((conf) => {
  571. if (keyArr.includes(conf.targetKey)) {
  572. myDynamicData.contextData[conf.targetKey] = e[conf.originKey];
  573. }
  574. });
  575. }
  576. // 联动接收者逻辑结束
  577. optionsData.dataType == "staticData"
  578. ? this.staticDataFn(optionsData.staticData)
  579. : this.dynamicDataFn(optionsData.dynamicData, optionsData.refreshTime);
  580. },
  581. staticDataFn(val) {
  582. this.options.series[0]["data"] = val;
  583. const optionsSetup = this.optionsSetup;
  584. const label = this.options.series[1]["label"];
  585. const normal = {
  586. show: true,
  587. color: "#fff",
  588. fontWeight: "bold",
  589. position: "inside",
  590. formatter: function (para) {
  591. return "{cnNum|" + para.data.value[2] + "}";
  592. },
  593. rich: {
  594. cnNum: {
  595. fontSize: optionsSetup.fontDataSize,
  596. color: optionsSetup.fontDataColor,
  597. fontWeight: optionsSetup.fontDataWeight,
  598. },
  599. },
  600. };
  601. const data = convertData(val);
  602. this.options.series[1]["data"] = data;
  603. label["normal"] = normal;
  604. },
  605. dynamicDataFn(val, refreshTime) {
  606. if (!val) return;
  607. if (this.ispreview) {
  608. this.getEchartData(val);
  609. this.flagInter = setInterval(() => {
  610. this.getEchartData(val);
  611. }, refreshTime);
  612. } else {
  613. this.getEchartData(val);
  614. }
  615. },
  616. getEchartData(val) {
  617. const data = this.queryEchartsData(val);
  618. data.then((res) => {
  619. this.renderingFn(res);
  620. });
  621. },
  622. renderingFn(val) {
  623. this.options.series[0]["data"] = val;
  624. const optionsSetup = this.optionsSetup;
  625. const label = this.options.series[1]["label"];
  626. const normal = {
  627. show: true,
  628. color: "#fff",
  629. fontWeight: "bold",
  630. position: "inside",
  631. formatter: function (para) {
  632. return "{cnNum|" + para.data.value[2] + "}";
  633. },
  634. rich: {
  635. cnNum: {
  636. fontSize: optionsSetup.fontDataSize,
  637. color: optionsSetup.fontDataColor,
  638. fontWeight: optionsSetup.fontDataWeight,
  639. },
  640. },
  641. };
  642. const data = convertData(val);
  643. this.options.series[1]["data"] = data;
  644. label["normal"] = normal;
  645. },
  646. },
  647. };
  648. </script>
  649. <style lang="scss" scoped>
  650. .echartMap {
  651. width: 100%;
  652. height: 100%;
  653. }
  654. .echarts {
  655. width: 100%;
  656. height: 100%;
  657. overflow: hidden;
  658. }
  659. </style>