review:【IoT 物联网】场景联动的部分 review
parent
a72c297e86
commit
bbd38e026b
|
|
@ -1,4 +1,5 @@
|
|||
<!-- IoT场景联动规则表单 - 主表单组件 -->
|
||||
<!-- TODO @puhui999:要不搞个 form 目录,不用 components;保持和别的模块风格一致哈; -->
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="drawerVisible"
|
||||
|
|
@ -37,6 +38,7 @@
|
|||
</div>
|
||||
|
||||
<!-- 抽屉底部操作栏 -->
|
||||
<!-- TODO @puhui999:这个按钮逻辑,和别的模块一致 -->
|
||||
<template #footer>
|
||||
<div class="drawer-footer">
|
||||
<el-button @click="handleClose" size="large">取消</el-button>
|
||||
|
|
@ -65,7 +67,7 @@ import { getBaseValidationRules } from '../utils/validation'
|
|||
import { transformFormToApi, transformApiToForm, createDefaultFormData } from '../utils/transform'
|
||||
import { handleValidationError, showSuccess, withErrorHandling } from '../utils/errorHandler'
|
||||
|
||||
/** IoT场景联动规则表单 - 主表单组件 */
|
||||
/** IoT 场景联动规则表单 - 主表单组件 */
|
||||
defineOptions({ name: 'RuleSceneForm' })
|
||||
|
||||
interface Props {
|
||||
|
|
@ -96,7 +98,7 @@ const actionValidation = ref({ valid: true, message: '' })
|
|||
|
||||
// 计算属性
|
||||
const isEdit = computed(() => !!props.ruleScene?.id)
|
||||
const drawerTitle = computed(() => (isEdit.value ? '编辑场景联动规则' : '新增场景联动规则'))
|
||||
const drawerTitle = computed(() => (isEdit.value ? '编辑场景联动规则' : '新增场景联动规则')) // TODO @puhui999:这个风格,和别的模块一致;
|
||||
|
||||
const canSubmit = computed(() => {
|
||||
return (
|
||||
|
|
@ -140,6 +142,7 @@ const handleValidate = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO @puhui999:参考下别的模块,不用这么复杂哈;
|
||||
const handleSubmit = async () => {
|
||||
const result = await withErrorHandling(
|
||||
async () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<!-- 告警配置组件 -->
|
||||
<template>
|
||||
<div class="alert-config">
|
||||
<!-- TODO @puhui999:触发告警时,不用选择配置哈; -->
|
||||
<el-form-item label="告警配置" required>
|
||||
<el-select
|
||||
v-model="localValue"
|
||||
|
|
@ -95,7 +96,7 @@ const isValid = ref(true)
|
|||
|
||||
// 计算属性
|
||||
const selectedConfig = computed(() => {
|
||||
return alertConfigs.value.find(config => config.id === localValue.value)
|
||||
return alertConfigs.value.find((config) => config.id === localValue.value)
|
||||
})
|
||||
|
||||
// 工具函数
|
||||
|
|
@ -121,7 +122,7 @@ const updateValidationResult = () => {
|
|||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const config = selectedConfig.value
|
||||
if (!config) {
|
||||
isValid.value = false
|
||||
|
|
@ -129,14 +130,14 @@ const updateValidationResult = () => {
|
|||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!config.enabled) {
|
||||
isValid.value = false
|
||||
validationMessage.value = '选择的告警配置已禁用'
|
||||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 验证通过
|
||||
isValid.value = true
|
||||
validationMessage.value = '告警配置验证通过'
|
||||
|
|
@ -149,6 +150,7 @@ const getAlertConfigs = async () => {
|
|||
try {
|
||||
// 这里应该调用真实的API获取告警配置
|
||||
// 暂时使用模拟数据
|
||||
// TODO @puhui999:这里是模拟数据
|
||||
alertConfigs.value = [
|
||||
{
|
||||
id: 1,
|
||||
|
|
@ -183,9 +185,12 @@ const getAlertConfigs = async () => {
|
|||
}
|
||||
|
||||
// 监听值变化
|
||||
watch(() => localValue.value, () => {
|
||||
updateValidationResult()
|
||||
})
|
||||
watch(
|
||||
() => localValue.value,
|
||||
() => {
|
||||
updateValidationResult()
|
||||
}
|
||||
)
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<!-- 单个条件配置组件 -->
|
||||
<!-- TODO @puhui999:这里需要在对下阿里云 IoT,不太对;它是条件类型;然后选择产品、设备;接着选条件类型对应的比较; -->
|
||||
<template>
|
||||
<div class="condition-config">
|
||||
<el-row :gutter="16">
|
||||
|
|
@ -105,11 +106,11 @@ const conditionPreview = computed(() => {
|
|||
if (!condition.value.identifier || !condition.value.operator || !condition.value.param) {
|
||||
return ''
|
||||
}
|
||||
|
||||
|
||||
const propertyName = propertyConfig.value?.name || condition.value.identifier
|
||||
const operatorText = getOperatorText(condition.value.operator)
|
||||
const value = condition.value.param
|
||||
|
||||
|
||||
return `当 ${propertyName} ${operatorText} ${value} 时触发`
|
||||
})
|
||||
|
||||
|
|
@ -122,8 +123,8 @@ const getOperatorText = (operator: string) => {
|
|||
'>=': '大于等于',
|
||||
'<': '小于',
|
||||
'<=': '小于等于',
|
||||
'in': '包含于',
|
||||
'between': '介于'
|
||||
in: '包含于',
|
||||
between: '介于'
|
||||
}
|
||||
return operatorMap[operator] || operator
|
||||
}
|
||||
|
|
@ -137,11 +138,11 @@ const updateConditionField = (field: keyof ConditionFormData, value: any) => {
|
|||
const handlePropertyChange = (propertyInfo: { type: string; config: any }) => {
|
||||
propertyType.value = propertyInfo.type
|
||||
propertyConfig.value = propertyInfo.config
|
||||
|
||||
|
||||
// 重置操作符和值
|
||||
condition.value.operator = '='
|
||||
condition.value.param = ''
|
||||
|
||||
|
||||
updateValidationResult()
|
||||
}
|
||||
|
||||
|
|
@ -164,21 +165,21 @@ const updateValidationResult = () => {
|
|||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!condition.value.operator) {
|
||||
isValid.value = false
|
||||
validationMessage.value = '请选择操作符'
|
||||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!condition.value.param) {
|
||||
isValid.value = false
|
||||
validationMessage.value = '请输入比较值'
|
||||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 值验证
|
||||
if (!valueValidation.value.valid) {
|
||||
isValid.value = false
|
||||
|
|
@ -186,7 +187,7 @@ const updateValidationResult = () => {
|
|||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 验证通过
|
||||
isValid.value = true
|
||||
validationMessage.value = '条件配置验证通过'
|
||||
|
|
@ -194,9 +195,13 @@ const updateValidationResult = () => {
|
|||
}
|
||||
|
||||
// 监听条件变化
|
||||
watch(() => [condition.value.identifier, condition.value.operator, condition.value.param], () => {
|
||||
updateValidationResult()
|
||||
}, { deep: true })
|
||||
watch(
|
||||
() => [condition.value.identifier, condition.value.operator, condition.value.param],
|
||||
() => {
|
||||
updateValidationResult()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -40,15 +40,9 @@
|
|||
</div>
|
||||
|
||||
<!-- 逻辑连接符 -->
|
||||
<div
|
||||
v-if="index < group.conditions!.length - 1"
|
||||
class="logic-connector"
|
||||
>
|
||||
<el-select
|
||||
v-model="group.logicOperator"
|
||||
size="small"
|
||||
style="width: 80px;"
|
||||
>
|
||||
<!-- TODO @puhui999:不用这个哈; -->
|
||||
<div v-if="index < group.conditions!.length - 1" class="logic-connector">
|
||||
<el-select v-model="group.logicOperator" size="small" style="width: 80px">
|
||||
<el-option label="且" value="AND" />
|
||||
<el-option label="或" value="OR" />
|
||||
</el-select>
|
||||
|
|
@ -67,23 +61,22 @@
|
|||
</div>
|
||||
|
||||
<!-- 添加条件按钮 -->
|
||||
<div v-if="group.conditions && group.conditions.length > 0 && group.conditions.length < maxConditions" class="add-condition">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="addCondition"
|
||||
class="add-condition-btn"
|
||||
>
|
||||
<div
|
||||
v-if="
|
||||
group.conditions && group.conditions.length > 0 && group.conditions.length < maxConditions
|
||||
"
|
||||
class="add-condition"
|
||||
>
|
||||
<el-button type="primary" plain @click="addCondition" class="add-condition-btn">
|
||||
<Icon icon="ep:plus" />
|
||||
继续添加条件
|
||||
</el-button>
|
||||
<span class="add-condition-text">
|
||||
最多可添加 {{ maxConditions }} 个条件
|
||||
</span>
|
||||
<span class="add-condition-text"> 最多可添加 {{ maxConditions }} 个条件 </span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 验证结果 -->
|
||||
<!-- TODO @puhui999:是不是不用这种提示;只要 validator rules 能展示出来就好了呀。。。 -->
|
||||
<div v-if="validationMessage" class="validation-result">
|
||||
<el-alert
|
||||
:title="validationMessage"
|
||||
|
|
@ -98,10 +91,10 @@
|
|||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import ConditionConfig from './ConditionConfig.vue'
|
||||
import {
|
||||
ConditionGroupFormData,
|
||||
import {
|
||||
ConditionGroupFormData,
|
||||
ConditionFormData,
|
||||
IotRuleSceneTriggerTypeEnum
|
||||
IotRuleSceneTriggerTypeEnum
|
||||
} from '@/api/iot/rule/scene/scene.types'
|
||||
|
||||
/** 条件组配置组件 */
|
||||
|
|
@ -155,11 +148,11 @@ const addCondition = () => {
|
|||
if (!group.value.conditions) {
|
||||
group.value.conditions = []
|
||||
}
|
||||
|
||||
|
||||
if (group.value.conditions.length >= maxConditions) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const newCondition: ConditionFormData = {
|
||||
type: props.triggerType,
|
||||
productId: props.productId || 0,
|
||||
|
|
@ -168,7 +161,7 @@ const addCondition = () => {
|
|||
operator: '=',
|
||||
param: ''
|
||||
}
|
||||
|
||||
|
||||
group.value.conditions.push(newCondition)
|
||||
}
|
||||
|
||||
|
|
@ -176,10 +169,10 @@ const removeCondition = (index: number) => {
|
|||
if (group.value.conditions) {
|
||||
group.value.conditions.splice(index, 1)
|
||||
delete conditionValidations.value[index]
|
||||
|
||||
|
||||
// 重新索引验证结果
|
||||
const newValidations: { [key: number]: { valid: boolean; message: string } } = {}
|
||||
Object.keys(conditionValidations.value).forEach(key => {
|
||||
Object.keys(conditionValidations.value).forEach((key) => {
|
||||
const numKey = parseInt(key)
|
||||
if (numKey > index) {
|
||||
newValidations[numKey - 1] = conditionValidations.value[numKey]
|
||||
|
|
@ -188,7 +181,7 @@ const removeCondition = (index: number) => {
|
|||
}
|
||||
})
|
||||
conditionValidations.value = newValidations
|
||||
|
||||
|
||||
updateValidationResult()
|
||||
}
|
||||
}
|
||||
|
|
@ -205,28 +198,29 @@ const updateValidationResult = () => {
|
|||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const validations = Object.values(conditionValidations.value)
|
||||
const allValid = validations.every(v => v.valid)
|
||||
|
||||
const allValid = validations.every((v) => v.valid)
|
||||
|
||||
if (allValid) {
|
||||
isValid.value = true
|
||||
validationMessage.value = '条件组配置验证通过'
|
||||
} else {
|
||||
isValid.value = false
|
||||
const errorMessages = validations
|
||||
.filter(v => !v.valid)
|
||||
.map(v => v.message)
|
||||
const errorMessages = validations.filter((v) => !v.valid).map((v) => v.message)
|
||||
validationMessage.value = `条件配置错误: ${errorMessages.join('; ')}`
|
||||
}
|
||||
|
||||
|
||||
emit('validate', { valid: isValid.value, message: validationMessage.value })
|
||||
}
|
||||
|
||||
// 监听条件数量变化
|
||||
watch(() => group.value.conditions?.length, () => {
|
||||
updateValidationResult()
|
||||
})
|
||||
watch(
|
||||
() => group.value.conditions?.length,
|
||||
() => {
|
||||
updateValidationResult()
|
||||
}
|
||||
)
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<!-- 设备控制配置组件 -->
|
||||
<!-- TODO @puhui999:貌似没生效~~~ -->
|
||||
<template>
|
||||
<div class="device-control-config">
|
||||
<!-- 产品和设备选择 -->
|
||||
|
|
@ -19,15 +20,10 @@
|
|||
@input="handleParamsChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<!-- 参数示例 -->
|
||||
<div class="params-example">
|
||||
<el-alert
|
||||
title="参数格式示例"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
>
|
||||
<el-alert title="参数格式示例" type="info" :closable="false" show-icon>
|
||||
<template #default>
|
||||
<div class="example-content">
|
||||
<p>属性设置示例:</p>
|
||||
|
|
@ -109,14 +105,14 @@ const updateValidationResult = () => {
|
|||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!action.value.params || Object.keys(action.value.params).length === 0) {
|
||||
isValid.value = false
|
||||
validationMessage.value = '请配置控制参数'
|
||||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 验证通过
|
||||
isValid.value = true
|
||||
validationMessage.value = '设备控制配置验证通过'
|
||||
|
|
@ -132,11 +128,15 @@ onMounted(() => {
|
|||
})
|
||||
|
||||
// 监听参数变化
|
||||
watch(() => action.value.params, (newParams) => {
|
||||
if (newParams && typeof newParams === 'object') {
|
||||
paramsJson.value = JSON.stringify(newParams, null, 2)
|
||||
}
|
||||
}, { deep: true })
|
||||
watch(
|
||||
() => action.value.params,
|
||||
(newParams) => {
|
||||
if (newParams && typeof newParams === 'object') {
|
||||
paramsJson.value = JSON.stringify(newParams, null, 2)
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -8,17 +8,10 @@
|
|||
@change="handleDeviceChange"
|
||||
/>
|
||||
|
||||
<!-- TODO @puhui999:这里有点冗余,建议去掉 -->
|
||||
<!-- 设备状态变更提示 -->
|
||||
<div
|
||||
v-if="trigger.type === TriggerTypeEnum.DEVICE_STATE_UPDATE"
|
||||
class="state-update-notice"
|
||||
>
|
||||
<el-alert
|
||||
title="设备状态变更触发"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
>
|
||||
<div v-if="trigger.type === TriggerTypeEnum.DEVICE_STATE_UPDATE" class="state-update-notice">
|
||||
<el-alert title="设备状态变更触发" type="info" :closable="false" show-icon>
|
||||
<template #default>
|
||||
<p>当选中的设备上线或离线时将自动触发场景规则</p>
|
||||
<p class="notice-tip">无需配置额外的触发条件</p>
|
||||
|
|
@ -27,13 +20,11 @@
|
|||
</div>
|
||||
|
||||
<!-- 条件组配置 -->
|
||||
<div
|
||||
v-else-if="needsConditions"
|
||||
class="condition-groups"
|
||||
>
|
||||
<div v-else-if="needsConditions" class="condition-groups">
|
||||
<div class="condition-groups-header">
|
||||
<div class="header-left">
|
||||
<span class="header-title">触发条件</span>
|
||||
<!-- TODO @puhui999:去掉数量限制 -->
|
||||
<el-tag size="small" type="info">
|
||||
{{ trigger.conditionGroups?.length || 0 }}/{{ maxConditionGroups }}
|
||||
</el-tag>
|
||||
|
|
@ -52,7 +43,10 @@
|
|||
</div>
|
||||
|
||||
<!-- 条件组列表 -->
|
||||
<div v-if="trigger.conditionGroups && trigger.conditionGroups.length > 0" class="condition-groups-list">
|
||||
<div
|
||||
v-if="trigger.conditionGroups && trigger.conditionGroups.length > 0"
|
||||
class="condition-groups-list"
|
||||
>
|
||||
<div
|
||||
v-for="(group, groupIndex) in trigger.conditionGroups"
|
||||
:key="`group-${groupIndex}`"
|
||||
|
|
@ -61,10 +55,11 @@
|
|||
<div class="group-header">
|
||||
<div class="group-title">
|
||||
<span>条件组 {{ groupIndex + 1 }}</span>
|
||||
<!-- TODO @puhui999:不用“且、或”哈。条件组之间,就是或;条件之间就是且 -->
|
||||
<el-select
|
||||
v-model="group.logicOperator"
|
||||
size="small"
|
||||
style="width: 80px; margin-left: 12px;"
|
||||
style="width: 80px; margin-left: 12px"
|
||||
>
|
||||
<el-option label="且" value="AND" />
|
||||
<el-option label="或" value="OR" />
|
||||
|
|
@ -120,10 +115,10 @@
|
|||
import { useVModel } from '@vueuse/core'
|
||||
import ProductDeviceSelector from '../selectors/ProductDeviceSelector.vue'
|
||||
import ConditionGroupConfig from './ConditionGroupConfig.vue'
|
||||
import {
|
||||
TriggerFormData,
|
||||
import {
|
||||
TriggerFormData,
|
||||
ConditionGroupFormData,
|
||||
IotRuleSceneTriggerTypeEnum as TriggerTypeEnum
|
||||
IotRuleSceneTriggerTypeEnum as TriggerTypeEnum
|
||||
} from '@/api/iot/rule/scene/scene.types'
|
||||
|
||||
/** 设备触发配置组件 */
|
||||
|
|
@ -173,16 +168,16 @@ const addConditionGroup = () => {
|
|||
if (!trigger.value.conditionGroups) {
|
||||
trigger.value.conditionGroups = []
|
||||
}
|
||||
|
||||
|
||||
if (trigger.value.conditionGroups.length >= maxConditionGroups) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const newGroup: ConditionGroupFormData = {
|
||||
conditions: [],
|
||||
logicOperator: 'AND'
|
||||
}
|
||||
|
||||
|
||||
trigger.value.conditionGroups.push(newGroup)
|
||||
}
|
||||
|
||||
|
|
@ -190,10 +185,10 @@ const removeConditionGroup = (index: number) => {
|
|||
if (trigger.value.conditionGroups) {
|
||||
trigger.value.conditionGroups.splice(index, 1)
|
||||
delete groupValidations.value[index]
|
||||
|
||||
|
||||
// 重新索引验证结果
|
||||
const newValidations: { [key: number]: { valid: boolean; message: string } } = {}
|
||||
Object.keys(groupValidations.value).forEach(key => {
|
||||
Object.keys(groupValidations.value).forEach((key) => {
|
||||
const numKey = parseInt(key)
|
||||
if (numKey > index) {
|
||||
newValidations[numKey - 1] = groupValidations.value[numKey]
|
||||
|
|
@ -202,7 +197,7 @@ const removeConditionGroup = (index: number) => {
|
|||
}
|
||||
})
|
||||
groupValidations.value = newValidations
|
||||
|
||||
|
||||
updateValidationResult()
|
||||
}
|
||||
}
|
||||
|
|
@ -220,7 +215,7 @@ const updateValidationResult = () => {
|
|||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 设备状态变更不需要条件验证
|
||||
if (trigger.value.type === TriggerTypeEnum.DEVICE_STATE_UPDATE) {
|
||||
isValid.value = true
|
||||
|
|
@ -228,7 +223,7 @@ const updateValidationResult = () => {
|
|||
emit('validate', { valid: true, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 条件组验证
|
||||
if (!trigger.value.conditionGroups || trigger.value.conditionGroups.length === 0) {
|
||||
isValid.value = false
|
||||
|
|
@ -236,33 +231,38 @@ const updateValidationResult = () => {
|
|||
emit('validate', { valid: false, message: validationMessage.value })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const validations = Object.values(groupValidations.value)
|
||||
const allValid = validations.every(v => v.valid)
|
||||
|
||||
const allValid = validations.every((v) => v.valid)
|
||||
|
||||
if (allValid) {
|
||||
isValid.value = true
|
||||
validationMessage.value = '设备触发配置验证通过'
|
||||
} else {
|
||||
isValid.value = false
|
||||
const errorMessages = validations
|
||||
.filter(v => !v.valid)
|
||||
.map(v => v.message)
|
||||
const errorMessages = validations.filter((v) => !v.valid).map((v) => v.message)
|
||||
validationMessage.value = `条件组配置错误: ${errorMessages.join('; ')}`
|
||||
}
|
||||
|
||||
|
||||
emit('validate', { valid: isValid.value, message: validationMessage.value })
|
||||
}
|
||||
|
||||
// 监听触发器类型变化
|
||||
watch(() => trigger.value.type, () => {
|
||||
updateValidationResult()
|
||||
})
|
||||
watch(
|
||||
() => trigger.value.type,
|
||||
() => {
|
||||
updateValidationResult()
|
||||
}
|
||||
)
|
||||
|
||||
// 监听产品设备变化
|
||||
watch(() => [trigger.value.productId, trigger.value.deviceId], () => {
|
||||
updateValidationResult()
|
||||
})
|
||||
watch(
|
||||
() => [trigger.value.productId, trigger.value.deviceId],
|
||||
() => {
|
||||
updateValidationResult()
|
||||
}
|
||||
)
|
||||
// TODO @puhui999:unocss
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@
|
|||
<span class="header-title">定时触发配置</span>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="showBuilder = !showBuilder"
|
||||
>
|
||||
<el-button type="text" size="small" @click="showBuilder = !showBuilder">
|
||||
<Icon :icon="showBuilder ? 'ep:edit' : 'ep:setting'" />
|
||||
{{ showBuilder ? '手动编辑' : '可视化编辑' }}
|
||||
</el-button>
|
||||
|
|
@ -19,6 +15,7 @@
|
|||
</div>
|
||||
|
||||
<!-- 可视化编辑器 -->
|
||||
<!-- TODO @puhui999:是不是复用现有的 cron 组件;不然有点重复哈;维护比较复杂 -->
|
||||
<div v-if="showBuilder" class="visual-builder">
|
||||
<CronBuilder v-model="localValue" @validate="handleValidate" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<!-- CRON 可视化构建器组件 -->
|
||||
<!-- TODO @puhui999:看看能不能复用全局的 cron 组件 -->
|
||||
<template>
|
||||
<div class="cron-builder">
|
||||
<div class="builder-header">
|
||||
<span class="header-title">可视化 CRON 编辑器</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="builder-content">
|
||||
<!-- 快捷选项 -->
|
||||
<div class="quick-options">
|
||||
|
|
@ -36,9 +37,9 @@
|
|||
<el-option label="每分钟" value="*" />
|
||||
<el-option
|
||||
v-for="i in 60"
|
||||
:key="i-1"
|
||||
:label="`${i-1}分`"
|
||||
:value="String(i-1)"
|
||||
:key="i - 1"
|
||||
:label="`${i - 1}分`"
|
||||
:value="String(i - 1)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
|
@ -49,9 +50,9 @@
|
|||
<el-option label="每小时" value="*" />
|
||||
<el-option
|
||||
v-for="i in 24"
|
||||
:key="i-1"
|
||||
:label="`${i-1}时`"
|
||||
:value="String(i-1)"
|
||||
:key="i - 1"
|
||||
:label="`${i - 1}时`"
|
||||
:value="String(i - 1)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
|
@ -60,12 +61,7 @@
|
|||
<el-form-item label="日">
|
||||
<el-select v-model="cronParts.day" @change="updateCronExpression">
|
||||
<el-option label="每日" value="*" />
|
||||
<el-option
|
||||
v-for="i in 31"
|
||||
:key="i"
|
||||
:label="`${i}日`"
|
||||
:value="String(i)"
|
||||
/>
|
||||
<el-option v-for="i in 31" :key="i" :label="`${i}日`" :value="String(i)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
|
@ -132,7 +128,20 @@ const cronParts = reactive({
|
|||
})
|
||||
|
||||
// 常量数据
|
||||
const months = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
|
||||
const months = [
|
||||
'1月',
|
||||
'2月',
|
||||
'3月',
|
||||
'4月',
|
||||
'5月',
|
||||
'6月',
|
||||
'7月',
|
||||
'8月',
|
||||
'9月',
|
||||
'10月',
|
||||
'11月',
|
||||
'12月'
|
||||
]
|
||||
const weeks = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
||||
|
||||
// 快捷选项
|
||||
|
|
@ -159,7 +168,7 @@ const applyQuickOption = (option: any) => {
|
|||
|
||||
const parseCronExpression = () => {
|
||||
if (!localValue.value) return
|
||||
|
||||
|
||||
const parts = localValue.value.split(' ')
|
||||
if (parts.length >= 6) {
|
||||
cronParts.second = parts[0] || '0'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<!-- CRON 表达式输入组件 -->
|
||||
<!-- TODO @puhui999:看看能不能复用全局的 cron 组件 -->
|
||||
<template>
|
||||
<div class="cron-input">
|
||||
<el-input
|
||||
|
|
@ -13,15 +14,10 @@
|
|||
</el-tooltip>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
|
||||
<!-- 帮助信息 -->
|
||||
<div v-if="showHelp" class="cron-help">
|
||||
<el-alert
|
||||
title="CRON 表达式格式:秒 分 时 日 月 周"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
>
|
||||
<el-alert title="CRON 表达式格式:秒 分 时 日 月 周" type="info" :closable="false" show-icon>
|
||||
<template #default>
|
||||
<div class="help-content">
|
||||
<p><strong>示例:</strong></p>
|
||||
|
|
@ -83,7 +79,7 @@ const validateExpression = () => {
|
|||
emit('validate', { valid: false, message: '请输入CRON表达式' })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const isValid = validateCronExpression(localValue.value)
|
||||
if (isValid) {
|
||||
emit('validate', { valid: true, message: 'CRON表达式验证通过' })
|
||||
|
|
@ -93,9 +89,12 @@ const validateExpression = () => {
|
|||
}
|
||||
|
||||
// 监听值变化
|
||||
watch(() => localValue.value, () => {
|
||||
validateExpression()
|
||||
})
|
||||
watch(
|
||||
() => localValue.value,
|
||||
() => {
|
||||
validateExpression()
|
||||
}
|
||||
)
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -15,19 +15,10 @@
|
|||
|
||||
<!-- 描述模板 -->
|
||||
<teleport to="body">
|
||||
<div
|
||||
v-if="showTemplates"
|
||||
ref="templateDropdownRef"
|
||||
class="templates"
|
||||
:style="dropdownStyle"
|
||||
>
|
||||
<div v-if="showTemplates" ref="templateDropdownRef" class="templates" :style="dropdownStyle">
|
||||
<div class="templates-header">
|
||||
<span class="templates-title">描述模板</span>
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="showTemplates = false"
|
||||
>
|
||||
<el-button type="text" size="small" @click="showTemplates = false">
|
||||
<Icon icon="ep:close" />
|
||||
</el-button>
|
||||
</div>
|
||||
|
|
@ -45,13 +36,10 @@
|
|||
</div>
|
||||
</teleport>
|
||||
|
||||
<!-- TODO @puhui999:不用模版哈,简单点。。。 -->
|
||||
<!-- 模板按钮 -->
|
||||
<div v-if="!localValue && !showTemplates" class="template-trigger">
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="toggleTemplates"
|
||||
>
|
||||
<el-button type="text" size="small" @click="toggleTemplates">
|
||||
<Icon icon="ep:document" class="mr-1" />
|
||||
使用模板
|
||||
</el-button>
|
||||
|
|
@ -163,8 +151,12 @@ const toggleTemplates = () => {
|
|||
|
||||
// 点击外部关闭下拉框
|
||||
const handleClickOutside = (event: Event) => {
|
||||
if (templateDropdownRef.value && !templateDropdownRef.value.contains(event.target as Node) &&
|
||||
inputRef.value && !inputRef.value.$el.contains(event.target as Node)) {
|
||||
if (
|
||||
templateDropdownRef.value &&
|
||||
!templateDropdownRef.value.contains(event.target as Node) &&
|
||||
inputRef.value &&
|
||||
!inputRef.value.$el.contains(event.target as Node)
|
||||
) {
|
||||
showTemplates.value = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@
|
|||
<Icon icon="ep:edit" class="input-icon" />
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
|
||||
<!-- 智能提示 -->
|
||||
<!-- TODO @puhui999:暂时不用考虑智能推荐哈。用途不大 -->
|
||||
<div v-if="showSuggestions && suggestions.length > 0" class="suggestions">
|
||||
<div class="suggestions-header">
|
||||
<span class="suggestions-title">推荐名称</span>
|
||||
|
|
@ -72,9 +73,12 @@ const nameTemplates = [
|
|||
const handleInput = (value: string) => {
|
||||
if (value.length > 0 && value.length < 10) {
|
||||
// 根据输入内容过滤建议
|
||||
suggestions.value = nameTemplates.filter(template =>
|
||||
template.includes(value) || value.includes('温度') && template.includes('温度')
|
||||
).slice(0, 5)
|
||||
suggestions.value = nameTemplates
|
||||
.filter(
|
||||
(template) =>
|
||||
template.includes(value) || (value.includes('温度') && template.includes('温度'))
|
||||
)
|
||||
.slice(0, 5)
|
||||
showSuggestions.value = suggestions.value.length > 0
|
||||
} else {
|
||||
showSuggestions.value = false
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<!-- 值输入组件 -->
|
||||
<!-- TODO @yunai:这个需要在看看。。。 -->
|
||||
<template>
|
||||
<div class="value-input">
|
||||
<!-- 布尔值选择 -->
|
||||
|
|
@ -64,12 +65,7 @@
|
|||
</el-input>
|
||||
<div v-if="listPreview.length > 0" class="list-preview">
|
||||
<span class="preview-label">解析结果:</span>
|
||||
<el-tag
|
||||
v-for="(item, index) in listPreview"
|
||||
:key="index"
|
||||
size="small"
|
||||
class="preview-tag"
|
||||
>
|
||||
<el-tag v-for="(item, index) in listPreview" :key="index" size="small" class="preview-tag">
|
||||
{{ item }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
|
@ -110,7 +106,11 @@
|
|||
class="w-full"
|
||||
>
|
||||
<template #suffix>
|
||||
<el-tooltip v-if="propertyConfig?.unit" :content="`单位:${propertyConfig.unit}`" placement="top">
|
||||
<el-tooltip
|
||||
v-if="propertyConfig?.unit"
|
||||
:content="`单位:${propertyConfig.unit}`"
|
||||
placement="top"
|
||||
>
|
||||
<span class="input-unit">{{ propertyConfig.unit }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
|
@ -172,7 +172,10 @@ const enumOptions = computed(() => {
|
|||
|
||||
const listPreview = computed(() => {
|
||||
if (props.operator === 'in' && localValue.value) {
|
||||
return localValue.value.split(',').map(item => item.trim()).filter(item => item)
|
||||
return localValue.value
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter((item) => item)
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
|
@ -195,12 +198,12 @@ const getInputType = () => {
|
|||
|
||||
const getPlaceholder = () => {
|
||||
const typeMap = {
|
||||
'string': '请输入字符串',
|
||||
'int': '请输入整数',
|
||||
'float': '请输入浮点数',
|
||||
'double': '请输入双精度数',
|
||||
'struct': '请输入JSON格式数据',
|
||||
'array': '请输入数组格式数据'
|
||||
string: '请输入字符串',
|
||||
int: '请输入整数',
|
||||
float: '请输入浮点数',
|
||||
double: '请输入双精度数',
|
||||
struct: '请输入JSON格式数据',
|
||||
array: '请输入数组格式数据'
|
||||
}
|
||||
return typeMap[props.propertyType || ''] || '请输入值'
|
||||
}
|
||||
|
|
@ -325,18 +328,24 @@ const validateValue = () => {
|
|||
}
|
||||
|
||||
// 监听值变化
|
||||
watch(() => localValue.value, () => {
|
||||
validateValue()
|
||||
})
|
||||
watch(
|
||||
() => localValue.value,
|
||||
() => {
|
||||
validateValue()
|
||||
}
|
||||
)
|
||||
|
||||
// 监听操作符变化
|
||||
watch(() => props.operator, () => {
|
||||
localValue.value = ''
|
||||
rangeStart.value = ''
|
||||
rangeEnd.value = ''
|
||||
dateValue.value = ''
|
||||
numberValue.value = undefined
|
||||
})
|
||||
watch(
|
||||
() => props.operator,
|
||||
() => {
|
||||
localValue.value = ''
|
||||
rangeStart.value = ''
|
||||
rangeEnd.value = ''
|
||||
dateValue.value = ''
|
||||
numberValue.value = undefined
|
||||
}
|
||||
)
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<!-- 配置预览组件 -->
|
||||
<!-- TODO @puhui999:应该暂时不用预览哈 -->
|
||||
<template>
|
||||
<div class="config-preview">
|
||||
<div class="preview-items">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<!-- 执行器配置组件 -->
|
||||
<!-- todo @puhui999:参考“触发器配置”,简化下。 -->
|
||||
<template>
|
||||
<el-card class="action-section" shadow="never">
|
||||
<template #header>
|
||||
|
|
@ -35,19 +36,12 @@
|
|||
|
||||
<!-- 执行器列表 -->
|
||||
<div v-else class="actions-list">
|
||||
<div
|
||||
v-for="(action, index) in actions"
|
||||
:key="`action-${index}`"
|
||||
class="action-item"
|
||||
>
|
||||
<div v-for="(action, index) in actions" :key="`action-${index}`" class="action-item">
|
||||
<div class="action-header">
|
||||
<div class="action-title">
|
||||
<Icon icon="ep:setting" class="action-icon" />
|
||||
<span>执行器 {{ index + 1 }}</span>
|
||||
<el-tag
|
||||
:type="getActionTypeTag(action.type)"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="getActionTypeTag(action.type)" size="small">
|
||||
{{ getActionTypeName(action.type) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
|
@ -94,18 +88,11 @@
|
|||
|
||||
<!-- 添加提示 -->
|
||||
<div v-if="actions.length > 0 && actions.length < maxActions" class="add-more">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="addAction"
|
||||
class="add-more-btn"
|
||||
>
|
||||
<el-button type="primary" plain @click="addAction" class="add-more-btn">
|
||||
<Icon icon="ep:plus" />
|
||||
继续添加执行器
|
||||
</el-button>
|
||||
<span class="add-more-text">
|
||||
最多可添加 {{ maxActions }} 个执行器
|
||||
</span>
|
||||
<span class="add-more-text"> 最多可添加 {{ maxActions }} 个执行器 </span>
|
||||
</div>
|
||||
|
||||
<!-- 验证结果 -->
|
||||
|
|
@ -126,9 +113,9 @@ import { useVModel } from '@vueuse/core'
|
|||
import ActionTypeSelector from '../selectors/ActionTypeSelector.vue'
|
||||
import DeviceControlConfig from '../configs/DeviceControlConfig.vue'
|
||||
import AlertConfig from '../configs/AlertConfig.vue'
|
||||
import {
|
||||
ActionFormData,
|
||||
IotRuleSceneActionTypeEnum as ActionTypeEnum
|
||||
import {
|
||||
ActionFormData,
|
||||
IotRuleSceneActionTypeEnum as ActionTypeEnum
|
||||
} from '@/api/iot/rule/scene/scene.types'
|
||||
import { createDefaultActionData } from '../../utils/transform'
|
||||
|
||||
|
|
@ -174,17 +161,11 @@ const actionTypeTags = {
|
|||
|
||||
// 工具函数
|
||||
const isDeviceAction = (type: number) => {
|
||||
return [
|
||||
ActionTypeEnum.DEVICE_PROPERTY_SET,
|
||||
ActionTypeEnum.DEVICE_SERVICE_INVOKE
|
||||
].includes(type)
|
||||
return [ActionTypeEnum.DEVICE_PROPERTY_SET, ActionTypeEnum.DEVICE_SERVICE_INVOKE].includes(type)
|
||||
}
|
||||
|
||||
const isAlertAction = (type: number) => {
|
||||
return [
|
||||
ActionTypeEnum.ALERT_TRIGGER,
|
||||
ActionTypeEnum.ALERT_RECOVER
|
||||
].includes(type)
|
||||
return [ActionTypeEnum.ALERT_TRIGGER, ActionTypeEnum.ALERT_RECOVER].includes(type)
|
||||
}
|
||||
|
||||
const getActionTypeName = (type: number) => {
|
||||
|
|
@ -200,7 +181,7 @@ const addAction = () => {
|
|||
if (actions.value.length >= maxActions) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const newAction = createDefaultActionData()
|
||||
actions.value.push(newAction)
|
||||
}
|
||||
|
|
@ -208,10 +189,10 @@ const addAction = () => {
|
|||
const removeAction = (index: number) => {
|
||||
actions.value.splice(index, 1)
|
||||
delete actionValidations.value[index]
|
||||
|
||||
|
||||
// 重新索引验证结果
|
||||
const newValidations: { [key: number]: { valid: boolean; message: string } } = {}
|
||||
Object.keys(actionValidations.value).forEach(key => {
|
||||
Object.keys(actionValidations.value).forEach((key) => {
|
||||
const numKey = parseInt(key)
|
||||
if (numKey > index) {
|
||||
newValidations[numKey - 1] = actionValidations.value[numKey]
|
||||
|
|
@ -220,7 +201,7 @@ const removeAction = (index: number) => {
|
|||
}
|
||||
})
|
||||
actionValidations.value = newValidations
|
||||
|
||||
|
||||
updateValidationResult()
|
||||
}
|
||||
|
||||
|
|
@ -258,9 +239,9 @@ const handleActionValidate = (index: number, result: { valid: boolean; message:
|
|||
|
||||
const updateValidationResult = () => {
|
||||
const validations = Object.values(actionValidations.value)
|
||||
const allValid = validations.every(v => v.valid)
|
||||
const allValid = validations.every((v) => v.valid)
|
||||
const hasValidations = validations.length > 0
|
||||
|
||||
|
||||
if (!hasValidations) {
|
||||
isValid.value = true
|
||||
validationMessage.value = ''
|
||||
|
|
@ -269,19 +250,20 @@ const updateValidationResult = () => {
|
|||
validationMessage.value = '所有执行器配置验证通过'
|
||||
} else {
|
||||
isValid.value = false
|
||||
const errorMessages = validations
|
||||
.filter(v => !v.valid)
|
||||
.map(v => v.message)
|
||||
const errorMessages = validations.filter((v) => !v.valid).map((v) => v.message)
|
||||
validationMessage.value = `执行器配置错误: ${errorMessages.join('; ')}`
|
||||
}
|
||||
|
||||
|
||||
emit('validate', { valid: isValid.value, message: validationMessage.value })
|
||||
}
|
||||
|
||||
// 监听执行器数量变化
|
||||
watch(() => actions.value.length, () => {
|
||||
updateValidationResult()
|
||||
})
|
||||
watch(
|
||||
() => actions.value.length,
|
||||
() => {
|
||||
updateValidationResult()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@
|
|||
|
||||
<div class="section-content">
|
||||
<el-row :gutter="24">
|
||||
<!-- TODO @puhui999:NameInput、StatusRadio、DescriptionInput 是不是直接写在当前界面哈。有点散; -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="场景名称" prop="name" required>
|
||||
<NameInput v-model="formData.name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- TODO @puhui999:每个一行会好点? -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="场景状态" prop="status" required>
|
||||
<StatusRadio v-model="formData.status" />
|
||||
|
|
@ -59,6 +61,7 @@ const props = defineProps<Props>()
|
|||
const emit = defineEmits<Emits>()
|
||||
|
||||
const formData = useVModel(props, 'modelValue', emit)
|
||||
// TODO @puhui999:看看能不能 unocss
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<!-- 预览区域组件 -->
|
||||
<!-- TODO @puhui999:是不是不用这个哈? -->
|
||||
<template>
|
||||
<el-card class="preview-section" shadow="never">
|
||||
<template #header>
|
||||
|
|
@ -8,12 +9,7 @@
|
|||
<span class="section-title">配置预览</span>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleValidate"
|
||||
:loading="validating"
|
||||
>
|
||||
<el-button type="primary" size="small" @click="handleValidate" :loading="validating">
|
||||
<Icon icon="ep:check" />
|
||||
验证配置
|
||||
</el-button>
|
||||
|
|
@ -101,7 +97,7 @@ const handleValidate = async () => {
|
|||
validating.value = true
|
||||
try {
|
||||
// 延迟一下模拟验证过程
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
emit('validate')
|
||||
} finally {
|
||||
validating.value = false
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
<div class="header-left">
|
||||
<Icon icon="ep:lightning" class="section-icon" />
|
||||
<span class="section-title">触发器配置</span>
|
||||
<!-- TODO @puhui999:是不是去掉 maxTriggers;计数 -->
|
||||
<el-tag size="small" type="info">{{ triggers.length }}/{{ maxTriggers }}</el-tag>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
<!-- 空状态 -->
|
||||
<div v-if="triggers.length === 0" class="empty-state">
|
||||
<el-empty description="暂无触发器配置">
|
||||
<!-- TODO @puhui999:这个要不要去掉哈;入口统一点 -->
|
||||
<el-button type="primary" @click="addTrigger">
|
||||
<Icon icon="ep:plus" />
|
||||
添加第一个触发器
|
||||
|
|
@ -35,19 +37,12 @@
|
|||
|
||||
<!-- 触发器列表 -->
|
||||
<div v-else class="triggers-list">
|
||||
<div
|
||||
v-for="(trigger, index) in triggers"
|
||||
:key="`trigger-${index}`"
|
||||
class="trigger-item"
|
||||
>
|
||||
<div v-for="(trigger, index) in triggers" :key="`trigger-${index}`" class="trigger-item">
|
||||
<div class="trigger-header">
|
||||
<div class="trigger-title">
|
||||
<Icon icon="ep:lightning" class="trigger-icon" />
|
||||
<span>触发器 {{ index + 1 }}</span>
|
||||
<el-tag
|
||||
:type="getTriggerTypeTag(trigger.type)"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="getTriggerTypeTag(trigger.type)" size="small">
|
||||
{{ getTriggerTypeName(trigger.type) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
|
@ -93,19 +88,13 @@
|
|||
</div>
|
||||
|
||||
<!-- 添加提示 -->
|
||||
<!-- TODO @puhui999:这个要不要去掉哈;入口统一点 -->
|
||||
<div v-if="triggers.length > 0 && triggers.length < maxTriggers" class="add-more">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="addTrigger"
|
||||
class="add-more-btn"
|
||||
>
|
||||
<el-button type="primary" plain @click="addTrigger" class="add-more-btn">
|
||||
<Icon icon="ep:plus" />
|
||||
继续添加触发器
|
||||
</el-button>
|
||||
<span class="add-more-text">
|
||||
最多可添加 {{ maxTriggers }} 个触发器
|
||||
</span>
|
||||
<span class="add-more-text"> 最多可添加 {{ maxTriggers }} 个触发器 </span>
|
||||
</div>
|
||||
|
||||
<!-- 验证结果 -->
|
||||
|
|
@ -126,9 +115,9 @@ import { useVModel } from '@vueuse/core'
|
|||
import TriggerTypeSelector from '../selectors/TriggerTypeSelector.vue'
|
||||
import DeviceTriggerConfig from '../configs/DeviceTriggerConfig.vue'
|
||||
import TimerTriggerConfig from '../configs/TimerTriggerConfig.vue'
|
||||
import {
|
||||
TriggerFormData,
|
||||
IotRuleSceneTriggerTypeEnum as TriggerTypeEnum
|
||||
import {
|
||||
TriggerFormData,
|
||||
IotRuleSceneTriggerTypeEnum as TriggerTypeEnum
|
||||
} from '@/api/iot/rule/scene/scene.types'
|
||||
import { createDefaultTriggerData } from '../../utils/transform'
|
||||
|
||||
|
|
@ -197,7 +186,7 @@ const addTrigger = () => {
|
|||
if (triggers.value.length >= maxTriggers) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const newTrigger = createDefaultTriggerData()
|
||||
triggers.value.push(newTrigger)
|
||||
}
|
||||
|
|
@ -205,10 +194,10 @@ const addTrigger = () => {
|
|||
const removeTrigger = (index: number) => {
|
||||
triggers.value.splice(index, 1)
|
||||
delete triggerValidations.value[index]
|
||||
|
||||
|
||||
// 重新索引验证结果
|
||||
const newValidations: { [key: number]: { valid: boolean; message: string } } = {}
|
||||
Object.keys(triggerValidations.value).forEach(key => {
|
||||
Object.keys(triggerValidations.value).forEach((key) => {
|
||||
const numKey = parseInt(key)
|
||||
if (numKey > index) {
|
||||
newValidations[numKey - 1] = triggerValidations.value[numKey]
|
||||
|
|
@ -217,7 +206,7 @@ const removeTrigger = (index: number) => {
|
|||
}
|
||||
})
|
||||
triggerValidations.value = newValidations
|
||||
|
||||
|
||||
updateValidationResult()
|
||||
}
|
||||
|
||||
|
|
@ -263,9 +252,9 @@ const handleTriggerValidate = (index: number, result: { valid: boolean; message:
|
|||
|
||||
const updateValidationResult = () => {
|
||||
const validations = Object.values(triggerValidations.value)
|
||||
const allValid = validations.every(v => v.valid)
|
||||
const allValid = validations.every((v) => v.valid)
|
||||
const hasValidations = validations.length > 0
|
||||
|
||||
|
||||
if (!hasValidations) {
|
||||
isValid.value = true
|
||||
validationMessage.value = ''
|
||||
|
|
@ -274,19 +263,20 @@ const updateValidationResult = () => {
|
|||
validationMessage.value = '所有触发器配置验证通过'
|
||||
} else {
|
||||
isValid.value = false
|
||||
const errorMessages = validations
|
||||
.filter(v => !v.valid)
|
||||
.map(v => v.message)
|
||||
const errorMessages = validations.filter((v) => !v.valid).map((v) => v.message)
|
||||
validationMessage.value = `触发器配置错误: ${errorMessages.join('; ')}`
|
||||
}
|
||||
|
||||
|
||||
emit('validate', { valid: isValid.value, message: validationMessage.value })
|
||||
}
|
||||
|
||||
// 监听触发器数量变化
|
||||
watch(() => triggers.value.length, () => {
|
||||
updateValidationResult()
|
||||
})
|
||||
watch(
|
||||
() => triggers.value.length,
|
||||
() => {
|
||||
updateValidationResult()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<!-- 执行器类型选择组件 -->
|
||||
<template>
|
||||
<div class="action-type-selector">
|
||||
<!-- TODO @puhui999:1)设备属性设置时,貌似没选属性;2)服务调用时,貌似也没的设置哈; -->
|
||||
<el-form-item label="执行类型" required>
|
||||
<el-select
|
||||
v-model="localValue"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
</el-select>
|
||||
|
||||
<!-- 操作符说明 -->
|
||||
<!-- TODO @puhui999:这个去掉 -->
|
||||
<div v-if="selectedOperator" class="operator-description">
|
||||
<div class="desc-content">
|
||||
<Icon icon="ep:info-filled" class="desc-icon" />
|
||||
|
|
@ -155,14 +156,12 @@ const availableOperators = computed(() => {
|
|||
if (!props.propertyType) {
|
||||
return allOperators
|
||||
}
|
||||
|
||||
return allOperators.filter(op =>
|
||||
op.supportedTypes.includes(props.propertyType!)
|
||||
)
|
||||
|
||||
return allOperators.filter((op) => op.supportedTypes.includes(props.propertyType!))
|
||||
})
|
||||
|
||||
const selectedOperator = computed(() => {
|
||||
return allOperators.find(op => op.value === localValue.value)
|
||||
return allOperators.find((op) => op.value === localValue.value)
|
||||
})
|
||||
|
||||
// 事件处理
|
||||
|
|
@ -171,14 +170,17 @@ const handleChange = (value: string) => {
|
|||
}
|
||||
|
||||
// 监听属性类型变化
|
||||
watch(() => props.propertyType, () => {
|
||||
// 如果当前选择的操作符不支持新的属性类型,则清空选择
|
||||
if (localValue.value && selectedOperator.value) {
|
||||
if (!selectedOperator.value.supportedTypes.includes(props.propertyType || '')) {
|
||||
localValue.value = ''
|
||||
watch(
|
||||
() => props.propertyType,
|
||||
() => {
|
||||
// 如果当前选择的操作符不支持新的属性类型,则清空选择
|
||||
if (localValue.value && selectedOperator.value) {
|
||||
if (!selectedOperator.value.supportedTypes.includes(props.propertyType || '')) {
|
||||
localValue.value = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
<div class="option-name">{{ product.name }}</div>
|
||||
<div class="option-key">{{ product.productKey }}</div>
|
||||
</div>
|
||||
<!-- TODO @puhui999:是不是用字典 -->
|
||||
<el-tag size="small" :type="product.status === 0 ? 'success' : 'danger'">
|
||||
{{ product.status === 0 ? '正常' : '禁用' }}
|
||||
</el-tag>
|
||||
|
|
@ -38,8 +39,8 @@
|
|||
<el-col :span="12">
|
||||
<el-form-item label="设备选择模式" required>
|
||||
<el-radio-group v-model="deviceSelectionMode" @change="handleDeviceSelectionModeChange">
|
||||
<el-radio value="specific">选择设备</el-radio>
|
||||
<el-radio value="all">全部设备</el-radio>
|
||||
<el-radio value="specific">选择设备</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
|
@ -48,7 +49,9 @@
|
|||
<!-- 具体设备选择 -->
|
||||
<el-row v-if="deviceSelectionMode === 'specific'" :gutter="16">
|
||||
<el-col :span="24">
|
||||
<!-- TODO @puhui999:貌似产品选择不上; -->
|
||||
<el-form-item label="选择设备" required>
|
||||
<!-- TODO @puhui999:请先选择产品,是不是改成请选择设备?然后上面,localProductId 为空(未选择)的时候,禁用 deviceSelectionMode -->
|
||||
<el-select
|
||||
v-model="localDeviceId"
|
||||
placeholder="请先选择产品"
|
||||
|
|
@ -70,10 +73,7 @@
|
|||
<div class="option-name">{{ device.deviceName }}</div>
|
||||
<div class="option-nickname">{{ device.nickname || '无备注' }}</div>
|
||||
</div>
|
||||
<el-tag
|
||||
size="small"
|
||||
:type="getDeviceStatusTag(device.state)"
|
||||
>
|
||||
<el-tag size="small" :type="getDeviceStatusTag(device.state)">
|
||||
{{ getDeviceStatusText(device.state) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
</el-row>
|
||||
|
||||
<!-- 选择结果展示 -->
|
||||
<div v-if="localProductId && (localDeviceId !== undefined)" class="selection-result">
|
||||
<div v-if="localProductId && localDeviceId !== undefined" class="selection-result">
|
||||
<div class="result-header">
|
||||
<Icon icon="ep:check" class="result-icon" />
|
||||
<span class="result-title">已选择设备</span>
|
||||
|
|
@ -99,18 +99,8 @@
|
|||
<span class="result-label">设备:</span>
|
||||
<span v-if="deviceSelectionMode === 'all'" class="result-value">全部设备</span>
|
||||
<span v-else class="result-value">{{ selectedDevice?.deviceName }}</span>
|
||||
<el-tag
|
||||
v-if="deviceSelectionMode === 'all'"
|
||||
size="small"
|
||||
type="warning"
|
||||
>
|
||||
全部
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-else
|
||||
size="small"
|
||||
:type="getDeviceStatusTag(selectedDevice?.state)"
|
||||
>
|
||||
<el-tag v-if="deviceSelectionMode === 'all'" size="small" type="warning"> 全部 </el-tag>
|
||||
<el-tag v-else size="small" :type="getDeviceStatusTag(selectedDevice?.state)">
|
||||
{{ getDeviceStatusText(selectedDevice?.state) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
|
@ -145,7 +135,8 @@ const localProductId = useVModel(props, 'productId', emit)
|
|||
const localDeviceId = useVModel(props, 'deviceId', emit)
|
||||
|
||||
// 设备选择模式
|
||||
const deviceSelectionMode = ref<'specific' | 'all'>('specific')
|
||||
// TODO @puhui999:默认选中 all
|
||||
const deviceSelectionMode = ref<'specific' | 'all'>('all')
|
||||
|
||||
// 数据状态
|
||||
const productLoading = ref(false)
|
||||
|
|
@ -155,29 +146,38 @@ const deviceList = ref<any[]>([])
|
|||
|
||||
// 计算属性
|
||||
const selectedProduct = computed(() => {
|
||||
return productList.value.find(p => p.id === localProductId.value)
|
||||
return productList.value.find((p) => p.id === localProductId.value)
|
||||
})
|
||||
|
||||
const selectedDevice = computed(() => {
|
||||
return deviceList.value.find(d => d.id === localDeviceId.value)
|
||||
return deviceList.value.find((d) => d.id === localDeviceId.value)
|
||||
})
|
||||
|
||||
// TODO @puhui999:字典下;
|
||||
// 设备状态映射
|
||||
const getDeviceStatusText = (state?: number) => {
|
||||
switch (state) {
|
||||
case 0: return '未激活'
|
||||
case 1: return '在线'
|
||||
case 2: return '离线'
|
||||
default: return '未知'
|
||||
case 0:
|
||||
return '未激活'
|
||||
case 1:
|
||||
return '在线'
|
||||
case 2:
|
||||
return '离线'
|
||||
default:
|
||||
return '未知'
|
||||
}
|
||||
}
|
||||
|
||||
const getDeviceStatusTag = (state?: number) => {
|
||||
switch (state) {
|
||||
case 0: return 'info'
|
||||
case 1: return 'success'
|
||||
case 2: return 'danger'
|
||||
default: return 'info'
|
||||
case 0:
|
||||
return 'info'
|
||||
case 1:
|
||||
return 'success'
|
||||
case 2:
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,10 +203,10 @@ const handleDeviceSelectionModeChange = (mode: 'specific' | 'all') => {
|
|||
deviceSelectionMode.value = mode
|
||||
|
||||
if (mode === 'all') {
|
||||
// 全部设备时,设备ID设为0
|
||||
// 全部设备时,设备 ID 设为 0
|
||||
localDeviceId.value = 0
|
||||
} else {
|
||||
// 选择设备时,清空设备ID
|
||||
// 选择设备时,清空设备 ID
|
||||
localDeviceId.value = undefined
|
||||
}
|
||||
|
||||
|
|
@ -229,6 +229,7 @@ const getProductList = async () => {
|
|||
} catch (error) {
|
||||
console.error('获取产品列表失败:', error)
|
||||
// 模拟数据
|
||||
// TODO @puhui999:移除下,不太合理
|
||||
productList.value = [
|
||||
{ id: 1, name: '智能温度传感器', productKey: 'temp_sensor_001', status: 0 },
|
||||
{ id: 2, name: '智能空调控制器', productKey: 'ac_controller_001', status: 0 },
|
||||
|
|
@ -247,6 +248,7 @@ const getDeviceList = async (productId: number) => {
|
|||
} catch (error) {
|
||||
console.error('获取设备列表失败:', error)
|
||||
// 模拟数据
|
||||
// TODO @puhui999:移除下,不太合理
|
||||
deviceList.value = [
|
||||
{ id: 1, deviceName: 'sensor_001', nickname: '客厅温度传感器', state: 1, productId },
|
||||
{ id: 2, deviceName: 'sensor_002', nickname: '卧室温度传感器', state: 2, productId },
|
||||
|
|
@ -261,7 +263,7 @@ const getDeviceList = async (productId: number) => {
|
|||
onMounted(async () => {
|
||||
await getProductList()
|
||||
|
||||
// 根据初始设备ID设置选择模式
|
||||
// 根据初始设备 ID 设置选择模式
|
||||
if (localDeviceId.value === 0) {
|
||||
deviceSelectionMode.value = 'all'
|
||||
} else if (localDeviceId.value) {
|
||||
|
|
@ -274,11 +276,15 @@ onMounted(async () => {
|
|||
})
|
||||
|
||||
// 监听产品变化
|
||||
watch(() => localProductId.value, async (newProductId) => {
|
||||
if (newProductId && deviceList.value.length === 0) {
|
||||
await getDeviceList(newProductId)
|
||||
watch(
|
||||
() => localProductId.value,
|
||||
async (newProductId) => {
|
||||
if (newProductId && deviceList.value.length === 0) {
|
||||
await getDeviceList(newProductId)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
// TODO @puhui999:是不是 unocss
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<!-- 属性选择器组件 -->
|
||||
<!-- TODO @yunai:可能要在 review 下 -->
|
||||
<template>
|
||||
<div class="property-selector">
|
||||
<el-select
|
||||
|
|
@ -10,11 +11,7 @@
|
|||
class="w-full"
|
||||
:loading="loading"
|
||||
>
|
||||
<el-option-group
|
||||
v-for="group in propertyGroups"
|
||||
:key="group.label"
|
||||
:label="group.label"
|
||||
>
|
||||
<el-option-group v-for="group in propertyGroups" :key="group.label" :label="group.label">
|
||||
<el-option
|
||||
v-for="property in group.options"
|
||||
:key="property.identifier"
|
||||
|
|
@ -70,7 +67,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { IotRuleSceneTriggerTypeEnum } from '@/api/iot/rule/scene/scene.types'
|
||||
import { ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
|
||||
import { ThingModelApi } from '@/api/iot/thingmodel'
|
||||
import { IoTThingModelTypeEnum } from '@/views/iot/utils/constants'
|
||||
import type { IotThingModelTSLRespVO, PropertySelectorItem } from './types'
|
||||
|
||||
|
|
@ -106,65 +103,65 @@ const propertyGroups = computed(() => {
|
|||
if (props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST) {
|
||||
groups.push({
|
||||
label: '设备属性',
|
||||
options: propertyList.value.filter(p => p.type === IoTThingModelTypeEnum.PROPERTY)
|
||||
options: propertyList.value.filter((p) => p.type === IoTThingModelTypeEnum.PROPERTY)
|
||||
})
|
||||
}
|
||||
|
||||
if (props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST) {
|
||||
groups.push({
|
||||
label: '设备事件',
|
||||
options: propertyList.value.filter(p => p.type === IoTThingModelTypeEnum.EVENT)
|
||||
options: propertyList.value.filter((p) => p.type === IoTThingModelTypeEnum.EVENT)
|
||||
})
|
||||
}
|
||||
|
||||
if (props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE) {
|
||||
groups.push({
|
||||
label: '设备服务',
|
||||
options: propertyList.value.filter(p => p.type === IoTThingModelTypeEnum.SERVICE)
|
||||
options: propertyList.value.filter((p) => p.type === IoTThingModelTypeEnum.SERVICE)
|
||||
})
|
||||
}
|
||||
|
||||
return groups.filter(group => group.options.length > 0)
|
||||
return groups.filter((group) => group.options.length > 0)
|
||||
})
|
||||
|
||||
const selectedProperty = computed(() => {
|
||||
return propertyList.value.find(p => p.identifier === localValue.value)
|
||||
return propertyList.value.find((p) => p.identifier === localValue.value)
|
||||
})
|
||||
|
||||
// 工具函数
|
||||
const getPropertyTypeName = (dataType: string) => {
|
||||
const typeMap = {
|
||||
'int': '整数',
|
||||
'float': '浮点数',
|
||||
'double': '双精度',
|
||||
'text': '字符串',
|
||||
'bool': '布尔值',
|
||||
'enum': '枚举',
|
||||
'date': '日期',
|
||||
'struct': '结构体',
|
||||
'array': '数组'
|
||||
int: '整数',
|
||||
float: '浮点数',
|
||||
double: '双精度',
|
||||
text: '字符串',
|
||||
bool: '布尔值',
|
||||
enum: '枚举',
|
||||
date: '日期',
|
||||
struct: '结构体',
|
||||
array: '数组'
|
||||
}
|
||||
return typeMap[dataType] || dataType
|
||||
}
|
||||
|
||||
const getPropertyTypeTag = (dataType: string) => {
|
||||
const tagMap = {
|
||||
'int': 'primary',
|
||||
'float': 'success',
|
||||
'double': 'success',
|
||||
'text': 'info',
|
||||
'bool': 'warning',
|
||||
'enum': 'danger',
|
||||
'date': 'primary',
|
||||
'struct': 'info',
|
||||
'array': 'warning'
|
||||
int: 'primary',
|
||||
float: 'success',
|
||||
double: 'success',
|
||||
text: 'info',
|
||||
bool: 'warning',
|
||||
enum: 'danger',
|
||||
date: 'primary',
|
||||
struct: 'info',
|
||||
array: 'warning'
|
||||
}
|
||||
return tagMap[dataType] || 'info'
|
||||
}
|
||||
|
||||
// 事件处理
|
||||
const handleChange = (value: string) => {
|
||||
const property = propertyList.value.find(p => p.identifier === value)
|
||||
const property = propertyList.value.find((p) => p.identifier === value)
|
||||
if (property) {
|
||||
emit('change', {
|
||||
type: property.dataType,
|
||||
|
|
@ -321,14 +318,21 @@ const getDataRange = (dataSpecs: any) => {
|
|||
}
|
||||
|
||||
// 监听产品变化
|
||||
watch(() => props.productId, () => {
|
||||
getThingModelTSL()
|
||||
}, { immediate: true })
|
||||
watch(
|
||||
() => props.productId,
|
||||
() => {
|
||||
getThingModelTSL()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// 监听触发类型变化
|
||||
watch(() => props.triggerType, () => {
|
||||
localValue.value = ''
|
||||
})
|
||||
watch(
|
||||
() => props.triggerType,
|
||||
() => {
|
||||
localValue.value = ''
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -16,12 +16,14 @@
|
|||
>
|
||||
<div class="trigger-option">
|
||||
<div class="option-content">
|
||||
<!-- TODO @puhui999:貌似没对齐? -->
|
||||
<Icon :icon="option.icon" class="option-icon" />
|
||||
<div class="option-info">
|
||||
<div class="option-label">{{ option.label }}</div>
|
||||
<div class="option-desc">{{ option.description }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO @puhui999:这个要不去掉? -->
|
||||
<el-tag :type="option.tag" size="small">
|
||||
{{ option.category }}
|
||||
</el-tag>
|
||||
|
|
@ -31,6 +33,7 @@
|
|||
</el-form-item>
|
||||
|
||||
<!-- 类型说明 -->
|
||||
<!-- TODO @puhui999:这个去掉。感觉没啥内容哈; -->
|
||||
<div v-if="selectedOption" class="type-description">
|
||||
<div class="desc-header">
|
||||
<Icon :icon="selectedOption.icon" class="desc-icon" />
|
||||
|
|
@ -39,11 +42,7 @@
|
|||
<div class="desc-content">
|
||||
<p class="desc-text">{{ selectedOption.description }}</p>
|
||||
<div class="desc-features">
|
||||
<div
|
||||
v-for="feature in selectedOption.features"
|
||||
:key="feature"
|
||||
class="feature-item"
|
||||
>
|
||||
<div v-for="feature in selectedOption.features" :key="feature" class="feature-item">
|
||||
<Icon icon="ep:check" class="feature-icon" />
|
||||
<span class="feature-text">{{ feature }}</span>
|
||||
</div>
|
||||
|
|
@ -83,11 +82,7 @@ const triggerTypeOptions = [
|
|||
icon: 'ep:connection',
|
||||
tag: 'warning',
|
||||
category: '设备状态',
|
||||
features: [
|
||||
'监控设备连接状态',
|
||||
'实时响应设备变化',
|
||||
'无需配置额外条件'
|
||||
]
|
||||
features: ['监控设备连接状态', '实时响应设备变化', '无需配置额外条件']
|
||||
},
|
||||
{
|
||||
value: IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST,
|
||||
|
|
@ -96,11 +91,7 @@ const triggerTypeOptions = [
|
|||
icon: 'ep:data-line',
|
||||
tag: 'primary',
|
||||
category: '数据监控',
|
||||
features: [
|
||||
'监控设备属性变化',
|
||||
'支持多种比较条件',
|
||||
'可配置阈值范围'
|
||||
]
|
||||
features: ['监控设备属性变化', '支持多种比较条件', '可配置阈值范围']
|
||||
},
|
||||
{
|
||||
value: IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST,
|
||||
|
|
@ -109,11 +100,7 @@ const triggerTypeOptions = [
|
|||
icon: 'ep:bell',
|
||||
tag: 'success',
|
||||
category: '事件监控',
|
||||
features: [
|
||||
'监控设备事件',
|
||||
'支持事件参数过滤',
|
||||
'实时事件响应'
|
||||
]
|
||||
features: ['监控设备事件', '支持事件参数过滤', '实时事件响应']
|
||||
},
|
||||
{
|
||||
value: IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE,
|
||||
|
|
@ -122,11 +109,7 @@ const triggerTypeOptions = [
|
|||
icon: 'ep:service',
|
||||
tag: 'info',
|
||||
category: '服务监控',
|
||||
features: [
|
||||
'监控服务调用',
|
||||
'支持参数条件',
|
||||
'服务执行跟踪'
|
||||
]
|
||||
features: ['监控服务调用', '支持参数条件', '服务执行跟踪']
|
||||
},
|
||||
{
|
||||
value: IotRuleSceneTriggerTypeEnum.TIMER,
|
||||
|
|
@ -135,17 +118,13 @@ const triggerTypeOptions = [
|
|||
icon: 'ep:timer',
|
||||
tag: 'danger',
|
||||
category: '定时任务',
|
||||
features: [
|
||||
'支持CRON表达式',
|
||||
'灵活的时间配置',
|
||||
'可视化时间设置'
|
||||
]
|
||||
features: ['支持CRON表达式', '灵活的时间配置', '可视化时间设置']
|
||||
}
|
||||
]
|
||||
|
||||
// 计算属性
|
||||
const selectedOption = computed(() => {
|
||||
return triggerTypeOptions.find(option => option.value === localValue.value)
|
||||
return triggerTypeOptions.find((option) => option.value === localValue.value)
|
||||
})
|
||||
|
||||
// 事件处理
|
||||
|
|
@ -155,6 +134,7 @@ const handleChange = (value: number) => {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/** TODO @puhui999:unocss 哈 */
|
||||
.trigger-type-selector {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
<!-- 改进的场景联动规则管理页面 -->
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 页面头部 -->
|
||||
|
|
@ -186,6 +185,7 @@
|
|||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<!-- TODO @puhui999:间隙大了点 -->
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary" link @click="handleEdit(row)">
|
||||
<Icon icon="ep:edit" />
|
||||
|
|
@ -197,6 +197,7 @@
|
|||
@click="handleToggleStatus(row)"
|
||||
>
|
||||
<Icon :icon="row.status === 0 ? 'ep:video-pause' : 'ep:video-play'" />
|
||||
<!-- TODO @puhui999:翻译,字典 -->
|
||||
{{ row.status === 0 ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button type="danger" link @click="handleDelete(row)">
|
||||
|
|
@ -241,7 +242,7 @@
|
|||
</div>
|
||||
|
||||
<!-- 表单对话框 -->
|
||||
<RuleSceneForm v-model="formVisible" :rule-scene="currentRule" @success="handleFormSuccess" />
|
||||
<RuleSceneForm v-model="formVisible" :rule-scene="currentRule" @success="getList" />
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
|
|
@ -252,11 +253,11 @@ import { IotRuleScene } from '@/api/iot/rule/scene/scene.types'
|
|||
import { getRuleSceneSummary } from './utils/transform'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
/** 改进的场景联动规则管理页面 */
|
||||
defineOptions({ name: 'ImprovedRuleSceneIndex' })
|
||||
/** 场景联动规则管理页面 */
|
||||
defineOptions({ name: 'IoTSceneRule' })
|
||||
|
||||
const message = useMessage()
|
||||
// const { t } = useI18n()
|
||||
// const { t } = useI18n() // TODO @puhui999:可以删除
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
|
|
@ -267,6 +268,7 @@ const queryParams = reactive({
|
|||
})
|
||||
|
||||
// 数据状态
|
||||
// TODO @puhui999:变量名,和别的页面保持一致哈
|
||||
const loading = ref(true)
|
||||
const list = ref<IotRuleScene[]>([])
|
||||
const total = ref(0)
|
||||
|
|
@ -285,6 +287,7 @@ const statistics = ref({
|
|||
})
|
||||
|
||||
// 获取列表数据
|
||||
// TODO @puhui999:接入
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
|
|
@ -352,6 +355,8 @@ const getList = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO @puhui999:方法注释,使用 /** */ 风格
|
||||
|
||||
// 更新统计数据
|
||||
const updateStatistics = () => {
|
||||
statistics.value = {
|
||||
|
|
@ -384,6 +389,7 @@ const resetQuery = () => {
|
|||
handleQuery()
|
||||
}
|
||||
|
||||
// TODO @puhui999:这个要不还是使用 open 方式,只是弹出的右侧;
|
||||
const handleAdd = () => {
|
||||
currentRule.value = undefined
|
||||
formVisible.value = true
|
||||
|
|
@ -394,6 +400,7 @@ const handleEdit = (row: IotRuleScene) => {
|
|||
formVisible.value = true
|
||||
}
|
||||
|
||||
// TODO @puhui999:handleDelete、handleToggleStatus 保持和别的模块一致哇?
|
||||
const handleDelete = async (row: IotRuleScene) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除这个规则吗?', '提示', {
|
||||
|
|
@ -481,10 +488,6 @@ const handleBatchDelete = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
const handleFormSuccess = () => {
|
||||
getList()
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getList()
|
||||
|
|
@ -492,6 +495,7 @@ onMounted(() => {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/** TODO @puhui999:看看下面的,是不是可以用 unocss 替代 */
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
|
|||
Loading…
Reference in New Issue