mirror of
https://github.com/yudaocode/yudao-ui-admin-vue3.git
synced 2026-03-29 23:25:52 +00:00
feat(iot):【设备订单:50%】简化设备定位功能,支持 GeoLocation 自动更新,基于 calm-roaming-pillow.md
This commit is contained in:
@@ -21,7 +21,6 @@ export interface DeviceVO {
|
||||
mqttUsername: string // MQTT 用户名
|
||||
mqttPassword: string // MQTT 密码
|
||||
authType: string // 认证类型
|
||||
locationType: number // 定位类型
|
||||
latitude?: number // 设备位置的纬度
|
||||
longitude?: number // 设备位置的经度
|
||||
areaId: number // 地区编码
|
||||
@@ -49,7 +48,7 @@ export interface IotDevicePropertyRespVO {
|
||||
updateTime: Date // 更新时间
|
||||
}
|
||||
|
||||
// TODO @芋艿:调整到 constants
|
||||
// TODO @AI:调整到 constants
|
||||
// IoT 设备状态枚举
|
||||
export enum DeviceStateEnum {
|
||||
INACTIVE = 0, // 未激活
|
||||
|
||||
@@ -13,7 +13,6 @@ export interface ProductVO {
|
||||
description: string // 产品描述
|
||||
status: number // 产品状态
|
||||
deviceType: number // 设备类型
|
||||
locationType: number // 设备类型
|
||||
netType: number // 联网方式
|
||||
codecType: string // 数据格式(编解码器类型)
|
||||
deviceCount: number // 设备数量
|
||||
@@ -26,12 +25,6 @@ export enum DeviceTypeEnum {
|
||||
GATEWAY_SUB = 1, // 网关子设备
|
||||
GATEWAY = 2 // 网关设备
|
||||
}
|
||||
// IOT 产品定位类型枚举类 0: 手动定位, 1: IP 定位, 2: 定位模块定位
|
||||
export enum LocationTypeEnum {
|
||||
IP = 1, // IP 定位
|
||||
MODULE = 2, // 设备定位
|
||||
MANUAL = 3 // 手动定位
|
||||
}
|
||||
// IOT 数据格式(编解码器类型)枚举类
|
||||
export enum CodecTypeEnum {
|
||||
ALINK = 'Alink' // 阿里云 Alink 协议
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- 地图组件:基于百度地图GL实现 -->
|
||||
<!-- TODO @super:还存在两个没解决的小bug,一个是修改手动定位时一次加载 不知道为何定位点在地图左上角 调了半天没解决 第二个是检索地址确定定位的功能参照百度的文档没也搞好 回头再解决一下 -->
|
||||
<!-- 地图组件:基于百度地图 GL 实现 -->
|
||||
<!-- TODO @AI:还存在两个没解决的小bug,一个是修改手动定位时一次加载 不知道为何定位点在地图左上角 调了半天没解决 第二个是检索地址确定定位的功能参照百度的文档没也搞好 回头再解决一下 -->
|
||||
<template>
|
||||
<div v-if="props.isWrite">
|
||||
<el-form ref="form" label-width="120px">
|
||||
@@ -73,6 +73,7 @@ const props = defineProps({
|
||||
|
||||
watch(
|
||||
() => props.center,
|
||||
// TODO @AI:这里有个 linter 报错
|
||||
(newVal, oldVal) => {
|
||||
if (newVal) {
|
||||
// 当 center 变化时 重新加载mark点
|
||||
@@ -100,7 +101,7 @@ const loadMap = () => {
|
||||
initGeocoder()
|
||||
initAutoComplete()
|
||||
|
||||
// TODO @super:这里加一行注释
|
||||
// TODO @AI:这里加一行注释
|
||||
if (props.clickMap) {
|
||||
state.map.addEventListener('click', (e: any) => {
|
||||
console.log(e)
|
||||
@@ -194,9 +195,10 @@ const handleAddressSelect = (value: string) => {
|
||||
* 添加标记点
|
||||
* @param lnglat 经纬度数组
|
||||
*/
|
||||
// TODO @super:拼写;尽量不要有 idea 绿色提醒哈
|
||||
const setMarker = (lnglat: any) => {
|
||||
if (!lnglat) return
|
||||
if (!lnglat) {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果点标记已存在则先移除原点
|
||||
if (state.mapMarker !== null) {
|
||||
@@ -217,14 +219,14 @@ const setMarker = (lnglat: any) => {
|
||||
* 经纬度转化为地址、添加标记点
|
||||
* @param lonLat 经度,纬度字符串
|
||||
*/
|
||||
// TODO @super:拼写;尽量不要有 idea 绿色提醒哈
|
||||
const regeoCode = (lonLat: string) => {
|
||||
if (!lonLat) return
|
||||
|
||||
// TODO @super:拼写;尽量不要有 idea 绿色提醒哈
|
||||
if (!lonLat) {
|
||||
return
|
||||
}
|
||||
const lnglat = lonLat.split(',')
|
||||
if (lnglat.length !== 2) return
|
||||
|
||||
if (lnglat.length !== 2) {
|
||||
return
|
||||
}
|
||||
state.longitude = lnglat[0]
|
||||
state.latitude = lnglat[1]
|
||||
|
||||
@@ -241,7 +243,6 @@ const regeoCode = (lonLat: string) => {
|
||||
getAddress(lnglat)
|
||||
}
|
||||
|
||||
// TODO @super:lnglat 拼写
|
||||
/**
|
||||
* 根据经纬度获取地址信息
|
||||
*
|
||||
@@ -266,6 +267,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
// @AI:尽量 unocss 简化掉;
|
||||
.mapContainer {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
|
||||
@@ -66,44 +66,31 @@
|
||||
<el-form-item label="设备序列号" prop="serialNumber">
|
||||
<el-input v-model="formData.serialNumber" placeholder="请输入设备序列号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="定位类型" prop="locationType">
|
||||
<el-radio-group v-model="formData.locationType">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.IOT_LOCATION_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<el-form-item label="设备经度" prop="longitude" type="number">
|
||||
<el-input
|
||||
v-model="formData.longitude"
|
||||
placeholder="请输入设备经度"
|
||||
@blur="updateLocationFromCoordinates"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- LocationTypeEnum.MANUAL:手动定位 -->
|
||||
<template v-if="LocationTypeEnum.MANUAL === formData.locationType">
|
||||
<el-form-item label="设备经度" prop="longitude" type="number">
|
||||
<el-input
|
||||
v-model="formData.longitude"
|
||||
placeholder="请输入设备经度"
|
||||
@blur="updateLocationFromCoordinates"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备维度" prop="latitude" type="number">
|
||||
<el-input
|
||||
v-model="formData.latitude"
|
||||
placeholder="请输入设备维度"
|
||||
@blur="updateLocationFromCoordinates"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div class="pl-0 h-[400px] w-full ml-[-18px]" v-if="showMap">
|
||||
<Map
|
||||
:isWrite="true"
|
||||
:clickMap="true"
|
||||
:center="formData.location"
|
||||
@locate-change="handleLocationChange"
|
||||
ref="mapRef"
|
||||
class="h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<el-form-item label="设备维度" prop="latitude" type="number">
|
||||
<el-input
|
||||
v-model="formData.latitude"
|
||||
placeholder="请输入设备维度"
|
||||
@blur="updateLocationFromCoordinates"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- TODO @AI:然后后面有个按钮【标注地图】可以手动按需调整; -->
|
||||
<div class="pl-0 h-[400px] w-full ml-[-18px]" v-if="showMap">
|
||||
<Map
|
||||
:isWrite="true"
|
||||
:clickMap="true"
|
||||
:center="formData.location"
|
||||
@locate-change="handleLocationChange"
|
||||
ref="mapRef"
|
||||
class="h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-form>
|
||||
@@ -116,9 +103,8 @@
|
||||
<script setup lang="ts">
|
||||
import { DeviceApi, DeviceVO } from '@/api/iot/device/device'
|
||||
import { DeviceGroupApi } from '@/api/iot/device/group'
|
||||
import { DeviceTypeEnum, LocationTypeEnum, ProductApi, ProductVO } from '@/api/iot/product/product'
|
||||
import { DeviceTypeEnum, ProductApi, ProductVO } from '@/api/iot/product/product'
|
||||
import { UploadImg } from '@/components/UploadFile'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import Map from '@/components/Map/index.vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
@@ -144,14 +130,14 @@ const formData = ref({
|
||||
gatewayId: undefined,
|
||||
deviceType: undefined as number | undefined,
|
||||
serialNumber: undefined,
|
||||
locationType: undefined as number | undefined,
|
||||
longitude: undefined,
|
||||
latitude: undefined,
|
||||
location: '', // 格式: "经度,纬度"
|
||||
location: '', // 格式: "经度,纬度" // TODO @AI:单独搞个字段出来,不放在 formData 里!
|
||||
groupIds: [] as number[]
|
||||
})
|
||||
|
||||
/** 监听经纬度变化,更新location */
|
||||
/** 监听经纬度变化,更新 location */
|
||||
// TODO @AI:交互上,想改成上面展示 longitude、latitude 两个地址;然后后面有个按钮【标注地图】可以手动按需调整;
|
||||
watch([() => formData.value.longitude, () => formData.value.latitude], ([newLong, newLat]) => {
|
||||
if (newLong && newLat) {
|
||||
formData.value.location = `${newLong},${newLat}`
|
||||
@@ -197,6 +183,7 @@ const formRules = reactive({
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
// TODO @AI:加个校验。如果 longitude、latitude 有一个非空,必须两个都非空;
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const products = ref<ProductVO[]>([]) // 产品列表
|
||||
@@ -248,16 +235,6 @@ const submitForm = async () => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as DeviceVO
|
||||
// 如果非手动定位,不进行提交该字段
|
||||
if (data.locationType !== LocationTypeEnum.MANUAL) {
|
||||
data.longitude = undefined
|
||||
data.latitude = undefined
|
||||
}
|
||||
// TODO @宗超:【设备定位】address 和 areaId 也要处理;
|
||||
// 1. 手动定位时:longitude + latitude + areaId + address:要稍微注意,address 可能要去掉省市区部分?!
|
||||
// 2. IP 定位时:IotDeviceMessage 的 buildStateUpdateOnline 时,增加 ip 字段。这样,解析到 areaId;另外看看能不能通过 https://lbsyun.baidu.com/faq/api?title=webapi/ip-api-base(只获取 location 就 ok 啦)
|
||||
// 3. 设备定位时:问问 haohao,一般怎么做。
|
||||
|
||||
if (formType.value === 'create') {
|
||||
await DeviceApi.createDevice(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
@@ -284,10 +261,8 @@ const resetForm = () => {
|
||||
gatewayId: undefined,
|
||||
deviceType: undefined,
|
||||
serialNumber: undefined,
|
||||
locationType: undefined,
|
||||
longitude: undefined,
|
||||
latitude: undefined,
|
||||
// TODO @宗超:【设备定位】location 是不是拿出来,不放在 formData 里
|
||||
location: '',
|
||||
groupIds: []
|
||||
}
|
||||
@@ -304,10 +279,10 @@ const handleProductChange = (productId: number) => {
|
||||
}
|
||||
const product = products.value?.find((item) => item.id === productId)
|
||||
formData.value.deviceType = product?.deviceType
|
||||
formData.value.locationType = product?.locationType
|
||||
}
|
||||
|
||||
/** 处理位置变化 */
|
||||
// todo @AI:这了有 linter 报错:TS7044: Parameter lnglat implicitly has an any type, but a better type may be inferred from usage.
|
||||
const handleLocationChange = (lnglat) => {
|
||||
formData.value.longitude = lnglat[0]
|
||||
formData.value.latitude = lnglat[1]
|
||||
@@ -319,6 +294,7 @@ const updateLocationFromCoordinates = () => {
|
||||
if (formData.value.longitude && formData.value.latitude) {
|
||||
// 更新 location 字段,地图组件会根据此字段更新
|
||||
formData.value.location = `${formData.value.longitude},${formData.value.latitude}`
|
||||
// TODO @AI:这里有告警;
|
||||
mapRef.value.regeoCode(formData.value.location)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
<el-descriptions-item label="设备类型">
|
||||
<dict-tag :type="DICT_TYPE.IOT_PRODUCT_DEVICE_TYPE" :value="product.deviceType" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="定位类型">
|
||||
<dict-tag :type="DICT_TYPE.IOT_LOCATION_TYPE" :value="device.locationType" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="DeviceName">
|
||||
{{ device.deviceName }}
|
||||
</el-descriptions-item>
|
||||
@@ -67,6 +64,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<div class="h-[400px] w-full">
|
||||
<!-- TODO @AI:是不是可以通过 getLocationString() 简化判断; -->
|
||||
<Map v-if="showMap" :center="getLocationString()" class="h-full w-full" />
|
||||
<div
|
||||
v-else
|
||||
|
||||
@@ -62,17 +62,6 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="定位类型" prop="locationType">
|
||||
<el-radio-group v-model="formData.locationType" :disabled="formType === 'update'">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.IOT_LOCATION_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据格式" prop="codecType">
|
||||
<el-radio-group v-model="formData.codecType" :disabled="formType === 'update'">
|
||||
<el-radio
|
||||
@@ -130,7 +119,6 @@ const formData = ref({
|
||||
picUrl: undefined,
|
||||
description: undefined,
|
||||
deviceType: undefined,
|
||||
locationType: undefined,
|
||||
netType: undefined,
|
||||
codecType: CodecTypeEnum.ALINK
|
||||
})
|
||||
@@ -139,7 +127,6 @@ const formRules = reactive({
|
||||
name: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
|
||||
categoryId: [{ required: true, message: '产品分类不能为空', trigger: 'change' }],
|
||||
deviceType: [{ required: true, message: '设备类型不能为空', trigger: 'change' }],
|
||||
locationType: [{ required: true, message: '定位类型不能为空', trigger: 'change' }],
|
||||
netType: [
|
||||
{
|
||||
required: true,
|
||||
@@ -206,7 +193,6 @@ const resetForm = () => {
|
||||
picUrl: undefined,
|
||||
description: undefined,
|
||||
deviceType: undefined,
|
||||
locationType: undefined,
|
||||
netType: undefined,
|
||||
codecType: CodecTypeEnum.ALINK
|
||||
}
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
<el-descriptions-item label="设备类型">
|
||||
<dict-tag :type="DICT_TYPE.IOT_PRODUCT_DEVICE_TYPE" :value="product.deviceType" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="定位类型">
|
||||
<dict-tag :type="DICT_TYPE.IOT_LOCATION_TYPE" :value="product.locationType" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ formatDate(product.createTime) }}
|
||||
</el-descriptions-item>
|
||||
|
||||
Reference in New Issue
Block a user