PanelSjwg.vue 13 KB

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