FloatPanelKysg.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. <template>
  2. <div class="slider-container" v-if="!layoutStore.footerCollapse">
  3. <el-form v-model="form" inline class="top-form">
  4. <el-form-item label="类型:" prop="meshType">
  5. <el-radio-group v-model="form.meshType">
  6. <el-radio label="二维" value="二维" />
  7. <el-radio label="三维" value="三维" />
  8. </el-radio-group>
  9. </el-form-item>
  10. <el-form-item>
  11. <el-checkbox v-model="form.grade">评分</el-checkbox>
  12. </el-form-item>
  13. <el-form-item>
  14. <el-checkbox v-model="form.deepShow">深度显示</el-checkbox>
  15. </el-form-item>
  16. <el-form-item label="范围:" prop="meshRange">
  17. <el-radio-group v-model="form.meshRange">
  18. <el-radio label="自适应" value="自适应" @click="handleTest" />
  19. <el-radio label="自定义" value="自定义" />
  20. <el-radio label="行政区划" value="行政区划" />
  21. </el-radio-group>
  22. </el-form-item>
  23. <el-form-item v-if="form.meshRange === '自定义'">
  24. <el-button class="btn-secondary" @click="handleDraw">绘制</el-button>
  25. </el-form-item>
  26. <el-form-item v-if="form.meshRange === '行政区划'">
  27. <el-cascader
  28. v-model="form.district"
  29. @change="handleSelectDistrict"
  30. :options="districtList"
  31. :props="cascaderProps"
  32. placeholder="请选择行政区划"
  33. class="w-32" />
  34. </el-form-item>
  35. </el-form>
  36. <!-- 单点选择滑动条 -->
  37. <div class="slider-bg single-slider" :class="{ disabled: form.meshRange === '自适应' }">
  38. <div class="slider">
  39. <div
  40. v-for="(point, index) in DenseData"
  41. :key="index"
  42. class="marker"
  43. :class="{ 'key-point': form.meshLevel === point.level }"
  44. :style="{ left: index * (100 / (DenseData.length - 1)) + '%' }"
  45. @click="selectPoint(point)">
  46. <span class="label">{{ point.label }}</span>
  47. <span class="des">{{ point.des }}</span>
  48. </div>
  49. </div>
  50. </div>
  51. <!-- 范围选择滑动条 -->
  52. <el-slider
  53. class="slider-bg range-slider"
  54. :class="{ disabled: form.meshRange === '自适应' }"
  55. v-model="form.meshHeights"
  56. :min="0"
  57. :max="600"
  58. range
  59. :marks="marks"
  60. tooltip-class="range-tooltip"
  61. placement="bottom"
  62. :format-tooltip="(val) => val + 'm'" />
  63. </div>
  64. </template>
  65. <script setup>
  66. import { ref, onBeforeUnmount, watch, reactive, nextTick } from 'vue'
  67. import { addKysgScale, DrawPolygon, QueryDistrict, QueryCube, clearDraw } from '@/utils/map/addLayer.js'
  68. import useLayoutStore from '@/store/layout'
  69. import { useMapStore } from '@/store/map'
  70. import { GetDistrictTree } from '@/service/http'
  71. import { geometryMeshEffect } from '@/utils/map/addTool.js'
  72. const mapStore = useMapStore()
  73. const layoutStore = useLayoutStore()
  74. const form = ref({
  75. meshType: '三维',
  76. grade: true,
  77. deepShow: false,
  78. meshRange: '自适应',
  79. meshLevel: '',
  80. meshHeights: [0, 0],
  81. })
  82. const marks = reactive({
  83. 0: '0m',
  84. 100: '100m',
  85. 200: '200m',
  86. 300: '300m',
  87. 400: '400m',
  88. 500: '500m',
  89. 600: '600m',
  90. })
  91. let lastEvaluation
  92. let rings
  93. const districtList = ref([])
  94. function handleSelectDistrict(val) {
  95. QueryDistrict(val)
  96. }
  97. const cascaderProps = {
  98. expandTrigger: 'hover',
  99. checkStrictly: true,
  100. }
  101. watch(
  102. () => form.value.meshRange,
  103. (val) => {
  104. console.log(val)
  105. clearDraw()
  106. clearQueryCube()
  107. rings = null
  108. if (val === '自适应') {
  109. layoutStore.toggleGlobalLoading(true)
  110. setTimeout(() => {
  111. layoutStore.toggleGlobalLoading(false)
  112. }, 3000)
  113. addKysgScale(true)
  114. clearPoints()
  115. } else {
  116. addKysgScale(false)
  117. if (val === '行政区划' && !districtList.value.length) {
  118. GetDistrictTree()
  119. .then((res) => {
  120. if (!Array.isArray(res.data)) {
  121. ElMessage({ offset: 90, type: 'warning', message: '行政区划获取失败' })
  122. return
  123. }
  124. districtList.value = res.data
  125. })
  126. .catch(() => {
  127. ElMessage({ offset: 90, type: 'warning', message: '行政区划获取失败' })
  128. })
  129. }
  130. }
  131. },
  132. { immediate: true }
  133. )
  134. watch(
  135. form,
  136. (val) => {
  137. showGrid()
  138. },
  139. { deep: true }
  140. )
  141. function handleDraw() {
  142. clearQueryCube()
  143. getMesh('hide')
  144. DrawPolygon({ hasZ: true })
  145. }
  146. watch(
  147. () => mapStore.draw_geometry,
  148. (val) => {
  149. rings = val?.rings
  150. clearDraw()
  151. getMesh('show')
  152. showGrid()
  153. },
  154. { deep: true }
  155. )
  156. watch(
  157. () => mapStore.queryResult,
  158. (val) => {
  159. rings = val[0].geometry.rings
  160. showGrid()
  161. },
  162. { deep: true }
  163. )
  164. watch(
  165. () => mapStore.currentCubeLevel,
  166. (val) => {
  167. if (val) {
  168. form.value.meshLevel = val
  169. const maxHeight = Number(DenseData.find((d) => d.level == val).des.slice(1, -2))
  170. form.value.meshHeights = [0, Math.min(600, maxHeight)]
  171. }
  172. }
  173. )
  174. const DenseData = reactive([
  175. // { label: '1/8″网格', level: 24, size: { x: 3.3055614085396883, y: 3.8513467134689563, z: 3.3678982462941303 }, des: '(3.9m)'},
  176. { label: '1/4″网格', level: 23, size: { x: 6.7358, y: 7.7024, z: 6.6111 }, des: '(7m)' },
  177. {
  178. label: '1/2″网格',
  179. level: 22,
  180. size: { x: 13.222978864358083, y: 15.406742669116284, z: 13.471596544164798 },
  181. des: '(15m)',
  182. },
  183. {
  184. label: '1′网格',
  185. level: 21,
  186. size: { x: 26.44107169853396, y: 30.815525255577086, z: 26.943221541824816 },
  187. des: '(30.9m)',
  188. },
  189. {
  190. label: '2′网格',
  191. level: 20,
  192. size: { x: 52.887881893650956, y: 61.627412608112536, z: 53.887012166300096 },
  193. des: '(61m)',
  194. },
  195. {
  196. label: '4′网格',
  197. level: 19,
  198. size: { x: 105.79869494173579, y: 123.43930203184209, z: 107.77356906534683 },
  199. des: '(123.7m)',
  200. },
  201. {
  202. label: '8′网格',
  203. level: 18,
  204. size: { x: 211.66439868324142, y: 246.49964316944533, z: 215.5489592181181 },
  205. des: '(247.4m)',
  206. },
  207. {
  208. label: '16′网格',
  209. level: 17,
  210. size: { x: 424.0518521273334, y: 492.09123171765896, z: 431.1052029077298 },
  211. des: '(376.7m)',
  212. },
  213. {
  214. label: '32′网格',
  215. level: 16,
  216. size: { x: 844.8477050379952, y: 990.1345935064928, z: 862.239544684298 },
  217. des: '(746.4m)',
  218. },
  219. {
  220. label: '1′网格',
  221. level: 15,
  222. size: { x: 1582.2726823222256, y: 1856.6079667204758, z: 1724.5956527210265 },
  223. des: '(1850m)',
  224. },
  225. // { label: '2′网格', level: 14, size: { x: 2984.4, y: 2984.4, z: 2984.4 }, des: '(2984.4m)'},
  226. ])
  227. function clearPoints() {
  228. form.value.meshLevel = ''
  229. form.value.meshHeights = [0, 0]
  230. nextTick(() => {
  231. const toolsTips = document.getElementsByClassName('range-tooltip')
  232. for (let i = 0; i < toolsTips.length; i++) {
  233. toolsTips[i].style.visibility = 'hidden'
  234. }
  235. const sliderBtns = document.getElementsByClassName('el-slider__button-wrapper')
  236. for (let i = 0; i < toolsTips.length; i++) {
  237. sliderBtns[i].addEventListener('mousedown', () => {
  238. toolsTips[i].style.visibility = 'visible'
  239. })
  240. }
  241. })
  242. }
  243. // 单点选择逻辑
  244. const selectPoint = (point) => {
  245. form.value.meshLevel = point.level
  246. showGrid()
  247. }
  248. let timer_showGrid
  249. // 显示网格
  250. const showGrid = () => {
  251. if (timer_showGrid) {
  252. clearTimeout(timer_showGrid)
  253. }
  254. timer_showGrid = setTimeout(() => {
  255. console.log('showGrid')
  256. console.log('rings:', rings)
  257. console.log('form', form.value)
  258. if (!rings) return
  259. const { grade, deepShow, meshLevel, meshHeights } = form.value
  260. if (!meshLevel || meshHeights.every((h) => h === 0)) return
  261. clearQueryCube()
  262. setTimeout(() => {
  263. QueryCube({
  264. id: 'query_cube_kysg',
  265. evaluation: grade,
  266. deepShow: deepShow,
  267. show: true,
  268. level: meshLevel,
  269. minZ: meshHeights[0],
  270. maxZ: meshHeights[1],
  271. rings: rings,
  272. })
  273. }, 300)
  274. lastEvaluation = grade
  275. }, 1000)
  276. }
  277. function clearQueryCube() {
  278. QueryCube({
  279. id: 'query_cube_kysg',
  280. evaluation: lastEvaluation,
  281. show: false,
  282. })
  283. }
  284. //获取带高度三维体
  285. function getMesh(status) {
  286. if (status == 'hide') {
  287. geometryMeshEffect({
  288. id: 'sgzy_mesh',
  289. status: 'hide',
  290. })
  291. return
  292. }
  293. const [h1, h2] = form.value.meshHeights
  294. console.log('h1&h2:', h1, h2)
  295. let height = h2 - h1
  296. let tempRings = []
  297. for (let i = 0; i < rings[0].length; i++) {
  298. tempRings.push([rings[0][i][0], rings[0][i][1], h1 - 0.01])
  299. }
  300. geometryMeshEffect({
  301. id: 'sgzy_mesh',
  302. status: 'show',
  303. data: [
  304. {
  305. type: 'polygon',
  306. shape: {
  307. height: height,
  308. rings: [tempRings],
  309. color: [0, 255, 0, 0.2],
  310. },
  311. },
  312. ],
  313. })
  314. }
  315. onBeforeUnmount(() => {
  316. addKysgScale(false)
  317. getMesh('hide')
  318. clearDraw()
  319. clearQueryCube()
  320. })
  321. </script>
  322. <style lang="scss" scoped>
  323. .slider-container {
  324. position: absolute;
  325. top: calc(var(--aside-height) - 246px - var(--footer-height));
  326. left: calc(50% - 448px);
  327. width: 896px;
  328. height: 260px;
  329. background: rgba(0, 10, 30, 0.6);
  330. box-shadow: 0px 1px 6px 0px rgba(37, 37, 37, 0.6);
  331. border: 1px solid #98baffb6;
  332. visibility: visible;
  333. }
  334. :deep(.top-form) {
  335. height: 55px;
  336. display: flex;
  337. flex-wrap: nowrap;
  338. align-items: center;
  339. margin: 15px 12px 0;
  340. .el-form-item {
  341. margin-right: 10px;
  342. }
  343. .el-radio-group {
  344. flex-wrap: nowrap;
  345. }
  346. }
  347. .type-list {
  348. width: 100%;
  349. height: 60px;
  350. display: flex;
  351. flex-direction: row;
  352. justify-content: center;
  353. align-items: center;
  354. }
  355. .slider-bg {
  356. width: calc(100% - 10px);
  357. height: 25px;
  358. background: url('../../../assets/images/page/slider-bg.png') no-repeat;
  359. background-size: 100% 100%;
  360. padding: 0 70px 0 60px;
  361. margin-right: 10px;
  362. &.disabled {
  363. pointer-events: none;
  364. cursor: not-allowed;
  365. }
  366. }
  367. .slider {
  368. position: relative;
  369. height: 100%;
  370. }
  371. .marker {
  372. position: absolute;
  373. width: 25px;
  374. height: 25px;
  375. cursor: pointer;
  376. transition: background-color 0.3s;
  377. &::before {
  378. content: '';
  379. position: absolute;
  380. display: block;
  381. top: 9px;
  382. left: 8px;
  383. width: 8px;
  384. height: 8px;
  385. transform: rotateZ(45deg);
  386. border: 2px solid #6495ed;
  387. }
  388. &.active::before {
  389. border-color: #ffd700;
  390. }
  391. &.hover::before {
  392. background-color: #ffd700;
  393. }
  394. &.key-point {
  395. span {
  396. font-weight: bold;
  397. font-size: 18px;
  398. color: #ffffff;
  399. }
  400. &::before {
  401. border-color: #fffcf5;
  402. box-shadow: 0 0 4px 5px #ffd90062;
  403. background-color: #ffd700;
  404. }
  405. }
  406. }
  407. .label {
  408. position: absolute;
  409. top: -45px;
  410. left: 50%;
  411. transform: translateX(-50%);
  412. white-space: nowrap;
  413. font-size: 16px;
  414. color: #9daac7;
  415. }
  416. .des {
  417. position: absolute;
  418. top: -25px;
  419. left: 50%;
  420. transform: translateX(-50%);
  421. white-space: nowrap;
  422. font-size: 14px;
  423. color: #9daac7;
  424. }
  425. .single-slider {
  426. margin-top: 50px;
  427. .label {
  428. font-size: 14px;
  429. }
  430. .key-point span {
  431. font-size: 16px;
  432. }
  433. }
  434. :deep(.range-slider) {
  435. margin-top: 53px;
  436. --el-slider-height: 22px;
  437. .el-slider__runway {
  438. background: none;
  439. .el-slider__bar {
  440. background: linear-gradient(
  441. 0deg,
  442. rgba(255, 212, 0, 0.2) 0%,
  443. rgba(255, 212, 0, 0.6) 40%,
  444. rgba(255, 212, 0, 0.7) 50%,
  445. rgba(255, 212, 0, 0.6) 60%,
  446. rgba(255, 212, 0, 0.2) 100%
  447. );
  448. }
  449. .el-slider__button-wrapper {
  450. top: -6px;
  451. // pointer-events: none;
  452. .el-slider__button {
  453. width: 14px;
  454. height: 14px;
  455. border-radius: 0;
  456. transform: rotateZ(45deg);
  457. box-shadow: 0 0 2px 3px #ffd9003e;
  458. background-color: #ffd700;
  459. border-color: #fffcf5;
  460. border-width: 2px;
  461. }
  462. }
  463. .el-slider__marks .el-slider__marks-text {
  464. top: -43px;
  465. font-size: 16px;
  466. color: #9daac7;
  467. }
  468. .el-slider__marks-stop {
  469. top: 7px;
  470. width: 8px;
  471. height: 8px;
  472. transform: translateX(-50%) rotateZ(45deg);
  473. border: 2px solid #6495ed;
  474. border-radius: 0;
  475. background: transparent;
  476. }
  477. }
  478. }
  479. </style>
  480. <style lang="scss">
  481. .range-tooltip {
  482. display: block !important;
  483. background: none !important;
  484. border: none !important;
  485. margin-top: -7px;
  486. visibility: hidden;
  487. span {
  488. color: #ddd;
  489. font-size: 16px;
  490. }
  491. .el-popper__arrow {
  492. display: none;
  493. }
  494. }
  495. </style>