applications/drone-command/src/main.js
@@ -41,6 +41,7 @@ import dayjs from 'dayjs' import 'styles/common.scss' import * as Cesium from 'cesium' import _ from 'lodash' // 视频回放 import 'video.js/dist/video-js.css' @@ -81,13 +82,14 @@ location.reload() } else { if (localStorage.getItem("isReload")) { localStorage.removeItem("isReload") localStorage.removeItem('isReload') } } window.$crudCommon = crudCommon window.axios = axios window._ = _ const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) applications/drone-command/src/utils/cesium/publicCesium.js
@@ -159,7 +159,7 @@ terrainLoadCallback?.() }) } else { // 正则:第一位非0,第二位任意数字,后10位都是0 /*// 正则:第一位非0,第二位任意数字,后10位都是0 const re = /^[1-9]\d0{10}$/ let result = null @@ -186,7 +186,7 @@ }).then(terrainProvider => { this.viewer.terrainProvider = terrainProvider terrainLoadCallback?.() }) })*/ } } catch (error) { console.error('地形加载失败:', error) applications/drone-command/src/views/aICrudTemplate/FormDiaLog.vue
@@ -1,5 +1,5 @@ <template> <el-dialog v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close> <el-dialog v-model="visible" :title="titleEnum[dialogMode]" :close-on-click-modal="false"> <div v-if="readonly"> <el-row> <el-col :span="12"> @@ -123,7 +123,7 @@ const emit = defineEmits(['success']) const formRef = ref(null) // 表单实例 const formData = ref(initForm()) // 表单数据 const visible = ref(false) // 弹框显隐 const visible = defineModel() // 弹框显隐 const dialogMode = ref('add') // 弹框模式 const submitting = ref(false) // 提交中 const userList = ref([]) // 用户列表 @@ -179,15 +179,9 @@ formData.value = res?.data?.data ?? {} } // 关闭后重置 function handleClosed() { formData.value = initForm() } // 打开弹框 async function open({ mode, row } = {}) { dialogMode.value = mode || 'add' visible.value = true formData.value = dialogMode.value === 'add' ? initForm() : row dialogMode.value !== 'add' && (await loadDetail()) } applications/drone-command/src/views/aICrudTemplate/index.vue
@@ -56,7 +56,7 @@ </el-row> </el-form> <div> <el-button type="primary" @click="handleAdd">新增</el-button> <el-button type="primary" @click="openForm('add')">新增</el-button> <el-button type="primary" @click="exportFile" :loading="exportLoading">导出</el-button> <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button> </div> @@ -73,8 +73,8 @@ <el-table-column prop="deviceSpecification" label="规格" /> <el-table-column label="操作"> <template v-slot="{ row }"> <el-link @click="handleView(row)" type="primary">查看</el-link> <el-link @click="handleEdit(row)" type="warning">编辑</el-link> <el-link @click="openForm('view', row)" type="primary">查看</el-link> <el-link @click="openForm('edit', row)" type="warning">编辑</el-link> <el-link @click="handleDelete(row)" type="danger">删除</el-link> </template> </el-table-column> @@ -88,7 +88,7 @@ /> </div> <FormDiaLog ref="dialogRef" @success="getList" /> <FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" @success="getList" /> </basic-container> </template> @@ -115,6 +115,7 @@ const selectedIds = ref([]) // 勾选的设备ID列表 const queryParamsRef = ref(null) // 查询表单实例 const dialogRef = ref(null) // 弹框实例 const dialogVisible = ref(null) // 弹框实例 const dictObj = ref({ deviceType: [], //设备类型 deviceAtt: [], //设备属性 @@ -156,16 +157,6 @@ queryParamsRef.value?.resetFields() searchParams.value.current = 1 getList() } // 查看 function handleView(row) { dialogRef.value?.open({ mode: 'view', row: { ...row } }) } // 编辑 function handleEdit(row) { dialogRef.value?.open({ mode: 'edit', row: { ...row } }) } // 删除 @@ -210,9 +201,12 @@ }) } // 新增 function handleAdd() { dialogRef.value?.open({ mode: 'add' }) // 新增/编辑/查看 弹框 function openForm(mode, row) { dialogVisible.value = true nextTick(() => { dialogRef.value?.open({ mode, row }) }) } onMounted(() => { applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
@@ -1,5 +1,5 @@ <template> <el-dialog v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close> <el-dialog v-model="visible" :title="titleEnum[dialogMode]"> <div v-if="readonly"> <el-row> <el-col :span="12"> @@ -29,23 +29,15 @@ </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="经度" prop="longitude"> <el-input-number v-model="formData.longitude" max="50" placeholder="请输入" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="纬度" prop="latitude"> <el-input-number v-model="formData.latitude" max="50" placeholder="请输入" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="位置" prop="address"> <el-input v-model="formData.address" maxlength="50" placeholder="请输入" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="联系方式" prop="contactPhone"> <el-input v-model="formData.contactPhone" maxlength="50" placeholder="请输入" clearable /> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="位置" prop="longitude"> <el-button @click="selectLocation" style="width: 100%"> {{ formData.longitude }}, {{ formData.longitude }}, {{ formData.address }} </el-button> </el-form-item> </el-col> </el-row> @@ -56,14 +48,25 @@ 确定 </el-button> </template> <el-dialog v-model="visible1" title="选择位置" destroy-on-close @closed="positionClosed"> <div class="ztzf-cesium" id="mapContainer"></div> <template #footer> <el-button @click="handleMapCancel">取消</el-button> <el-button type="primary" @click="handleMapConfirm">确定</el-button> </template> </el-dialog> </el-dialog> </template> <script setup> import { computed, ref } from 'vue' import { computed, nextTick, ref } from 'vue' import { ElMessage } from 'element-plus' import { fwPoliceStationDetailApi, fwPoliceStationSubmitApi } from './precinctInfoApi' import { fieldRules } from '@ztzf/utils' import { PublicCesium } from '@/utils/cesium/publicCesium' import * as Cesium from 'cesium' import axios from 'axios' const initForm = () => ({ address: '', // 位置 @@ -77,11 +80,13 @@ const emit = defineEmits(['success']) const formRef = ref(null) // 表单实例 const formData = ref(initForm()) // 表单数据 const visible = ref(false) // 弹框显隐 const visible = defineModel() // 弹框显隐 const visible1 = ref(false) // 弹框显隐 const dialogMode = ref('add') // 弹框模式 const submitting = ref(false) // 提交中 const readonly = computed(() => dialogMode.value === 'view') const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' }) const tempLocation = ref({ longitude: null, latitude: null }) const rules = { address: fieldRules(true, 50), @@ -117,18 +122,98 @@ formData.value = res?.data?.data ?? {} } // 关闭后重置 function handleClosed() { formData.value = initForm() function LeftClickEvent(click) { const pos = click.position // Cartesian2 屏幕坐标 // 屏幕坐标 -> 椭球面坐标(不考虑地形/模型) const cartesian3 = viewer.camera.pickEllipsoid(pos, viewer.scene.globe.ellipsoid) if (!cartesian3) { return { longitude: 112, latitude: 23 } } // 椭球面坐标 -> 经纬度 const carto = Cesium.Cartographic.fromCartesian(cartesian3) const longitude = Cesium.Math.toDegrees(carto.longitude) const latitude = Cesium.Math.toDegrees(carto.latitude) tempLocation.value = { longitude, latitude } // 添加红点(全局仅保留一个)并渲染经纬度标签 if (!redPointEntity) { redPointEntity = viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(longitude, latitude), point: { color: Cesium.Color.RED, pixelSize: 10, }, label: { text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`, fillColor: Cesium.Color.WHITE, outlineColor: Cesium.Color.BLACK, outlineWidth: 2, style: Cesium.LabelStyle.FILL_AND_OUTLINE, verticalOrigin: Cesium.VerticalOrigin.BOTTOM, pixelOffset: new Cesium.Cartesian2(0, -12), }, }) } else { redPointEntity.position = Cesium.Cartesian3.fromDegrees(longitude, latitude) redPointEntity.label.text = `${longitude.toFixed(6)}, ${latitude.toFixed(6)}` } } function positionClosed() { redPointEntity = null } let viewer let redPointEntity async function selectLocation() { visible1.value = true await nextTick() const publicCesiumInstance = new PublicCesium({ dom: 'mapContainer', flatMode: false, terrain: true, layerMode: 4, boundary: false, }) publicCesiumInstance.addLeftClickEvent(null, LeftClickEvent) viewer = publicCesiumInstance.getViewer() } function handleMapCancel() { tempLocation.value = { longitude: null, latitude: null } visible1.value = false } async function getLocationName() { const tk = import.meta.env.VITE_APP_TDT_TOKEN const { latitude, longitude } = tempLocation.value const http = axios.create({ withCredentials: false }) const res = await http({ url: `https://api.tianditu.gov.cn/geocoder?postStr={'lon':${longitude},'lat':${latitude},'ver':1}&tk=${tk}`, method: 'get', }) formData.value.address = res.data.result.formatted_address } function handleMapConfirm() { if (tempLocation.value.longitude != null && tempLocation.value.latitude != null) { formData.value.longitude = _.round(tempLocation.value.longitude, 6) formData.value.latitude = _.round(tempLocation.value.latitude, 6) getLocationName() } visible1.value = false } // 打开弹框 async function open({ mode, row } = {}) { dialogMode.value = mode || 'add' visible.value = true formData.value = dialogMode.value === 'add' ? initForm() : row dialogMode.value !== 'add' && (await loadDetail()) } defineExpose({ open }) </script> <style scoped lang="scss"> #mapContainer { width: 100%; height: 500px; } </style> applications/drone-command/src/views/areaManage/precinctInfo/index.vue
@@ -16,7 +16,7 @@ </el-row> </el-form> <div> <el-button type="primary" @click="handleAdd">新增</el-button> <el-button type="primary" @click="openForm('add')">新增</el-button> <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button> </div> <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange"> @@ -30,7 +30,7 @@ <el-table-column prop="contactPhone" label="联系方式" /> <el-table-column label="操作"> <template v-slot="{ row }"> <el-link @click="handleEdit(row)" type="warning">编辑</el-link> <el-link @click="openForm('edit', row)" type="warning">编辑</el-link> <el-link @click="handleDelete(row)" type="danger">删除</el-link> </template> </el-table-column> @@ -44,7 +44,7 @@ /> </div> <FormDiaLog ref="dialogRef" @success="getList" /> <FormDiaLog ref="dialogRef" @success="getList" v-if="dialogVisible" v-model="dialogVisible" /> </basic-container> </template> @@ -66,6 +66,7 @@ const selectedIds = ref([]) // 勾选的ID列表 const queryParamsRef = ref(null) // 查询表单实例 const dialogRef = ref(null) // 弹框实例 const dialogVisible = ref(false) // 获取列表 async function getList() { @@ -92,16 +93,6 @@ getList() } // 查看 function handleView(row) { dialogRef.value?.open({ mode: 'view', row: { ...row } }) } // 编辑 function handleEdit(row) { dialogRef.value?.open({ mode: 'edit', row: { ...row } }) } // 删除 async function handleDelete(row) { const tips = row ? '该条' : '选中的项' @@ -119,8 +110,11 @@ } // 新增 function handleAdd() { dialogRef.value?.open({ mode: 'add' }) function openForm(mode, row) { dialogVisible.value = true nextTick(() => { dialogRef.value?.open({ mode, row }) }) } onMounted(() => { applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue
@@ -1,19 +1,24 @@ <template> <el-dialog v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close> <div> <div>无人机名称:{{ formData.droneName }}</div> <div>序列号: {{ formData.serialNo }}</div> <div>告警时间: {{ formData.alarmTime }}</div> <div>触发原因: {{ formData.triggerType }}</div> <div>停留时长: {{ formData.stayDuration }}</div> <div class="bodyBox"> <div class="leftMap ztzf-cesium" id="leftMapContainer"></div> <div class="rightInfo"> <div> <div>无人机名称:{{ formData.droneName }}</div> <div>序列号: {{ formData.serialNo }}</div> <div>告警时间: {{ formData.alarmTime }}</div> <div>触发原因: {{ formData.triggerType }}</div> <div>停留时长: {{ formData.stayDuration }}</div> </div> <el-table :data="list"> <el-table-column type="index" width="60" label="序号" /> <el-table-column label="经纬度"> <template #default="{ row }">{{ row.longitude }},{{ row.latitude }}</template> </el-table-column> <el-table-column prop="createTime" label="时间" /> </el-table> </div> </div> <el-table :data="list"> <el-table-column type="index" width="60" label="序号" /> <el-table-column prop="areaCode" label="areaCode" /> <el-table-column prop="createTime" label="createTime" /> <el-table-column prop="latitude" label="latitude" /> <el-table-column prop="longitude" label="longitude" /> </el-table> <template #footer> <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button> </template> @@ -24,6 +29,9 @@ import { computed, ref } from 'vue' import { fwDroneFlightRecordDetailApi } from '@/views/recordManage/historyTracks/fwDroneFlightRecord' import { fwDroneFlightRecordDetailPageApi } from '@/views/recordManage/historyTracks/flyTrajectory' import { PublicCesium } from '@/utils/cesium/publicCesium' import * as Cesium from 'cesium' import { flyVisual } from '@/utils/cesium/mapUtil' const initForm = () => ({ droneName: '', @@ -59,7 +67,28 @@ const res = await fwDroneFlightRecordDetailApi({ id: formData.value.id }) const res1 = await fwDroneFlightRecordDetailPageApi({ flightRecordId: formData.value.id }) list.value = res1.data.data.records ?? [] drawFlightPath() formData.value = res?.data?.data ?? {} } // 角度转c3 function degreesToC3({ longitude, latitude, height = 0 }) { return Cesium.Cartesian3.fromDegrees(longitude, latitude, height) } // 渲染航线 function drawFlightPath() { const positions = list.value.map(p => degreesToC3(p)) droneLineEntity && geometricSource.entities.remove(droneLineEntity) const color = Cesium.Color.fromCssColorString('#00D690') droneLineEntity = geometricSource.entities.add({ polyline: { positions: positions, width: 3, material: new Cesium.PolylineOutlineMaterialProperty({ color }), clampToGround: false, }, }) viewer.flyTo(droneLineEntity) } // 关闭后重置 @@ -67,10 +96,27 @@ formData.value = initForm() } let viewer, geometricSource, droneLineEntity function initMap() { const publicCesiumInstance = new PublicCesium({ dom: 'leftMapContainer', flatMode: false, terrain: true, layerMode: 4, boundary: false, }) viewer = publicCesiumInstance.getViewer() geometricSource = new Cesium.CustomDataSource('geometricSource') viewer.dataSources.add(geometricSource) } // 打开弹框 async function open({ mode, row } = {}) { dialogMode.value = mode || 'add' visible.value = true await nextTick(() => { initMap() }) if (dialogMode.value === 'add') { formData.value = initForm() } else { @@ -81,3 +127,16 @@ defineExpose({ open }) </script> <style scoped lang="scss"> .bodyBox { display: flex; .rightInfo { width: 50%; height: 500px; } .leftMap { width: 50%; height: 500px; } } </style> uniapps/work-app/src/pages/voiceCall/index.vue
@@ -2,7 +2,7 @@ <div class="voiceCallContainer" :style="{ paddingTop: topMargin + 'px' }"> <!-- 顶部搜索栏 --> <div class="searchBar"> <up-search placeholder="请输入关键字搜索" v-model="defaultParam.searchKeyword" :animation="true" :show-action="false"></up-search> <up-search placeholder="请输入关键字搜索" v-model="defaultParam.friendNickName" :animation="true" :show-action="false" @confirm="onSearch" @search="onSearch" @clear="handleClear"></up-search> </div> <!-- 联系人列表 --> @@ -43,16 +43,17 @@ <script setup> import { getStatusBarHeight } from '@/utils/common'; const topMargin = getStatusBarHeight() import { getPhoneBookListApi } from '@/api/voiceCall/index.js' import { onShow } from '@dcloudio/uni-app' import defaultAvatar from '/static/images/defaultAvatar.svg' import defaultAvatar from '/static/images/defaultAvatar.svg' // 联系人数据 const contacts = ref([]) const defaultParam =ref( { const defaultParam = ref({ current: 1, size: 10, searchKeyword:'' friendNickName: '' }) // 加载状态 @@ -62,19 +63,16 @@ // 获取通讯录 const getPhoneBookList = () => { if (loading.value || !hasMore.value) return loading.value = true // 添加分页参数 const params = { current: defaultParam.value.current, size: defaultParam.value.size, searchKeyword: defaultParam.value.searchKeyword friendNickName: defaultParam.value.friendNickName } getPhoneBookListApi(params).then(res => { const response = res.data.data console.log('通讯录', response) if (defaultParam.value.current === 1) { contacts.value = response.records } else { @@ -108,7 +106,18 @@ defaultParam.value.current++ getPhoneBookList() } const onSearch = () => { defaultParam.value.current = 1 hasMore.value = true getPhoneBookList() } const handleClear = () => { defaultParam.value.friendNickName = '' defaultParam.value.current = 1 hasMore.value = true getPhoneBookList() } // 拨打电话方法 const makeCall = (contact) => { const contactName = contact.friendNickName || '未知联系人' @@ -224,12 +233,13 @@ height: 60rpx; } .loadingMore { display: flex; justify-content: center; align-items: center; height: 80rpx; font-size: 28rpx; color: #999; } .loadingMore { display: flex; justify-content: center; align-items: center; height: 80rpx; font-size: 28rpx; color: #999; } </style>