feat:【iot 物联网】优化场景联动的界面

pull/817/MERGE
YunaiV 2025-09-03 23:07:55 +08:00
parent be964a6287
commit 3686b58dbb
4 changed files with 52 additions and 99 deletions

View File

@ -150,7 +150,6 @@ const authDialogVisible = ref(false) // 定义设备认证信息弹框的可见
const authPasswordVisible = ref(false) // const authPasswordVisible = ref(false) //
const authInfo = ref<IotDeviceAuthInfoVO>({} as IotDeviceAuthInfoVO) // const authInfo = ref<IotDeviceAuthInfoVO>({} as IotDeviceAuthInfoVO) //
// TODO @AI使 /** */
/** 控制地图显示的标志 */ /** 控制地图显示的标志 */
const showMap = computed(() => { const showMap = computed(() => {
return !!(device.longitude && device.latitude) return !!(device.longitude && device.latitude)

View File

@ -1,15 +1,12 @@
<!-- 值输入组件 --> <!-- 值输入组件 -->
<!-- TODO @yunai这个需要在看看 -->
<template> <template>
<div class="w-full min-w-0"> <div class="w-full min-w-0">
<!-- 布尔值选择 --> <!-- 布尔值选择 -->
<el-select <el-select
v-if="propertyType === 'bool'" v-if="propertyType === IoTDataSpecsDataTypeEnum.BOOL"
v-model="localValue" v-model="localValue"
placeholder="请选择布尔值" placeholder="请选择布尔值"
@change="handleChange"
class="w-full!" class="w-full!"
style="width: 100% !important"
> >
<el-option label="真 (true)" value="true" /> <el-option label="真 (true)" value="true" />
<el-option label="假 (false)" value="false" /> <el-option label="假 (false)" value="false" />
@ -17,12 +14,10 @@
<!-- 枚举值选择 --> <!-- 枚举值选择 -->
<el-select <el-select
v-else-if="propertyType === 'enum' && enumOptions.length > 0" v-else-if="propertyType === IoTDataSpecsDataTypeEnum.ENUM && enumOptions.length > 0"
v-model="localValue" v-model="localValue"
placeholder="请选择枚举值" placeholder="请选择枚举值"
@change="handleChange"
class="w-full!" class="w-full!"
style="width: 100% !important"
> >
<el-option <el-option
v-for="option in enumOptions" v-for="option in enumOptions"
@ -34,9 +29,8 @@
<!-- 范围输入 (between 操作符) --> <!-- 范围输入 (between 操作符) -->
<div <div
v-else-if="operator === 'between'" v-else-if="operator === IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN.value"
class="w-full! flex items-center gap-8px" class="w-full! flex items-center gap-8px"
style="width: 100% !important"
> >
<el-input <el-input
v-model="rangeStart" v-model="rangeStart"
@ -53,19 +47,15 @@
placeholder="最大值" placeholder="最大值"
@input="handleRangeChange" @input="handleRangeChange"
class="flex-1 min-w-0" class="flex-1 min-w-0"
style="width: auto !important"
/> />
</div> </div>
<!-- 列表输入 (in 操作符) --> <!-- 列表输入 (in 操作符) -->
<div v-else-if="operator === 'in'" class="w-full!" style="width: 100% !important"> <div
<el-input v-else-if="operator === IotRuleSceneTriggerConditionParameterOperatorEnum.IN.value"
v-model="localValue" class="w-full!"
placeholder="请输入值列表,用逗号分隔" >
@input="handleChange" <el-input v-model="localValue" placeholder="请输入值列表,用逗号分隔" class="w-full!">
class="w-full!"
style="width: 100% !important"
>
<template #suffix> <template #suffix>
<el-tooltip content="多个值用逗号分隔1,2,3" placement="top"> <el-tooltip content="多个值用逗号分隔1,2,3" placement="top">
<Icon <Icon
@ -85,7 +75,7 @@
<!-- 日期时间输入 --> <!-- 日期时间输入 -->
<el-date-picker <el-date-picker
v-else-if="propertyType === 'date'" v-else-if="propertyType === IoTDataSpecsDataTypeEnum.DATE"
v-model="dateValue" v-model="dateValue"
type="datetime" type="datetime"
placeholder="请选择日期时间" placeholder="请选择日期时间"
@ -93,7 +83,6 @@
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
@change="handleDateChange" @change="handleDateChange"
class="w-full!" class="w-full!"
style="width: 100% !important"
/> />
<!-- 数字输入 --> <!-- 数字输入 -->
@ -107,7 +96,6 @@
placeholder="请输入数值" placeholder="请输入数值"
@change="handleNumberChange" @change="handleNumberChange"
class="w-full!" class="w-full!"
style="width: 100% !important"
/> />
<!-- 文本输入 --> <!-- 文本输入 -->
@ -116,9 +104,7 @@
v-model="localValue" v-model="localValue"
:type="getInputType()" :type="getInputType()"
:placeholder="getPlaceholder()" :placeholder="getPlaceholder()"
@input="handleChange"
class="w-full!" class="w-full!"
style="width: 100% !important"
> >
<template #suffix> <template #suffix>
<el-tooltip <el-tooltip
@ -126,9 +112,9 @@
:content="`单位:${propertyConfig.unit}`" :content="`单位:${propertyConfig.unit}`"
placement="top" placement="top"
> >
<span class="text-12px text-[var(--el-text-color-secondary)] px-4px">{{ <span class="text-12px text-[var(--el-text-color-secondary)] px-4px">
propertyConfig.unit {{ propertyConfig.unit }}
}}</span> </span>
</el-tooltip> </el-tooltip>
</template> </template>
</el-input> </el-input>
@ -137,7 +123,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { IoTDataSpecsDataTypeEnum } from '@/views/iot/utils/constants' import {
IoTDataSpecsDataTypeEnum,
IotRuleSceneTriggerConditionParameterOperatorEnum
} from '@/views/iot/utils/constants'
/** 值输入组件 */ /** 值输入组件 */
defineOptions({ name: 'ValueInput' }) defineOptions({ name: 'ValueInput' })
@ -165,7 +154,7 @@ const rangeEnd = ref('') // 范围结束值
const dateValue = ref('') // const dateValue = ref('') //
const numberValue = ref<number>() // const numberValue = ref<number>() //
// /** 计算属性:枚举选项 */
const enumOptions = computed(() => { const enumOptions = computed(() => {
if (props.propertyConfig?.enum) { if (props.propertyConfig?.enum) {
return props.propertyConfig.enum.map((item: any) => ({ return props.propertyConfig.enum.map((item: any) => ({
@ -176,9 +165,12 @@ const enumOptions = computed(() => {
return [] return []
}) })
// /** 计算属性:列表预览 */
const listPreview = computed(() => { const listPreview = computed(() => {
if (props.operator === 'in' && localValue.value) { if (
props.operator === IotRuleSceneTriggerConditionParameterOperatorEnum.IN.value &&
localValue.value
) {
return localValue.value return localValue.value
.split(',') .split(',')
.map((item) => item.trim()) .map((item) => item.trim())
@ -187,10 +179,7 @@ const listPreview = computed(() => {
return [] return []
}) })
/** /** 判断是否为数字类型 */
* 判断是否为数字类型
* @returns 是否为数字类型
*/
const isNumericType = () => { const isNumericType = () => {
return [ return [
IoTDataSpecsDataTypeEnum.INT, IoTDataSpecsDataTypeEnum.INT,
@ -199,10 +188,7 @@ const isNumericType = () => {
].includes((props.propertyType || '') as any) ].includes((props.propertyType || '') as any)
} }
/** /** 获取输入框类型 */
* 获取输入框类型
* @returns 输入框类型
*/
const getInputType = () => { const getInputType = () => {
switch (props.propertyType) { switch (props.propertyType) {
case IoTDataSpecsDataTypeEnum.INT: case IoTDataSpecsDataTypeEnum.INT:
@ -214,10 +200,7 @@ const getInputType = () => {
} }
} }
/** /** 获取占位符文本 */
* 获取占位符文本
* @returns 占位符文本
*/
const getPlaceholder = () => { const getPlaceholder = () => {
const typeMap = { const typeMap = {
[IoTDataSpecsDataTypeEnum.TEXT]: '请输入字符串', [IoTDataSpecsDataTypeEnum.TEXT]: '请输入字符串',
@ -230,48 +213,27 @@ const getPlaceholder = () => {
return typeMap[props.propertyType || ''] || '请输入值' return typeMap[props.propertyType || ''] || '请输入值'
} }
/** /** 获取数字精度 */
* 获取数字精度
* @returns 数字精度
*/
const getPrecision = () => { const getPrecision = () => {
return props.propertyType === IoTDataSpecsDataTypeEnum.INT ? 0 : 2 return props.propertyType === IoTDataSpecsDataTypeEnum.INT ? 0 : 2
} }
/** /** 获取数字步长 */
* 获取数字步长
* @returns 数字步长
*/
const getStep = () => { const getStep = () => {
return props.propertyType === IoTDataSpecsDataTypeEnum.INT ? 1 : 0.1 return props.propertyType === IoTDataSpecsDataTypeEnum.INT ? 1 : 0.1
} }
/** /** 获取最小值 */
* 获取最小值
* @returns 最小值
*/
const getMin = () => { const getMin = () => {
return props.propertyConfig?.min || undefined return props.propertyConfig?.min || undefined
} }
/** /** 获取最大值 */
* 获取最大值
* @returns 最大值
*/
const getMax = () => { const getMax = () => {
return props.propertyConfig?.max || undefined return props.propertyConfig?.max || undefined
} }
/** /** 处理范围变化事件 */
* 处理值变化事件
*/
const handleChange = () => {
//
}
/**
* 处理范围变化事件
*/
const handleRangeChange = () => { const handleRangeChange = () => {
if (rangeStart.value && rangeEnd.value) { if (rangeStart.value && rangeEnd.value) {
localValue.value = `${rangeStart.value},${rangeEnd.value}` localValue.value = `${rangeStart.value},${rangeEnd.value}`
@ -280,23 +242,17 @@ const handleRangeChange = () => {
} }
} }
/** /** 处理日期变化事件 */
* 处理日期变化事件
* @param value 日期值
*/
const handleDateChange = (value: string) => { const handleDateChange = (value: string) => {
localValue.value = value || '' localValue.value = value || ''
} }
/** /** 处理数字变化事件 */
* 处理数字变化事件
* @param value 数字值
*/
const handleNumberChange = (value: number | undefined) => { const handleNumberChange = (value: number | undefined) => {
localValue.value = value?.toString() || '' localValue.value = value?.toString() || ''
} }
// /** 监听操作符变化 */
watch( watch(
() => props.operator, () => props.operator,
() => { () => {

View File

@ -114,7 +114,7 @@
<el-tag size="small" type="warning">自动执行</el-tag> <el-tag size="small" type="warning">自动执行</el-tag>
</div> </div>
<div class="text-12px text-[var(--el-text-color-secondary)] leading-relaxed"> <div class="text-12px text-[var(--el-text-color-secondary)] leading-relaxed">
当触发条件满足时系统将自动发送告警通知无需额外配置 当触发条件满足时系统将自动发送告警通知可在菜单 [告警中心 -> 告警配置] 管理
</div> </div>
</div> </div>
</div> </div>

View File

@ -79,9 +79,9 @@
<Icon icon="ep:document" /> <Icon icon="ep:document" />
</div> </div>
<div> <div>
<div class="text-24px font-600 text-[#303133] leading-none">{{ <div class="text-24px font-600 text-[#303133] leading-none">
statistics.total {{ statistics.total }}
}}</div> </div>
<div class="text-14px text-[#909399] mt-4px">总规则数</div> <div class="text-14px text-[#909399] mt-4px">总规则数</div>
</div> </div>
</div> </div>
@ -99,9 +99,9 @@
<Icon icon="ep:check" /> <Icon icon="ep:check" />
</div> </div>
<div> <div>
<div class="text-24px font-600 text-[#303133] leading-none">{{ <div class="text-24px font-600 text-[#303133] leading-none">
statistics.enabled {{ statistics.enabled }}
}}</div> </div>
<div class="text-14px text-[#909399] mt-4px">启用规则</div> <div class="text-14px text-[#909399] mt-4px">启用规则</div>
</div> </div>
</div> </div>
@ -119,9 +119,9 @@
<Icon icon="ep:close" /> <Icon icon="ep:close" />
</div> </div>
<div> <div>
<div class="text-24px font-600 text-[#303133] leading-none">{{ <div class="text-24px font-600 text-[#303133] leading-none">
statistics.disabled {{ statistics.disabled }}
}}</div> </div>
<div class="text-14px text-[#909399] mt-4px">禁用规则</div> <div class="text-14px text-[#909399] mt-4px">禁用规则</div>
</div> </div>
</div> </div>
@ -139,9 +139,9 @@
<Icon icon="ep:timer" /> <Icon icon="ep:timer" />
</div> </div>
<div> <div>
<div class="text-24px font-600 text-[#303133] leading-none">{{ <div class="text-24px font-600 text-[#303133] leading-none">
statistics.timerRules {{ statistics.timerRules }}
}}</div> </div>
<div class="text-14px text-[#909399] mt-4px">定时规则</div> <div class="text-14px text-[#909399] mt-4px">定时规则</div>
</div> </div>
</div> </div>
@ -214,7 +214,7 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" width="210" fixed="right"> <el-table-column label="操作" width="210" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<div class="flex gap-8px"> <div>
<el-button type="primary" link @click="handleEdit(row)"> <el-button type="primary" link @click="handleEdit(row)">
<Icon icon="ep:edit" /> <Icon icon="ep:edit" />
编辑 编辑
@ -293,7 +293,6 @@ const statistics = ref({
total: 0, total: 0,
enabled: 0, enabled: 0,
disabled: 0, disabled: 0,
triggered: 0, // (使)
timerRules: 0 // timerRules: 0 //
}) })
@ -321,9 +320,9 @@ const getRuleSceneSummary = (rule: IotSceneRule) => {
} }
// //
if (trigger.deviceId) { if (trigger.deviceId) {
description += ` [设备ID: ${trigger.deviceId}]` description += ` [设备 ID: ${trigger.deviceId}]`
} else if (trigger.productId) { } else if (trigger.productId) {
description += ` [产品ID: ${trigger.productId}]` description += ` [产品 ID: ${trigger.productId}]`
} }
return description return description
}) || [] }) || []
@ -334,13 +333,13 @@ const getRuleSceneSummary = (rule: IotSceneRule) => {
let description = getActionTypeLabel(action.type) let description = getActionTypeLabel(action.type)
// //
if (action.deviceId) { if (action.deviceId) {
description += ` [设备ID: ${action.deviceId}]` description += ` [设备 ID: ${action.deviceId}]`
} else if (action.productId) { } else if (action.productId) {
description += ` [产品ID: ${action.productId}]` description += ` [产品 ID: ${action.productId}]`
} }
// //
if (action.alertConfigId) { if (action.alertConfigId) {
description += ` [告警配置ID: ${action.alertConfigId}]` description += ` [告警配置 ID: ${action.alertConfigId}]`
} }
return description return description
}) || [] }) || []
@ -371,7 +370,6 @@ const updateStatistics = () => {
total: list.value.length, total: list.value.length,
enabled: list.value.filter((item) => item.status === CommonStatusEnum.ENABLE).length, enabled: list.value.filter((item) => item.status === CommonStatusEnum.ENABLE).length,
disabled: list.value.filter((item) => item.status === CommonStatusEnum.DISABLE).length, disabled: list.value.filter((item) => item.status === CommonStatusEnum.DISABLE).length,
triggered: list.value.filter((item) => item.status === CommonStatusEnum.ENABLE).length,
timerRules: list.value.filter((item) => hasTimerTrigger(item)).length timerRules: list.value.filter((item) => hasTimerTrigger(item)).length
} }
} }