index.vue 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475
  1. <template>
  2. <div class='overview'>
  3. <!-- 顶部路由 -->
  4. <!-- <div class='menu'>
  5. <div class='home' @click='$emit("update:modelNum", 0)'>
  6. <div class='downright'></div>
  7. <div class='home-box'>
  8. <img src='@/assets/imgs/logo.png' alt />
  9. <span>{{plazas.length>0?formatter(plazaId,plazas):'--'}}</span>
  10. </div>
  11. </div>
  12. <div class='home-right'>
  13. <span style='color:#8D9399'>
  14. <span class='span3'>{{times}}</span>
  15. </span>
  16. </div>
  17. </div> -->
  18. <!-- 面包屑 -->
  19. <nav class='navigation'>
  20. <section class='crumbs'>
  21. <span>{{currentZoneData.cname}}</span>
  22. <span v-for='(item , index) in crumbsHtml' :key='index' @click='navgateFromCrumbs(item[1], index)'>
  23. {{item[0]}}
  24. <em v-if='index !== crumbsHtml.length - 1'>></em>
  25. </span>
  26. </section>
  27. <section>
  28. <TreeSelect
  29. width='280'
  30. :hideClear='false'
  31. :isReadOnly='false'
  32. :caption='"选择管理分区"'
  33. :data='regulateDistrictData'
  34. placeholder="全部"
  35. :disabled='false'
  36. @change='selectProjectItem'
  37. />
  38. </section>
  39. </nav>
  40. <main class='container'>
  41. <section class='system-general'>
  42. <div class='system-main-title'>
  43. <h4 class='section-title'>系统概况</h4>
  44. </div>
  45. <div class='system-list'>
  46. <ul>
  47. <li
  48. @click='getInfoOfProject(item)'
  49. :class='["system-item", item.selected ? "selected":""]'
  50. v-for='(item , index) in systemList'
  51. :key='index'
  52. >
  53. <div class='system-name'>
  54. <div :class='["img", item.selected ? "selected":""]'>
  55. <img :src='imgSrc(item.smsxt)' />
  56. </div>
  57. <span>{{item.smsxtname}}</span>
  58. </div>
  59. <div class='system-equipments' v-if='item.assetTypeList'>
  60. <div class='number' v-for='(equip , index) in item.assetTypeList' :key='index'>
  61. <div class="title">
  62. <P>{{equip.category_name}}</p>
  63. <span :style="{right: equip.is_exception_num > 99 ? '-2rem' : equip.is_exception_num > 9 ? '-1.5rem' :'-1rem'}" v-if='equip.is_exception_num'>{{equip.is_exception_num}}</span>
  64. </div>
  65. <p>
  66. <span>{{equip.asset_num}}</span>
  67. <span>台</span>
  68. </p>
  69. </div>
  70. </div>
  71. <div v-else style='width:65%;text-align:center;'>暂无数据</div>
  72. </li>
  73. </ul>
  74. </div>
  75. </section>
  76. <section class='map-box'>
  77. <section class='cards-list'>
  78. <div class='card' @click='navToManageNumber'>
  79. <div class='img'>
  80. <img src='../../assets/images/icons/ratio.png' />
  81. </div>
  82. <p>上线管理说明书数量</p>
  83. <strong>{{plazaInfoCount.upwcCount}}</strong>
  84. </div>
  85. <div class='card' @click='navToIntroduceUpdate'>
  86. <div class='img'>
  87. <img src='../../assets/images/icons/audit.png' />
  88. </div>
  89. <p>当月说明书更新项目数量</p>
  90. <strong>{{plazaInfoCount.rptChangeCount}}</strong>
  91. </div>
  92. <div class='card' @click='navToFocusItems'>
  93. <div class='img'>
  94. <img src='../../assets/images/icons/contract.png' />
  95. </div>
  96. <p>当月重点关注事项项目数量</p>
  97. <strong>{{plazaInfoCount.zhsxChangeCount}}</strong>
  98. </div>
  99. </section>
  100. <section class='map-container'>
  101. <div id='map'></div>
  102. <div class='legend'>
  103. <div class='legend-bar' v-for='(bar , index) in legendList' :key='index'>
  104. <span :style='{background:bar.color}'></span>
  105. <em>{{bar.num}}</em>
  106. </div>
  107. </div>
  108. </section>
  109. </section>
  110. <section class='ratio-list'>
  111. <section class='block' :style='panelStyle(item)' v-for='(item , index) in maintainList' :key='index'>
  112. <h4 class='section-title' @click='toggerPanel(item)'>{{item.title}}<img @mouseenter="showToolTip(index, item.title)" @mouseleave="hideToolTip(index, item.title)" src="../../assets/images/icons/notice.png" /></h4>
  113. <transition name="selectDownUpExtendTop">
  114. <div class="tool-tip" v-if="item.showToolTip">
  115. <h5 style="font-weight:bold;">{{item.title + '比值'}}</h5>
  116. <p>{{item.title === '专维' ? '逾期未完成/今年即将到期任务数/总任务' : '当月即将到期/总任务'}}</p>
  117. <div class="tip-triangle" :style="{left: item.title === '第三方检测' ? '12.5rem': '7.8rem'}"> </div>
  118. </div>
  119. </transition>
  120. <div class='list'>
  121. <ul>
  122. <li v-for='(option , index) in item.data' :key='index'>
  123. <div class='ratio'>
  124. <span>{{option.cname}}</span>
  125. <span v-if='item.title === "专维"'>
  126. <span>{{option.rptGlsmsStatistics.unfinished}} /</span>
  127. <span>{{option.rptGlsmsStatistics.due_num}} /</span>
  128. <span>{{option.rptGlsmsStatistics.total}}</span>
  129. </span>
  130. <span v-else>
  131. <span>{{option.rptGlsmsStatistics.due_num}} /</span>
  132. <span>{{option.rptGlsmsStatistics.total}}</span>
  133. </span>
  134. </div>
  135. <div class='bar'>
  136. <div class='inner-bar' :style='barStyle(option.rptGlsmsStatistics, item.title)'></div>
  137. </div>
  138. </li>
  139. </ul>
  140. <div
  141. class='arrow'
  142. v-show='item.expand !== 2'
  143. :style='{transform: item.expand === 1 ? "rotate(-90deg)" : "rotate(90deg)"}'
  144. @click='toggerPanel(item)'
  145. >
  146. <img src='../../assets/images/icons/right.png' />
  147. </div>
  148. </div>
  149. </section>
  150. </section>
  151. </main>
  152. </div>
  153. </template>
  154. <script>
  155. let myMaps = null
  156. import { login } from '@/api/login'
  157. import { getPlazaInfoCount, querySystemCard, getCardList, queryEventStatus } from '@/api/homePage'
  158. import { formatTime } from '@/utils/format.js'
  159. import { mapGetters } from 'vuex'
  160. import { sortBy } from 'lodash'
  161. import moment from 'moment'
  162. import L from 'leaflet'
  163. import 'leaflet-contextmenu'
  164. export default {
  165. data() {
  166. return {
  167. crumbsHtml: [], // 导航数据
  168. zoneNames: { // 各个中心对应的拼音名称
  169. "东北": 'dongbei',
  170. "西北": 'xibei',
  171. "华北": 'huabei',
  172. "华中": 'huazhong',
  173. "华东": 'huadong',
  174. "中南": 'zhongnan',
  175. "西南": 'xinan',
  176. "华南": 'huanan',
  177. "东南": 'dongnan',
  178. "其他": 'Other'
  179. },
  180. provinceCities: { // 各个区域的的省会城市 坐标 缩放级别
  181. "青岛": { province: 'shandong', center: [36.06667, 120.33333], zoom: 8 },
  182. "北京": { province: 'beijing', center: [39.604882122321174, 116.43660987308282], zoom: 8 },
  183. "天津": { province: 'tianjin', center: [39.130593, 117.260892], zoom: 7 },
  184. "济南": { province: 'shandong', center: [36.4, 117.0], zoom: 7 },
  185. "上海": { province: 'shanghai', center: [31.090574094954192, 121.46299249603001], zoom: 10 },
  186. "南京": { province: 'jiangsu', center: [32.05, 118.78333], zoom: 7 },
  187. "无锡": { province: 'jiangsu', center: [31.57, 120.3], zoom: 9 },
  188. "厦门": { province: 'fujian', center: [24.46667, 118.1], zoom: 9 },
  189. "宁波": { province: 'zhejiang', center: [29.52, 121.33], zoom: 8 },
  190. "杭州": { province: 'zhejiang', center: [30.323100460201648, 120.50270908851112], zoom: 9 },
  191. "福州": { province: 'fujian', center: [26.05, 119.18], zoom: 8 },
  192. "合肥": { province: 'anhui', center: [31.52, 117.17], zoom: 7 },
  193. "长沙": { province: 'hunan', center: [28.12, 112.59], zoom: 8 },
  194. "南昌": { province: 'jiangxi', center: [28.4, 115.55], zoom: 8 },
  195. "大连": { province: 'liaoning', center: [39.98974718404572, 122.66034125625548], zoom: 8 },
  196. "沈阳": { province: 'liaoning', center: [41.72213058512578, 122.93479223528372], zoom: 8 },
  197. "长春": { province: 'jilin', center: [42.88033923363183, 127.02188147691707], zoom: 8 },
  198. "东莞": { province: 'guangdong', center: [23.644524198573688, 114.83844948916203], zoom: 8 },
  199. "南宁": { province: 'guangxi', center: [22.48, 108.19], zoom: 7 },
  200. "广州": { province: 'guangdong', center: [22.755920681486405, 112.01244459124548], zoom: 7 },
  201. "太原": { province: 'shanxi1', center: [37.54, 112.33], zoom: 7 },
  202. "武汉": { province: 'hubei', center: [31.147006308556566, 112.97898510653813], zoom: 7 },
  203. "郑州": { province: 'henan', center: [34.46, 113.4], zoom: 7 },
  204. "哈尔滨": { province: 'heilongjiang', center: [46.800059446787316, 128.40505022926592], zoom: 7 },
  205. "乌鲁木齐": { province: 'xinjiang', center: [43.45, 87.36], zoom: 8 },
  206. "呼和浩特": { province: 'neimenggu', center: [45.182036837015886, 116.82329020199336], zoom: 5 },
  207. "西安": { province: 'shanxi2', center: [36.08462129606931, 109.4527754086213], zoom: 7 },
  208. "银川": { province: 'ningxia', center: [38.108627664321276, 102.36578196225639], zoom: 7 },
  209. "成都": { province: 'sichuan', center: [30.24957724046765, 103.4647651330231], zoom: 7 },
  210. "昆明": { province: 'yunnan', center: [25.903703303407667, 105.10972266685498], zoom: 8 },
  211. "重庆": { province: 'chongqing', center: [29.35, 106.33], zoom: 8 }
  212. },
  213. zoneCenter: { // 各个区域的中心坐标
  214. dongbei: [43.161614, 124.396818], // 取自 东北运营中心 长春区域 四平万达广场
  215. xibei: [37.999781, 106.197518], // 西北 银川区域 吴忠万达广场 zoom 5
  216. huabei: [39.019075, 117.686953], // 华北中心 天津区域 天津塘沽万达广场
  217. huazhong: [33.999249, 113.872131], // 华中 郑州 许昌万达广场
  218. huadong: [32.477565, 119.923391], // 华东 南京 泰州
  219. zhongnan: [28.860875644389676, 114.55131767165491],
  220. huanan: [22.635788, 110.17923], // 华南 南宁 玉林万达广场
  221. dongnan: [26.657907, 119.545678], //东南 福州 宁德万达广场
  222. xinan: [28.749998, 104.648788] // 西南 成都 宜宾万达广场
  223. },
  224. legendList: [ // 地图图例数据
  225. { color: '#F54E45', num: '≥200' },
  226. { color: '#F58300', num: '100-200' },
  227. { color: '#FFBA6B', num: '50-99' },
  228. { color: '#0091FF', num: '10-49' },
  229. { color: '#82C7FC', num: '1-9' },
  230. { color: '#EEEEEE', num: '0' }
  231. ],
  232. plazaInfoCount: {},
  233. systemList: [], // 系统列表数据
  234. currentZoneData: {}, // 当前分区
  235. currentDistrictData: [], //当前管理分区数据
  236. currentSelectedSys: {}, // 当前所选择的系统
  237. currentSysId: '', // 当前系统ID
  238. regulateDistrictData: [], // 分区数据
  239. maintainList: [], // 专维 维保 第三方检测 数据
  240. circleSize: { // 根据缩放级别 规定绘制点的尺寸 下标文字离点的距离
  241. 4: { inner: 50000, outer: 100000, offset: 2 },
  242. 5: { inner: 15000, outer: 30000, offset: 0.8 },
  243. 6: { inner: 15000, outer: 30000, offset: 0.8 },
  244. 7: { inner: 8000, outer: 16000, offset: 0.3 },
  245. 8: { inner: 4000, outer: 8000, offset: 0.14 },
  246. 9: { inner: 2000, outer: 4000, offset: 0.08 },
  247. 10: { inner: 1000, outer: 2000, offset: 0.04 },
  248. 11: { inner: 1000, outer: 2000, offset: 0.04 }
  249. },
  250. barColors :{ // // 专维 维保 第三方检测 颜色
  251. 0: 'linear-gradient(to right, #369CF7 , #025BAA)', // 蓝色
  252. 1: 'linear-gradient(to right, #F58300 , #F58300)', // 黄色
  253. 2: 'linear-gradient(to right, #F54E45 , #F54E45)' // 红色
  254. }
  255. }
  256. },
  257. computed: {
  258. ...mapGetters(['plazas', 'plazaId', 'accessLevel'])
  259. },
  260. watch: {},
  261. created() {
  262. this.currentTime()
  263. },
  264. mounted() {
  265. window.vm = this
  266. this.getFrameworkTreeData()
  267. window.addEventListener('resize', this.reinitalMap, false)
  268. },
  269. methods: {
  270. showToolTip (index) {
  271. this.maintainList.forEach(item => {item.showToolTip = false})
  272. this.maintainList[index].showToolTip = true
  273. },
  274. hideToolTip (index) {
  275. this.maintainList[index].showToolTip = false
  276. },
  277. reinitalMap () {
  278. if (myMaps) {
  279. myMaps.invalidateSize(true);
  280. }
  281. },
  282. imgSrc(code) {
  283. return require('../../assets/images/icons/' + code + '.png')
  284. },
  285. /**
  286. * 跳转到 当月说明书更新项目数量
  287. */
  288. navToIntroduceUpdate() {
  289. // this.$router.push({path:''})
  290. },
  291. /**
  292. * 跳转到 上线管理说明书数量
  293. */
  294. navToManageNumber() {
  295. // this.$router.push({path:''})
  296. },
  297. /**
  298. * 跳转到 当月重点关注事项项目数量
  299. */
  300. navToFocusItems() {
  301. // this.$router.push({path:''})
  302. },
  303. /**
  304. * @Description 根据数据创建导航路径
  305. * @Param data 当前点击的项
  306. */
  307. createNavpathByData(data) {
  308. if (data.level === 1) {
  309. this.crumbsHtml = []
  310. } else {
  311. let pathArr = data.path.split('/')
  312. let newArr = []
  313. pathArr.forEach(item => {
  314. newArr.push(item.split('&'))
  315. })
  316. this.crumbsHtml = newArr
  317. }
  318. this.currentZoneData = data
  319. },
  320. /**
  321. * @Description 通过导航进行导航
  322. * @Param id 所选区域的id
  323. * @Param index 点击项的序号
  324. */
  325. navgateFromCrumbs(id, index) {
  326. let data = this.getDataByCityCcode(id) // 根据
  327. if (!data) return
  328. let level = data.level //1集团 2中心 3区域 0广场
  329. this.currentZoneData = data
  330. this.currentLevel = level
  331. this.getEventStatusData(level, data.ccode)
  332. this.queryPlazaInfoCount(data.ccode, data.level, data.cid)
  333. this.getSystemList(data.ccode, data.level)
  334. if (level === 1) {
  335. this.pantGroupMap(data, this.currentSysId)
  336. } else if (level === 2) {
  337. this.pantZonesMap(data, this.currentSysId)
  338. } else {
  339. this.pantProjectsMap(data, this.currentSysId)
  340. }
  341. this.crumbsHtml = this.crumbsHtml.splice(0, index + 1)
  342. },
  343. /**
  344. * @Description 分区树 点击选择分区
  345. * @Param ids 返回选中项的id
  346. * @Param data 返回选中项的数据
  347. */
  348. selectProjectItem(ids) {
  349. let data = this.getDataByCityCcode(ids)
  350. this.currentLevel = data.level
  351. if (data.level === 0) { // level=0时 plazaId必填
  352. this.queryPlazaInfoCount(data.ccode, data.level, data.ccode)
  353. } else {
  354. this.queryPlazaInfoCount(data.ccode, data.level)
  355. }
  356. this.getSystemList(data.ccode, data.level)
  357. this.getEventStatusData(this.currentLevel, data.ccode)
  358. this.createNavpathByData(data)
  359. this.currentZoneData = data
  360. let level = data.level // //1集团 2中心 3区域 0广场
  361. if (level === 1) {
  362. this.pantGroupMap(data, this.currentSysId)
  363. } else if (level === 2) {
  364. this.pantZonesMap(data, '1001')
  365. } else if (level === 3) {
  366. this.pantProjectsMap(data, this.currentSysId)
  367. } else { // 点击项为广场时 直接跳转到详情页 地图不用响应
  368. let pathData = {fromGroup: 'true'}
  369. let routeData = this.$router.resolve({
  370. path:'/home/overview',query:{plazaId:data.ccode}
  371. });
  372. window.open(routeData.href, true);
  373. window.__fromGroupPage = {
  374. level: 0,
  375. plazaName: data.cname,
  376. plazaId: data.ccode
  377. }
  378. this.$store.commit('SETPLAZENAME', data.cname)
  379. // localStorage.setItem('PLAZAID', data.ccode)
  380. // this.$store.commit('STOREPLAZAID', data.ccode)
  381. this.$store.commit('SETISGETMAP',true)
  382. }
  383. },
  384. /**
  385. * @Description 查询项目信息统计数量
  386. * @Param ccode 管理分区编码 必填
  387. * @Param level 1集团 2中心 3区域 0广场 必填
  388. * @Param plazaId level=0时 必填
  389. */
  390. queryPlazaInfoCount(ccode, level, plazaId) {
  391. let params = {}
  392. if (level === 0 ) {
  393. params = {getParams:{ccode, level}}
  394. } else {
  395. params = {getParams:{ccode, level, plazaId}}
  396. }
  397. getPlazaInfoCount(params).then(res => {
  398. if (res.result === 'success') {
  399. if (res.data) {
  400. this.plazaInfoCount = res.data
  401. }
  402. }
  403. })
  404. },
  405. /**
  406. * @Description 根据指定id 查找数据
  407. * @Param id 要查找数据的id
  408. */
  409. getDataByCityCcode(id) {
  410. return recursionFindData(this.currentDistrictData, id)
  411. let target = null
  412. function recursionFindData(data, id) {
  413. data.forEach(item => {
  414. if (item.ccode === id) {
  415. target = item
  416. } else {
  417. if (item.children && item.children.length) {
  418. recursionFindData(item.children, id)
  419. }
  420. }
  421. })
  422. if (target) {
  423. return target
  424. }
  425. }
  426. },
  427. /**
  428. * @Description 获取分区树数据
  429. * @method param empty
  430. */
  431. getFrameworkTreeData() {
  432. login({
  433. returnTree: true
  434. }).then(res => {
  435. if (res.result === 'success') {
  436. recursionData(res.treeData)
  437. let data = res.treeData[0]
  438. let level = data.level
  439. this.currentZoneData = data
  440. this.currentLevel = level // 初始化的时候判断是哪一级 //1集团 2中心 3区域 0广场
  441. data.open = true // 默认展开第一级
  442. this.regulateDistrictData = [data]
  443. this.currentDistrictData = res.treeData
  444. this.getSystemList(data.ccode, level )
  445. this.getEventStatusData(level, data.ccode)
  446. if (level === 0) { // level=0时 plazaId必填
  447. this.queryPlazaInfoCount(data.ccode, level, data.ccode)
  448. } else {
  449. this.queryPlazaInfoCount(data.ccode, level)
  450. }
  451. }
  452. function recursionData(data, parent) {
  453. data.forEach(item => {
  454. item.name = item.cname
  455. item.id = item.ccode
  456. if (parent) {
  457. item.path = parent + '/' + (item.cname + '&' + item.ccode)
  458. }
  459. let children = item.children
  460. if (Array.isArray(children) && children.length) {
  461. recursionData(children, item.path ? item.path : item.cname + '&' + item.ccode)
  462. }
  463. })
  464. }
  465. })
  466. },
  467. /**
  468. * @Description 展开 维保 第三方检测 专维
  469. * @Param item 展开的某一项
  470. */
  471. toggerPanel(item) {
  472. // expand 0 初始状态 1 展开状态 2 隐藏状态
  473. if (item.expand === 0 || item.expand === 2) {
  474. item.expand = 1 // 展开
  475. this.maintainList.forEach(option => {
  476. if (option.title !== item.title) {
  477. option.expand = 2 // 隐藏
  478. }
  479. })
  480. } else {
  481. this.maintainList.forEach(option => {
  482. option.expand = 0 // 回到初始状态
  483. })
  484. }
  485. },
  486. /**
  487. * @Description 计算 维保/第三方检测/专维 三个模块的高度
  488. * @Param item 数据中的每一项
  489. */
  490. panelStyle(item) {
  491. // expand 0 初始状态 1 展开状态 2 隐藏状态
  492. if (item.expand === 0) {
  493. return { height: `calc((100% - 2rem) / 3)` }
  494. } else if (item.expand === 1) {
  495. return { height: 'calc(100% - 11.2rem)' }
  496. } else {
  497. return { height: '4.6rem' }
  498. }
  499. },
  500. /**
  501. *@Description 根据数据计算维保/第三方检测/专维各自颜色条的长度 维保、第三方检测是用即将逾期数据判断;专维是如果有逾期未完成直接标红,如果没有,按照即将逾期的数量显示颜色
  502. *@Param item 某一项的数据
  503. *@Param title 标题
  504. */
  505. barStyle(item, title) {
  506. let bg = ''
  507. let type = this.selectColorByNum(item.due_num, item.total)
  508. let ratio = 0
  509. if (title === '专维') {
  510. if (item.unfinished>0) {
  511. bg = 'linear-gradient(to right, #F54E45 , #F54E45)'
  512. } else {
  513. bg = this.barColors[type]
  514. }
  515. ratio = ((item.unfinished + item.due_num) / item.total) * 100
  516. } else {
  517. bg = this.barColors[type]
  518. ratio = (item.due_num / item.total) * 100
  519. }
  520. return {
  521. width: ratio + '%',
  522. backgroundImage: bg
  523. }
  524. },
  525. /**
  526. * @Description 根据数据来返 维保/第三方检测/专维 三个模块中显示条的颜色
  527. * @Param num 即将逾期数 / 逾期未完成数
  528. * @Param total 总数
  529. */
  530. selectColorByNum (num, total) {
  531. let devide = num / total
  532. if ( (num > 1 && num <=24 ) || ( devide > 0.1 && devide <=0.2 ) ) {
  533. return 0
  534. } else if ( (num > 24 && num <= 49 ) || ( devide > 0.2 && devide <=0.5 ) ) {
  535. return 1
  536. } else {
  537. return 2
  538. }
  539. },
  540. /**
  541. * @Description 根据数据 判定地图上图标的显示显示颜色
  542. * @Param data 作为判断的数据
  543. */
  544. paintColorByData(data) {
  545. let color = {
  546. area: '',
  547. border: ''
  548. }
  549. if (data >= 200) {
  550. color.area = '#F54E45'
  551. color.border = 'rgba(245,78,69,0.2)'
  552. } else if (data >= 100 && data < 200) {
  553. color.area = '#F58300'
  554. color.border = 'rgba(245,131,0,0.2)'
  555. } else if (data >= 50 && data < 100) {
  556. color.area = '#FFBA6B'
  557. color.border = 'rgba(255,186,107,0.2)'
  558. } else if (data >= 10 && data < 50) {
  559. color.area = '#0091FF'
  560. color.border = 'rgba(0,145,255,0.2)'
  561. } else if (data >= 1 && data < 10) {
  562. color.area = '#82C7FC'
  563. color.border = 'rgba(130,199,25,0.2)'
  564. } else {
  565. color.area = '#EEEEEE'
  566. color.border = '#CCC'
  567. }
  568. return color
  569. },
  570. /**
  571. * @Description 显示当期时间
  572. */
  573. currentTime() {
  574. this.times = moment().format('YYYY.MM.DD HH:mm')
  575. setTimeout(this.currentTime, 1000)
  576. },
  577. /**
  578. * @Description 格式化时间
  579. * @method param empty
  580. */
  581. formatter(str, arrv) {
  582. if (str && arrv) {
  583. const Objs = arrv.find(e => e && e.plazaid == str)
  584. return Objs ? Objs.plazaname : '--'
  585. } else {
  586. return ''
  587. }
  588. },
  589. /**
  590. * @Description 获取集团 中心 区域 项目 数据树数据
  591. * @method param ccode 管理分区编码 集团首页必填
  592. * @method param level 1集团 2中心 3区域 0广场 集团首页必填
  593. * @method param plazaId 广场id 如果是广场则此参数必填 其他情况下非必填
  594. */
  595. getSystemList(ccode, level) {
  596. let params = null
  597. if (level === 0) {
  598. params = {getParams:{plazaId: ccode, level}}
  599. } else {
  600. params = {getParams:{ccode, level}}
  601. }
  602. getCardList(params).then(res => {
  603. if (res.result == 'success') {
  604. let result = res.data
  605. if (result && Array.isArray(result)) {
  606. result.forEach(item => {
  607. if (item.assetTypeList) {
  608. item.assetTypeList = item.assetTypeList.splice(0, 3)
  609. }
  610. })
  611. res.data.forEach(item => {
  612. item.selected = false
  613. })
  614. res.data[0].selected = true
  615. this.currentSelectedSys = res.data[0]
  616. this.currentSysId = res.data[0].smsxt
  617. this.systemList = res.data
  618. this.initMap()
  619. }
  620. }
  621. })
  622. },
  623. /**
  624. * @Description 获取事项状态统计数据
  625. * @Param ccode 管理分区编码 必填
  626. * @Param level 1集团 2中心 3区域 0广场 必填
  627. */
  628. getEventStatusData(level, ccode) {
  629. let params = {
  630. getParams: {
  631. level,
  632. ccode
  633. }
  634. }
  635. queryEventStatus(params).then(res => {
  636. if (res.result === 'success') {
  637. let data = res.data
  638. let orginalObj = {
  639. 1:{title:'维保',data:[], expand: 0, showToolTip:false},
  640. 2:{title:'第三方检测',data:[], expand: 0, showToolTip:false},
  641. 0:{title:'专维',data:[], expand: 0, showToolTip:false},
  642. }
  643. let arr = new Array(3)
  644. for (let key in data) {
  645. let obj = {}
  646. let sortData = data[key]
  647. if (data.hasOwnProperty(key)) {
  648. // 维保,第三方检测排名按照即将到期任务数量从多到少排序,多的靠前。专维按照 逾期未完成 + 即将到期任务数据排序,多的靠前
  649. // due_num 即将到期 unfinished 逾期未完成
  650. if (key == '0') {
  651. sortData.forEach(item => {
  652. item.sort = item.rptGlsmsStatistics.due_num + item.rptGlsmsStatistics.unfinished
  653. })
  654. } else {
  655. sortData.forEach(item => {
  656. item.sort = item.rptGlsmsStatistics.due_num
  657. })
  658. }
  659. orginalObj[key].data = sortBy(sortData, option => {return option.sort }).reverse()
  660. }
  661. }
  662. arr[0] = orginalObj[1]
  663. arr[1] = orginalObj[2]
  664. arr[2] = orginalObj[0]
  665. this.maintainList = arr
  666. }
  667. })
  668. },
  669. /**
  670. *@Description 初始化地图
  671. */
  672. initMap() {
  673. let that = this
  674. myMaps = L.map('map', {
  675. center: new L.LatLng(38, 103),
  676. zoom: 4,
  677. // maxZoom:4,
  678. // minZoom:4,
  679. crs: L.CRS.EPSG3857,
  680. zoomControl: false,
  681. boxZoom: false,
  682. attributionControl: false,
  683. contextmenu: true,
  684. contextmenuWidth: 140,
  685. dragging: false,
  686. closePopupOnClick:true,
  687. doubleClickZoom:false
  688. })
  689. let JsonData = require('../../assets/geoData/china.json')
  690. let layer = L.geoJSON(JsonData.features, {
  691. style: function() {
  692. return {
  693. weight: 1,
  694. color: '#B3D2FF',
  695. fillColor: '#DEECFF'
  696. }
  697. }
  698. }).addTo(myMaps)
  699. if (this.currentLevel === 1) {//1集团
  700. this.pantGroupMap(this.currentZoneData, this.currentSysId)
  701. } else if (this.currentLevel === 2) { // 2中心
  702. this.pantZonesMap(this.currentZoneData, this.currentSysId)
  703. } else if (this.currentLevel === 3) { // 3区域
  704. this.pantProjectsMap(this.currentZoneData, this.currentSysId)
  705. }
  706. },
  707. /**
  708. * @Description 清空地图 并添加全国底图
  709. */
  710. clearAllLayers() {
  711. myMaps.eachLayer(function(layer) {
  712. myMaps.removeLayer(layer)
  713. })
  714. let JsonData = require('../../assets/geoData/china.json')
  715. let layer = L.geoJSON(JsonData.features, {
  716. style: function() {
  717. return {
  718. weight: 1,
  719. color: '#B3D2FF',
  720. fillColor: '#DEECFF'
  721. }
  722. }
  723. }).addTo(myMaps)
  724. },
  725. /**
  726. * @Description 移动地图到指定中心
  727. */
  728. flyTo(center, zoom) {
  729. myMaps.setView(center, zoom)
  730. },
  731. /**
  732. * @Description 切换系统时 地图响应
  733. * @Params system:当前选择的系统
  734. */
  735. getInfoOfProject(system) {
  736. this.systemList.forEach(item => {
  737. item.selected = false
  738. })
  739. system.selected = true
  740. this.currentSelectedSys = system
  741. this.currentSysId = system.smsxt
  742. //1集团 2中心 3区域 0广场
  743. this.clearAllLayers()
  744. if (this.currentLevel === 1) {
  745. this.pantGroupMap(this.currentZoneData, system.smsxt)
  746. } else if (this.currentLevel === 2) {
  747. this.pantZonesMap(this.currentZoneData, system.smsxt)
  748. } else if (this.currentLevel === 3) {
  749. this.pantProjectsMap(this.currentZoneData, system.smsxt)
  750. }
  751. },
  752. /**
  753. * @Description 用于绘制集团级别的地图
  754. * @Param data 数据
  755. */
  756. pantGroupMap(data) {
  757. this.clearAllLayers()
  758. myMaps.setView([38, 103], 4)
  759. let that = this
  760. let groupLayer = new L.layerGroup()
  761. let promiseAll = []
  762. let layerArr = []
  763. data.children.forEach(item => {
  764. let promise = new Promise((resolve, reject) => {
  765. let params = {
  766. getParams: {
  767. smsxt: this.currentSysId,
  768. level: 2, // 中心
  769. ccode: item.ccode
  770. }
  771. }
  772. querySystemCard(params).then(res => {
  773. if (res.result === 'success') {
  774. let num = 0
  775. let name = item.cname.substr(0, 2)
  776. if (res.data) {
  777. let equip = res.data[0]
  778. if (equip.assetTypeList) {
  779. equip.assetTypeList.forEach(item => {
  780. if (item.is_exception_num) {
  781. num += item.is_exception_num
  782. }
  783. })
  784. layerArr.push({
  785. name,
  786. num,
  787. list: equip,
  788. data: item,
  789. fullName: item.cname
  790. })
  791. }
  792. resolve(item)
  793. }
  794. }
  795. })
  796. })
  797. promiseAll.push(promise)
  798. })
  799. Promise.all(promiseAll).then(result => {
  800. layerArr.forEach(item => {
  801. let zone = this.zoneNames[item.name]
  802. let center = this.zoneCenter[zone]
  803. let contentHtml = that.createPopupHtmlContent(item.list.assetTypeList, item.fullName)
  804. if (contentHtml) {
  805. this.createPointOnMap(item.num, groupLayer, contentHtml, center, item.data, item.name , 'group', 4)
  806. }
  807. })
  808. })
  809. groupLayer.addTo(myMaps)
  810. },
  811. /**
  812. * @Description 用于绘制中心地图
  813. * @Param data 数据
  814. */
  815. pantZonesMap(data) {
  816. let that = this
  817. this.clearAllLayers()
  818. let name = data.cname.substr(0, 2)
  819. let zone = this.zoneNames[name]
  820. let zoom, inner, outer, offset
  821. if (['xibei'].includes(zone)) {
  822. zoom = 5
  823. } else {
  824. zoom = 6
  825. }
  826. this.flyTo(this.zoneCenter[zone], zoom)
  827. let groupLayer = new L.layerGroup()
  828. data.children.forEach(item => {
  829. let params = {
  830. getParams: {
  831. smsxt: this.currentSysId,
  832. level: 3, // 区域
  833. ccode: item.ccode
  834. }
  835. }
  836. querySystemCard(params).then(res => {
  837. if (res.result === 'success') {
  838. let name = item.cname.split('区域')[0]
  839. let city = that.provinceCities[name]
  840. let num = 0
  841. let center = city.center
  842. if (res.data) {
  843. let equip = res.data[0]
  844. let contentHtml = that.createPopupHtmlContent(equip.assetTypeList, item.cname)
  845. if (equip.assetTypeList) {
  846. equip.assetTypeList.forEach(item => {
  847. if (item.is_exception_num) {
  848. num += item.is_exception_num
  849. }
  850. })
  851. }
  852. that.createPointOnMap(num, groupLayer, contentHtml, center, item, item.cname, 'zone', 6)
  853. }
  854. }
  855. })
  856. })
  857. groupLayer.addTo(myMaps)
  858. },
  859. /**
  860. * @Description 用于绘制项目的点
  861. * @Param data 数据
  862. */
  863. pantProjectsMap(data) {
  864. this.clearAllLayers()
  865. let name = data.cname.split('区域')[0]
  866. let geo = this.provinceCities[name]
  867. this.flyTo(geo.center, geo.zoom)
  868. data.children.forEach(item => {
  869. this.pantPlazaMap(item, geo.zoom)
  870. })
  871. },
  872. /**
  873. * @Description 用于绘制广场
  874. * @Param data 数据
  875. */
  876. pantPlazaMap(item, zoom) {
  877. let that = this
  878. let groupLayer = new L.layerGroup()
  879. let params = {
  880. getParams: {
  881. plazaId: item.cid, // 所查对象是广场的时候plazaId必传
  882. smsxt: this.currentSysId,
  883. level: 0, // 广场
  884. ccode: item.ccode
  885. }
  886. }
  887. querySystemCard(params).then(res => {
  888. if (res.result === 'success') {
  889. let num = 0
  890. if (item.latitude && item.longitude) {
  891. let center = [item.latitude, item.longitude]
  892. if (res.data) {
  893. let equip = res.data[0]
  894. let contentHtml = that.createPopupHtmlContent(equip.assetTypeList, item.cname)
  895. if (equip.assetTypeList) {
  896. equip.assetTypeList.forEach(item => {
  897. if (item.is_exception_num) {
  898. num += item.is_exception_num
  899. }
  900. })
  901. }
  902. that.createPointOnMap(num, groupLayer, contentHtml, center, item, item.cname, 'project', zoom)
  903. }
  904. }
  905. }
  906. groupLayer.addTo(myMaps)
  907. })
  908. },
  909. /**
  910. * @Description 用于绘制广场的点
  911. * @Param num 当前点的逾期设备数
  912. * @Param groupLayer 地图图层
  913. * @Param contentHtml 弹出框的Html内容
  914. * @Param center 该点坐标
  915. * @Param data 该点数据
  916. * @Param name 改点名称
  917. * @Param type 地图级别 group / zone / project 集团 / 中心 / 区域
  918. * @param zoom 缩放级别
  919. */
  920. createPointOnMap (num, groupLayer, contentHtml, center, data, name, type , zoom) {
  921. let popup = null
  922. let marker = null
  923. let outCircle = null
  924. let innerCircle = null
  925. let color = this.paintColorByData(num)
  926. let myIcon = L.divIcon({
  927. html: name,
  928. className: 'my-leaflet-div-icon',
  929. iconSize: 30
  930. })
  931. let sizes = this.circleSize[zoom]
  932. if (color && center) {
  933. popup = L.popup({closeOnClick:true}).setLatLng(center).setContent(contentHtml)
  934. marker = L.marker([center[0] - sizes.offset, center[1]], { icon: myIcon }).addTo(groupLayer)
  935. outCircle = L.circle(center, sizes.outer, { color: 'transparent', fillColor: color.border, fillOpacity: 1 }).bindPopup(contentHtml).addTo(groupLayer)
  936. innerCircle =L.circle(center, sizes.inner, { color: 'transparent', fillColor: color.area, fillOpacity: 1 }).bindPopup(contentHtml).addTo(groupLayer)
  937. let feaureGroup = L.featureGroup([marker, outCircle, innerCircle, popup])
  938. feaureGroup.on('mouseover', ev => {
  939. popup.openOn(myMaps)
  940. })
  941. feaureGroup.on('click', ev => {
  942. this.currentZoneData = data
  943. this.currentLevel = data.level
  944. this.getEventStatusData(data.level, data.ccode)
  945. this.queryPlazaInfoCount(data.ccode, data.level)
  946. if (type !== 'project') {
  947. groupLayer.clearLayers()
  948. }
  949. this.createNavpathByData(data)
  950. this.getSystemList(data.ccode, data.level)
  951. if (type === 'group') {
  952. this.pantZonesMap(data, this.currentSysId)
  953. }
  954. if (type === 'zone') {
  955. this.pantProjectsMap(data, this.currentSysId)
  956. }
  957. if (type === 'project') {
  958. let routeData = this.$router.resolve({
  959. path:'/home/overview',query:{plazaId:data.ccode}
  960. });
  961. window.open(routeData.href, '_blank');
  962. window.__fromGroupPage = {
  963. level: 0,
  964. plazaName: data.cname,
  965. plazaId: data.ccode
  966. }
  967. this.$store.commit('SETPLAZENAME', data.cname)
  968. // localStorage.setItem('PLAZAID', data.ccode)
  969. // this.$store.commit('STOREPLAZAID', data.ccode)
  970. this.$store.commit('SETISGETMAP',true)
  971. }
  972. })
  973. }
  974. },
  975. /**
  976. *@Description 生成弹出框HTML
  977. */
  978. createPopupHtmlContent(data, name) {
  979. if (Array.isArray(data) && data.length) {
  980. let that = this
  981. let html = ''
  982. let len = data.length
  983. for (let i = 0; i < len; i++) {
  984. let item = data[i]
  985. if (item.is_exception_num) {
  986. html += `<div class="line">
  987. <span>${item.category_name}</span>
  988. <span>${item.asset_num || '--'}台</span>
  989. <span><span style="display: inline-block;width: 16px;height: 16px;line-height: 16px;border-radius: 8px; color: white; background : #F54E45;text-align: center;cursor: default;">!</span><em>${
  990. item.is_exception_num
  991. }</em></span>
  992. </div>`
  993. } else {
  994. html += `<div class="line">
  995. <span>${item.category_name}</span>
  996. <span>${item.asset_num || '--'}台</span>
  997. </div>`
  998. }
  999. }
  1000. return `<div class="leaflet-mypopup-content"><div class="title"><span>${name}</span>-<span>${that.currentSelectedSys.smsxtname}</span></div>${html}</div>`
  1001. } else {
  1002. return ''
  1003. }
  1004. },
  1005. },
  1006. beforeDestroy() {
  1007. window.removeEventListener('resize', this.reinitalMap)
  1008. }
  1009. }
  1010. </script>
  1011. <style scoped lang="less">
  1012. @keyframes selectDownUpExtendTop {
  1013. from {
  1014. transform: translateY(8px);
  1015. opacity: 0;
  1016. }
  1017. to {
  1018. transform: translateY(0);
  1019. opacity: 1;
  1020. }
  1021. }
  1022. .selectDownUpExtendTop-enter-active {
  1023. animation: selectDownUpExtendTop .3s
  1024. }
  1025. .selectDownUpExtendTop-leave-active {
  1026. animation: selectDownUpExtendTop .3s reverse
  1027. }
  1028. .overview {
  1029. height: 100vh;
  1030. background: rgba(247, 249, 250, 1);
  1031. .menu {
  1032. height: 48px;
  1033. min-width: 1366px;
  1034. color: #1f2429;
  1035. font-size: 1.6rem;
  1036. background: rgba(255, 255, 255, 1);
  1037. box-shadow: 0px 2px 10px 0px rgba(31, 35, 41, 0.1);
  1038. overflow: hidden;
  1039. .home {
  1040. width: 265px;
  1041. height: 48px;
  1042. // line-height: 48px;
  1043. text-align: center;
  1044. cursor: pointer;
  1045. color: #ffffff;
  1046. float: left;
  1047. margin-right: 83px;
  1048. // background: linear-gradient(180deg, rgba(54, 156, 247, 1) 0%, rgba(2, 91, 170, 1) 100%);
  1049. background: linear-gradient(180deg, rgba(54, 156, 247, 1) 0%, rgba(2, 91, 170, 1) 100%);
  1050. position: relative;
  1051. .downright {
  1052. position: absolute;
  1053. width: 0;
  1054. height: 0;
  1055. border-left: 20px solid transparent;
  1056. border-bottom: 48px solid #fff;
  1057. right: 0px;
  1058. top: 0;
  1059. }
  1060. .home-box {
  1061. height: 100%;
  1062. display: flex;
  1063. align-items: center;
  1064. img {
  1065. width: 28px;
  1066. height: 28px;
  1067. margin-left: 20px;
  1068. margin-right: 24px;
  1069. margin-top: -3px;
  1070. }
  1071. span {
  1072. font-size: 2rem;
  1073. font-family: MicrosoftYaHei;
  1074. height: 27px;
  1075. line-height: 27px;
  1076. margin-top: -4px;
  1077. &:after {
  1078. content: '|';
  1079. position: absolute;
  1080. left: 60px;
  1081. }
  1082. }
  1083. }
  1084. }
  1085. & > div:nth-of-type(2) {
  1086. & > div {
  1087. line-height: 48px;
  1088. text-align: center;
  1089. //background: url('../assets/images/topbar1.png') no-repeat;
  1090. float: left;
  1091. width: 80px;
  1092. height: 48px;
  1093. cursor: pointer;
  1094. transition: all 0.2s;
  1095. }
  1096. .is-active {
  1097. color: #025baa;
  1098. font-weight: bolder;
  1099. border-bottom: 2px solid #025baa;
  1100. background: linear-gradient(180deg, rgba(2, 91, 170, 0) 0%, rgba(2, 91, 170, 0.2) 100%);
  1101. }
  1102. }
  1103. .home-right {
  1104. float: right;
  1105. margin-right: 20px;
  1106. line-height: 48px;
  1107. color: #646c73;
  1108. font-size: 1.4rem;
  1109. cursor: pointer;
  1110. display: flex;
  1111. align-content: center;
  1112. img {
  1113. margin-top: -2px;
  1114. }
  1115. .span-out {
  1116. position: relative;
  1117. margin: 0 16px;
  1118. .span2-num {
  1119. position: absolute;
  1120. right: -5px;
  1121. top: 5px;
  1122. display: inline-block;
  1123. width: 18px;
  1124. height: 18px;
  1125. background: red;
  1126. border-radius: 90px;
  1127. font-size: 1.2rem;
  1128. text-align: center;
  1129. line-height: 18px;
  1130. color: #ffffff;
  1131. }
  1132. }
  1133. }
  1134. .span1,
  1135. .span2 {
  1136. padding: 0 6px 0 3px;
  1137. }
  1138. .span3 {
  1139. padding-left: 3px;
  1140. }
  1141. }
  1142. .navigation {
  1143. display: flex;
  1144. justify-content: space-between;
  1145. align-items: center;
  1146. height: 50px;
  1147. padding: 0 16px;
  1148. background: white;
  1149. .crumbs {
  1150. span {
  1151. cursor: pointer;
  1152. }
  1153. span:first-child {
  1154. display: inline-block;
  1155. width: 14.4rem;
  1156. height: 2.8rem;
  1157. margin-right: 1.2rem;
  1158. border: 1px solid rgba(195, 199, 203, 1);
  1159. border-radius: 1.4rem;
  1160. text-align: center;
  1161. line-height: 2.8rem;
  1162. font-size: 14px;
  1163. cursor: default;
  1164. }
  1165. span:last-child {
  1166. font-weight: bold;
  1167. }
  1168. }
  1169. }
  1170. .container {
  1171. display: flex;
  1172. justify-content: space-between;
  1173. height: calc(100vh - 98px);
  1174. padding: 1.2rem 1.6rem;
  1175. .section-title {
  1176. display: flex;
  1177. align-items: center;
  1178. padding-left: 10px;
  1179. border-left: 4px solid #025baa;
  1180. color: #1f2429;
  1181. font-size: 1.6rem;
  1182. font-weight: bolder;
  1183. line-height: 2rem;
  1184. cursor: pointer;
  1185. img{
  1186. width: 2rem;
  1187. margin-left: 1.2rem;
  1188. }
  1189. }
  1190. .system-general {
  1191. width: 25.4%;
  1192. box-sizing: border-box;
  1193. background: white;
  1194. .system-main-title {
  1195. padding: 2rem 0 2.4rem 1.6rem;
  1196. }
  1197. .system-list {
  1198. ul {
  1199. height: 80vh;
  1200. li.system-item {
  1201. display: flex;
  1202. justify-content: flex-start;
  1203. align-items: center;
  1204. width: calc(100% - 3.2rem);
  1205. height: calc(100% / 8);
  1206. margin-left: 1.6rem;
  1207. // padding: 1rem 0;
  1208. box-sizing: border-box;
  1209. border: 1px solid white;
  1210. border-radius: 4px;
  1211. border-bottom: 1px solid #eff0f1;
  1212. transition: 0.5s;
  1213. cursor: pointer;
  1214. .system-name {
  1215. display: flex;
  1216. align-items: center;
  1217. width: 28%;
  1218. padding: 1.2rem 0 1.2rem 1rem ;
  1219. border-right: 1px solid #EFF0F1;
  1220. .img {
  1221. height: 2.8rem;
  1222. width: 2.8rem;
  1223. display: flex;
  1224. align-items: center;
  1225. justify-content: center;
  1226. border-radius: 50%;
  1227. background: #dce7f0;
  1228. filter: saturate(0%);
  1229. img {
  1230. width: 3.2rem;
  1231. height: 3.2rem;
  1232. }
  1233. }
  1234. .selected {
  1235. filter: saturate(100%);
  1236. }
  1237. span {
  1238. display: inline-block;
  1239. margin-left: 0.8rem;
  1240. color: #1f2429;
  1241. font-size: 1.4rem;
  1242. font-weight: bolder;
  1243. }
  1244. }
  1245. .system-equipments {
  1246. width: 72%;
  1247. display: flex;
  1248. align-items: center;
  1249. padding-left: 1.4rem;
  1250. .number {
  1251. width: 33.3%;
  1252. margin-right: 1.2rem;
  1253. .title{
  1254. display: inline-block;
  1255. position: relative;
  1256. p{
  1257. display: inline-block;
  1258. position: relative;
  1259. width: 100%;
  1260. color: #1f2429;
  1261. font-size: 1.4rem;
  1262. line-height: 1.6rem;
  1263. white-space: nowrap;
  1264. text-overflow: ellipsis;
  1265. overflow: hidden;
  1266. }
  1267. span {
  1268. position: absolute;
  1269. top: -1.6rem;
  1270. right: -2rem;
  1271. padding: 2px 4px;
  1272. border-radius: 0.9rem;
  1273. font-size: 1.2rem;
  1274. color: #f54e45;
  1275. background: #fde3e2;
  1276. }
  1277. }
  1278. p:nth-of-type(2) {
  1279. span:first-child {
  1280. color: #1f2429;
  1281. font-size: 1.8rem;
  1282. font-weight: bold;
  1283. }
  1284. }
  1285. }
  1286. }
  1287. }
  1288. li.selected {
  1289. border-color: rgba(0, 118, 197, 0.6);
  1290. box-shadow: 0 2px 10px 0px rgba(0, 118, 197, 0.4);
  1291. }
  1292. li:last-child {
  1293. border-bottom: none;
  1294. }
  1295. li:hover {
  1296. border-color: #66addd;
  1297. box-shadow: 0 2px 10px 0px rgba(204, 100, 77, 0.4);
  1298. }
  1299. }
  1300. }
  1301. }
  1302. .map-box {
  1303. width: calc(56.6% - 2.4rem);
  1304. margin: 0 12px;
  1305. .cards-list {
  1306. display: flex;
  1307. justify-content: space-between;
  1308. .card {
  1309. width: calc((100% - 24px) / 3);
  1310. box-sizing: border-box;
  1311. padding: 12px 16px;
  1312. border-radius: 4px;
  1313. background: white;
  1314. .img {
  1315. display: flex;
  1316. justify-content: center;
  1317. align-items: center;
  1318. width: 4.4rem;
  1319. height: 4.4rem;
  1320. border-radius: 50%;
  1321. img {
  1322. width: 2rem;
  1323. height: 2rem;
  1324. }
  1325. }
  1326. }
  1327. div:nth-of-type(1) .img {
  1328. background: rgba(130, 199, 252, 0.14);
  1329. }
  1330. div:nth-of-type(2) .img {
  1331. background: rgba(0, 214, 185, 0.14);
  1332. }
  1333. div:nth-of-type(3) .img {
  1334. background: rgba(255, 186, 107, 0.14);
  1335. }
  1336. p {
  1337. color: #646c73;
  1338. margin: 12px 0 8px 0;
  1339. }
  1340. strong {
  1341. color: #1f2429;
  1342. font-size: 2.4rem;
  1343. }
  1344. }
  1345. .map-container {
  1346. position: relative;
  1347. height: 70vh;
  1348. #map {
  1349. height: 70vh;
  1350. overflow: hidden;
  1351. pointer-events: none;
  1352. }
  1353. .legend {
  1354. position: absolute;
  1355. left: 1.4rem;
  1356. bottom: 2.7rem;
  1357. padding: 0.6rem;
  1358. background: white;
  1359. .legend-bar {
  1360. display: flex;
  1361. align-items: center;
  1362. span {
  1363. display: inline-block;
  1364. height: 4px;
  1365. width: 20px;
  1366. margin-right: 12px;
  1367. }
  1368. }
  1369. }
  1370. }
  1371. }
  1372. .ratio-list {
  1373. width: 18%;
  1374. .block {
  1375. position: relative;
  1376. padding: 1.2rem 1.6rem;
  1377. height: calc((100% - 2rem) / 3);
  1378. border-radius: 4px;
  1379. margin-bottom: 1rem;
  1380. background: white;
  1381. // overflow: auto;
  1382. transition: height 0.3s ease-in-out;
  1383. .section-title {
  1384. font-size: 1.6rem;
  1385. }
  1386. .tool-tip{
  1387. position: absolute;
  1388. left: 0;
  1389. top: -6.6rem;
  1390. width: 100%;
  1391. border-radius: 4px;
  1392. padding: 0.8rem 1.6rem 1.6rem;
  1393. font-size: 1.4rem;
  1394. line-height: 2.2rem;
  1395. background: white;
  1396. border:1px solid rgba(151,151,151, 0.2);
  1397. filter: drop-shadow(0 2px 4px rgba(31, 35, 41, .1)) ;
  1398. .tip-triangle{
  1399. position: absolute;
  1400. position: absolute;
  1401. left: 7.8rem;
  1402. border-style: solid;
  1403. width: 0;
  1404. height: 0;
  1405. bottom: -6px;
  1406. border-width: 6px 6px 0 6px;
  1407. border-color: rgba(247, 249, 250, 1) transparent transparent transparent;
  1408. }
  1409. }
  1410. .list {
  1411. margin-top: 1.4rem;
  1412. overflow: hidden;
  1413. height: calc(100% - 3rem);
  1414. ul {
  1415. li {
  1416. display: flex;
  1417. flex-direction: column;
  1418. justify-content: center;
  1419. margin-bottom: 1.2rem;
  1420. .ratio {
  1421. display: flex;
  1422. justify-content: space-between;
  1423. margin-bottom: 0.5rem;
  1424. span:first-child {
  1425. color: #646c73;
  1426. font-size: 1.4rem;
  1427. }
  1428. }
  1429. .bar {
  1430. position: relative;
  1431. height: 6px;
  1432. border-radius: 3px;
  1433. background: #c3c7cb;
  1434. .inner-bar {
  1435. position: absolute;
  1436. height: 6px;
  1437. left: 0;
  1438. top: 0;
  1439. border-radius: 3px;
  1440. }
  1441. }
  1442. }
  1443. }
  1444. }
  1445. .arrow {
  1446. position: absolute;
  1447. bottom: 0;
  1448. left: 50%;
  1449. cursor: pointer;
  1450. transform-origin: center center;
  1451. transform: translateX(-50%) rotate(90deg);
  1452. }
  1453. }
  1454. section:last-child.block {
  1455. margin-bottom: 0;
  1456. }
  1457. }
  1458. }
  1459. }
  1460. </style>