fix: iot warn
This commit is contained in:
@@ -101,6 +101,7 @@ export interface Action {
|
|||||||
identifier?: string;
|
identifier?: string;
|
||||||
value?: any;
|
value?: any;
|
||||||
alertConfigId?: number;
|
alertConfigId?: number;
|
||||||
|
params?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 查询场景联动规则分页 */
|
/** 查询场景联动规则分页 */
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
label: '关联场景联动规则',
|
label: '关联场景联动规则',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleRuleSceneList,
|
api: () => getSimpleRuleSceneList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
@@ -76,7 +76,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
label: '接收的用户',
|
label: '接收的用户',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleUserList,
|
api: () => getSimpleUserList(),
|
||||||
labelField: 'nickname',
|
labelField: 'nickname',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
label: '告警配置',
|
label: '告警配置',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleAlertConfigList,
|
api: () => getSimpleAlertConfigList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择告警配置',
|
placeholder: '请选择告警配置',
|
||||||
@@ -40,7 +40,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
label: '产品',
|
label: '产品',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductList,
|
api: () => getSimpleProductList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择产品',
|
placeholder: '请选择产品',
|
||||||
@@ -53,7 +53,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
label: '设备',
|
label: '设备',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleDeviceList,
|
api: () => getSimpleDeviceList(),
|
||||||
labelField: 'deviceName',
|
labelField: 'deviceName',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择设备',
|
placeholder: '请选择设备',
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ async function handleProcess(row: AlertRecord) {
|
|||||||
try {
|
try {
|
||||||
await processAlertRecord(row.id as number, processRemark);
|
await processAlertRecord(row.id as number, processRemark);
|
||||||
message.success('处理成功');
|
message.success('处理成功');
|
||||||
onRefresh();
|
handleRefresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('处理失败:', error);
|
console.error('处理失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { DICT_TYPE } from '@vben/constants';
|
|||||||
import { getDictOptions } from '@vben/hooks';
|
import { getDictOptions } from '@vben/hooks';
|
||||||
|
|
||||||
import { z } from '#/adapter/form';
|
import { z } from '#/adapter/form';
|
||||||
|
import { getSimpleDeviceList } from '#/api/iot/device/device';
|
||||||
import { getSimpleDeviceGroupList } from '#/api/iot/device/group';
|
import { getSimpleDeviceGroupList } from '#/api/iot/device/group';
|
||||||
import {
|
import {
|
||||||
DeviceTypeEnum,
|
DeviceTypeEnum,
|
||||||
@@ -27,7 +28,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
label: '产品',
|
label: '产品',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductList,
|
api: () => getSimpleProductList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择产品',
|
placeholder: '请选择产品',
|
||||||
@@ -55,12 +56,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
label: '网关设备',
|
label: '网关设备',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: async () => {
|
api: () => getSimpleDeviceList(DeviceTypeEnum.GATEWAY),
|
||||||
const { getSimpleDeviceList } = await import(
|
|
||||||
'#/api/iot/device/device'
|
|
||||||
);
|
|
||||||
return getSimpleDeviceList(DeviceTypeEnum.GATEWAY);
|
|
||||||
},
|
|
||||||
labelField: 'nickname',
|
labelField: 'nickname',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '子设备可选择父设备',
|
placeholder: '子设备可选择父设备',
|
||||||
@@ -93,7 +89,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
label: '设备分组',
|
label: '设备分组',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleDeviceGroupList,
|
api: () => getSimpleDeviceGroupList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
@@ -160,7 +156,7 @@ export function useGroupFormSchema(): VbenFormSchema[] {
|
|||||||
label: '设备分组',
|
label: '设备分组',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleDeviceGroupList,
|
api: () => getSimpleDeviceGroupList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
@@ -203,7 +199,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
label: '产品',
|
label: '产品',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductList,
|
api: () => getSimpleProductList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择产品',
|
placeholder: '请选择产品',
|
||||||
@@ -253,7 +249,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
label: '设备分组',
|
label: '设备分组',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleDeviceGroupList,
|
api: () => getSimpleDeviceGroupList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择设备分组',
|
placeholder: '请选择设备分组',
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import { getSimpleProductList } from '#/api/iot/product/product';
|
|||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
import { useGridColumns } from './data';
|
import { useGridColumns } from './data';
|
||||||
// @ts-ignore
|
|
||||||
import DeviceCardView from './modules/DeviceCardView.vue';
|
import DeviceCardView from './modules/DeviceCardView.vue';
|
||||||
import DeviceForm from './modules/DeviceForm.vue';
|
import DeviceForm from './modules/DeviceForm.vue';
|
||||||
import DeviceGroupForm from './modules/DeviceGroupForm.vue';
|
import DeviceGroupForm from './modules/DeviceGroupForm.vue';
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { IotDeviceApi } from '#/api/iot/device/device';
|
||||||
|
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { message } from 'ant-design-vue';
|
|
||||||
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
import {
|
import { message } from 'ant-design-vue';
|
||||||
createDevice,
|
|
||||||
getDevice,
|
import { createDevice, getDevice, updateDevice } from '#/api/iot/device/device';
|
||||||
updateDevice,
|
|
||||||
} from '#/api/iot/device/device';
|
|
||||||
import type { IotDeviceApi } from '#/api/iot/device/device';
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
import { useFormSchema } from '../data';
|
import { useFormSchema } from '../data';
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
// 关闭并提示
|
// 关闭并提示
|
||||||
await modalApi.close();
|
await modalApi.close();
|
||||||
emit('success');
|
emit('success');
|
||||||
message.success($t('common.updateSuccess'));
|
message.success($t('ui.actionMessage.operationSuccess'));
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ async function saveConfig() {
|
|||||||
try {
|
try {
|
||||||
config.value = JSON.parse(configString.value);
|
config.value = JSON.parse(configString.value);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('JSON格式错误:', error);
|
||||||
message.error({ content: 'JSON格式错误,请修正后再提交!' });
|
message.error({ content: 'JSON格式错误,请修正后再提交!' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -176,33 +177,31 @@ async function updateDeviceConfig() {
|
|||||||
placeholder="请输入 JSON 格式的配置信息"
|
placeholder="请输入 JSON 格式的配置信息"
|
||||||
class="json-editor"
|
class="json-editor"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.json-viewer-container {
|
.json-viewer-container {
|
||||||
|
max-height: 600px;
|
||||||
|
padding: 12px;
|
||||||
|
overflow-y: auto;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
border: 1px solid #d9d9d9;
|
border: 1px solid #d9d9d9;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 12px;
|
|
||||||
max-height: 600px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.json-code {
|
.json-code {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
|
font-family: Monaco, Menlo, 'Ubuntu Mono', Consolas, monospace;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: #333;
|
color: #333;
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.json-editor {
|
.json-editor {
|
||||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
|
font-family: Monaco, Menlo, 'Ubuntu Mono', Consolas, monospace;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,12 +1,27 @@
|
|||||||
<script setup lang="ts">
|
<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 { ContentWrap } from '@vben/common-ui';
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { formatDate } from '@vben/utils';
|
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 { getDeviceMessagePage } from '#/api/iot/device/device';
|
||||||
import { DictTag } from '#/components/dict-tag';
|
import { DictTag } from '#/components/dict-tag';
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import type { ThingModelData } from '#/api/iot/thingmodel';
|
|||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { ContentWrap } from '@vben/common-ui';
|
import { ContentWrap } from '@vben/common-ui';
|
||||||
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -19,16 +20,14 @@ import {
|
|||||||
Tabs,
|
Tabs,
|
||||||
Textarea,
|
Textarea,
|
||||||
} from 'ant-design-vue';
|
} from 'ant-design-vue';
|
||||||
import { DownOutlined, UpOutlined } from '@ant-design/icons-vue';
|
|
||||||
|
|
||||||
import { DeviceStateEnum, sendDeviceMessage } from '#/api/iot/device/device';
|
import { DeviceStateEnum, sendDeviceMessage } from '#/api/iot/device/device';
|
||||||
|
import DataDefinition from '#/views/iot/thingmodel/modules/components/DataDefinition.vue';
|
||||||
import {
|
import {
|
||||||
IotDeviceMessageMethodEnum,
|
IotDeviceMessageMethodEnum,
|
||||||
IoTThingModelTypeEnum,
|
IoTThingModelTypeEnum,
|
||||||
} from '#/views/iot/utils/constants';
|
} from '#/views/iot/utils/constants';
|
||||||
|
|
||||||
import DataDefinition from '#/views/iot/thingmodel/modules/components/DataDefinition.vue';
|
|
||||||
|
|
||||||
import DeviceDetailsMessage from './DeviceDetailsMessage.vue';
|
import DeviceDetailsMessage from './DeviceDetailsMessage.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -340,7 +339,7 @@ async function handleServiceInvoke(row: ThingModelData) {
|
|||||||
<template>
|
<template>
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<!-- 上方:指令调试区域 -->
|
<!-- 上方:指令调试区域 -->
|
||||||
<Card class="mb-4 simulator-tabs">
|
<Card class="simulator-tabs mb-4">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span>指令调试</span>
|
<span>指令调试</span>
|
||||||
@@ -349,233 +348,233 @@ async function handleServiceInvoke(row: ThingModelData) {
|
|||||||
size="small"
|
size="small"
|
||||||
@click="debugCollapsed = !debugCollapsed"
|
@click="debugCollapsed = !debugCollapsed"
|
||||||
>
|
>
|
||||||
<UpOutlined v-if="!debugCollapsed" />
|
<IconifyIcon icon="lucide:chevron-up" v-if="!debugCollapsed" />
|
||||||
<DownOutlined v-if="debugCollapsed" />
|
<IconifyIcon icon="lucide:chevron-down" v-if="debugCollapsed" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div v-show="!debugCollapsed">
|
<div v-show="!debugCollapsed">
|
||||||
<Tabs v-model:active-key="activeTab" size="small">
|
<Tabs v-model:active-key="activeTab" size="small">
|
||||||
<!-- 上行指令调试 -->
|
<!-- 上行指令调试 -->
|
||||||
<Tabs.TabPane key="upstream" tab="上行指令调试">
|
<Tabs.TabPane key="upstream" tab="上行指令调试">
|
||||||
<Tabs
|
<Tabs
|
||||||
v-if="activeTab === 'upstream'"
|
v-if="activeTab === 'upstream'"
|
||||||
v-model:active-key="upstreamTab"
|
v-model:active-key="upstreamTab"
|
||||||
size="small"
|
size="small"
|
||||||
|
>
|
||||||
|
<!-- 属性上报 -->
|
||||||
|
<Tabs.TabPane
|
||||||
|
:key="IotDeviceMessageMethodEnum.PROPERTY_POST.method"
|
||||||
|
tab="属性上报"
|
||||||
>
|
>
|
||||||
<!-- 属性上报 -->
|
<ContentWrap>
|
||||||
<Tabs.TabPane
|
<Table
|
||||||
:key="IotDeviceMessageMethodEnum.PROPERTY_POST.method"
|
:data-source="propertyList"
|
||||||
tab="属性上报"
|
align="center"
|
||||||
>
|
:columns="propertyColumns"
|
||||||
<ContentWrap>
|
:pagination="false"
|
||||||
<Table
|
:scroll="{ y: 300 }"
|
||||||
:data-source="propertyList"
|
size="small"
|
||||||
align="center"
|
>
|
||||||
:columns="propertyColumns"
|
<template #bodyCell="{ column, record }">
|
||||||
:pagination="false"
|
<template v-if="column.key === 'dataType'">
|
||||||
:scroll="{ y: 300 }"
|
{{ record.property?.dataType ?? '-' }}
|
||||||
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>
|
|
||||||
</template>
|
</template>
|
||||||
</Table>
|
<template v-else-if="column.key === 'dataDefinition'">
|
||||||
<div class="mt-4 flex items-center justify-between">
|
<DataDefinition :data="record" />
|
||||||
<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>
|
</template>
|
||||||
</Table>
|
<template v-else-if="column.key === 'value'">
|
||||||
</ContentWrap>
|
<Input
|
||||||
</Tabs.TabPane>
|
: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
|
<Tabs.TabPane
|
||||||
:key="IotDeviceMessageMethodEnum.STATE_UPDATE.method"
|
:key="IotDeviceMessageMethodEnum.EVENT_POST.method"
|
||||||
tab="状态变更"
|
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"
|
|
||||||
>
|
>
|
||||||
<!-- 属性调试 -->
|
<ContentWrap>
|
||||||
<Tabs.TabPane
|
<Table
|
||||||
:key="IotDeviceMessageMethodEnum.PROPERTY_SET.method"
|
:data-source="eventList"
|
||||||
tab="属性设置"
|
align="center"
|
||||||
>
|
:columns="eventColumns"
|
||||||
<ContentWrap>
|
:pagination="false"
|
||||||
<Table
|
:scroll="{ y: 300 }"
|
||||||
:data-source="propertyList"
|
size="small"
|
||||||
align="center"
|
>
|
||||||
:columns="propertyColumns"
|
<template #bodyCell="{ column, record }">
|
||||||
:pagination="false"
|
<template v-if="column.key === 'dataType'">
|
||||||
:scroll="{ y: 300 }"
|
{{ record.event?.dataType ?? '-' }}
|
||||||
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>
|
|
||||||
</template>
|
</template>
|
||||||
</Table>
|
<template v-else-if="column.key === 'dataDefinition'">
|
||||||
<div class="mt-4 flex items-center justify-between">
|
<DataDefinition :data="record" />
|
||||||
<span class="text-sm text-gray-600">
|
</template>
|
||||||
设置属性值后,点击「发送属性设置」按钮
|
<template v-else-if="column.key === 'value'">
|
||||||
</span>
|
<Textarea
|
||||||
<Button type="primary" @click="handlePropertySet">
|
:value="getFormValue(record.identifier)"
|
||||||
发送属性设置
|
@update:value="
|
||||||
</Button>
|
setFormValue(record.identifier, $event)
|
||||||
</div>
|
"
|
||||||
</ContentWrap>
|
:rows="3"
|
||||||
</Tabs.TabPane>
|
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
|
<Tabs.TabPane
|
||||||
:key="IotDeviceMessageMethodEnum.SERVICE_INVOKE.method"
|
:key="IotDeviceMessageMethodEnum.STATE_UPDATE.method"
|
||||||
tab="设备服务调用"
|
tab="状态变更"
|
||||||
>
|
>
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<Table
|
<div class="flex gap-4">
|
||||||
:data-source="serviceList"
|
<Button
|
||||||
align="center"
|
type="primary"
|
||||||
:columns="serviceColumns"
|
@click="handleDeviceState(DeviceStateEnum.ONLINE)"
|
||||||
:pagination="false"
|
|
||||||
:scroll="{ y: 300 }"
|
|
||||||
size="small"
|
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
设备上线
|
||||||
<template v-if="column.key === 'dataDefinition'">
|
</Button>
|
||||||
<DataDefinition :data="record" />
|
<Button
|
||||||
</template>
|
danger
|
||||||
<template v-else-if="column.key === 'value'">
|
@click="handleDeviceState(DeviceStateEnum.OFFLINE)"
|
||||||
<Textarea
|
>
|
||||||
:value="getFormValue(record.identifier)"
|
设备下线
|
||||||
@update:value="
|
</Button>
|
||||||
setFormValue(record.identifier, $event)
|
</div>
|
||||||
"
|
</ContentWrap>
|
||||||
:rows="3"
|
</Tabs.TabPane>
|
||||||
placeholder="输入服务参数(JSON格式)"
|
</Tabs>
|
||||||
size="small"
|
</Tabs.TabPane>
|
||||||
/>
|
|
||||||
</template>
|
<!-- 下行指令调试 -->
|
||||||
<template v-else-if="column.key === 'action'">
|
<Tabs.TabPane key="downstream" tab="下行指令调试">
|
||||||
<Button
|
<Tabs
|
||||||
type="primary"
|
v-if="activeTab === 'downstream'"
|
||||||
size="small"
|
v-model:active-key="downstreamTab"
|
||||||
@click="handleServiceInvoke(record)"
|
size="small"
|
||||||
>
|
>
|
||||||
服务调用
|
<!-- 属性调试 -->
|
||||||
</Button>
|
<Tabs.TabPane
|
||||||
</template>
|
: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>
|
||||||
</Table>
|
<template v-else-if="column.key === 'dataDefinition'">
|
||||||
</ContentWrap>
|
<DataDefinition :data="record" />
|
||||||
</Tabs.TabPane>
|
</template>
|
||||||
</Tabs>
|
<template v-else-if="column.key === 'value'">
|
||||||
</Tabs.TabPane>
|
<Input
|
||||||
</Tabs>
|
:value="getFormValue(record.identifier)"
|
||||||
</div>
|
@update:value="
|
||||||
</Card>
|
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>
|
<Card>
|
||||||
@@ -587,8 +586,8 @@ async function handleServiceInvoke(row: ThingModelData) {
|
|||||||
size="small"
|
size="small"
|
||||||
@click="messageCollapsed = !messageCollapsed"
|
@click="messageCollapsed = !messageCollapsed"
|
||||||
>
|
>
|
||||||
<UpOutlined v-if="!messageCollapsed" />
|
<IconifyIcon icon="lucide:chevron-down" v-if="!messageCollapsed" />
|
||||||
<DownOutlined v-if="messageCollapsed" />
|
<IconifyIcon icon="lucide:chevron-down" v-if="messageCollapsed" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -41,4 +41,3 @@ onMounted(() => {
|
|||||||
<!-- TODO: 实现子设备列表展示和管理功能 -->
|
<!-- TODO: 实现子设备列表展示和管理功能 -->
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -137,7 +137,9 @@ async function getList() {
|
|||||||
try {
|
try {
|
||||||
const data = await getHistoryDevicePropertyList(queryParams);
|
const data = await getHistoryDevicePropertyList(queryParams);
|
||||||
// 后端直接返回数组,不是 { list: [] } 格式
|
// 后端直接返回数组,不是 { 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;
|
total.value = list.value.length;
|
||||||
|
|
||||||
// 如果是图表模式且不是复杂数据类型,渲染图表
|
// 如果是图表模式且不是复杂数据类型,渲染图表
|
||||||
@@ -176,29 +178,29 @@ function renderChart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderEcharts({
|
renderEcharts({
|
||||||
title: {
|
title: {
|
||||||
text: '属性值趋势',
|
text: '属性值趋势',
|
||||||
left: 'center',
|
left: 'center',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
grid: {
|
||||||
grid: {
|
left: 60,
|
||||||
left: 60,
|
right: 60,
|
||||||
right: 60,
|
bottom: 100,
|
||||||
bottom: 100,
|
top: 80,
|
||||||
top: 80,
|
containLabel: true,
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
axisPointer: {
|
|
||||||
type: 'cross',
|
|
||||||
},
|
},
|
||||||
formatter: (params: any) => {
|
tooltip: {
|
||||||
const param = params[0];
|
trigger: 'axis',
|
||||||
return `
|
axisPointer: {
|
||||||
|
type: 'cross',
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const param = params[0];
|
||||||
|
return `
|
||||||
<div style="padding: 8px;">
|
<div style="padding: 8px;">
|
||||||
<div style="margin-bottom: 4px; font-weight: bold;">
|
<div style="margin-bottom: 4px; font-weight: bold;">
|
||||||
${formatDate(new Date(param.value[0]), 'YYYY-MM-DD HH:mm:ss')}
|
${formatDate(new Date(param.value[0]), 'YYYY-MM-DD HH:mm:ss')}
|
||||||
@@ -209,76 +211,76 @@ function renderChart() {
|
|||||||
</div>
|
</div>
|
||||||
</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') || '');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
xAxis: {
|
||||||
yAxis: {
|
type: 'time',
|
||||||
type: 'value',
|
name: '时间',
|
||||||
name: '属性值',
|
nameTextStyle: {
|
||||||
nameTextStyle: {
|
padding: [10, 0, 0, 0],
|
||||||
padding: [0, 0, 10, 0],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '属性值',
|
|
||||||
type: 'line',
|
|
||||||
smooth: true,
|
|
||||||
symbol: 'circle',
|
|
||||||
symbolSize: 6,
|
|
||||||
lineStyle: {
|
|
||||||
width: 2,
|
|
||||||
color: '#1890FF',
|
|
||||||
},
|
},
|
||||||
itemStyle: {
|
axisLabel: {
|
||||||
color: '#1890FF',
|
formatter: (value: number) => {
|
||||||
},
|
return String(formatDate(new Date(value), 'MM-DD HH:mm') || '');
|
||||||
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,
|
|
||||||
},
|
},
|
||||||
],
|
yAxis: {
|
||||||
dataZoom: [
|
type: 'value',
|
||||||
{
|
name: '属性值',
|
||||||
type: 'inside',
|
nameTextStyle: {
|
||||||
start: 0,
|
padding: [0, 0, 10, 0],
|
||||||
end: 100,
|
},
|
||||||
},
|
},
|
||||||
{
|
series: [
|
||||||
type: 'slider',
|
{
|
||||||
height: 30,
|
name: '属性值',
|
||||||
bottom: 20,
|
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 完全准备好
|
}, 300); // 延迟300ms渲染,确保 DOM 完全准备好
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,11 +34,9 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
label: '父级分组',
|
label: '父级分组',
|
||||||
component: 'ApiTreeSelect',
|
component: 'ApiTreeSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleDeviceGroupList,
|
api: () => getSimpleDeviceGroupList(),
|
||||||
fieldNames: {
|
labelField: 'name',
|
||||||
label: 'name',
|
valueField: 'id',
|
||||||
value: 'id',
|
|
||||||
},
|
|
||||||
placeholder: '请选择父级分组',
|
placeholder: '请选择父级分组',
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
label: '所属产品',
|
label: '所属产品',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductList,
|
api: () => getSimpleProductList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择产品',
|
placeholder: '请选择产品',
|
||||||
@@ -85,7 +85,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
label: '产品',
|
label: '产品',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductList,
|
api: () => getSimpleProductList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择产品',
|
placeholder: '请选择产品',
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
label: '所属产品',
|
label: '所属产品',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductList,
|
api: () => getSimpleProductList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择产品',
|
placeholder: '请选择产品',
|
||||||
@@ -86,7 +86,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
label: '产品',
|
label: '产品',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductList,
|
api: () => getSimpleProductList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择产品',
|
placeholder: '请选择产品',
|
||||||
|
|||||||
@@ -34,11 +34,9 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
label: '父级分类',
|
label: '父级分类',
|
||||||
component: 'ApiTreeSelect',
|
component: 'ApiTreeSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductCategoryList,
|
api: () => getSimpleProductCategoryList(),
|
||||||
fieldNames: {
|
labelField: 'name',
|
||||||
label: 'name',
|
valueField: 'id',
|
||||||
value: 'id',
|
|
||||||
},
|
|
||||||
placeholder: '请选择父级分类',
|
placeholder: '请选择父级分类',
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export function useFormSchema(formApi?: any): VbenFormSchema[] {
|
|||||||
label: '产品分类',
|
label: '产品分类',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductCategoryList,
|
api: () => getSimpleProductCategoryList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择产品分类',
|
placeholder: '请选择产品分类',
|
||||||
@@ -246,7 +246,7 @@ export function useBasicFormSchema(formApi?: any): VbenFormSchema[] {
|
|||||||
label: '产品分类',
|
label: '产品分类',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductCategoryList,
|
api: () => getSimpleProductCategoryList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择产品分类',
|
placeholder: '请选择产品分类',
|
||||||
|
|||||||
@@ -11,16 +11,15 @@ import { downloadFileFromBlobPart } from '@vben/utils';
|
|||||||
import { Button, Card, Image, Input, message, Space } from 'ant-design-vue';
|
import { Button, Card, Image, Input, message, Space } from 'ant-design-vue';
|
||||||
|
|
||||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getSimpleProductCategoryList } from '#/api/iot/product/category';
|
||||||
import {
|
import {
|
||||||
deleteProduct,
|
deleteProduct,
|
||||||
exportProduct,
|
exportProduct,
|
||||||
getProductPage,
|
getProductPage,
|
||||||
} from '#/api/iot/product/product';
|
} from '#/api/iot/product/product';
|
||||||
import { getSimpleProductCategoryList } from '#/api/iot/product/category';
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
import { useGridColumns, useImagePreview } from './data';
|
import { useGridColumns, useImagePreview } from './data';
|
||||||
// @ts-ignore
|
|
||||||
import ProductCardView from './modules/ProductCardView.vue';
|
import ProductCardView from './modules/ProductCardView.vue';
|
||||||
import ProductForm from './modules/ProductForm.vue';
|
import ProductForm from './modules/ProductForm.vue';
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,18 @@ import {
|
|||||||
} from '#/api/iot/product/product';
|
} from '#/api/iot/product/product';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
import { generateProductKey, useBasicFormSchema, useAdvancedFormSchema } from '../data';
|
import {
|
||||||
|
generateProductKey,
|
||||||
|
useAdvancedFormSchema,
|
||||||
|
useBasicFormSchema,
|
||||||
|
} from '../data';
|
||||||
|
|
||||||
defineOptions({ name: 'IoTProductForm' });
|
defineOptions({ name: 'IoTProductForm' });
|
||||||
|
|
||||||
|
const emit = defineEmits(['success']);
|
||||||
|
|
||||||
const CollapsePanel = Collapse.Panel;
|
const CollapsePanel = Collapse.Panel;
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
|
||||||
const formData = ref<any>();
|
const formData = ref<any>();
|
||||||
const getTitle = computed(() => {
|
const getTitle = computed(() => {
|
||||||
return formData.value?.id ? '编辑产品' : '新增产品';
|
return formData.value?.id ? '编辑产品' : '新增产品';
|
||||||
@@ -84,7 +89,10 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const values = { ...basicValues, ...advancedValues } as IotProductApi.Product;
|
const values = {
|
||||||
|
...basicValues,
|
||||||
|
...advancedValues,
|
||||||
|
} as IotProductApi.Product;
|
||||||
const data = formData.value?.id
|
const data = formData.value?.id
|
||||||
? { ...values, id: formData.value.id }
|
? { ...values, id: formData.value.id }
|
||||||
: values;
|
: values;
|
||||||
@@ -132,7 +140,11 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 如果有图标、图片或描述,自动展开折叠面板以便显示
|
// 如果有图标、图片或描述,自动展开折叠面板以便显示
|
||||||
if (formData.value.icon || formData.value.picUrl || formData.value.description) {
|
if (
|
||||||
|
formData.value.icon ||
|
||||||
|
formData.value.picUrl ||
|
||||||
|
formData.value.description
|
||||||
|
) {
|
||||||
activeKey.value = ['advanced'];
|
activeKey.value = ['advanced'];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ interface Props {
|
|||||||
defineProps<Props>();
|
defineProps<Props>();
|
||||||
|
|
||||||
/** 格式化日期 */
|
/** 格式化日期 */
|
||||||
const formatDate = (date?: Date | string) => {
|
function formatDate(date?: Date | string) {
|
||||||
if (!date) return '-';
|
if (!date) return '-';
|
||||||
return new Date(date).toLocaleString('zh-CN');
|
return new Date(date).toLocaleString('zh-CN');
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
label: '产品',
|
label: '产品',
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: getSimpleProductList,
|
api: () => getSimpleProductList(),
|
||||||
labelField: 'name',
|
labelField: 'name',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
placeholder: '请选择产品',
|
placeholder: '请选择产品',
|
||||||
|
|||||||
@@ -210,7 +210,8 @@ function handleOperatorChange() {
|
|||||||
<!-- 设备状态条件配置 -->
|
<!-- 设备状态条件配置 -->
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS
|
condition.type ===
|
||||||
|
IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS.toString()
|
||||||
"
|
"
|
||||||
class="gap-16px flex flex-col"
|
class="gap-16px flex flex-col"
|
||||||
>
|
>
|
||||||
@@ -222,7 +223,7 @@ function handleOperatorChange() {
|
|||||||
<Select
|
<Select
|
||||||
:model-value="condition.operator"
|
:model-value="condition.operator"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(value) => updateConditionField('operator', value)
|
(value: any) => updateConditionField('operator', value)
|
||||||
"
|
"
|
||||||
placeholder="请选择操作符"
|
placeholder="请选择操作符"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ const timeValue2 = computed(() => {
|
|||||||
* @param value 字段值
|
* @param value 字段值
|
||||||
*/
|
*/
|
||||||
function updateConditionField(field: any, value: any) {
|
function updateConditionField(field: any, value: any) {
|
||||||
condition.value[field] = value;
|
(condition.value as any)[field] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
options: getDictOptions(DICT_TYPE.IOT_THING_MODEL_TYPE, 'number'),
|
options: getDictOptions(DICT_TYPE.IOT_THING_MODEL_TYPE, 'number'),
|
||||||
placeholder: '请选择功能类型',
|
placeholder: '请选择功能类型',
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, provide, ref } from 'vue';
|
|
||||||
|
|
||||||
import { message } from 'ant-design-vue';
|
|
||||||
import { Page } from '@vben/common-ui';
|
|
||||||
|
|
||||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
|
||||||
import { deleteThingModel, getThingModelPage } from '#/api/iot/thingmodel';
|
|
||||||
import { getProduct } from '#/api/iot/product/product';
|
|
||||||
import type { IotProductApi } from '#/api/iot/product/product';
|
import type { IotProductApi } from '#/api/iot/product/product';
|
||||||
|
|
||||||
import { useGridColumns, useGridFormSchema } from './data';
|
import { onMounted, provide, ref } from 'vue';
|
||||||
|
|
||||||
|
import { Page } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getProduct } from '#/api/iot/product/product';
|
||||||
|
import { deleteThingModel, getThingModelPage } from '#/api/iot/thingmodel';
|
||||||
|
|
||||||
import { getDataTypeOptionsLabel, IOT_PROVIDE_KEY } from '../utils/constants';
|
import { getDataTypeOptionsLabel, IOT_PROVIDE_KEY } from '../utils/constants';
|
||||||
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
|
import { DataDefinition } from './modules/components';
|
||||||
import ThingModelForm from './modules/ThingModelForm.vue';
|
import ThingModelForm from './modules/ThingModelForm.vue';
|
||||||
import ThingModelTSL from './modules/ThingModelTSL.vue';
|
import ThingModelTSL from './modules/ThingModelTSL.vue';
|
||||||
import { DataDefinition } from './modules/components';
|
|
||||||
|
|
||||||
defineOptions({ name: 'IoTThingModel' });
|
defineOptions({ name: 'IoTThingModel' });
|
||||||
|
|
||||||
@@ -119,7 +121,7 @@ onMounted(async () => {
|
|||||||
description="管理产品的物模型定义,包括属性、服务和事件"
|
description="管理产品的物模型定义,包括属性、服务和事件"
|
||||||
title="物模型管理"
|
title="物模型管理"
|
||||||
>
|
>
|
||||||
<Grid ref="xGrid">
|
<Grid>
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<TableAction
|
<TableAction
|
||||||
:actions="[
|
:actions="[
|
||||||
|
|||||||
@@ -21,11 +21,7 @@ defineOptions({ name: 'ThingModelEvent' });
|
|||||||
|
|
||||||
const props = defineProps<{ isStructDataSpecs?: boolean; modelValue: any }>();
|
const props = defineProps<{ isStructDataSpecs?: boolean; modelValue: any }>();
|
||||||
const emits = defineEmits(['update:modelValue']);
|
const emits = defineEmits(['update:modelValue']);
|
||||||
const thingModelEvent = useVModel(
|
const thingModelEvent = useVModel(props, 'modelValue', emits) as Ref<any>;
|
||||||
props,
|
|
||||||
'modelValue',
|
|
||||||
emits,
|
|
||||||
) as Ref<any>;
|
|
||||||
|
|
||||||
// 默认选中,INFO 信息
|
// 默认选中,INFO 信息
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ const formRef = ref(); // 表单 Ref
|
|||||||
async function open(type: string, id?: number) {
|
async function open(type: string, id?: number) {
|
||||||
dialogVisible.value = true;
|
dialogVisible.value = true;
|
||||||
// 设置标题:create -> 新增,update -> 编辑
|
// 设置标题:create -> 新增,update -> 编辑
|
||||||
dialogTitle.value = type === 'create' ? $t('page.action.add') : $t('page.action.edit');
|
dialogTitle.value =
|
||||||
|
type === 'create' ? $t('page.action.add') : $t('page.action.edit');
|
||||||
formType.value = type;
|
formType.value = type;
|
||||||
resetForm();
|
resetForm();
|
||||||
if (id) {
|
if (id) {
|
||||||
|
|||||||
@@ -42,8 +42,13 @@ const getDataTypeOptions2 = computed(() => {
|
|||||||
if (!props.isStructDataSpecs) {
|
if (!props.isStructDataSpecs) {
|
||||||
return getDataTypeOptions();
|
return getDataTypeOptions();
|
||||||
}
|
}
|
||||||
const excludedTypes = [IoTDataSpecsDataTypeEnum.STRUCT, IoTDataSpecsDataTypeEnum.ARRAY];
|
const excludedTypes = new Set([
|
||||||
return getDataTypeOptions().filter((item: any) => !excludedTypes.includes(item.value));
|
IoTDataSpecsDataTypeEnum.ARRAY,
|
||||||
|
IoTDataSpecsDataTypeEnum.STRUCT,
|
||||||
|
]);
|
||||||
|
return getDataTypeOptions().filter(
|
||||||
|
(item: any) => !excludedTypes.has(item.value),
|
||||||
|
);
|
||||||
}); // 获得数据类型列表
|
}); // 获得数据类型列表
|
||||||
|
|
||||||
/** 属性值的数据类型切换时初始化相关数据 */
|
/** 属性值的数据类型切换时初始化相关数据 */
|
||||||
@@ -52,19 +57,11 @@ function handleChange(dataType: any) {
|
|||||||
property.value.dataSpecsList = [];
|
property.value.dataSpecsList = [];
|
||||||
// 不是列表型数据才设置 dataSpecs.dataType
|
// 不是列表型数据才设置 dataSpecs.dataType
|
||||||
![
|
![
|
||||||
IoTDataSpecsDataTypeEnum.ENUM,
|
|
||||||
IoTDataSpecsDataTypeEnum.BOOL,
|
IoTDataSpecsDataTypeEnum.BOOL,
|
||||||
|
IoTDataSpecsDataTypeEnum.ENUM,
|
||||||
IoTDataSpecsDataTypeEnum.STRUCT,
|
IoTDataSpecsDataTypeEnum.STRUCT,
|
||||||
].includes(dataType) && (property.value.dataSpecs.dataType = dataType);
|
].includes(dataType) && (property.value.dataSpecs.dataType = dataType);
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case IoTDataSpecsDataTypeEnum.ENUM: {
|
|
||||||
property.value.dataSpecsList.push({
|
|
||||||
dataType: IoTDataSpecsDataTypeEnum.ENUM,
|
|
||||||
name: '', // 枚举项的名称
|
|
||||||
value: undefined, // 枚举值
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IoTDataSpecsDataTypeEnum.BOOL: {
|
case IoTDataSpecsDataTypeEnum.BOOL: {
|
||||||
for (let i = 0; i < 2; i++) {
|
for (let i = 0; i < 2; i++) {
|
||||||
property.value.dataSpecsList.push({
|
property.value.dataSpecsList.push({
|
||||||
@@ -75,6 +72,14 @@ function handleChange(dataType: any) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case IoTDataSpecsDataTypeEnum.ENUM: {
|
||||||
|
property.value.dataSpecsList.push({
|
||||||
|
dataType: IoTDataSpecsDataTypeEnum.ENUM,
|
||||||
|
name: '', // 枚举项的名称
|
||||||
|
value: undefined, // 枚举值
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// useVModel 会自动同步数据到父组件,不需要手动 emit
|
// useVModel 会自动同步数据到父组件,不需要手动 emit
|
||||||
}
|
}
|
||||||
@@ -95,9 +100,7 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Form.Item
|
<Form.Item label="数据类型">
|
||||||
label="数据类型"
|
|
||||||
>
|
|
||||||
<Select
|
<Select
|
||||||
v-model:value="property.dataType"
|
v-model:value="property.dataType"
|
||||||
placeholder="请选择数据类型"
|
placeholder="请选择数据类型"
|
||||||
@@ -135,7 +138,7 @@ watch(
|
|||||||
label="布尔值"
|
label="布尔值"
|
||||||
>
|
>
|
||||||
<template v-for="item in property.dataSpecsList" :key="item.value">
|
<template v-for="item in property.dataSpecsList" :key="item.value">
|
||||||
<div class="flex items-center justify-start w-1/1 mb-5px">
|
<div class="w-1/1 mb-5px flex items-center justify-start">
|
||||||
<span>{{ item.value }}</span>
|
<span>{{ item.value }}</span>
|
||||||
<span class="mx-2">-</span>
|
<span class="mx-2">-</span>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
|
|||||||
@@ -92,26 +92,26 @@ watch(tslString, (newValue) => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.json-viewer-container {
|
.json-viewer-container {
|
||||||
|
max-height: 600px;
|
||||||
|
padding: 12px;
|
||||||
|
overflow-y: auto;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
border: 1px solid #d9d9d9;
|
border: 1px solid #d9d9d9;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 12px;
|
|
||||||
max-height: 600px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.json-code {
|
.json-code {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
|
font-family: Monaco, Menlo, 'Ubuntu Mono', Consolas, monospace;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: #333;
|
color: #333;
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.json-editor {
|
.json-editor {
|
||||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
|
font-family: Monaco, Menlo, 'Ubuntu Mono', Consolas, monospace;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -19,22 +19,30 @@ const props = defineProps<{ data: ThingModelData }>();
|
|||||||
|
|
||||||
// 格式化布尔值和枚举值列表为字符串
|
// 格式化布尔值和枚举值列表为字符串
|
||||||
const formattedDataSpecsList = computed(() => {
|
const formattedDataSpecsList = computed(() => {
|
||||||
if (!props.data.property?.dataSpecsList || props.data.property.dataSpecsList.length === 0) {
|
if (
|
||||||
|
!props.data.property?.dataSpecsList ||
|
||||||
|
props.data.property.dataSpecsList.length === 0
|
||||||
|
) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return props.data.property.dataSpecsList
|
return props.data.property.dataSpecsList
|
||||||
.map(item => `${item.value}-${item.name}`)
|
.map((item) => `${item.value}-${item.name}`)
|
||||||
.join('、');
|
.join('、');
|
||||||
});
|
});
|
||||||
|
|
||||||
// 显示的简短文本(第一个值)
|
// 显示的简短文本(第一个值)
|
||||||
const shortText = computed(() => {
|
const shortText = computed(() => {
|
||||||
if (!props.data.property?.dataSpecsList || props.data.property.dataSpecsList.length === 0) {
|
if (
|
||||||
|
!props.data.property?.dataSpecsList ||
|
||||||
|
props.data.property.dataSpecsList.length === 0
|
||||||
|
) {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
const first = props.data.property.dataSpecsList[0];
|
const first = props.data.property.dataSpecsList[0];
|
||||||
const count = props.data.property.dataSpecsList.length;
|
const count = props.data.property.dataSpecsList.length;
|
||||||
return count > 1 ? `${first.value}-${first.name} 等${count}项` : `${first.value}-${first.name}`;
|
return count > 1
|
||||||
|
? `${first.value}-${first.name} 等${count}项`
|
||||||
|
: `${first.value}-${first.name}`;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -108,8 +116,8 @@ const shortText = computed(() => {
|
|||||||
border-bottom: 1px dashed #d9d9d9;
|
border-bottom: 1px dashed #d9d9d9;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-bottom-color: #1890ff;
|
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
|
border-bottom-color: #1890ff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ function handleChange(val: any) {
|
|||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="元素个数" name="property.dataSpecs.size">
|
<Form.Item label="元素个数" name="property.dataSpecs.size">
|
||||||
<Input v-model:value="dataSpecs.size" placeholder="请输入数组中的元素个数" />
|
<Input
|
||||||
|
v-model:value="dataSpecs.size"
|
||||||
|
placeholder="请输入数组中的元素个数"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<!-- Struct 型配置-->
|
<!-- Struct 型配置-->
|
||||||
<ThingModelStructDataSpecs
|
<ThingModelStructDataSpecs
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ function deleteEnum(index: number) {
|
|||||||
}
|
}
|
||||||
dataSpecsList.value.splice(index, 1);
|
dataSpecsList.value.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -41,7 +40,7 @@ function deleteEnum(index: number) {
|
|||||||
<div
|
<div
|
||||||
v-for="(item, index) in dataSpecsList"
|
v-for="(item, index) in dataSpecsList"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="flex items-center justify-between mb-5px"
|
class="mb-5px flex items-center justify-between"
|
||||||
>
|
>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<Input v-model:value="item.value" placeholder="请输入枚举值,如'0'" />
|
<Input v-model:value="item.value" placeholder="请输入枚举值,如'0'" />
|
||||||
|
|||||||
@@ -1,4 +1,35 @@
|
|||||||
<!-- dataType:number 数组类型 -->
|
<!-- dataType:number 数组类型 -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
|
||||||
|
import type { DataSpecsNumberData } from '#/api/iot/thingmodel';
|
||||||
|
|
||||||
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
import { getDictOptions } from '@vben/hooks';
|
||||||
|
|
||||||
|
import { useVModel } from '@vueuse/core';
|
||||||
|
import { Form, Input, Select } from 'ant-design-vue';
|
||||||
|
|
||||||
|
/** 数值型的 dataSpecs 配置组件 */
|
||||||
|
defineOptions({ name: 'ThingModelNumberDataSpecs' });
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: any }>();
|
||||||
|
const emits = defineEmits(['update:modelValue']);
|
||||||
|
const dataSpecs = useVModel(
|
||||||
|
props,
|
||||||
|
'modelValue',
|
||||||
|
emits,
|
||||||
|
) as Ref<DataSpecsNumberData>;
|
||||||
|
|
||||||
|
/** 单位发生变化时触发 */
|
||||||
|
const unitChange = (UnitSpecs: any) => {
|
||||||
|
if (!UnitSpecs) return;
|
||||||
|
const [unitName, unit] = String(UnitSpecs).split('-');
|
||||||
|
dataSpecs.value.unitName = unitName;
|
||||||
|
dataSpecs.value.unit = unit;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Form.Item label="取值范围">
|
<Form.Item label="取值范围">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
@@ -38,38 +69,6 @@
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import type { Ref } from 'vue';
|
|
||||||
|
|
||||||
import type { DataSpecsNumberData } from '#/api/iot/thingmodel';
|
|
||||||
|
|
||||||
import { useVModel } from '@vueuse/core';
|
|
||||||
|
|
||||||
import { Form, Input, Select } from 'ant-design-vue';
|
|
||||||
|
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
|
||||||
import { getDictOptions } from '@vben/hooks';
|
|
||||||
|
|
||||||
/** 数值型的 dataSpecs 配置组件 */
|
|
||||||
defineOptions({ name: 'ThingModelNumberDataSpecs' });
|
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: any }>();
|
|
||||||
const emits = defineEmits(['update:modelValue']);
|
|
||||||
const dataSpecs = useVModel(
|
|
||||||
props,
|
|
||||||
'modelValue',
|
|
||||||
emits,
|
|
||||||
) as Ref<DataSpecsNumberData>;
|
|
||||||
|
|
||||||
/** 单位发生变化时触发 */
|
|
||||||
const unitChange = (UnitSpecs: any) => {
|
|
||||||
if (!UnitSpecs) return;
|
|
||||||
const [unitName, unit] = String(UnitSpecs).split('-');
|
|
||||||
dataSpecs.value.unitName = unitName;
|
|
||||||
dataSpecs.value.unit = unit;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
:deep(.ant-form-item) {
|
:deep(.ant-form-item) {
|
||||||
.ant-form-item {
|
.ant-form-item {
|
||||||
|
|||||||
@@ -115,7 +115,6 @@ function resetForm() {
|
|||||||
structFormRef.value?.resetFields();
|
structFormRef.value?.resetFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** 组件初始化 */
|
/** 组件初始化 */
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
@@ -134,18 +133,14 @@ onMounted(async () => {
|
|||||||
>
|
>
|
||||||
<span>参数:{{ item.name }}</span>
|
<span>参数:{{ item.name }}</span>
|
||||||
<div class="btn">
|
<div class="btn">
|
||||||
<Button type="link" @click="openStructForm(item)">
|
<Button type="link" @click="openStructForm(item)"> 编辑 </Button>
|
||||||
编辑
|
|
||||||
</Button>
|
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
<Button type="link" danger @click="deleteStructItem(index)">
|
<Button type="link" danger @click="deleteStructItem(index)">
|
||||||
删除
|
删除
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button type="link" @click="openStructForm(null)">
|
<Button type="link" @click="openStructForm(null)"> +新增参数 </Button>
|
||||||
+新增参数
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<!-- struct 表单 -->
|
<!-- struct 表单 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user