feat(device): 增加设备详情和地图功能
- 在 Device 接口中添加了 hardwareVersion、firmwareVersion、productName 和 id 字段 - 修改了 sendCommand函数,增加了 deviceId 和 qos 参数- 更新了设备详情页面,添加了新的设备信息字段 - 在地图页面添加了设备列表和设备信息窗口 - 优化了表单组件,增加了 Qos等级选择和字段校验- 统一了删除按钮的样式
This commit is contained in:
parent
39fca49ea6
commit
085d0ae690
@ -13,6 +13,10 @@ export interface DeviceRecord {
|
||||
longitude?: number;
|
||||
latitude?: number;
|
||||
icon?: string;
|
||||
hardwareVersion?: string;
|
||||
firmwareVersion?: string;
|
||||
productName?: string;
|
||||
id?: number;
|
||||
}
|
||||
|
||||
export interface DeviceCreateRecord {
|
||||
@ -108,10 +112,15 @@ export function triggerEvent(data: DeviceEventRecord) {
|
||||
|
||||
// 下发命令
|
||||
export function sendCommand(data: any) {
|
||||
const { deviceId, qos, ...rest } = data;
|
||||
return axios({
|
||||
url: `/api/rest/device/send`,
|
||||
method: 'post',
|
||||
data,
|
||||
params: {
|
||||
deviceId,
|
||||
qos,
|
||||
},
|
||||
data: rest,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@
|
||||
status="success"
|
||||
@click="openServeForm(record)"
|
||||
>
|
||||
执行
|
||||
<icon-plus />执行
|
||||
</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
@ -90,7 +90,7 @@
|
||||
</a-card>
|
||||
|
||||
<a-modal
|
||||
width="900px"
|
||||
width="1100px"
|
||||
height="500px"
|
||||
:visible="visible"
|
||||
@cancel="handleCancel"
|
||||
@ -111,7 +111,7 @@
|
||||
<script lang="ts" setup>
|
||||
import dayjs from 'dayjs';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { queryDeviceDetail, queryDeviceRecord, sendCommand } from '@/api/device';
|
||||
import { queryServeDetail, queryServeList } from '@/api/tsl';
|
||||
@ -174,6 +174,7 @@
|
||||
];
|
||||
const activeKey = ref('1');
|
||||
const renderData = ref({
|
||||
deviceId: 0,
|
||||
clientId: 1,
|
||||
productId: 1
|
||||
});
|
||||
@ -200,6 +201,7 @@
|
||||
activeKey.value = e;
|
||||
};
|
||||
const dynamicFormData = ref();
|
||||
const formRef = ref();
|
||||
// 关闭
|
||||
const handleCancel = async () => {
|
||||
setVisible(false);
|
||||
@ -211,12 +213,20 @@
|
||||
fields.value = res.data.inputs;
|
||||
};
|
||||
|
||||
const handleFormDataUpdate = (newFormData) => {
|
||||
const handleFormDataUpdate = (newFormData: any) => {
|
||||
dynamicFormData.value = newFormData; // 更新 formData
|
||||
};
|
||||
// 确定
|
||||
const handleSubmit = async () => {
|
||||
const res = await sendCommand(dynamicFormData.value);
|
||||
try {
|
||||
const { qos, ...rest } = dynamicFormData.value;
|
||||
const params = {
|
||||
deviceId: id,
|
||||
qos,
|
||||
paras: rest,
|
||||
};
|
||||
console.log(params);
|
||||
const res = await sendCommand(params);
|
||||
if (res.status === 200) {
|
||||
Message.success({
|
||||
content: '执行成功',
|
||||
@ -229,6 +239,12 @@
|
||||
duration: 5 * 1000,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
Message.error({
|
||||
content: '表单不能为空',
|
||||
duration: 5 * 1000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -122,7 +122,7 @@
|
||||
style="width: 140px"
|
||||
/>
|
||||
<a-button type="text" @click="handleDeleteParams(index)"
|
||||
><icon-minus-circle
|
||||
><icon-minus-circle style="color: red;"
|
||||
/></a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
@ -1,10 +1,22 @@
|
||||
<template>
|
||||
<a-form
|
||||
:model="formData"
|
||||
:style="{ width: '800px' }"
|
||||
ref="formRef"
|
||||
:style="{ width: '1000px'}"
|
||||
>
|
||||
<a-form-item label="Qos等级" :rules="rules.qos">
|
||||
<a-select
|
||||
v-model="formData.qos"
|
||||
placeholder="Qos等级"
|
||||
:options="[
|
||||
{ value: 0, label: '最多一次' },
|
||||
{ value: 1, label: '至少一次' },
|
||||
{ value: 2, label: '仅一次' },
|
||||
]"
|
||||
/>
|
||||
</a-form-item>
|
||||
<template v-for="(field, index) in fields" :key="index">
|
||||
<a-form-item :label="field.name">
|
||||
<a-form-item :label="field.name" :rules="rules[field.identifier]">
|
||||
<a-input
|
||||
v-model="formData[field.identifier]"
|
||||
:placeholder="field.name"
|
||||
@ -26,11 +38,23 @@
|
||||
|
||||
const formData = ref({});
|
||||
const emit = defineEmits(['update:formData']);
|
||||
const formRef = ref(null); // 定义 formRef
|
||||
|
||||
// 定义校验规则
|
||||
const rules = {
|
||||
qos: [{ required: true, message: '请选择 Qos 等级', trigger: 'change' }],
|
||||
...props.fields.reduce((acc, field) => {
|
||||
acc[field.identifier] = [
|
||||
{ required: true, message: `${field.name}不能为空`, trigger: 'blur' },
|
||||
];
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
// 监听 formData 变化并触发事件
|
||||
watch(formData, (newValue) => {
|
||||
emit('update:formData', newValue);
|
||||
}, { deep: true });
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -140,7 +140,7 @@
|
||||
style="width: 140px"
|
||||
/>
|
||||
<a-button type="text" @click="handleDeleteParams(index)"
|
||||
><icon-minus-circle
|
||||
><icon-minus-circle style="color: red;"
|
||||
/></a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
@ -2,6 +2,9 @@
|
||||
<a-layout>
|
||||
<a-layout-sider :resize-directions="['right']">
|
||||
<a-row>
|
||||
<a-col :span="24" v-for="device in deviceList" :key="device.id" @click="selectDevice(device)">
|
||||
{{ device.name }}
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-layout-sider>
|
||||
<a-layout-content>
|
||||
@ -11,13 +14,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import AMapLoader from '@amap/amap-jsapi-loader';
|
||||
import { DeviceRecord, queryDeviceList } from '@/api/device';
|
||||
import "@amap/amap-jsapi-types";
|
||||
import router from '@/router';
|
||||
|
||||
let map: any = null;
|
||||
const deviceList = ref<DeviceRecord[]>([]);
|
||||
let selectDevice = (device: DeviceRecord) => {
|
||||
console.log(device);
|
||||
}
|
||||
|
||||
AMapLoader.load({
|
||||
key: 'a4e80eed798a56451b226dcfca81b846', // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
@ -30,32 +38,71 @@
|
||||
zoom: 11, // 初始化地图级别
|
||||
center: [116.397428, 39.90923], // 初始化地图中心点位置
|
||||
});
|
||||
map.on('click', (e: any) => {
|
||||
// 获取点击位置的经纬度
|
||||
const lngLat = e.lnglat;
|
||||
|
||||
selectDevice = (device: DeviceRecord) => {
|
||||
if (device.longitude && device.latitude) {
|
||||
const lngLat = [device.longitude, device.latitude];
|
||||
const content = document.createElement('div');
|
||||
content.className = 'custom-content-marker';
|
||||
content.innerHTML = `
|
||||
<img src="https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png" alt="标记点" />
|
||||
<div class="close-btn">×</div>`;
|
||||
<img src="https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png" alt="标记点" />`;
|
||||
|
||||
// 创建标记点
|
||||
const marker = new AMap.Marker({
|
||||
position: lngLat,
|
||||
content,
|
||||
});
|
||||
|
||||
// 将标记点添加到地图上
|
||||
map.setCenter(lngLat);
|
||||
map.setZoom(16);
|
||||
map.add(marker);
|
||||
// 监听删除按钮的点击事件
|
||||
const closeBtn = marker.getContent().querySelector('.close-btn');
|
||||
closeBtn.addEventListener('click', () => {
|
||||
map.remove(marker); // 移除标记点
|
||||
|
||||
// const infoContent = `
|
||||
// <div style="width: 220px; padding: 10px; background: #fff; border-radius: 4px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);">
|
||||
// <h4 style="margin: 0 0 10px; font-size: 16px; color: #333;">设备信息</h4>
|
||||
// <p style="margin: 0; font-size: 14px; color: #555;"><strong>设备名称:</strong> ${device.name}</p>
|
||||
// <p style="margin: 0; font-size: 14px; color: #555;"><strong>硬件版本:</strong> ${device.hardwareVersion}</p>
|
||||
// <p style="margin: 0; font-size: 14px; color: #555;"><strong>固件版本:</strong> ${device.firmwareVersion}</p>
|
||||
// <p style="margin: 0; font-size: 14px; color: #555;"><strong>所属产品:</strong> ${device.productId}</p>
|
||||
// </div>
|
||||
// `;
|
||||
const infoContent = [
|
||||
`<br/><div style="width: 300px;margin-left: 20px;padding-bottom: 10px"><b>设备名称: ${device.name}</b>`,
|
||||
`<span style="font-size: 16px; color: #333;">硬件版本: ${device.hardwareVersion}</span>`,
|
||||
`<span style="font-size: 16px; color: #333;">固件版本: ${device.firmwareVersion}</span>`,
|
||||
`<span style="font-size: 16px; color: #333;">所属产品: ${device.productName}</span>`,
|
||||
`<a-button style="margin-left: 200px;color: #0960bd" onclick="handleViewDetail('${device.id}')">查看详情
|
||||
</a-button></div>`
|
||||
]
|
||||
|
||||
const handleViewDetail = (deviceId: number) => {
|
||||
router.push({ name: 'deviceDetail', params: { id: deviceId } });
|
||||
};
|
||||
window.handleViewDetail = handleViewDetail;
|
||||
|
||||
|
||||
// 初始化信息窗体
|
||||
const infoWindow = new AMap.InfoWindow({
|
||||
content: infoContent.join('<br>'),
|
||||
offset: new AMap.Pixel(10, 10), // 调整信息窗体的偏移量
|
||||
});
|
||||
|
||||
// 绑定标记点点击事件
|
||||
marker.on('click', () => {
|
||||
infoWindow.open(map, lngLat); // 打开信息窗体
|
||||
});
|
||||
} else {
|
||||
console.warn('设备缺少经纬度信息', device);
|
||||
}
|
||||
};
|
||||
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
// 将依赖于 AMap 的代码放在 then 回调中
|
||||
onMounted(() => {
|
||||
// 获取设备列表
|
||||
queryDeviceList({
|
||||
|
@ -107,7 +107,7 @@
|
||||
style="width: 140px"
|
||||
/>
|
||||
<a-button type="text" @click="handleDeleteParams(index)"
|
||||
><icon-minus-circle
|
||||
><icon-minus-circle style="color: red;"
|
||||
/></a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
@ -235,7 +235,7 @@
|
||||
<a-button
|
||||
type="text"
|
||||
@click="handleDeleteParams(index, 'serveInputData')"
|
||||
><icon-minus-circle
|
||||
><icon-minus-circle style="color: red;"
|
||||
/></a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
@ -280,7 +280,7 @@
|
||||
<a-button
|
||||
type="text"
|
||||
@click="handleDeleteParams(index, 'serveOutputData')"
|
||||
><icon-minus-circle
|
||||
><icon-minus-circle style="color: red;"
|
||||
/></a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
@ -385,7 +385,7 @@
|
||||
<a-button
|
||||
type="text"
|
||||
@click="handleDeleteParams(index, 'eventOutputData')"
|
||||
><icon-minus-circle
|
||||
><icon-minus-circle style="color: red;"
|
||||
/></a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user