PanelSjwg.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. <template>
  2. <div class="panel-sjwg flex flex-col aside-left-inner">
  3. <div class="title-main shrink-0">数据网格可视化</div>
  4. <div class="title-sub my-4 shrink-0">
  5. 底板数据
  6. <i @click="toggleContentShow('b1')" class="drop-down" :class="{ 'reverse': contentShow.b1 }"></i>
  7. </div>
  8. <Transition>
  9. <ul class="list-base flex flex-wrap shrink-0" v-if="contentShow.b1" v-collapse>
  10. <li v-for="item in basicList" class="text-center w-1/4 mb-4 cursor-pointer" @click="handleBaseClick(item)">
  11. <img :src="getAssetsFile(`page/${item.active ? item.icon + '-h' : item.icon}.png`)" alt="" class="ml-2 mb-1">
  12. <span class="block text-base ml-2">{{ item.label }}</span>
  13. </li>
  14. </ul>
  15. </Transition>
  16. <div class="title-sub mb-4 shrink-0">
  17. 低空要素数据
  18. <i @click="toggleContentShow('b2')" class="drop-down" :class="{ 'reverse': contentShow.b2 }"></i>
  19. </div>
  20. <Transition>
  21. <div v-if="contentShow.b2" v-collapse="'scroll'" class="flex-1 pr-1">
  22. <div class="title-shade">
  23. <span @click="handleCheckShiFei">空域</span>
  24. <i class="btn-selectall" :class="{ 'active': checkAll.area }" @click="handleCheckAll('area')"></i>
  25. <i class="drop-down" @click="toggleContentShow('b2r1')" :class="{ 'reverse': contentShow.b2r1 }"></i>
  26. </div>
  27. <Transition>
  28. <ul v-if="contentShow.b2r1" v-collapse>
  29. <li v-for="item in areaList" class="list-item">
  30. <img src="../../../assets/images/page/icon-location.png" alt="">
  31. <span>{{ item.name }}</span>
  32. <i title="查看" @click="handleCheck(item, 'area')" :class="{ 'active': item.check }"></i>
  33. <i title="网格" @click="handleMesh(item, 'area')" :class="{ 'active': item.mesh }"></i>
  34. <i title="删除" @click="handleDelete(item, 'area')"></i>
  35. </li>
  36. </ul>
  37. </Transition>
  38. <div class="title-shade">
  39. <span>航线</span>
  40. <i class="btn-selectall" :class="{ 'active': checkAll.route }" @click="handleCheckAll('route')"></i>
  41. <i class="drop-down" @click="toggleContentShow('b2r2')" :class="{ 'reverse': contentShow.b2r2 }"></i>
  42. </div>
  43. <Transition>
  44. <ul v-if="contentShow.b2r2" v-collapse>
  45. <li v-for="item in routeList" class="list-item">
  46. <img src="../../../assets/images/page/icon-route.png" alt="">
  47. <span>{{ item.name }}</span>
  48. <i title="查看" @click="handleCheck(item, 'route')" :class="{ 'active': item.check }"></i>
  49. <i title="网格" @click="handleMesh(item, 'route')" :class="{ 'active': item.mesh }"></i>
  50. <i title="删除" @click="handleDelete(item, 'route')"></i>
  51. </li>
  52. </ul>
  53. </Transition>
  54. <div class="title-shade">
  55. <span>起降场</span>
  56. <i class="btn-selectall" :class="{ 'active': checkAll.plot }" @click="handleCheckAll('plot')"></i>
  57. <i class="drop-down" @click="toggleContentShow('b2r3')" :class="{ 'reverse': contentShow.b2r3 }"></i>
  58. </div>
  59. <Transition>
  60. <ul v-if="contentShow.b2r3" v-collapse>
  61. <li v-for="item in plotList" class="list-item">
  62. <img src="../../../assets/images/page/icon-plot.png" alt="">
  63. <span>{{ item.name }}</span>
  64. <i title="查看" @click="handleCheck(item, 'plot')" :class="{ 'active': item.check }"></i>
  65. <i title="网格" @click="handleMesh(item, 'plot')" :class="{ 'active': item.mesh }"></i>
  66. <i title="删除" @click="handleDelete(item, 'plot')"></i>
  67. </li>
  68. </ul>
  69. </Transition>
  70. </div>
  71. </Transition>
  72. </div>
  73. </template>
  74. <script setup>
  75. import { getAssetsFile, getData } from '@/utils/require';
  76. import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue';
  77. import { showShapes, heatMap, AddSingleLayer, InspectCube } from '@/utils/map/addLayer'
  78. import { DeleteArea, DeletePlot, DeleteRoute, GetAreaList, GetPlotList, GetRouteList } from '@/service/http'
  79. import { airSpaceTypes } from '@/utils/options';
  80. import { hexToRgb } from '@/utils/tools';
  81. import usePanelStore from '@/store/panel';
  82. import shifei from '@/data/shifei.json'
  83. const panelStore = usePanelStore()
  84. let shifeiStatus = false;
  85. onBeforeMount(() => {
  86. getLists()
  87. })
  88. function showWarning(message) {
  89. ElMessage({ offset: 90, type: 'warning', message })
  90. }
  91. function getLists() {
  92. Promise.all([
  93. GetAreaList().then(res => {
  94. if (!Array.isArray(res.data.data)) {
  95. showWarning('空域列表查询失败')
  96. return
  97. }
  98. areaList.value = res.data.data.map(row => ({
  99. ...row,
  100. check: false,
  101. mesh: false
  102. }))
  103. contentShow.b2r1 = true
  104. }).catch(() => {
  105. showWarning('空域列表查询失败')
  106. }),
  107. GetPlotList().then(res => {
  108. if (!Array.isArray(res.data.data)) {
  109. showWarning('起降场列表查询失败')
  110. return
  111. }
  112. plotList.value = res.data.data.map(row => ({
  113. ...row,
  114. check: false,
  115. mesh: false
  116. }))
  117. }).catch(() => {
  118. showWarning('起降场列表查询失败')
  119. }),
  120. GetRouteList().then(res => {
  121. if (!Array.isArray(res.data.data)) {
  122. showWarning('航线列表查询失败')
  123. return
  124. }
  125. routeList.value = res.data.data.map(row => ({
  126. ...row,
  127. check: false,
  128. mesh: false
  129. }))
  130. }).catch(() => {
  131. showWarning('航线列表查询失败')
  132. })
  133. ]).finally(() => {
  134. // 恢复选中状态
  135. handleRestoreChecked()
  136. })
  137. }
  138. const basicList = ref([
  139. { label: '白模', alias: '全市白模', icon: 'icon-layer-bm', active: false },
  140. { label: '精模', alias: '五角场精模', icon: 'icon-layer-jm', active: false },
  141. { label: '道路', alias: ['快速路', '高速公路', '地面道路'], icon: 'icon-layer-dl', active: false },
  142. { label: '河流', alias: '全市河流', icon: 'icon-layer-hl', active: false },
  143. { label: '人口', alias: '人口', icon: 'icon-layer-rk', active: false },
  144. ])
  145. async function handleBaseClick(layer) {
  146. layer.active = !layer.active
  147. const aliasArr = Array.isArray(layer.alias) ? layer.alias : [layer.alias]
  148. const resources = await getData('resources.json')
  149. const targetServices = resources.filter(r => aliasArr.some(i => i === r.title))
  150. if (targetServices.length === 0) return
  151. targetServices.forEach(service => {
  152. if (service.type == "feature") {
  153. heatMap({
  154. ...service,
  155. visible: layer.active,
  156. })
  157. } else {
  158. AddSingleLayer({
  159. ...service,
  160. visible: layer.active,
  161. opacity: 1
  162. })
  163. }
  164. })
  165. }
  166. const areaList = ref([])
  167. function handleCheck(item, type) {
  168. // console.log(item)
  169. item.check = !item.check
  170. let color
  171. switch (type) {
  172. case 'area':
  173. color = hexToRgb(airSpaceTypes.find(i => i.value === item.spaceType).color, 0.5)
  174. break;
  175. case 'plot':
  176. color = [0, 0, 255, 0.5]
  177. break;
  178. case 'route':
  179. color = [255, 255, 0, 0.5]
  180. }
  181. const data = [{
  182. type: type === 'route' ? item.type : item.geoType,
  183. shape: {
  184. ...JSON.parse(item.shape),
  185. color
  186. }
  187. }]
  188. showShapes({
  189. id: item.id,
  190. data: item.check ? data : null
  191. })
  192. }
  193. function handleCheckShiFei(){
  194. shifeiStatus = !shifeiStatus;
  195. if(shifeiStatus){
  196. shifei.forEach((item) =>{
  197. showShapes({
  198. id:item.attributes.FID,
  199. data:[
  200. {
  201. type: 'polygon',
  202. shape:{
  203. color:[0,255,0,0.5],
  204. height:120,
  205. rings:item.geometry.rings
  206. }
  207. }
  208. ]
  209. })
  210. })
  211. }else{
  212. shifei.forEach((item) =>{
  213. showShapes({
  214. id:item.attributes.FID
  215. })
  216. })
  217. }
  218. }
  219. function handleDelete(item, type) {
  220. switch (type) {
  221. case 'area':
  222. DeleteArea(item.id).then(res => {
  223. if (res.data.code === 200) {
  224. ElMessage.success('删除成功')
  225. getLists()
  226. }
  227. })
  228. break;
  229. case 'plot':
  230. DeletePlot(item.id).then(res => {
  231. if (res.data.code === 200) {
  232. ElMessage.success('删除成功')
  233. getLists()
  234. }
  235. })
  236. break;
  237. case 'route':
  238. DeleteRoute(item.id).then(res => {
  239. if (res.data.code === 200) {
  240. ElMessage.success('删除成功')
  241. getLists()
  242. }
  243. })
  244. }
  245. }
  246. function handleMesh(item) {
  247. item.mesh = !item.mesh
  248. InspectCube({
  249. id: item.id,
  250. show: item.mesh,
  251. type: item.geoType || item.type,
  252. shape: JSON.parse(item.shape)
  253. })
  254. }
  255. const routeList = ref([])
  256. const plotList = ref([])
  257. const contentShow = reactive({
  258. b1: true,
  259. b2: true,
  260. b2r1: false,
  261. b2r2: false,
  262. b2r3: false,
  263. })
  264. function toggleContentShow(id) {
  265. contentShow[id] = !contentShow[id]
  266. const subs = ['b2r1', 'b2r2', 'b2r3']
  267. subs.forEach(i => {
  268. if (contentShow[id] && subs.includes(id) && id !== i) {
  269. contentShow[i] = false
  270. }
  271. })
  272. }
  273. function clearAllFeatures() {
  274. [...areaList.value, ...plotList.value, ...routeList.value].forEach(row => {
  275. if (row.check) {
  276. showShapes({
  277. id: row.id,
  278. data: null
  279. })
  280. }
  281. if (row.mesh) {
  282. InspectCube({
  283. id: row.id,
  284. show: false,
  285. type: row.geoType || row.type,
  286. })
  287. }
  288. })
  289. }
  290. const checkAll = reactive({
  291. area: false,
  292. route: false,
  293. plot: false
  294. })
  295. function handleCheckAll(type) {
  296. checkAll[type] = !checkAll[type]
  297. const targetList = type === 'area' ? areaList.value : type === 'route' ? routeList.value : plotList.value
  298. targetList.forEach(row => {
  299. if (checkAll[type] && !row.check) {
  300. handleCheck(row, type)
  301. } else if (!checkAll[type] && row.check) {
  302. handleCheck(row, type)
  303. }
  304. })
  305. }
  306. function handleStoreChecked() {
  307. panelStore.setSjwg({
  308. basicList: basicList.value.map(({ label, active }) => ({ label, active })),
  309. checkAll,
  310. areaList: areaList.value.map(({ id, check, mesh }) => ({ id, check, mesh })),
  311. routeList: routeList.value.map(({ id, check, mesh }) => ({ id, check, mesh })),
  312. plotList: plotList.value.map(({ id, check, mesh }) => ({ id, check, mesh })),
  313. })
  314. }
  315. function handleRestoreChecked() {
  316. if (!Object.keys(panelStore.sjwg).length) return
  317. const temp = panelStore.sjwg
  318. basicList.value.forEach(i => {
  319. i.active = temp.basicList.find(t => t.label === i.label).active
  320. })
  321. Object.keys(checkAll).forEach(k => {
  322. checkAll[k] = temp.checkAll[k]
  323. })
  324. areaList.value.forEach(i => {
  325. const target = temp.areaList.find(t => t.id === i.id)
  326. if (target) {
  327. i.check = target.check
  328. i.mesh = target.mesh
  329. }
  330. })
  331. routeList.value.forEach(i => {
  332. const target = temp.routeList.find(t => t.id === i.id)
  333. if (target) {
  334. i.check = target.check
  335. i.mesh = target.mesh
  336. }
  337. })
  338. plotList.value.forEach(i => {
  339. const target = temp.plotList.find(t => t.id === i.id)
  340. if (target) {
  341. i.check = target.check
  342. i.mesh = target.mesh
  343. }
  344. })
  345. }
  346. onBeforeUnmount(() => {
  347. // clearAllFeatures()
  348. handleStoreChecked()
  349. })
  350. const vCollapse = {
  351. beforeMount(el, binding) {
  352. el.style.height = '0';
  353. el.style.overflow = binding.value === 'scroll' ? 'auto' : 'hidden';
  354. el.style.transition = 'height 0.5s ease';
  355. },
  356. mounted(el) {
  357. const naturalHeight = el.scrollHeight;
  358. el.style.height = `${naturalHeight}px`;
  359. },
  360. beforeUnmount(el) {
  361. el.style.height = '0';
  362. }
  363. }
  364. </script>
  365. <style lang="scss" scoped>
  366. .list-base {
  367. li {
  368. span {
  369. width: 75px;
  370. }
  371. img {
  372. width: 75px;
  373. height: 77px;
  374. }
  375. }
  376. }
  377. .drop-down {
  378. display: block;
  379. width: 20px;
  380. height: 20px;
  381. margin-left: 10px;
  382. background: url('../../../assets/images/buttons/btn-dropdown.png') no-repeat;
  383. background-size: 10px 7px;
  384. background-position: center;
  385. transition: transform 0.3s ease;
  386. cursor: pointer;
  387. &.reverse {
  388. transform: rotateZ(180deg);
  389. }
  390. }
  391. .title-shade {
  392. position: relative;
  393. height: 32px;
  394. background: linear-gradient(to right, rgba(101, 131, 190, 1) 0%, rgba(101, 131, 190, 0) 100%);
  395. display: flex;
  396. align-items: center;
  397. padding: 0 16px;
  398. margin-bottom: 10px;
  399. &::before {
  400. content: '';
  401. position: absolute;
  402. left: 0;
  403. top: 0;
  404. width: 4px;
  405. height: 32px;
  406. background: #C0D5FF;
  407. }
  408. span {
  409. text-shadow: 0px 4px 4px rgba(21, 41, 91, 0.45);
  410. margin-right: 10px;
  411. }
  412. .btn-selectall {
  413. display: block;
  414. width: 26px;
  415. height: 26px;
  416. background: url('../../../assets/images/buttons/btn-selectall.png');
  417. background-size: 100%;
  418. background-position: center 1px;
  419. cursor: pointer;
  420. filter: opacity(0.6);
  421. &.active {
  422. filter: opacity(1) drop-shadow(0 0 5px #379bff);
  423. transform: scale(1.1);
  424. }
  425. }
  426. .drop-down {
  427. margin-left: auto;
  428. }
  429. }
  430. .list-item {
  431. position: relative;
  432. height: 40px;
  433. padding: 0 15px;
  434. margin: 9px 0;
  435. display: flex;
  436. align-items: center;
  437. background: #4352704d;
  438. &::before {
  439. content: '';
  440. position: absolute;
  441. left: 0;
  442. top: 0;
  443. width: 1px;
  444. height: 40px;
  445. background: #808DC9;
  446. }
  447. &:last-child {
  448. margin-bottom: 15px;
  449. }
  450. img {
  451. width: 24px;
  452. height: 24px;
  453. margin-right: 10px;
  454. }
  455. span {
  456. flex: 1;
  457. }
  458. i {
  459. display: block;
  460. width: 34px;
  461. height: 34px;
  462. margin-left: 5px;
  463. cursor: pointer;
  464. filter: grayscale(1);
  465. &.active {
  466. filter: grayscale(0);
  467. }
  468. }
  469. i:nth-child(3) {
  470. background: url('../../../assets/images/buttons/btn-check.png');
  471. background-size: cover;
  472. }
  473. i:nth-child(4) {
  474. background: url('../../../assets/images/buttons/btn-mesh.png');
  475. background-size: cover;
  476. }
  477. i:nth-child(5) {
  478. background: url('../../../assets/images/buttons/btn-delete.png');
  479. background-size: 40px 40px;
  480. background-position: center;
  481. }
  482. }
  483. </style>