commit
966f1b8b7e
|
|
@ -80,17 +80,21 @@ export interface RedisStreamMQConfig extends Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 数据桥梁类型 */
|
/** 数据桥梁类型 */
|
||||||
// TODO @puhui999:枚举用 number 可以么?
|
|
||||||
export const IoTDataBridgeConfigType = {
|
export const IoTDataBridgeConfigType = {
|
||||||
HTTP: '1',
|
HTTP: 1,
|
||||||
TCP: '2',
|
TCP: 2,
|
||||||
WEBSOCKET: '3',
|
WEBSOCKET: 3,
|
||||||
MQTT: '10',
|
MQTT: 10,
|
||||||
DATABASE: '20',
|
DATABASE: 20,
|
||||||
REDIS_STREAM: '21',
|
REDIS_STREAM: 21,
|
||||||
ROCKETMQ: '30',
|
ROCKETMQ: 30,
|
||||||
RABBITMQ: '31',
|
RABBITMQ: 31,
|
||||||
KAFKA: '32'
|
KAFKA: 32
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const IotDataBridgeDirectionEnum = {
|
||||||
|
INPUT: 1, // 输入
|
||||||
|
OUTPUT: 2 // 输出
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
// 数据桥梁 API
|
// 数据桥梁 API
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,5 @@
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
import { IotRuleSceneTriggerConfig } from '@/api/iot/rule/scene/scene.types'
|
import { IotRuleScene } from './scene.types'
|
||||||
|
|
||||||
// IoT 场景联动 VO
|
|
||||||
export interface RuleSceneVO {
|
|
||||||
id?: number // 场景编号
|
|
||||||
name: string // 场景名称
|
|
||||||
description?: string // 场景描述
|
|
||||||
status: number // 场景状态
|
|
||||||
triggers: IotRuleSceneTriggerConfig[] // 触发器数组
|
|
||||||
actions?: any[] // 执行器数组
|
|
||||||
}
|
|
||||||
|
|
||||||
// IoT 场景联动 API
|
// IoT 场景联动 API
|
||||||
export const RuleSceneApi = {
|
export const RuleSceneApi = {
|
||||||
|
|
@ -24,12 +14,12 @@ export const RuleSceneApi = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// 新增场景联动
|
// 新增场景联动
|
||||||
createRuleScene: async (data: RuleSceneVO) => {
|
createRuleScene: async (data: IotRuleScene) => {
|
||||||
return await request.post({ url: `/iot/rule-scene/create`, data })
|
return await request.post({ url: `/iot/rule-scene/create`, data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 修改场景联动
|
// 修改场景联动
|
||||||
updateRuleScene: async (data: RuleSceneVO) => {
|
updateRuleScene: async (data: IotRuleScene) => {
|
||||||
return await request.put({ url: `/iot/rule-scene/update`, data })
|
return await request.put({ url: `/iot/rule-scene/update`, data })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,222 +1,131 @@
|
||||||
/**
|
/**
|
||||||
* 场景规则触发器配置
|
* IoT 场景联动接口定义
|
||||||
*/
|
*/
|
||||||
export interface IotRuleSceneTriggerConfig {
|
|
||||||
/**
|
// 枚举定义
|
||||||
* 触发类型
|
const IotRuleSceneTriggerTypeEnum = {
|
||||||
* - 1: 设备触发
|
DEVICE: 1, // 设备触发
|
||||||
* - 2: 定时触发
|
TIMER: 2 // 定时触发
|
||||||
*/
|
} as const
|
||||||
type: number
|
|
||||||
/** 产品标识 */
|
const IotRuleSceneActionTypeEnum = {
|
||||||
productKey: string
|
DEVICE_CONTROL: 1, // 设备执行
|
||||||
/** 设备名称数组 */
|
ALERT: 2, // 告警执行
|
||||||
deviceNames: string[]
|
DATA_BRIDGE: 3 // 桥接执行
|
||||||
/** 触发条件数组。条件之间是"或"的关系 */
|
} as const
|
||||||
conditions: IotRuleSceneTriggerCondition[]
|
|
||||||
/** CRON 表达式。当 type = 2 时必填 */
|
const IotDeviceMessageTypeEnum = {
|
||||||
cronExpression?: string
|
PROPERTY: 'property', // 属性
|
||||||
|
SERVICE: 'service', // 服务
|
||||||
|
EVENT: 'event' // 事件
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const IotDeviceMessageIdentifierEnum = {
|
||||||
|
PROPERTY_SET: 'set', // 属性设置
|
||||||
|
SERVICE_INVOKE: '${identifier}' // 服务调用
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const IotRuleSceneTriggerConditionParameterOperatorEnum = {
|
||||||
|
EQUALS: { name: '等于', value: '=' }, // 等于
|
||||||
|
NOT_EQUALS: { name: '不等于', value: '!=' }, // 不等于
|
||||||
|
GREATER_THAN: { name: '大于', value: '>' }, // 大于
|
||||||
|
GREATER_THAN_OR_EQUALS: { name: '大于等于', value: '>=' }, // 大于等于
|
||||||
|
LESS_THAN: { name: '小于', value: '<' }, // 小于
|
||||||
|
LESS_THAN_OR_EQUALS: { name: '小于等于', value: '<=' }, // 小于等于
|
||||||
|
IN: { name: '在...之中', value: 'in' }, // 在...之中
|
||||||
|
NOT_IN: { name: '不在...之中', value: 'not in' }, // 不在...之中
|
||||||
|
BETWEEN: { name: '在...之间', value: 'between' }, // 在...之间
|
||||||
|
NOT_BETWEEN: { name: '不在...之间', value: 'not between' }, // 不在...之间
|
||||||
|
LIKE: { name: '字符串匹配', value: 'like' }, // 字符串匹配
|
||||||
|
NOT_NULL: { name: '非空', value: 'not null' } // 非空
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const IotAlertConfigReceiveTypeEnum = {
|
||||||
|
SMS: 1, // 短信
|
||||||
|
MAIL: 2, // 邮箱
|
||||||
|
NOTIFY: 3 // 通知
|
||||||
|
} as const
|
||||||
|
|
||||||
|
// 基础接口
|
||||||
|
interface TenantBaseDO {
|
||||||
|
createTime?: Date // 创建时间
|
||||||
|
updateTime?: Date // 更新时间
|
||||||
|
creator?: string // 创建者
|
||||||
|
updater?: string // 更新者
|
||||||
|
deleted?: boolean // 是否删除
|
||||||
|
tenantId?: number // 租户编号
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// 触发条件参数
|
||||||
* 触发条件
|
interface TriggerConditionParameter {
|
||||||
*/
|
identifier: string // 标识符(属性、事件、服务)
|
||||||
export interface IotRuleSceneTriggerCondition {
|
operator: string // 操作符
|
||||||
/**
|
value: string // 比较值
|
||||||
* 消息类型
|
|
||||||
* - property: 属性上报
|
|
||||||
* - event: 事件上报
|
|
||||||
*/
|
|
||||||
type: string
|
|
||||||
/** 消息标识符 */
|
|
||||||
identifier?: string
|
|
||||||
/** 参数数组。参数之间是"或"的关系 */
|
|
||||||
parameters: IotRuleSceneTriggerConditionParameter[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// 触发条件
|
||||||
* 触发条件参数
|
interface TriggerCondition {
|
||||||
*/
|
type: string // 消息类型
|
||||||
export interface IotRuleSceneTriggerConditionParameter {
|
identifier: string // 消息标识符
|
||||||
/** 标识符(属性、事件、服务) */
|
parameters: TriggerConditionParameter[] // 参数数组
|
||||||
identifier: string
|
|
||||||
/**
|
|
||||||
* 操作符
|
|
||||||
*/
|
|
||||||
operator: string
|
|
||||||
/**
|
|
||||||
* 比较值
|
|
||||||
* 如果有多个值,则使用 "," 分隔,类似 "1,2,3"
|
|
||||||
*/
|
|
||||||
value: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// 触发器配置
|
||||||
* 执行器配置
|
interface TriggerConfig {
|
||||||
*/
|
type: number // 触发类型
|
||||||
export interface IotRuleSceneActionConfig {
|
productKey: string // 产品标识
|
||||||
/**
|
deviceNames: string[] // 设备名称数组
|
||||||
* 执行类型
|
conditions?: TriggerCondition[] // 触发条件数组
|
||||||
* - 1: 设备控制
|
cronExpression?: string // CRON 表达式
|
||||||
* - 2: 数据桥接
|
|
||||||
*/
|
|
||||||
type: number
|
|
||||||
/** 设备控制配置。当 type = 1 时必填 */
|
|
||||||
deviceControl?: IotRuleSceneActionDeviceControl
|
|
||||||
/** 数据桥接编号。当 type = 2 时必填 */
|
|
||||||
dataBridgeId?: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// 执行设备控制
|
||||||
* 执行设备控制
|
interface ActionDeviceControl {
|
||||||
*/
|
productKey: string // 产品标识
|
||||||
export interface IotRuleSceneActionDeviceControl {
|
deviceNames: string[] // 设备名称数组
|
||||||
/** 产品标识 */
|
type: string // 消息类型
|
||||||
productKey: string
|
identifier: string // 消息标识符
|
||||||
/** 设备名称数组 */
|
data: Record<string, any> // 具体数据
|
||||||
deviceNames: string[]
|
|
||||||
/**
|
|
||||||
* 消息类型
|
|
||||||
* - property: 属性
|
|
||||||
* - service: 服务
|
|
||||||
*/
|
|
||||||
type: string
|
|
||||||
/**
|
|
||||||
* 消息标识符
|
|
||||||
* - property_set: 属性设置
|
|
||||||
* - service_invoke: 服务调用
|
|
||||||
*/
|
|
||||||
identifier: string
|
|
||||||
/** 具体数据 */
|
|
||||||
data: Record<string, any>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// 告警执行配置
|
||||||
* 场景规则创建/更新请求
|
interface ActionAlert {
|
||||||
*/
|
receiveType: number // 接收方式
|
||||||
export interface IotRuleSceneSaveReqVO {
|
phoneNumbers?: string[] // 手机号列表
|
||||||
/** 场景规则编号 */
|
emails?: string[] // 邮箱列表
|
||||||
id?: number
|
content: string // 通知内容
|
||||||
/** 场景规则名称 */
|
|
||||||
name: string
|
|
||||||
/** 场景规则状态(0=禁用 1=启用) */
|
|
||||||
status: number
|
|
||||||
/** 触发器配置 */
|
|
||||||
triggerConfig: IotRuleSceneTriggerConfig
|
|
||||||
/** 执行动作配置数组 */
|
|
||||||
actionConfigs: IotRuleSceneActionConfig[]
|
|
||||||
/** 备注 */
|
|
||||||
remark?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// 执行器配置
|
||||||
* 场景规则响应
|
interface ActionConfig {
|
||||||
*/
|
type: number // 执行类型
|
||||||
export interface IotRuleSceneRespVO {
|
deviceControl?: ActionDeviceControl // 设备控制
|
||||||
/** 场景规则编号 */
|
alert?: ActionAlert // 告警执行
|
||||||
id: number
|
dataBridgeId?: number // 数据桥接编号
|
||||||
/** 场景规则名称 */
|
|
||||||
name: string
|
|
||||||
/** 场景规则状态(0=禁用 1=启用) */
|
|
||||||
status: number
|
|
||||||
/** 触发器配置 */
|
|
||||||
triggerConfig: IotRuleSceneTriggerConfig
|
|
||||||
/** 执行动作配置数组 */
|
|
||||||
actionConfigs: IotRuleSceneActionConfig[]
|
|
||||||
/** 备注 */
|
|
||||||
remark?: string
|
|
||||||
/** 创建时间 */
|
|
||||||
createTime: Date
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// 主接口
|
||||||
* 场景规则分页项
|
interface IotRuleScene extends TenantBaseDO {
|
||||||
*/
|
id: number // 场景编号
|
||||||
export interface IotRuleScenePageItemRespVO extends IotRuleSceneRespVO {
|
name: string // 场景名称
|
||||||
/** 触发次数 */
|
description: string // 场景描述
|
||||||
triggerCount: number
|
status: number // 场景状态
|
||||||
/** 最后触发时间 */
|
triggers: TriggerConfig[] // 触发器数组
|
||||||
lastTriggerTime?: Date
|
actions: ActionConfig[] // 执行器数组
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export {
|
||||||
* 场景规则分页请求
|
IotRuleScene,
|
||||||
*/
|
TriggerConfig,
|
||||||
export interface IotRuleScenePageReqVO {
|
TriggerCondition,
|
||||||
/** 场景规则名称 */
|
TriggerConditionParameter,
|
||||||
name?: string
|
ActionConfig,
|
||||||
/** 场景规则状态(0=禁用 1=启用) */
|
ActionDeviceControl,
|
||||||
status?: number
|
ActionAlert,
|
||||||
/** 创建时间 */
|
IotRuleSceneTriggerTypeEnum,
|
||||||
createTime?: [Date, Date]
|
IotRuleSceneActionTypeEnum,
|
||||||
/** 页码 */
|
IotDeviceMessageTypeEnum,
|
||||||
pageNo?: number
|
IotDeviceMessageIdentifierEnum,
|
||||||
/** 每页条数 */
|
IotRuleSceneTriggerConditionParameterOperatorEnum,
|
||||||
pageSize?: number
|
IotAlertConfigReceiveTypeEnum
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 场景规则类型枚举
|
|
||||||
*/
|
|
||||||
export enum IotRuleSceneTriggerTypeEnum {
|
|
||||||
/** 设备触发 */
|
|
||||||
DEVICE = 1,
|
|
||||||
/** 定时触发 */
|
|
||||||
TIMER = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 场景规则动作类型枚举
|
|
||||||
*/
|
|
||||||
export enum IotRuleSceneActionTypeEnum {
|
|
||||||
/** 设备控制 */
|
|
||||||
DEVICE_CONTROL = 1,
|
|
||||||
/** 数据桥接 */
|
|
||||||
DATA_BRIDGE = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备消息类型枚举
|
|
||||||
*/
|
|
||||||
export enum IotDeviceMessageTypeEnum {
|
|
||||||
/** 属性 */
|
|
||||||
PROPERTY = 'property',
|
|
||||||
/** 事件 */
|
|
||||||
EVENT = 'event',
|
|
||||||
/** 服务 */
|
|
||||||
SERVICE = 'service'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备消息标识符枚举
|
|
||||||
*/
|
|
||||||
export enum IotDeviceMessageIdentifierEnum {
|
|
||||||
/** 属性上报 */
|
|
||||||
PROPERTY_REPORT = 'property_report',
|
|
||||||
/** 属性设置 */
|
|
||||||
PROPERTY_SET = 'property_set',
|
|
||||||
/** 事件上报 */
|
|
||||||
EVENT_REPORT = 'event_report',
|
|
||||||
/** 服务调用 */
|
|
||||||
SERVICE_INVOKE = 'service_invoke'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 触发条件参数操作符枚举
|
|
||||||
*/
|
|
||||||
export enum IotRuleSceneTriggerConditionParameterOperatorEnum {
|
|
||||||
/** 等于 */
|
|
||||||
EQ = 'eq',
|
|
||||||
/** 大于 */
|
|
||||||
GT = 'gt',
|
|
||||||
/** 大于等于 */
|
|
||||||
GTE = 'gte',
|
|
||||||
/** 小于 */
|
|
||||||
LT = 'lt',
|
|
||||||
/** 小于等于 */
|
|
||||||
LTE = 'lte',
|
|
||||||
/** 范围 */
|
|
||||||
BETWEEN = 'between',
|
|
||||||
/** 在列表中 */
|
|
||||||
IN = 'in'
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ export const ThingModelApi = {
|
||||||
// 获得产品物模型 TSL
|
// 获得产品物模型 TSL
|
||||||
getThingModelTSLByProductId: async (productId: number) => {
|
getThingModelTSLByProductId: async (productId: number) => {
|
||||||
return await request.get({
|
return await request.get({
|
||||||
url: `/iot/thing-model/tsl-by-product-id?productId=${productId}`
|
url: `/iot/thing-model/get-tsl?productId=${productId}`
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
<!-- TODO @puhui999:IoT 前缀去掉哈,文件名 -->
|
|
||||||
<!-- IoT 设备选择,使用弹窗展示 -->
|
<!-- IoT 设备选择,使用弹窗展示 -->
|
||||||
<template>
|
<template>
|
||||||
<Dialog :title="dialogTitle" v-model="dialogVisible" :appendToBody="true" width="60%">
|
<Dialog :title="dialogTitle" v-model="dialogVisible" :appendToBody="true" width="60%">
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
<!-- TODO @puhui999:IoT 前缀去掉哈,文件名 -->
|
|
||||||
<!-- IoT 产品选择,使用弹窗展示 -->
|
<!-- IoT 产品选择,使用弹窗展示 -->
|
||||||
<template>
|
<template>
|
||||||
<Dialog :title="dialogTitle" v-model="dialogVisible" :appendToBody="true" width="60%">
|
<Dialog :title="dialogTitle" v-model="dialogVisible" :appendToBody="true" width="60%">
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
v-if="showConfig(IoTDataBridgeConfigType.RABBITMQ)"
|
v-if="showConfig(IoTDataBridgeConfigType.RABBITMQ)"
|
||||||
v-model="formData.config"
|
v-model="formData.config"
|
||||||
/>
|
/>
|
||||||
<RedisStreamMQConfigForm
|
<RedisStreamConfigForm
|
||||||
v-if="showConfig(IoTDataBridgeConfigType.REDIS_STREAM)"
|
v-if="showConfig(IoTDataBridgeConfigType.REDIS_STREAM)"
|
||||||
v-model="formData.config"
|
v-model="formData.config"
|
||||||
/>
|
/>
|
||||||
|
|
@ -73,13 +73,19 @@
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { DICT_TYPE, getDictObj, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getDictObj, getIntDictOptions } from '@/utils/dict'
|
||||||
import { DataBridgeApi, DataBridgeVO, IoTDataBridgeConfigType } from '@/api/iot/rule/databridge'
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
|
import {
|
||||||
|
DataBridgeApi,
|
||||||
|
DataBridgeVO,
|
||||||
|
IoTDataBridgeConfigType,
|
||||||
|
IotDataBridgeDirectionEnum
|
||||||
|
} from '@/api/iot/rule/databridge'
|
||||||
import {
|
import {
|
||||||
HttpConfigForm,
|
HttpConfigForm,
|
||||||
KafkaMQConfigForm,
|
KafkaMQConfigForm,
|
||||||
MqttConfigForm,
|
MqttConfigForm,
|
||||||
RabbitMQConfigForm,
|
RabbitMQConfigForm,
|
||||||
RedisStreamMQConfigForm,
|
RedisStreamConfigForm,
|
||||||
RocketMQConfigForm
|
RocketMQConfigForm
|
||||||
} from './config'
|
} from './config'
|
||||||
|
|
||||||
|
|
@ -94,9 +100,9 @@ const dialogTitle = ref('') // 弹窗的标题
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
const formData = ref<DataBridgeVO>({
|
const formData = ref<DataBridgeVO>({
|
||||||
status: 0,
|
status: CommonStatusEnum.ENABLE,
|
||||||
direction: 1, // TODO @puhui999:枚举类
|
direction: IotDataBridgeDirectionEnum.INPUT,
|
||||||
type: 1, // TODO @puhui999:枚举类
|
type: IoTDataBridgeConfigType.HTTP,
|
||||||
config: {} as any
|
config: {} as any
|
||||||
})
|
})
|
||||||
const formRules = reactive({
|
const formRules = reactive({
|
||||||
|
|
@ -139,9 +145,9 @@ const formRules = reactive({
|
||||||
})
|
})
|
||||||
|
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
const showConfig = computed(() => (val: string) => {
|
const showConfig = computed(() => (val: number) => {
|
||||||
const dict = getDictObj(DICT_TYPE.IOT_DATA_BRIDGE_TYPE_ENUM, formData.value.type)
|
const dict = getDictObj(DICT_TYPE.IOT_DATA_BRIDGE_TYPE_ENUM, formData.value.type)
|
||||||
return dict && dict.value + '' === val
|
return dict && dict.value + '' === val + ''
|
||||||
}) // 显示对应的 Config 配置项
|
}) // 显示对应的 Config 配置项
|
||||||
|
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
|
|
@ -196,10 +202,9 @@ const handleTypeChange = (val: number) => {
|
||||||
/** 重置表单 */
|
/** 重置表单 */
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
// TODO @puhui999:换成枚举值哈
|
status: CommonStatusEnum.ENABLE,
|
||||||
status: 0,
|
direction: IotDataBridgeDirectionEnum.INPUT,
|
||||||
direction: 1,
|
type: IoTDataBridgeConfigType.HTTP,
|
||||||
type: 1,
|
|
||||||
config: {} as any
|
config: {} as any
|
||||||
}
|
}
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
config.value = {
|
config.value = {
|
||||||
type: IoTDataBridgeConfigType.HTTP,
|
type: IoTDataBridgeConfigType.HTTP + '', // 序列化成对应类型时使用
|
||||||
url: '',
|
url: '',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {},
|
headers: {},
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ onMounted(() => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
config.value = {
|
config.value = {
|
||||||
type: IoTDataBridgeConfigType.KAFKA,
|
type: IoTDataBridgeConfigType.KAFKA + '', // 序列化成对应类型时使用
|
||||||
bootstrapServers: '',
|
bootstrapServers: '',
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ onMounted(() => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
config.value = {
|
config.value = {
|
||||||
type: IoTDataBridgeConfigType.MQTT,
|
type: IoTDataBridgeConfigType.MQTT + '', // 序列化成对应类型时使用
|
||||||
url: '',
|
url: '',
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ onMounted(() => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
config.value = {
|
config.value = {
|
||||||
type: IoTDataBridgeConfigType.RABBITMQ,
|
type: IoTDataBridgeConfigType.RABBITMQ + '', // 序列化成对应类型时使用
|
||||||
host: '',
|
host: '',
|
||||||
port: 5672,
|
port: 5672,
|
||||||
virtualHost: '/',
|
virtualHost: '/',
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
<!-- TODO @puhui999:去掉 MQ 关键字哈 -->
|
|
||||||
<template>
|
<template>
|
||||||
<el-form-item label="主机地址" prop="config.host">
|
<el-form-item label="主机地址" prop="config.host">
|
||||||
<el-input v-model="config.host" placeholder="请输入主机地址,如:localhost" />
|
<el-input v-model="config.host" placeholder="请输入主机地址,如:localhost" />
|
||||||
|
|
@ -47,7 +46,7 @@ onMounted(() => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
config.value = {
|
config.value = {
|
||||||
type: IoTDataBridgeConfigType.REDIS_STREAM,
|
type: IoTDataBridgeConfigType.REDIS_STREAM + '', // 序列化成对应类型时使用
|
||||||
host: '',
|
host: '',
|
||||||
port: 6379,
|
port: 6379,
|
||||||
password: '',
|
password: '',
|
||||||
|
|
@ -45,7 +45,7 @@ onMounted(() => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
config.value = {
|
config.value = {
|
||||||
type: IoTDataBridgeConfigType.ROCKETMQ,
|
type: IoTDataBridgeConfigType.ROCKETMQ + '', // 序列化成对应类型时使用
|
||||||
nameServer: '',
|
nameServer: '',
|
||||||
accessKey: '',
|
accessKey: '',
|
||||||
secretKey: '',
|
secretKey: '',
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import MqttConfigForm from './MqttConfigForm.vue'
|
||||||
import RocketMQConfigForm from './RocketMQConfigForm.vue'
|
import RocketMQConfigForm from './RocketMQConfigForm.vue'
|
||||||
import KafkaMQConfigForm from './KafkaMQConfigForm.vue'
|
import KafkaMQConfigForm from './KafkaMQConfigForm.vue'
|
||||||
import RabbitMQConfigForm from './RabbitMQConfigForm.vue'
|
import RabbitMQConfigForm from './RabbitMQConfigForm.vue'
|
||||||
import RedisStreamMQConfigForm from './RedisStreamMQConfigForm.vue'
|
import RedisStreamConfigForm from './RedisStreamConfigForm.vue'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
HttpConfigForm,
|
HttpConfigForm,
|
||||||
|
|
@ -11,5 +11,5 @@ export {
|
||||||
RocketMQConfigForm,
|
RocketMQConfigForm,
|
||||||
KafkaMQConfigForm,
|
KafkaMQConfigForm,
|
||||||
RabbitMQConfigForm,
|
RabbitMQConfigForm,
|
||||||
RedisStreamMQConfigForm
|
RedisStreamConfigForm
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,16 +40,13 @@
|
||||||
@update:model-value="(val) => (formData.triggers[index] = val)"
|
@update:model-value="(val) => (formData.triggers[index] = val)"
|
||||||
class="mb-10px"
|
class="mb-10px"
|
||||||
>
|
>
|
||||||
<el-button
|
<el-button type="danger" round size="small" @click="removeTrigger(index)">
|
||||||
type="danger"
|
<Icon icon="ep:delete" />
|
||||||
round
|
</el-button>
|
||||||
:icon="Delete"
|
|
||||||
size="small"
|
|
||||||
@click="removeTrigger(index)"
|
|
||||||
/>
|
|
||||||
</device-listener>
|
</device-listener>
|
||||||
<!-- TODO @puhui999:可以使用 el-button,然后选个合适的样式哇 -->
|
<el-button class="ml-10px!" type="primary" size="small" @click="addTrigger">
|
||||||
<el-text class="ml-10px!" type="primary" @click="addTrigger">添加触发器</el-text>
|
添加触发器
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-divider content-position="left">执行动作配置</el-divider>
|
<el-divider content-position="left">执行动作配置</el-divider>
|
||||||
|
|
@ -67,14 +64,19 @@
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import { RuleSceneApi, RuleSceneVO } from '@/api/iot/rule/scene'
|
import { RuleSceneApi } from '@/api/iot/rule/scene'
|
||||||
import DeviceListener from './components/DeviceListener.vue'
|
import DeviceListener from './components/DeviceListener.vue'
|
||||||
// TODO @puhui999:尽量用 icon 组件哈,项目里的
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
import { Delete } from '@element-plus/icons-vue'
|
import {
|
||||||
import { IotRuleSceneTriggerConfig } from '@/api/iot/rule/scene/scene.types'
|
IotDeviceMessageIdentifierEnum,
|
||||||
|
IotDeviceMessageTypeEnum,
|
||||||
|
IotRuleScene,
|
||||||
|
IotRuleSceneTriggerTypeEnum,
|
||||||
|
TriggerConfig
|
||||||
|
} from '@/api/iot/rule/scene/scene.types'
|
||||||
|
|
||||||
/** IoT 规则场景(场景联动) 表单 */
|
/** IoT 场景联动表单 */
|
||||||
defineOptions({ name: 'RuleSceneForm' })
|
defineOptions({ name: 'IotRuleSceneForm' })
|
||||||
|
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
@ -83,10 +85,10 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
const dialogTitle = ref('') // 弹窗的标题
|
const dialogTitle = ref('') // 弹窗的标题
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
const formData = ref<RuleSceneVO>({
|
const formData = ref<IotRuleScene>({
|
||||||
status: 0, // TODO @puhui999:使用枚举值
|
status: CommonStatusEnum.ENABLE,
|
||||||
triggers: [] as IotRuleSceneTriggerConfig[]
|
triggers: [] as TriggerConfig[]
|
||||||
} as RuleSceneVO)
|
} as IotRuleScene)
|
||||||
const formRules = reactive({
|
const formRules = reactive({
|
||||||
name: [{ required: true, message: '场景名称不能为空', trigger: 'blur' }],
|
name: [{ required: true, message: '场景名称不能为空', trigger: 'blur' }],
|
||||||
status: [{ required: true, message: '场景状态不能为空', trigger: 'blur' }],
|
status: [{ required: true, message: '场景状态不能为空', trigger: 'blur' }],
|
||||||
|
|
@ -98,12 +100,13 @@ const formRef = ref() // 表单 Ref
|
||||||
/** 添加触发器 */
|
/** 添加触发器 */
|
||||||
const addTrigger = () => {
|
const addTrigger = () => {
|
||||||
formData.value.triggers.push({
|
formData.value.triggers.push({
|
||||||
type: 1, // TODO @puhui999:使用枚举值
|
type: IotRuleSceneTriggerTypeEnum.DEVICE,
|
||||||
productKey: '',
|
productKey: '',
|
||||||
deviceNames: [],
|
deviceNames: [],
|
||||||
conditions: [
|
conditions: [
|
||||||
{
|
{
|
||||||
type: 'property',
|
type: IotDeviceMessageTypeEnum.PROPERTY,
|
||||||
|
identifier: IotDeviceMessageIdentifierEnum.PROPERTY_SET,
|
||||||
parameters: []
|
parameters: []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -142,7 +145,7 @@ const submitForm = async () => {
|
||||||
// 提交请求
|
// 提交请求
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = formData.value as unknown as RuleSceneVO
|
const data = formData.value as unknown as IotRuleScene
|
||||||
if (formType.value === 'create') {
|
if (formType.value === 'create') {
|
||||||
await RuleSceneApi.createRuleScene(data)
|
await RuleSceneApi.createRuleScene(data)
|
||||||
message.success(t('common.createSuccess'))
|
message.success(t('common.createSuccess'))
|
||||||
|
|
@ -161,9 +164,9 @@ const submitForm = async () => {
|
||||||
/** 重置表单 */
|
/** 重置表单 */
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
status: 0, // TODO @puhui999:使用枚举值
|
status: CommonStatusEnum.ENABLE,
|
||||||
triggers: [] as IotRuleSceneTriggerConfig[]
|
triggers: [] as TriggerConfig[]
|
||||||
} as RuleSceneVO
|
} as IotRuleScene
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -1,29 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<el-select
|
<el-select v-model="selectedOperator" class="w-1/1" clearable :placeholder="placeholder">
|
||||||
v-model="selectedOperator"
|
<!-- 根据属性类型展示不同的可选条件 -->
|
||||||
class="condition-selector"
|
<el-option
|
||||||
clearable
|
v-for="(item, key) in filteredOperators"
|
||||||
:placeholder="placeholder"
|
:key="key"
|
||||||
>
|
:label="item.name"
|
||||||
<!-- TODO puhui999: 考虑根据属性类型不同展示不同的可选条件 -->
|
:value="item.value"
|
||||||
<!-- TODO @puhui999:可以在 scene.types.ts IotRuleSceneTriggerConditionParameterOperatorEnum 枚举下 -->
|
/>
|
||||||
<el-option label="等于" value="=" />
|
|
||||||
<el-option label="不等于" value="!=" />
|
|
||||||
<el-option label="大于" value=">" />
|
|
||||||
<el-option label="大于等于" value=">=" />
|
|
||||||
<el-option label="小于" value="<" />
|
|
||||||
<el-option label="小于等于" value="<=" />
|
|
||||||
<el-option label="在列表中" value="in" />
|
|
||||||
<el-option label="不在列表中" value="not in" />
|
|
||||||
<el-option label="在范围内" value="between" />
|
|
||||||
<el-option label="不在范围内" value="not between" />
|
|
||||||
<el-option label="包含" value="like" />
|
|
||||||
<el-option label="非空" value="not null" />
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { IotRuleSceneTriggerConditionParameterOperatorEnum } from '@/api/iot/rule/scene/scene.types'
|
||||||
|
|
||||||
/** 条件选择器 */
|
/** 条件选择器 */
|
||||||
defineOptions({ name: 'ConditionSelector' })
|
defineOptions({ name: 'ConditionSelector' })
|
||||||
|
|
@ -35,6 +24,10 @@ const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
},
|
||||||
|
dataType: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -44,11 +37,70 @@ const selectedOperator = computed({
|
||||||
get: () => props.modelValue,
|
get: () => props.modelValue,
|
||||||
set: (value) => emit('update:modelValue', value)
|
set: (value) => emit('update:modelValue', value)
|
||||||
})
|
})
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- TODO @puhui999:尽量用 unocss -->
|
// 根据数据类型过滤可用的操作符
|
||||||
<style scoped>
|
const filteredOperators = computed(() => {
|
||||||
.condition-selector {
|
// 如果没有指定数据类型,返回所有操作符
|
||||||
width: 100%;
|
if (!props.dataType) {
|
||||||
}
|
return IotRuleSceneTriggerConditionParameterOperatorEnum
|
||||||
</style>
|
}
|
||||||
|
|
||||||
|
const operatorMap = new Map()
|
||||||
|
|
||||||
|
// 添加通用的操作符(所有类型都有非空操作符)
|
||||||
|
operatorMap.set('NOT_NULL', IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_NULL)
|
||||||
|
|
||||||
|
// 根据数据类型添加特定的操作符
|
||||||
|
switch (props.dataType) {
|
||||||
|
case 'int':
|
||||||
|
case 'float':
|
||||||
|
case 'double':
|
||||||
|
// 数值类型支持的所有操作符
|
||||||
|
operatorMap.set('EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS)
|
||||||
|
operatorMap.set('NOT_EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_EQUALS)
|
||||||
|
operatorMap.set('GREATER_THAN', IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN)
|
||||||
|
operatorMap.set('GREATER_THAN_OR_EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN_OR_EQUALS)
|
||||||
|
operatorMap.set('LESS_THAN', IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN)
|
||||||
|
operatorMap.set('LESS_THAN_OR_EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS)
|
||||||
|
operatorMap.set('IN', IotRuleSceneTriggerConditionParameterOperatorEnum.IN)
|
||||||
|
operatorMap.set('NOT_IN', IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_IN)
|
||||||
|
operatorMap.set('BETWEEN', IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN)
|
||||||
|
operatorMap.set('NOT_BETWEEN', IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN)
|
||||||
|
break
|
||||||
|
case 'enum':
|
||||||
|
// 枚举类型支持的操作符
|
||||||
|
operatorMap.set('EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS)
|
||||||
|
operatorMap.set('NOT_EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_EQUALS)
|
||||||
|
operatorMap.set('IN', IotRuleSceneTriggerConditionParameterOperatorEnum.IN)
|
||||||
|
operatorMap.set('NOT_IN', IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_IN)
|
||||||
|
break
|
||||||
|
case 'bool':
|
||||||
|
// 布尔类型支持的操作符
|
||||||
|
operatorMap.set('EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS)
|
||||||
|
operatorMap.set('NOT_EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_EQUALS)
|
||||||
|
break
|
||||||
|
case 'text':
|
||||||
|
// 文本类型支持的操作符
|
||||||
|
operatorMap.set('EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS)
|
||||||
|
operatorMap.set('NOT_EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_EQUALS)
|
||||||
|
operatorMap.set('LIKE', IotRuleSceneTriggerConditionParameterOperatorEnum.LIKE)
|
||||||
|
break
|
||||||
|
case 'date':
|
||||||
|
// 日期类型支持的操作符
|
||||||
|
operatorMap.set('EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS)
|
||||||
|
operatorMap.set('NOT_EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_EQUALS)
|
||||||
|
operatorMap.set('GREATER_THAN', IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN)
|
||||||
|
operatorMap.set('GREATER_THAN_OR_EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN_OR_EQUALS)
|
||||||
|
operatorMap.set('LESS_THAN', IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN)
|
||||||
|
operatorMap.set('LESS_THAN_OR_EQUALS', IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS)
|
||||||
|
operatorMap.set('BETWEEN', IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN)
|
||||||
|
operatorMap.set('NOT_BETWEEN', IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN)
|
||||||
|
break
|
||||||
|
// struct 和 array 类型只支持非空操作符,已在通用部分添加
|
||||||
|
default:
|
||||||
|
return IotRuleSceneTriggerConditionParameterOperatorEnum
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.fromEntries(operatorMap)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="device-listener m-10px">
|
<div class="m-10px">
|
||||||
<div class="device-listener-header h-50px flex items-center px-10px">
|
<div class="relative bg-[#eff3f7] h-50px flex items-center px-10px">
|
||||||
<div class="flex items-center mr-60px">
|
<div class="flex items-center mr-60px">
|
||||||
<span class="mr-10px">触发条件</span>
|
<span class="mr-10px">触发条件</span>
|
||||||
<el-select
|
<el-select
|
||||||
|
|
@ -17,99 +17,119 @@
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center mr-60px">
|
<div
|
||||||
|
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
|
||||||
|
class="flex items-center mr-60px"
|
||||||
|
>
|
||||||
<span class="mr-10px">产品</span>
|
<span class="mr-10px">产品</span>
|
||||||
<el-button type="primary" @click="productTableSelectRef?.open()" size="small" plain>
|
<el-button type="primary" @click="productTableSelectRef?.open()" size="small" plain>
|
||||||
<!-- TODO @puhui999:最终最好是,product ? product.name : '选择产品',减少取反 -->
|
{{ product ? product.name : '选择产品' }}
|
||||||
{{ !product ? '选择产品' : product.name }}
|
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center mr-60px">
|
<div
|
||||||
|
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
|
||||||
|
class="flex items-center mr-60px"
|
||||||
|
>
|
||||||
<span class="mr-10px">设备</span>
|
<span class="mr-10px">设备</span>
|
||||||
<el-button type="primary" @click="openDeviceSelect" size="small" plain>
|
<el-button type="primary" @click="openDeviceSelect" size="small" plain>
|
||||||
{{ isEmpty(deviceList) ? '选择设备' : triggerConfig.deviceNames.join(',') }}
|
{{ isEmpty(deviceList) ? '选择设备' : triggerConfig.deviceNames.join(',') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<!-- 删除触发器 -->
|
<!-- 删除触发器 -->
|
||||||
<div class="device-listener-delete">
|
<div class="absolute top-auto right-16px bottom-auto">
|
||||||
<el-tooltip content="删除触发器" placement="top">
|
<el-tooltip content="删除触发器" placement="top">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 触发器条件 -->
|
<!-- 设备触发器条件 -->
|
||||||
<div
|
<template v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE">
|
||||||
class="device-listener-condition flex p-10px"
|
|
||||||
v-for="(condition, index) in triggerConfig.conditions"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-center justify-center mr-10px h-a">
|
|
||||||
<el-select
|
|
||||||
v-model="condition.type"
|
|
||||||
@change="condition.parameters = []"
|
|
||||||
class="!w-160px"
|
|
||||||
clearable
|
|
||||||
placeholder=""
|
|
||||||
>
|
|
||||||
<!-- <el-option-->
|
|
||||||
<!-- v-for="dict in getStrDictOptions(DICT_TYPE.IOT_DEVICE_MESSAGE_TYPE_ENUM)"-->
|
|
||||||
<!-- :key="dict.value"-->
|
|
||||||
<!-- :label="dict.label"-->
|
|
||||||
<!-- :value="dict.value"-->
|
|
||||||
<!-- />-->
|
|
||||||
<el-option label="属性" value="property" />
|
|
||||||
<el-option label="服务" value="service" />
|
|
||||||
<el-option label="事件" value="event" />
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
<div class="">
|
|
||||||
<DeviceListenerCondition
|
|
||||||
v-for="(parameter, index2) in condition.parameters"
|
|
||||||
:key="index2"
|
|
||||||
:model-value="parameter"
|
|
||||||
:thingModels="thingModels(condition)"
|
|
||||||
@update:model-value="(val) => (condition.parameters[index2] = val)"
|
|
||||||
class="mb-10px last:mb-0"
|
|
||||||
>
|
|
||||||
<el-tooltip content="删除参数" placement="top">
|
|
||||||
<el-button
|
|
||||||
class="device-listener-delete"
|
|
||||||
type="danger"
|
|
||||||
circle
|
|
||||||
:icon="Delete"
|
|
||||||
size="small"
|
|
||||||
@click="removeConditionParameter(condition.parameters, index2)"
|
|
||||||
/>
|
|
||||||
</el-tooltip>
|
|
||||||
</DeviceListenerCondition>
|
|
||||||
</div>
|
|
||||||
<!-- 添加参数 -->
|
|
||||||
<div class="flex flex-1 flex-col items-center justify-center w-60px h-a">
|
|
||||||
<el-tooltip content="添加参数" placement="top">
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
circle
|
|
||||||
:icon="Plus"
|
|
||||||
size="small"
|
|
||||||
@click="addConditionParameter(condition.parameters)"
|
|
||||||
/>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
<!-- 删除条件 -->
|
|
||||||
<div
|
<div
|
||||||
class="device-listener-condition flex flex-1 flex-col items-center justify-center w-a h-a"
|
class="bg-[#dbe5f6] flex p-10px"
|
||||||
|
v-for="(condition, index) in triggerConfig.conditions"
|
||||||
|
:key="index"
|
||||||
>
|
>
|
||||||
<el-tooltip content="删除条件" placement="top">
|
<div class="flex flex-col items-center justify-center mr-10px h-a">
|
||||||
<el-button type="danger" :icon="Delete" size="small" @click="removeCondition(index)" />
|
<el-select
|
||||||
</el-tooltip>
|
v-model="condition.type"
|
||||||
|
@change="condition.parameters = []"
|
||||||
|
class="!w-160px"
|
||||||
|
clearable
|
||||||
|
placeholder=""
|
||||||
|
>
|
||||||
|
<el-option label="属性" :value="IotDeviceMessageTypeEnum.PROPERTY" />
|
||||||
|
<el-option label="服务" :value="IotDeviceMessageTypeEnum.SERVICE" />
|
||||||
|
<el-option label="事件" :value="IotDeviceMessageTypeEnum.EVENT" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<DeviceListenerCondition
|
||||||
|
v-for="(parameter, index2) in condition.parameters"
|
||||||
|
:key="index2"
|
||||||
|
:model-value="parameter"
|
||||||
|
:thingModels="thingModels(condition)"
|
||||||
|
@update:model-value="(val) => (condition.parameters[index2] = val)"
|
||||||
|
class="mb-10px last:mb-0"
|
||||||
|
>
|
||||||
|
<el-tooltip content="删除参数" placement="top">
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
circle
|
||||||
|
size="small"
|
||||||
|
@click="removeConditionParameter(condition.parameters, index2)"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:delete" />
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</DeviceListenerCondition>
|
||||||
|
</div>
|
||||||
|
<!-- 添加参数 -->
|
||||||
|
<div class="flex flex-1 flex-col items-center justify-center w-60px h-a">
|
||||||
|
<el-tooltip content="添加参数" placement="top">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
circle
|
||||||
|
size="small"
|
||||||
|
@click="addConditionParameter(condition.parameters)"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:plus" />
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
<!-- 删除条件 -->
|
||||||
|
<div
|
||||||
|
class="device-listener-condition flex flex-1 flex-col items-center justify-center w-a h-a"
|
||||||
|
>
|
||||||
|
<el-tooltip content="删除条件" placement="top">
|
||||||
|
<el-button type="danger" size="small" @click="removeCondition(index)">
|
||||||
|
<Icon icon="ep:delete" />
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- 定时触发 -->
|
||||||
|
<div
|
||||||
|
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.TIMER"
|
||||||
|
class="bg-[#dbe5f6] flex items-center justify-between p-10px"
|
||||||
|
>
|
||||||
|
<span class="w-120px">CRON 表达式</span>
|
||||||
|
<crontab v-model="triggerConfig.cronExpression" />
|
||||||
</div>
|
</div>
|
||||||
<el-text class="ml-10px!" type="primary" @click="addCondition">添加触发条件</el-text>
|
<!-- 设备触发才可以设置多个触发条件 -->
|
||||||
|
<el-text
|
||||||
|
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
|
||||||
|
class="ml-10px!"
|
||||||
|
type="primary"
|
||||||
|
@click="addCondition"
|
||||||
|
>
|
||||||
|
添加触发条件
|
||||||
|
</el-text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 产品、设备的选择 -->
|
<!-- 产品、设备的选择 -->
|
||||||
<IoTProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
|
<ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
|
||||||
<IoTDeviceTableSelect
|
<DeviceTableSelect
|
||||||
ref="deviceTableSelectRef"
|
ref="deviceTableSelectRef"
|
||||||
multiple
|
multiple
|
||||||
:product-id="product?.id"
|
:product-id="product?.id"
|
||||||
|
|
@ -118,61 +138,64 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Delete, Plus } from '@element-plus/icons-vue'
|
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|
||||||
import DeviceListenerCondition from './DeviceListenerCondition.vue'
|
|
||||||
import IoTProductTableSelect from '@/views/iot/product/product/components/IoTProductTableSelect.vue'
|
|
||||||
import IoTDeviceTableSelect from '@/views/iot/device/device/components/IoTDeviceTableSelect.vue'
|
|
||||||
import {
|
|
||||||
IotRuleSceneTriggerCondition,
|
|
||||||
IotRuleSceneTriggerConditionParameter,
|
|
||||||
IotRuleSceneTriggerConfig
|
|
||||||
} from '@/api/iot/rule/scene/scene.types'
|
|
||||||
import { ProductVO } from '@/api/iot/product/product'
|
|
||||||
import { DeviceVO } from '@/api/iot/device/device'
|
|
||||||
import { useVModel } from '@vueuse/core'
|
import { useVModel } from '@vueuse/core'
|
||||||
import { isEmpty } from '@/utils/is'
|
import { isEmpty } from '@/utils/is'
|
||||||
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
|
import DeviceListenerCondition from './DeviceListenerCondition.vue'
|
||||||
|
import ProductTableSelect from '@/views/iot/product/product/components/ProductTableSelect.vue'
|
||||||
|
import DeviceTableSelect from '@/views/iot/device/device/components/DeviceTableSelect.vue'
|
||||||
|
import { ProductVO } from '@/api/iot/product/product'
|
||||||
|
import { DeviceVO } from '@/api/iot/device/device'
|
||||||
import { ThingModelApi } from '@/api/iot/thingmodel'
|
import { ThingModelApi } from '@/api/iot/thingmodel'
|
||||||
|
import {
|
||||||
|
IotDeviceMessageIdentifierEnum,
|
||||||
|
IotDeviceMessageTypeEnum,
|
||||||
|
IotRuleSceneTriggerTypeEnum,
|
||||||
|
TriggerCondition,
|
||||||
|
TriggerConditionParameter,
|
||||||
|
TriggerConfig
|
||||||
|
} from '@/api/iot/rule/scene/scene.types'
|
||||||
|
|
||||||
/** 场景联动之监听器组件 */
|
/** 场景联动之监听器组件 */
|
||||||
defineOptions({ name: 'DeviceListener' })
|
defineOptions({ name: 'DeviceListener' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: any }>()
|
const props = defineProps<{ modelValue: any }>()
|
||||||
const emits = defineEmits(['update:modelValue'])
|
const emits = defineEmits(['update:modelValue'])
|
||||||
const triggerConfig = useVModel(props, 'modelValue', emits) as Ref<IotRuleSceneTriggerConfig>
|
const triggerConfig = useVModel(props, 'modelValue', emits) as Ref<TriggerConfig>
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
/** 添加触发条件 */
|
/** 添加触发条件 */
|
||||||
const addCondition = () => {
|
const addCondition = () => {
|
||||||
triggerConfig.value.conditions.push({
|
triggerConfig.value.conditions?.push({
|
||||||
type: 'property',
|
type: IotDeviceMessageTypeEnum.PROPERTY,
|
||||||
|
identifier: IotDeviceMessageIdentifierEnum.PROPERTY_SET,
|
||||||
parameters: []
|
parameters: []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/** 移除触发条件 */
|
/** 移除触发条件 */
|
||||||
const removeCondition = (index: number) => {
|
const removeCondition = (index: number) => {
|
||||||
triggerConfig.value.conditions.splice(index, 1)
|
triggerConfig.value.conditions?.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 添加参数 */
|
/** 添加参数 */
|
||||||
const addConditionParameter = (conditionParameters: IotRuleSceneTriggerConditionParameter[]) => {
|
const addConditionParameter = (conditionParameters: TriggerConditionParameter[]) => {
|
||||||
if (!product.value) {
|
if (!product.value) {
|
||||||
message.warning('请先选择一个产品')
|
message.warning('请先选择一个产品')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
conditionParameters.push({} as IotRuleSceneTriggerConditionParameter)
|
conditionParameters.push({} as TriggerConditionParameter)
|
||||||
}
|
}
|
||||||
/** 移除参数 */
|
/** 移除参数 */
|
||||||
const removeConditionParameter = (
|
const removeConditionParameter = (
|
||||||
conditionParameters: IotRuleSceneTriggerConditionParameter[],
|
conditionParameters: TriggerConditionParameter[],
|
||||||
index: number
|
index: number
|
||||||
) => {
|
) => {
|
||||||
conditionParameters.splice(index, 1)
|
conditionParameters.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const productTableSelectRef = ref<InstanceType<typeof IoTProductTableSelect>>()
|
const productTableSelectRef = ref<InstanceType<typeof ProductTableSelect>>()
|
||||||
const deviceTableSelectRef = ref<InstanceType<typeof IoTDeviceTableSelect>>()
|
const deviceTableSelectRef = ref<InstanceType<typeof DeviceTableSelect>>()
|
||||||
const product = ref<ProductVO>()
|
const product = ref<ProductVO>()
|
||||||
const deviceList = ref<DeviceVO[]>([])
|
const deviceList = ref<DeviceVO[]>([])
|
||||||
/** 处理产品选择 */
|
/** 处理产品选择 */
|
||||||
|
|
@ -198,15 +221,14 @@ const openDeviceSelect = () => {
|
||||||
|
|
||||||
/** 获取产品物模型 */
|
/** 获取产品物模型 */
|
||||||
const thingModelTSL = ref<any>()
|
const thingModelTSL = ref<any>()
|
||||||
const thingModels = computed(() => (condition: IotRuleSceneTriggerCondition) => {
|
const thingModels = computed(() => (condition: TriggerCondition) => {
|
||||||
// TODO @puhui999:这里最好也用枚举
|
|
||||||
switch (condition.type) {
|
switch (condition.type) {
|
||||||
case 'property':
|
case IotDeviceMessageTypeEnum.PROPERTY:
|
||||||
return thingModelTSL.value.properties
|
return thingModelTSL.value.properties
|
||||||
// TODO puhui999: 服务和事件后续考虑
|
// TODO puhui999: 服务和事件后续考虑
|
||||||
case 'service':
|
case IotDeviceMessageTypeEnum.SERVICE:
|
||||||
return thingModelTSL.value.services
|
return thingModelTSL.value.services
|
||||||
case 'event':
|
case IotDeviceMessageTypeEnum.EVENT:
|
||||||
return thingModelTSL.value.events
|
return thingModelTSL.value.events
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
|
|
@ -218,24 +240,3 @@ const getThingModelTSL = async () => {
|
||||||
thingModelTSL.value = await ThingModelApi.getThingModelTSLByProductId(product.value.id)
|
thingModelTSL.value = await ThingModelApi.getThingModelTSLByProductId(product.value.id)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- TODO @puhui999:建议使用 unocss 哈 -->
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.device-listener {
|
|
||||||
.device-listener-header {
|
|
||||||
position: relative;
|
|
||||||
background-color: #eff3f7;
|
|
||||||
|
|
||||||
.device-listener-delete {
|
|
||||||
position: absolute;
|
|
||||||
top: auto;
|
|
||||||
right: 16px;
|
|
||||||
bottom: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-listener-condition {
|
|
||||||
background-color: #dbe5f6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,22 @@
|
||||||
:value="thingModel.identifier"
|
:value="thingModel.identifier"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
<ConditionSelector v-model="conditionParameter.operator" class="!w-180px mr-10px" />
|
<ConditionSelector
|
||||||
|
v-model="conditionParameter.operator"
|
||||||
|
:data-type="getDataType"
|
||||||
|
class="!w-180px mr-10px"
|
||||||
|
/>
|
||||||
<!-- TODO puhui999: 输入值范围校验? -->
|
<!-- TODO puhui999: 输入值范围校验? -->
|
||||||
<el-input v-model="conditionParameter.value" class="!w-240px mr-10px" placeholder="请输入值">
|
<el-input
|
||||||
<template #append> {{ getUnitName }} </template>
|
v-if="
|
||||||
|
conditionParameter.operator !==
|
||||||
|
IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_NULL.value
|
||||||
|
"
|
||||||
|
v-model="conditionParameter.value"
|
||||||
|
class="!w-240px mr-10px"
|
||||||
|
placeholder="请输入值"
|
||||||
|
>
|
||||||
|
<template v-if="getUnitName" #append> {{ getUnitName }} </template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<!-- 按钮插槽 -->
|
<!-- 按钮插槽 -->
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
|
@ -25,18 +37,28 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ConditionSelector from './ConditionSelector.vue'
|
import ConditionSelector from './ConditionSelector.vue'
|
||||||
import { IotRuleSceneTriggerConditionParameter } from '@/api/iot/rule/scene/scene.types'
|
import {
|
||||||
|
IotRuleSceneTriggerConditionParameterOperatorEnum,
|
||||||
|
TriggerConditionParameter
|
||||||
|
} from '@/api/iot/rule/scene/scene.types'
|
||||||
import { useVModel } from '@vueuse/core'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
defineOptions({ name: 'DeviceListenerCondition' })
|
defineOptions({ name: 'DeviceListenerCondition' })
|
||||||
const props = defineProps<{ modelValue: any; thingModels: any }>()
|
const props = defineProps<{ modelValue: any; thingModels: any }>()
|
||||||
const emits = defineEmits(['update:modelValue'])
|
const emits = defineEmits(['update:modelValue'])
|
||||||
const conditionParameter = useVModel(
|
const conditionParameter = useVModel(props, 'modelValue', emits) as Ref<TriggerConditionParameter>
|
||||||
props,
|
|
||||||
'modelValue',
|
|
||||||
emits
|
|
||||||
) as Ref<IotRuleSceneTriggerConditionParameter>
|
|
||||||
|
|
||||||
|
/** 获得物模型属性类型 */
|
||||||
|
const getDataType = computed(() => {
|
||||||
|
const model = props.thingModels?.find(
|
||||||
|
(item: any) => item.identifier === conditionParameter.value.identifier
|
||||||
|
)
|
||||||
|
// 属性
|
||||||
|
if (model?.dataSpecs) {
|
||||||
|
return model.dataSpecs.dataType
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
})
|
||||||
/** 获得属性单位 */
|
/** 获得属性单位 */
|
||||||
const getUnitName = computed(() => {
|
const getUnitName = computed(() => {
|
||||||
const model = props.thingModels?.find(
|
const model = props.thingModels?.find(
|
||||||
|
|
@ -46,11 +68,12 @@ const getUnitName = computed(() => {
|
||||||
if (model?.dataSpecs) {
|
if (model?.dataSpecs) {
|
||||||
return model.dataSpecs.unitName
|
return model.dataSpecs.unitName
|
||||||
}
|
}
|
||||||
|
// TODO puhui999: 先不考虑服务和事件的情况
|
||||||
// 服务和事件
|
// 服务和事件
|
||||||
// if (model?.outputParams) {
|
// if (model?.outputParams) {
|
||||||
// return model.dataSpecs.unitName
|
// return model.dataSpecs.unitName
|
||||||
// }
|
// }
|
||||||
return '单位'
|
return ''
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,23 +110,24 @@
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
<IoTRuleSceneForm ref="formRef" @success="getList" />
|
<RuleSceneForm ref="formRef" @success="getList" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
import { RuleSceneApi, RuleSceneVO } from '@/api/iot/rule/scene'
|
import { RuleSceneApi } from '@/api/iot/rule/scene'
|
||||||
import IoTRuleSceneForm from './IoTRuleSceneForm.vue'
|
import RuleSceneForm from './RuleSceneForm.vue'
|
||||||
|
import { IotRuleScene } from '@/api/iot/rule/scene/scene.types'
|
||||||
|
|
||||||
/** IoT 规则场景(场景联动) 列表 */
|
/** IoT 场景联动 列表 */
|
||||||
defineOptions({ name: 'IotRuleScene' })
|
defineOptions({ name: 'IotRuleScene' })
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
const loading = ref(true) // 列表的加载中
|
||||||
const list = ref<RuleSceneVO[]>([]) // 列表的数据
|
const list = ref<IotRuleScene[]>([]) // 列表的数据
|
||||||
const total = ref(0) // 列表的总页数
|
const total = ref(0) // 列表的总页数
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||||
|
<el-scrollbar height="600px">
|
||||||
|
<pre><code v-dompurify-html="highlightedCode()" class="hljs"></code></pre>
|
||||||
|
</el-scrollbar>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import hljs from 'highlight.js' // 导入代码高亮文件
|
||||||
|
import 'highlight.js/styles/github.css' // 导入代码高亮样式
|
||||||
|
import json from 'highlight.js/lib/languages/json'
|
||||||
|
import { ThingModelApi } from '@/api/iot/thingmodel'
|
||||||
|
import { ProductVO } from '@/api/iot/product/product'
|
||||||
|
import { IOT_PROVIDE_KEY } from '@/views/iot/utils/constants'
|
||||||
|
|
||||||
|
defineOptions({ name: 'ThingModelTSL' })
|
||||||
|
|
||||||
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
const dialogTitle = ref('物模型 TSL') // 弹窗的标题
|
||||||
|
const product = inject<Ref<ProductVO>>(IOT_PROVIDE_KEY.PRODUCT) // 注入产品信息
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = () => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
defineExpose({ open })
|
||||||
|
|
||||||
|
const thingModelTSL = ref('')
|
||||||
|
/** 获取 TSL */
|
||||||
|
const getTsl = async () => {
|
||||||
|
const res = await ThingModelApi.getThingModelTSLByProductId(product?.value?.id || 0)
|
||||||
|
thingModelTSL.value = JSON.stringify(res, null, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码高亮
|
||||||
|
*/
|
||||||
|
const highlightedCode = () => {
|
||||||
|
const result = hljs.highlight('json', thingModelTSL.value, true)
|
||||||
|
return result.value || ' '
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 **/
|
||||||
|
onMounted(async () => {
|
||||||
|
// 注册代码高亮的各种语言
|
||||||
|
hljs.registerLanguage('json', json)
|
||||||
|
await getTsl()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -42,6 +42,9 @@
|
||||||
<Icon class="mr-5px" icon="ep:plus" />
|
<Icon class="mr-5px" icon="ep:plus" />
|
||||||
添加功能
|
添加功能
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button v-hasPermi="[`iot:thing-model:query`]" plain type="primary" @click="openTSL">
|
||||||
|
TSL
|
||||||
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
@ -99,6 +102,7 @@
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
<ThingModelForm ref="formRef" @success="getList" />
|
<ThingModelForm ref="formRef" @success="getList" />
|
||||||
|
<ThingModelTSL ref="thingModelTSLRef" />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
|
import { ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
|
||||||
|
|
@ -160,6 +164,12 @@ const openForm = (type: string, id?: number) => {
|
||||||
formRef.value.open(type, id)
|
formRef.value.open(type, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 展示物模型 TSL */
|
||||||
|
const thingModelTSLRef = ref()
|
||||||
|
const openTSL = () => {
|
||||||
|
thingModelTSLRef.value?.open()
|
||||||
|
}
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
const handleDelete = async (id: number) => {
|
const handleDelete = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue