FloatPanelKysg.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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="自适应" />
  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 v-model="form.district" @change="handleSelectDistrict" :options="districtList"
  28. :props="cascaderProps" placeholder="请选择行政区划" />
  29. </el-form-item>
  30. </el-form>
  31. <!-- 单点选择滑动条 -->
  32. <div class="slider-bg single-slider">
  33. <div class="slider">
  34. <div v-for="(point, index) in DenseData" :key="index" class="marker"
  35. :class="{ 'key-point': selectedPoint === index }"
  36. :style="{ left: index * (100 / (DenseData.length - 1)) + '%' }" @click="selectPoint(index)">
  37. <span class="label">{{ point.label }}</span>
  38. <span class="des">{{ point.des }}</span>
  39. </div>
  40. </div>
  41. </div>
  42. <!-- 范围选择滑动条 -->
  43. <div class="slider-bg range-slider">
  44. <div class="slider">
  45. <!-- 高亮背景 -->
  46. <div class="range-highlight" v-if="rangeStart !== null && rangeEnd !== null" :style="{
  47. left: rangeStart * (100 / (heightData.length - 1)) + 1.5 + '%',
  48. width: (rangeEnd - rangeStart) * (100 / (heightData.length - 1)) + '%',
  49. }"></div>
  50. <!-- 数据点 -->
  51. <div v-for="(point, index) in heightData" :key="index" class="marker" :class="{
  52. 'active': (rangeStart !== null && rangeEnd === null && index === rangeStart) || (rangeStart !== null && rangeEnd !== null && index >= rangeStart && index <= rangeEnd),
  53. 'hover': shouldHighlightOnHover(index),
  54. 'key-point': index === rangeStart || index === rangeEnd
  55. }" :style="{ left: index * (100 / (heightData.length - 1)) + '%' }" @click="selectRange(index)"
  56. @mouseover="handleMouseOver(index)" @mouseleave="handleMouseLeave">
  57. <span class="label">{{ point.label }}</span>
  58. </div>
  59. </div>
  60. </div>
  61. </div>
  62. </template>
  63. <script>
  64. import { ref, reactive, onMounted, onBeforeUnmount, watch } from 'vue';
  65. import { addKysgScale, DrawPolygon, QueryDistrict, QueryCube, clearDraw } from "@/utils/map/addLayer.js";
  66. import useLayoutStore from '@/store/layout';
  67. import { useMapStore } from '@/store/map';
  68. import { GetDistrictTree } from '@/service/http';
  69. import {geometryMeshEffect} from "@/utils/map/addTool.js";
  70. export default {
  71. setup(props, { emit }) {
  72. const mapStore = useMapStore()
  73. const layoutStore = useLayoutStore()
  74. const form = ref({
  75. meshType: '二维',
  76. grade: true,
  77. deepShow:false,
  78. meshRange: '自适应'
  79. })
  80. let lastEvaluation
  81. let rings
  82. const selectedPoint = ref(null); // 单点选择的索引
  83. const rangeStart = ref(null); // 范围选择起点
  84. const rangeEnd = ref(null); // 范围选择终点
  85. const hoverIndex = ref(null); // 鼠标悬浮的索引
  86. const districtList = ref([])
  87. function handleSelectDistrict(val) {
  88. QueryDistrict(val)
  89. }
  90. const cascaderProps = {
  91. expandTrigger: 'hover',
  92. checkStrictly: true
  93. }
  94. watch(() => form.value.meshRange, (val) => {
  95. console.log(val)
  96. clearDraw()
  97. clearQueryCube()
  98. rings = null
  99. if (val === '自适应') {
  100. layoutStore.toggleGlobalLoading(true)
  101. setTimeout(() => {
  102. layoutStore.toggleGlobalLoading(false)
  103. }, 3000);
  104. addKysgScale(true)
  105. clearPoints()
  106. } else {
  107. addKysgScale(false)
  108. if (val === '行政区划' && !districtList.value.length) {
  109. GetDistrictTree().then(res => {
  110. if (!Array.isArray(res.data)) {
  111. ElMessage({ offset: 90, type: 'warning', message: '行政区划获取失败' })
  112. return
  113. }
  114. districtList.value = res.data
  115. }).catch(() => {
  116. ElMessage({ offset: 90, type: 'warning', message: '行政区划获取失败' })
  117. })
  118. }
  119. }
  120. }, { immediate: true })
  121. watch(form, (val) => {
  122. showGrid()
  123. }, { deep: true })
  124. function handleDraw() {
  125. clearQueryCube();
  126. getMesh("hide");
  127. DrawPolygon({ hasZ: true })
  128. }
  129. watch(() => mapStore.draw_geometry, (val) => {
  130. rings = val?.rings
  131. clearDraw();
  132. getMesh("show");
  133. showGrid()
  134. }, { deep: true })
  135. watch(() => mapStore.queryResult, (val) => {
  136. rings = val[0].geometry.rings
  137. showGrid()
  138. }, { deep: true })
  139. const DenseData = reactive([
  140. // { label: '1/8″网格', level: 24, size: { x: 3.3055614085396883, y: 3.8513467134689563, z: 3.3678982462941303 }, des: '(3.9m)'},
  141. { label: '1/4″网格', level: 23, size: { x: 6.7358, y: 7.7024, z: 6.6111 }, des: '(7m)' },
  142. { label: '1/2″网格', level: 22, size: { x: 13.222978864358083, y: 15.406742669116284, z: 13.471596544164798 }, des: '(15m)' },
  143. { label: '1′网格', level: 21, size: { x: 26.44107169853396, y: 30.815525255577086, z: 26.943221541824816 }, des: '(30.9m)' },
  144. { label: '2′网格', level: 20, size: { x: 52.887881893650956, y: 61.627412608112536, z: 53.887012166300096 }, des: '(61m)' },
  145. { label: '4′网格', level: 19, size: { x: 105.79869494173579, y: 123.43930203184209, z: 107.77356906534683 }, des: '(123.7m)' },
  146. { label: '8′网格', level: 18, size: { x: 211.66439868324142, y: 246.49964316944533, z: 215.5489592181181 }, des: '(247.4m)' },
  147. { label: '16′网格', level: 17, size: { x: 424.0518521273334, y: 492.09123171765896, z: 431.1052029077298 }, des: '(376.7m)' },
  148. { label: '32′网格', level: 16, size: { x: 844.8477050379952, y: 990.1345935064928, z: 862.239544684298 }, des: '(746.4m)' },
  149. { label: '1′网格', level: 15, size: { x: 1582.2726823222256, y: 1856.6079667204758, z: 1724.5956527210265 }, des: '(1850m)' },
  150. // { label: '2′网格', level: 14, size: { x: 2984.4, y: 2984.4, z: 2984.4 }, des: '(2984.4m)'},
  151. ]);
  152. const heightData = reactive([
  153. { label: "0m", value: 0 },
  154. { label: "20m", value: 20 },
  155. { label: "40m", value: 40 },
  156. { label: "60m", value: 60 },
  157. { label: "80m", value: 80 },
  158. { label: "100m", value: 100 },
  159. { label: "120m", value: 120 },
  160. { label: "200m", value: 200 },
  161. { label: "300m", value: 300 },
  162. { label: "600m", value: 600 },
  163. { label: "1000m", value: 1000 },
  164. ]);
  165. function clearPoints() {
  166. selectedPoint.value = null
  167. rangeStart.value = null
  168. rangeEnd.value = null
  169. }
  170. // 单点选择逻辑
  171. const selectPoint = (index) => {
  172. selectedPoint.value = index;
  173. showGrid()
  174. };
  175. // 范围选择逻辑
  176. const selectRange = (index) => {
  177. if (rangeStart.value === null || rangeEnd.value !== null) {
  178. // 如果未选择起点或范围已完成,重新设置起点
  179. rangeStart.value = index;
  180. rangeEnd.value = null;
  181. } else {
  182. // 如果已经选择起点,设置终点
  183. rangeEnd.value = index;
  184. if (rangeEnd.value < rangeStart.value) {
  185. // 保证起点小于终点
  186. [rangeStart.value, rangeEnd.value] = [rangeEnd.value, rangeStart.value];
  187. }
  188. }
  189. showGrid()
  190. };
  191. const handleMouseOver = (index) => {
  192. hoverIndex.value = index;
  193. };
  194. const handleMouseLeave = () => {
  195. hoverIndex.value = null;
  196. };
  197. const shouldHighlightOnHover = (index) => {
  198. if ((hoverIndex.value !== null && rangeStart.value === null) || (rangeStart.value !== null && rangeEnd.value !== null)) {
  199. // 如果未选择起点,只高亮当前悬浮的点
  200. return index === hoverIndex.value;
  201. } else if (hoverIndex.value !== null && rangeStart.value !== null && rangeEnd.value === null) {
  202. // 如果已选择起点,且未选择终点,高亮起点到悬浮点之间的范围
  203. return (
  204. (index >= rangeStart.value && index <= hoverIndex.value) ||
  205. (index <= rangeStart.value && index >= hoverIndex.value)
  206. );
  207. }
  208. return false; // 不满足条件时,不高亮
  209. };
  210. let timer_showGrid
  211. // 显示网格
  212. const showGrid = () => {
  213. if (timer_showGrid) {
  214. clearTimeout(timer_showGrid)
  215. }
  216. timer_showGrid = setTimeout(() => {
  217. console.log('showGrid')
  218. console.log('rings:',rings)
  219. console.log('points',selectedPoint.value, rangeStart.value, rangeEnd.value)
  220. if (!rings) return
  221. if (selectedPoint.value===null || rangeStart.value===null || rangeEnd.value===null) return
  222. clearQueryCube()
  223. setTimeout(() => {
  224. QueryCube({
  225. id: 'query_cube_kysg',
  226. evaluation: form.value.grade,
  227. deepShow: form.value.deepShow,
  228. show: true,
  229. level: DenseData[selectedPoint.value].level,
  230. minZ: heightData[rangeStart.value].value,
  231. maxZ: heightData[rangeEnd.value].value,
  232. rings: rings
  233. })
  234. }, 300);
  235. lastEvaluation = form.value.grade
  236. }, 1000)
  237. };
  238. function clearQueryCube() {
  239. QueryCube({
  240. id: 'query_cube_kysg',
  241. evaluation: lastEvaluation,
  242. show: false,
  243. })
  244. }
  245. //获取带高度三维体
  246. function getMesh(status){
  247. if(status == "hide"){
  248. geometryMeshEffect({
  249. id: 'sgzy_mesh',
  250. status: "hide"
  251. })
  252. return
  253. }
  254. let height = heightData[rangeEnd.value].value - heightData[rangeStart.value].value;
  255. let tempRings = [];
  256. for(let i = 0; i < rings[0].length; i++){
  257. tempRings.push([rings[0][i][0],rings[0][i][1],heightData[rangeStart.value].value - 0.01])
  258. }
  259. debugger
  260. geometryMeshEffect({
  261. id: 'sgzy_mesh',
  262. status:"show",
  263. data: [{
  264. type: 'polygon',
  265. shape: {
  266. height: height,
  267. rings: [tempRings],
  268. color: [0,255,0,0.2]
  269. }
  270. }]
  271. })
  272. }
  273. onBeforeUnmount(() => {
  274. addKysgScale(false);
  275. getMesh("hide")
  276. clearDraw()
  277. clearQueryCube()
  278. })
  279. return {
  280. layoutStore,
  281. form,
  282. DenseData,
  283. heightData,
  284. selectedPoint,
  285. rangeStart,
  286. rangeEnd,
  287. selectPoint,
  288. selectRange,
  289. handleMouseOver,
  290. handleMouseLeave,
  291. shouldHighlightOnHover,
  292. handleDraw,
  293. handleSelectDistrict,
  294. cascaderProps,
  295. districtList
  296. };
  297. },
  298. };
  299. </script>
  300. <style lang="scss" scoped>
  301. .slider-container {
  302. position: absolute;
  303. top: calc(var(--aside-height) - 246px - var(--footer-height));
  304. left: calc(50% - 448px);
  305. width: 896px;
  306. height: 260px;
  307. background: rgba(0, 10, 30, 0.6);
  308. box-shadow: 0px 1px 6px 0px rgba(37, 37, 37, 0.6);
  309. border: 1px solid #98baffb6;
  310. visibility: visible;
  311. }
  312. .top-form {
  313. display: flex;
  314. flex-wrap: nowrap;
  315. align-items: center;
  316. margin: 15px 12px 0;
  317. .el-form-item {
  318. margin-right: 10px;
  319. }
  320. }
  321. .type-list {
  322. width: 100%;
  323. height: 60px;
  324. display: flex;
  325. flex-direction: row;
  326. justify-content: center;
  327. align-items: center;
  328. }
  329. .slider-bg {
  330. width: calc(100% - 10px);
  331. height: 25px;
  332. background: url('../../../assets/images/page/slider-bg.png') no-repeat;
  333. background-size: 100% 100%;
  334. padding: 0 70px 0 60px;
  335. margin-right: 10px;
  336. }
  337. .slider {
  338. position: relative;
  339. height: 100%;
  340. /* background-color: #2a2f3a; */
  341. }
  342. .marker {
  343. position: absolute;
  344. width: 25px;
  345. height: 25px;
  346. cursor: pointer;
  347. transition: background-color 0.3s;
  348. &::before {
  349. content: '';
  350. position: absolute;
  351. display: block;
  352. top: 9px;
  353. left: 8px;
  354. width: 8px;
  355. height: 8px;
  356. transform: rotateZ(45deg);
  357. border: 2px solid #6495ed;
  358. }
  359. &.active::before {
  360. border-color: #ffd700;
  361. }
  362. &.hover::before {
  363. background-color: #ffd700;
  364. }
  365. &.key-point {
  366. span {
  367. font-weight: bold;
  368. font-size: 18px;
  369. color: #FFFFFF;
  370. }
  371. &::before {
  372. border-color: #FFFCF5;
  373. box-shadow: 0 0 4px 5px #ffd90062;
  374. background-color: #ffd700;
  375. }
  376. }
  377. }
  378. .label {
  379. position: absolute;
  380. top: -45px;
  381. left: 50%;
  382. transform: translateX(-50%);
  383. white-space: nowrap;
  384. font-size: 16px;
  385. color: #9DAAC7;
  386. }
  387. .des {
  388. position: absolute;
  389. top: -25px;
  390. left: 50%;
  391. transform: translateX(-50%);
  392. white-space: nowrap;
  393. font-size: 14px;
  394. color: #9DAAC7;
  395. }
  396. .range-highlight {
  397. position: absolute;
  398. top: 3px;
  399. height: 80%;
  400. background-color: rgba(255, 212, 0, 0.2);
  401. z-index: 1;
  402. }
  403. .single-slider {
  404. margin-top: 50px;
  405. .label {
  406. font-size: 14px;
  407. }
  408. .key-point span {
  409. font-size: 16px;
  410. }
  411. }
  412. .range-slider {
  413. margin-top: 53px;
  414. .slider .marker {
  415. z-index: 2;
  416. }
  417. .label {
  418. top: -25px;
  419. }
  420. }
  421. </style>