mapdata.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. <template>
  2. <moduleEnclosure>
  3. <zj-page-container>
  4. <map-data-statistics />
  5. <zj-page-fill>
  6. <div style="
  7. width: 100%;
  8. height: 100%;
  9. overflow: hidden;
  10. position: relative;
  11. ">
  12. <div id="chart_7" class="chart t_btn9" style="width: 100%; height: 100%"></div>
  13. <div class="leixingxianshi">
  14. <div class="viewfilediv">
  15. <div class="zfx" style="width:60px; background: linear-gradient(to right, #6ada40,#ebe24a,#d73921);">
  16. </div>
  17. </div>
  18. </div>
  19. <div class="pingtaixianshisheng" v-if="shengData">
  20. <div class="shengbiaoti">
  21. {{ isSelectBool }}
  22. <i @click="() => {
  23. shengData = null
  24. if (isSelectBool) {
  25. myChart.dispatchAction({
  26. type: 'unselect',
  27. name: [isSelectBool]
  28. })
  29. isSelectBool = ''
  30. }
  31. }" style="float: right;color: #fff;margin-top: 12px; margin-right: -8px;" class="el-icon-circle-close"></i>
  32. </div>
  33. <div class="shengshujukuang">
  34. <div>商户数:{{ shengData.companyNum }}</div>
  35. <div>师傅数量:{{ shengData.allWorker }}</div>
  36. <div>分销员数:{{ shengData.allService }}</div>
  37. <div>用户数:{{ shengData.allUser }}</div>
  38. <div>总工单:{{ shengData.allWorkerOrderNum }}</div>
  39. <div>未完工:{{ shengData.neverWorkerNum }}</div>
  40. <div>销售订单:{{ shengData.salesOrderNum }}</div>
  41. <div>增值订单:{{ shengData.increOrderNum }}</div>
  42. </div>
  43. </div>
  44. <div v-if="list_.length" class="pingtaixianshisheng" style="width: 100%;height: auto;min-height: 100px;">
  45. <div class="shengbiaoti"
  46. style="font-size: 14px;border-bottom: 1px solid #50C9FD; line-height: 38px; margin-bottom: 8px;display: flex;justify-content: space-between;align-items: center;box-sizing: border-box;position: relative;padding-right: 30px;">
  47. <!-- <span>{{ isSelectBool }}</span> -->
  48. <span>{{ userInfo && userInfo.city }}</span>
  49. <span>总已完工 {{ cityInfo.num }}单/台 总未完工 <span>{{ cityInfo.numWwg }}</span>单/台</span>
  50. <i @click="guanbitanchuang2" style="position: absolute; top: 10px; right: -8px;color: #fff;"
  51. class="el-icon-circle-close"></i>
  52. </div>
  53. <div style="width:100%;">
  54. <table style="width:100%;">
  55. <thead style="width:100%;">
  56. <tr>
  57. <th style="color: #50C9FD;font-size:12px;width: 17.5%;text-align: left;">
  58. <div align="left">街道</div>
  59. </th>
  60. <th style="color: #50C9FD;font-size:12px;width: 16.5%;text-align: left;">
  61. <div align="left">安装完工/未完工(台)</div>
  62. </th>
  63. <th style="color: #50C9FD;font-size:12px;width: 16.5%;text-align: left;">
  64. <div align="left">维修完工/未完工(台)</div>
  65. </th>
  66. <th style="color: #50C9FD;font-size:12px;width: 16.5%;text-align: left;">
  67. <div align="left">清洗完工/未完工(台)</div>
  68. </th>
  69. <th style="color: #50C9FD;font-size:12px;width: 16.5%;text-align: left;">
  70. <div align="left">增值完工/未完工(台)</div>
  71. </th>
  72. <th style="color: #50C9FD;font-size:12px;width: 16.5%;text-align: left;">
  73. <div align="left">维保完工/未完工(台)</div>
  74. </th>
  75. </tr>
  76. </thead>
  77. <tbody>
  78. <tr v-for="(item, index) in list_" :key="index">
  79. <td>
  80. <div class="bkiuybhlinjm left" style="font-size: 14px;">{{ item.streetName }}</div>
  81. </td>
  82. <td>
  83. <div class="bkiuybhlinjm">
  84. <span style="font-size: 14px;">{{ item.azgd }}</span>
  85. <span style="padding: 0 2px;">/</span>
  86. <span style="font-size: 14px;"><span style="color:#50C9FD">{{ item.azgdwwg }}</span></span>
  87. </div>
  88. </td>
  89. <td>
  90. <div class="bkiuybhlinjm">
  91. <span style="font-size: 14px;">{{ item.wxgd }}</span>
  92. <span style="padding: 0 2px;">/</span>
  93. <span style="font-size: 14px;"><span style="color:#50C9FD">{{ item.wxgdwwg }}</span></span>
  94. </div>
  95. </td>
  96. <td>
  97. <div class="bkiuybhlinjm">
  98. <span style="font-size: 14px;">{{ item.qxgd }}</span>
  99. <span style="padding: 0 2px;">/</span>
  100. <span style="font-size: 14px;"><span style="color:#50C9FD">{{ item.qxgdwwg }}</span></span>
  101. </div>
  102. </td>
  103. <td>
  104. <div class="bkiuybhlinjm">
  105. <span style="font-size: 14px;">{{ item.zzgd }}</span>
  106. <span style="padding: 0 2px;">/</span>
  107. <span style="font-size: 14px;"><span style="color:#50C9FD">{{ item.zzgdwwg }}</span></span>
  108. </div>
  109. </td>
  110. <td>
  111. <div class="bkiuybhlinjm">
  112. <span style="font-size: 14px;">{{ item.wbgd }}</span>
  113. <span style="padding: 0 2px;">/</span>
  114. <span style="font-size: 14px;"><span style="color:#50C9FD">{{ item.wbgdwwg }}</span></span>
  115. </div>
  116. </td>
  117. </tr>
  118. </tbody>
  119. </table>
  120. <div style="width:100%;display: flex;justify-content: center;align-items: center">
  121. <el-button type="text" :disabled="!(pageNum > 1)" @click="shangyiye">上一页</el-button>
  122. <div style="width:50px;text-align: center;color:#50C9FD">{{ pageNum }}</div>
  123. <el-button type="text" :disabled="!(parseInt(total / 5) > pageNum)" @click="xiayiye">下一页</el-button>
  124. </div>
  125. </div>
  126. </div>
  127. </div>
  128. </zj-page-fill>
  129. </zj-page-container>
  130. </moduleEnclosure>
  131. </template>
  132. <script>
  133. import zhejiang from './js/zhejiang.js';
  134. import moduleEnclosure from '@/components/moduleEnclosure.vue';
  135. import mapDataStatistics from './mapDataStatistics.vue';
  136. import * as echarts from 'echarts';
  137. import { bigGetRegion, bigGetLarge9, bigGetLarge10, bigList, bigGetLarge1, bigGetLarge11 } from '@/api/common.js';
  138. // 计算 GeoJSON 数据的边界框
  139. function calculateBoundingBox(geoJsonData) {
  140. let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
  141. geoJsonData.features.forEach((feature) => {
  142. const coordinates = feature.geometry.coordinates;
  143. const type = feature.geometry.type;
  144. if (type === 'Polygon') {
  145. coordinates.forEach((polygon) => {
  146. polygon.forEach((coord) => {
  147. minX = Math.min(minX, coord[0]);
  148. minY = Math.min(minY, coord[1]);
  149. maxX = Math.max(maxX, coord[0]);
  150. maxY = Math.max(maxY, coord[1]);
  151. });
  152. });
  153. } else if (type === 'MultiPolygon') {
  154. coordinates.forEach((multiPolygon) => {
  155. multiPolygon.forEach((polygon) => {
  156. polygon.forEach((coord) => {
  157. minX = Math.min(minX, coord[0]);
  158. minY = Math.min(minY, coord[1]);
  159. maxX = Math.max(maxX, coord[0]);
  160. maxY = Math.max(maxY, coord[1]);
  161. });
  162. });
  163. });
  164. }
  165. });
  166. return { minX, minY, maxX, maxY };
  167. }
  168. export default {
  169. components: {
  170. moduleEnclosure,
  171. mapDataStatistics,
  172. },
  173. data() {
  174. return {
  175. list: [],
  176. geoCoordMap: zhejiang.features.map(function (item) {
  177. return {
  178. name: item.properties.name,
  179. value: item.properties.center,
  180. };
  181. }),
  182. data: zhejiang.features.map(function (item) {
  183. return {
  184. name: item.properties.name,
  185. };
  186. }),
  187. cIndex: 0,
  188. oldcIndex: 0,
  189. myChart: null,
  190. getLarge9: [],
  191. getLarge10: [],
  192. shengData: null,
  193. isSelectBool: "",
  194. pageNum: 1,
  195. total: 0,
  196. list_: [],
  197. cityInfo: {}
  198. };
  199. },
  200. computed: {
  201. userInfo() {
  202. return JSON.parse(localStorage.getItem('greemall_user'))
  203. },
  204. },
  205. watch: {
  206. "userInfo.type": {
  207. handler(newVal, oldVal) {
  208. this.init(true)
  209. },
  210. deep: true,
  211. immediate: true,
  212. }
  213. },
  214. methods: {
  215. guanbitanchuang() {
  216. this.shengData = null
  217. if (this.isSelectBool) {
  218. this.myChart.dispatchAction({
  219. type: 'unselect',
  220. name: [this.isSelectBool]
  221. })
  222. this.isSelectBool = ''
  223. }
  224. },
  225. guanbitanchuang2() {
  226. this.pageNum = 1
  227. this.total = 0
  228. this.list_ = []
  229. this.cityInfo = {}
  230. if (this.isSelectBool) {
  231. this.myChart.dispatchAction({
  232. type: 'unselect',
  233. name: [this.isSelectBool]
  234. })
  235. this.isSelectBool = ''
  236. }
  237. },
  238. init() {
  239. if (this.userInfo) {
  240. if (this.userInfo.type == 2) {
  241. this.getbigGetLarge9({ country: 100000 });
  242. } else {
  243. this.getbigGetLarge10({ province: this.userInfo.province, city: this.userInfo.city });
  244. }
  245. }
  246. },
  247. getbigGetLarge9(pam) {
  248. bigGetLarge9().then(res => {
  249. this.getLarge9 = res.data
  250. this.getLarge10 = []
  251. this.echart_7(pam);
  252. })
  253. },
  254. getbigGetLarge10(pam) {
  255. bigGetLarge10(pam).then(res => {
  256. this.getLarge9 = []
  257. this.getLarge10 = res.data
  258. this.echart_7({ province: this.userInfo.province, city: this.userInfo.city });
  259. })
  260. },
  261. echart_7(pam) {
  262. var that = this;
  263. if (that.myChart) {
  264. that.myChart.dispose();
  265. }
  266. bigGetRegion(pam).then(res => {
  267. // 初始化 ECharts 实例
  268. that.myChart = echarts.init(document.getElementById('chart_7'));
  269. const geoJsonData = JSON.parse(res.data);
  270. // 计算边界框
  271. const boundingBox = calculateBoundingBox(geoJsonData);
  272. // 计算中心点
  273. const center = [
  274. (boundingBox.minX + boundingBox.maxX) / 2,
  275. (boundingBox.minY + boundingBox.maxY) / 2
  276. ];
  277. echarts.registerMap('china', geoJsonData);
  278. // 指定城市和颜色
  279. var cityColors = {};
  280. this.getLarge9.map(name => {
  281. cityColors[name] = "#009eff";
  282. });
  283. this.getLarge10.map(item => {
  284. cityColors[item.area] = item.num <= 2 ? "#6ada40" : item.num <= 4 ? "#ebe24a" : item.num > 4 ? "#d73921" : "none"
  285. });
  286. // 构建 series 数据
  287. var seriesData = Object.keys(cityColors).map(function (city) {
  288. return {
  289. name: city,
  290. itemStyle: {
  291. normal: {
  292. areaColor: cityColors[city],
  293. borderColor: cityColors[city],
  294. },
  295. emphasis: {
  296. label: { show: true, color: "#ffffff", },
  297. areaColor: 'none',
  298. borderColor: 1
  299. }
  300. },
  301. };
  302. });
  303. var mapcon = {
  304. label: {
  305. emphasis: {
  306. textStyle: {
  307. color: '#beecff',
  308. },
  309. },
  310. },
  311. itemStyle: {
  312. normal: {
  313. borderColor: 'rgba(0,144,255, 1)',
  314. borderWidth: 1,
  315. areaColor: {
  316. type: 'radial',
  317. x: 0.4,
  318. y: 0.5,
  319. r: 1,
  320. colorStops: [
  321. {
  322. offset: 0,
  323. color: 'rgba(0,144,255, 0)', // 0% 处的颜色
  324. },
  325. {
  326. offset: 1,
  327. color: 'rgba(0,144,255, .1)', // 100% 处的颜色
  328. },
  329. {
  330. offset: 1,
  331. color: 'rgba(0,144,255, 1)', // 100% 处的颜色
  332. },
  333. ],
  334. globalCoord: false, // 缺省为 false
  335. },
  336. shadowColor: 'rgba(0,144,255, 1)',
  337. shadowOffsetX: -1,
  338. shadowOffsetY: 1,
  339. shadowBlur: 10,
  340. },
  341. emphasis: {
  342. label: { show: true, color: "#ffffff", },
  343. areaColor: 'none',
  344. borderColor: 1
  345. }
  346. },
  347. select: {
  348. // 地图选中区域样式
  349. label: {
  350. // 选中区域的label(文字)样式
  351. color: '#fff',
  352. },
  353. itemStyle: {
  354. // 选中区域的默认样式
  355. areaColor: '#7024fb',
  356. },
  357. },
  358. animation: false,
  359. };
  360. // 配置项
  361. var option = {
  362. tooltip: {
  363. confine: true,
  364. trigger: 'item',
  365. formatter: '{b}'
  366. },
  367. geo: {
  368. map: 'china',
  369. roam: true,
  370. zoom: pam.country ? 1.3 : pam.city ? 4.5 : pam.province ? 1.7 : 2, // 设置缩放比例
  371. center: center,
  372. ...mapcon,
  373. regions: [
  374. ...seriesData,
  375. {
  376. name: "南海诸岛",
  377. itemStyle: {
  378. normal: {
  379. opacity: 0,
  380. }
  381. },
  382. label: {
  383. show: false
  384. }
  385. }
  386. ]
  387. },
  388. series: [{
  389. type: 'map',
  390. map: 'china',
  391. ...mapcon,
  392. geoIndex: 0,
  393. data: seriesData,
  394. emphasis: {
  395. label: { show: true, color: "#ffffff", },
  396. areaColor: 'none',
  397. borderColor: 1
  398. }
  399. }]
  400. };
  401. // 使用配置项生成图表
  402. that.myChart.setOption(option);
  403. var bool_i = false
  404. that.myChart.on('selectchanged', function (params) {
  405. bool_i = !!params.selected.length
  406. });
  407. // 监听点击事件
  408. that.myChart.on('click', function (params) {
  409. if (bool_i) {
  410. if (that.userInfo) {
  411. if (that.userInfo.type == 2) {
  412. // 平台
  413. if (!!cityColors[params.name]) {
  414. bigGetLarge1({
  415. province: params.name
  416. }).then(res => {
  417. that.shengData = res.data
  418. that.isSelectBool = params.name
  419. })
  420. }
  421. } else {
  422. // 商户
  423. if (!!cityColors[params.name]) {
  424. that.isSelectBool = params.name
  425. that.pageNum = 1
  426. that.total = 0
  427. that.pagedatalist()
  428. bigGetLarge11({
  429. // area: that.isSelectBool,
  430. city: that.userInfo.city,
  431. province: that.userInfo.province,
  432. }).then(res => {
  433. that.cityInfo = res.data
  434. })
  435. }
  436. }
  437. }
  438. }
  439. if (!cityColors[params.name]) {
  440. that.guanbitanchuang()
  441. that.guanbitanchuang2()
  442. }
  443. });
  444. });
  445. },
  446. pagedatalist() {
  447. if (this.isSelectBool) {
  448. bigList({
  449. pageNum: this.pageNum,
  450. pageSize: 5,
  451. city: this.userInfo.city,
  452. province: this.userInfo.province,
  453. area: this.isSelectBool
  454. }).then(res => {
  455. this.total = res.data.total
  456. this.list_ = res.data.records
  457. })
  458. }
  459. },
  460. xiayiye() {
  461. if (parseInt(this.total / 5) > this.pageNum) {
  462. this.pageNum++
  463. this.pagedatalist()
  464. } else {
  465. }
  466. },
  467. shangyiye() {
  468. if (this.pageNum > 1) {
  469. this.pageNum--
  470. this.pagedatalist()
  471. } else {
  472. }
  473. }
  474. },
  475. };
  476. </script>
  477. <style scoped lang="scss">
  478. .pingtaixianshisheng {
  479. position: absolute;
  480. top: 0;
  481. left: 0;
  482. width: 467px;
  483. height: 120px;
  484. background: #04418F;
  485. border-radius: 5px;
  486. opacity: 0.8;
  487. z-index: 999;
  488. box-sizing: border-box;
  489. padding: 0 20px;
  490. .shengbiaoti {
  491. font-family: Source Han Sans CN;
  492. font-weight: 500;
  493. font-size: 18px;
  494. color: #50C9FD;
  495. line-height: 49px;
  496. }
  497. .shengshujukuang {
  498. width: 100%;
  499. height: auto;
  500. display: flex;
  501. flex-direction: row;
  502. flex-wrap: wrap;
  503. font-family: Source Han Sans CN;
  504. font-weight: 400;
  505. font-size: 13px;
  506. color: #FFFFFF;
  507. line-height: 26px;
  508. div {
  509. width: 25%;
  510. }
  511. }
  512. }
  513. .leixingxianshi {
  514. // width: 120px;
  515. height: auto;
  516. position: absolute;
  517. bottom: 0px;
  518. right: -10px;
  519. }
  520. .viewfilediv {
  521. width: 100%;
  522. height: auto;
  523. display: flex;
  524. flex-direction: row;
  525. align-items: center;
  526. &:not(:last-child) {
  527. margin-bottom: 10px;
  528. }
  529. span {
  530. font-size: 14px;
  531. font-family: Source Han Sans CN, Source Han Sans CN-Regular;
  532. font-weight: 400;
  533. text-align: right;
  534. color: #8ab1dc;
  535. }
  536. }
  537. .zfx {
  538. width: 14px;
  539. height: 14px;
  540. margin-right: 6px;
  541. }
  542. .c1 {
  543. background: linear-gradient(0deg, #21c8fb 0%, #21c8fb 100%);
  544. }
  545. .c2 {
  546. background: linear-gradient(0deg, #ffc737 0%, #ffc737 100%);
  547. }
  548. .c3 {
  549. background: linear-gradient(0deg, #ea539b 0%, #e34691 100%);
  550. }
  551. .c4 {
  552. background: linear-gradient(0deg, #59cfe6 0%, #59cfe6 100%);
  553. }
  554. .c5 {
  555. background: linear-gradient(0deg, #8061ff 0%, #8061ff 100%);
  556. }
  557. .c6 {
  558. color: #e15960 !important;
  559. }
  560. .c7 {
  561. color: #ffffff !important;
  562. }
  563. .c8 {
  564. color: #ff9900 !important;
  565. }
  566. .c9 {
  567. background: linear-gradient(0deg, #3ce783 0%, #3ce783 100%);
  568. }
  569. .bkiuybhlinjm {
  570. padding: 8px 0;
  571. color: #fff;
  572. box-sizing: border-box;
  573. padding-right: 30px;
  574. width: 100%;
  575. display: flex;
  576. flex-direction: row;
  577. justify-content: center;
  578. &.left {
  579. justify-content: flex-start;
  580. }
  581. }
  582. </style>