feat(iot): 添加设备事件记录功能
- 在设备详情页面新增事件记录标签页 - 实现事件记录的数据加载和展示 - 优化设备地图组件样式 - 更新产品编辑页面的预览图和图标上传逻辑 -调整产品 TSL 页面的样式和功能 - 新增事件日志查询接口
This commit is contained in:
parent
a045b8fc2d
commit
1e95c39a86
@ -29,3 +29,4 @@ export function deleteLogs(ids: number[]) {
|
||||
data: ids,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ export interface eventRecord extends Record {
|
||||
level?: string;
|
||||
identifier?: string;
|
||||
productId?: number;
|
||||
clientId?: number;
|
||||
}
|
||||
|
||||
export function queryServeList(data: ServeRecord) {
|
||||
@ -50,6 +51,15 @@ export function queryEventList(data: eventRecord) {
|
||||
});
|
||||
}
|
||||
|
||||
// 事件记录
|
||||
export function queryEventLog(data: eventRecord) {
|
||||
return axios({
|
||||
url: '/api/rest/tsl/event/log',
|
||||
method: 'get',
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function createServe(data: any) {
|
||||
return axios.post(`/api/rest/tsl/serve`, data);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ const IOT: AppRouteRecordRaw = {
|
||||
locale: '设备管理(卡片)',
|
||||
title: '设备管理(卡片)',
|
||||
requiresAuth: true,
|
||||
permissions: ['iot:device'],
|
||||
permissions: ['iot:deviceCard'],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -100,15 +100,25 @@
|
||||
</template>
|
||||
<template #operations="{ record }">
|
||||
<a-button type="text" @click="openDetailModal(record)">
|
||||
<template #icon><icon-list /></template>
|
||||
查看详情
|
||||
<icon-list/>
|
||||
详情
|
||||
</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="5" tab="事件记录" title="事件记录">
|
||||
<a-table :columns="deviceEventColumns" :data="deviceEventData" >
|
||||
<template #operations="{ record }">
|
||||
<a-button type="text" @click="openDetailModal(record)">
|
||||
<icon-list />
|
||||
详情
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="5" tab="设备地图" title="设备地图">
|
||||
<a-tab-pane key="6" tab="设备地图" title="设备地图">
|
||||
<DeviceMap v-if="renderData.id" :renderData=renderData />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
@ -157,7 +167,7 @@
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { queryDeviceDetail, queryDeviceRecord, sendCommand } from '@/api/device';
|
||||
import { queryServeDetail, queryServeList } from '@/api/tsl';
|
||||
import { queryEventLog, queryServeDetail, queryServeList } from '@/api/tsl';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import dynamicForm from './dynamic-form.vue';
|
||||
import DeviceMap from './device-map.vue';
|
||||
@ -221,6 +231,18 @@
|
||||
slotName: 'operations',
|
||||
},
|
||||
];
|
||||
const deviceEventColumns = [
|
||||
{
|
||||
title: ' 事件名称',
|
||||
dataIndex: 'name',
|
||||
slotName: 'name',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
slotName: 'operations',
|
||||
},
|
||||
];;
|
||||
const activeKey = ref('1');
|
||||
|
||||
const detailVisible = ref(false);
|
||||
@ -243,24 +265,44 @@
|
||||
}
|
||||
);
|
||||
const deviceReportData = ref([]);
|
||||
const deviceEventData = ref([]);
|
||||
|
||||
const deviceServerData = ref([]);
|
||||
const fields = ref([]);
|
||||
const fetchData = async (Id: number) => {
|
||||
const res = await queryDeviceDetail(Id);
|
||||
renderData.value = res.data;
|
||||
const res1 = await queryDeviceRecord({
|
||||
clientId: renderData.value.clientId,
|
||||
size: 10,
|
||||
current: 1,
|
||||
});
|
||||
deviceReportData.value = res1.data.records;
|
||||
const res2 = await queryServeList({
|
||||
productId: renderData.value.productId,
|
||||
size: 10,
|
||||
current: 1,
|
||||
})
|
||||
deviceServerData.value = res2.data.records;
|
||||
try {
|
||||
const [detailRes, recordRes, serveRes, eventRes] = await Promise.all([
|
||||
queryDeviceDetail(Id),
|
||||
queryDeviceRecord({
|
||||
clientId: renderData.value.clientId,
|
||||
size: 10,
|
||||
current: 1,
|
||||
}),
|
||||
queryServeList({
|
||||
productId: renderData.value.productId,
|
||||
size: 10,
|
||||
current: 1,
|
||||
}),
|
||||
queryEventLog({
|
||||
clientId: renderData.value.clientId,
|
||||
size: 10,
|
||||
current: 1,
|
||||
}),
|
||||
]);
|
||||
|
||||
renderData.value = detailRes.data;
|
||||
deviceReportData.value = recordRes.data.records;
|
||||
deviceServerData.value = serveRes.data.records;
|
||||
deviceEventData.value = eventRes.data.records;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch data:', error);
|
||||
Message.error({
|
||||
content: '数据加载失败',
|
||||
duration: 5 * 1000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleMenuClick = (e: any) => {
|
||||
activeKey.value = e;
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<a-layout>
|
||||
<a-layout-content>
|
||||
<div id="container" style="height: 600px"></div>
|
||||
<div id="container" style="height: 500px"></div>
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
@ -82,12 +82,12 @@
|
||||
label="预览图"
|
||||
>
|
||||
<a-upload
|
||||
v-model="formData.attachment"
|
||||
placeholder="请选择预览图"
|
||||
|
||||
v-model="formData.preview"
|
||||
show-file-list
|
||||
:file-list="fileList"
|
||||
:custom-request="customRequest"
|
||||
:file-list="previewList"
|
||||
:limit="1"
|
||||
list-type="picture-card"
|
||||
:custom-request="(option: any) => customRequest(option, 'preview')"
|
||||
@before-remove="beforeRemove"
|
||||
/>
|
||||
</a-form-item>
|
||||
@ -97,11 +97,10 @@
|
||||
>
|
||||
<a-upload
|
||||
v-model="formData.icon"
|
||||
placeholder="请选择图标"
|
||||
|
||||
show-file-list
|
||||
:file-list="fileList"
|
||||
:custom-request="customRequest"
|
||||
:file-list="iconList"
|
||||
:limit="1"
|
||||
:custom-request="(option: any) => customRequest(option, 'icon')"
|
||||
@before-remove="beforeRemove"
|
||||
/>
|
||||
</a-form-item>
|
||||
@ -153,81 +152,11 @@
|
||||
>
|
||||
</template>
|
||||
</a-modal>
|
||||
|
||||
<!-- <a-modal-->
|
||||
<!-- width="900px"-->
|
||||
<!-- :visible="paramsVisible"-->
|
||||
<!-- @cancel="paramsCancel"-->
|
||||
<!-- >-->
|
||||
<!-- <template #title>添加参数</template>-->
|
||||
<!-- <a-form-->
|
||||
<!-- ref="CreateRef"-->
|
||||
<!-- :model="paramsData"-->
|
||||
<!-- :style="{ width: '650px' }"-->
|
||||
<!-- >-->
|
||||
<!-- <!– 添加参数 –>-->
|
||||
<!-- <a-form-item-->
|
||||
<!-- field="name"-->
|
||||
<!-- label="参数名称"-->
|
||||
<!-- :rules="[{ required: true, message: '参数名称不能为空' }]"-->
|
||||
<!-- :validate-trigger="['change']"-->
|
||||
<!-- >-->
|
||||
<!-- <a-input-->
|
||||
<!-- v-model="paramsData.name"-->
|
||||
<!-- placeholder='请选择参数名称'-->
|
||||
<!-- />-->
|
||||
<!-- </a-form-item>-->
|
||||
<!-- <!– 参数标识 –>-->
|
||||
<!-- <a-form-item-->
|
||||
<!-- field="identifier"-->
|
||||
<!-- label="参数标识"-->
|
||||
<!-- :rules="[{ required: true, message: '参数标识不能为空' }]"-->
|
||||
<!-- :validate-trigger="['change']"-->
|
||||
<!-- >-->
|
||||
<!-- <a-input-->
|
||||
<!-- v-model="paramsData.identifier"-->
|
||||
<!-- placeholder='请选择参数标识'-->
|
||||
<!-- />-->
|
||||
<!-- </a-form-item>-->
|
||||
|
||||
<!-- <a-form-item-->
|
||||
<!-- field="dataType"-->
|
||||
<!-- label="数据类型"-->
|
||||
<!-- :rules="[{ required: true, message: '数据类型不能为空' }]"-->
|
||||
<!-- :validate-trigger="['change']"-->
|
||||
<!-- >-->
|
||||
<!-- <a-select-->
|
||||
<!-- v-model="paramsData.dataType"-->
|
||||
<!-- placeholder='请输入数据类型'-->
|
||||
<!-- :options="dataTypeOptions"-->
|
||||
<!-- allow-search-->
|
||||
<!-- />-->
|
||||
<!-- </a-form-item>-->
|
||||
<!-- <a-form-item-->
|
||||
<!-- field="type"-->
|
||||
<!-- label="参数类型"-->
|
||||
<!-- :rules="[{ required: true, message: '参数类型不能为空' }]"-->
|
||||
<!-- :validate-trigger="['change']"-->
|
||||
<!-- >-->
|
||||
<!-- <a-select-->
|
||||
<!-- v-model="paramsData.type"-->
|
||||
<!-- placeholder='请输入参数类型'-->
|
||||
<!-- :options="typeOptions"-->
|
||||
<!-- allow-search-->
|
||||
<!-- />-->
|
||||
<!-- </a-form-item>-->
|
||||
<!-- </a-form>-->
|
||||
|
||||
<!-- <template #footer>-->
|
||||
<!-- <a-button class="editor-button" @click="paramsCancel">取消</a-button>-->
|
||||
<!-- <a-button class="editor-button" type="primary" @click="paramsSubmit">确定</a-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </a-modal>-->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import useVisible from '@/hooks/visible';
|
||||
import { computed, defineEmits, onMounted, PropType, ref } from 'vue';
|
||||
import { computed, defineEmits, PropType, ref } from 'vue';
|
||||
import { FormInstance } from '@arco-design/web-vue/es/form';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import '@wangeditor/editor/dist/css/style.css';
|
||||
@ -329,8 +258,18 @@
|
||||
};
|
||||
|
||||
// 预览图列表
|
||||
const fileList = computed(() => {
|
||||
return formData.value.attachments?.map((item: any) => {
|
||||
const previewList = computed(() => {
|
||||
return formData.value.preview?.map((item: any) => {
|
||||
return {
|
||||
name: item.fileName,
|
||||
url: item.url,
|
||||
uid: item.id,
|
||||
};
|
||||
});
|
||||
});
|
||||
// 图标列表
|
||||
const iconList = computed(() => {
|
||||
return formData.value.icon?.map((item: any) => {
|
||||
return {
|
||||
name: item.fileName,
|
||||
url: item.url,
|
||||
@ -339,7 +278,7 @@
|
||||
});
|
||||
});
|
||||
// 自定义预览图上传
|
||||
const customRequest = async (option: any) => {
|
||||
const customRequest = async (option: any,type: string) => {
|
||||
const { fileItem, onSuccess, onError } = option;
|
||||
const formDataFile = new FormData();
|
||||
formDataFile.append('file', fileItem.file);
|
||||
@ -347,7 +286,13 @@
|
||||
const res = await addAttachments(formDataFile);
|
||||
if (res.status === 200) {
|
||||
onSuccess(res.data);
|
||||
formData.value.attachmentIds?.push(res.data.id);
|
||||
if (type === 'preview') {
|
||||
formData.value.preview = formData.value.preview || [];
|
||||
formData.value.preview.push(res.data);
|
||||
} else if (type === 'icon') {
|
||||
formData.value.icon = formData.value.icon || [];
|
||||
formData.value.icon.push(res.data);
|
||||
}
|
||||
} else {
|
||||
onError(res.data);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
新建
|
||||
</a-button>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:columns="propertyColumns"
|
||||
:data="propertyData"
|
||||
:pagination="true"
|
||||
>
|
||||
@ -56,7 +56,7 @@
|
||||
<template #icon><icon-plus /></template>
|
||||
新建
|
||||
</a-button>
|
||||
<a-table :columns="columns" :data="serveData">
|
||||
<a-table :columns="serveColumns" :data="serveData">
|
||||
<template #operation="{ record }">
|
||||
<a-button
|
||||
v-permission="['iot:serve:delete']"
|
||||
@ -82,7 +82,10 @@
|
||||
<template #icon><icon-plus /></template>
|
||||
新建
|
||||
</a-button>
|
||||
<a-table :columns="columns" :data="eventData">
|
||||
<a-table :columns="eventColumns" :data="eventData">
|
||||
<template #type="{ record }">
|
||||
<div>{{ record.type === 'PASSIVE' ? '主动' : '被动' }}</div>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<a-button
|
||||
v-permission="['iot:event:delete']"
|
||||
@ -362,7 +365,13 @@
|
||||
:key="index"
|
||||
style="margin-bottom: 5px"
|
||||
>
|
||||
<a-input v-model="param.name" placeholder="属性" allow-clear style="width: 140px" />
|
||||
<a-select
|
||||
v-model="param.name"
|
||||
:options="propertyOptions"
|
||||
allow-search
|
||||
placeholder="属性"
|
||||
style="width: 140px"
|
||||
/>
|
||||
<a-input
|
||||
v-model="param.identifier"
|
||||
placeholder="标识"
|
||||
@ -450,7 +459,29 @@
|
||||
const { visible, setVisible } = useVisible();
|
||||
const route = useRoute();
|
||||
const id = Number(route.params.id);
|
||||
const columns = [
|
||||
const serveColumns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
slotName: 'name',
|
||||
},
|
||||
{
|
||||
title: '标识',
|
||||
dataIndex: 'identifier',
|
||||
slotName: 'identifier',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
slotName: 'remark',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
slotName: 'operation',
|
||||
},
|
||||
];
|
||||
const propertyColumns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
@ -477,6 +508,33 @@
|
||||
slotName: 'operation',
|
||||
},
|
||||
];
|
||||
const eventColumns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
slotName: 'name',
|
||||
},
|
||||
{
|
||||
title: '标识',
|
||||
dataIndex: 'identifier',
|
||||
slotName: 'identifier',
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
slotName: 'type',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
slotName: 'remark',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
slotName: 'operation',
|
||||
},
|
||||
];
|
||||
const serveData = ref([]);
|
||||
const propertyData = ref([]);
|
||||
const eventData = ref([]);
|
||||
@ -549,6 +607,14 @@
|
||||
value: 'PASSIVE',
|
||||
},
|
||||
]);
|
||||
const propertyOptions = computed(() => {
|
||||
return propertyData.value.map((item) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.identifier,
|
||||
};
|
||||
});
|
||||
});
|
||||
const propertyAddData = ref({
|
||||
productId: id,
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user