PanelKyhs.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. <!-- 空域划设面板 -->
  2. <template>
  3. <div class="panel-kyhs flex flex-col aside-left-inner">
  4. <div class="title-main">空域划设</div>
  5. <el-steps :active="currentStep" finish-status="success" align-center>
  6. <el-step title="空域划设" />
  7. <el-step title="空域评估" />
  8. </el-steps>
  9. <div class="flex-1 relative mb-4">
  10. <Transition name="emerge-left">
  11. <el-form ref="formRef" v-show="currentStep === 0" :model="form" :rules="rules" label-position="left"
  12. size="large" class="p-form p-main" hide-required-asterisk>
  13. <el-form-item label="名称" prop="name">
  14. <el-input v-model="form.name" clearable></el-input>
  15. </el-form-item>
  16. <el-form-item label="类型" prop="areaType">
  17. <el-select v-model="form.areaType" placeholder="">
  18. <el-option v-for="item in airSpaceTypes" :label="item.label" :value="item.value" />
  19. </el-select>
  20. </el-form-item>
  21. <el-form-item label="类型" prop="type">
  22. <el-radio-group v-model="form.type" size="large">
  23. <el-radio-button label="长期" value="长期" />
  24. <el-radio-button label="临时" value="临时" />
  25. </el-radio-group>
  26. </el-form-item>
  27. <template v-if="form.type === '临时'">
  28. <el-form-item label="日期" prop="startDate">
  29. <el-date-picker v-model="form.startDate" value-format="YYYY-MM-DD" type="date"
  30. class="flex-1"></el-date-picker>
  31. <div class="mx-3">--</div>
  32. <el-date-picker v-model="form.endDate" value-format="YYYY-MM-DD" type="date"
  33. class="flex-1"></el-date-picker>
  34. </el-form-item>
  35. <el-form-item label="时间" prop="startTime">
  36. <el-time-select v-model="form.startTime" :max-time="form.endTime" placeholder="" start="00:00"
  37. step="00:15" end="23:45" class="flex-1" />
  38. <div class="mx-3">--</div>
  39. <el-time-select v-model="form.endTime" :min-time="form.startTime" placeholder="" start="00:00"
  40. step="00:15" end="23:45" class="flex-1" />
  41. </el-form-item>
  42. <el-form-item label="划设方式" prop="dataType">
  43. <el-radio-group v-model="form.dataType" size="large">
  44. <el-radio label="手动划设" value="手动划设" />
  45. <el-radio label="导入" value="导入" />
  46. </el-radio-group>
  47. </el-form-item>
  48. </template>
  49. <el-form-item label="类型" prop="drawType" v-if="form.dataType === '手动划设'">
  50. <el-radio-group v-model="form.drawType" size="large" class="content-between h-24">
  51. <el-radio-button v-for="item in drawTypes" :label="item.label" :value="item.value" />
  52. </el-radio-group>
  53. </el-form-item>
  54. <el-form-item label="上传文件" prop="file" v-else>
  55. <el-upload v-model:file-list="form.file" action="" class="single-uplaod">
  56. <div class="upload-trigger">点击上传文件</div>
  57. </el-upload>
  58. </el-form-item>
  59. <el-form-item label="高度" prop="height">
  60. <el-input v-model="form.height" type="number" clearable>
  61. <template #suffix>
  62. <span>米</span>
  63. </template>
  64. </el-input>
  65. </el-form-item>
  66. <el-form-item label="顶半径" prop="topRadius" v-if="['mesh', 'point'].includes(form.drawType)">
  67. <el-input v-model="form.topRadius" type="number" clearable>
  68. <template #suffix>
  69. <span>米</span>
  70. </template>
  71. </el-input>
  72. </el-form-item>
  73. <el-form-item label="底半径" prop="bottomRadius" v-if="form.drawType === 'mesh'">
  74. <el-input v-model="form.bottomRadius" type="number" clearable>
  75. <template #suffix>
  76. <span>米</span>
  77. </template>
  78. </el-input>
  79. </el-form-item>
  80. <el-form-item label="绘制空域">
  81. <el-button class="btn-secondary" @click="handleDraw">绘制</el-button>
  82. </el-form-item>
  83. </el-form>
  84. </Transition>
  85. <Transition name="emerge-right">
  86. <div v-show="currentStep === 1" class="p-main">
  87. <div v-if="isSafe" class="msg-safe mb-2">当前空域无冲突!</div>
  88. <template v-else>
  89. <div v-for="item in results" :key="item.title" class="mb-3">
  90. <div class="title-sub">
  91. {{ item.title }}
  92. <div class="flex-1 flex justify-between items-center pl-2">
  93. <span v-if="item.level" class="risk-label"
  94. :class="riskTypes.find(i => i.value === item.level).color">{{
  95. riskTypes.find(i => i.value === item.level).label
  96. }}</span>
  97. <i v-else></i>
  98. <span class="btn-inline" @click="handleReInspect">重新评估</span>
  99. </div>
  100. </div>
  101. <table class="table-default stripe mt-2">
  102. <thead>
  103. <tr>
  104. <th v-for="th in item.cols">{{ th }}</th>
  105. <th class="w-20">地图显示</th>
  106. </tr>
  107. </thead>
  108. <tbody>
  109. <tr v-for="row in item.details">
  110. <td v-for="key in Object.keys(item.cols)">{{ row[key] }}</td>
  111. <td class="w-20">
  112. <el-checkbox size="large" v-model="row.display"
  113. @change="val => handleDisplay(row, val)"></el-checkbox>
  114. </td>
  115. </tr>
  116. </tbody>
  117. </table>
  118. </div>
  119. </template>
  120. </div>
  121. </Transition>
  122. </div>
  123. <div class="text-center">
  124. <template v-if="currentStep === 0">
  125. <el-button :disabled="!hasDraw" class="btn-main" @click="toNext">下一步</el-button>
  126. </template>
  127. <template v-if="currentStep === 1">
  128. <el-button class="btn-main" @click="toPrev">上一步</el-button>
  129. <el-button class="btn-main" @click="handleSave" :loading="loading.save">保存</el-button>
  130. </template>
  131. </div>
  132. </div>
  133. </template>
  134. <script setup>
  135. import { computed, onBeforeUnmount, reactive, ref, watch } from 'vue';
  136. import { drawArea, clearDraw, InspectCube, showShapes } from '@/utils/map/addLayer'
  137. import { airSpaceTypes } from '@/utils/options';
  138. import { hexToRgb } from '@/utils/tools';
  139. import { useMapStore } from '@/store/map';
  140. import { AddArea } from '@/service/http';
  141. import { riskTypes } from '@/utils/options';
  142. import useLayoutStore from '@/store/layout';
  143. const mapStore = useMapStore()
  144. const layoutStore = useLayoutStore()
  145. const currentStep = ref(0)
  146. const form = ref({
  147. areaType: '1',
  148. dataType: '手动划设',
  149. type: '长期',
  150. drawType: 'rectangle',
  151. height: '100',
  152. topRadius: '150',
  153. bottomRadius: '50'
  154. })
  155. const rules = {
  156. name: [{ required: true, message: '请输入空域名称', trigger: 'none' }],
  157. height: [{ required: true, message: '此项不可为空', trigger: 'none' }],
  158. topRadius: [{ required: true, message: '此项不可为空', trigger: 'none' }],
  159. bottomRadius: [{ required: true, message: '此项不可为空', trigger: 'none' }],
  160. }
  161. const drawTypes = [
  162. { label: '矩形', value: 'rectangle', geoType: '2', shapeType: 'polygon' },
  163. { label: '圆柱', value: 'circle', geoType: '1', shapeType: 'polygon' },
  164. { label: '多面体', value: 'polygon', geoType: '3', shapeType: 'polygon' },
  165. { label: '圆台', value: 'mesh', geoType: '6', shapeType: 'frustum-cone' },
  166. { label: '圆锥', value: 'point', geoType: '4', shapeType: 'inverted-cone' },
  167. ]
  168. const results = ref([
  169. {
  170. title: '空域评估',
  171. level: null,
  172. cols: { areaName: '冲突空域', time: '冲突时段', height: '冲突高度' },
  173. details: []
  174. },
  175. {
  176. title: '计划冲突',
  177. level: null,
  178. cols: { areaName: '冲突空域', time: '冲突时段', height: '冲突高度' },
  179. details: []
  180. },
  181. {
  182. title: '障碍物分析',
  183. level: null,
  184. cols: { object: '冲突空域', height: '冲突高度' },
  185. details: []
  186. },
  187. {
  188. title: '地面安全评估',
  189. level: null,
  190. cols: { element: '要素', coverage: '覆盖率' },
  191. details: []
  192. }
  193. ])
  194. const isSafe = computed(() => {
  195. return results.value.every(i => i.level === 1)
  196. })
  197. const hasDraw = ref(false)
  198. function handleDraw() {
  199. formRef.value.validateField(['height', 'topRadius', 'bottomRadius'], async (isValid) => {
  200. if (!isValid) return
  201. if (hasDraw.value) {
  202. showShapes({
  203. id: 'kyhs_drew_non_editable',
  204. data: null
  205. })
  206. handleInspect(false)
  207. await new Promise((res) => {
  208. setTimeout(() => res(), 500);
  209. })
  210. }
  211. // 处理绘制
  212. const color = hexToRgb(airSpaceTypes.find(i => i.value === form.value.areaType).color, 0.5)
  213. drawArea({
  214. ...form.value,
  215. color
  216. })
  217. })
  218. }
  219. function getShapeInfo() {
  220. let res = {}
  221. const { height, topRadius, bottomRadius } = form.value
  222. const geoType = drawTypes.find(i => i.value === form.value.drawType).geoType
  223. const { x, y, z } = drawGeometry
  224. switch (geoType) {
  225. case '1':
  226. case '2':
  227. case '3':
  228. res = {
  229. height: +height,
  230. rings: drawGeometry.rings
  231. }
  232. break;
  233. case '4':
  234. res = {
  235. point: { x, y, z },
  236. height: +height,
  237. radius: +topRadius
  238. }
  239. break;
  240. case '6':
  241. res = {
  242. point: { x, y, z },
  243. height: +height,
  244. topRadius: +topRadius,
  245. bottomRadius: +bottomRadius
  246. }
  247. break;
  248. }
  249. return res
  250. }
  251. function handleInspect(show = true) {
  252. InspectCube({
  253. type: drawTypes.find(i => i.value === form.value.drawType).shapeType,
  254. id: 'inspect_kyhs',
  255. show,
  256. shape: show ? getShapeInfo() : {}
  257. })
  258. }
  259. let drawGeometry
  260. watch(() => mapStore.draw_geometry, (val) => {
  261. console.log('draw_geometry:', val)
  262. drawGeometry = val
  263. hasDraw.value = true
  264. }, { deep: true })
  265. watch(() => mapStore.cubeResult, (val) => {
  266. console.log('cubeResult:', val)
  267. formatResult(val)
  268. }, { deep: true })
  269. function formatResult(val) {
  270. const { groundSafety, lineConflictArray, obstacle, spaceConflictArray } = val
  271. results.value[0].level = val.spaceRisk
  272. results.value[1].level = val.lineRisk
  273. results.value[2].level = obstacle.risk
  274. results.value[3].level = groundSafety.risk
  275. results.value[0].details = spaceConflictArray.map(i => ({
  276. areaName: i.gridName,
  277. height: i.heightRange,
  278. time: i.conflictTime,
  279. display: true
  280. }))
  281. results.value[1].details = lineConflictArray.map(i => ({
  282. areaName: i.gridName,
  283. height: i.heightRange,
  284. time: i.conflictTime,
  285. display: true
  286. }))
  287. results.value[2].details = [
  288. { time: '01-249:00-01-25 10-00', object: '建筑物', height: obstacle.buildingConflictHeight, display: true },
  289. { time: '01-249:00-01-25 10-00', object: '建筑物缓冲区', height: obstacle.buildingBufferConflictHeight, display: true },
  290. ]
  291. const toPercent = (num) => {
  292. return Math.round(num * 100) + '%'
  293. }
  294. results.value[3].details = [
  295. { element: '道路', coverage: toPercent(groundSafety.road), display: true },
  296. { element: '河流', coverage: toPercent(groundSafety.river), display: true },
  297. { element: '绿化', coverage: toPercent(groundSafety.green), display: true },
  298. { element: '综合占比', coverage: toPercent(groundSafety.road + groundSafety.river + groundSafety.green), display: true },
  299. ]
  300. layoutStore.toggleGlobalLoading(false)
  301. }
  302. function handleDisplay(row, val) {
  303. console.log(row, val)
  304. }
  305. const formRef = ref(null)
  306. function toNext() {
  307. formRef.value.validate(valid => {
  308. if (valid) {
  309. layoutStore.toggleGlobalLoading(true)
  310. clearDraw()
  311. showShapes({
  312. id: 'kyhs_drew_non_editable',
  313. data: [{
  314. type: drawTypes.find(i => i.value === form.value.drawType).shapeType,
  315. shape: {
  316. ...getShapeInfo(),
  317. color: hexToRgb(airSpaceTypes.find(i => i.value===form.value.areaType).color, 0.5)
  318. }
  319. }]
  320. })
  321. handleInspect(true)
  322. currentStep.value = 1
  323. }
  324. })
  325. }
  326. function handleReInspect() {
  327. layoutStore.toggleGlobalLoading(true)
  328. handleInspect(false)
  329. setTimeout(() => {
  330. handleInspect(true)
  331. }, 500);
  332. }
  333. function toPrev() {
  334. currentStep.value = 0
  335. }
  336. const loading = reactive({
  337. save: false
  338. })
  339. function handleSave() {
  340. loading.save = true
  341. let params = {
  342. ...form.value,
  343. spaceType: form.value.areaType,
  344. geoType: drawTypes.find(i => i.value === form.value.drawType).geoType,
  345. upRadius: form.value.topRadius,
  346. downRadius: form.value.bottomRadius,
  347. shape: JSON.stringify(getShapeInfo())
  348. }
  349. AddArea(params).then(({ data: res }) => {
  350. if (res.msg === 'success') {
  351. ElMessage({ offset: 90, type: 'success', message: '保存成功' })
  352. } else {
  353. ElMessage({ offset: 90, type: 'error', message: `保存失败,${res.msg}` })
  354. }
  355. }).catch((e) => {
  356. ElMessage({ offset: 90, type: 'error', message: `保存失败,${e}` })
  357. }).finally(() => {
  358. loading.save = false
  359. })
  360. }
  361. onBeforeUnmount(() => {
  362. clearDraw()
  363. handleInspect(false)
  364. showShapes({
  365. id: 'kyhs_drew_non_editable',
  366. data: null
  367. })
  368. })
  369. </script>
  370. <style lang="scss">
  371. @use '../../../assets/styles/panel-form.scss';
  372. </style>
  373. <style lang="scss" scoped>
  374. .panel-kyhs {
  375. :deep(.p-form) {
  376. .el-form-item__label {
  377. width: 105px;
  378. }
  379. }
  380. }
  381. </style>