index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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 commonMixins from 'data-room-ui/js/mixins/commonMixins.js'
  17. import paramsMixins from 'data-room-ui/js/mixins/paramsMixins'
  18. import linkageMixins from 'data-room-ui/js/mixins/linkageMixins'
  19. export default {
  20. name: 'MapCharts',
  21. mixins: [paramsMixins, commonMixins, linkageMixins],
  22. props: {
  23. id: {
  24. type: String,
  25. default: ''
  26. },
  27. config: {
  28. type: Object,
  29. default: () => ({})
  30. }
  31. },
  32. data() {
  33. return {
  34. currentDeep: 0,
  35. mapList: [],
  36. charts: null,
  37. hasData: false,
  38. level: '',
  39. option: {}
  40. }
  41. },
  42. computed: {
  43. Data() {
  44. return JSON.parse(JSON.stringify(this.config))
  45. }
  46. },
  47. watch: {
  48. Data: {
  49. handler(newVal, oldVal) {
  50. if (newVal.w !== oldVal.w || newVal.h !== oldVal.h) {
  51. this.$nextTick(() => {
  52. this.charts.resize()
  53. })
  54. }
  55. },
  56. deep: true
  57. }
  58. },
  59. mounted() {
  60. this.chartInit()
  61. },
  62. beforeDestroy() {
  63. this.charts?.clear()
  64. },
  65. methods: {
  66. chartInit() {
  67. const config = this.config
  68. // key和code相等,说明是一进来刷新,调用list接口
  69. if (this.config.code === this.config.key || this.isPreview) {
  70. // 改变数据
  71. this.changeDataByCode(config).then((res) => {
  72. // 改变样式
  73. // config = this.changeStyle(res)
  74. this.newChart(config)
  75. }).catch(() => {
  76. })
  77. } else {
  78. // 否则说明是更新,这里的更新只指更新数据(改变样式时是直接调取changeStyle方法),因为更新数据会改变key,调用chart接口
  79. this.changeData(config).then((res) => {
  80. // 初始化图表
  81. this.newChart(res)
  82. })
  83. }
  84. },
  85. /**
  86. * 数据格式化
  87. * 该方法继承自commonMixins
  88. * @param {*} config
  89. * @param {Array} data
  90. */
  91. dataFormatting(config, data) {
  92. const dataList = []
  93. data?.data?.forEach(item => {
  94. dataList.push({
  95. name: item[config.customize.name],
  96. value: [item[config.customize.xaxis], item[config.customize.yaxis], item[config.customize.value]],
  97. // 原始数据
  98. originData: item
  99. })
  100. })
  101. config.option = {
  102. ...config.option,
  103. data: dataList
  104. }
  105. return config
  106. },
  107. /**
  108. * 返回上一级
  109. * @param {*} config
  110. */
  111. async backToPreviousLevel(config) {
  112. this.currentDeep--
  113. let map = this.mapList[this.currentDeep]
  114. // 移除mapList中的最后一个元素
  115. this.mapList.pop()
  116. let mapData = JSON.parse(map.geoJson)
  117. this.option.geo.map = map.name;
  118. // this.changeData({...config, customize: {...config.customize, level: map.level, scope: map.name}})
  119. echarts.registerMap(map.name, mapData);
  120. this.charts.setOption(this.option, true);
  121. },
  122. /**
  123. * 修改地图数据
  124. * @param {Array} data
  125. */
  126. changeMapData(data) {
  127. this.option.series[0].data = data
  128. this.charts.setOption(this.option)
  129. },
  130. /**
  131. * 初始化地图
  132. * 该方法继承自commonMixins
  133. * @param {*} config
  134. */
  135. async newChart(config) {
  136. this.charts = echarts.init(
  137. document.getElementById(`chart${this.config.code}`)
  138. )
  139. // 处理option,将配置项转换为echarts的option
  140. this.handleOption(config)
  141. let hasMapId = !!config.customize.mapId
  142. // 根据mapId获取地图数据
  143. let mapInfoUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/info/${config.customize.mapId}`
  144. // 如果设置了地图id,就用地图id获取地图数据,否则用默认的世界地图
  145. if (!hasMapId) {
  146. mapInfoUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/default/chinaMap.country/中华人民共和国`
  147. }
  148. const mapResp = await this.$dataRoomAxios.get(decodeURI(mapInfoUrl), {}, true)
  149. const map = hasMapId ? JSON.parse(mapResp.data.geoJson) : mapResp
  150. if (hasMapId && mapResp.data.uploadedGeoJson !== 1) {
  151. // 没有上传过geoJson
  152. this.$message({
  153. message: '请先上传地图数据',
  154. type: 'warning'
  155. })
  156. return
  157. }
  158. this.mapList.push(mapResp.data)
  159. echarts.registerMap(config.customize.scope, map)
  160. this.charts.setOption(this.option)
  161. // 注册点击事件
  162. this.registerClickEvent(config)
  163. },
  164. /**
  165. * 处理配置项option
  166. * @param {*} config
  167. */
  168. handleOption(config) {
  169. let center1 = config.customize.center1 ? config.customize.center1 + '%' : '50%'
  170. let center2 = config.customize.center2 ? config.customize.center2 + '%' : '50%'
  171. let scatterSeries = {
  172. type: 'scatter',
  173. // 坐标系类型
  174. coordinateSystem: 'geo',
  175. // 标记符号形状 'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none'
  176. symbol: config.customize.scatterSymbol ? config.customize.scatterSymbol : 'pin',
  177. // 是否允许图例和散点图之间的联动效果
  178. legendHoverLink: true,
  179. // 散点图标记符号的大小,[宽度,高度]
  180. symbolSize: config.customize.scatterSize ? [config.customize.scatterSize, config.customize.scatterSize] : [40, 40],
  181. // 触发特效的方式
  182. showEffectOn: 'render',
  183. rippleEffect: {
  184. brushType: 'stroke'
  185. },
  186. hoverAnimation: true,
  187. zlevel: 11,
  188. // 这里渲染标志里的内容以及样式
  189. label: {
  190. show: config.customize.hasOwnProperty('showScatterValue') ? config.customize.showScatterValue : true,
  191. formatter(value) {
  192. return value.data.value[2]
  193. },
  194. color: config.customize.scatterColor
  195. },
  196. // 标志的样式
  197. itemStyle: {
  198. normal: {
  199. color: config.customize.scatterBackgroundColor,
  200. shadowBlur: 2,
  201. shadowColor: 'D8BC37'
  202. }
  203. },
  204. data: config.option?.data
  205. }
  206. let mapSeries = {
  207. type: 'map',
  208. map: config.customize.scope,
  209. geoIndex: 0,
  210. roam: false,
  211. zoom: 1.5,
  212. center: [105, 36],
  213. showLegendSymbol: false, // 存在legend时显示
  214. data: config.option?.data,
  215. tooltip: {
  216. formatter(params) {
  217. return `<p style="text-align:center;line-height: 30px;height:30px;font-size: 14px;border-bottom: 1px solid #7A8698;">${
  218. params.name
  219. }</p>
  220. <div style="line-height:22px;margin-top:5px">${config.customize.tooltipTitle ? config.customize.tooltipTitle : 'GDP'}<span style="margin-left:12px;color:#fff;float:right">${
  221. params.data?.value[2] || '--'
  222. }</span></div>`
  223. },
  224. show: true
  225. }
  226. }
  227. let series = config.customize.scatter ? [ scatterSeries ] : [ mapSeries ]
  228. this.option = {
  229. // 背景颜色
  230. backgroundColor: config.customize.backgroundColor,
  231. graphic: [],
  232. geo: {
  233. map: config.customize.scope,
  234. zlevel: 9,
  235. show: true,
  236. // 地图中心点位置
  237. layoutCenter: [center1, center2],
  238. roam: true,
  239. layoutSize: "100%",
  240. zoom: config.customize.zoom || 1,
  241. label: {
  242. // 通常状态下的样式
  243. normal: {
  244. show: config.customize.mapName,
  245. textStyle: {
  246. color: config.customize.mapNameColor || '#fff',
  247. fontSize: config.customize.mapNameSize || 12,
  248. fontWeight: config.customize.mapNameWeight || 500
  249. }
  250. },
  251. // 鼠标放上去的样式
  252. emphasis: {
  253. textStyle: {
  254. color: config.customize.mapNameColor || '#fff',
  255. fontSize: config.customize.mapNameSize || 12,
  256. fontWeight: config.customize.mapNameWeight || 500
  257. }
  258. }
  259. },
  260. // 地图区域的样式设置
  261. itemStyle: {
  262. normal: {
  263. borderColor: config.customize.mapLineColor,
  264. borderWidth: 1,
  265. areaColor: config.customize.areaColor,
  266. shadowColor: 'fffff',
  267. shadowOffsetX: -2,
  268. shadowOffsetY: 2,
  269. shadowBlur: 10
  270. },
  271. // 鼠标放上去高亮的样式
  272. emphasis: {
  273. areaColor: config.customize.emphasisColor ? config.customize.emphasisColor :'#389BB7',
  274. borderWidth: 0
  275. }
  276. }
  277. },
  278. // 提示浮窗样式
  279. tooltip: {
  280. show: false,
  281. trigger: 'item',
  282. alwaysShowContent: false,
  283. backgroundColor: config.customize.tooltipBackgroundColor,
  284. borderColor: config.customize.borderColor,
  285. hideDelay: 100,
  286. triggerOn: 'mousemove',
  287. enterable: true,
  288. textStyle: {
  289. color: '#DADADA',
  290. fontSize: '12',
  291. width: 20,
  292. height: 30,
  293. overflow: 'break'
  294. },
  295. showDelay: 100
  296. },
  297. // 视觉映射
  298. visualMap: {
  299. show: !config.customize.scatter,
  300. calculable: config.customize.visual,
  301. min: config.customize.range[0],
  302. max: config.customize.range[1],
  303. seriesIndex: config.customize.scatter ? -1 : 0,
  304. inRange: {
  305. color: config.customize.rangeColor
  306. }
  307. },
  308. series: series
  309. }
  310. },
  311. /**
  312. * 注册点击事件
  313. * @param config 地图组件配置项
  314. */
  315. registerClickEvent(config) {
  316. this.charts.on('click', async (params) => {
  317. let data = params?.data?.originData
  318. if (data) {
  319. this.linkage({...data, clickAreaName: params.name})
  320. } else {
  321. this.linkage({clickAreaName: params.name})
  322. }
  323. if (params.name == '') return
  324. if (!config.customize.down) {
  325. return
  326. }
  327. // 到达允许下钻的层数,则不再下钻
  328. if (this.currentDeep >= config.customize.downLevel) return
  329. const downMapUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/data/${this.mapList[this.currentDeep].id}/${params.name}`
  330. const downMap = await this.$dataRoomAxios.get(decodeURI(downMapUrl), {}, false)
  331. // 地图不可用
  332. if (downMap.available !== 1) {
  333. this.$message({
  334. message: '未找到该地图配置',
  335. type: 'warning'
  336. })
  337. return
  338. }
  339. let geoJsonObj
  340. try {
  341. geoJsonObj = JSON.parse(downMap.geoJson)
  342. } catch (error) {
  343. this.$message({
  344. message: params.name + '地图数据格式错误',
  345. type: 'warning'
  346. })
  347. return
  348. }
  349. this.currentDeep++
  350. this.mapList.push(downMap)
  351. // this.changeData({...config, customize: {...config.customize, scope: params.name}})
  352. this.option.geo.map = params.name
  353. echarts.registerMap(params.name, geoJsonObj);
  354. this.charts.setOption(this.option, true);
  355. });
  356. },
  357. }
  358. }
  359. </script>
  360. <style lang="scss" scoped>
  361. @import '../../assets/style/echartStyle';
  362. .light-theme {
  363. background-color: #ffffff;
  364. color: #000000;
  365. }
  366. .auto-theme {
  367. background-color: rgba(0, 0, 0, 0);
  368. }
  369. .bs-design-wrap {
  370. position: relative;
  371. .button {
  372. position: absolute;
  373. z-index: 999;
  374. }
  375. }
  376. </style>