index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. <template>
  2. <div
  3. class="bs-design-wrap bs-bar"
  4. style="width: 100%; height: 100%"
  5. >
  6. <el-button v-if="currentDeep > 0" class="button" type='text' @click="backToPreviousLevel(config)"> 返回上一级</el-button>
  7. <div
  8. :id="`chart${config.code}`"
  9. style="width: 100%; height: 100%"
  10. />
  11. </div>
  12. </template>
  13. <script>
  14. import 'insert-css'
  15. import * as echarts from 'echarts'
  16. import {nameMap} from './json/mapData.js'
  17. import commonMixins from 'data-room-ui/js/mixins/commonMixins.js'
  18. import paramsMixins from 'data-room-ui/js/mixins/paramsMixins'
  19. import linkageMixins from 'data-room-ui/js/mixins/linkageMixins'
  20. export default {
  21. name: 'MapCharts',
  22. mixins: [paramsMixins, commonMixins, linkageMixins],
  23. props: {
  24. id: {
  25. type: String,
  26. default: ''
  27. },
  28. config: {
  29. type: Object,
  30. default: () => ({})
  31. }
  32. },
  33. data() {
  34. return {
  35. charts: null,
  36. hasData: false,
  37. level: '',
  38. option: {},
  39. mapList: [],
  40. currentDeep: 0,
  41. }
  42. },
  43. computed: {
  44. Data() {
  45. return JSON.parse(JSON.stringify(this.config))
  46. }
  47. },
  48. watch: {
  49. Data: {
  50. handler(newVal, oldVal) {
  51. if (newVal.w !== oldVal.w || newVal.h !== oldVal.h) {
  52. this.$nextTick(() => {
  53. this.charts.resize()
  54. })
  55. }
  56. },
  57. deep: true
  58. }
  59. },
  60. mounted() {
  61. this.chartInit()
  62. },
  63. beforeDestroy() {
  64. this.charts?.clear()
  65. },
  66. methods: {
  67. chartInit() {
  68. const config = this.config
  69. // key和code相等,说明是一进来刷新,调用list接口
  70. if (this.config.code === this.config.key || this.isPreview) {
  71. // 改变数据
  72. this.changeDataByCode(config).then((res) => {
  73. // 改变样式
  74. // config = this.changeStyle(res)
  75. this.newChart(config)
  76. }).catch(() => {
  77. })
  78. } else {
  79. // 否则说明是更新,这里的更新只指更新数据(改变样式时是直接调取changeStyle方法),因为更新数据会改变key,调用chart接口
  80. this.changeData(config).then((res) => {
  81. // 初始化图表
  82. this.newChart(res)
  83. })
  84. }
  85. },
  86. /**
  87. * 数据格式化
  88. * 该方法继承自commonMixins
  89. * @param {*} config
  90. * @param {Array} data
  91. */
  92. dataFormatting(config, data) {
  93. let coordinates = {}
  94. if (data.data) {
  95. coordinates = this.formatMapData(data.data)
  96. }
  97. config.option = {
  98. ...config.option,
  99. data: data?.data,
  100. ...coordinates
  101. }
  102. return config
  103. },
  104. /**
  105. * 格式化地图飞线数据
  106. * @param {Array} data
  107. */
  108. formatMapData(data) {
  109. const lines_coord = []
  110. let from_coord = []
  111. let to_coord = []
  112. if (data) {
  113. // 起点名称
  114. const fromName = this.config.customize.dataField?.fromName || 'from'
  115. // 起点经度
  116. const fromLng = this.config.customize.dataField?.fromLng || 'lng1'
  117. // 起点纬度
  118. const fromLat = this.config.customize.dataField?.fromLat || 'lat1'
  119. // 终点名称
  120. const toName = this.config.customize.dataField?.toName || 'to'
  121. // 终点经度
  122. const toLng = this.config.customize.dataField?.toLng || 'lng2'
  123. // 终点纬度
  124. const toLat = this.config.customize.dataField?.toLat || 'lat2'
  125. // 值
  126. const value = this.config.customize.dataField?.value || 'value'
  127. data.forEach(val => {
  128. // 飞线
  129. lines_coord.push({value: val[value], msg: {...val}, coords: [[val[fromLng], val[fromLat]], [val[toLng], val[toLat]]]})
  130. // 起点散点
  131. from_coord.push({name: val[fromName], value: [val[fromLng], val[fromLat], val[value]], msg: {...val}})
  132. // 终点散点
  133. to_coord.push({name: val[toName], value: [val[toLng], val[toLat], val[value]], msg: {...val}})
  134. })
  135. }
  136. return {
  137. lines_coord,
  138. from_coord,
  139. to_coord
  140. }
  141. },
  142. /**
  143. * 返回上一级
  144. * @param {*} config
  145. */
  146. async backToPreviousLevel(config) {
  147. this.currentDeep--
  148. let map = this.mapList[this.currentDeep]
  149. // 移除mapList中的最后一个元素
  150. this.mapList.pop()
  151. let mapData = JSON.parse(map.geoJson)
  152. this.option.geo.map = map.name;
  153. // this.changeData({...config, customize: {...config.customize, level: map.level, scope: map.name}})
  154. echarts.registerMap(map.name, mapData);
  155. this.charts.setOption(this.option, true);
  156. },
  157. /**
  158. * 修改地图数据
  159. * @param {Array} data
  160. */
  161. changeMapData(data) {
  162. let coordinates = this.formatMapData(data)
  163. this.option.series[0].data = coordinates.to_coord
  164. this.option.series[1].data = coordinates.from_coord
  165. this.option.series[2].data = coordinates.lines_coord
  166. this.charts.setOption(this.option)
  167. },
  168. /**
  169. * 初始化地图
  170. * 该方法继承自commonMixins
  171. * @param {*} config
  172. */
  173. async newChart(config) {
  174. this.charts = echarts.init(
  175. document.getElementById(`chart${this.config.code}`)
  176. )
  177. // 处理option,将配置项转换为echarts的option
  178. this.handleOption(config)
  179. this.level = config.customize.level
  180. let hasMapId = !!config.customize.mapId
  181. // 根据mapId获取地图数据
  182. let mapInfoUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/info/${config.customize.mapId}`
  183. // 如果设置了地图id,就用地图id获取地图数据,否则用默认的世界地图
  184. if (!hasMapId) {
  185. mapInfoUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/default/worldMap/world`
  186. }
  187. const mapResp = await this.$dataRoomAxios.get(decodeURI(mapInfoUrl), {}, true)
  188. let mapData = hasMapId ? JSON.parse(mapResp.data.geoJson) : mapResp
  189. if (hasMapId && mapResp.data.uploadedGeoJson !== 1) {
  190. // 没有上传过geoJson
  191. this.$message({
  192. message: '请先上传地图数据',
  193. type: 'warning'
  194. })
  195. return
  196. }
  197. this.mapList.push(mapResp.data)
  198. echarts.registerMap(config.customize.scope, mapData)
  199. this.charts.setOption(this.option)
  200. // 注册点击事件
  201. this.registerClickEvent(config)
  202. },
  203. /**
  204. * 处理配置项option
  205. * @param {*} config
  206. */
  207. handleOption(config) {
  208. this.option = {
  209. nameMap: config.customize.level == '0' ? nameMap : '',
  210. // graphic: [
  211. // ],
  212. geo: {
  213. map: config.customize.scope,
  214. zlevel: 10,
  215. show: true,
  216. layoutCenter: ['50%', '50%'],
  217. roam: true,
  218. layoutSize: "100%",
  219. zoom: 1,
  220. label: {
  221. // 通常状态下的样式
  222. normal: {
  223. show: config.customize.mapName,
  224. textStyle: {
  225. color: config.customize.mapNameColor || '#fff',
  226. fontSize: config.customize.mapNameSize || 12,
  227. fontWeight: config.customize.mapNameWeight || 500
  228. }
  229. },
  230. // 鼠标放上去的样式
  231. emphasis: {
  232. textStyle: {
  233. color: config.customize.mapNameColor || '#fff',
  234. fontSize: config.customize.mapNameSize || 12,
  235. fontWeight: config.customize.mapNameWeight || 500
  236. }
  237. }
  238. },
  239. // 地图区域的样式设置
  240. itemStyle: {
  241. normal: {
  242. borderColor: config.customize.mapLineColor,
  243. borderWidth: 1,
  244. areaColor: config.customize.areaColor,
  245. shadowColor: 'fffff',
  246. shadowOffsetX: -2,
  247. shadowOffsetY: 2,
  248. shadowBlur: 10
  249. },
  250. // 鼠标放上去高亮的样式
  251. emphasis: {
  252. areaColor: '#389BB7',
  253. borderWidth: 0
  254. }
  255. }
  256. },
  257. tooltip: {
  258. backgroundColor: config.customize.tooltipBackgroundColor,
  259. borderColor: config.customize.borderColor,
  260. show: true,
  261. textStyle: {
  262. color: config.customize.fontColor,
  263. },
  264. },
  265. series: [
  266. {
  267. type: 'effectScatter',
  268. coordinateSystem: 'geo',
  269. zlevel: 15,
  270. symbolSize: 8,
  271. rippleEffect: {
  272. period: 4, brushType: 'stroke', scale: 4
  273. },
  274. tooltip: {
  275. trigger: 'item',
  276. formatter(params) {
  277. const a = eval(config.customize.scatterFormatter)
  278. return a
  279. },
  280. },
  281. itemStyle: {
  282. color: config.customize.scatterColor,
  283. opacity: 1
  284. },
  285. data: config.option.to_coord
  286. },
  287. {
  288. type: 'effectScatter',
  289. coordinateSystem: 'geo',
  290. zlevel: 15,
  291. symbolSize: 12,
  292. tooltip: {
  293. trigger: 'item',
  294. formatter(params) {
  295. const a = eval(config.customize.scatterFormatter)
  296. return a
  297. },
  298. },
  299. rippleEffect: {
  300. period: 6, brushType: 'stroke', scale: 8
  301. },
  302. itemStyle: {
  303. color: config.customize.scatterCenterColor,
  304. opacity: 1
  305. },
  306. data: config.option.from_coord
  307. },
  308. {
  309. type: 'lines',
  310. coordinateSystem: 'geo',
  311. zlevel: 15,
  312. tooltip: {
  313. trigger: 'item',
  314. formatter(params) {
  315. const a = eval(config.customize.lineFormatter)
  316. return a
  317. },
  318. },
  319. effect: {
  320. show: true,
  321. period: 5,
  322. trailLength: 0,
  323. symbol: config.customize.symbol,
  324. color: config.customize.symbolColor,
  325. symbolSize: config.customize.symbolSize,
  326. },
  327. lineStyle: {
  328. normal: {
  329. color: function (value) {
  330. return '#ffffff'
  331. }, width: 2, opacity: 0.6, curveness: 0.2
  332. }
  333. },
  334. data: config.option.lines_coord
  335. }
  336. ]
  337. }
  338. if (config.customize.visual) {
  339. this.option.visualMap = {
  340. show: false,
  341. min: config.customize.range[0],
  342. max: config.customize.range[1],
  343. seriesIndex: [0, 2],
  344. inRange: {
  345. color: config.customize.rangeColor
  346. }
  347. }
  348. }
  349. },
  350. /**
  351. * 注册点击事件
  352. * @param config 地图组件配置项
  353. */
  354. registerClickEvent(config) {
  355. this.charts.on('click', async (params) => {
  356. let data = params?.data?.msg
  357. if (data) {
  358. this.linkage({...data, clickAreaName: params.name})
  359. } else {
  360. this.linkage({clickAreaName: params.name})
  361. }
  362. if (params.name == '') return
  363. if (!config.customize.down) {
  364. return
  365. }
  366. // 到达允许下钻的层数,则不再下钻
  367. if (this.currentDeep >= config.customize.downLevel) return
  368. const mapUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/data/${this.mapList[this.currentDeep].id}/${params.name}`
  369. const map = await this.$dataRoomAxios.get(decodeURI(mapUrl), {}, false)
  370. // 地图不可用
  371. if (map.available !== 1) {
  372. this.$message({
  373. message: '未找到该地图配置',
  374. type: 'warning'
  375. })
  376. return
  377. }
  378. this.currentDeep++
  379. this.mapList.push(map)
  380. // this.changeData({...config, customize: {...config.customize, scope: params.name}})
  381. this.option.geo.map = params.name
  382. echarts.registerMap(params.name, JSON.parse(map.geoJson));
  383. this.charts.setOption(this.option, true);
  384. });
  385. }
  386. }
  387. }
  388. </script>
  389. <style lang="scss" scoped>
  390. @import '../../assets/style/echartStyle';
  391. .light-theme {
  392. background-color: #ffffff;
  393. color: #000000;
  394. }
  395. .auto-theme {
  396. background-color: rgba(0, 0, 0, 0);
  397. }
  398. .bs-design-wrap {
  399. position: relative;
  400. .button {
  401. position: absolute;
  402. z-index: 999;
  403. }
  404. }
  405. </style>