review:【iot 物联网】场景联动的逻辑
parent
c83a2dff93
commit
a5478ffcfe
|
|
@ -105,9 +105,7 @@ export const CRON_PRESETS = {
|
||||||
|
|
||||||
/** CRON 表达式工具类 */
|
/** CRON 表达式工具类 */
|
||||||
export class CronUtils {
|
export class CronUtils {
|
||||||
/**
|
/** 验证 CRON 表达式格式 */
|
||||||
* 验证 CRON 表达式格式
|
|
||||||
*/
|
|
||||||
static validate(cronExpression: string): boolean {
|
static validate(cronExpression: string): boolean {
|
||||||
if (!cronExpression || typeof cronExpression !== 'string') {
|
if (!cronExpression || typeof cronExpression !== 'string') {
|
||||||
return false
|
return false
|
||||||
|
|
@ -125,9 +123,7 @@ export class CronUtils {
|
||||||
return parts.every((part) => cronRegex.test(part))
|
return parts.every((part) => cronRegex.test(part))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 解析单个 CRON 字段 */
|
||||||
* 解析单个 CRON 字段
|
|
||||||
*/
|
|
||||||
static parseField(
|
static parseField(
|
||||||
fieldValue: string,
|
fieldValue: string,
|
||||||
fieldType: CronFieldType,
|
fieldType: CronFieldType,
|
||||||
|
|
@ -206,9 +202,7 @@ export class CronUtils {
|
||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 解析完整的 CRON 表达式 */
|
||||||
* 解析完整的 CRON 表达式
|
|
||||||
*/
|
|
||||||
static parse(cronExpression: string): ParsedCronExpression {
|
static parse(cronExpression: string): ParsedCronExpression {
|
||||||
const result: ParsedCronExpression = {
|
const result: ParsedCronExpression = {
|
||||||
second: { type: 'any', values: [], original: '*', description: '每秒' },
|
second: { type: 'any', values: [], original: '*', description: '每秒' },
|
||||||
|
|
@ -259,9 +253,7 @@ export class CronUtils {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 生成 CRON 表达式的可读描述 */
|
||||||
* 生成 CRON 表达式的可读描述
|
|
||||||
*/
|
|
||||||
static generateDescription(parsed: ParsedCronExpression): string {
|
static generateDescription(parsed: ParsedCronExpression): string {
|
||||||
const parts: string[] = []
|
const parts: string[] = []
|
||||||
|
|
||||||
|
|
@ -305,9 +297,7 @@ export class CronUtils {
|
||||||
return parts.length > 0 ? parts.join(' ') : '自定义时间规则'
|
return parts.length > 0 ? parts.join(' ') : '自定义时间规则'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 格式化 CRON 表达式为可读文本 */
|
||||||
* 格式化 CRON 表达式为可读文本
|
|
||||||
*/
|
|
||||||
static format(cronExpression: string): string {
|
static format(cronExpression: string): string {
|
||||||
if (!cronExpression) return ''
|
if (!cronExpression) return ''
|
||||||
|
|
||||||
|
|
@ -315,9 +305,7 @@ export class CronUtils {
|
||||||
return parsed.isValid ? parsed.description : cronExpression
|
return parsed.isValid ? parsed.description : cronExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 获取预设的 CRON 表达式列表 */
|
||||||
* 获取预设的 CRON 表达式列表
|
|
||||||
*/
|
|
||||||
static getPresets() {
|
static getPresets() {
|
||||||
return Object.entries(CRON_PRESETS).map(([key, value]) => ({
|
return Object.entries(CRON_PRESETS).map(([key, value]) => ({
|
||||||
label: this.format(value),
|
label: this.format(value),
|
||||||
|
|
@ -326,9 +314,7 @@ export class CronUtils {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 计算 CRON 表达式的下次执行时间 */
|
||||||
* 计算 CRON 表达式的下次执行时间
|
|
||||||
*/
|
|
||||||
static getNextExecutionTime(cronExpression: string, fromDate?: Date): Date | null {
|
static getNextExecutionTime(cronExpression: string, fromDate?: Date): Date | null {
|
||||||
const parsed = this.parse(cronExpression)
|
const parsed = this.parse(cronExpression)
|
||||||
if (!parsed.isValid) {
|
if (!parsed.isValid) {
|
||||||
|
|
@ -402,9 +388,7 @@ export class CronUtils {
|
||||||
return new Date(now.getTime() + 60000) // 1分钟后
|
return new Date(now.getTime() + 60000) // 1分钟后
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 获取 CRON 表达式的执行频率描述 */
|
||||||
* 获取 CRON 表达式的执行频率描述
|
|
||||||
*/
|
|
||||||
static getFrequencyDescription(cronExpression: string): string {
|
static getFrequencyDescription(cronExpression: string): string {
|
||||||
const parsed = this.parse(cronExpression)
|
const parsed = this.parse(cronExpression)
|
||||||
if (!parsed.isValid) {
|
if (!parsed.isValid) {
|
||||||
|
|
@ -435,9 +419,7 @@ export class CronUtils {
|
||||||
return '按计划执行'
|
return '按计划执行'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 检查 CRON 表达式是否会在指定时间执行 */
|
||||||
* 检查 CRON 表达式是否会在指定时间执行
|
|
||||||
*/
|
|
||||||
static willExecuteAt(cronExpression: string, targetDate: Date): boolean {
|
static willExecuteAt(cronExpression: string, targetDate: Date): boolean {
|
||||||
const parsed = this.parse(cronExpression)
|
const parsed = this.parse(cronExpression)
|
||||||
if (!parsed.isValid) {
|
if (!parsed.isValid) {
|
||||||
|
|
@ -462,9 +444,7 @@ export class CronUtils {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 检查字段值是否匹配 */
|
||||||
* 检查字段值是否匹配
|
|
||||||
*/
|
|
||||||
private static fieldMatches(field: ParsedCronField, value: number): boolean {
|
private static fieldMatches(field: ParsedCronField, value: number): boolean {
|
||||||
if (field.type === 'any') {
|
if (field.type === 'any') {
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import RuleIndex from './rule/index.vue'
|
||||||
import SinkIndex from './sink/index.vue'
|
import SinkIndex from './sink/index.vue'
|
||||||
|
|
||||||
/** IoT 数据流转 */
|
/** IoT 数据流转 */
|
||||||
defineOptions({ name: 'IotData' })
|
defineOptions({ name: 'IoTDataRule' })
|
||||||
|
|
||||||
const activeTab = ref('rule')
|
const activeTab = ref('rule')
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,6 @@ const validateActions = (_rule: any, value: any, callback: any) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const formRules = reactive({
|
const formRules = reactive({
|
||||||
// 表单校验规则
|
|
||||||
name: [
|
name: [
|
||||||
{ required: true, message: '场景名称不能为空', trigger: 'blur' },
|
{ required: true, message: '场景名称不能为空', trigger: 'blur' },
|
||||||
{ type: 'string', min: 1, max: 50, message: '场景名称长度应在1-50个字符之间', trigger: 'blur' }
|
{ type: 'string', min: 1, max: 50, message: '场景名称长度应在1-50个字符之间', trigger: 'blur' }
|
||||||
|
|
@ -234,17 +233,13 @@ const formRules = reactive({
|
||||||
],
|
],
|
||||||
triggers: [{ required: true, validator: validateTriggers, trigger: 'change' }],
|
triggers: [{ required: true, validator: validateTriggers, trigger: 'change' }],
|
||||||
actions: [{ required: true, validator: validateActions, trigger: 'change' }]
|
actions: [{ required: true, validator: validateActions, trigger: 'change' }]
|
||||||
})
|
}) // 表单校验规则
|
||||||
|
|
||||||
const submitLoading = ref(false) // 提交加载状态
|
const submitLoading = ref(false) // 提交加载状态
|
||||||
const isEdit = ref(false) // 是否为编辑模式
|
const isEdit = ref(false) // 是否为编辑模式
|
||||||
|
const drawerTitle = computed(() => (isEdit.value ? '编辑场景联动规则' : '新增场景联动规则')) // 抽屉标题
|
||||||
|
|
||||||
// 计算属性:抽屉标题
|
/** 提交表单 */
|
||||||
const drawerTitle = computed(() => (isEdit.value ? '编辑场景联动规则' : '新增场景联动规则'))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 提交表单
|
|
||||||
*/
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
// 校验表单
|
// 校验表单
|
||||||
if (!formRef.value) return
|
if (!formRef.value) return
|
||||||
|
|
@ -275,16 +270,12 @@ const handleSubmit = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 处理抽屉关闭事件 */
|
||||||
* 处理抽屉关闭事件
|
|
||||||
*/
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
drawerVisible.value = false
|
drawerVisible.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 初始化表单数据 */
|
||||||
* 初始化表单数据
|
|
||||||
*/
|
|
||||||
const initFormData = () => {
|
const initFormData = () => {
|
||||||
if (props.ruleScene) {
|
if (props.ruleScene) {
|
||||||
// 编辑模式:数据结构已对齐,直接使用后端数据
|
// 编辑模式:数据结构已对齐,直接使用后端数据
|
||||||
|
|
@ -316,7 +307,7 @@ const initFormData = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听抽屉显示
|
/** 监听抽屉显示 */
|
||||||
watch(drawerVisible, async (visible) => {
|
watch(drawerVisible, async (visible) => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
initFormData()
|
initFormData()
|
||||||
|
|
@ -326,7 +317,7 @@ watch(drawerVisible, async (visible) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听编辑数据变化
|
/** 监听编辑数据变化 */
|
||||||
watch(
|
watch(
|
||||||
() => props.ruleScene,
|
() => props.ruleScene,
|
||||||
() => {
|
() => {
|
||||||
|
|
|
||||||
|
|
@ -183,14 +183,12 @@ const condition = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
const propertyType = ref<string>('string') // 属性类型
|
const propertyType = ref<string>('string') // 属性类型
|
||||||
const propertyConfig = ref<any>(null) // 属性配置
|
const propertyConfig = ref<any>(null) // 属性配置
|
||||||
|
|
||||||
// 计算属性:判断是否为设备相关条件
|
|
||||||
const isDeviceCondition = computed(() => {
|
const isDeviceCondition = computed(() => {
|
||||||
return (
|
return (
|
||||||
condition.value.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS ||
|
condition.value.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS ||
|
||||||
condition.value.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
|
condition.value.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
|
||||||
)
|
)
|
||||||
})
|
}) // 计算属性:判断是否为设备相关条件
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新条件字段
|
* 更新条件字段
|
||||||
|
|
@ -240,20 +238,14 @@ const handleConditionTypeChange = (type: number) => {
|
||||||
condition.value.param = ''
|
condition.value.param = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 处理产品变化事件 */
|
||||||
* 处理产品变化事件
|
|
||||||
* @param _ 产品ID(未使用)
|
|
||||||
*/
|
|
||||||
const handleProductChange = (_: number) => {
|
const handleProductChange = (_: number) => {
|
||||||
// 产品变化时清空设备和属性
|
// 产品变化时清空设备和属性
|
||||||
condition.value.deviceId = undefined
|
condition.value.deviceId = undefined
|
||||||
condition.value.identifier = ''
|
condition.value.identifier = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 处理设备变化事件 */
|
||||||
* 处理设备变化事件
|
|
||||||
* @param _ 设备ID(未使用)
|
|
||||||
*/
|
|
||||||
const handleDeviceChange = (_: number) => {
|
const handleDeviceChange = (_: number) => {
|
||||||
// 设备变化时清空属性
|
// 设备变化时清空属性
|
||||||
condition.value.identifier = ''
|
condition.value.identifier = ''
|
||||||
|
|
@ -268,13 +260,11 @@ const handlePropertyChange = (propertyInfo: { type: string; config: any }) => {
|
||||||
propertyConfig.value = propertyInfo.config
|
propertyConfig.value = propertyInfo.config
|
||||||
|
|
||||||
// 重置操作符和值
|
// 重置操作符和值
|
||||||
condition.value.operator = '='
|
condition.value.operator = IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.value
|
||||||
condition.value.param = ''
|
condition.value.param = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 处理操作符变化事件 */
|
||||||
* 处理操作符变化事件
|
|
||||||
*/
|
|
||||||
const handleOperatorChange = () => {
|
const handleOperatorChange = () => {
|
||||||
// 重置值
|
// 重置值
|
||||||
condition.value.param = ''
|
condition.value.param = ''
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,6 @@
|
||||||
<!-- 当前时间条件配置组件 -->
|
<!-- 当前时间条件配置组件 -->
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-16px">
|
<div class="flex flex-col gap-16px">
|
||||||
<div
|
|
||||||
class="flex items-center gap-8px p-12px px-16px bg-orange-50 rounded-6px border border-orange-200"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:timer" class="text-orange-500 text-18px" />
|
|
||||||
<span class="text-14px font-500 text-orange-700">当前时间条件配置</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<!-- 时间操作符选择 -->
|
<!-- 时间操作符选择 -->
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
|
|
@ -224,7 +217,7 @@ const handleTimeValue2Change = (value: string) => {
|
||||||
condition.value.param = currentParams.slice(0, 2).join(',')
|
condition.value.param = currentParams.slice(0, 2).join(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听操作符变化,清理不相关的时间值
|
/** 监听操作符变化,清理不相关的时间值 */
|
||||||
watch(
|
watch(
|
||||||
() => condition.value.operator,
|
() => condition.value.operator,
|
||||||
(newOperator) => {
|
(newOperator) => {
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ const isServiceInvokeAction = computed(() => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理产品变化事件
|
* 处理产品变化事件
|
||||||
* @param productId 产品ID
|
* @param productId 产品 ID
|
||||||
*/
|
*/
|
||||||
const handleProductChange = (productId?: number) => {
|
const handleProductChange = (productId?: number) => {
|
||||||
// 当产品变化时,清空设备选择和参数配置
|
// 当产品变化时,清空设备选择和参数配置
|
||||||
|
|
@ -160,7 +160,7 @@ const handleProductChange = (productId?: number) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理设备变化事件
|
* 处理设备变化事件
|
||||||
* @param deviceId 设备ID
|
* @param deviceId 设备 ID
|
||||||
*/
|
*/
|
||||||
const handleDeviceChange = (deviceId?: number) => {
|
const handleDeviceChange = (deviceId?: number) => {
|
||||||
// 当设备变化时,清空参数配置
|
// 当设备变化时,清空参数配置
|
||||||
|
|
@ -338,14 +338,12 @@ const initializeComponent = async () => {
|
||||||
isInitialized.value = true
|
isInitialized.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 组件初始化 */
|
||||||
* 组件初始化
|
|
||||||
*/
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initializeComponent()
|
initializeComponent()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听关键字段的变化,避免深度监听导致的性能问题
|
/** 监听关键字段的变化,避免深度监听导致的性能问题 */
|
||||||
watch(
|
watch(
|
||||||
() => [action.value.productId, action.value.type, action.value.identifier],
|
() => [action.value.productId, action.value.type, action.value.identifier],
|
||||||
async ([newProductId, , newIdentifier], [oldProductId, , oldIdentifier]) => {
|
async ([newProductId, , newIdentifier], [oldProductId, , oldIdentifier]) => {
|
||||||
|
|
|
||||||
|
|
@ -205,9 +205,7 @@ const handleTriggerTypeChange = (type: number) => {
|
||||||
emit('trigger-type-change', type)
|
emit('trigger-type-change', type)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 添加子条件组 */
|
||||||
* 添加子条件组
|
|
||||||
*/
|
|
||||||
const addSubGroup = async () => {
|
const addSubGroup = async () => {
|
||||||
if (!trigger.value.conditionGroups) {
|
if (!trigger.value.conditionGroups) {
|
||||||
trigger.value.conditionGroups = []
|
trigger.value.conditionGroups = []
|
||||||
|
|
@ -246,9 +244,7 @@ const updateSubGroup = (index: number, subGroup: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 移除整个条件组 */
|
||||||
* 移除整个条件组
|
|
||||||
*/
|
|
||||||
const removeConditionGroup = () => {
|
const removeConditionGroup = () => {
|
||||||
trigger.value.conditionGroups = undefined
|
trigger.value.conditionGroups = undefined
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -295,18 +295,14 @@ const handleTriggerTypeChange = (type: number) => {
|
||||||
emit('trigger-type-change', type)
|
emit('trigger-type-change', type)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 处理产品变化事件 */
|
||||||
* 处理产品变化事件
|
|
||||||
*/
|
|
||||||
const handleProductChange = () => {
|
const handleProductChange = () => {
|
||||||
// 产品变化时清空设备和属性
|
// 产品变化时清空设备和属性
|
||||||
condition.value.deviceId = undefined
|
condition.value.deviceId = undefined
|
||||||
condition.value.identifier = ''
|
condition.value.identifier = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 处理设备变化事件 */
|
||||||
* 处理设备变化事件
|
|
||||||
*/
|
|
||||||
const handleDeviceChange = () => {
|
const handleDeviceChange = () => {
|
||||||
// 设备变化时清空属性
|
// 设备变化时清空属性
|
||||||
condition.value.identifier = ''
|
condition.value.identifier = ''
|
||||||
|
|
@ -331,6 +327,7 @@ const handlePropertyChange = (propertyInfo: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:这个貌似没用上
|
||||||
/**
|
/**
|
||||||
* 处理操作符变化事件
|
* 处理操作符变化事件
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -105,9 +105,7 @@ const subGroup = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
const maxConditions = computed(() => props.maxConditions || 3) // 最大条件数量
|
const maxConditions = computed(() => props.maxConditions || 3) // 最大条件数量
|
||||||
|
|
||||||
/**
|
/** 添加条件 */
|
||||||
* 添加条件
|
|
||||||
*/
|
|
||||||
const addCondition = async () => {
|
const addCondition = async () => {
|
||||||
// 确保 subGroup.value 是一个数组
|
// 确保 subGroup.value 是一个数组
|
||||||
if (!subGroup.value) {
|
if (!subGroup.value) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<!-- 执行器配置组件 -->
|
<!-- 执行器配置组件 -->
|
||||||
|
<!-- TODO @puhui999:每个执行器的 UI 风格,应该和【触发器配置】那,有点像 -->
|
||||||
<template>
|
<template>
|
||||||
<el-card class="border border-[var(--el-border-color-light)] rounded-8px" shadow="never">
|
<el-card class="border border-[var(--el-border-color-light)] rounded-8px" shadow="never">
|
||||||
<template #header>
|
<template #header>
|
||||||
|
|
|
||||||
|
|
@ -137,9 +137,7 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const triggers = useVModel(props, 'triggers', emit)
|
const triggers = useVModel(props, 'triggers', emit)
|
||||||
|
|
||||||
/**
|
/** 添加触发器 */
|
||||||
* 添加触发器
|
|
||||||
*/
|
|
||||||
const addTrigger = () => {
|
const addTrigger = () => {
|
||||||
const newTrigger: Trigger = {
|
const newTrigger: Trigger = {
|
||||||
type: TriggerTypeEnum.DEVICE_STATE_UPDATE,
|
type: TriggerTypeEnum.DEVICE_STATE_UPDATE,
|
||||||
|
|
@ -184,9 +182,9 @@ const updateTriggerDeviceConfig = (index: number, newTrigger: Trigger) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新触发器CRON配置
|
* 更新触发器 CRON 配置
|
||||||
* @param index 触发器索引
|
* @param index 触发器索引
|
||||||
* @param cronExpression CRON表达式
|
* @param cronExpression CRON 表达式
|
||||||
*/
|
*/
|
||||||
const updateTriggerCronConfig = (index: number, cronExpression?: string) => {
|
const updateTriggerCronConfig = (index: number, cronExpression?: string) => {
|
||||||
triggers.value[index].cronExpression = cronExpression
|
triggers.value[index].cronExpression = cronExpression
|
||||||
|
|
@ -208,7 +206,7 @@ const onTriggerTypeChange = (index: number, _: number) => {
|
||||||
triggerItem.conditionGroups = []
|
triggerItem.conditionGroups = []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化:确保至少有一个触发器
|
/** 初始化:确保至少有一个触发器 */
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (triggers.value.length === 0) {
|
if (triggers.value.length === 0) {
|
||||||
addTrigger()
|
addTrigger()
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between w-full py-4px">
|
<div class="flex items-center justify-between w-full py-4px">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px"
|
<div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px">
|
||||||
>{{ device.deviceName }}
|
{{ device.deviceName }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-12px text-[var(--el-text-color-secondary)]">{{ device.deviceKey }}</div>
|
<div class="text-12px text-[var(--el-text-color-secondary)]">{{ device.deviceKey }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-4px">
|
<div class="flex items-center gap-4px" v-if="device.id > 0">
|
||||||
<el-tag size="small" :type="getDeviceEnableStatusTagType(device.status)">
|
<el-tag size="small" :type="getDeviceEnableStatusTagType(device.status)">
|
||||||
{{ getDeviceEnableStatusText(device.status) }}
|
{{ getDeviceEnableStatusText(device.status) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
|
|
@ -87,7 +87,7 @@ const getDeviceList = async () => {
|
||||||
console.error('获取设备列表失败:', error)
|
console.error('获取设备列表失败:', error)
|
||||||
deviceList.value = []
|
deviceList.value = []
|
||||||
} finally {
|
} finally {
|
||||||
deviceList.value.push(DEVICE_SELECTOR_OPTIONS.ALL_DEVICES)
|
deviceList.value.unshift(DEVICE_SELECTOR_OPTIONS.ALL_DEVICES)
|
||||||
deviceLoading.value = false
|
deviceLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,6 @@ const availableOperators = computed(() => {
|
||||||
if (!props.propertyType) {
|
if (!props.propertyType) {
|
||||||
return allOperators
|
return allOperators
|
||||||
}
|
}
|
||||||
|
|
||||||
return allOperators.filter((op) => op.supportedTypes.includes(props.propertyType!))
|
return allOperators.filter((op) => op.supportedTypes.includes(props.propertyType!))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -239,7 +238,7 @@ const handleChange = (value: string) => {
|
||||||
emit('change', value)
|
emit('change', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听属性类型变化
|
/** 监听属性类型变化 */
|
||||||
watch(
|
watch(
|
||||||
() => props.propertyType,
|
() => props.propertyType,
|
||||||
() => {
|
() => {
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between w-full py-4px">
|
<div class="flex items-center justify-between w-full py-4px">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px"
|
<div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px">
|
||||||
>{{ product.name }}
|
{{ product.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-12px text-[var(--el-text-color-secondary)]"
|
<div class="text-12px text-[var(--el-text-color-secondary)]">
|
||||||
>{{ product.productKey }}
|
{{ product.productKey }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="product.status" />
|
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="product.status" />
|
||||||
|
|
@ -51,16 +51,14 @@ const productList = ref<any[]>([]) // 产品列表
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理选择变化事件
|
* 处理选择变化事件
|
||||||
* @param value 选中的产品ID
|
* @param value 选中的产品 ID
|
||||||
*/
|
*/
|
||||||
const handleChange = (value?: number) => {
|
const handleChange = (value?: number) => {
|
||||||
emit('update:modelValue', value)
|
emit('update:modelValue', value)
|
||||||
emit('change', value)
|
emit('change', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 获取产品列表 */
|
||||||
* 获取产品列表
|
|
||||||
*/
|
|
||||||
const getProductList = async () => {
|
const getProductList = async () => {
|
||||||
try {
|
try {
|
||||||
productLoading.value = true
|
productLoading.value = true
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
<!-- 属性选择器组件 -->
|
<!-- 属性选择器组件 -->
|
||||||
<!-- TODO @yunai:可能要在 review 下 -->
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex items-center gap-8px">
|
<div class="flex items-center gap-8px">
|
||||||
<el-select
|
<el-select
|
||||||
|
|
@ -263,6 +262,7 @@ const handleChange = (value: string) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:这里没用到哈;
|
||||||
/**
|
/**
|
||||||
* 获取物模型TSL数据
|
* 获取物模型TSL数据
|
||||||
*/
|
*/
|
||||||
|
|
@ -292,13 +292,12 @@ const getThingModelTSL = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 解析物模型 TSL 数据 */
|
||||||
* 解析物模型TSL数据
|
|
||||||
*/
|
|
||||||
const parseThingModelData = () => {
|
const parseThingModelData = () => {
|
||||||
const tsl = thingModelTSL.value
|
const tsl = thingModelTSL.value
|
||||||
const properties: PropertySelectorItem[] = []
|
const properties: PropertySelectorItem[] = []
|
||||||
|
|
||||||
|
// TODO @puhui999:if return,减少下括号层级;
|
||||||
if (tsl) {
|
if (tsl) {
|
||||||
// 解析属性
|
// 解析属性
|
||||||
if (tsl.properties && Array.isArray(tsl.properties)) {
|
if (tsl.properties && Array.isArray(tsl.properties)) {
|
||||||
|
|
@ -397,7 +396,7 @@ const getPropertyRange = (property: any) => {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听产品变化
|
/** 监听产品变化 *、
|
||||||
watch(
|
watch(
|
||||||
() => props.productId,
|
() => props.productId,
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -406,7 +405,7 @@ watch(
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
// 监听触发类型变化
|
/** 监听触发类型变化 */
|
||||||
watch(
|
watch(
|
||||||
() => props.triggerType,
|
() => props.triggerType,
|
||||||
() => {
|
() => {
|
||||||
|
|
|
||||||
|
|
@ -225,14 +225,7 @@
|
||||||
@click="handleToggleStatus(row)"
|
@click="handleToggleStatus(row)"
|
||||||
>
|
>
|
||||||
<Icon :icon="row.status === 0 ? 'ep:video-pause' : 'ep:video-play'" />
|
<Icon :icon="row.status === 0 ? 'ep:video-pause' : 'ep:video-play'" />
|
||||||
{{
|
{{ getDictLabel(DICT_TYPE.COMMON_STATUS, row.status) }}
|
||||||
getDictLabel(
|
|
||||||
DICT_TYPE.COMMON_STATUS,
|
|
||||||
row.status === CommonStatusEnum.ENABLE
|
|
||||||
? CommonStatusEnum.DISABLE
|
|
||||||
: CommonStatusEnum.ENABLE
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="danger" class="!mr-10px" link @click="handleDelete(row.id)">
|
<el-button type="danger" class="!mr-10px" link @click="handleDelete(row.id)">
|
||||||
<Icon icon="ep:delete" />
|
<Icon icon="ep:delete" />
|
||||||
|
|
@ -300,7 +293,7 @@ const statistics = ref({
|
||||||
total: 0,
|
total: 0,
|
||||||
enabled: 0,
|
enabled: 0,
|
||||||
disabled: 0,
|
disabled: 0,
|
||||||
triggered: 0,
|
triggered: 0, // 已触发的规则数量 (暂时使用启用状态的规则数量)
|
||||||
timerRules: 0 // 定时规则数量
|
timerRules: 0 // 定时规则数量
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -326,14 +319,12 @@ const getRuleSceneSummary = (rule: IotSceneRule) => {
|
||||||
default:
|
default:
|
||||||
description = getTriggerTypeLabel(trigger.type)
|
description = getTriggerTypeLabel(trigger.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加设备信息(如果有)
|
// 添加设备信息(如果有)
|
||||||
if (trigger.deviceId) {
|
if (trigger.deviceId) {
|
||||||
description += ` [设备ID: ${trigger.deviceId}]`
|
description += ` [设备ID: ${trigger.deviceId}]`
|
||||||
} else if (trigger.productId) {
|
} else if (trigger.productId) {
|
||||||
description += ` [产品ID: ${trigger.productId}]`
|
description += ` [产品ID: ${trigger.productId}]`
|
||||||
}
|
}
|
||||||
|
|
||||||
return description
|
return description
|
||||||
}) || []
|
}) || []
|
||||||
|
|
||||||
|
|
@ -341,19 +332,16 @@ const getRuleSceneSummary = (rule: IotSceneRule) => {
|
||||||
rule.actions?.map((action: any) => {
|
rule.actions?.map((action: any) => {
|
||||||
// 构建基础描述
|
// 构建基础描述
|
||||||
let description = getActionTypeLabel(action.type)
|
let description = getActionTypeLabel(action.type)
|
||||||
|
|
||||||
// 添加设备信息(如果有)
|
// 添加设备信息(如果有)
|
||||||
if (action.deviceId) {
|
if (action.deviceId) {
|
||||||
description += ` [设备ID: ${action.deviceId}]`
|
description += ` [设备ID: ${action.deviceId}]`
|
||||||
} else if (action.productId) {
|
} else if (action.productId) {
|
||||||
description += ` [产品ID: ${action.productId}]`
|
description += ` [产品ID: ${action.productId}]`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加告警配置信息(如果有)
|
// 添加告警配置信息(如果有)
|
||||||
if (action.alertConfigId) {
|
if (action.alertConfigId) {
|
||||||
description += ` [告警配置ID: ${action.alertConfigId}]`
|
description += ` [告警配置ID: ${action.alertConfigId}]`
|
||||||
}
|
}
|
||||||
|
|
||||||
return description
|
return description
|
||||||
}) || []
|
}) || []
|
||||||
|
|
||||||
|
|
@ -383,9 +371,7 @@ const updateStatistics = () => {
|
||||||
total: list.value.length,
|
total: list.value.length,
|
||||||
enabled: list.value.filter((item) => item.status === CommonStatusEnum.ENABLE).length,
|
enabled: list.value.filter((item) => item.status === CommonStatusEnum.ENABLE).length,
|
||||||
disabled: list.value.filter((item) => item.status === CommonStatusEnum.DISABLE).length,
|
disabled: list.value.filter((item) => item.status === CommonStatusEnum.DISABLE).length,
|
||||||
// 已触发的规则数量 (暂时使用启用状态的规则数量)
|
|
||||||
triggered: list.value.filter((item) => item.status === CommonStatusEnum.ENABLE).length,
|
triggered: list.value.filter((item) => item.status === CommonStatusEnum.ENABLE).length,
|
||||||
// 定时规则数量
|
|
||||||
timerRules: list.value.filter((item) => hasTimerTrigger(item)).length
|
timerRules: list.value.filter((item) => hasTimerTrigger(item)).length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,7 @@ export const getActionTypeLabel = (type: number): string => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取执行器标签类型(用于 el-tag 的 type 属性) */
|
/** 获取执行器标签类型(用于 el-tag 的 type 属性) */
|
||||||
|
// TODO @puhui999:这种跟界面相关的,可以拿到对应组件里;
|
||||||
export const getActionTypeTag = (
|
export const getActionTypeTag = (
|
||||||
type: number
|
type: number
|
||||||
): 'primary' | 'success' | 'info' | 'warning' | 'danger' => {
|
): 'primary' | 'success' | 'info' | 'warning' | 'danger' => {
|
||||||
|
|
@ -368,6 +369,7 @@ export const getActionTypeTag = (
|
||||||
return actionTypeTags[type] || 'info'
|
return actionTypeTags[type] || 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:建议不设置最大值哈。
|
||||||
/** 场景联动规则配置常量 */
|
/** 场景联动规则配置常量 */
|
||||||
export const SCENE_RULE_CONFIG = {
|
export const SCENE_RULE_CONFIG = {
|
||||||
MAX_ACTIONS: 5, // 最大执行器数量
|
MAX_ACTIONS: 5, // 最大执行器数量
|
||||||
|
|
@ -375,6 +377,7 @@ export const SCENE_RULE_CONFIG = {
|
||||||
MAX_CONDITIONS: 20 // 最大条件数量
|
MAX_CONDITIONS: 20 // 最大条件数量
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
// TODO @puhui999:下面这个要去掉么?
|
||||||
/** IoT 设备消息类型枚举 */
|
/** IoT 设备消息类型枚举 */
|
||||||
export const IotDeviceMessageTypeEnum = {
|
export const IotDeviceMessageTypeEnum = {
|
||||||
PROPERTY: 'property', // 属性
|
PROPERTY: 'property', // 属性
|
||||||
|
|
@ -434,6 +437,7 @@ export const IoTDeviceStatusEnum = {
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
/** 设备启用状态枚举 */
|
/** 设备启用状态枚举 */
|
||||||
|
// TODO @puhui999:这个是不是和 IoTDeviceStatusEnum 合并下;额外增加一个 value2
|
||||||
export const IoTDeviceEnableStatusEnum = {
|
export const IoTDeviceEnableStatusEnum = {
|
||||||
ENABLED: {
|
ENABLED: {
|
||||||
label: '正常',
|
label: '正常',
|
||||||
|
|
@ -448,6 +452,7 @@ export const IoTDeviceEnableStatusEnum = {
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
/** 设备激活状态枚举 */
|
/** 设备激活状态枚举 */
|
||||||
|
// TODO @puhui999:这个是不是搞到界面里。label 就是 IoTDeviceStatusEnum,然后 tag 界面里处理;;或者也可以在想想,= = 主要设备状态有 3 个枚举,嘿嘿~
|
||||||
export const IoTDeviceActiveStatusEnum = {
|
export const IoTDeviceActiveStatusEnum = {
|
||||||
ACTIVATED: {
|
ACTIVATED: {
|
||||||
label: '已激活',
|
label: '已激活',
|
||||||
|
|
@ -505,11 +510,13 @@ export const deviceStatusChangeOptions = [
|
||||||
|
|
||||||
/** 获取设备启用状态文本 */
|
/** 获取设备启用状态文本 */
|
||||||
export const getDeviceEnableStatusText = (status: number): string => {
|
export const getDeviceEnableStatusText = (status: number): string => {
|
||||||
|
// TODO @puhui999:设备有 3 个状态,上线、离线,未激活;
|
||||||
const statusItem = Object.values(IoTDeviceEnableStatusEnum).find((item) => item.value === status)
|
const statusItem = Object.values(IoTDeviceEnableStatusEnum).find((item) => item.value === status)
|
||||||
return statusItem?.label || '未知'
|
return statusItem?.label || '未知'
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取设备启用状态标签类型 */
|
/** 获取设备启用状态标签类型 */
|
||||||
|
// TODO @puhui999:这个是不是可以直接在界面里处理;或者也可以在想想,= = 主要设备状态有 3 个枚举,嘿嘿~
|
||||||
export const getDeviceEnableStatusTagType = (
|
export const getDeviceEnableStatusTagType = (
|
||||||
status: number
|
status: number
|
||||||
): 'primary' | 'success' | 'info' | 'warning' | 'danger' => {
|
): 'primary' | 'success' | 'info' | 'warning' | 'danger' => {
|
||||||
|
|
@ -518,6 +525,7 @@ export const getDeviceEnableStatusTagType = (
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取设备激活状态文本和标签类型 */
|
/** 获取设备激活状态文本和标签类型 */
|
||||||
|
// TODO @puhui999:这个是不是可以直接在界面里处理;或者也可以在想想,= = 主要设备状态有 3 个枚举,嘿嘿~
|
||||||
export const getDeviceActiveStatus = (activeTime?: string | null) => {
|
export const getDeviceActiveStatus = (activeTime?: string | null) => {
|
||||||
const isActivated = !!activeTime
|
const isActivated = !!activeTime
|
||||||
return {
|
return {
|
||||||
|
|
@ -541,14 +549,13 @@ export const IotRuleSceneTriggerTimeOperatorEnum = {
|
||||||
TODAY: { name: '在今日之间', value: 'today' } // 在今日之间
|
TODAY: { name: '在今日之间', value: 'today' } // 在今日之间
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
// ========== 辅助函数 ==========
|
|
||||||
|
|
||||||
/** 获取触发器类型标签 */
|
/** 获取触发器类型标签 */
|
||||||
export const getTriggerTypeLabel = (type: number): string => {
|
export const getTriggerTypeLabel = (type: number): string => {
|
||||||
const option = triggerTypeOptions.find((item) => item.value === type)
|
const option = triggerTypeOptions.find((item) => item.value === type)
|
||||||
return option?.label || '未知类型'
|
return option?.label || '未知类型'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:这种跟界面相关的,可以拿到对应组件里;
|
||||||
/** 获取触发器标签类型(用于 el-tag 的 type 属性) */
|
/** 获取触发器标签类型(用于 el-tag 的 type 属性) */
|
||||||
export const getTriggerTagType = (
|
export const getTriggerTagType = (
|
||||||
type: number
|
type: number
|
||||||
|
|
@ -559,9 +566,9 @@ export const getTriggerTagType = (
|
||||||
return isDeviceTrigger(type) ? 'success' : 'info'
|
return isDeviceTrigger(type) ? 'success' : 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== JSON参数输入组件相关常量 ==========
|
// ========== JSON 参数输入组件相关常量 ==========
|
||||||
|
|
||||||
/** JSON参数输入组件类型枚举 */
|
/** JSON 参数输入组件类型枚举 */
|
||||||
export const JsonParamsInputTypeEnum = {
|
export const JsonParamsInputTypeEnum = {
|
||||||
SERVICE: 'service',
|
SERVICE: 'service',
|
||||||
EVENT: 'event',
|
EVENT: 'event',
|
||||||
|
|
@ -569,11 +576,11 @@ export const JsonParamsInputTypeEnum = {
|
||||||
CUSTOM: 'custom'
|
CUSTOM: 'custom'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
/** JSON参数输入组件类型 */
|
/** JSON 参数输入组件类型 */
|
||||||
export type JsonParamsInputType =
|
export type JsonParamsInputType =
|
||||||
(typeof JsonParamsInputTypeEnum)[keyof typeof JsonParamsInputTypeEnum]
|
(typeof JsonParamsInputTypeEnum)[keyof typeof JsonParamsInputTypeEnum]
|
||||||
|
|
||||||
/** JSON参数输入组件文本常量 */
|
/** JSON 参数输入组件文本常量 */
|
||||||
export const JSON_PARAMS_INPUT_CONSTANTS = {
|
export const JSON_PARAMS_INPUT_CONSTANTS = {
|
||||||
// 基础文本
|
// 基础文本
|
||||||
PLACEHOLDER: '请输入JSON格式的参数',
|
PLACEHOLDER: '请输入JSON格式的参数',
|
||||||
|
|
@ -628,7 +635,7 @@ export const JSON_PARAMS_INPUT_CONSTANTS = {
|
||||||
}
|
}
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
/** JSON参数输入组件图标常量 */
|
/** JSON 参数输入组件图标常量 */
|
||||||
export const JSON_PARAMS_INPUT_ICONS = {
|
export const JSON_PARAMS_INPUT_ICONS = {
|
||||||
// 标题图标
|
// 标题图标
|
||||||
TITLE_ICONS: {
|
TITLE_ICONS: {
|
||||||
|
|
@ -655,7 +662,7 @@ export const JSON_PARAMS_INPUT_ICONS = {
|
||||||
}
|
}
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
/** JSON参数输入组件示例值常量 */
|
/** JSON 参数输入组件示例值常量 */
|
||||||
export const JSON_PARAMS_EXAMPLE_VALUES = {
|
export const JSON_PARAMS_EXAMPLE_VALUES = {
|
||||||
[IoTDataSpecsDataTypeEnum.INT]: { display: '25', value: 25 },
|
[IoTDataSpecsDataTypeEnum.INT]: { display: '25', value: 25 },
|
||||||
[IoTDataSpecsDataTypeEnum.FLOAT]: { display: '25.5', value: 25.5 },
|
[IoTDataSpecsDataTypeEnum.FLOAT]: { display: '25.5', value: 25.5 },
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue