fix: iot warn
This commit is contained in:
@@ -5,6 +5,7 @@ import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
import { getSimpleDeviceList } from '#/api/iot/device/device';
|
||||
import { getSimpleDeviceGroupList } from '#/api/iot/device/group';
|
||||
import {
|
||||
DeviceTypeEnum,
|
||||
@@ -27,7 +28,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
label: '产品',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getSimpleProductList,
|
||||
api: () => getSimpleProductList(),
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择产品',
|
||||
@@ -55,12 +56,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
label: '网关设备',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: async () => {
|
||||
const { getSimpleDeviceList } = await import(
|
||||
'#/api/iot/device/device'
|
||||
);
|
||||
return getSimpleDeviceList(DeviceTypeEnum.GATEWAY);
|
||||
},
|
||||
api: () => getSimpleDeviceList(DeviceTypeEnum.GATEWAY),
|
||||
labelField: 'nickname',
|
||||
valueField: 'id',
|
||||
placeholder: '子设备可选择父设备',
|
||||
@@ -93,7 +89,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
label: '设备分组',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getSimpleDeviceGroupList,
|
||||
api: () => getSimpleDeviceGroupList(),
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
mode: 'multiple',
|
||||
@@ -160,7 +156,7 @@ export function useGroupFormSchema(): VbenFormSchema[] {
|
||||
label: '设备分组',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getSimpleDeviceGroupList,
|
||||
api: () => getSimpleDeviceGroupList(),
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
mode: 'multiple',
|
||||
@@ -203,7 +199,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
label: '产品',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getSimpleProductList,
|
||||
api: () => getSimpleProductList(),
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择产品',
|
||||
@@ -253,7 +249,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
label: '设备分组',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getSimpleDeviceGroupList,
|
||||
api: () => getSimpleDeviceGroupList(),
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择设备分组',
|
||||
|
||||
@@ -32,7 +32,6 @@ import { getSimpleProductList } from '#/api/iot/product/product';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns } from './data';
|
||||
// @ts-ignore
|
||||
import DeviceCardView from './modules/DeviceCardView.vue';
|
||||
import DeviceForm from './modules/DeviceForm.vue';
|
||||
import DeviceGroupForm from './modules/DeviceGroupForm.vue';
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { IotDeviceApi } from '#/api/iot/device/device';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import {
|
||||
createDevice,
|
||||
getDevice,
|
||||
updateDevice,
|
||||
} from '#/api/iot/device/device';
|
||||
import type { IotDeviceApi } from '#/api/iot/device/device';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { createDevice, getDevice, updateDevice } from '#/api/iot/device/device';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
@@ -44,7 +44,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('common.updateSuccess'));
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ async function saveConfig() {
|
||||
try {
|
||||
config.value = JSON.parse(configString.value);
|
||||
} catch (error) {
|
||||
console.error('JSON格式错误:', error);
|
||||
message.error({ content: 'JSON格式错误,请修正后再提交!' });
|
||||
return;
|
||||
}
|
||||
@@ -176,33 +177,31 @@ async function updateDeviceConfig() {
|
||||
placeholder="请输入 JSON 格式的配置信息"
|
||||
class="json-editor"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.json-viewer-container {
|
||||
max-height: 600px;
|
||||
padding: 12px;
|
||||
overflow-y: auto;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.json-code {
|
||||
margin: 0;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
|
||||
font-family: Monaco, Menlo, 'Ubuntu Mono', Consolas, monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.json-editor {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
|
||||
font-family: Monaco, Menlo, 'Ubuntu Mono', Consolas, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
||||
import {
|
||||
computed,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
reactive,
|
||||
ref,
|
||||
watch,
|
||||
} from 'vue';
|
||||
|
||||
import { ContentWrap } from '@vben/common-ui';
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { formatDate } from '@vben/utils';
|
||||
|
||||
import { Button, Form, Pagination, Select, Switch, Table, Tag } from 'ant-design-vue';
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
Pagination,
|
||||
Select,
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { getDeviceMessagePage } from '#/api/iot/device/device';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { ThingModelData } from '#/api/iot/thingmodel';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { ContentWrap } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -19,16 +20,14 @@ import {
|
||||
Tabs,
|
||||
Textarea,
|
||||
} from 'ant-design-vue';
|
||||
import { DownOutlined, UpOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
import { DeviceStateEnum, sendDeviceMessage } from '#/api/iot/device/device';
|
||||
import DataDefinition from '#/views/iot/thingmodel/modules/components/DataDefinition.vue';
|
||||
import {
|
||||
IotDeviceMessageMethodEnum,
|
||||
IoTThingModelTypeEnum,
|
||||
} from '#/views/iot/utils/constants';
|
||||
|
||||
import DataDefinition from '#/views/iot/thingmodel/modules/components/DataDefinition.vue';
|
||||
|
||||
import DeviceDetailsMessage from './DeviceDetailsMessage.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -340,7 +339,7 @@ async function handleServiceInvoke(row: ThingModelData) {
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 上方:指令调试区域 -->
|
||||
<Card class="mb-4 simulator-tabs">
|
||||
<Card class="simulator-tabs mb-4">
|
||||
<template #title>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>指令调试</span>
|
||||
@@ -349,233 +348,233 @@ async function handleServiceInvoke(row: ThingModelData) {
|
||||
size="small"
|
||||
@click="debugCollapsed = !debugCollapsed"
|
||||
>
|
||||
<UpOutlined v-if="!debugCollapsed" />
|
||||
<DownOutlined v-if="debugCollapsed" />
|
||||
<IconifyIcon icon="lucide:chevron-up" v-if="!debugCollapsed" />
|
||||
<IconifyIcon icon="lucide:chevron-down" v-if="debugCollapsed" />
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
<div v-show="!debugCollapsed">
|
||||
<Tabs v-model:active-key="activeTab" size="small">
|
||||
<!-- 上行指令调试 -->
|
||||
<Tabs.TabPane key="upstream" tab="上行指令调试">
|
||||
<Tabs
|
||||
v-if="activeTab === 'upstream'"
|
||||
v-model:active-key="upstreamTab"
|
||||
size="small"
|
||||
<!-- 上行指令调试 -->
|
||||
<Tabs.TabPane key="upstream" tab="上行指令调试">
|
||||
<Tabs
|
||||
v-if="activeTab === 'upstream'"
|
||||
v-model:active-key="upstreamTab"
|
||||
size="small"
|
||||
>
|
||||
<!-- 属性上报 -->
|
||||
<Tabs.TabPane
|
||||
:key="IotDeviceMessageMethodEnum.PROPERTY_POST.method"
|
||||
tab="属性上报"
|
||||
>
|
||||
<!-- 属性上报 -->
|
||||
<Tabs.TabPane
|
||||
:key="IotDeviceMessageMethodEnum.PROPERTY_POST.method"
|
||||
tab="属性上报"
|
||||
>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:data-source="propertyList"
|
||||
align="center"
|
||||
:columns="propertyColumns"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 300 }"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dataType'">
|
||||
{{ record.property?.dataType ?? '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'dataDefinition'">
|
||||
<DataDefinition :data="record" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'value'">
|
||||
<Input
|
||||
:value="getFormValue(record.identifier)"
|
||||
@update:value="
|
||||
setFormValue(record.identifier, $event)
|
||||
"
|
||||
placeholder="输入值"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:data-source="propertyList"
|
||||
align="center"
|
||||
:columns="propertyColumns"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 300 }"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dataType'">
|
||||
{{ record.property?.dataType ?? '-' }}
|
||||
</template>
|
||||
</Table>
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<span class="text-sm text-gray-600">
|
||||
设置属性值后,点击「发送属性上报」按钮
|
||||
</span>
|
||||
<Button type="primary" @click="handlePropertyPost">
|
||||
发送属性上报
|
||||
</Button>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 事件上报 -->
|
||||
<Tabs.TabPane
|
||||
:key="IotDeviceMessageMethodEnum.EVENT_POST.method"
|
||||
tab="事件上报"
|
||||
>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:data-source="eventList"
|
||||
align="center"
|
||||
:columns="eventColumns"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 300 }"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dataType'">
|
||||
{{ record.event?.dataType ?? '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'dataDefinition'">
|
||||
<DataDefinition :data="record" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'value'">
|
||||
<Textarea
|
||||
:value="getFormValue(record.identifier)"
|
||||
@update:value="
|
||||
setFormValue(record.identifier, $event)
|
||||
"
|
||||
:rows="3"
|
||||
placeholder="输入事件参数(JSON格式)"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleEventPost(record)"
|
||||
>
|
||||
上报事件
|
||||
</Button>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'dataDefinition'">
|
||||
<DataDefinition :data="record" />
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
</Tabs.TabPane>
|
||||
<template v-else-if="column.key === 'value'">
|
||||
<Input
|
||||
:value="getFormValue(record.identifier)"
|
||||
@update:value="
|
||||
setFormValue(record.identifier, $event)
|
||||
"
|
||||
placeholder="输入值"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<span class="text-sm text-gray-600">
|
||||
设置属性值后,点击「发送属性上报」按钮
|
||||
</span>
|
||||
<Button type="primary" @click="handlePropertyPost">
|
||||
发送属性上报
|
||||
</Button>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 状态变更 -->
|
||||
<Tabs.TabPane
|
||||
:key="IotDeviceMessageMethodEnum.STATE_UPDATE.method"
|
||||
tab="状态变更"
|
||||
>
|
||||
<ContentWrap>
|
||||
<div class="flex gap-4">
|
||||
<Button
|
||||
type="primary"
|
||||
@click="handleDeviceState(DeviceStateEnum.ONLINE)"
|
||||
>
|
||||
设备上线
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
@click="handleDeviceState(DeviceStateEnum.OFFLINE)"
|
||||
>
|
||||
设备下线
|
||||
</Button>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 下行指令调试 -->
|
||||
<Tabs.TabPane key="downstream" tab="下行指令调试">
|
||||
<Tabs
|
||||
v-if="activeTab === 'downstream'"
|
||||
v-model:active-key="downstreamTab"
|
||||
size="small"
|
||||
<!-- 事件上报 -->
|
||||
<Tabs.TabPane
|
||||
:key="IotDeviceMessageMethodEnum.EVENT_POST.method"
|
||||
tab="事件上报"
|
||||
>
|
||||
<!-- 属性调试 -->
|
||||
<Tabs.TabPane
|
||||
:key="IotDeviceMessageMethodEnum.PROPERTY_SET.method"
|
||||
tab="属性设置"
|
||||
>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:data-source="propertyList"
|
||||
align="center"
|
||||
:columns="propertyColumns"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 300 }"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dataType'">
|
||||
{{ record.property?.dataType ?? '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'dataDefinition'">
|
||||
<DataDefinition :data="record" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'value'">
|
||||
<Input
|
||||
:value="getFormValue(record.identifier)"
|
||||
@update:value="
|
||||
setFormValue(record.identifier, $event)
|
||||
"
|
||||
placeholder="输入值"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:data-source="eventList"
|
||||
align="center"
|
||||
:columns="eventColumns"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 300 }"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dataType'">
|
||||
{{ record.event?.dataType ?? '-' }}
|
||||
</template>
|
||||
</Table>
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<span class="text-sm text-gray-600">
|
||||
设置属性值后,点击「发送属性设置」按钮
|
||||
</span>
|
||||
<Button type="primary" @click="handlePropertySet">
|
||||
发送属性设置
|
||||
</Button>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</Tabs.TabPane>
|
||||
<template v-else-if="column.key === 'dataDefinition'">
|
||||
<DataDefinition :data="record" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'value'">
|
||||
<Textarea
|
||||
:value="getFormValue(record.identifier)"
|
||||
@update:value="
|
||||
setFormValue(record.identifier, $event)
|
||||
"
|
||||
:rows="3"
|
||||
placeholder="输入事件参数(JSON格式)"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleEventPost(record)"
|
||||
>
|
||||
上报事件
|
||||
</Button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 服务调用 -->
|
||||
<Tabs.TabPane
|
||||
:key="IotDeviceMessageMethodEnum.SERVICE_INVOKE.method"
|
||||
tab="设备服务调用"
|
||||
>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:data-source="serviceList"
|
||||
align="center"
|
||||
:columns="serviceColumns"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 300 }"
|
||||
size="small"
|
||||
<!-- 状态变更 -->
|
||||
<Tabs.TabPane
|
||||
:key="IotDeviceMessageMethodEnum.STATE_UPDATE.method"
|
||||
tab="状态变更"
|
||||
>
|
||||
<ContentWrap>
|
||||
<div class="flex gap-4">
|
||||
<Button
|
||||
type="primary"
|
||||
@click="handleDeviceState(DeviceStateEnum.ONLINE)"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dataDefinition'">
|
||||
<DataDefinition :data="record" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'value'">
|
||||
<Textarea
|
||||
:value="getFormValue(record.identifier)"
|
||||
@update:value="
|
||||
setFormValue(record.identifier, $event)
|
||||
"
|
||||
:rows="3"
|
||||
placeholder="输入服务参数(JSON格式)"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleServiceInvoke(record)"
|
||||
>
|
||||
服务调用
|
||||
</Button>
|
||||
</template>
|
||||
设备上线
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
@click="handleDeviceState(DeviceStateEnum.OFFLINE)"
|
||||
>
|
||||
设备下线
|
||||
</Button>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 下行指令调试 -->
|
||||
<Tabs.TabPane key="downstream" tab="下行指令调试">
|
||||
<Tabs
|
||||
v-if="activeTab === 'downstream'"
|
||||
v-model:active-key="downstreamTab"
|
||||
size="small"
|
||||
>
|
||||
<!-- 属性调试 -->
|
||||
<Tabs.TabPane
|
||||
:key="IotDeviceMessageMethodEnum.PROPERTY_SET.method"
|
||||
tab="属性设置"
|
||||
>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:data-source="propertyList"
|
||||
align="center"
|
||||
:columns="propertyColumns"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 300 }"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dataType'">
|
||||
{{ record.property?.dataType ?? '-' }}
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</Card>
|
||||
<template v-else-if="column.key === 'dataDefinition'">
|
||||
<DataDefinition :data="record" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'value'">
|
||||
<Input
|
||||
:value="getFormValue(record.identifier)"
|
||||
@update:value="
|
||||
setFormValue(record.identifier, $event)
|
||||
"
|
||||
placeholder="输入值"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<span class="text-sm text-gray-600">
|
||||
设置属性值后,点击「发送属性设置」按钮
|
||||
</span>
|
||||
<Button type="primary" @click="handlePropertySet">
|
||||
发送属性设置
|
||||
</Button>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 服务调用 -->
|
||||
<Tabs.TabPane
|
||||
:key="IotDeviceMessageMethodEnum.SERVICE_INVOKE.method"
|
||||
tab="设备服务调用"
|
||||
>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:data-source="serviceList"
|
||||
align="center"
|
||||
:columns="serviceColumns"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 300 }"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dataDefinition'">
|
||||
<DataDefinition :data="record" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'value'">
|
||||
<Textarea
|
||||
:value="getFormValue(record.identifier)"
|
||||
@update:value="
|
||||
setFormValue(record.identifier, $event)
|
||||
"
|
||||
:rows="3"
|
||||
placeholder="输入服务参数(JSON格式)"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleServiceInvoke(record)"
|
||||
>
|
||||
服务调用
|
||||
</Button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<!-- 下方:设备消息区域 -->
|
||||
<Card>
|
||||
@@ -587,8 +586,8 @@ async function handleServiceInvoke(row: ThingModelData) {
|
||||
size="small"
|
||||
@click="messageCollapsed = !messageCollapsed"
|
||||
>
|
||||
<UpOutlined v-if="!messageCollapsed" />
|
||||
<DownOutlined v-if="messageCollapsed" />
|
||||
<IconifyIcon icon="lucide:chevron-down" v-if="!messageCollapsed" />
|
||||
<IconifyIcon icon="lucide:chevron-down" v-if="messageCollapsed" />
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -41,4 +41,3 @@ onMounted(() => {
|
||||
<!-- TODO: 实现子设备列表展示和管理功能 -->
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -137,7 +137,9 @@ async function getList() {
|
||||
try {
|
||||
const data = await getHistoryDevicePropertyList(queryParams);
|
||||
// 后端直接返回数组,不是 { list: [] } 格式
|
||||
list.value = (Array.isArray(data) ? data : (data?.list || [])) as IotDeviceApi.DevicePropertyDetail[];
|
||||
list.value = (
|
||||
Array.isArray(data) ? data : data?.list || []
|
||||
) as IotDeviceApi.DevicePropertyDetail[];
|
||||
total.value = list.value.length;
|
||||
|
||||
// 如果是图表模式且不是复杂数据类型,渲染图表
|
||||
@@ -176,29 +178,29 @@ function renderChart() {
|
||||
}
|
||||
|
||||
renderEcharts({
|
||||
title: {
|
||||
text: '属性值趋势',
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'normal',
|
||||
title: {
|
||||
text: '属性值趋势',
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'normal',
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: 60,
|
||||
right: 60,
|
||||
bottom: 100,
|
||||
top: 80,
|
||||
containLabel: true,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
grid: {
|
||||
left: 60,
|
||||
right: 60,
|
||||
bottom: 100,
|
||||
top: 80,
|
||||
containLabel: true,
|
||||
},
|
||||
formatter: (params: any) => {
|
||||
const param = params[0];
|
||||
return `
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
},
|
||||
formatter: (params: any) => {
|
||||
const param = params[0];
|
||||
return `
|
||||
<div style="padding: 8px;">
|
||||
<div style="margin-bottom: 4px; font-weight: bold;">
|
||||
${formatDate(new Date(param.value[0]), 'YYYY-MM-DD HH:mm:ss')}
|
||||
@@ -209,76 +211,76 @@ function renderChart() {
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
name: '时间',
|
||||
nameTextStyle: {
|
||||
padding: [10, 0, 0, 0],
|
||||
},
|
||||
axisLabel: {
|
||||
formatter: (value: number) => {
|
||||
return String(formatDate(new Date(value), 'MM-DD HH:mm') || '');
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '属性值',
|
||||
nameTextStyle: {
|
||||
padding: [0, 0, 10, 0],
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '属性值',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: '#1890FF',
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
name: '时间',
|
||||
nameTextStyle: {
|
||||
padding: [10, 0, 0, 0],
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#1890FF',
|
||||
},
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(24, 144, 255, 0.3)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(24, 144, 255, 0.05)',
|
||||
},
|
||||
],
|
||||
axisLabel: {
|
||||
formatter: (value: number) => {
|
||||
return String(formatDate(new Date(value), 'MM-DD HH:mm') || '');
|
||||
},
|
||||
},
|
||||
data: chartData,
|
||||
},
|
||||
],
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 100,
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '属性值',
|
||||
nameTextStyle: {
|
||||
padding: [0, 0, 10, 0],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'slider',
|
||||
height: 30,
|
||||
bottom: 20,
|
||||
},
|
||||
],
|
||||
});
|
||||
series: [
|
||||
{
|
||||
name: '属性值',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: '#1890FF',
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#1890FF',
|
||||
},
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(24, 144, 255, 0.3)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(24, 144, 255, 0.05)',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
data: chartData,
|
||||
},
|
||||
],
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 100,
|
||||
},
|
||||
{
|
||||
type: 'slider',
|
||||
height: 30,
|
||||
bottom: 20,
|
||||
},
|
||||
],
|
||||
});
|
||||
}, 300); // 延迟300ms渲染,确保 DOM 完全准备好
|
||||
}
|
||||
|
||||
|
||||
@@ -34,11 +34,9 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
label: '父级分组',
|
||||
component: 'ApiTreeSelect',
|
||||
componentProps: {
|
||||
api: getSimpleDeviceGroupList,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
api: () => getSimpleDeviceGroupList(),
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择父级分组',
|
||||
allowClear: true,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user