|
|
@@ -1,40 +1,37 @@
|
|
|
<template>
|
|
|
- <div class="step-config">
|
|
|
- <el-form :model="form" label-width="90px" size="small">
|
|
|
- <el-form-item label="步骤名称">
|
|
|
+ <el-dialog
|
|
|
+ :title="dialogTitle"
|
|
|
+ :visible.sync="visible"
|
|
|
+ width="900px"
|
|
|
+ @close="handleClose"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ >
|
|
|
+ <el-form ref="form" :model="form" :rules="rules" label-width="120px">
|
|
|
+ <!-- 步骤名称 -->
|
|
|
+ <el-form-item label="步骤名称" prop="stepName">
|
|
|
<el-input v-model="form.stepName" placeholder="请输入步骤名称" />
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="步骤代码">
|
|
|
- <el-input v-model="form.stepCode" placeholder="自动生成" disabled />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="步骤类型">
|
|
|
- <el-tag :type="getStepTypeTag(form.stepType)" size="medium">
|
|
|
- <i :class="getStepTypeIcon(form.stepType)" style="margin-right: 4px"></i>
|
|
|
- {{ getStepTypeName(form.stepType) }}
|
|
|
- </el-tag>
|
|
|
+ <!-- 步骤类型 -->
|
|
|
+ <el-form-item label="步骤类型" prop="stepType">
|
|
|
+ <el-select v-model="form.stepType" placeholder="请选择步骤类型" @change="handleStepTypeChange">
|
|
|
+ <el-option label="能力调用" value="ABILITY" />
|
|
|
+ <el-option label="延时等待" value="DELAY" />
|
|
|
+ <el-option label="条件判断" value="CONDITION" />
|
|
|
+ <el-option label="循环执行" value="LOOP" />
|
|
|
+ <el-option label="属性查询" value="ATTR_QUERY" />
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <!-- 能力调用配置 -->
|
|
|
+ <!-- ==================== 能力调用配置 ==================== -->
|
|
|
<template v-if="form.stepType === 'ABILITY'">
|
|
|
- <el-divider content-position="left">目标配置</el-divider>
|
|
|
-
|
|
|
- <el-form-item label="对象类型">
|
|
|
- <el-radio-group v-model="form.targetObjType" @change="handleObjTypeChange">
|
|
|
- <el-radio :label="2">设备</el-radio>
|
|
|
- <el-radio :label="3">系统</el-radio>
|
|
|
- </el-radio-group>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- 设备选择 -->
|
|
|
- <el-form-item label="选择设备" v-if="form.targetObjType === 2">
|
|
|
+ <!-- 目标设备 -->
|
|
|
+ <el-form-item label="目标设备" prop="targetObjCode">
|
|
|
<el-select
|
|
|
v-model="form.targetObjCode"
|
|
|
+ placeholder="请选择目标设备"
|
|
|
filterable
|
|
|
- placeholder="搜索选择设备"
|
|
|
- style="width: 100%"
|
|
|
- @change="handleObjChange"
|
|
|
+ @change="handleDeviceChange"
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="device in deviceList"
|
|
|
@@ -43,45 +40,18 @@
|
|
|
:value="device.deviceCode"
|
|
|
>
|
|
|
<span>{{ device.deviceName }}</span>
|
|
|
- <span style="color: #909399; font-size: 12px; margin-left: 8px">{{ device.deviceCode }}</span>
|
|
|
+ <span style="float: right; color: #8492a6; font-size: 13px">
|
|
|
+ {{ device.deviceCode }}
|
|
|
+ </span>
|
|
|
</el-option>
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <!-- 系统选择 -->
|
|
|
- <el-form-item label="选择系统" v-else-if="form.targetObjType === 3">
|
|
|
- <el-select
|
|
|
- v-model="form.targetObjCode"
|
|
|
- filterable
|
|
|
- placeholder="搜索选择系统"
|
|
|
- style="width: 100%"
|
|
|
- @change="handleObjChange"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="system in systemList"
|
|
|
- :key="system.systemCode"
|
|
|
- :label="system.systemName"
|
|
|
- :value="system.systemCode"
|
|
|
- >
|
|
|
- <span>{{ system.systemName }}</span>
|
|
|
- <span style="color: #909399; font-size: 12px; margin-left: 8px">{{ system.systemCode }}</span>
|
|
|
- </el-option>
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="模型代码">
|
|
|
- <el-input v-model="form.targetModelCode" placeholder="自动关联" disabled />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- 能力选择 - 从物模型加载 -->
|
|
|
- <el-form-item label="选择能力">
|
|
|
+ <!-- 设备能力 -->
|
|
|
+ <el-form-item label="设备能力" prop="abilityKey">
|
|
|
<el-select
|
|
|
v-model="form.abilityKey"
|
|
|
- filterable
|
|
|
- placeholder="请选择要调用的能力"
|
|
|
- style="width: 100%"
|
|
|
- :loading="loadingModel"
|
|
|
- :disabled="!form.targetObjCode"
|
|
|
+ placeholder="请选择设备能力"
|
|
|
@change="handleAbilityChange"
|
|
|
>
|
|
|
<el-option
|
|
|
@@ -90,690 +60,633 @@
|
|
|
:label="ability.abilityName"
|
|
|
:value="ability.abilityKey"
|
|
|
>
|
|
|
- <div style="display: flex; justify-content: space-between; align-items: center;">
|
|
|
- <span>
|
|
|
- <i class="el-icon-magic-stick" style="color: #f5a623; margin-right: 4px"></i>
|
|
|
- {{ ability.abilityName }}
|
|
|
- </span>
|
|
|
- <el-tag size="mini" type="info">{{ ability.abilityKey }}</el-tag>
|
|
|
- </div>
|
|
|
+ <span>{{ ability.abilityName }}</span>
|
|
|
+ <span style="float: right; color: #8492a6; font-size: 13px">
|
|
|
+ {{ ability.abilityDesc }}
|
|
|
+ </span>
|
|
|
</el-option>
|
|
|
</el-select>
|
|
|
- <div class="form-tip" v-if="currentAbility">
|
|
|
- {{ currentAbility.abilityDesc || '暂无描述' }}
|
|
|
- </div>
|
|
|
- <div class="form-tip" v-else-if="!loadingModel && abilityList.length === 0 && form.targetObjCode">
|
|
|
- ⚠️ 该对象暂无能力定义
|
|
|
- </div>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-divider content-position="left">参数配置</el-divider>
|
|
|
-
|
|
|
- <!-- 参数定义预览 -->
|
|
|
- <el-form-item label="参数定义" v-if="currentAbility && currentAbility.paramDefinition">
|
|
|
- <el-alert
|
|
|
- type="info"
|
|
|
- :closable="false"
|
|
|
- style="margin-bottom: 12px"
|
|
|
- >
|
|
|
- <template slot="title">
|
|
|
- <div style="display: flex; align-items: center; justify-content: space-between;">
|
|
|
- <span>参数结构</span>
|
|
|
- <el-button type="text" size="mini" @click="showParamHelp">
|
|
|
- <i class="el-icon-question"></i> 帮助
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <pre class="param-definition-preview">{{ formatParamDefinition }}</pre>
|
|
|
- </el-alert>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="参数来源">
|
|
|
+ <!-- 参数来源 -->
|
|
|
+ <el-form-item label="参数来源" prop="paramSource">
|
|
|
<el-radio-group v-model="form.paramSource" @change="handleParamSourceChange">
|
|
|
- <el-radio label="STATIC">静态值</el-radio>
|
|
|
- <el-radio label="CONTEXT">上下文变量</el-radio>
|
|
|
- <el-radio label="EXPR">表达式</el-radio>
|
|
|
+ <el-radio label="STATIC">静态参数</el-radio>
|
|
|
+ <el-radio label="CONTEXT">上下文参数</el-radio>
|
|
|
+ <el-radio label="ATTR">设备属性</el-radio>
|
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <!-- 静态参数 - 动态渲染 -->
|
|
|
- <template v-if="form.paramSource === 'STATIC' && currentAbility">
|
|
|
- <div class="static-params-container">
|
|
|
- <!-- 根据paramDefinition动态渲染 -->
|
|
|
- <component
|
|
|
- :is="getParamComponent(paramDef)"
|
|
|
- v-model="staticParamValue"
|
|
|
- :definition="paramDef"
|
|
|
- @change="handleStaticParamChange"
|
|
|
- />
|
|
|
+ <!-- 静态参数配置 -->
|
|
|
+ <el-form-item
|
|
|
+ v-if="form.paramSource === 'STATIC' && currentAbility"
|
|
|
+ label="参数值"
|
|
|
+ >
|
|
|
+ <!-- 场景1: paramDefinition 为 null - 无参数 -->
|
|
|
+ <div v-if="!currentAbility.paramDefinition">
|
|
|
+ <el-tag type="info">该能力无需参数</el-tag>
|
|
|
</div>
|
|
|
|
|
|
- <el-form-item label="参数预览">
|
|
|
- <el-input
|
|
|
- type="textarea"
|
|
|
- :value="JSON.stringify(staticParams, null, 2)"
|
|
|
- :rows="3"
|
|
|
- readonly
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </template>
|
|
|
+ <!-- 场景2: Options 类型 - 枚举值 -->
|
|
|
+ <div v-else-if="getParamType(currentAbility.paramDefinition) === 'Options'">
|
|
|
+ <el-select
|
|
|
+ v-model="form.abilityParam"
|
|
|
+ placeholder="请选择参数"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="option in parseOptions(currentAbility.paramDefinition)"
|
|
|
+ :key="option.value"
|
|
|
+ :label="option.key"
|
|
|
+ :value="option.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <div style="margin-top: 8px; color: #909399; font-size: 12px;">
|
|
|
+ 当前选择: {{ getSelectedOptionLabel() }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 上下文变量映射 -->
|
|
|
- <template v-if="form.paramSource === 'CONTEXT'">
|
|
|
- <el-form-item label="变量映射">
|
|
|
- <el-input
|
|
|
- type="textarea"
|
|
|
- v-model="paramMappingStr"
|
|
|
- :rows="4"
|
|
|
- placeholder='如: {"param1": "${context.varKey1}", "param2": "${context.varKey2}"}'
|
|
|
+ <!-- 场景3: Slider 类型 - 滑块 -->
|
|
|
+ <div v-else-if="getParamType(currentAbility.paramDefinition) === 'Slider'">
|
|
|
+ <el-slider
|
|
|
+ v-model="sliderValue"
|
|
|
+ :min="getSliderMin(currentAbility.paramDefinition)"
|
|
|
+ :max="getSliderMax(currentAbility.paramDefinition)"
|
|
|
+ :format-tooltip="formatSliderTooltip"
|
|
|
+ @change="handleSliderChange"
|
|
|
/>
|
|
|
- <div class="form-tip">格式: {"参数名": "${context.变量键}"}</div>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="可用变量">
|
|
|
- <div class="variable-list">
|
|
|
- <el-tag
|
|
|
- v-for="variable in contextVariables"
|
|
|
- :key="variable.varKey"
|
|
|
- size="small"
|
|
|
- @click="insertVariable(variable.varKey)"
|
|
|
- class="variable-tag"
|
|
|
- >
|
|
|
- {{ variable.varName }} (${context.{{ variable.varKey }}})
|
|
|
- </el-tag>
|
|
|
+ <div style="margin-top: 8px;">
|
|
|
+ <el-input-number
|
|
|
+ v-model="sliderValue"
|
|
|
+ :min="getSliderMin(currentAbility.paramDefinition)"
|
|
|
+ :max="getSliderMax(currentAbility.paramDefinition)"
|
|
|
+ @change="handleSliderChange"
|
|
|
+ />
|
|
|
+ <span style="margin-left: 10px; color: #909399;">
|
|
|
+ {{ getSliderUnit(currentAbility.paramDefinition) }}
|
|
|
+ </span>
|
|
|
</div>
|
|
|
- </el-form-item>
|
|
|
- </template>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 表达式 -->
|
|
|
- <template v-if="form.paramSource === 'EXPR'">
|
|
|
- <el-form-item label="参数表达式">
|
|
|
+ <!-- 场景4: Input 类型 - 文本输入 -->
|
|
|
+ <div v-else-if="getParamType(currentAbility.paramDefinition) === 'Input'">
|
|
|
<el-input
|
|
|
- type="textarea"
|
|
|
v-model="form.abilityParam"
|
|
|
- :rows="4"
|
|
|
- placeholder='支持JS表达式和变量引用'
|
|
|
- />
|
|
|
- <div class="form-tip">
|
|
|
- 支持:变量引用 ${varKey}、运算符、函数等
|
|
|
- </div>
|
|
|
- </el-form-item>
|
|
|
- </template>
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- 延时等待配置 -->
|
|
|
- <template v-if="form.stepType === 'DELAY'">
|
|
|
- <el-divider content-position="left">延时配置</el-divider>
|
|
|
-
|
|
|
- <el-form-item label="延时时间">
|
|
|
- <el-input-number
|
|
|
- v-model="form.delaySeconds"
|
|
|
- :min="0"
|
|
|
- :max="86400"
|
|
|
- style="width: 180px"
|
|
|
- />
|
|
|
- <span style="margin-left: 8px; color: #909399">秒</span>
|
|
|
- </el-form-item>
|
|
|
+ placeholder="请输入参数值"
|
|
|
+ >
|
|
|
+ <template #append v-if="getInputUnit(currentAbility.paramDefinition)">
|
|
|
+ {{ getInputUnit(currentAbility.paramDefinition) }}
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
|
|
|
- <el-form-item label="快捷设置">
|
|
|
- <div class="delay-shortcuts">
|
|
|
- <el-tag @click="form.delaySeconds = 5" class="delay-tag">5秒</el-tag>
|
|
|
- <el-tag @click="form.delaySeconds = 10" class="delay-tag">10秒</el-tag>
|
|
|
- <el-tag @click="form.delaySeconds = 30" class="delay-tag">30秒</el-tag>
|
|
|
- <el-tag @click="form.delaySeconds = 60" class="delay-tag">1分钟</el-tag>
|
|
|
- <el-tag @click="form.delaySeconds = 300" class="delay-tag">5分钟</el-tag>
|
|
|
- <el-tag @click="form.delaySeconds = 600" class="delay-tag">10分钟</el-tag>
|
|
|
+ <!-- 未知类型 - 兜底 -->
|
|
|
+ <div v-else>
|
|
|
+ <el-input
|
|
|
+ v-model="form.abilityParam"
|
|
|
+ placeholder="请输入参数值"
|
|
|
+ />
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- 条件判断配置 -->
|
|
|
- <template v-if="form.stepType === 'CONDITION'">
|
|
|
- <el-divider content-position="left">条件配置</el-divider>
|
|
|
|
|
|
- <el-form-item label="条件表达式">
|
|
|
- <condition-builder
|
|
|
- v-model="form.conditionExpr"
|
|
|
- :variables="allVariables"
|
|
|
- :device-list="deviceList"
|
|
|
- :show-attr-select="true"
|
|
|
+ <!-- 上下文参数配置 -->
|
|
|
+ <el-form-item v-if="form.paramSource === 'CONTEXT'" label="参数映射">
|
|
|
+ <el-input
|
|
|
+ v-model="form.paramMapping"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder='示例: {"value": "context.switchState"}'
|
|
|
/>
|
|
|
+ <div style="margin-top: 8px; color: #909399; font-size: 12px;">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ 从执行上下文中动态获取参数值
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="满足时">
|
|
|
- <el-input v-model="form.trueBranch" placeholder="跳转到步骤代码或继续" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="不满足时">
|
|
|
- <el-input v-model="form.falseBranch" placeholder="跳转到步骤代码或跳过" />
|
|
|
- </el-form-item>
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- 并行执行配置 -->
|
|
|
- <template v-if="form.stepType === 'PARALLEL'">
|
|
|
- <el-divider content-position="left">并行配置</el-divider>
|
|
|
-
|
|
|
- <el-form-item label="并行步骤">
|
|
|
+ <!-- 设备属性配置 -->
|
|
|
+ <el-form-item v-if="form.paramSource === 'ATTR'" label="属性映射">
|
|
|
<el-input
|
|
|
+ v-model="form.paramMapping"
|
|
|
type="textarea"
|
|
|
- v-model="form.parallelSteps"
|
|
|
:rows="3"
|
|
|
- placeholder="输入步骤代码,逗号分隔"
|
|
|
+ placeholder='示例: {"value": "D-B-QS-10000001.Switch"}'
|
|
|
/>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="等待策略">
|
|
|
- <el-radio-group v-model="form.waitStrategy">
|
|
|
- <el-radio label="ALL">全部完成</el-radio>
|
|
|
- <el-radio label="ANY">任一完成</el-radio>
|
|
|
- </el-radio-group>
|
|
|
+ <div style="margin-top: 8px; color: #909399; font-size: 12px;">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ 从设备属性中动态获取参数值
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
</template>
|
|
|
|
|
|
- <!-- 循环执行配置 -->
|
|
|
+ <!-- ==================== 延时等待配置 ==================== -->
|
|
|
+ <el-form-item v-if="form.stepType === 'DELAY'" label="延时时长" prop="delaySeconds">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.delaySeconds"
|
|
|
+ :min="1"
|
|
|
+ :max="3600"
|
|
|
+ placeholder="秒"
|
|
|
+ />
|
|
|
+ <span style="margin-left: 10px;">秒</span>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- ==================== 条件判断配置 ==================== -->
|
|
|
+ <el-form-item v-if="form.stepType === 'CONDITION'" label="条件表达式">
|
|
|
+ <el-input
|
|
|
+ v-model="form.conditionExpr"
|
|
|
+ type="textarea"
|
|
|
+ :rows="4"
|
|
|
+ placeholder='简单格式: {"left":"Switch","op":"==","right":"1"}
|
|
|
+复杂格式: {"logic":"AND","conditions":[{"left":"Switch","op":"==","right":"1"}]}'
|
|
|
+ />
|
|
|
+ <div style="margin-top: 8px; color: #909399; font-size: 12px;">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ 支持简单条件和复杂逻辑组合(AND/OR)
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- ==================== 循环执行配置 ==================== -->
|
|
|
<template v-if="form.stepType === 'LOOP'">
|
|
|
- <el-divider content-position="left">循环配置</el-divider>
|
|
|
+ <el-card shadow="never" style="margin-bottom: 20px; background: #f5f7fa;">
|
|
|
+ <div slot="header" class="clearfix">
|
|
|
+ <span style="font-weight: 600;">循环配置</span>
|
|
|
+ </div>
|
|
|
|
|
|
- <el-form-item label="循环类型">
|
|
|
- <el-radio-group v-model="form.loopType">
|
|
|
- <el-radio label="COUNT">固定次数</el-radio>
|
|
|
- <el-radio label="CONDITION">条件循环</el-radio>
|
|
|
- </el-radio-group>
|
|
|
- </el-form-item>
|
|
|
+ <!-- 最大循环次数 -->
|
|
|
+ <el-form-item label="最大次数" label-width="120px">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.loopMaxCount"
|
|
|
+ :min="0"
|
|
|
+ :max="1000"
|
|
|
+ placeholder="0表示无限循环"
|
|
|
+ />
|
|
|
+ <span style="margin-left: 10px; color: #909399;">
|
|
|
+ 0表示无限循环(需配置跳出条件)
|
|
|
+ </span>
|
|
|
+ </el-form-item>
|
|
|
|
|
|
- <el-form-item label="循环次数" v-if="form.loopType === 'COUNT'">
|
|
|
- <el-input-number v-model="form.loopCount" :min="1" :max="100" />
|
|
|
- </el-form-item>
|
|
|
+ <!-- 循环间隔 -->
|
|
|
+ <el-form-item label="循环间隔" label-width="120px">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.loopInterval"
|
|
|
+ :min="100"
|
|
|
+ :max="60000"
|
|
|
+ :step="100"
|
|
|
+ />
|
|
|
+ <span style="margin-left: 10px;">毫秒</span>
|
|
|
+ </el-form-item>
|
|
|
|
|
|
- <el-form-item label="循环条件" v-if="form.loopType === 'CONDITION'">
|
|
|
- <condition-builder v-model="form.loopCondition" :variables="allVariables" />
|
|
|
+ <!-- 跳出条件 -->
|
|
|
+ <el-form-item label="跳出条件" label-width="120px">
|
|
|
+ <el-input
|
|
|
+ v-model="form.loopCondition"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder='示例: {"left":"current_Switch","op":"==","right":"1"}'
|
|
|
+ />
|
|
|
+ <div style="margin-top: 8px; color: #909399; font-size: 12px;">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ 满足条件时跳出循环,可引用上下文变量(如 current_Switch)
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 子步骤配置提示 -->
|
|
|
+ <el-alert
|
|
|
+ title="子步骤配置"
|
|
|
+ type="info"
|
|
|
+ :closable="false"
|
|
|
+ style="margin-bottom: 20px;"
|
|
|
+ >
|
|
|
+ 循环步骤需要配置子步骤,子步骤将在每次循环中依次执行。
|
|
|
+ 保存当前步骤后,请在编排器中为此循环步骤添加子步骤。
|
|
|
+ </el-alert>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- ==================== 属性查询配置 ==================== -->
|
|
|
+ <template v-if="form.stepType === 'ATTR_QUERY'">
|
|
|
+ <!-- 目标设备 -->
|
|
|
+ <el-form-item label="目标设备" prop="targetObjCode">
|
|
|
+ <el-select
|
|
|
+ v-model="form.targetObjCode"
|
|
|
+ placeholder="请选择设备"
|
|
|
+ filterable
|
|
|
+ @change="handleDeviceChange"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="device in deviceList"
|
|
|
+ :key="device.deviceCode"
|
|
|
+ :label="device.deviceName"
|
|
|
+ :value="device.deviceCode"
|
|
|
+ >
|
|
|
+ <span>{{ device.deviceName }}</span>
|
|
|
+ <span style="float: right; color: #8492a6; font-size: 13px">
|
|
|
+ {{ device.deviceCode }}
|
|
|
+ </span>
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="循环间隔">
|
|
|
- <el-input-number v-model="form.loopInterval" :min="0" style="width: 180px" />
|
|
|
- <span style="margin-left: 8px; color: #909399">秒</span>
|
|
|
+ <!-- 属性键 -->
|
|
|
+ <el-form-item label="属性键" prop="abilityKey">
|
|
|
+ <el-input
|
|
|
+ v-model="form.abilityKey"
|
|
|
+ placeholder="例如: Switch"
|
|
|
+ />
|
|
|
+ <div style="margin-top: 8px; color: #909399; font-size: 12px;">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ 查询结果将保存到上下文: current_[属性键名]
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
</template>
|
|
|
|
|
|
- <el-divider content-position="left">执行控制</el-divider>
|
|
|
-
|
|
|
- <el-form-item label="失败重试">
|
|
|
- <el-switch v-model="form.retryOnFail" :active-value="1" :inactive-value="0" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="重试次数" v-if="form.retryOnFail === 1">
|
|
|
- <el-input-number v-model="form.retryTimes" :min="1" :max="5" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="超时时间">
|
|
|
- <el-input-number v-model="form.timeout" :min="0" style="width: 180px" />
|
|
|
- <span style="margin-left: 8px; color: #909399">秒</span>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="是否启用">
|
|
|
- <el-switch v-model="form.enable" :active-value="1" :inactive-value="0" />
|
|
|
+ <!-- ==================== 通用配置 ==================== -->
|
|
|
+ <!-- 执行条件 -->
|
|
|
+ <el-form-item label="执行条件" v-if="form.stepType !== 'LOOP'">
|
|
|
+ <el-input
|
|
|
+ v-model="form.conditionExpr"
|
|
|
+ type="textarea"
|
|
|
+ :rows="2"
|
|
|
+ placeholder='留空表示无条件执行,示例: {"left":"status","op":"==","right":"ready"}'
|
|
|
+ />
|
|
|
+ <div style="margin-top: 8px; color: #909399; font-size: 12px;">
|
|
|
+ <i class="el-icon-question"></i>
|
|
|
+ 此步骤执行前需满足的条件(可选)
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="备注">
|
|
|
- <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="可选" />
|
|
|
+ <!-- 失败后继续 -->
|
|
|
+ <el-form-item label="失败后继续">
|
|
|
+ <el-switch v-model="continueOnFailSwitch" />
|
|
|
+ <span style="margin-left: 10px; color: #909399;">
|
|
|
+ 开启后,即使此步骤失败也会继续执行后续步骤
|
|
|
+ </span>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
- </div>
|
|
|
+
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button @click="visible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="handleSubmit" :loading="submitting">确定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { getModelByCode } from '@/api/basecfg/objModel';
|
|
|
-import { listSubsystemAll } from '@/api/adapter/subsystem';
|
|
|
-import ConditionBuilder from './ConditionBuilder.vue';
|
|
|
+import { getModelByCode } from '@/api/basecfg/objModel'
|
|
|
|
|
|
export default {
|
|
|
name: 'StepConfig',
|
|
|
- components: { ConditionBuilder },
|
|
|
-
|
|
|
props: {
|
|
|
- step: {
|
|
|
- type: Object,
|
|
|
- default: () => ({})
|
|
|
- },
|
|
|
deviceList: {
|
|
|
type: Array,
|
|
|
default: () => []
|
|
|
- },
|
|
|
- contextVariables: {
|
|
|
- type: Array,
|
|
|
- default: () => []
|
|
|
}
|
|
|
},
|
|
|
-
|
|
|
data() {
|
|
|
return {
|
|
|
- form: {},
|
|
|
- systemList: [],
|
|
|
+ visible: false,
|
|
|
+ dialogTitle: '配置步骤',
|
|
|
+ submitting: false,
|
|
|
+ form: {
|
|
|
+ id: null,
|
|
|
+ stepCode: '',
|
|
|
+ stepName: '',
|
|
|
+ stepType: 'ABILITY',
|
|
|
+ stepIndex: 1,
|
|
|
+ targetObjCode: '',
|
|
|
+ targetObjType: 2,
|
|
|
+ targetModelCode: '',
|
|
|
+ abilityKey: '',
|
|
|
+ abilityParam: '',
|
|
|
+ paramSource: 'STATIC',
|
|
|
+ paramMapping: '',
|
|
|
+ delaySeconds: 10,
|
|
|
+ conditionExpr: '',
|
|
|
+ continueOnFail: 0,
|
|
|
+ // 循环配置
|
|
|
+ loopMaxCount: 10,
|
|
|
+ loopInterval: 1000,
|
|
|
+ loopCondition: '',
|
|
|
+ parentStepCode: null
|
|
|
+ },
|
|
|
+ rules: {
|
|
|
+ stepName: [
|
|
|
+ { required: true, message: '请输入步骤名称', trigger: 'blur' }
|
|
|
+ ],
|
|
|
+ stepType: [
|
|
|
+ { required: true, message: '请选择步骤类型', trigger: 'change' }
|
|
|
+ ],
|
|
|
+ targetObjCode: [
|
|
|
+ { required: true, message: '请选择目标设备', trigger: 'change' }
|
|
|
+ ],
|
|
|
+ abilityKey: [
|
|
|
+ { required: true, message: '请选择设备能力或属性键', trigger: 'blur' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
abilityList: [],
|
|
|
currentAbility: null,
|
|
|
- loadingModel: false,
|
|
|
-
|
|
|
- staticParams: {},
|
|
|
- staticParamValue: null,
|
|
|
- paramMappingStr: '',
|
|
|
- paramDef: null
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
- computed: {
|
|
|
- allVariables() {
|
|
|
- return this.contextVariables.map(v => ({
|
|
|
- key: 'context.' + v.varKey,
|
|
|
- name: v.varName,
|
|
|
- type: 'context'
|
|
|
- }));
|
|
|
- },
|
|
|
-
|
|
|
- formatParamDefinition() {
|
|
|
- if (!this.currentAbility || !this.currentAbility.paramDefinition) {
|
|
|
- return '无参数定义';
|
|
|
- }
|
|
|
- try {
|
|
|
- return JSON.stringify(JSON.parse(this.currentAbility.paramDefinition), null, 2);
|
|
|
- } catch {
|
|
|
- return this.currentAbility.paramDefinition;
|
|
|
- }
|
|
|
+ sliderValue: 50,
|
|
|
+ continueOnFailSwitch: false
|
|
|
}
|
|
|
},
|
|
|
-
|
|
|
watch: {
|
|
|
- step: {
|
|
|
- immediate: true,
|
|
|
- handler(val) {
|
|
|
- this.form = { ...val };
|
|
|
-
|
|
|
- // 解析已有参数
|
|
|
- if (val.abilityParam) {
|
|
|
- try {
|
|
|
- this.staticParams = JSON.parse(val.abilityParam);
|
|
|
- } catch (e) {
|
|
|
- this.staticParams = {};
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (val.paramMapping) {
|
|
|
- this.paramMappingStr = val.paramMapping;
|
|
|
- }
|
|
|
+ continueOnFailSwitch(val) {
|
|
|
+ this.form.continueOnFail = val ? 1 : 0
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ /**
|
|
|
+ * 打开对话框
|
|
|
+ */
|
|
|
+ open(step, parentStepCode) {
|
|
|
+ this.visible = true
|
|
|
+ this.resetForm()
|
|
|
+
|
|
|
+ if (step) {
|
|
|
+ this.dialogTitle = '编辑步骤'
|
|
|
+ this.form = { ...step }
|
|
|
+ this.continueOnFailSwitch = step.continueOnFail === 1
|
|
|
|
|
|
// 加载能力列表
|
|
|
- if (val.targetObjCode && val.targetModelCode) {
|
|
|
- this.loadObjectModel(val.targetModelCode);
|
|
|
+ if (step.targetModelCode && step.stepType === 'ABILITY') {
|
|
|
+ this.loadAbilities(step.targetModelCode)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.dialogTitle = '新增步骤'
|
|
|
+ if (parentStepCode) {
|
|
|
+ this.form.parentStepCode = parentStepCode
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
- form: {
|
|
|
- deep: true,
|
|
|
- handler(val) {
|
|
|
- this.$emit('change', val);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
|
|
|
- created() {
|
|
|
- this.loadSystems();
|
|
|
- },
|
|
|
-
|
|
|
- methods: {
|
|
|
- // 加载系统列表
|
|
|
- async loadSystems() {
|
|
|
- try {
|
|
|
- const response = await listSubsystemAll();
|
|
|
- this.systemList = response.data || [];
|
|
|
- } catch (error) {
|
|
|
- console.error('加载系统列表失败', error);
|
|
|
- this.systemList = [];
|
|
|
+ /**
|
|
|
+ * 重置表单
|
|
|
+ */
|
|
|
+ resetForm() {
|
|
|
+ this.form = {
|
|
|
+ id: null,
|
|
|
+ stepCode: '',
|
|
|
+ stepName: '',
|
|
|
+ stepType: 'ABILITY',
|
|
|
+ stepIndex: 1,
|
|
|
+ targetObjCode: '',
|
|
|
+ targetObjType: 2,
|
|
|
+ targetModelCode: '',
|
|
|
+ abilityKey: '',
|
|
|
+ abilityParam: '',
|
|
|
+ paramSource: 'STATIC',
|
|
|
+ paramMapping: '',
|
|
|
+ delaySeconds: 10,
|
|
|
+ conditionExpr: '',
|
|
|
+ continueOnFail: 0,
|
|
|
+ loopMaxCount: 10,
|
|
|
+ loopInterval: 1000,
|
|
|
+ loopCondition: '',
|
|
|
+ parentStepCode: null
|
|
|
}
|
|
|
+ this.abilityList = []
|
|
|
+ this.currentAbility = null
|
|
|
+ this.sliderValue = 50
|
|
|
+ this.continueOnFailSwitch = false
|
|
|
},
|
|
|
|
|
|
- // 对象类型改变
|
|
|
- handleObjTypeChange() {
|
|
|
- this.form.targetObjCode = null;
|
|
|
- this.form.targetModelCode = null;
|
|
|
- this.form.abilityKey = null;
|
|
|
- this.abilityList = [];
|
|
|
- this.currentAbility = null;
|
|
|
- },
|
|
|
-
|
|
|
- // 对象改变
|
|
|
- async handleObjChange(objCode) {
|
|
|
- let modelCode = null;
|
|
|
-
|
|
|
- if (this.form.targetObjType === 2) {
|
|
|
- // 设备 - 使用 deviceModel
|
|
|
- const device = this.deviceList.find(d => d.deviceCode === objCode);
|
|
|
- if (device) {
|
|
|
- modelCode = device.modelCode || device.deviceModel;
|
|
|
- }
|
|
|
- } else if (this.form.targetObjType === 3) {
|
|
|
- // 系统
|
|
|
- const system = this.systemList.find(s => s.systemCode === objCode);
|
|
|
- if (system) {
|
|
|
- modelCode = system.modelCode;
|
|
|
+ /**
|
|
|
+ * 设备改变事件
|
|
|
+ */
|
|
|
+ handleDeviceChange(deviceCode) {
|
|
|
+ const device = this.deviceList.find(d => d.deviceCode === deviceCode)
|
|
|
+ if (device) {
|
|
|
+ this.form.targetModelCode = device.deviceModel || device.modelCode
|
|
|
+ this.form.targetObjType = 2
|
|
|
+
|
|
|
+ // 加载能力列表(仅能力调用类型需要)
|
|
|
+ if (this.form.stepType === 'ABILITY') {
|
|
|
+ this.loadAbilities(this.form.targetModelCode)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- this.form.targetModelCode = modelCode;
|
|
|
-
|
|
|
- if (modelCode) {
|
|
|
- await this.loadObjectModel(modelCode);
|
|
|
- } else {
|
|
|
- this.$message.warning('该对象未配置物模型');
|
|
|
- this.abilityList = [];
|
|
|
- }
|
|
|
+ // 重置能力和参数
|
|
|
+ this.form.abilityKey = ''
|
|
|
+ this.form.abilityParam = ''
|
|
|
+ this.currentAbility = null
|
|
|
},
|
|
|
|
|
|
- // 加载物模型(核心方法)
|
|
|
- async loadObjectModel(modelCode) {
|
|
|
- if (!modelCode) return;
|
|
|
-
|
|
|
- this.loadingModel = true;
|
|
|
+ /**
|
|
|
+ * 加载设备能力列表
|
|
|
+ */
|
|
|
+ async loadAbilities(modelCode) {
|
|
|
try {
|
|
|
- const response = await getModelByCode(modelCode);
|
|
|
- const modelData = response.data;
|
|
|
-
|
|
|
- if (!modelData) {
|
|
|
- throw new Error('物模型数据为空');
|
|
|
- }
|
|
|
-
|
|
|
- // 加载能力列表
|
|
|
- this.abilityList = modelData.abilityList || [];
|
|
|
-
|
|
|
- console.log('物模型加载成功:', {
|
|
|
- modelCode,
|
|
|
- abilityCount: this.abilityList.length
|
|
|
- });
|
|
|
-
|
|
|
- // 如果已选择能力,重新加载能力详情
|
|
|
- if (this.form.abilityKey) {
|
|
|
- this.handleAbilityChange(this.form.abilityKey);
|
|
|
- }
|
|
|
-
|
|
|
+ const res = await getModelByCode(modelCode)
|
|
|
+ this.abilityList = res.data?.abilityList?.filter(a => a.hiddenFlag === 1) || []
|
|
|
} catch (error) {
|
|
|
- console.error('加载物模型失败', error);
|
|
|
- this.$message.error('加载物模型失败: ' + (error.message || '未知错误'));
|
|
|
- this.abilityList = [];
|
|
|
- } finally {
|
|
|
- this.loadingModel = false;
|
|
|
+ console.error('加载能力失败:', error)
|
|
|
+ this.$message.error('加载能力列表失败')
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- // 能力改变
|
|
|
+ /**
|
|
|
+ * 能力改变事件
|
|
|
+ */
|
|
|
handleAbilityChange(abilityKey) {
|
|
|
- const ability = this.abilityList.find(a => a.abilityKey === abilityKey);
|
|
|
- this.currentAbility = ability;
|
|
|
- this.form.abilityName = ability?.abilityName;
|
|
|
-
|
|
|
- // 解析参数定义
|
|
|
- if (ability && ability.paramDefinition) {
|
|
|
- try {
|
|
|
- this.paramDef = JSON.parse(ability.paramDefinition);
|
|
|
- console.log('参数定义:', this.paramDef);
|
|
|
-
|
|
|
- // 初始化参数值
|
|
|
- this.initStaticParams();
|
|
|
- } catch (e) {
|
|
|
- console.error('解析参数定义失败', e);
|
|
|
- this.paramDef = null;
|
|
|
- }
|
|
|
- } else {
|
|
|
- this.paramDef = null;
|
|
|
+ this.currentAbility = this.abilityList.find(a => a.abilityKey === abilityKey)
|
|
|
+
|
|
|
+ // 重置参数
|
|
|
+ this.form.abilityParam = ''
|
|
|
+ this.form.paramMapping = ''
|
|
|
+
|
|
|
+ // 如果是 Slider 类型,设置默认值
|
|
|
+ if (this.currentAbility && this.getParamType(this.currentAbility.paramDefinition) === 'Slider') {
|
|
|
+ const min = this.getSliderMin(this.currentAbility.paramDefinition)
|
|
|
+ const max = this.getSliderMax(this.currentAbility.paramDefinition)
|
|
|
+ this.sliderValue = Math.floor((min + max) / 2)
|
|
|
+ this.form.abilityParam = String(this.sliderValue)
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- // 初始化静态参数
|
|
|
- initStaticParams() {
|
|
|
- if (!this.paramDef) return;
|
|
|
+ /**
|
|
|
+ * 参数来源改变事件
|
|
|
+ */
|
|
|
+ handleParamSourceChange() {
|
|
|
+ this.form.abilityParam = ''
|
|
|
+ this.form.paramMapping = ''
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Slider 值改变事件
|
|
|
+ */
|
|
|
+ handleSliderChange(value) {
|
|
|
+ this.form.abilityParam = String(value)
|
|
|
+ },
|
|
|
|
|
|
- // 根据参数定义类型初始化默认值
|
|
|
- if (this.paramDef.type === 'Options') {
|
|
|
- // 下拉选项,取第一个值
|
|
|
- if (this.paramDef.list && this.paramDef.list.length > 0) {
|
|
|
- this.staticParamValue = this.paramDef.list[0].value;
|
|
|
- }
|
|
|
- } else if (this.paramDef.type === 'Slider') {
|
|
|
- // 滑块,取中间值
|
|
|
- const min = this.paramDef.min || 0;
|
|
|
- const max = this.paramDef.max || 100;
|
|
|
- this.staticParamValue = Math.floor((min + max) / 2);
|
|
|
+ /**
|
|
|
+ * 步骤类型改变事件
|
|
|
+ */
|
|
|
+ handleStepTypeChange(newType) {
|
|
|
+ // 切换类型时重置相关字段
|
|
|
+ if (newType === 'ABILITY') {
|
|
|
+ // 能力调用
|
|
|
+ this.form.targetObjCode = ''
|
|
|
+ this.form.abilityKey = ''
|
|
|
+ this.form.abilityParam = ''
|
|
|
+ this.form.paramSource = 'STATIC'
|
|
|
+ } else if (newType === 'DELAY') {
|
|
|
+ // 延时等待
|
|
|
+ this.form.delaySeconds = 10
|
|
|
+ } else if (newType === 'CONDITION') {
|
|
|
+ // 条件判断
|
|
|
+ this.form.conditionExpr = ''
|
|
|
+ } else if (newType === 'LOOP') {
|
|
|
+ // 循环执行
|
|
|
+ this.form.loopMaxCount = 10
|
|
|
+ this.form.loopInterval = 1000
|
|
|
+ this.form.loopCondition = ''
|
|
|
+ } else if (newType === 'ATTR_QUERY') {
|
|
|
+ // 属性查询
|
|
|
+ this.form.targetObjCode = ''
|
|
|
+ this.form.abilityKey = '' // 用于存储属性键
|
|
|
}
|
|
|
+ },
|
|
|
|
|
|
- // 如果已有参数值,使用已有值
|
|
|
- if (this.staticParams && Object.keys(this.staticParams).length > 0) {
|
|
|
- this.staticParamValue = Object.values(this.staticParams)[0];
|
|
|
+ /**
|
|
|
+ * 获取参数类型
|
|
|
+ */
|
|
|
+ getParamType(paramDefinition) {
|
|
|
+ if (!paramDefinition) return null
|
|
|
+ try {
|
|
|
+ const def = JSON.parse(paramDefinition)
|
|
|
+ return def.type
|
|
|
+ } catch (e) {
|
|
|
+ return null
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- // 静态参数改变
|
|
|
- handleStaticParamChange(value) {
|
|
|
- // 根据能力键名构建参数对象
|
|
|
- this.staticParams = { value: value };
|
|
|
- this.form.abilityParam = JSON.stringify(this.staticParams);
|
|
|
+ /**
|
|
|
+ * 解析 Options 列表
|
|
|
+ */
|
|
|
+ parseOptions(paramDefinition) {
|
|
|
+ try {
|
|
|
+ const def = JSON.parse(paramDefinition)
|
|
|
+ if (def.type === 'Options' && def.list) {
|
|
|
+ return def.list
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('解析Options失败:', e)
|
|
|
+ }
|
|
|
+ return []
|
|
|
},
|
|
|
|
|
|
- // 参数来源改变
|
|
|
- handleParamSourceChange() {
|
|
|
- this.staticParams = {};
|
|
|
- this.paramMappingStr = '';
|
|
|
+ /**
|
|
|
+ * 获取选中的选项标签
|
|
|
+ */
|
|
|
+ getSelectedOptionLabel() {
|
|
|
+ if (!this.currentAbility || !this.form.abilityParam) return ''
|
|
|
+ const options = this.parseOptions(this.currentAbility.paramDefinition)
|
|
|
+ const option = options.find(opt => opt.value === this.form.abilityParam)
|
|
|
+ return option ? option.key : this.form.abilityParam
|
|
|
},
|
|
|
|
|
|
- // 插入变量
|
|
|
- insertVariable(varKey) {
|
|
|
- this.paramMappingStr += `\${context.${varKey}}`;
|
|
|
+ /**
|
|
|
+ * 获取 Slider 最小值
|
|
|
+ */
|
|
|
+ getSliderMin(paramDefinition) {
|
|
|
+ try {
|
|
|
+ const def = JSON.parse(paramDefinition)
|
|
|
+ return def.min || 0
|
|
|
+ } catch (e) {
|
|
|
+ return 0
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
- // 显示参数帮助
|
|
|
- showParamHelp() {
|
|
|
- this.$alert(`
|
|
|
- <div style="line-height: 1.8; font-size: 13px;">
|
|
|
- <p><b>参数定义格式:</b></p>
|
|
|
- <p><b>1. Options(下拉选择):</b></p>
|
|
|
- <pre>{
|
|
|
- "type": "Options",
|
|
|
- "list": [
|
|
|
- {"key": "开灯", "value": "1"},
|
|
|
- {"key": "关灯", "value": "0"}
|
|
|
- ]
|
|
|
-}</pre>
|
|
|
- <p><b>2. Slider(滑块):</b></p>
|
|
|
- <pre>{
|
|
|
- "type": "Slider",
|
|
|
- "min": 0,
|
|
|
- "max": 100
|
|
|
-}</pre>
|
|
|
- </div>
|
|
|
- `, '参数定义说明', {
|
|
|
- dangerouslyUseHTMLString: true,
|
|
|
- customClass: 'param-help-dialog'
|
|
|
- });
|
|
|
+ /**
|
|
|
+ * 获取 Slider 最大值
|
|
|
+ */
|
|
|
+ getSliderMax(paramDefinition) {
|
|
|
+ try {
|
|
|
+ const def = JSON.parse(paramDefinition)
|
|
|
+ return def.max || 100
|
|
|
+ } catch (e) {
|
|
|
+ return 100
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
- // 根据参数定义获取组件
|
|
|
- getParamComponent(def) {
|
|
|
- if (!def) return 'ParamInput';
|
|
|
-
|
|
|
- if (def.type === 'Options') return 'ParamOptions';
|
|
|
- if (def.type === 'Slider') return 'ParamSlider';
|
|
|
- return 'ParamInput';
|
|
|
+ /**
|
|
|
+ * 获取 Slider 单位
|
|
|
+ */
|
|
|
+ getSliderUnit(paramDefinition) {
|
|
|
+ try {
|
|
|
+ const def = JSON.parse(paramDefinition)
|
|
|
+ return def.unit || ''
|
|
|
+ } catch (e) {
|
|
|
+ return ''
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
- // 辅助方法
|
|
|
- getStepTypeName(type) {
|
|
|
- const map = {
|
|
|
- 'ABILITY': '能力调用',
|
|
|
- 'DELAY': '延时等待',
|
|
|
- 'CONDITION': '条件判断',
|
|
|
- 'PARALLEL': '并行执行',
|
|
|
- 'LOOP': '循环执行'
|
|
|
- };
|
|
|
- return map[type] || type;
|
|
|
+ /**
|
|
|
+ * 格式化 Slider 提示
|
|
|
+ */
|
|
|
+ formatSliderTooltip(value) {
|
|
|
+ const unit = this.getSliderUnit(this.currentAbility?.paramDefinition)
|
|
|
+ return unit ? `${value}${unit}` : String(value)
|
|
|
},
|
|
|
|
|
|
- getStepTypeTag(type) {
|
|
|
- const map = {
|
|
|
- 'ABILITY': '',
|
|
|
- 'DELAY': 'success',
|
|
|
- 'CONDITION': 'warning',
|
|
|
- 'PARALLEL': 'info',
|
|
|
- 'LOOP': 'danger'
|
|
|
- };
|
|
|
- return map[type] || '';
|
|
|
+ /**
|
|
|
+ * 获取 Input 单位
|
|
|
+ */
|
|
|
+ getInputUnit(paramDefinition) {
|
|
|
+ try {
|
|
|
+ const def = JSON.parse(paramDefinition)
|
|
|
+ return def.unit || ''
|
|
|
+ } catch (e) {
|
|
|
+ return ''
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
- getStepTypeIcon(type) {
|
|
|
- const map = {
|
|
|
- 'ABILITY': 'el-icon-s-operation',
|
|
|
- 'DELAY': 'el-icon-time',
|
|
|
- 'CONDITION': 'el-icon-question',
|
|
|
- 'PARALLEL': 'el-icon-sort',
|
|
|
- 'LOOP': 'el-icon-refresh'
|
|
|
- };
|
|
|
- return map[type] || 'el-icon-tickets';
|
|
|
- }
|
|
|
- },
|
|
|
+ /**
|
|
|
+ * 提交表单
|
|
|
+ */
|
|
|
+ handleSubmit() {
|
|
|
+ this.$refs.form.validate((valid) => {
|
|
|
+ if (valid) {
|
|
|
+ // 特殊验证
|
|
|
+ if (this.form.stepType === 'LOOP') {
|
|
|
+ if (this.form.loopMaxCount === 0 && !this.form.loopCondition) {
|
|
|
+ this.$message.warning('无限循环必须配置跳出条件')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // 动态参数组件
|
|
|
- components: {
|
|
|
- ConditionBuilder,
|
|
|
+ this.submitting = true
|
|
|
+ this.$emit('submit', this.form)
|
|
|
|
|
|
- ParamInput: {
|
|
|
- props: ['value', 'definition'],
|
|
|
- template: '<el-input v-model="inputValue" @input="handleInput" placeholder="请输入参数值" />',
|
|
|
- data() {
|
|
|
- return { inputValue: this.value };
|
|
|
- },
|
|
|
- watch: {
|
|
|
- value(val) { this.inputValue = val; }
|
|
|
- },
|
|
|
- methods: {
|
|
|
- handleInput(val) {
|
|
|
- this.$emit('input', val);
|
|
|
- this.$emit('change', val);
|
|
|
+ // 延迟关闭,等待外部处理
|
|
|
+ setTimeout(() => {
|
|
|
+ this.submitting = false
|
|
|
+ this.visible = false
|
|
|
+ }, 500)
|
|
|
}
|
|
|
- }
|
|
|
+ })
|
|
|
},
|
|
|
|
|
|
- ParamOptions: {
|
|
|
- props: ['value', 'definition'],
|
|
|
- template: `
|
|
|
- <el-select v-model="selectValue" @change="handleChange" style="width: 100%">
|
|
|
- <el-option
|
|
|
- v-for="opt in definition.list"
|
|
|
- :key="opt.value"
|
|
|
- :label="opt.key"
|
|
|
- :value="opt.value"
|
|
|
- >
|
|
|
- <span>{{ opt.key }}</span>
|
|
|
- <span style="float: right; color: #909399; font-size: 12px">{{ opt.value }}</span>
|
|
|
- </el-option>
|
|
|
- </el-select>
|
|
|
- `,
|
|
|
- data() {
|
|
|
- return { selectValue: this.value };
|
|
|
- },
|
|
|
- watch: {
|
|
|
- value(val) { this.selectValue = val; }
|
|
|
- },
|
|
|
- methods: {
|
|
|
- handleChange(val) {
|
|
|
- this.$emit('input', val);
|
|
|
- this.$emit('change', val);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- ParamSlider: {
|
|
|
- props: ['value', 'definition'],
|
|
|
- template: `
|
|
|
- <div>
|
|
|
- <el-slider
|
|
|
- v-model="sliderValue"
|
|
|
- @change="handleChange"
|
|
|
- :min="definition.min || 0"
|
|
|
- :max="definition.max || 100"
|
|
|
- show-input
|
|
|
- />
|
|
|
- <div style="font-size: 12px; color: #909399; margin-top: 4px;">
|
|
|
- 范围: {{ definition.min || 0 }} - {{ definition.max || 100 }}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- `,
|
|
|
- data() {
|
|
|
- return { sliderValue: this.value || 0 };
|
|
|
- },
|
|
|
- watch: {
|
|
|
- value(val) { this.sliderValue = val; }
|
|
|
- },
|
|
|
- methods: {
|
|
|
- handleChange(val) {
|
|
|
- this.$emit('input', val);
|
|
|
- this.$emit('change', val);
|
|
|
- }
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * 关闭对话框
|
|
|
+ */
|
|
|
+ handleClose() {
|
|
|
+ this.$refs.form.resetFields()
|
|
|
}
|
|
|
}
|
|
|
-};
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
-<style lang="scss" scoped>
|
|
|
-.step-config {
|
|
|
- .delay-shortcuts {
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 8px;
|
|
|
-
|
|
|
- .delay-tag {
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- background: #ecf5ff;
|
|
|
- color: #409eff;
|
|
|
- transform: scale(1.05);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .param-definition-preview {
|
|
|
- background: #f5f7fa;
|
|
|
- padding: 8px;
|
|
|
- border-radius: 4px;
|
|
|
- font-size: 12px;
|
|
|
- line-height: 1.5;
|
|
|
- max-height: 150px;
|
|
|
- overflow-y: auto;
|
|
|
- margin: 0;
|
|
|
- }
|
|
|
-
|
|
|
- .static-params-container {
|
|
|
- margin-bottom: 12px;
|
|
|
- }
|
|
|
-
|
|
|
- .variable-list {
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 8px;
|
|
|
-
|
|
|
- .variable-tag {
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
+<style scoped>
|
|
|
+.el-select {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
|
|
|
- &:hover {
|
|
|
- background: #ecf5ff;
|
|
|
- color: #409eff;
|
|
|
- transform: scale(1.05);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+.clearfix:before,
|
|
|
+.clearfix:after {
|
|
|
+ display: table;
|
|
|
+ content: "";
|
|
|
+}
|
|
|
|
|
|
- .form-tip {
|
|
|
- font-size: 12px;
|
|
|
- color: #909399;
|
|
|
- margin-top: 4px;
|
|
|
- }
|
|
|
+.clearfix:after {
|
|
|
+ clear: both;
|
|
|
}
|
|
|
</style>
|