Merge remote-tracking branch 'origin/master'
| | |
| | | |
| | | import PlanarRouteLineList from '@/components/PlanarRouteLineList/PlanarRouteLineList.vue' |
| | | |
| | | import { flyVisual } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | |
| | | import * as Cesium from 'cesium' |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium' |
| | |
| | | import { Cartesian3, Terrain, Viewer } from 'cesium'; |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium'; |
| | | import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'; |
| | | import { flyVisual } from '@/utils/cesium/mapUtil'; |
| | | import { flyVisual } from '@ztzf/utils'; |
| | | import * as turf from '@turf/turf'; |
| | | |
| | | import { nextTick, onBeforeUnmount, onMounted, onUnmounted } from 'vue'; |
| | |
| | | import endPointImg from '@/assets/images/EndPointicon.png' |
| | | |
| | | import { ArrowLineMaterialProperty } from '@/utils/cesium/Material' |
| | | import { flyVisual } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | |
| | | import { useStore } from 'vuex' |
| | | |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | |
| | | import logo from '@/assets/images/topContainer/logo.png' |
| | | |
| | | import { mapGetters } from 'vuex' |
| | |
| | | }, |
| | | methods: { |
| | | logout () { |
| | | this.$confirm(this.$t('logoutTip'), this.$t('提示'), { |
| | | confirmButtonText: this.$t('submitText'), |
| | | cancelButtonText: this.$t('cancelText'), |
| | | ElMessageBox.confirm(`是否退出系统, 是否继续?`, '提示', { |
| | | type: 'warning', |
| | | customClass: 'command-page-view-message-box', |
| | | confirmButtonClass: 'command-message-box-confirm', |
| | | cancelButtonClass: 'command-message-box-cancel', |
| | | }).then(() => { |
| | | this.$store.dispatch('LogOut').then(() => { |
| | | const env = import.meta.env.VITE_APP_ENV |
| | |
| | | </div> |
| | | <div class="login-weaper"> |
| | | <div class="login-left animate__animated animate__fadeInLeft"> |
| | | <img class="img" src="/img/logo.png" alt="" /> |
| | | <img class="img" src="/img/logo.png" alt="" /> |
| | | <p class="title">{{ $t('login.info') }}</p> |
| | | </div> |
| | | <div class="login-border animate__animated animate__fadeInRight"> |
| | | <div class="login-main"> |
| | | <div class="lock-form animate__animated animate__bounceInDown"> |
| | | <div |
| | | class="animate__animated" |
| | | :class="{ shake: passwdError, animate__bounceOut: pass }" |
| | | > |
| | | <div class="animate__animated" :class="{ shake: passwdError, animate__bounceOut: pass }"> |
| | | <h3 style="color: #333">{{ userInfo.username }}</h3> |
| | | <el-input |
| | | placeholder="请输入登录密码" |
| | | type="password" |
| | | class="input-with-select animated" |
| | | v-model="passwd" |
| | | @keyup.enter="handleLogin" |
| | | > |
| | | <el-input placeholder="请输入登录密码" type="password" class="input-with-select animated" v-model="passwd" |
| | | @keyup.enter="handleLogin"> |
| | | <template #append> |
| | | <i class="icon-bofangqi-suoping" @click="handleLogin"></i> |
| | | |
| | |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import { mapGetters } from 'vuex'; |
| | | import { removeRefreshToken, removeToken } from '@/utils/auth'; |
| | | import { mapGetters } from 'vuex' |
| | | import { removeRefreshToken, removeToken } from '@/utils/auth' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | |
| | | export default { |
| | | name: 'lock', |
| | | data() { |
| | | data () { |
| | | return { |
| | | time: '', |
| | | timeTimer: null, |
| | | passwd: '', |
| | | passwdError: false, |
| | | pass: false, |
| | | }; |
| | | }, |
| | | created() { |
| | | this.getTime(); |
| | | if (this.timeTimer) clearInterval(this.timeTimer); |
| | | this.timeTimer = setInterval(() => { |
| | | this.getTime(); |
| | | }, 1000); |
| | | }, |
| | | beforeDestroy() { |
| | | if (this.timeTimer) { |
| | | clearInterval(this.timeTimer); |
| | | this.timeTimer = null; |
| | | } |
| | | }, |
| | | mounted() {}, |
| | | created () { |
| | | this.getTime() |
| | | if (this.timeTimer) clearInterval(this.timeTimer) |
| | | this.timeTimer = setInterval(() => { |
| | | this.getTime() |
| | | }, 1000) |
| | | }, |
| | | beforeDestroy () { |
| | | if (this.timeTimer) { |
| | | clearInterval(this.timeTimer) |
| | | this.timeTimer = null |
| | | } |
| | | }, |
| | | mounted () { }, |
| | | computed: { |
| | | ...mapGetters(['userInfo', 'tag', 'lockPasswd']), |
| | | }, |
| | | props: [], |
| | | methods: { |
| | | getTime() { |
| | | this.time = this.$dayjs().format('YYYY年MM月DD日 HH:mm:ss'); |
| | | getTime () { |
| | | this.time = this.$dayjs().format('YYYY年MM月DD日 HH:mm:ss') |
| | | }, |
| | | handleLogout() { |
| | | this.$confirm('是否退出系统, 是否继续?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | handleLogout () { |
| | | ElMessageBox.confirm(`是否退出系统, 是否继续?`, '提示', { |
| | | type: 'warning', |
| | | customClass: 'command-page-view-message-box', |
| | | confirmButtonClass: 'command-message-box-confirm', |
| | | cancelButtonClass: 'command-message-box-cancel', |
| | | }).then(() => { |
| | | this.$store.dispatch('LogOut').then(() => { |
| | | // 清除token信息 |
| | | removeToken(); |
| | | removeRefreshToken(); |
| | | this.$router.push({ path: '/login' }); |
| | | }); |
| | | }); |
| | | removeToken() |
| | | removeRefreshToken() |
| | | this.$router.push({ path: '/login' }) |
| | | }) |
| | | }) |
| | | }, |
| | | handleLogin() { |
| | | handleLogin () { |
| | | if (this.passwd !== this.lockPasswd) { |
| | | this.passwd = ''; |
| | | this.passwd = '' |
| | | this.$message({ |
| | | message: '解锁密码错误,请重新输入', |
| | | type: 'error', |
| | | }); |
| | | this.passwdError = true; |
| | | }) |
| | | this.passwdError = true |
| | | setTimeout(() => { |
| | | this.passwdError = false; |
| | | }, 1000); |
| | | return; |
| | | this.passwdError = false |
| | | }, 1000) |
| | | return |
| | | } |
| | | this.pass = true; |
| | | this.pass = true |
| | | setTimeout(() => { |
| | | this.$store.commit('CLEAR_LOCK'); |
| | | this.$store.commit('CLEAR_LOCK') |
| | | this.$router.push({ |
| | | path: this.tag.path, |
| | | }); |
| | | }, 1000); |
| | | }) |
| | | }, 1000) |
| | | }, |
| | | }, |
| | | components: {}, |
| | | }; |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"></style> |
| | |
| | | import * as turf from '@turf/turf' |
| | | import { boxTransformScale } from '@/utils/turfFunc' |
| | | import { ElMessage } from 'element-plus' |
| | | import { flyVisual, getPointPositionsHeight } from '@/utils/cesium/mapUtil' |
| | | import { getPointPositionsHeight } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | |
| | | /** |
| | | * 多边形绘制与编辑工具类 |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 飞到中心点并且所有点在可视范围 |
| | | * @param positionsData 二维数组,每项为 [lon, lat] 或 三维数组 [lon, lat, height] |
| | | * @param viewer Cesium.Viewer 实例 |
| | | * @param multiple 缩放倍数-默认为4 |
| | | * @param pitch 俯仰角-默认为-90 |
| | | */ |
| | | export function flyVisual({ positionsData, viewer, multiple = 4, pitch = -90 }) { |
| | | if (!Array.isArray(positionsData) || positionsData.length === 0) return |
| | | |
| | | // 如果是一个点,加两个点方便后续生成外包围盒 |
| | | if (positionsData.length === 1) { |
| | | const [lon, lat, height = 0] = positionsData[0] |
| | | positionsData = [ |
| | | [lon + 0.001, lat + 0.001, height], |
| | | [lon - 0.001, lat - 0.001, height], |
| | | [lon, lat, height], |
| | | ] |
| | | } |
| | | const positions = positionsData.map(([lon, lat, height = 0]) => |
| | | Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat), Number(height || 0)) |
| | | ) |
| | | |
| | | // 计算最高高度和中心点经纬度 |
| | | let maxHeight = -Infinity |
| | | let sumLon = 0, |
| | | sumLat = 0 |
| | | for (const [lon, lat, height] of positionsData) { |
| | | sumLon += Number(lon) |
| | | sumLat += Number(lat) |
| | | maxHeight = Math.max(maxHeight, Number(height || 0)) |
| | | } |
| | | const centerLon = sumLon / positionsData.length |
| | | const centerLat = sumLat / positionsData.length |
| | | const boundingSphere = Cesium.BoundingSphere.fromPoints(positions) |
| | | // 暂时飞向 BoundingSphere |
| | | viewer.camera.flyToBoundingSphere(Cesium.BoundingSphere.fromPoints(positions), { |
| | | duration: 0, |
| | | offset: { |
| | | heading: Cesium.Math.toRadians(0), |
| | | pitch: Cesium.Math.toRadians(pitch), |
| | | range: boundingSphere.radius * multiple, |
| | | }, |
| | | complete: () => { |
| | | // 飞行完成后检查相机高度是否小于最高点 |
| | | const cameraHeight = Cesium.Cartographic.fromCartesian(viewer.camera.position).height |
| | | if (cameraHeight < maxHeight) { |
| | | viewer.camera.flyTo({ |
| | | destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, maxHeight + 100), // 加100米缓冲 |
| | | duration: 1.5, |
| | | orientation: { |
| | | heading: viewer.camera.heading, |
| | | pitch: viewer.camera.pitch, |
| | | roll: viewer.camera.roll, |
| | | }, |
| | | }) |
| | | } |
| | | }, |
| | | }) |
| | | } |
| | | |
| | | // 获取视口地图中心点 |
| | | export function getMapCenterPoint(viewer) { |
| | | const centerResult = viewer.camera.pickEllipsoid( |
| | |
| | | <el-col :span="4"> |
| | | <el-form-item label="类型" prop="deviceType"> |
| | | <el-select v-model="searchParams.deviceType" placeholder="请选择" clearable @change="handleSearch"> |
| | | <el-option |
| | | v-for="item in dictObj.deviceType" |
| | | :key="item.dictKey" |
| | | :label="item.dictValue" |
| | | :value="item.dictKey" |
| | | /> |
| | | <el-option v-for="item in dictObj.deviceType" :key="item.dictKey" :label="item.dictValue" |
| | | :value="item.dictKey" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="部门" prop="belongDept"> |
| | | <el-tree-select |
| | | v-model="searchParams.belongDept" |
| | | :data="deptTree" |
| | | :props="treeProps" |
| | | node-key="id" |
| | | check-strictly |
| | | clearable |
| | | @change="handleSearch" |
| | | /> |
| | | <el-tree-select v-model="searchParams.belongDept" :data="deptTree" :props="treeProps" |
| | | node-key="id" check-strictly clearable @change="handleSearch" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="时间"> |
| | | <el-date-picker |
| | | popper-class="command-date-picker-popper" |
| | | class="command-date-picker" |
| | | v-model="dateRange" |
| | | type="daterange" |
| | | range-separator="至" |
| | | start-placeholder="开始日期" |
| | | end-placeholder="结束日期" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | @change="handleSearch" |
| | | /> |
| | | <el-date-picker popper-class="command-date-picker-popper" class="command-date-picker" |
| | | v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" |
| | | end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:ss" @change="handleSearch" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | |
| | | </el-table-column> |
| | | </el-table> |
| | | <div> |
| | | <el-pagination |
| | | v-model:current-page="searchParams.current" |
| | | v-model:page-size="searchParams.size" |
| | | :total="total" |
| | | @change="getList" |
| | | /> |
| | | <el-pagination v-model:current-page="searchParams.current" v-model:page-size="searchParams.size" |
| | | :total="total" @change="getList" /> |
| | | </div> |
| | | |
| | | <FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" @success="getList" /> |
| | |
| | | provide('deptTree', deptTree) |
| | | |
| | | // 获取列表 |
| | | async function getList() { |
| | | async function getList () { |
| | | const range = dateRangeFormat(dateRange.value) |
| | | loading.value = true |
| | | try { |
| | |
| | | } |
| | | |
| | | // 查询 |
| | | function handleSearch() { |
| | | function handleSearch () { |
| | | searchParams.value.current = 1 |
| | | getList() |
| | | } |
| | | |
| | | // 重置查询 |
| | | function resetForm() { |
| | | function resetForm () { |
| | | dateRange.value = [] |
| | | queryParamsRef.value?.resetFields() |
| | | searchParams.value.current = 1 |
| | |
| | | } |
| | | |
| | | // 删除 |
| | | async function handleDelete(row) { |
| | | async function handleDelete (row) { |
| | | const tips = row ? '该条' : '选中的项' |
| | | await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' }) |
| | | await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { |
| | | type: 'warning', |
| | | customClass: 'command-page-view-message-box', |
| | | confirmButtonClass: 'command-message-box-confirm', |
| | | cancelButtonClass: 'command-message-box-cancel', |
| | | }) |
| | | const ids = row ? row.id : selectedIds.value.join(',') |
| | | await fwDeviceRemoveApi({ ids }) |
| | | ElMessage.success('删除成功') |
| | |
| | | } |
| | | |
| | | // 勾选值设置 |
| | | function handleSelectionChange(rows) { |
| | | function handleSelectionChange (rows) { |
| | | selectedIds.value = rows.map(item => item.id) |
| | | } |
| | | |
| | | // 导出 |
| | | function exportFile() { |
| | | function exportFile () { |
| | | exportLoading.value = true |
| | | exportFwDeviceApi() |
| | | .then(res => { |
| | |
| | | } |
| | | |
| | | // 获取字典 |
| | | function getDictList() { |
| | | function getDictList () { |
| | | getDictionaryByCode('deviceType,deviceAtt').then(res => { |
| | | dictObj.value = res.data.data |
| | | }) |
| | | } |
| | | |
| | | // 获取部门树 |
| | | function getDeptTreeFun() { |
| | | function getDeptTreeFun () { |
| | | getDeptTree().then(res => { |
| | | deptTree.value = res.data.data |
| | | }) |
| | | } |
| | | |
| | | // 新增/编辑/查看 弹框 |
| | | function openForm(mode, row) { |
| | | function openForm (mode, row) { |
| | | dialogVisible.value = true |
| | | nextTick(() => { |
| | | dialogRef.value?.open({ mode, row }) |
| | |
| | | // 删除 |
| | | async function handleDelete (row) { |
| | | const tips = row ? '该条' : '选中的项' |
| | | await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' }) |
| | | await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { |
| | | type: 'warning', |
| | | customClass: 'command-page-view-message-box', |
| | | confirmButtonClass: 'command-message-box-confirm', |
| | | cancelButtonClass: 'command-message-box-cancel', |
| | | }) |
| | | const ids = row ? row.id : selectedIds.value.join(',') |
| | | await fwAreaDivideRemoveApi({ ids }) |
| | | ElMessage.success('删除成功') |
| | |
| | | params: { descs: 'update_time', ...params }, |
| | | }) |
| | | } |
| | | // 查list |
| | | // 查list --- 区域 |
| | | export const fwAreaDivideListApi = params => { |
| | | return request({ |
| | | url: `/drone-fw/area/fwAreaDivide/list`, |
| | |
| | | // 删除 |
| | | async function handleDelete(row) { |
| | | const tips = row ? '该条' : '选中的项' |
| | | await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' }) |
| | | await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { |
| | | type: 'warning', |
| | | customClass: 'command-page-view-message-box', |
| | | confirmButtonClass: 'command-message-box-confirm', |
| | | cancelButtonClass: 'command-message-box-cancel', |
| | | }) |
| | | const ids = row ? row.id : selectedIds.value.join(',') |
| | | await fwDeviceRemoveApi({ ids }) |
| | | ElMessage.success('删除成功') |
| | |
| | | <div class="val">{{ formData.deviceSn }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">场景</div> |
| | | <div class="val">{{ formData.defenseSceneName }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">区域</div> |
| | | <div class="val">{{ formData.areaDivideName }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">场景</div> |
| | | <div class="val">{{ formData.defenseSceneName }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">调度人员</div> |
| | |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="场景" prop="defenseSceneId"> |
| | | <el-select class="command-select" popper-class="command-select-popper" |
| | | v-model="formData.defenseSceneId" placeholder="请选择" clearable @change="handleSceneChange"> |
| | | <el-option v-for="item in sceneList" :key="item.id" :label="item.sceneName" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="12"> |
| | | <el-form-item label="区域" prop="areaDivideId"> |
| | | <el-select class="command-select" popper-class="command-select-popper" |
| | | v-model="formData.areaDivideId" placeholder="请先选择场景" clearable |
| | | :disabled="!formData.defenseSceneId" @change="handleAreaChange"> |
| | | v-model="formData.areaDivideId" placeholder="请选择" clearable |
| | | @change="handleAreaChange"> |
| | | <el-option v-for="item in areaList" :key="item.id" :label="item.areaName" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="12"> |
| | | <el-form-item label="调度人员" prop="dispatchUser"> |
| | | <el-input class="command-input" v-model="formData.dispatchUser" maxlength="50" placeholder="请输入" |
| | | clearable /> |
| | | <el-form-item label="场景" prop="defenseSceneId"> |
| | | <el-select class="command-select" popper-class="command-select-popper" |
| | | v-model="formData.defenseSceneId" placeholder="请先选择区域" clearable |
| | | :disabled="!formData.areaDivideId" @change="handleSceneChange"> |
| | | <el-option v-for="item in sceneList" :key="item.id" :label="item.sceneName" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="12"> |
| | | <el-form-item label="调度日期" prop="dispatchDateRange"> |
| | | <el-date-picker class="command-date-picker" popper-class="command-date-picker-popper" |
| | | v-model="formData.dispatchDateRange" type="datetimerange" range-separator="至" |
| | | start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:ss" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="24"> |
| | | <el-form-item label="调度位置" prop="longitude"> |
| | | <!-- <el-button @click="selectLocation" style="width: 100%">--> |
| | | <el-button style="width: 100%"> |
| | |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="12"> |
| | | <el-form-item label="调度人员" prop="dispatchUser"> |
| | | <el-input class="command-input" v-model="formData.dispatchUser" maxlength="50" placeholder="请输入" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="调度日期" prop="dispatchDateRange"> |
| | | <el-date-picker class="command-date-picker" popper-class="command-date-picker-popper" |
| | | v-model="formData.dispatchDateRange" type="daterange" range-separator="至" |
| | | start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:ss" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="24"> |
| | | <el-form-item label="调度内容" prop="dispatchContent"> |
| | | <el-input class="command-input" v-model="formData.dispatchContent" type="textarea" :rows="3" |
| | |
| | | import { ElMessage } from 'element-plus' |
| | | import { fwTaskScheduleDetailApi, fwTaskScheduleSubmitApi } from './taskScheduleApi' |
| | | import { fwAreaDivideListApi } from '@/views/areaManage/partition/partitionApi' |
| | | import { fwDefenseSceneListApi } from '@/views/areaManage/sceneConfig/sceneConfigApi' |
| | | import { fieldRules } from '@ztzf/utils' |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium' |
| | | import * as Cesium from 'cesium' |
| | |
| | | }) |
| | | |
| | | const props = defineProps({ |
| | | sceneList: { type: Array, default: () => [] }, |
| | | deviceList: { type: Array, default: () => [] }, |
| | | }) |
| | | |
| | |
| | | const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' }) |
| | | const tempLocation = ref({ longitude: null, latitude: null }) |
| | | const areaList = ref([]) // 区域列表 |
| | | const sceneList = ref([]) // 场景列表 |
| | | const deviceStatus = ref('') |
| | | |
| | | const rules = { |
| | |
| | | } |
| | | } |
| | | |
| | | // 场景选择变化处理 |
| | | async function handleSceneChange (sceneId) { |
| | | formData.value.areaDivideId = '' |
| | | if (sceneId) { |
| | | await loadAreaList(sceneId) |
| | | async function handleAreaChange (areaId) { |
| | | formData.value.defenseSceneId = '' |
| | | if (areaId) { |
| | | await loadSceneList(areaId) |
| | | } else { |
| | | areaList.value = [] |
| | | sceneList.value = [] |
| | | } |
| | | } |
| | | |
| | | function handleAreaChange (areaId) { |
| | | const find = areaList.value.find(item => item.id === areaId) |
| | | // 场景选择变化处理 |
| | | async function handleSceneChange (sceneId) { |
| | | const find = sceneList.value.find(item => item.id === sceneId) |
| | | formData.value.longitude = find?.longitude |
| | | formData.value.latitude = find?.latitude |
| | | } |
| | | |
| | | // 加载区域列表 |
| | | async function loadAreaList (sceneId) { |
| | | const res = await fwAreaDivideListApi({ sceneId }) |
| | | async function loadAreaList () { |
| | | const res = await fwAreaDivideListApi() |
| | | areaList.value = res?.data?.data ?? [] |
| | | } |
| | | |
| | | // 加载场景列表 |
| | | async function loadSceneList(areaId) { |
| | | const res = await fwDefenseSceneListApi({areaId}) |
| | | sceneList.value = res?.data?.data ?? [] |
| | | } |
| | | |
| | | // 关闭弹框 |
| | |
| | | } |
| | | |
| | | defineExpose({ open }) |
| | | |
| | | onMounted(() => { |
| | | loadAreaList() |
| | | }) |
| | | </script> |
| | | <style scoped lang="scss"> |
| | | #mapContainer { |
| | |
| | | <el-table-column type="index" show-overflow-tooltip width="64" label="序号" /> |
| | | <el-table-column prop="deviceName" show-overflow-tooltip width="140" label="设备名称" /> |
| | | <el-table-column prop="deviceSn" show-overflow-tooltip width="140" label="设备编码" /> |
| | | |
| | | |
| | | <el-table-column prop="status" show-overflow-tooltip width="100" label="设备状态"> |
| | | <template v-slot="{ row }"> |
| | | {{ getDictLabel(row.deviceStatus, dictObj.deviceStatus) }} |
| | |
| | | <el-table-column prop="dispatchUser" show-overflow-tooltip width="100" label="调度人员" /> |
| | | <el-table-column show-overflow-tooltip label="调度日期"> |
| | | <template v-slot="{ row }"> |
| | | {{ formatDispatchDate(row) }} |
| | | {{dayjs(row.dispatchStartTime).format('YYYY-MM-DD')}}至{{dayjs(row.dispatchEndTime).format('YYYY-MM-DD')}} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="dispatchContent" show-overflow-tooltip width="160" label="调度内容" /> |
| | |
| | | @success="getList" |
| | | v-if="dialogVisible" |
| | | v-model="dialogVisible" |
| | | :sceneList="sceneList" |
| | | :deviceList="deviceList" |
| | | /> |
| | | </basic-container> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import dayjs from 'dayjs' |
| | | import { Search, RefreshRight, Plus, Delete } from '@element-plus/icons-vue' |
| | | import { nextTick, onMounted, ref, provide } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { fwTaskSchedulePageApi, fwTaskScheduleRemoveApi } from './taskScheduleApi' |
| | | import { fwDefenseSceneListApi } from '@/views/areaManage/sceneConfig/sceneConfigApi' |
| | | import { fwDeviceListApi } from '@/views/basicManage/deviceStock/fwDevice' |
| | | import { getDictionaryByCode } from '@/api/system/dictbiz' |
| | | import FormDiaLog from './FormDiaLog.vue' |
| | |
| | | deviceType: [], // 设备类型 |
| | | deviceStatus: [], // 设备状态 |
| | | }) |
| | | const sceneList = ref([]) // 场景列表 |
| | | const deviceList = ref([]) // 设备列表 |
| | | |
| | | // 注入字典到子组件 |
| | |
| | | // 删除 |
| | | async function handleDelete(row) { |
| | | const tips = row ? '该条' : '选中的项' |
| | | await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' }) |
| | | await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { |
| | | type: 'warning', |
| | | customClass: 'command-page-view-message-box', |
| | | confirmButtonClass: 'command-message-box-confirm', |
| | | cancelButtonClass: 'command-message-box-cancel', |
| | | }) |
| | | const ids = row ? row.id : selectedIds.value.join(',') |
| | | await fwTaskScheduleRemoveApi({ ids }) |
| | | ElMessage.success('删除成功') |
| | |
| | | }) |
| | | } |
| | | |
| | | // 加载场景列表 |
| | | async function loadSceneList() { |
| | | const res = await fwDefenseSceneListApi() |
| | | sceneList.value = res?.data?.data ?? [] |
| | | } |
| | | |
| | | // 加载设备列表 |
| | | async function loadDeviceList() { |
| | | const res = await fwDeviceListApi() |
| | |
| | | |
| | | onMounted(() => { |
| | | getDictList() |
| | | loadSceneList() |
| | | loadDeviceList() |
| | | getList() |
| | | }) |
| | |
| | | <div id="deviceJobDetailsMap" class="command-cesium"></div> |
| | | </template> |
| | | <script setup> |
| | | import { flyVisual } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | import _ from 'lodash' |
| | | |
| | | import * as Cesium from 'cesium' |
| | |
| | | import { dataFolderApi } from '@/api/layer/index'; |
| | | import folderFile from '@/views/layerManagement/components/folderFile.vue'; |
| | | import { parseGeoDataToPositions } from '@/utils/geoParseUtil'; |
| | | import { flyVisual } from '@/utils/cesium/mapUtil'; |
| | | import { flyVisual } from '@ztzf/utils'; |
| | | import rightEdit from '@/views/layerManagement/components/rightEdit.vue'; |
| | | import leftList from '@/views/layerManagement/components/leftList.vue'; |
| | | import { DrawPolygon } from '@/views/layerManagement/components/utils'; |
| | |
| | | import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'; |
| | | import {Delete, Refresh, Search} from '@element-plus/icons-vue'; |
| | | import { ArrowLineMaterialProperty } from '@/utils/cesium/Material' |
| | | import { flyVisual } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | import rwqfdImg from '@/assets/images/signMachineNest/rwqfd.png' |
| | | import endPointImg from '@/assets/images/EndPointicon.png' |
| | | import { nextTick } from 'vue'; |
| | |
| | | height: 500px; |
| | | width: 100%; |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | <div class="right-container"> |
| | | <div class="header"> |
| | | <span>{{ dialogTitle }}</span> |
| | | |
| | | |
| | | <el-icon class="close-btn" @click.stop="visible = false"><Close /></el-icon> |
| | | </div> |
| | | |
| | | |
| | | <div class="content"> |
| | | <div class="detail-title">轨迹信息</div> |
| | | <el-row> |
| | |
| | | import { fwDroneFlightRecordDetailPageApi } from '@/views/recordManage/historyTracks/flyTrajectory' |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium' |
| | | import * as Cesium from 'cesium' |
| | | import { flyVisual } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | import { ArrowLineMaterialProperty } from '@/utils/cesium/Material' |
| | | import droneIcon from '@/assets/images/dataCockpit/map/drone.png' |
| | | |
| | |
| | | <div class="table-overlay"> |
| | | <div class="table-content"> |
| | | <div class="tabname">图斑列表</div> |
| | | <div class="tabBoxLoading" |
| | | <div class="tabBoxLoading" |
| | | v-loading="tableLoading" |
| | | element-loading-text="加载中..." |
| | | element-loading-text="加载中..." |
| | | element-loading-background="rgba(0, 0, 0, 0.1)" |
| | | > |
| | | <el-table |
| | | v-if="tableData.length > 0" |
| | | |
| | | |
| | | ref="polygonTableEle" |
| | | highlight-current-row |
| | | :row-class-name="tableRowClassName" |
| | | :data="tableData" |
| | | @row-click="handleLocationPolygon" |
| | | |
| | | |
| | | > |
| | | <el-table-column type="index" align="center" :width="pxToRemNum(30)" label="序号"> |
| | | <template #default="{ $index }"> |
| | | {{ ($index + 1).toString().padStart(2, '0') }} |
| | | {{ ($index + 1).toString().padStart(2, '0') }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="dkbh" align="center" label="图斑名称" show-overflow-tooltip /> |
| | |
| | | {{ isEditing && selectionIds === scope.row.id ? '取消编辑' : '编辑' }} |
| | | </span> --> |
| | | <template v-if="scope.row.is_exception == 2"> |
| | | <el-button |
| | | <el-button |
| | | v-if="!isEditing || selectionIds !== scope.row.id" |
| | | icon="el-icon-edit" |
| | | link |
| | | icon="el-icon-edit" |
| | | link |
| | | @click.stop="handleSelectionChange(scope.row)" |
| | | /> |
| | | <el-button |
| | | <el-button |
| | | v-else |
| | | icon="el-icon-circle-close" |
| | | link |
| | | icon="el-icon-circle-close" |
| | | link |
| | | @click.stop="handleSelectionChange(scope.row)" |
| | | |
| | | |
| | | /> |
| | | </template> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | |
| | | </div> |
| | | </div> |
| | | <!--绘制按钮--> |
| | |
| | | ref="drawPolygonRef" |
| | | v-if="isEditing" |
| | | @upDateDrawState="handleUpDateDrawState" |
| | | |
| | | |
| | | /> |
| | | <!-- 完成/取消 --> |
| | | <div class="btnGroups" v-if="props.title === '图斑编辑'"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { flyVisual } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | import { pxToRem, pxToRemNum } from '@/utils/rem' |
| | | import DrawPolygon from '@/views/resource/components/DrawPolygon.vue'; |
| | | import { ElMessage, ElMessageBox } from 'element-plus'; |
| | |
| | | const isInit = ref(true); |
| | | // 图斑管理表格 |
| | | const getTableList = () => { |
| | | tableLoading.value = true; |
| | | tableLoading.value = true; |
| | | const requestParams = { |
| | | patchesInfoId: props.detailid, |
| | | }; |
| | |
| | | // 地图 |
| | | const initMap = () => { |
| | | if (!document.getElementById('spotMap') || isMapReady.value) return; |
| | | |
| | | |
| | | publicCesiumInstance = new PublicCesium({ |
| | | dom: 'spotMap', |
| | | flatMode: false, |
| | |
| | | layerMode: 4, |
| | | contour: false, |
| | | }); |
| | | |
| | | |
| | | homeViewer.value = publicCesiumInstance.getViewer(); |
| | | viewer = publicCesiumInstance.getViewer(); |
| | | viewInstance.value = publicCesiumInstance; |
| | | viewInstance.value = publicCesiumInstance; |
| | | viewer.scene.globe.depthTestAgainstTerrain = true; |
| | | viewInstance.value.switchContour(true); |
| | | |
| | | // 确保 readyPromise 存在 |
| | | if (viewer.readyPromise) { |
| | | viewer.readyPromise.then(() => { |
| | | isMapReady.value = true; |
| | | isMapReady.value = true; |
| | | getTableList(); |
| | | }).catch((error) => { |
| | | console.error('地图加载失败:', error); |
| | |
| | | // 仅弹框初始化时执行的定位逻辑 |
| | | const initMapLocation = () => { |
| | | if (tbJwdList.length === 0 || !homeViewer.value) return; |
| | | |
| | | |
| | | // 计算所有图斑的包围球(用于初始化定位) |
| | | const allPositions = tbJwdList.flatMap(item => |
| | | const allPositions = tbJwdList.flatMap(item => |
| | | Cesium.Cartesian3.fromDegreesArray(item.grouped) |
| | | ); |
| | | const boundingSphere = Cesium.BoundingSphere.fromPoints(allPositions); |
| | | |
| | | |
| | | // 初始化定位(仅执行一次) |
| | | homeViewer.value.camera.flyToBoundingSphere(boundingSphere, { |
| | | duration: 0, |
| | | offset: new Cesium.HeadingPitchRange( |
| | | Cesium.Math.toRadians(0), |
| | | Cesium.Math.toRadians(0), |
| | | Cesium.Math.toRadians(-90) |
| | | ), |
| | | }); |
| | |
| | | if (!isMapReady.value || !viewer) return; |
| | | viewer?.entities.removeAll(); |
| | | tbJwdList = []; // 重置经纬度列表 |
| | | |
| | | |
| | | tableData.value.forEach(item => { |
| | | const numbersWithCommas = item.dkfw.match(/\d+(\.\d+)?/g); |
| | | if (!numbersWithCommas) return; |
| | |
| | | tbJwdList.push({ ...item, grouped }); // 存储经纬度用于后续操作 |
| | | |
| | | // 绘制图斑(保留原有逻辑,仅移除定位相关代码) |
| | | const fillColor = item.is_exception === 2 |
| | | ? Cesium.Color.RED.withAlpha(0.5) |
| | | const fillColor = item.is_exception === 2 |
| | | ? Cesium.Color.RED.withAlpha(0.5) |
| | | : Cesium.Color.YELLOW.withAlpha(0.5); |
| | | const outlineColor = item.is_exception === 2 |
| | | ? Cesium.Color.RED |
| | | const outlineColor = item.is_exception === 2 |
| | | ? Cesium.Color.RED |
| | | : Cesium.Color.YELLOW; |
| | | |
| | | homeViewer.value?.entities?.add({ |
| | |
| | | for (let i = 0; i < numbersWithCommas.length; i += 2) { |
| | | const lon = Number(numbersWithCommas[i]); |
| | | const lat = Number(numbersWithCommas[i + 1]); |
| | | positionsData.push([lon, lat, 10]); |
| | | positionsData.push([lon, lat, 10]); |
| | | } |
| | | flyVisual({ |
| | | positionsData: positionsData, |
| | | viewer: homeViewer.value, |
| | | multiple: 15, |
| | | |
| | | positionsData: positionsData, |
| | | viewer: homeViewer.value, |
| | | multiple: 15, |
| | | |
| | | }); |
| | | } |
| | | } |
| | |
| | | |
| | | // 定位到选中的图斑 |
| | | handleLocationPolygon(row); |
| | | |
| | | |
| | | // 仅异常图斑可编辑 |
| | | if (row.is_exception !== 2) { |
| | | ElMessage.warning('仅异常图斑支持编辑绘制'); |
| | |
| | | |
| | | watch(uploadPatchDialog, newVal => { |
| | | if (newVal) { |
| | | isInit.value = true; |
| | | isMapReady.value = false; |
| | | isInit.value = true; |
| | | isMapReady.value = false; |
| | | setTimeout(() => { |
| | | initMap(); |
| | | getspotManagementTableApi(); |
| | | initMap(); |
| | | getspotManagementTableApi(); |
| | | }, 0); |
| | | } else { |
| | | tableLoading.value = false; |
| | | tableData.value = []; |
| | | tableData.value = []; |
| | | isEditing.value = false; |
| | | handleUpDateDrawState(false); |
| | | destroyMap(); |
| | | isInit.value = false; |
| | | isInit.value = false; |
| | | clearSelect(); |
| | | isMapReady.value = false; |
| | | isMapReady.value = false; |
| | | } |
| | | refreshonload(); |
| | | }); |
| | |
| | | } |
| | | :global(.spotDialog .el-dialog__body) { |
| | | padding: 2rem 2rem 0 !important; |
| | | |
| | | |
| | | } |
| | | .container { |
| | | display: flex; |
| | |
| | | |
| | | // 选中行 |
| | | .current-row td { |
| | | |
| | | |
| | | background-color: rgba(64, 158, 255,0.3) !important; |
| | | } |
| | | } |
| | |
| | | width: 100%; |
| | | } |
| | | .el-button { |
| | | padding: 0; |
| | | color: #fff; |
| | | padding: 0; |
| | | color: #fff; |
| | | width: 17px; |
| | | } |
| | | </style> |
| | |
| | | |
| | | import PlanarRouteLineList from '@/components/PlanarRouteLineList/PlanarRouteLineList.vue' |
| | | |
| | | import { flyVisual } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | |
| | | import * as Cesium from 'cesium' |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium' |
| | |
| | | }) |
| | | viewer.value = viewInstance.value.getViewer() |
| | | if (props.showAdminBoundary) { |
| | | console.log(props.zoomToBoundary,'props.zoomToBoundary') |
| | | adminBoundarySources = await loadJaAdminBoundary(viewer.value, { |
| | | zoomTo: props.zoomToBoundary, |
| | | }) |
| | |
| | | const getMap = () => ({ viewer: viewer.value, publicCesium: viewInstance.value }) |
| | | const getViewer = () => viewer.value |
| | | const getPublicCesium = () => viewInstance.value |
| | | const flyBoundary = () => adminBoundarySources.flyBoundary() |
| | | |
| | | defineExpose({ getMap, getViewer, getPublicCesium }) |
| | | defineExpose({ getMap, getViewer, getPublicCesium,flyBoundary }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | |
| | | import { Cartesian3, Terrain, Viewer } from 'cesium'; |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium'; |
| | | import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'; |
| | | import { flyVisual } from '@/utils/cesium/mapUtil'; |
| | | import { flyVisual } from '@ztzf/utils'; |
| | | import * as turf from '@turf/turf'; |
| | | |
| | | import { nextTick, onBeforeUnmount, onMounted, onUnmounted } from 'vue'; |
| | |
| | | import endPointImg from '@/assets/images/EndPointicon.png' |
| | | |
| | | import { ArrowLineMaterialProperty } from '@/utils/cesium/Material' |
| | | import { flyVisual } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | |
| | | import { useStore } from 'vuex' |
| | | |
| | |
| | | import * as turf from '@turf/turf' |
| | | import { boxTransformScale } from '@/utils/turfFunc' |
| | | import { ElMessage } from 'element-plus' |
| | | import { flyVisual, getPointPositionsHeight } from '@/utils/cesium/mapUtil' |
| | | import { getPointPositionsHeight } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | |
| | | /** |
| | | * 多边形绘制与编辑工具类 |
| | |
| | | viewer.dataSources.add(labelSource) |
| | | |
| | | if (config.zoomTo) { |
| | | await viewer.flyTo(boundarySource, { |
| | | flyBoundary() |
| | | } |
| | | |
| | | const flyBoundary = () => { |
| | | viewer.flyTo(boundarySource, { |
| | | duration: 0, |
| | | offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-75), 0), |
| | | }) |
| | | } |
| | | |
| | | return { boundarySource, lineSource, labelSource } |
| | | return { boundarySource, lineSource, labelSource,flyBoundary } |
| | | } |
| | | |
| | | export const removeJaAdminBoundary = (viewer, sources) => { |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 飞到中心点并且所有点在可视范围 |
| | | * @param positionsData 二维数组,每项为 [lon, lat] 或 三维数组 [lon, lat, height] |
| | | * @param viewer Cesium.Viewer 实例 |
| | | * @param multiple 缩放倍数-默认为4 |
| | | * @param pitch 俯仰角-默认为-90 |
| | | */ |
| | | export function flyVisual({ positionsData, viewer, multiple = 4, pitch = -90 }) { |
| | | if (!Array.isArray(positionsData) || positionsData.length === 0) return |
| | | |
| | | // 如果是一个点,加两个点方便后续生成外包围盒 |
| | | if (positionsData.length === 1) { |
| | | const [lon, lat, height = 0] = positionsData[0] |
| | | positionsData = [ |
| | | [lon + 0.001, lat + 0.001, height], |
| | | [lon - 0.001, lat - 0.001, height], |
| | | [lon, lat, height], |
| | | ] |
| | | } |
| | | const positions = positionsData.map(([lon, lat, height = 0]) => |
| | | Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat), Number(height || 0)) |
| | | ) |
| | | |
| | | // 计算最高高度和中心点经纬度 |
| | | let maxHeight = -Infinity |
| | | let sumLon = 0, |
| | | sumLat = 0 |
| | | for (const [lon, lat, height] of positionsData) { |
| | | sumLon += Number(lon) |
| | | sumLat += Number(lat) |
| | | maxHeight = Math.max(maxHeight, Number(height || 0)) |
| | | } |
| | | const centerLon = sumLon / positionsData.length |
| | | const centerLat = sumLat / positionsData.length |
| | | const boundingSphere = Cesium.BoundingSphere.fromPoints(positions) |
| | | // 暂时飞向 BoundingSphere |
| | | viewer.camera.flyToBoundingSphere(Cesium.BoundingSphere.fromPoints(positions), { |
| | | duration: 0, |
| | | offset: { |
| | | heading: Cesium.Math.toRadians(0), |
| | | pitch: Cesium.Math.toRadians(pitch), |
| | | range: boundingSphere.radius * multiple, |
| | | }, |
| | | complete: () => { |
| | | // 飞行完成后检查相机高度是否小于最高点 |
| | | const cameraHeight = Cesium.Cartographic.fromCartesian(viewer.camera.position).height |
| | | if (cameraHeight < maxHeight) { |
| | | viewer.camera.flyTo({ |
| | | destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, maxHeight + 100), // 加100米缓冲 |
| | | duration: 1.5, |
| | | orientation: { |
| | | heading: viewer.camera.heading, |
| | | pitch: viewer.camera.pitch, |
| | | roll: viewer.camera.roll, |
| | | }, |
| | | }) |
| | | } |
| | | }, |
| | | }) |
| | | } |
| | | |
| | | // 获取视口地图中心点 |
| | | export function getMapCenterPoint(viewer) { |
| | |
| | | destroy-on-close |
| | | > |
| | | <div class="content" style="display: flex"> |
| | | <div class="processBox"> |
| | | <div class="processBox" v-if="dialogMode !== 'add'"> |
| | | <el-steps direction="vertical" :active="processList.length"> |
| | | <el-step v-for="item in processList" :title="item.flowName" :description="item.flowDesc" /> |
| | | </el-steps> |
| | |
| | | :layer-mode="4" |
| | | :boundary="false" |
| | | :zoomToBoundary="false" |
| | | @ready="mapReady" |
| | | /> |
| | | </div> |
| | | <div class="rightBox" v-if="!dialogReadonly"> |
| | |
| | | <script setup> |
| | | import { computed, ref, onMounted, nextTick } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { dateRangeFormat, fieldRules, geomAnalysis, getDictLabel } from '@ztzf/utils' |
| | | import { dateRangeFormat, fieldRules, flyVisual, geomAnalysis, getDictLabel } from '@ztzf/utils' |
| | | import { |
| | | gdWorkOrderDetailApi, |
| | | gdWorkOrderFlowListApi, |
| | |
| | | material: Cesium.Color.RED, |
| | | }, |
| | | }) |
| | | viewer.flyTo(mian, { duration: 0 }) |
| | | flyVisual({ |
| | | positionsData: pointList.map(i => [i.longitude, i.latitude, i.height || 0]), |
| | | viewer, |
| | | }) |
| | | } |
| | | |
| | | // 同步关联场景 |
| | |
| | | viewer = map?.viewer || null |
| | | } |
| | | |
| | | function mapReady() { |
| | | console.log(6363) |
| | | dialogMode.value === 'add' && mapRef.value.flyBoundary() |
| | | } |
| | | |
| | | defineExpose({ open }) |
| | | </script> |
| | | |
| | |
| | | <div class="table-overlay"> |
| | | <div class="table-content"> |
| | | <div class="tabname">图斑列表</div> |
| | | <div class="tabBoxLoading" |
| | | <div class="tabBoxLoading" |
| | | v-loading="tableLoading" |
| | | element-loading-text="加载中..." |
| | | element-loading-text="加载中..." |
| | | element-loading-background="rgba(0, 0, 0, 0.1)" |
| | | > |
| | | <el-table |
| | | v-if="tableData.length > 0" |
| | | |
| | | |
| | | ref="polygonTableEle" |
| | | highlight-current-row |
| | | :row-class-name="tableRowClassName" |
| | | :data="tableData" |
| | | @row-click="handleLocationPolygon" |
| | | |
| | | |
| | | > |
| | | <el-table-column type="index" align="center" :width="pxToRemNum(30)" label="序号"> |
| | | <template #default="{ $index }"> |
| | | {{ ($index + 1).toString().padStart(2, '0') }} |
| | | {{ ($index + 1).toString().padStart(2, '0') }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="dkbh" align="center" label="图斑名称" show-overflow-tooltip /> |
| | |
| | | {{ isEditing && selectionIds === scope.row.id ? '取消编辑' : '编辑' }} |
| | | </span> --> |
| | | <template v-if="scope.row.is_exception == 2"> |
| | | <el-button |
| | | <el-button |
| | | v-if="!isEditing || selectionIds !== scope.row.id" |
| | | icon="el-icon-edit" |
| | | link |
| | | icon="el-icon-edit" |
| | | link |
| | | @click.stop="handleSelectionChange(scope.row)" |
| | | /> |
| | | <el-button |
| | | <el-button |
| | | v-else |
| | | icon="el-icon-circle-close" |
| | | link |
| | | icon="el-icon-circle-close" |
| | | link |
| | | @click.stop="handleSelectionChange(scope.row)" |
| | | |
| | | |
| | | /> |
| | | </template> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | |
| | | </div> |
| | | </div> |
| | | <!--绘制按钮--> |
| | |
| | | ref="drawPolygonRef" |
| | | v-if="isEditing" |
| | | @upDateDrawState="handleUpDateDrawState" |
| | | |
| | | |
| | | /> |
| | | <!-- 完成/取消 --> |
| | | <div class="btnGroups" v-if="props.title === '图斑编辑'"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { flyVisual } from '@/utils/cesium/mapUtil' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | import { pxToRem, pxToRemNum } from '@/utils/rem' |
| | | import DrawPolygon from '@/views/resource/components/DrawPolygon.vue'; |
| | | import { ElMessage, ElMessageBox } from 'element-plus'; |
| | |
| | | const isInit = ref(true); |
| | | // 图斑管理表格 |
| | | const getTableList = () => { |
| | | tableLoading.value = true; |
| | | tableLoading.value = true; |
| | | const requestParams = { |
| | | patchesInfoId: props.detailid, |
| | | }; |
| | |
| | | // 地图 |
| | | const initMap = () => { |
| | | if (!document.getElementById('spotMap') || isMapReady.value) return; |
| | | |
| | | |
| | | publicCesiumInstance = new PublicCesium({ |
| | | dom: 'spotMap', |
| | | flatMode: false, |
| | |
| | | layerMode: 4, |
| | | contour: false, |
| | | }); |
| | | |
| | | |
| | | homeViewer.value = publicCesiumInstance.getViewer(); |
| | | viewer = publicCesiumInstance.getViewer(); |
| | | viewInstance.value = publicCesiumInstance; |
| | | viewInstance.value = publicCesiumInstance; |
| | | viewer.scene.globe.depthTestAgainstTerrain = true; |
| | | viewInstance.value.switchContour(true); |
| | | |
| | | // 确保 readyPromise 存在 |
| | | if (viewer.readyPromise) { |
| | | viewer.readyPromise.then(() => { |
| | | isMapReady.value = true; |
| | | isMapReady.value = true; |
| | | getTableList(); |
| | | }).catch((error) => { |
| | | console.error('地图加载失败:', error); |
| | |
| | | // 仅弹框初始化时执行的定位逻辑 |
| | | const initMapLocation = () => { |
| | | if (tbJwdList.length === 0 || !homeViewer.value) return; |
| | | |
| | | |
| | | // 计算所有图斑的包围球(用于初始化定位) |
| | | const allPositions = tbJwdList.flatMap(item => |
| | | const allPositions = tbJwdList.flatMap(item => |
| | | Cesium.Cartesian3.fromDegreesArray(item.grouped) |
| | | ); |
| | | const boundingSphere = Cesium.BoundingSphere.fromPoints(allPositions); |
| | | |
| | | |
| | | // 初始化定位(仅执行一次) |
| | | homeViewer.value.camera.flyToBoundingSphere(boundingSphere, { |
| | | duration: 0, |
| | | offset: new Cesium.HeadingPitchRange( |
| | | Cesium.Math.toRadians(0), |
| | | Cesium.Math.toRadians(0), |
| | | Cesium.Math.toRadians(-90) |
| | | ), |
| | | }); |
| | |
| | | if (!isMapReady.value || !viewer) return; |
| | | viewer?.entities.removeAll(); |
| | | tbJwdList = []; // 重置经纬度列表 |
| | | |
| | | |
| | | tableData.value.forEach(item => { |
| | | const numbersWithCommas = item.dkfw.match(/\d+(\.\d+)?/g); |
| | | if (!numbersWithCommas) return; |
| | |
| | | tbJwdList.push({ ...item, grouped }); // 存储经纬度用于后续操作 |
| | | |
| | | // 绘制图斑(保留原有逻辑,仅移除定位相关代码) |
| | | const fillColor = item.is_exception === 2 |
| | | ? Cesium.Color.RED.withAlpha(0.5) |
| | | const fillColor = item.is_exception === 2 |
| | | ? Cesium.Color.RED.withAlpha(0.5) |
| | | : Cesium.Color.YELLOW.withAlpha(0.5); |
| | | const outlineColor = item.is_exception === 2 |
| | | ? Cesium.Color.RED |
| | | const outlineColor = item.is_exception === 2 |
| | | ? Cesium.Color.RED |
| | | : Cesium.Color.YELLOW; |
| | | |
| | | homeViewer.value?.entities?.add({ |
| | |
| | | for (let i = 0; i < numbersWithCommas.length; i += 2) { |
| | | const lon = Number(numbersWithCommas[i]); |
| | | const lat = Number(numbersWithCommas[i + 1]); |
| | | positionsData.push([lon, lat, 10]); |
| | | positionsData.push([lon, lat, 10]); |
| | | } |
| | | flyVisual({ |
| | | positionsData: positionsData, |
| | | viewer: homeViewer.value, |
| | | multiple: 15, |
| | | |
| | | positionsData: positionsData, |
| | | viewer: homeViewer.value, |
| | | multiple: 15, |
| | | |
| | | }); |
| | | } |
| | | } |
| | |
| | | |
| | | // 定位到选中的图斑 |
| | | handleLocationPolygon(row); |
| | | |
| | | |
| | | // 仅异常图斑可编辑 |
| | | if (row.is_exception !== 2) { |
| | | ElMessage.warning('仅异常图斑支持编辑绘制'); |
| | |
| | | |
| | | watch(uploadPatchDialog, newVal => { |
| | | if (newVal) { |
| | | isInit.value = true; |
| | | isMapReady.value = false; |
| | | isInit.value = true; |
| | | isMapReady.value = false; |
| | | setTimeout(() => { |
| | | initMap(); |
| | | getspotManagementTableApi(); |
| | | initMap(); |
| | | getspotManagementTableApi(); |
| | | }, 0); |
| | | } else { |
| | | tableLoading.value = false; |
| | | tableData.value = []; |
| | | tableData.value = []; |
| | | isEditing.value = false; |
| | | handleUpDateDrawState(false); |
| | | destroyMap(); |
| | | isInit.value = false; |
| | | isInit.value = false; |
| | | clearSelect(); |
| | | isMapReady.value = false; |
| | | isMapReady.value = false; |
| | | } |
| | | refreshonload(); |
| | | }); |
| | |
| | | } |
| | | :global(.spotDialog .el-dialog__body) { |
| | | padding: 2rem 2rem 0 !important; |
| | | |
| | | |
| | | } |
| | | .container { |
| | | display: flex; |
| | |
| | | |
| | | // 选中行 |
| | | .current-row td { |
| | | |
| | | |
| | | background-color: rgba(64, 158, 255,0.3) !important; |
| | | } |
| | | } |
| | |
| | | width: 100%; |
| | | } |
| | | .el-button { |
| | | padding: 0; |
| | | color: #fff; |
| | | padding: 0; |
| | | color: #fff; |
| | | width: 17px; |
| | | } |
| | | </style> |
| | |
| | | return rules |
| | | } |
| | | |
| | | |
| | | export function getDictLabel(value, dictList = []) { |
| | | return dictList.filter(item => String(value).includes(String(item.dictKey))).map(item => item.dictValue).join(',') |
| | | if (value === null || value === undefined || value === '') return ''; |
| | | const values = String(value).split(','); |
| | | return dictList |
| | | .filter(item => values.includes(String(item.dictKey))) |
| | | .map(item => item.dictValue) |
| | | .join(','); |
| | | } |
| | | |
| | | export function blobDownload(res) { |
| | |
| | | /* |
| | | * @Author : yuan |
| | | * @Date : 2025-12-26 11:01:31 |
| | | * @LastEditors : yuan |
| | | * @LastEditTime : 2025-12-31 16:00:54 |
| | | * @FilePath : \packages\utils\map\index.js |
| | | * @Description : |
| | | * Copyright 2025 OBKoro1, All Rights Reserved. |
| | | * 2025-12-26 11:01:31 |
| | | */ |
| | | |
| | | import * as Cesium from 'cesium' |
| | | |
| | | |
| | | // 获取正射影像4至点 |
| | | export function getOrthoImageBoundaryPoints (data) { |
| | |
| | | east, |
| | | north |
| | | ) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 飞到中心点并且所有点在可视范围 |
| | | * @param positionsData 二维数组,每项为 [lon, lat] 或 三维数组 [lon, lat, height] |
| | | * @param viewer Cesium.Viewer 实例 |
| | | * @param multiple 缩放倍数-默认为4 |
| | | * @param pitch 俯仰角-默认为-90 |
| | | */ |
| | | export function flyVisual({ positionsData, viewer, multiple = 4, pitch = -90 }) { |
| | | if (!Array.isArray(positionsData) || positionsData.length === 0) return |
| | | |
| | | // 如果是一个点,加两个点方便后续生成外包围盒 |
| | | if (positionsData.length === 1) { |
| | | const [lon, lat, height = 0] = positionsData[0] |
| | | positionsData = [ |
| | | [lon + 0.001, lat + 0.001, height], |
| | | [lon - 0.001, lat - 0.001, height], |
| | | [lon, lat, height], |
| | | ] |
| | | } |
| | | const positions = positionsData.map(([lon, lat, height = 0]) => |
| | | Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat), Number(height || 0)) |
| | | ) |
| | | |
| | | // 计算最高高度和中心点经纬度 |
| | | let maxHeight = -Infinity |
| | | let sumLon = 0, |
| | | sumLat = 0 |
| | | for (const [lon, lat, height] of positionsData) { |
| | | sumLon += Number(lon) |
| | | sumLat += Number(lat) |
| | | maxHeight = Math.max(maxHeight, Number(height || 0)) |
| | | } |
| | | const centerLon = sumLon / positionsData.length |
| | | const centerLat = sumLat / positionsData.length |
| | | const boundingSphere = Cesium.BoundingSphere.fromPoints(positions) |
| | | // 暂时飞向 BoundingSphere |
| | | viewer.camera.flyToBoundingSphere(Cesium.BoundingSphere.fromPoints(positions), { |
| | | duration: 0, |
| | | offset: { |
| | | heading: Cesium.Math.toRadians(0), |
| | | pitch: Cesium.Math.toRadians(pitch), |
| | | range: boundingSphere.radius * multiple, |
| | | }, |
| | | complete: () => { |
| | | // 飞行完成后检查相机高度是否小于最高点 |
| | | const cameraHeight = Cesium.Cartographic.fromCartesian(viewer.camera.position).height |
| | | if (cameraHeight < maxHeight) { |
| | | viewer.camera.flyTo({ |
| | | destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, maxHeight + 100), // 加100米缓冲 |
| | | duration: 1.5, |
| | | orientation: { |
| | | heading: viewer.camera.heading, |
| | | pitch: viewer.camera.pitch, |
| | | roll: viewer.camera.roll, |
| | | }, |
| | | }) |
| | | } |
| | | }, |
| | | }) |
| | | } |