review:【IoT 物联网】场景联动的部分 review

pull/794/MERGE
YunaiV 2025-07-18 23:55:51 +08:00
parent a72c297e86
commit bbd38e026b
23 changed files with 383 additions and 397 deletions

View File

@ -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 () => {

View File

@ -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(() => {

View File

@ -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(() => {

View File

@ -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(() => {

View File

@ -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>

View File

@ -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 @puhui999unocss
</script>
<style scoped>

View File

@ -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>

View File

@ -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'

View File

@ -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(() => {

View File

@ -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
}
}

View File

@ -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

View File

@ -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(() => {

View File

@ -1,4 +1,5 @@
<!-- 配置预览组件 -->
<!-- TODO @puhui999应该暂时不用预览哈 -->
<template>
<div class="config-preview">
<div class="preview-items">

View File

@ -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>

View File

@ -17,11 +17,13 @@
<div class="section-content">
<el-row :gutter="24">
<!-- TODO @puhui999NameInputStatusRadioDescriptionInput 是不是直接写在当前界面哈有点散 -->
<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>

View File

@ -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

View File

@ -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>

View File

@ -1,6 +1,7 @@
<!-- 执行器类型选择组件 -->
<template>
<div class="action-type-selector">
<!-- TODO @puhui9991设备属性设置时貌似没选属性2服务调用时貌似也没的设置哈 -->
<el-form-item label="执行类型" required>
<el-select
v-model="localValue"

View File

@ -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>

View File

@ -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') {
// ID0
// 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>

View File

@ -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>

View File

@ -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 @puhui999unocss 哈 */
.trigger-type-selector {
width: 100%;
}

View File

@ -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 @puhui999handleDeletehandleToggleStatus
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;