|
|
@@ -10,13 +10,10 @@
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="步骤类型">
|
|
|
- <el-select v-model="form.stepType" style="width: 100%" disabled>
|
|
|
- <el-option label="能力调用" value="ABILITY" />
|
|
|
- <el-option label="延时等待" value="DELAY" />
|
|
|
- <el-option label="条件判断" value="CONDITION" />
|
|
|
- <el-option label="并行执行" value="PARALLEL" />
|
|
|
- <el-option label="循环执行" value="LOOP" />
|
|
|
- </el-select>
|
|
|
+ <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>
|
|
|
|
|
|
<!-- 能力调用配置 -->
|
|
|
@@ -24,19 +21,20 @@
|
|
|
<el-divider content-position="left">目标配置</el-divider>
|
|
|
|
|
|
<el-form-item label="对象类型">
|
|
|
- <el-radio-group v-model="form.targetObjType">
|
|
|
+ <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-select
|
|
|
v-model="form.targetObjCode"
|
|
|
filterable
|
|
|
placeholder="搜索选择设备"
|
|
|
style="width: 100%"
|
|
|
- @change="handleDeviceChange"
|
|
|
+ @change="handleObjChange"
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="device in deviceList"
|
|
|
@@ -50,16 +48,40 @@
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="系统代码" v-else>
|
|
|
- <el-input v-model="form.targetObjCode" placeholder="请输入系统代码" />
|
|
|
+ <!-- 系统选择 -->
|
|
|
+ <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-select
|
|
|
v-model="form.abilityKey"
|
|
|
filterable
|
|
|
placeholder="请选择要调用的能力"
|
|
|
style="width: 100%"
|
|
|
+ :loading="loadingModel"
|
|
|
+ :disabled="!form.targetObjCode"
|
|
|
@change="handleAbilityChange"
|
|
|
>
|
|
|
<el-option
|
|
|
@@ -68,61 +90,69 @@
|
|
|
:label="ability.abilityName"
|
|
|
:value="ability.abilityKey"
|
|
|
>
|
|
|
- <span>{{ ability.abilityName }}</span>
|
|
|
- <span style="color: #909399; font-size: 12px; margin-left: 8px">{{ ability.abilityKey }}</span>
|
|
|
+ <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>
|
|
|
</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-radio-group v-model="form.paramSource" @change="handleParamSourceChange">
|
|
|
<el-radio label="STATIC">静态值</el-radio>
|
|
|
<el-radio label="CONTEXT">上下文变量</el-radio>
|
|
|
- <el-radio label="ATTR">设备属性</el-radio>
|
|
|
+ <el-radio label="EXPR">表达式</el-radio>
|
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <!-- 静态参数 -->
|
|
|
- <template v-if="form.paramSource === 'STATIC'">
|
|
|
- <el-form-item
|
|
|
- v-for="param in currentAbilityParams"
|
|
|
- :key="param.paramKey"
|
|
|
- :label="param.paramName"
|
|
|
- >
|
|
|
- <template v-if="param.paramType === 'NUMBER'">
|
|
|
- <el-input-number
|
|
|
- v-model="staticParams[param.paramKey]"
|
|
|
- :min="param.min"
|
|
|
- :max="param.max"
|
|
|
- style="width: 100%"
|
|
|
- />
|
|
|
- </template>
|
|
|
- <template v-else-if="param.paramType === 'BOOLEAN'">
|
|
|
- <el-switch v-model="staticParams[param.paramKey]" />
|
|
|
- </template>
|
|
|
- <template v-else-if="param.paramType === 'SELECT'">
|
|
|
- <el-select v-model="staticParams[param.paramKey]" style="width: 100%">
|
|
|
- <el-option
|
|
|
- v-for="opt in param.options"
|
|
|
- :key="opt.value"
|
|
|
- :label="opt.label"
|
|
|
- :value="opt.value"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- </template>
|
|
|
- <template v-else>
|
|
|
- <el-input v-model="staticParams[param.paramKey]" :placeholder="param.placeholder" />
|
|
|
- </template>
|
|
|
- <div v-if="param.description" class="param-desc">{{ param.description }}</div>
|
|
|
- </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"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
|
|
|
- <el-form-item label="JSON预览">
|
|
|
+ <el-form-item label="参数预览">
|
|
|
<el-input
|
|
|
type="textarea"
|
|
|
:value="JSON.stringify(staticParams, null, 2)"
|
|
|
- :rows="4"
|
|
|
+ :rows="3"
|
|
|
readonly
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
@@ -130,43 +160,43 @@
|
|
|
|
|
|
<!-- 上下文变量映射 -->
|
|
|
<template v-if="form.paramSource === 'CONTEXT'">
|
|
|
- <el-form-item
|
|
|
- v-for="param in currentAbilityParams"
|
|
|
- :key="param.paramKey"
|
|
|
- :label="param.paramName"
|
|
|
- >
|
|
|
- <el-select
|
|
|
- v-model="paramMapping[param.paramKey]"
|
|
|
- filterable
|
|
|
- allow-create
|
|
|
- placeholder="选择上下文变量"
|
|
|
- style="width: 100%"
|
|
|
- >
|
|
|
- <el-option
|
|
|
+ <el-form-item label="变量映射">
|
|
|
+ <el-input
|
|
|
+ type="textarea"
|
|
|
+ v-model="paramMappingStr"
|
|
|
+ :rows="4"
|
|
|
+ placeholder='如: {"param1": "${context.varKey1}", "param2": "${context.varKey2}"}'
|
|
|
+ />
|
|
|
+ <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"
|
|
|
- :label="variable.varName"
|
|
|
- :value="'context.' + variable.varKey"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
+ size="small"
|
|
|
+ @click="insertVariable(variable.varKey)"
|
|
|
+ class="variable-tag"
|
|
|
+ >
|
|
|
+ {{ variable.varName }} (${context.{{ variable.varKey }}})
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
</template>
|
|
|
|
|
|
- <!-- 属性值映射 -->
|
|
|
- <template v-if="form.paramSource === 'ATTR'">
|
|
|
- <el-form-item
|
|
|
- v-for="param in currentAbilityParams"
|
|
|
- :key="param.paramKey"
|
|
|
- :label="param.paramName"
|
|
|
- >
|
|
|
- <el-cascader
|
|
|
- v-model="paramMapping[param.paramKey]"
|
|
|
- :options="deviceAttrOptions"
|
|
|
- :props="{ value: 'code', label: 'name' }"
|
|
|
- filterable
|
|
|
- placeholder="选择设备.属性"
|
|
|
- style="width: 100%"
|
|
|
+ <!-- 表达式 -->
|
|
|
+ <template v-if="form.paramSource === 'EXPR'">
|
|
|
+ <el-form-item label="参数表达式">
|
|
|
+ <el-input
|
|
|
+ type="textarea"
|
|
|
+ v-model="form.abilityParam"
|
|
|
+ :rows="4"
|
|
|
+ placeholder='支持JS表达式和变量引用'
|
|
|
/>
|
|
|
+ <div class="form-tip">
|
|
|
+ 支持:变量引用 ${varKey}、运算符、函数等
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
</template>
|
|
|
</template>
|
|
|
@@ -206,6 +236,7 @@
|
|
|
v-model="form.conditionExpr"
|
|
|
:variables="allVariables"
|
|
|
:device-list="deviceList"
|
|
|
+ :show-attr-select="true"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
|
|
|
@@ -223,14 +254,12 @@
|
|
|
<el-divider content-position="left">并行配置</el-divider>
|
|
|
|
|
|
<el-form-item label="并行步骤">
|
|
|
- <el-select
|
|
|
+ <el-input
|
|
|
+ type="textarea"
|
|
|
v-model="form.parallelSteps"
|
|
|
- multiple
|
|
|
- placeholder="选择要并行执行的步骤"
|
|
|
- style="width: 100%"
|
|
|
- >
|
|
|
- <!-- 从父组件获取所有步骤 -->
|
|
|
- </el-select>
|
|
|
+ :rows="3"
|
|
|
+ placeholder="输入步骤代码,逗号分隔"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="等待策略">
|
|
|
@@ -268,15 +297,6 @@
|
|
|
|
|
|
<el-divider content-position="left">执行控制</el-divider>
|
|
|
|
|
|
- <el-form-item label="执行条件">
|
|
|
- <el-input
|
|
|
- v-model="form.conditionExpr"
|
|
|
- type="textarea"
|
|
|
- :rows="2"
|
|
|
- placeholder="可选,满足条件才执行此步骤"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
<el-form-item label="失败重试">
|
|
|
<el-switch v-model="form.retryOnFail" :active-value="1" :inactive-value="0" />
|
|
|
</el-form-item>
|
|
|
@@ -285,16 +305,6 @@
|
|
|
<el-input-number v-model="form.retryTimes" :min="1" :max="5" />
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="重试间隔" v-if="form.retryOnFail === 1">
|
|
|
- <el-input-number v-model="form.retryInterval" :min="1" style="width: 180px" />
|
|
|
- <span style="margin-left: 8px; color: #909399">秒</span>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="失败继续">
|
|
|
- <el-switch v-model="form.continueOnFail" :active-value="1" :inactive-value="0" />
|
|
|
- <span class="form-tip">失败后是否继续执行后续步骤</span>
|
|
|
- </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>
|
|
|
@@ -312,8 +322,8 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { listAbility } from '@/api/basecfg/objAbility';
|
|
|
-import { getObjAttr } from '@/api/basecfg/objAttribute';
|
|
|
+import { getModelByCode } from '@/api/basecfg/objModel';
|
|
|
+import { listSubsystemAll } from '@/api/adapter/subsystem';
|
|
|
import ConditionBuilder from './ConditionBuilder.vue';
|
|
|
|
|
|
export default {
|
|
|
@@ -338,25 +348,36 @@ export default {
|
|
|
data() {
|
|
|
return {
|
|
|
form: {},
|
|
|
+ systemList: [],
|
|
|
abilityList: [],
|
|
|
currentAbility: null,
|
|
|
- currentAbilityParams: [],
|
|
|
+ loadingModel: false,
|
|
|
+
|
|
|
staticParams: {},
|
|
|
- paramMapping: {},
|
|
|
- deviceAttrOptions: []
|
|
|
+ staticParamValue: null,
|
|
|
+ paramMappingStr: '',
|
|
|
+ paramDef: null
|
|
|
};
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
allVariables() {
|
|
|
- // 合并上下文变量和设备属性
|
|
|
- return [
|
|
|
- ...this.contextVariables.map(v => ({
|
|
|
- key: 'context.' + v.varKey,
|
|
|
- name: v.varName,
|
|
|
- type: 'context'
|
|
|
- }))
|
|
|
- ];
|
|
|
+ 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;
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
|
|
|
@@ -376,121 +397,323 @@ export default {
|
|
|
}
|
|
|
|
|
|
if (val.paramMapping) {
|
|
|
- try {
|
|
|
- this.paramMapping = JSON.parse(val.paramMapping);
|
|
|
- } catch (e) {
|
|
|
- this.paramMapping = {};
|
|
|
- }
|
|
|
+ this.paramMappingStr = val.paramMapping;
|
|
|
}
|
|
|
|
|
|
- // 加载设备能力
|
|
|
- if (val.targetObjCode) {
|
|
|
- this.loadDeviceAbilities(val.targetObjCode);
|
|
|
+ // 加载能力列表
|
|
|
+ if (val.targetObjCode && val.targetModelCode) {
|
|
|
+ this.loadObjectModel(val.targetModelCode);
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
form: {
|
|
|
deep: true,
|
|
|
handler(val) {
|
|
|
- // 同步参数到form
|
|
|
- if (val.paramSource === 'STATIC') {
|
|
|
- val.abilityParam = JSON.stringify(this.staticParams);
|
|
|
- } else {
|
|
|
- val.paramMapping = JSON.stringify(this.paramMapping);
|
|
|
- }
|
|
|
-
|
|
|
this.$emit('change', val);
|
|
|
}
|
|
|
- },
|
|
|
- staticParams: {
|
|
|
- deep: true,
|
|
|
- handler() {
|
|
|
- if (this.form.paramSource === 'STATIC') {
|
|
|
- this.form.abilityParam = JSON.stringify(this.staticParams);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- paramMapping: {
|
|
|
- deep: true,
|
|
|
- handler() {
|
|
|
- if (this.form.paramSource !== 'STATIC') {
|
|
|
- this.form.paramMapping = JSON.stringify(this.paramMapping);
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
},
|
|
|
|
|
|
created() {
|
|
|
- this.buildDeviceAttrOptions();
|
|
|
+ this.loadSystems();
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
- async handleDeviceChange(deviceCode) {
|
|
|
- const device = this.deviceList.find(d => d.deviceCode === deviceCode);
|
|
|
- if (device) {
|
|
|
- this.form.targetModelCode = device.modelCode;
|
|
|
- await this.loadDeviceAbilities(deviceCode);
|
|
|
+ // 加载系统列表
|
|
|
+ async loadSystems() {
|
|
|
+ try {
|
|
|
+ const response = await listSubsystemAll();
|
|
|
+ this.systemList = response.data || [];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载系统列表失败', error);
|
|
|
+ this.systemList = [];
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 对象类型改变
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.form.targetModelCode = modelCode;
|
|
|
+
|
|
|
+ if (modelCode) {
|
|
|
+ await this.loadObjectModel(modelCode);
|
|
|
+ } else {
|
|
|
+ this.$message.warning('该对象未配置物模型');
|
|
|
+ this.abilityList = [];
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- async loadDeviceAbilities(deviceCode) {
|
|
|
- const device = this.deviceList.find(d => d.deviceCode === deviceCode);
|
|
|
- if (!device) return;
|
|
|
+ // 加载物模型(核心方法)
|
|
|
+ async loadObjectModel(modelCode) {
|
|
|
+ if (!modelCode) return;
|
|
|
|
|
|
+ this.loadingModel = true;
|
|
|
try {
|
|
|
- const response = await listAbility({ modelCode: device.modelCode });
|
|
|
- this.abilityList = response.rows || [];
|
|
|
+ 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);
|
|
|
}
|
|
|
+
|
|
|
} catch (error) {
|
|
|
- console.error('加载能力列表失败', error);
|
|
|
+ console.error('加载物模型失败', error);
|
|
|
+ this.$message.error('加载物模型失败: ' + (error.message || '未知错误'));
|
|
|
+ this.abilityList = [];
|
|
|
+ } finally {
|
|
|
+ this.loadingModel = false;
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ // 能力改变
|
|
|
handleAbilityChange(abilityKey) {
|
|
|
const ability = this.abilityList.find(a => a.abilityKey === abilityKey);
|
|
|
this.currentAbility = ability;
|
|
|
+ this.form.abilityName = ability?.abilityName;
|
|
|
|
|
|
- // 解析能力参数定义
|
|
|
- if (ability && ability.inputParam) {
|
|
|
+ // 解析参数定义
|
|
|
+ if (ability && ability.paramDefinition) {
|
|
|
try {
|
|
|
- this.currentAbilityParams = JSON.parse(ability.inputParam);
|
|
|
+ this.paramDef = JSON.parse(ability.paramDefinition);
|
|
|
+ console.log('参数定义:', this.paramDef);
|
|
|
+
|
|
|
+ // 初始化参数值
|
|
|
+ this.initStaticParams();
|
|
|
} catch (e) {
|
|
|
- this.currentAbilityParams = [];
|
|
|
+ console.error('解析参数定义失败', e);
|
|
|
+ this.paramDef = null;
|
|
|
}
|
|
|
} else {
|
|
|
- // 提供默认参数示例
|
|
|
- this.currentAbilityParams = [
|
|
|
- { paramKey: 'value', paramName: '参数值', paramType: 'STRING', placeholder: '请输入参数值' }
|
|
|
- ];
|
|
|
+ this.paramDef = null;
|
|
|
}
|
|
|
+ },
|
|
|
|
|
|
- // 初始化参数值
|
|
|
- this.currentAbilityParams.forEach(param => {
|
|
|
- if (!(param.paramKey in this.staticParams)) {
|
|
|
- this.$set(this.staticParams, param.paramKey, param.defaultValue || null);
|
|
|
- }
|
|
|
- if (!(param.paramKey in this.paramMapping)) {
|
|
|
- this.$set(this.paramMapping, param.paramKey, null);
|
|
|
+ // 初始化静态参数
|
|
|
+ initStaticParams() {
|
|
|
+ if (!this.paramDef) return;
|
|
|
+
|
|
|
+ // 根据参数定义类型初始化默认值
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果已有参数值,使用已有值
|
|
|
+ if (this.staticParams && Object.keys(this.staticParams).length > 0) {
|
|
|
+ this.staticParamValue = Object.values(this.staticParams)[0];
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
+ // 静态参数改变
|
|
|
+ handleStaticParamChange(value) {
|
|
|
+ // 根据能力键名构建参数对象
|
|
|
+ this.staticParams = { value: value };
|
|
|
+ this.form.abilityParam = JSON.stringify(this.staticParams);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 参数来源改变
|
|
|
handleParamSourceChange() {
|
|
|
- // 切换参数来源时清空映射
|
|
|
- this.paramMapping = {};
|
|
|
+ this.staticParams = {};
|
|
|
+ this.paramMappingStr = '';
|
|
|
},
|
|
|
|
|
|
- buildDeviceAttrOptions() {
|
|
|
- // 构建设备-属性级联选项
|
|
|
- this.deviceAttrOptions = this.deviceList.map(device => ({
|
|
|
- code: device.deviceCode,
|
|
|
- name: device.deviceName,
|
|
|
- children: [] // 延迟加载
|
|
|
- }));
|
|
|
+ // 插入变量
|
|
|
+ insertVariable(varKey) {
|
|
|
+ this.paramMappingStr += `\${context.${varKey}}`;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 显示参数帮助
|
|
|
+ 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'
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 根据参数定义获取组件
|
|
|
+ getParamComponent(def) {
|
|
|
+ if (!def) return 'ParamInput';
|
|
|
+
|
|
|
+ if (def.type === 'Options') return 'ParamOptions';
|
|
|
+ if (def.type === 'Slider') return 'ParamSlider';
|
|
|
+ return 'ParamInput';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 辅助方法
|
|
|
+ getStepTypeName(type) {
|
|
|
+ const map = {
|
|
|
+ 'ABILITY': '能力调用',
|
|
|
+ 'DELAY': '延时等待',
|
|
|
+ 'CONDITION': '条件判断',
|
|
|
+ 'PARALLEL': '并行执行',
|
|
|
+ 'LOOP': '循环执行'
|
|
|
+ };
|
|
|
+ return map[type] || type;
|
|
|
+ },
|
|
|
+
|
|
|
+ getStepTypeTag(type) {
|
|
|
+ const map = {
|
|
|
+ 'ABILITY': '',
|
|
|
+ 'DELAY': 'success',
|
|
|
+ 'CONDITION': 'warning',
|
|
|
+ 'PARALLEL': 'info',
|
|
|
+ 'LOOP': 'danger'
|
|
|
+ };
|
|
|
+ return map[type] || '';
|
|
|
+ },
|
|
|
+
|
|
|
+ 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';
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 动态参数组件
|
|
|
+ components: {
|
|
|
+ ConditionBuilder,
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
@@ -505,24 +728,52 @@ export default {
|
|
|
|
|
|
.delay-tag {
|
|
|
cursor: pointer;
|
|
|
+ transition: all 0.2s;
|
|
|
|
|
|
&:hover {
|
|
|
background: #ecf5ff;
|
|
|
color: #409eff;
|
|
|
+ transform: scale(1.05);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .param-desc {
|
|
|
+ .param-definition-preview {
|
|
|
+ background: #f5f7fa;
|
|
|
+ padding: 8px;
|
|
|
+ border-radius: 4px;
|
|
|
font-size: 12px;
|
|
|
- color: #909399;
|
|
|
- margin-top: 4px;
|
|
|
+ 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;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #ecf5ff;
|
|
|
+ color: #409eff;
|
|
|
+ transform: scale(1.05);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.form-tip {
|
|
|
font-size: 12px;
|
|
|
color: #909399;
|
|
|
- margin-left: 12px;
|
|
|
+ margin-top: 4px;
|
|
|
}
|
|
|
}
|
|
|
</style>
|