widgetLineMap.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  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 "../../../../../../node_modules/echarts/map/js/china.js";
  9. import "../../../../../../node_modules/echarts/map/js/world.js";
  10. import "../../../../../../node_modules/echarts/map/js/province/anhui";
  11. import "../../../../../../node_modules/echarts/map/js/province/aomen";
  12. import "../../../../../../node_modules/echarts/map/js/province/beijing";
  13. import "../../../../../../node_modules/echarts/map/js/province/chongqing";
  14. import "../../../../../../node_modules/echarts/map/js/province/fujian";
  15. import "../../../../../../node_modules/echarts/map/js/province/gansu";
  16. import "../../../../../../node_modules/echarts/map/js/province/guangxi";
  17. import "../../../../../../node_modules/echarts/map/js/province/guizhou";
  18. import "../../../../../../node_modules/echarts/map/js/province/hainan";
  19. import "../../../../../../node_modules/echarts/map/js/province/hebei";
  20. import "../../../../../../node_modules/echarts/map/js/province/heilongjiang";
  21. import "../../../../../../node_modules/echarts/map/js/province/henan";
  22. import "../../../../../../node_modules/echarts/map/js/province/hubei";
  23. import "../../../../../../node_modules/echarts/map/js/province/hunan";
  24. import "../../../../../../node_modules/echarts/map/js/province/jiangsu";
  25. import "../../../../../../node_modules/echarts/map/js/province/jiangxi";
  26. import "../../../../../../node_modules/echarts/map/js/province/jilin";
  27. import "../../../../../../node_modules/echarts/map/js/province/liaoning";
  28. import "../../../../../../node_modules/echarts/map/js/province/neimenggu";
  29. import "../../../../../../node_modules/echarts/map/js/province/ningxia";
  30. import "../../../../../../node_modules/echarts/map/js/province/qinghai";
  31. import "../../../../../../node_modules/echarts/map/js/province/shandong";
  32. import "../../../../../../node_modules/echarts/map/js/province/shanghai";
  33. import "../../../../../../node_modules/echarts/map/js/province/shanxi";
  34. import "../../../../../../node_modules/echarts/map/js/province/shanxi1";
  35. import "../../../../../../node_modules/echarts/map/js/province/sichuan";
  36. import "../../../../../../node_modules/echarts/map/js/province/taiwan";
  37. import "../../../../../../node_modules/echarts/map/js/province/tianjin";
  38. import "../../../../../../node_modules/echarts/map/js/province/xianggang";
  39. import "../../../../../../node_modules/echarts/map/js/province/xinjiang";
  40. import "../../../../../../node_modules/echarts/map/js/province/xizang";
  41. import "../../../../../../node_modules/echarts/map/js/province/yunnan";
  42. import "../../../../../../node_modules/echarts/map/js/province/zhejiang";
  43. import "echarts/map/js/china.js";
  44. import echarts from "echarts";
  45. import {conversionCity} from "@/utils/china";
  46. let geoCoordMap = conversionCity;
  47. let planePath =
  48. "path://M1705.06,1318.313v-89.254l-319.9-221.799l0.073-208.063c0.521-84.662-26.629-121.796-63.961-121.491c-37.332-0.305-64.482,36.829-63.961,121.491l0.073,208.063l-319.9,221.799v89.254l330.343-157.288l12.238,241.308l-134.449,92.931l0.531,42.034l175.125-42.917l175.125,42.917l0.531-42.034l-134.449-92.931l12.238-241.308L1705.06,1318.313z";
  49. export default {
  50. name: "widgetLineMap",
  51. props: {
  52. value: Object,
  53. ispreview: Boolean,
  54. },
  55. data() {
  56. return {
  57. options: {
  58. title: {
  59. left: "center",
  60. textStyle: {
  61. color: "#fff",
  62. },
  63. },
  64. tooltip: {
  65. trigger: "item",
  66. formatter: function (params, ticket, callback) {
  67. if (params.seriesType == "effectScatter") {
  68. return "线路:" + params.data.name + "" + params.data.value[2];
  69. } else if (params.seriesType == "lines") {
  70. return (
  71. params.data.fromName +
  72. ">" +
  73. params.data.toName +
  74. "<br />" +
  75. params.data.value
  76. );
  77. } else {
  78. return params.name;
  79. }
  80. },
  81. },
  82. legend: {
  83. show: true,
  84. orient: "vertical",
  85. top: "bottom",
  86. left: "right",
  87. textStyle: {
  88. color: "#fff",
  89. },
  90. selectedMode: "multiple",
  91. },
  92. geo: {
  93. map: "china",
  94. label: {
  95. emphasis: {
  96. show: true,
  97. color: "white",
  98. },
  99. },
  100. roam: true,
  101. itemStyle: {
  102. normal: {
  103. borderColor: new echarts.graphic.LinearGradient(
  104. 0,
  105. 0,
  106. 0,
  107. 1,
  108. [
  109. {
  110. offset: 0,
  111. color: "#00F6FF",
  112. },
  113. {
  114. offset: 1,
  115. color: "#53D9FF",
  116. },
  117. ],
  118. false
  119. ),
  120. borderWidth: 1,
  121. shadowColor: "rgba(10,76,139,1)",
  122. shadowOffsetY: 0,
  123. shadowBlur: 60,
  124. },
  125. },
  126. },
  127. series: [
  128. {
  129. type: "lines",
  130. zlevel: 1,
  131. effect: {
  132. show: false,
  133. period: 6,
  134. trailLength: 0.7,
  135. color: "#fff",
  136. symbolSize: 3,
  137. },
  138. lineStyle: {
  139. normal: {
  140. color: "#a6c84c",
  141. width: 0,
  142. curveness: 0.2,
  143. },
  144. },
  145. data: [],
  146. },
  147. {
  148. type: "lines",
  149. zlevel: 2,
  150. symbol: ["none", "arrow"],
  151. symbolSize: 10,
  152. effect: {
  153. show: true,
  154. period: 4,
  155. trailLength: 0,
  156. symbol: "arrow",
  157. symbolSize: 5,
  158. },
  159. lineStyle: {
  160. normal: {
  161. // 颜色+ 线条
  162. color: "#ffa022",
  163. width: 1,
  164. opacity: 0.4,
  165. curveness: 0.2,
  166. },
  167. },
  168. data: [],
  169. },
  170. {
  171. // 起点
  172. type: "effectScatter",
  173. coordinateSystem: "geo",
  174. zlevel: 2,
  175. rippleEffect: {
  176. brushType: "stroke",
  177. },
  178. label: {
  179. normal: {
  180. show: true,
  181. position: "right",
  182. formatter: "{b}",
  183. fontSize: 22,
  184. },
  185. },
  186. // 点的大小
  187. symbolSize: 10,
  188. itemStyle: {
  189. normal: {
  190. // 地图点颜色
  191. color: "#46bee9",
  192. },
  193. },
  194. data: [],
  195. },
  196. {
  197. // 终点
  198. type: "effectScatter",
  199. coordinateSystem: "geo",
  200. zlevel: 2,
  201. rippleEffect: {
  202. brushType: "stroke",
  203. },
  204. label: {
  205. normal: {
  206. show: true,
  207. position: "right",
  208. formatter: "{b}",
  209. fontSize: 22,
  210. },
  211. },
  212. // 点的大小
  213. symbolSize: 10,
  214. itemStyle: {
  215. normal: {
  216. // 地图点颜色
  217. color: "#46bee9",
  218. },
  219. },
  220. data: [],
  221. },
  222. ],
  223. },
  224. optionsSetup: {},
  225. flagInter: null,
  226. };
  227. },
  228. computed: {
  229. styleObj() {
  230. return {
  231. position: this.ispreview ? "absolute" : "static",
  232. width: this.optionsStyle.width + "px",
  233. height: this.optionsStyle.height + "px",
  234. left: this.optionsStyle.left + "px",
  235. top: this.optionsStyle.top + "px",
  236. background: this.optionsSetup.background,
  237. };
  238. },
  239. allComponentLinkage() {
  240. return this.$store.state.designer.allComponentLinkage;
  241. },
  242. },
  243. watch: {
  244. value: {
  245. handler(val) {
  246. this.optionsStyle = val.position;
  247. this.optionsData = val.data;
  248. this.optionsSetup = val.setup;
  249. this.editorOptions();
  250. },
  251. deep: true,
  252. },
  253. },
  254. created() {
  255. this.optionsStyle = this.value.position;
  256. this.optionsData = this.value.data;
  257. this.optionsSetup = this.value.setup;
  258. },
  259. mounted() {
  260. this.editorOptions();
  261. targetWidgetLinkageLogic(this); // 联动-目标组件逻辑
  262. },
  263. methods: {
  264. convertData(data) {
  265. let res = [];
  266. for (let i = 0; i < data.length; i++) {
  267. let dataItem = data[i];
  268. let sourceCoord = geoCoordMap[dataItem.source];
  269. let targetCoord = geoCoordMap[dataItem.target];
  270. if (sourceCoord && targetCoord) {
  271. res.push({
  272. fromName: dataItem.source,
  273. toName: dataItem.target,
  274. coords: [sourceCoord, targetCoord],
  275. value: dataItem.value,
  276. });
  277. }
  278. }
  279. return res;
  280. },
  281. editorOptions() {
  282. this.setOptionsTitle();
  283. this.setOptionsGeo();
  284. this.setOptionsSource();
  285. this.setOptionsTarget();
  286. this.setOptionsSymbol();
  287. this.setOptionsLine();
  288. this.setOptionsTooltip();
  289. this.setOptionsData();
  290. },
  291. // 标题设置
  292. setOptionsTitle() {
  293. const optionsSetup = this.optionsSetup;
  294. const title = {
  295. text: optionsSetup.text,
  296. show: optionsSetup.isShowTitle,
  297. left: optionsSetup.titleLeft,
  298. top: optionsSetup.titleTop + "%",
  299. itemGap: optionsSetup.titleItemGap,
  300. textStyle: {
  301. color: optionsSetup.textColor,
  302. fontSize: optionsSetup.textFontSize,
  303. fontWeight: optionsSetup.textFontWeight,
  304. fontStyle: optionsSetup.textFontStyle,
  305. fontFamily: optionsSetup.textFontFamily,
  306. },
  307. subtext: optionsSetup.subtext,
  308. subtextStyle: {
  309. color: optionsSetup.subtextColor,
  310. fontWeight: optionsSetup.subtextFontWeight,
  311. fontSize: optionsSetup.subtextFontSize,
  312. fontStyle: optionsSetup.subtextFontStyle,
  313. fontFamily: optionsSetup.subtextFontFamily
  314. },
  315. };
  316. this.options.title = title;
  317. },
  318. // 地图设置
  319. setOptionsGeo() {
  320. const optionsSetup = this.optionsSetup;
  321. const geo = {
  322. map: this.optionsSetup.mapName == '' ? "china" : this.optionsSetup.mapName,
  323. show: true,
  324. roam: true,
  325. layoutSize: "80%",
  326. label: {
  327. //调整数值
  328. // 地图省市区显隐
  329. show: optionsSetup.isShowMap,
  330. color: optionsSetup.fontColor,
  331. fontSize: optionsSetup.fontSize,
  332. fontWeight: optionsSetup.fontWeight,
  333. fontStyle: optionsSetup.fontStyle,
  334. fontFamily: optionsSetup.fontFamily,
  335. },
  336. itemStyle: {
  337. normal: {
  338. //地图块颜色
  339. areaColor: {
  340. x: 0,
  341. y: 0,
  342. x2: 0,
  343. y2: 1,
  344. colorStops: [
  345. {
  346. offset: 0,
  347. color: optionsSetup.fontColor0, // 0% 处的颜色
  348. },
  349. {
  350. offset: 1,
  351. color: optionsSetup.fontColor100, // 100% 处的颜色
  352. },
  353. ],
  354. },
  355. borderType: optionsSetup.borderType,
  356. borderColor: optionsSetup.borderColor,
  357. borderWidth: optionsSetup.borderWidth,
  358. shadowColor: optionsSetup.shadowColor,
  359. shadowBlur: optionsSetup.shadowBlur,
  360. opacity: optionsSetup.opacity / 100,
  361. },
  362. },
  363. //鼠标放置颜色加深
  364. emphasis: {
  365. label: {
  366. show: optionsSetup.isShowEmphasisLabel,
  367. color: optionsSetup.emphasisLabelFontColor,
  368. fontSize: optionsSetup.emphasisLabelFontSize,
  369. fontWeight: optionsSetup.emphasisLabelFontWeight,
  370. fontStyle: optionsSetup.emphasisLabelFontStyle,
  371. fontFamily: optionsSetup.emphasisLabelFontFamily,
  372. },
  373. itemStyle: {
  374. areaColor: {
  375. x: 0,
  376. y: 0,
  377. x2: 0,
  378. y2: 1,
  379. colorStops: [
  380. {
  381. offset: 0,
  382. color: optionsSetup.emphasisLabelFontColor0, // 0% 处的颜色
  383. },
  384. {
  385. offset: 1,
  386. color: optionsSetup.emphasisLabelFontColor100, // 100% 处的颜色
  387. },
  388. ],
  389. },
  390. },
  391. },
  392. }
  393. this.options.geo = geo;
  394. },
  395. // 起点设置
  396. setOptionsSource() {
  397. const optionsSetup = this.optionsSetup;
  398. const series = this.options.series[2];
  399. const normal = {
  400. show: optionsSetup.isShowSource,
  401. position: optionsSetup.sourceFontPosition,
  402. color: optionsSetup.sourceFontColor,
  403. fontSize: optionsSetup.sourceFontSize,
  404. fontWeight: optionsSetup.sourceFontWeight,
  405. fontStyle: optionsSetup.sourceFontStyle,
  406. fontFamily: optionsSetup.sourceFontFamily
  407. };
  408. const itemStyle = {
  409. normal: {
  410. color: optionsSetup.sourcePointColor,
  411. },
  412. };
  413. series.symbolSize = optionsSetup.sourceSymbolSize;
  414. series.label.normal = normal;
  415. series.itemStyle = itemStyle;
  416. },
  417. // 终点设置
  418. setOptionsTarget() {
  419. const optionsSetup = this.optionsSetup;
  420. const series = this.options.series[3];
  421. const normal = {
  422. show: optionsSetup.isShowTarget,
  423. position: optionsSetup.targetFontPosition,
  424. color: optionsSetup.targetFontColor,
  425. fontSize: optionsSetup.targetFontSize,
  426. fontWeight: optionsSetup.targetFontWeight,
  427. fontStyle: optionsSetup.sourceFontStyle,
  428. fontFamily: optionsSetup.sourceFontFamily
  429. };
  430. const itemStyle = {
  431. normal: {
  432. color: optionsSetup.targetPointColor,
  433. },
  434. };
  435. series.symbolSize = optionsSetup.targetSymbolSize;
  436. series.label.normal = normal;
  437. series.itemStyle = itemStyle;
  438. },
  439. // 图标设置
  440. setOptionsSymbol() {
  441. const optionsSetup = this.optionsSetup;
  442. const series = this.options.series[1];
  443. const effect = {
  444. show: true,
  445. period: this.setPeriod(optionsSetup),
  446. trailLength: 0,
  447. symbol: this.setSymbol(optionsSetup),
  448. symbolSize: optionsSetup.symbolSize,
  449. color: optionsSetup.symbolColor,
  450. };
  451. series["effect"] = effect;
  452. },
  453. setSymbol(optionsSetup) {
  454. let symbol;
  455. if (optionsSetup.symbol == "plane") {
  456. symbol = planePath;
  457. } else {
  458. symbol = "arrow";
  459. }
  460. return symbol;
  461. },
  462. setPeriod(optionsSetup) {
  463. let period;
  464. if (optionsSetup.symbol == "plane") {
  465. period = optionsSetup.symbolPeriod - 1;
  466. } else {
  467. period = optionsSetup.symbolPeriod;
  468. }
  469. return period;
  470. },
  471. // 线设置
  472. setOptionsLine() {
  473. const optionsSetup = this.optionsSetup;
  474. const series = this.options.series[1];
  475. const lineStyle = {
  476. normal: {
  477. // 线条颜色
  478. color: optionsSetup.lineColor,
  479. width: optionsSetup.lineWidth,
  480. opacity: 0.4,
  481. curveness: 0.2,
  482. },
  483. };
  484. series["lineStyle"] = lineStyle;
  485. },
  486. // tooltip 设置
  487. setOptionsTooltip() {
  488. const optionsSetup = this.optionsSetup;
  489. const tooltip = {
  490. trigger: "item",
  491. show: optionsSetup.isShowTooltip,
  492. textStyle: {
  493. color: optionsSetup.tooltipColor,
  494. fontSize: optionsSetup.tooltipFontSize,
  495. fontWeight: optionsSetup.tooltipFontWeight,
  496. fontStyle: optionsSetup.tooltipFontStyle,
  497. fontFamily: optionsSetup.tooltipFontFamily,
  498. },
  499. formatter: function (params, ticket, callback) {
  500. if (params.seriesType == "effectScatter") {
  501. return "线路:" + params.data.name + "" + params.data.value[2];
  502. } else if (params.seriesType == "lines") {
  503. return (
  504. params.data.fromName +
  505. ">" +
  506. params.data.toName +
  507. "<br />" +
  508. params.data.value
  509. );
  510. } else {
  511. return params.name;
  512. }
  513. },
  514. };
  515. this.options.tooltip = tooltip;
  516. },
  517. //数据解析
  518. setOptionsData(e, paramsConfig) {
  519. const optionsData = this.optionsData; // 数据类型 静态 or 动态
  520. // 联动接收者逻辑开始
  521. optionsData.dynamicData = optionsData.dynamicData || {}; // 兼容 dynamicData undefined
  522. const myDynamicData = optionsData.dynamicData;
  523. clearInterval(this.flagInter); // 不管咋,先干掉上一次的定时任务,避免多跑
  524. if (
  525. e &&
  526. optionsData.dataType !== "staticData" &&
  527. Object.keys(myDynamicData.contextData).length
  528. ) {
  529. const keyArr = Object.keys(myDynamicData.contextData);
  530. paramsConfig.forEach((conf) => {
  531. if (keyArr.includes(conf.targetKey)) {
  532. myDynamicData.contextData[conf.targetKey] = e[conf.originKey];
  533. }
  534. });
  535. }
  536. // 联动接收者逻辑结束
  537. optionsData.dataType == "staticData"
  538. ? this.staticDataFn(optionsData.staticData)
  539. : this.dynamicDataFn(optionsData.dynamicData, optionsData.refreshTime);
  540. },
  541. staticDataFn(val) {
  542. const series = this.options.series;
  543. series[0]["data"] = this.convertData(val);
  544. series[1]["data"] = this.convertData(val);
  545. series[2]["data"] = val.map(function (dataItem) {
  546. if (geoCoordMap[dataItem.source] && geoCoordMap[dataItem.target]) {
  547. return {
  548. name: dataItem.source,
  549. value: geoCoordMap[dataItem.source].concat([dataItem.value]),
  550. };
  551. }
  552. });
  553. series[3]["data"] = val.map(function (dataItem) {
  554. if (geoCoordMap[dataItem.source] && geoCoordMap[dataItem.target]) {
  555. return {
  556. name: dataItem.target,
  557. value: geoCoordMap[dataItem.target].concat([dataItem.value]),
  558. };
  559. }
  560. });
  561. },
  562. dynamicDataFn(val, refreshTime) {
  563. if (!val) return;
  564. if (this.ispreview) {
  565. this.getEchartData(val);
  566. this.flagInter = setInterval(() => {
  567. this.getEchartData(val);
  568. }, refreshTime);
  569. } else {
  570. this.getEchartData(val);
  571. }
  572. },
  573. getEchartData(val) {
  574. const data = this.queryEchartsData(val);
  575. data.then((res) => {
  576. this.renderingFn(res);
  577. });
  578. },
  579. renderingFn(val) {
  580. const series = this.options.series;
  581. series[0]["data"] = this.convertData(val);
  582. series[1]["data"] = this.convertData(val);
  583. series[2]["data"] = val.map(function (dataItem) {
  584. if (geoCoordMap[dataItem.source] && geoCoordMap[dataItem.target]) {
  585. return {
  586. name: dataItem.source,
  587. value: geoCoordMap[dataItem.source].concat([dataItem.value]),
  588. };
  589. }
  590. });
  591. series[3]["data"] = val.map(function (dataItem) {
  592. if (geoCoordMap[dataItem.source] && geoCoordMap[dataItem.target]) {
  593. return {
  594. name: dataItem.target,
  595. value: geoCoordMap[dataItem.target].concat([dataItem.value]),
  596. };
  597. }
  598. });
  599. },
  600. },
  601. };
  602. </script>
  603. <style lang="scss" scoped>
  604. .echartMap {
  605. width: 100%;
  606. height: 100%;
  607. }
  608. .echarts {
  609. width: 100%;
  610. height: 100%;
  611. overflow: hidden;
  612. }
  613. </style>