index.vue 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184
  1. <template>
  2. <div class="gis-layout">
  3. <EasyMapComponent
  4. class="map"
  5. @easyMapLoad="mapLoad"
  6. />
  7. <div class="gis-menu">
  8. <img class="gis-menu-bottom" src="@/assets/images/gis-layout/gis-layout-menu_bottom.png" alt=""/>
  9. <template v-for="item in menuCpt">
  10. <div class="gis-menu-item __hover" :class="{active: $route.name === item.name, max: String(item.meta.title).length >5}" @click="$router.push({name: item.name})">
  11. {{item.meta.title}}
  12. </div>
  13. </template>
  14. </div>
  15. <div class="gis-tools">
  16. <div class="gt-search __box-shadow">
  17. <el-autocomplete
  18. ref="ref_search"
  19. v-model="searchInput"
  20. :fetch-suggestions="searchHandleMapSearch"
  21. :popper-append-to-body="false"
  22. clearable
  23. :disabled="true"
  24. :debounce="800"
  25. placeholder="请输入关键字进行搜索"
  26. @select="searchToMapLocation"
  27. >
  28. <template #default="{ item }">
  29. <div class="search-item __hover">
  30. <img :src="item.icon"/>
  31. <div class="name">{{item.name}}</div>
  32. </div>
  33. </template>
  34. </el-autocomplete>
  35. <div class="search-icon __hover" @click="ref_search.focus()">
  36. <SvgIcon name="search" color="#ffffff"/>
  37. </div>
  38. </div>
  39. <div class="gt-tools __box-shadow">
  40. <template v-for="(item, index) in ToolsMapper">
  41. <div class="tools-line" v-if="index > 0"/>
  42. <div class="tools-item __hover" :class="{active: toolsType === item.value, disabled: item.disabled}" @click="item.disabled ? undefined : toolsHandleClick(item)">
  43. {{item.label}}
  44. </div>
  45. </template>
  46. </div>
  47. <div class="gt-tools-component" v-if="toolsType && toolsType !== 'clear' && toolsCom">
  48. <Component :is="toolsCom" :map="map" :mapFunc="mapFunc" v-model:transfer="toolsParams[toolsType]" @cancel="toolsType = ''"/>
  49. </div>
  50. </div>
  51. <div class="gis-content">
  52. <RouterViewCom/>
  53. </div>
  54. <VideoPlayKedaCom v-if="showVideo" v-model:layout="videoLayout" :form="qyParams.sbInfo" @close="showVideo = false"/>
  55. <div class="mockButtons">
  56. <el-button @click="mockSB1">模拟设备1</el-button>
  57. </div>
  58. </div>
  59. <div ref="ref_qyDom" class="qy-info" :class="`qy--info-${qyParams.qyInfo.tab}`">
  60. <div class="qy-bg-icon qy-bg-icon-1"/>
  61. <div class="qy-bg-icon qy-bg-icon-2"/>
  62. <div class="qy-bg-icon qy-bg-icon-3"/>
  63. <div class="qy-bg-icon qy-bg-icon-4"/>
  64. <div class="qy-main">
  65. <div class="qy-main-head">
  66. <div class="qy-main-head-tips">【企业】</div>
  67. <div class="qy-main-head-name">{{ qyParams.qyInfo.name }}</div>
  68. <SvgIcon class="__hover" name="close_4" size="14" color="#8FFFFF" @click="onCloseQy"/>
  69. </div>
  70. <div class="qy-main-tab">
  71. <template v-for="item in [
  72. {label: '基本信息', value: '1', disabled: false},
  73. {label: '税收信息', value: '2', disabled: true},
  74. {label: '运输车辆', value: '3', disabled: true},
  75. {label: '能耗消息', value: '4', disabled: true},
  76. {label: '周边设备', value: '5', disabled: false},
  77. ]">
  78. <div class="qy-main-tab-item __hover" :class="{active: item.value === qyParams.qyInfo.tab, disabled: item.disabled}" @click="item.disabled ? undefined : qyParams.qyInfo.tab = item.value">{{item.label}}</div>
  79. </template>
  80. </div>
  81. <div v-if="qyParams.qyInfo.tab === '1'" class="qy-main-content-1">
  82. <div>企业名称:{{qyParams.qyInfo[qyParams.qyInfo.tab].name}}</div>
  83. <div>企业法人:{{qyParams.qyInfo[qyParams.qyInfo.tab].people}}</div>
  84. <div>统一社会信用代码:{{qyParams.qyInfo[qyParams.qyInfo.tab].number}}</div>
  85. <div>经营地址:{{qyParams.qyInfo[qyParams.qyInfo.tab].address}}</div>
  86. </div>
  87. <div v-else-if="qyParams.qyInfo.tab === '5'" class="qy-main-content-5">
  88. <div class="qy-main-content-5-radius">
  89. 周边范围:
  90. <div class="radius-min __hover" @click="qyParams.qyInfo[qyParams.qyInfo.tab].radius > 1 ? (qyParams.qyInfo[qyParams.qyInfo.tab].radius--) : undefined">-</div>
  91. <CusFormColumn
  92. link="number"
  93. label=""
  94. :min="1"
  95. :max="60"
  96. :clearable="false"
  97. v-model:param="qyParams.qyInfo[qyParams.qyInfo.tab].radius"
  98. @blur="handleRangeBlur"/>
  99. <div class="radius-max __hover" @click="qyParams.qyInfo[qyParams.qyInfo.tab].radius < 60 ? (qyParams.qyInfo[qyParams.qyInfo.tab].radius++) : undefined">+</div>
  100. km
  101. <div class="submit __hover" @click="onRadiusSubmit">确定</div>
  102. <div class="reset __hover" @click="onRadiusReset">重置</div>
  103. </div>
  104. <div class="qy-main-content-5-table" :style="`height: ${25 + (qyParams.qyInfo[qyParams.qyInfo.tab].tableData.length > 5 ? 5 * 25 : qyParams.qyInfo[qyParams.qyInfo.tab].tableData.length * 25)}px;`" v-loading="qyParams.qyInfo[qyParams.qyInfo.tab].loading" element-loading-background="rgba(0, 0, 0, 0.3)">
  105. <el-auto-resizer>
  106. <template #default="{ height, width }">
  107. <V2Table
  108. :width="width"
  109. :height="height"
  110. :data="qyParams.qyInfo[qyParams.qyInfo.tab].tableData"
  111. :center="qyParams.qyInfo['5'].center"
  112. />
  113. <!-- <el-table-v2-->
  114. <!-- class="__gis-overlay_table-v2"-->
  115. <!-- :columns="qyParams.qyInfo[qyParams.qyInfo.tab].tableHead"-->
  116. <!-- :data="qyParams.qyInfo[qyParams.qyInfo.tab].tableData"-->
  117. <!-- :width="width"-->
  118. <!-- :height="height"-->
  119. <!-- fixed-->
  120. <!-- :row-height="25"-->
  121. <!-- :header-height="25"-->
  122. <!-- >-->
  123. <!-- <template #empty></template>-->
  124. <!-- </el-table-v2>-->
  125. </template>
  126. </el-auto-resizer>
  127. </div>
  128. </div>
  129. </div>
  130. </div>
  131. <div ref="ref_sbDom" class="sb-info">
  132. <div class="sb-info-head">
  133. <SvgIcon class="__hover" name="tips" size="14" color="#8FFFFF"/>设备
  134. </div>
  135. <div class="sb-info-close __hover" @click="onCloseSb">
  136. <img src="@/components/easyMap/images/close.png" alt=""/>
  137. </div>
  138. <div class="sb-main">
  139. <div class="sb-item">
  140. <div class="sb-item-label">名称:</div>
  141. <div class="sb-item-value">{{qyParams.sbInfo?.name}}</div>
  142. </div>
  143. <div class="sb-item">
  144. <div class="sb-item-label">状态:</div>
  145. <div class="sb-item-value">{{qyParams.sbInfo?.online === '0' ? '在线' : '离线'}}</div>
  146. </div>
  147. <div class="play-button __hover" @click="showVideo = true">视频调阅</div>
  148. </div>
  149. </div>
  150. <GisDefaultDom ref="ref_gisDefault"/>
  151. </template>
  152. <script lang="ts">
  153. import {
  154. defineComponent,
  155. computed,
  156. onMounted,
  157. ref,
  158. reactive,
  159. watch,
  160. getCurrentInstance,
  161. ComponentInternalInstance,
  162. toRefs,
  163. nextTick,
  164. markRaw
  165. } from 'vue'
  166. import {useStore} from 'vuex'
  167. import {useRouter, useRoute} from 'vue-router'
  168. import {ElMessage, ElMessageBox} from "element-plus";
  169. import RouterViewCom from '@/layout/router-view.vue'
  170. import ElementCom from './tools/element.vue'
  171. import SelectCom from './tools/select.vue'
  172. import AnalysisCom from './tools/analysis.vue'
  173. import ToolCom from './tools/tool.vue'
  174. import PositionCom from './tools/position.vue'
  175. import BaseCom from './tools/base.vue'
  176. import ExampleCom from './tools/example.vue'
  177. import * as source from "ol/source";
  178. import * as layer from "ol/layer";
  179. import * as style from "ol/style";
  180. import * as format from "ol/format";
  181. import QyStyle from '../map-info/style/qy'
  182. import SbStyle from '../map-info/style/sb'
  183. import CommonStyle from '../map-info/style/common'
  184. import * as ol from "ol";
  185. import { v4 } from "uuid";
  186. import * as proj from "ol/proj";
  187. import * as turf from '@turf/turf'
  188. import * as geom from 'ol/geom';
  189. import VideoPlayKedaCom from "@/views/gis/business/common/VideoPlayKeda.vue";
  190. import V2Table from "./v2-table.vue";
  191. import {deviceQuery, enterpriseQuery} from "@/api/modules/enterprise";
  192. import {clearLocationDom} from '@/components/easyMap/func/location'
  193. import {clearMeasureDom} from '@/components/easyMap/func/measure'
  194. import GisDefaultDom from '../map-info/overlay/default/index.vue'
  195. export default defineComponent({
  196. name: '',
  197. components: {
  198. RouterViewCom,
  199. ElementCom,
  200. SelectCom,
  201. AnalysisCom,
  202. ToolCom,
  203. PositionCom,
  204. BaseCom,
  205. ExampleCom,
  206. VideoPlayKedaCom,
  207. V2Table,
  208. GisDefaultDom
  209. },
  210. props: {},
  211. setup(props, {emit}) {
  212. const store = useStore();
  213. const router = useRouter();
  214. const route = useRoute();
  215. const that = (getCurrentInstance() as ComponentInternalInstance).appContext.config.globalProperties
  216. const state = reactive({
  217. map: <any>null,
  218. mapFunc: <any>null,
  219. searchInput: '',
  220. toolsType: '',
  221. toolsCom: null,
  222. toolsParams: {
  223. element: null,
  224. position: null
  225. },
  226. qyParams: {
  227. layer: <any>null,
  228. source: <any>null,
  229. tempFeature: <any>null,
  230. overlay: <any>null,
  231. qyInfo: <any>{},
  232. analysisLayer: <any>null,
  233. analysisSource: <any>null,
  234. analysisCircle: <any>null,
  235. tempSbFeature: <any>null,
  236. sbOverlay: <any>null,
  237. sbInfo: <any>{
  238. name: '505县道新安村路口1-枪机-0110580',
  239. code: '46044123124125125',
  240. },
  241. },
  242. videoLayout: {
  243. width: 640,
  244. height: 366,
  245. left: 1200,
  246. top: 460
  247. },
  248. showVideo: false
  249. })
  250. const ToolsMapper = [
  251. {label: '图层', value: 'element', com: ElementCom, disabled: true},
  252. {label: '框选', value: 'select', com: SelectCom, disabled: true},
  253. {label: '周边分析', value: 'analysis', com: AnalysisCom, disabled: true},
  254. {label: '清空图层', value: 'clear', com: undefined},
  255. {label: '工具', value: 'tool', com: ToolCom},
  256. {label: '定位', value: 'position', com: PositionCom},
  257. {label: '底图', value: 'base', com: BaseCom},
  258. {label: '图例', value: 'example', com: ExampleCom},
  259. ]
  260. const ref_search = ref()
  261. const ref_gisDefault = ref()
  262. const mapLoad = (map, func) => {
  263. state.map = map
  264. store.dispatch('gis/LOAD_GIS_MAP', {
  265. map: state.map,
  266. defaultDom: ref_gisDefault.value
  267. })
  268. state.mapFunc = func
  269. state.map.on('singleclick', e => {
  270. let flag = false
  271. map.forEachFeatureAtPixel(e.pixel, (f) => {
  272. if (!flag && f.get('featureType')) {
  273. flag = true
  274. if (f.get('featureType') === 'qy') { // 企业
  275. // 恢复上一个要素的样式
  276. if (f.getId() !== state.qyParams.tempFeature?.getId()) {
  277. state.qyParams.tempFeature?.get('resetStyle')?.()
  278. state.qyParams.analysisSource?.clear()
  279. // 新的要素
  280. if (f.get('isAnalysis')) {
  281. f.setStyle(f.get('analysisActiveStyle'))
  282. } else {
  283. f.setStyle(f.get('activeStyle'))
  284. }
  285. state.qyParams.qyInfo = JSON.parse(JSON.stringify(f.get('info')))
  286. store.dispatch('gis/LOAD_ACTIVE_QY_ID', f.getId())
  287. // 备份新的要素
  288. state.qyParams.tempFeature = f
  289. }
  290. state.qyParams.overlay.setPosition(f.getGeometry().getCoordinates())
  291. } else if (f.get('featureType') === 'sb') { // 设备
  292. // 恢复上一个要素的样式
  293. if (f.getId() !== state.qyParams.tempSbFeature?.getId()) {
  294. state.qyParams.tempSbFeature?.get('resetStyle')?.()
  295. // 新的要素
  296. if (f.get('isAnalysis')) {
  297. f.setStyle(f.get('analysisActiveStyle'))
  298. } else {
  299. f.setStyle(f.get('activeStyle'))
  300. }
  301. state.qyParams.sbInfo = JSON.parse(JSON.stringify(f.get('info')))
  302. state.qyParams.sbOverlay.setPosition(f.getGeometry().getCoordinates())
  303. // 备份新的要素
  304. state.qyParams.tempSbFeature = f
  305. }
  306. state.qyParams.overlay.setPosition(undefined)
  307. }
  308. }
  309. }, {
  310. hitTolerance: 0,
  311. });
  312. })
  313. initQYLayer()
  314. }
  315. const menuCpt = computed(() => {
  316. return router.options.routes.filter(v => v.name === store.state.gis.menuRootName)[0].children?.filter(v => !v.meta.noMenu)
  317. })
  318. const searchHandleMapSearch = (queryString: string, cb: (arg: any) => void) => {
  319. if (queryString.trim()) {
  320. const arr = [
  321. {name: 123},
  322. {name: 123},
  323. {name: 123},
  324. {name: 123},
  325. {name: 123},
  326. {name: 123},
  327. ]
  328. cb(arr)
  329. } else {
  330. cb([])
  331. }
  332. }
  333. const searchToMapLocation = (val) => {
  334. state.searchInput = val.name
  335. // if (val.source === 'element') {
  336. // state.elementFilter.forEach(l => {
  337. // l.list.forEach(v => {
  338. // if (v.value === val.elementType) {
  339. // v.active = true
  340. // }
  341. // })
  342. // })
  343. // }
  344. // positionSwitchGeom(val.geomType, val.coordinates, true, val.source)
  345. }
  346. const toolsHandleClick = (item) => {
  347. if (item.value === 'clear') {
  348. // 工具-标绘'layerName', 'toolDrawViewsLayer'
  349. state.map.getLayers().getArray().filter(v => v.get('layerName') === 'toolDrawViewsLayer')?.[0]?.getSource()?.clear()
  350. state.map.getOverlayById('toolOverlay')?.setPosition(undefined)
  351. // 工具-测量'layerName', 'measureLayer'
  352. state.map.getLayers().getArray().filter(v => v.get('layerName') === 'measureLayer')?.[0]?.getSource()?.clear()
  353. clearMeasureDom(state.map)
  354. // 定位'layerName', 'positionLayer'
  355. state.map.getLayers().getArray().filter(v => v.get('layerName') === 'positionLayer')?.[0]?.getSource()?.clear()
  356. clearLocationDom()
  357. } else {
  358. state.toolsCom = markRaw(item.com)
  359. state.toolsType = (state.toolsType === item.value ? '' : item.value)
  360. }
  361. }
  362. const ref_qyDom = ref()
  363. const ref_sbDom = ref()
  364. const initQYLayer = () => {
  365. enterpriseQuery({
  366. "pageNumber":1,
  367. "pageSize":200,
  368. // "entName":null,
  369. // "entType":"加工增值"
  370. }).then((res: any) => {
  371. if (res.resp_code === 0 && res.datas?.length > 0) {
  372. const features: any = []
  373. res.datas.forEach(v => {
  374. try {
  375. const feat: any = new format.WKT().readFeature(`POINT(${v.longitude} ${v.latitude})`)
  376. let type = ''
  377. if (v.qykx === '零关税自用进口生产设备') {
  378. type = 'lgszyjkscsb'
  379. } else if (v.qykx === '加工增值免关税') {
  380. type = 'jgzzmgs'
  381. } else if (v.qykx === '零关税进口原辅料') {
  382. type = 'lgsjkyfl'
  383. }
  384. feat.set('defaultStyle', QyStyle.qyStyle(type))
  385. feat.set('activeStyle', [...CommonStyle.activeStyle(), ...QyStyle.qyStyle(type)])
  386. feat.set('analysisStyle', [...CommonStyle.analysisStyle(), ...QyStyle.qyStyle(type)])
  387. feat.set('analysisActiveStyle', [...CommonStyle.activeStyle(), ...CommonStyle.analysisStyle(), ...QyStyle.qyStyle(type)])
  388. feat.set('isAnalysis', false)
  389. feat.set('resetStyle', () => {
  390. if (feat.get('isAnalysis')) {
  391. feat.setStyle(feat.get('analysisStyle'))
  392. } else {
  393. feat.setStyle(feat.get('defaultStyle'))
  394. }
  395. })
  396. const obj = {
  397. coordinates: feat.getGeometry().getCoordinates(),
  398. tab: '1',
  399. name: v.qymc,
  400. 1: {
  401. name: v.qymc,
  402. people: v.lerep,
  403. number: v.uniscid,
  404. address: v.dom
  405. },
  406. 2: {},
  407. 3: {},
  408. 4: {},
  409. 5: {
  410. radius: 10,
  411. center: feat.getGeometry().getCoordinates(),
  412. tableData: [],
  413. loading: false
  414. },
  415. }
  416. feat.set('info', obj)
  417. feat.set('featureType', 'qy')
  418. feat.setStyle(feat.get('defaultStyle'))
  419. feat.setId(v.id)
  420. feat.set('mockClick', () => {
  421. state.qyParams.tempFeature?.get('resetStyle')?.()
  422. state.qyParams.analysisSource?.clear()
  423. // 新的要素
  424. if (feat.get('isAnalysis')) {
  425. feat.setStyle(feat.get('analysisActiveStyle'))
  426. } else {
  427. feat.setStyle(feat.get('activeStyle'))
  428. }
  429. state.qyParams.qyInfo = JSON.parse(JSON.stringify(feat.get('info')))
  430. // 备份新的要素
  431. state.qyParams.tempFeature = feat
  432. state.qyParams.overlay.setPosition(feat.getGeometry().getCoordinates())
  433. store.dispatch('gis/LOAD_ACTIVE_QY_ID', v.id)
  434. })
  435. feat.set('reset', () => {
  436. onCloseQy()
  437. onCloseSb()
  438. })
  439. features.push(feat)
  440. } catch (e) {
  441. console.error('异常企业:', v)
  442. }
  443. })
  444. state.qyParams.source = new source.Vector({
  445. features: features,
  446. wrapX: false
  447. })
  448. state.qyParams.layer = new layer.VectorImage({
  449. source: state.qyParams.source,
  450. zIndex: 10,
  451. layerName: 'qy'
  452. })
  453. state.map.addLayer(state.qyParams.layer)
  454. // 详情
  455. state.qyParams.overlay = new ol.Overlay({
  456. id: v4(),
  457. element: ref_qyDom.value,
  458. autoPan: false,
  459. offset: [0, -60],
  460. positioning: 'bottom-center',
  461. stopEvent: true,
  462. autoPanAnimation: {
  463. duration: 250
  464. }
  465. })
  466. state.map.addOverlay(state.qyParams.overlay)
  467. // 详情
  468. state.qyParams.sbOverlay = new ol.Overlay({
  469. id: v4(),
  470. element: ref_sbDom.value,
  471. autoPan: false,
  472. offset: [0, -60],
  473. positioning: 'bottom-center',
  474. stopEvent: true,
  475. autoPanAnimation: {
  476. duration: 250
  477. }
  478. })
  479. state.map.addOverlay(state.qyParams.sbOverlay)
  480. }
  481. })
  482. }
  483. const onCloseQy = () => {
  484. state.qyParams.overlay?.setPosition(undefined)
  485. state.qyParams.tempFeature?.get('resetStyle')()
  486. state.qyParams.tempFeature = null
  487. state.qyParams.qyInfo = {}
  488. state.qyParams.analysisSource?.clear()
  489. store.dispatch('gis/LOAD_ACTIVE_QY_ID', null)
  490. }
  491. const onCloseSb = () => {
  492. state.qyParams.sbOverlay?.setPosition(undefined)
  493. state.qyParams.tempSbFeature?.get('resetStyle')()
  494. state.qyParams.tempSbFeature = null
  495. }
  496. const handleRangeBlur = () => {
  497. if (!state.qyParams.qyInfo['5'].radius) {
  498. state.qyParams.qyInfo['5'].radius = 10
  499. }
  500. // setCircle()
  501. }
  502. const setCircle = () => {
  503. const transformProjection = (arr, EPSG, EPSG2) => {
  504. try {
  505. if (EPSG2 && EPSG) {
  506. if (arr && arr.length === 4) {
  507. return proj.transformExtent(arr, EPSG, EPSG2);
  508. } else {
  509. return proj.transform(arr, EPSG, EPSG2);
  510. }
  511. }
  512. return undefined;
  513. } catch (e) {
  514. console.error(e);
  515. }
  516. }
  517. // @ts-ignore
  518. state.qyParams.analysisCircle.getGeometry().setRadius(transformProjection([state.qyParams.qyInfo['5'].radius * 1000, 0], 'EPSG:3857', 'EPSG:4326')[0] - transformProjection([0, 0], 'EPSG:3857', 'EPSG:4326')[0],'XY')
  519. state.qyParams.analysisCircle.getGeometry().setCenter(state.qyParams.qyInfo.coordinates)
  520. }
  521. const onRadiusSubmit = () => {
  522. if (!state.qyParams.analysisLayer) {
  523. state.qyParams.analysisSource = new source.Vector()
  524. state.qyParams.analysisLayer = new layer.Vector({
  525. zIndex: 9,
  526. source: state.qyParams.analysisSource,
  527. style: [
  528. new style.Style({
  529. stroke: new style.Stroke({
  530. color: '#2860F1',
  531. width: 2,
  532. lineDash: [10, 10]
  533. }),
  534. fill: new style.Fill({
  535. color: 'rgba(20, 129, 241, 0.3)',
  536. }),
  537. })
  538. ]
  539. });
  540. state.qyParams.analysisCircle = new ol.Feature({
  541. geometry: new geom.Circle(state.qyParams.qyInfo.coordinates, 0),
  542. })
  543. state.qyParams.analysisSource.addFeature(state.qyParams.analysisCircle)
  544. setCircle()
  545. state.map.addLayer(state.qyParams.analysisLayer)
  546. }
  547. state.qyParams.analysisSource.clear()
  548. state.qyParams.analysisSource.addFeature(state.qyParams.analysisCircle)
  549. setCircle()
  550. state.qyParams.qyInfo['5'].tableData = []
  551. state.qyParams.qyInfo['5'].loading = true
  552. that.$api.deviceQuery({
  553. lon: state.qyParams.qyInfo.coordinates[0],
  554. lat: state.qyParams.qyInfo.coordinates[1],
  555. radius: state.qyParams.qyInfo['5'].radius
  556. }).then((res: any) => {
  557. // console.log(res)
  558. // for (let i = 0; i < 500; i++) {
  559. // state.qyParams.qyInfo['5'].tableData.push({
  560. // name: '505县道新安村路口1-枪机-0110580_' + i,
  561. // code: '46044123124125125',
  562. // status: i % 3 === 0 ? '1' : '0',
  563. // typeName: '公安类',
  564. // type: i % 3 === 0 ? 'galsb' : (i % 3 === 1 ? 'shlsb' : 'mylsb'),
  565. // wkt: `POINT(${that.$util.randomNum(108.738329, 110.912130, 6)} ${that.$util.randomNum(18.154784, 20, 6)})`
  566. // })
  567. // }
  568. if (res.resp_code === 0 && res.datas?.length > 0) {
  569. console.log(res.datas?.length)
  570. const features: any = []
  571. res.datas?.forEach(v => {
  572. try {
  573. const feat: any = new format.WKT().readFeature(`POINT(${v.longitude} ${v.latitude})`)
  574. let type = ''
  575. if (v.type = '公安类') {
  576. type = 'galsb'
  577. } else if (v.type = '社会类') {
  578. type = 'shlsb'
  579. } else if (v.type = '民用类') {
  580. type = 'mylsb'
  581. }
  582. feat.set('defaultStyle', SbStyle.sbStyle(type))
  583. feat.set('activeStyle', [...CommonStyle.activeStyle(), ...SbStyle.sbStyle(type)])
  584. feat.set('analysisStyle', [...CommonStyle.analysisStyle(), ...SbStyle.sbStyle(type)])
  585. feat.set('analysisActiveStyle', [...CommonStyle.activeStyle(), ...CommonStyle.analysisStyle(), ...SbStyle.sbStyle(type)])
  586. feat.set('isAnalysis', true)
  587. feat.set('resetStyle', () => {
  588. if (feat.get('isAnalysis')) {
  589. feat.setStyle(feat.get('analysisStyle'))
  590. } else {
  591. feat.setStyle(feat.get('defaultStyle'))
  592. }
  593. })
  594. feat.setStyle(feat.get('analysisStyle'))
  595. feat.set('featureType', 'sb')
  596. feat.set('info', v)
  597. feat.setId(v.deviceid)
  598. features.push(feat)
  599. state.qyParams.qyInfo['5'].tableData.push(v)
  600. } catch (e) {
  601. console.error('异常设备', v)
  602. }
  603. })
  604. state.qyParams.analysisSource.addFeatures(features)
  605. }
  606. state.qyParams.qyInfo['5'].loading = false
  607. }).catch(() => {
  608. state.qyParams.qyInfo['5'].loading = true
  609. })
  610. }
  611. const onRadiusReset = () => {
  612. state.qyParams.qyInfo['5'].radius = 10
  613. onRadiusSubmit()
  614. }
  615. const mockSB1 = () => {
  616. store.dispatch('gis/LOAD_GIS_PARAMS_DEFAULT', {
  617. wkt: 'POINT(110.73667031084902 19.011386491828695)',
  618. info: {
  619. "pn": "2526",
  620. "groupid": "89800003866",
  621. "monitorid": "18678",
  622. "name": "测试通道2526",
  623. "online": "0",
  624. "savestatus": "1",
  625. "ptztype": "3",
  626. "deviceid": "46012003001310000011 aaa",
  627. "areacode": "4.60E+19",
  628. "longitude": "110.455893",
  629. "latitude": "19.89477",
  630. "faultedflag": "0",
  631. "status": "OFF",
  632. "isfront": "363658824",
  633. "type": "公安类",
  634. "civilcode": "46012003"
  635. }
  636. })
  637. }
  638. onMounted(() => {
  639. if (!sessionStorage.getItem('auth')) {
  640. if(navigator.userAgent.indexOf("Firefox") != -1 || navigator.userAgent.indexOf("Chrome") != -1){
  641. window.location.href = "about:blank";
  642. window.close();
  643. }else{
  644. window.opener = null;
  645. window.open("", "_self");
  646. window.close();
  647. }
  648. }
  649. })
  650. return {
  651. ...toRefs(state),
  652. mapLoad,
  653. ToolsMapper,
  654. ref_search,
  655. menuCpt,
  656. searchHandleMapSearch,
  657. searchToMapLocation,
  658. toolsHandleClick,
  659. ref_qyDom,
  660. onCloseQy,
  661. onCloseSb,
  662. handleRangeBlur,
  663. setCircle,
  664. onRadiusSubmit,
  665. onRadiusReset,
  666. ref_sbDom,
  667. ref_gisDefault,
  668. mockSB1
  669. }
  670. },
  671. })
  672. </script>
  673. <style scoped lang="scss">
  674. .gis-layout {
  675. width: 100%;
  676. height: 100vh;
  677. position: absolute;
  678. top: 0;
  679. left: 0;
  680. z-index: 2;
  681. display: flex;
  682. align-items: center;
  683. justify-content: center;
  684. .map {
  685. z-index: 1;
  686. }
  687. .gis-menu {
  688. $diff: 14px;
  689. $menuBottom: 16px;
  690. position: absolute;
  691. z-index: 2;
  692. bottom: $menuBottom;
  693. display: flex;
  694. justify-content: center;
  695. align-items: center;
  696. &:before, &:after {
  697. content: '';
  698. background-image: url("@/assets/images/gis-layout/gis-layout-menu_side.png");
  699. width: 246px;
  700. height: 39px;
  701. position: absolute;
  702. bottom: -$menuBottom;
  703. }
  704. &:before {
  705. left: calc((#{$diff} + 246px) * -1);
  706. }
  707. &:after {
  708. right: calc((#{$diff} + 246px) * -1);
  709. transform: rotateY(180deg);
  710. }
  711. .gis-menu-bottom {
  712. position: absolute;
  713. height: 9px;
  714. width: calc(100% + 10px + #{$diff} * 2);
  715. bottom: -$menuBottom;
  716. }
  717. .gis-menu-item {
  718. width: 95px;
  719. height: 36px;
  720. background-image: url("@/assets/images/gis-layout/gis-layout-menu_item-min.png");
  721. background-size: 100% 100%;
  722. background-repeat: no-repeat;
  723. display: flex;
  724. align-items: center;
  725. justify-content: center;
  726. font-size: 18px;
  727. font-family: YouSheBiaoTiHei;
  728. font-weight: 400;
  729. color: #60AEFF;
  730. &:not(&:last-child) {
  731. margin-right: 4px;
  732. }
  733. &.active {
  734. color: #FFFFFF;
  735. background-image: url("@/assets/images/gis-layout/gis-layout-menu_item-min_active.png");
  736. }
  737. &.max {
  738. width: 125px;
  739. background-image: url("@/assets/images/gis-layout/gis-layout-menu_item-max.png");
  740. &.active {
  741. background-image: url("@/assets/images/gis-layout/gis-layout-menu_item-max_active.png");
  742. }
  743. }
  744. }
  745. }
  746. .gis-tools {
  747. position: absolute;
  748. z-index: 3;
  749. top: 10px;
  750. right: 10px;
  751. width: 404px;
  752. >div {
  753. box-sizing: border-box;
  754. }
  755. .gt-search {
  756. height: 38px;
  757. width: 100%;
  758. display: flex;
  759. align-items: center;
  760. .search-icon {
  761. width: 54px;
  762. height: 100%;
  763. background-color: #3e8ef7;
  764. display: flex;
  765. align-items: center;
  766. justify-content: center;
  767. }
  768. :deep(.el-autocomplete) {
  769. flex: 1;
  770. height: 100%;
  771. .el-input {
  772. width: 100%;
  773. height: 100%;
  774. .el-input__wrapper {
  775. border-radius: 0;
  776. }
  777. }
  778. }
  779. :deep(.el-autocomplete__popper) {
  780. opacity: 0.9;
  781. background-image: linear-gradient(45deg,transparent 10px,#101D69 10px), linear-gradient(-135deg,transparent 10px,#101D69 10px);
  782. border: none;
  783. .el-popper__arrow::before {
  784. background: #101D69;
  785. border-color: #101D69;
  786. }
  787. .el-autocomplete-suggestion {
  788. .el-scrollbar {
  789. .el-autocomplete-suggestion__wrap {
  790. .el-scrollbar__view {
  791. >li {
  792. &:hover {
  793. background-color: rgba(59,84,172,0.5);
  794. }
  795. .search-item {
  796. display: flex;
  797. align-items: center;
  798. justify-content: flex-start;
  799. >img {
  800. margin-right: 6px;
  801. width: 16px;
  802. height: 16px;
  803. }
  804. .name {
  805. color: #ffffff;
  806. }
  807. }
  808. }
  809. }
  810. }
  811. }
  812. }
  813. }
  814. }
  815. .gt-tools {
  816. width: 100%;
  817. height: 38px;
  818. margin-top: 2px;
  819. background-color: #FFFFFF;
  820. border: 1px solid #C7CFDE;
  821. display: flex;
  822. align-items: center;
  823. justify-content: space-around;
  824. padding: 0 8px;
  825. .tools-item {
  826. font-size: 14px;
  827. font-family: Microsoft YaHei;
  828. font-weight: 400;
  829. color: #AEAEAE;
  830. &.active {
  831. color: #1174DB;
  832. font-weight: bold;
  833. }
  834. &.disabled {
  835. opacity: 0.5;
  836. cursor: not-allowed;
  837. }
  838. }
  839. .tools-line {
  840. width: 1px;
  841. height: 16px;
  842. background: linear-gradient(0deg, rgba(174,174,174,0) 0%, rgba(213,213,213,0.99) 50%, rgba(174,174,174,0) 100%);
  843. }
  844. }
  845. .gt-tools-component {
  846. width: 100%;
  847. margin-top: 2px;
  848. }
  849. }
  850. .gis-content {
  851. z-index: 4;
  852. }
  853. .qy-info {
  854. $bgColor: #2860CE;
  855. $footH: 10px;
  856. min-width: 315px;
  857. min-height: 168px - $footH;
  858. background-color: $bgColor;
  859. position: relative;
  860. display: flex;
  861. justify-content: center;
  862. &:after {
  863. content: '';
  864. position: absolute;
  865. bottom: -$footH;
  866. border-top: $footH solid $bgColor;
  867. border-left: $footH solid transparent;
  868. border-right: $footH solid transparent;
  869. }
  870. .qy-bg-icon {
  871. width: 16px;
  872. height: 16px;
  873. background-image: url("@/views/gis/map-info/qy-dom-icon1.png");
  874. background-repeat: no-repeat;
  875. background-size: 100% 100%;
  876. position: absolute;
  877. z-index: 1;
  878. &.qy-bg-icon-1 {
  879. left: 0;
  880. top: 0;
  881. }
  882. &.qy-bg-icon-2 {
  883. right: 0;
  884. top: 0;
  885. transform: rotate(90deg);
  886. }
  887. &.qy-bg-icon-3 {
  888. right: 0;
  889. bottom: 0;
  890. transform: rotate(180deg);
  891. }
  892. &.qy-bg-icon-4 {
  893. left: 0;
  894. bottom: 0;
  895. transform: rotate(270deg);
  896. }
  897. }
  898. .qy-main {
  899. width: 100%;
  900. height: auto;
  901. z-index: 2;
  902. padding: 12px;
  903. .qy-main-head {
  904. display: flex;
  905. align-items: center;
  906. position: relative;
  907. &:after {
  908. content: '';
  909. position: absolute;
  910. width: 100%;
  911. height: 4px;
  912. bottom: -9px;
  913. background-image: url("@/views/gis/map-info/qy-info-icon2.png");
  914. background-repeat: no-repeat;
  915. }
  916. .qy-main-head-tips {
  917. font-size: 10px;
  918. font-family: Microsoft YaHei;
  919. font-weight: bold;
  920. color: #8FFFFF;
  921. }
  922. .qy-main-head-name {
  923. font-size: 14px;
  924. font-family: Microsoft YaHei;
  925. font-weight: bold;
  926. color: #FFFFFF;
  927. }
  928. .svg-icon {
  929. margin-left: auto;
  930. }
  931. }
  932. .qy-main-tab {
  933. margin: 20px 0 10px 0;
  934. display: flex;
  935. align-items: center;
  936. .qy-main-tab-item {
  937. font-size: 12px;
  938. font-family: Microsoft YaHei;
  939. font-weight: 400;
  940. color: #CEE6FF;
  941. height: 16px;
  942. display: flex;
  943. align-items: center;
  944. justify-content: center;
  945. padding: 0 4px;
  946. border-radius: 2px;
  947. background-color: rgba(255, 255, 255, 0.2);
  948. &.active {
  949. background-color: #1280F1;
  950. color: #61FFFF;
  951. }
  952. &.disabled {
  953. cursor: not-allowed;
  954. opacity: 0.7;
  955. }
  956. &:not(:last-child) {
  957. margin-right: 2px;
  958. }
  959. }
  960. }
  961. .qy-main-content-1 {
  962. font-size: 12px;
  963. font-family: Microsoft YaHei;
  964. font-weight: 400;
  965. color: #FFFFFF;
  966. >div {
  967. line-height: 18px;
  968. }
  969. }
  970. .qy-main-content-5 {
  971. .qy-main-content-5-radius {
  972. display: flex;
  973. align-items: center;
  974. font-size: 12px;
  975. font-family: Microsoft YaHei;
  976. font-weight: 400;
  977. color: #FFFFFF;
  978. .radius-min, .radius-max {
  979. margin: 0 3px;
  980. &:hover {
  981. color: #409EFF;
  982. }
  983. }
  984. :deep(.cus-form-column) {
  985. max-width: unset;
  986. width: 44px;
  987. flex: unset;
  988. .el-form-item {
  989. margin: 0;
  990. .el-form-item__content {
  991. height: 18px;
  992. .el-input {
  993. .el-input__wrapper {
  994. padding: 0;
  995. border-radius: 5px;
  996. background-color: transparent;
  997. .el-input__inner {
  998. color: #FFFFFF;
  999. background-color: transparent;
  1000. height: 100%;
  1001. text-align: center;
  1002. border: 1px solid #ffffff;
  1003. border-radius: 5px;
  1004. &::placeholder {
  1005. font-size: 12px;
  1006. font-family: Microsoft YaHei;
  1007. }
  1008. }
  1009. }
  1010. }
  1011. }
  1012. }
  1013. }
  1014. .submit {
  1015. display: flex;
  1016. align-items: center;
  1017. justify-content: center;
  1018. width: 36px;
  1019. height: 18px;
  1020. background: #8FFFFF;
  1021. border-radius: 4px;
  1022. font-size: 12px;
  1023. font-family: Microsoft YaHei;
  1024. font-weight: 400;
  1025. color: #1174DB;
  1026. margin-left: 10px;
  1027. }
  1028. .reset {
  1029. display: flex;
  1030. align-items: center;
  1031. justify-content: center;
  1032. width: 36px;
  1033. height: 18px;
  1034. border: 1px solid #8FFFFF;
  1035. border-radius: 4px;
  1036. font-size: 12px;
  1037. font-family: Microsoft YaHei;
  1038. font-weight: 400;
  1039. color: #8FFFFF;
  1040. margin-left: 8px;
  1041. }
  1042. }
  1043. .qy-main-content-5-table {
  1044. margin-top: 10px;
  1045. width: 440px;
  1046. :deep(.__gis-overlay_table-v2) {
  1047. .el-table-v2__header-cell{
  1048. justify-content: center;
  1049. &:nth-child(2) {
  1050. color: #00C6FC;
  1051. }
  1052. }
  1053. .el-table-v2__header-cell, .el-table-v2__row-cell {
  1054. width: 60px !important;
  1055. $w1: 60px;
  1056. $w3: 60px;
  1057. $w4: 60px;
  1058. $w5: 60px;
  1059. &:nth-child(1) {
  1060. width: $w1 !important;
  1061. }
  1062. &:nth-child(2) {
  1063. width: calc(100% - #{$w1} - #{$w3} - #{$w4} - #{$w5} - 6px) !important;
  1064. }
  1065. &:nth-child(3) {
  1066. width: $w3 !important;
  1067. }
  1068. &:nth-child(4) {
  1069. width: $w4 !important;
  1070. }
  1071. &:nth-child(5) {
  1072. width: $w5 !important;
  1073. }
  1074. }
  1075. }
  1076. }
  1077. }
  1078. }
  1079. }
  1080. .sb-info {
  1081. $footH: 10px;
  1082. width: 220px;
  1083. background: linear-gradient(180deg, #3874C9 0%, #0043C4 100%);
  1084. border-radius: 0px 4px 4px 4px;
  1085. position: relative;
  1086. display: flex;
  1087. justify-content: center;
  1088. &:after {
  1089. content: '';
  1090. position: absolute;
  1091. bottom: -$footH;
  1092. border-top: $footH solid #0043C4;
  1093. border-left: $footH solid transparent;
  1094. border-right: $footH solid transparent;
  1095. }
  1096. .sb-info-head {
  1097. min-width: 68px;
  1098. height: 18px;
  1099. position: absolute;
  1100. top: -18px;
  1101. left: 0;
  1102. font-size: 12px;
  1103. font-family: PingFang SC;
  1104. font-weight: 500;
  1105. color: #FFFFFF;
  1106. display: flex;
  1107. align-items: center;
  1108. line-height: 8px;
  1109. &:before {
  1110. z-index: -1;
  1111. content: '';
  1112. position: absolute;
  1113. width: 100%;
  1114. height: 100%;
  1115. background: linear-gradient(180deg, #3874C9 0%, #0043C4 100%);
  1116. border-radius: 2px 2px 0 0;/* 设置圆角 */
  1117. transform: perspective(20px)rotateX(4deg);
  1118. /* 镜头距离元素表面的位置为8px,x轴为1.1倍y轴为1.3倍,绕x轴旋转5度 */
  1119. transform-origin: bottom left;
  1120. /* bottom left = left bottom = 0 100% 中心点偏移量*/
  1121. }
  1122. .svg-icon {
  1123. margin: 0 4px 0 6px;
  1124. }
  1125. }
  1126. .sb-info-close {
  1127. position: absolute;
  1128. right: 0;
  1129. top: -16px;
  1130. }
  1131. .sb-main {
  1132. width: 100%;
  1133. height: auto;
  1134. padding: 10px;
  1135. .sb-item {
  1136. display: flex;
  1137. .sb-item-label {
  1138. width: 42px;
  1139. font-size: 14px;
  1140. font-family: PingFang SC;
  1141. font-weight: 600;
  1142. color: #08FFFF;
  1143. line-height: 20px;
  1144. }
  1145. .sb-item-value {
  1146. flex: 1;
  1147. font-size: 14px;
  1148. font-family: PingFang SC;
  1149. font-weight: 400;
  1150. color: #FFFFFF;
  1151. line-height: 20px;
  1152. }
  1153. }
  1154. .play-button {
  1155. width: 76px;
  1156. height: 24px;
  1157. background: #1280F1;
  1158. border-radius: 2px;
  1159. border: 1px solid #4BA0FF;
  1160. display: flex;
  1161. align-items: center;
  1162. justify-content: center;
  1163. font-size: 14px;
  1164. font-family: PingFang SC;
  1165. font-weight: 400;
  1166. color: #FFFFFF;
  1167. margin-left: calc((100% - 76px) / 2);
  1168. margin-top: 10px;
  1169. }
  1170. }
  1171. }
  1172. }
  1173. .mockButtons {
  1174. position: absolute;
  1175. top: 0;
  1176. left: 500px;
  1177. }
  1178. </style>