| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860 |
- <template>
- <div class="trigger-config">
- <el-form :model="form" label-width="90px" size="small">
- <el-form-item label="触发器名称">
- <el-input v-model="form.triggerName" placeholder="请输入触发器名称" @input="emitChange" />
- </el-form-item>
- <el-form-item label="触发类型">
- <el-select v-model="form.triggerType" style="width: 100%" @change="handleTypeChange">
- <el-option label="事件触发" value="EVENT">
- <i class="el-icon-lightning" style="color: #f56c6c; margin-right: 8px"></i> 事件触发
- </el-option>
- <el-option label="状态切换" value="STATE_CHANGE">
- <i class="el-icon-switch-button" style="color: #67c23a; margin-right: 8px"></i> 状态切换
- </el-option>
- <el-option label="定时触发" value="TIME">
- <i class="el-icon-time" style="color: #409eff; margin-right: 8px"></i> 定时触发
- </el-option>
- </el-select>
- </el-form-item>
- <!-- ==================== 事件触发配置 ==================== -->
- <template v-if="form.triggerType === 'EVENT'">
- <el-divider content-position="left">
- <i class="el-icon-lightning"></i> 事件源配置
- </el-divider>
- <el-form-item label="选择设备">
- <el-select
- v-model="form.sourceObjCode"
- filterable
- placeholder="搜索选择设备"
- style="width: 100%"
- @change="handleDeviceSelect"
- >
- <el-option
- v-for="device in deviceList"
- :key="device.deviceCode"
- :label="device.deviceName"
- :value="device.deviceCode"
- >
- <span>{{ device.deviceName }}</span>
- <span style="color: #909399; font-size: 12px; margin-left: 8px">{{ device.deviceCode }}</span>
- </el-option>
- </el-select>
- </el-form-item>
- <el-form-item v-if="loadingModel">
- <div style="color: #909399; font-size: 12px;">
- <i class="el-icon-loading"></i> 正在加载物模型...
- </div>
- </el-form-item>
- <el-form-item label="监听事件" v-if="form.sourceObjCode && !loadingModel">
- <el-select
- v-model="form.eventKey"
- filterable
- placeholder="请选择事件"
- style="width: 100%"
- @change="emitChange"
- >
- <el-option
- v-for="event in eventList"
- :key="event.eventKey"
- :label="event.eventName"
- :value="event.eventKey"
- >
- <div style="display: flex; justify-content: space-between; align-items: center;">
- <span>
- <i class="el-icon-bell" style="color: #f56c6c; margin-right: 4px"></i>
- {{ event.eventName }}
- </span>
- <el-tag size="mini" type="info">{{ event.eventKey }}</el-tag>
- </div>
- </el-option>
- </el-select>
- <div class="form-tip" v-if="eventList.length === 0 && form.sourceObjCode">
- ⚠️ 该设备暂无事件定义
- </div>
- </el-form-item>
- <el-form-item v-if="form.sourceObjCode && form.eventKey">
- <div class="condition-preview success">
- <i class="el-icon-info"></i>
- 当设备 <strong>{{ getDeviceName(form.sourceObjCode) }}</strong>
- 触发 <strong>{{ getEventName(form.eventKey) }}</strong> 事件时执行策略
- </div>
- </el-form-item>
- </template>
- <!-- ==================== 状态切换触发配置 ==================== -->
- <template v-if="form.triggerType === 'STATE_CHANGE'">
- <el-divider content-position="left">
- <i class="el-icon-switch-button"></i> 监控模式
- </el-divider>
- <el-form-item label="监控模式">
- <el-radio-group v-model="stateChangeMode" @change="handleModeChange">
- <el-radio-button label="PASSIVE">
- <i class="el-icon-upload2"></i> 主动上报
- </el-radio-button>
- <el-radio-button label="POLLING">
- <i class="el-icon-refresh"></i> 轮询监控
- </el-radio-button>
- </el-radio-group>
- </el-form-item>
- <el-alert
- v-if="stateChangeMode === 'PASSIVE'"
- type="info"
- :closable="false"
- show-icon
- style="margin-bottom: 16px;"
- >
- <template slot="title">
- <strong>主动上报模式</strong>:适用于设备会主动推送状态变化的场景(如4G/WiFi直连设备)
- </template>
- </el-alert>
- <el-alert
- v-if="stateChangeMode === 'POLLING'"
- type="warning"
- :closable="false"
- show-icon
- style="margin-bottom: 16px;"
- >
- <template slot="title">
- <strong>轮询监控模式</strong>:适用于需要主动查询的设备(如485设备)
- </template>
- </el-alert>
- <el-divider content-position="left">
- <i class="el-icon-aim"></i> 设备与属性
- </el-divider>
- <el-form-item label="监控设备">
- <el-select
- v-model="form.sourceObjCode"
- filterable
- placeholder="请选择监控设备"
- style="width: 100%"
- @change="handleDeviceSelect"
- >
- <el-option
- v-for="device in deviceList"
- :key="device.deviceCode"
- :label="device.deviceName"
- :value="device.deviceCode"
- >
- <span>{{ device.deviceName }}</span>
- <span style="color: #909399; font-size: 12px; margin-left: 8px">{{ device.deviceCode }}</span>
- </el-option>
- </el-select>
- </el-form-item>
- <el-form-item v-if="loadingModel">
- <div style="color: #909399; font-size: 12px;">
- <i class="el-icon-loading"></i> 正在加载物模型...
- </div>
- </el-form-item>
- <el-form-item label="监控属性" v-if="form.sourceObjCode && !loadingModel">
- <el-select
- v-model="form.attrKey"
- filterable
- placeholder="请选择监控属性"
- style="width: 100%"
- @change="handleAttrSelect"
- >
- <el-option-group
- v-for="group in attrGroups"
- :key="group.name"
- :label="group.label"
- >
- <el-option
- v-for="attr in group.attrs"
- :key="attr.attrKey"
- :label="attr.attrName"
- :value="attr.attrKey"
- >
- <div style="display: flex; justify-content: space-between; align-items: center;">
- <span>
- <i class="el-icon-document" style="color: #409eff; margin-right: 4px"></i>
- {{ attr.attrName }}
- </span>
- <div>
- <el-tag size="mini" type="info" style="margin-right: 4px">{{ attr.attrKey }}</el-tag>
- <el-tag size="mini" v-if="attr.attrUnit">{{ attr.attrUnit }}</el-tag>
- </div>
- </div>
- </el-option>
- </el-option-group>
- </el-select>
- <div class="form-tip" v-if="attrList.length === 0 && form.sourceObjCode">
- ⚠️ 该设备暂无属性定义
- </div>
- </el-form-item>
- <el-form-item v-if="form.attrKey && selectedAttr">
- <div class="attr-type-info">
- <el-tag :type="getAttrTypeTag(selectedAttr.attrValueType)" size="small">
- {{ getAttrTypeName(selectedAttr.attrValueType) }}
- </el-tag>
- <span v-if="selectedAttr.attrUnit" style="margin-left: 8px; color: #606266;">
- 单位: {{ selectedAttr.attrUnit }}
- </span>
- </div>
- </el-form-item>
- <!-- 轮询监控专属配置 -->
- <template v-if="stateChangeMode === 'POLLING'">
- <el-divider content-position="left">
- <i class="el-icon-setting"></i> 轮询参数
- </el-divider>
- <el-form-item label="轮询间隔">
- <el-input-number
- v-model="pollingConfig.interval"
- :min="1000"
- :max="60000"
- :step="500"
- style="width: 150px"
- @change="updateCondition"
- />
- <span class="unit-label">毫秒(建议≥2000ms)</span>
- </el-form-item>
- <el-form-item label="初始延迟">
- <el-input-number
- v-model="pollingConfig.initialDelay"
- :min="0"
- :max="10000"
- :step="500"
- style="width: 150px"
- @change="updateCondition"
- />
- <span class="unit-label">毫秒</span>
- </el-form-item>
- <el-divider content-position="left">
- <i class="el-icon-s-promotion"></i> 主动查询配置
- </el-divider>
- <el-form-item label="主动查询">
- <el-switch
- v-model="pollingConfig.activeQuery"
- active-text="开启"
- inactive-text="关闭"
- @change="updateCondition"
- />
- <el-tooltip content="485设备需要主动下发查询指令才能获取最新状态" placement="right">
- <i class="el-icon-question" style="margin-left: 8px; color: #909399;"></i>
- </el-tooltip>
- </el-form-item>
- <template v-if="pollingConfig.activeQuery">
- <el-form-item label="查询能力">
- <el-select
- v-model="pollingConfig.queryAbilityKey"
- placeholder="请选择用于查询状态的能力"
- style="width: 100%"
- @change="updateCondition"
- >
- <el-option
- v-for="ability in abilityList"
- :key="ability.abilityKey"
- :label="ability.abilityName"
- :value="ability.abilityKey"
- >
- <span>{{ ability.abilityName }}</span>
- <span style="float: right; color: #909399; font-size: 12px">{{ ability.abilityKey }}</span>
- </el-option>
- </el-select>
- <div class="form-tip">
- 选择物模型中定义的能力,用于主动查询设备状态(如 syncState)
- </div>
- </el-form-item>
- <el-form-item label="等待时间">
- <el-input-number
- v-model="pollingConfig.queryWaitTime"
- :min="100"
- :max="5000"
- :step="100"
- style="width: 150px"
- @change="updateCondition"
- />
- <span class="unit-label">毫秒(设备响应等待时间)</span>
- </el-form-item>
- </template>
- </template>
- <!-- 触发条件配置(两种模式共用) -->
- <el-divider content-position="left">
- <i class="el-icon-s-check"></i> 触发条件
- </el-divider>
- <el-form-item label="触发条件">
- <el-row :gutter="8">
- <el-col :span="8">
- <el-select v-model="conditionOperator" style="width: 100%" @change="updateCondition">
- <el-option label="等于 (==)" value="==" />
- <el-option label="不等于 (!=)" value="!=" />
- <el-option label="大于 (>)" value=">" />
- <el-option label="大于等于 (>=)" value=">=" />
- <el-option label="小于 (<)" value="<" />
- <el-option label="小于等于 (<=)" value="<=" />
- </el-select>
- </el-col>
- <el-col :span="16">
- <el-select
- v-if="selectedAttr && selectedAttr.attrValueType === 'Enum' && attrEnums.length > 0"
- v-model="conditionValue"
- placeholder="请选择值"
- style="width: 100%"
- @change="updateCondition"
- >
- <el-option
- v-for="enumItem in attrEnums"
- :key="enumItem.attrValue"
- :label="enumItem.attrValueName"
- :value="enumItem.attrValue"
- >
- <span>{{ enumItem.attrValueName }}</span>
- <span style="float: right; color: #909399; font-size: 12px">{{ enumItem.attrValue }}</span>
- </el-option>
- </el-select>
- <el-input
- v-else
- v-model="conditionValue"
- placeholder="请输入比较值"
- @input="updateCondition"
- >
- <template slot="prepend">{{ form.attrKey || '属性' }}</template>
- </el-input>
- </el-col>
- </el-row>
- </el-form-item>
- <!-- 轮询模式下的触发方式 -->
- <el-form-item label="触发方式" v-if="stateChangeMode === 'POLLING'">
- <el-radio-group v-model="pollingConfig.triggerMode" @change="updateCondition">
- <el-radio label="ON_FIRST_MATCH">
- <span>首次满足</span>
- <el-tooltip content="条件首次满足时触发(边沿触发),避免重复执行" placement="top">
- <i class="el-icon-question" style="margin-left: 4px; color: #909399;"></i>
- </el-tooltip>
- </el-radio>
- <el-radio label="ON_CHANGE">
- <span>值变化</span>
- <el-tooltip content="值变化且条件满足时触发" placement="top">
- <i class="el-icon-question" style="margin-left: 4px; color: #909399;"></i>
- </el-tooltip>
- </el-radio>
- <el-radio label="ALWAYS">
- <span>总是触发</span>
- <el-tooltip content="只要条件满足就触发(谨慎使用)" placement="top">
- <i class="el-icon-warning" style="margin-left: 4px; color: #E6A23C;"></i>
- </el-tooltip>
- </el-radio>
- </el-radio-group>
- </el-form-item>
- <!-- 条件预览 -->
- <el-form-item label="配置预览">
- <div class="condition-preview" :class="conditionPreviewClass">
- <i class="el-icon-info"></i>
- {{ conditionPreviewText }}
- </div>
- </el-form-item>
- </template>
- <!-- ==================== 定时触发配置 ==================== -->
- <template v-if="form.triggerType === 'TIME'">
- <el-divider content-position="left">
- <i class="el-icon-time"></i> 定时配置
- </el-divider>
- <el-form-item label="CRON表达式">
- <el-input v-model="cronExpression" placeholder="0 0 8 * * ?" @input="updateCronCondition">
- <el-button slot="append" @click="showCronHelper">
- <i class="el-icon-question"></i>
- </el-button>
- </el-input>
- </el-form-item>
- <el-form-item label="快捷选择">
- <div class="cron-shortcuts">
- <el-tag
- v-for="shortcut in cronShortcuts"
- :key="shortcut.value"
- @click="setCron(shortcut.value)"
- class="cron-tag"
- :effect="cronExpression === shortcut.value ? 'dark' : 'plain'"
- >
- {{ shortcut.label }}
- </el-tag>
- </div>
- </el-form-item>
- <el-form-item label="表达式说明" v-if="cronExpression">
- <div class="cron-desc">{{ cronDescription }}</div>
- </el-form-item>
- </template>
- <!-- ==================== 高级配置 ==================== -->
- <el-divider content-position="left">
- <i class="el-icon-setting"></i> 高级配置
- </el-divider>
- <el-form-item label="优先级">
- <el-slider v-model="form.priority" :min="0" :max="100" show-input @change="emitChange" />
- </el-form-item>
- <el-form-item label="是否启用">
- <el-switch v-model="form.enable" :active-value="1" :inactive-value="0" @change="emitChange" />
- </el-form-item>
- </el-form>
- </div>
- </template>
- <script>
- import { getModelByCode } from '@/api/basecfg/objModel'
- export default {
- name: 'TriggerConfig',
- props: {
- trigger: { type: Object, default: () => ({}) },
- deviceList: { type: Array, default: () => [] },
- // 新增:策略代码
- strategyCode: { type: String, default: '' }
- },
- data() {
- return {
- form: {
- id: null, // 数据库ID,用于更新
- strategyCode: '', // 策略代码,必需字段
- triggerName: '',
- triggerType: 'STATE_CHANGE',
- sourceObjType: 2,
- sourceObjCode: '',
- sourceModelCode: '',
- eventKey: '',
- attrKey: '',
- conditionExpr: '',
- priority: 50,
- enable: 1
- },
- stateChangeMode: 'POLLING',
- eventList: [],
- attrList: [],
- abilityList: [],
- attrEnums: [],
- loadingModel: false,
- conditionOperator: '==',
- conditionValue: '',
- cronExpression: '',
- cronShortcuts: [
- { label: '每天8点', value: '0 0 8 * * ?' },
- { label: '每天0点', value: '0 0 0 * * ?' },
- { label: '每小时', value: '0 0 * * * ?' },
- { label: '每30分钟', value: '0 */30 * * * ?' },
- { label: '每5分钟', value: '0 */5 * * * ?' },
- { label: '工作日8点', value: '0 0 8 ? * MON-FRI' }
- ],
- pollingConfig: {
- interval: 2000,
- initialDelay: 1000,
- activeQuery: true,
- queryAbilityKey: 'syncState',
- queryAbilityParam: '',
- queryWaitTime: 500,
- triggerMode: 'ON_FIRST_MATCH'
- },
- lastLoadedModelCode: ''
- }
- },
- computed: {
- selectedAttr() {
- return this.attrList.find(a => a.attrKey === this.form.attrKey)
- },
- attrGroups() {
- const groups = {}
- this.attrList.forEach(attr => {
- const groupName = attr.attrGroup || 'Default'
- if (!groups[groupName]) groups[groupName] = []
- groups[groupName].push(attr)
- })
- return Object.entries(groups).map(([name, attrs]) => ({
- name,
- label: this.getGroupLabel(name),
- attrs
- }))
- },
- cronDescription() {
- const expr = this.cronExpression
- if (!expr) return ''
- if (expr === '0 0 8 * * ?') return '每天早上8点执行'
- if (expr === '0 0 0 * * ?') return '每天凌晨0点执行'
- if (expr === '0 0 * * * ?') return '每小时整点执行'
- if (expr === '0 */30 * * * ?') return '每30分钟执行一次'
- if (expr === '0 */5 * * * ?') return '每5分钟执行一次'
- if (expr === '0 0 8 ? * MON-FRI') return '工作日早上8点执行'
- const parts = expr.split(' ')
- if (parts.length >= 6) {
- return `秒:${parts[0]} 分:${parts[1]} 时:${parts[2]} 日:${parts[3]} 月:${parts[4]} 周:${parts[5]}`
- }
- return '表达式格式不正确'
- },
- conditionPreviewText() {
- const deviceName = this.getDeviceName(this.form.sourceObjCode) || '[未选择设备]'
- const attrName = this.selectedAttr ? this.selectedAttr.attrName : (this.form.attrKey || '[未选择属性]')
- const op = this.conditionOperator || '=='
- const value = this.getConditionValueDisplay()
- if (this.stateChangeMode === 'PASSIVE') {
- return `当 ${deviceName} 的 ${attrName} ${this.getOperatorText(op)} ${value} 时执行策略(等待设备主动上报)`
- } else {
- const interval = this.pollingConfig.interval / 1000
- const queryDesc = this.pollingConfig.activeQuery
- ? `,调用 ${this.pollingConfig.queryAbilityKey} 查询`
- : ''
- const modeDesc = this.getTriggerModeText(this.pollingConfig.triggerMode)
- return `每${interval}秒检查 ${deviceName} 的 ${attrName}${queryDesc},当 ${this.getOperatorText(op)} ${value} 时${modeDesc}`
- }
- },
- conditionPreviewClass() {
- if (!this.form.sourceObjCode || !this.form.attrKey) return 'warning'
- if (!this.conditionValue) return 'warning'
- return 'success'
- }
- },
- watch: {
- trigger: {
- immediate: true,
- handler(val) {
- if (val && Object.keys(val).length > 0) {
- this.initFormFromTrigger(val)
- }
- }
- },
- // 监听 strategyCode 变化
- strategyCode: {
- immediate: true,
- handler(val) {
- if (val && !this.form.strategyCode) {
- this.form.strategyCode = val
- }
- }
- }
- },
- methods: {
- initFormFromTrigger(trigger) {
- // 确保 strategyCode 存在
- const strategyCode = trigger.strategyCode || this.strategyCode
- this.form = {
- id: trigger.id || null, // 保留数据库ID
- strategyCode: strategyCode, // 确保 strategyCode 存在
- triggerName: trigger.triggerName || '',
- triggerType: this.mapTriggerType(trigger.triggerType),
- sourceObjType: trigger.sourceObjType || 2,
- sourceObjCode: trigger.sourceObjCode || '',
- sourceModelCode: trigger.sourceModelCode || '',
- eventKey: trigger.eventKey || '',
- attrKey: trigger.attrKey || '',
- conditionExpr: trigger.conditionExpr || '',
- priority: trigger.priority || 50,
- enable: trigger.enable !== undefined ? trigger.enable : 1
- }
- if (trigger.conditionExpr) {
- this.parseConditionExpr(trigger.conditionExpr)
- }
- if (trigger.sourceModelCode && trigger.sourceModelCode !== this.lastLoadedModelCode) {
- this.loadObjectModel(trigger.sourceModelCode)
- }
- },
- mapTriggerType(oldType) {
- if (oldType === 'ATTR' || oldType === 'POLLING') {
- return 'STATE_CHANGE'
- }
- return oldType || 'STATE_CHANGE'
- },
- parseConditionExpr(expr) {
- try {
- const condition = JSON.parse(expr)
- if (condition.cron) {
- this.cronExpression = condition.cron
- } else {
- this.conditionOperator = condition.op || '=='
- this.conditionValue = condition.right || ''
- }
- if (condition.polling && condition.polling.enabled) {
- this.stateChangeMode = 'POLLING'
- this.pollingConfig = {
- interval: condition.polling.interval || 2000,
- initialDelay: condition.polling.initialDelay || 1000,
- activeQuery: condition.polling.activeQuery !== false,
- queryAbilityKey: condition.polling.queryAbilityKey ||
- (condition.polling.queryAbility && condition.polling.queryAbility.abilityKey) ||
- 'syncState',
- queryAbilityParam: condition.polling.queryAbilityParam ||
- (condition.polling.queryAbility && condition.polling.queryAbility.abilityParam) ||
- '',
- queryWaitTime: condition.polling.queryWaitTime || 500,
- triggerMode: condition.polling.triggerMode || 'ON_FIRST_MATCH'
- }
- } else {
- this.stateChangeMode = 'PASSIVE'
- }
- } catch (e) {
- console.warn('解析条件表达式失败', e)
- }
- },
- handleTypeChange() {
- this.form.eventKey = ''
- this.form.attrKey = ''
- this.form.conditionExpr = ''
- this.conditionOperator = '=='
- this.conditionValue = ''
- this.cronExpression = ''
- this.eventList = []
- this.attrList = []
- this.abilityList = []
- this.attrEnums = []
- this.emitChange()
- },
- handleModeChange(mode) {
- this.stateChangeMode = mode
- this.updateCondition()
- },
- handleDeviceSelect(deviceCode) {
- const device = this.deviceList.find(d => d.deviceCode === deviceCode)
- if (!device) return
- const modelCode = device.deviceModel || device.modelCode
- this.form.sourceModelCode = modelCode || ''
- this.form.sourceObjType = 2
- this.form.eventKey = ''
- this.form.attrKey = ''
- this.eventList = []
- this.attrList = []
- this.abilityList = []
- this.attrEnums = []
- if (modelCode && modelCode !== this.lastLoadedModelCode) {
- this.loadObjectModel(modelCode)
- } else if (!modelCode) {
- this.$message.warning('该设备未配置物模型')
- }
- this.emitChange()
- },
- async loadObjectModel(modelCode) {
- if (!modelCode) return
- if (modelCode === this.lastLoadedModelCode) return
- if (this.loadingModel) return
- this.loadingModel = true
- this.lastLoadedModelCode = modelCode
- try {
- const response = await getModelByCode(modelCode)
- const modelData = response.data
- if (!modelData) {
- this.$message.warning('物模型数据为空')
- return
- }
- this.attrList = modelData.attrList || []
- this.eventList = modelData.eventList || []
- this.abilityList = modelData.abilityList || []
- if (this.abilityList.length > 0) {
- const syncAbility = this.abilityList.find(a => a.abilityKey === 'syncState')
- if (syncAbility && !this.pollingConfig.queryAbilityKey) {
- this.pollingConfig.queryAbilityKey = 'syncState'
- }
- }
- } catch (error) {
- console.error('加载物模型失败', error)
- this.$message.error('加载物模型失败')
- this.attrList = []
- this.eventList = []
- this.abilityList = []
- this.lastLoadedModelCode = ''
- } finally {
- this.loadingModel = false
- }
- },
- handleAttrSelect(attrKey) {
- const attr = this.attrList.find(a => a.attrKey === attrKey)
- this.conditionValue = ''
- this.conditionOperator = '=='
- if (attr && attr.attrValueType === 'Enum' && attr.valueEnums) {
- this.attrEnums = attr.valueEnums
- } else {
- this.attrEnums = []
- }
- this.updateCondition()
- },
- updateCondition() {
- if (this.form.triggerType === 'STATE_CHANGE') {
- const condition = {
- left: this.form.attrKey,
- op: this.conditionOperator,
- right: this.conditionValue
- }
- if (this.stateChangeMode === 'POLLING') {
- condition.polling = {
- enabled: true,
- interval: this.pollingConfig.interval,
- initialDelay: this.pollingConfig.initialDelay,
- activeQuery: this.pollingConfig.activeQuery,
- queryWaitTime: this.pollingConfig.queryWaitTime,
- triggerMode: this.pollingConfig.triggerMode
- }
- if (this.pollingConfig.activeQuery) {
- condition.polling.queryAbility = {
- abilityKey: this.pollingConfig.queryAbilityKey,
- abilityParam: this.pollingConfig.queryAbilityParam
- }
- }
- }
- this.form.conditionExpr = JSON.stringify(condition)
- }
- this.emitChange()
- },
- updateCronCondition() {
- this.form.conditionExpr = JSON.stringify({ cron: this.cronExpression })
- this.emitChange()
- },
- setCron(value) {
- this.cronExpression = value
- this.updateCronCondition()
- },
- showCronHelper() {
- this.$alert(`
- <div style="line-height: 1.8">
- <p><b>CRON表达式格式:</b>秒 分 时 日 月 周</p>
- <p><b>常用示例:</b></p>
- <p>• 0 0 8 * * ? - 每天8点执行</p>
- <p>• 0 0/30 * * * ? - 每30分钟执行</p>
- <p>• 0 0 9-18 * * ? - 每天9-18点整点执行</p>
- <p>• 0 0 8 ? * MON-FRI - 工作日8点执行</p>
- </div>
- `, 'CRON表达式帮助', { dangerouslyUseHTMLString: true })
- },
- emitChange() {
- // 构建完整的表单数据,确保包含 strategyCode
- const formData = {
- ...this.form,
- strategyCode: this.form.strategyCode || this.strategyCode // 双重保障
- }
- // 转换回后端需要的 triggerType
- if (formData.triggerType === 'STATE_CHANGE') {
- formData.triggerType = this.stateChangeMode === 'POLLING' ? 'POLLING' : 'ATTR'
- }
- this.$emit('change', formData)
- },
- getDeviceName(deviceCode) {
- const device = this.deviceList.find(d => d.deviceCode === deviceCode)
- return device ? device.deviceName : deviceCode
- },
- getEventName(eventKey) {
- const event = this.eventList.find(e => e.eventKey === eventKey)
- return event ? event.eventName : eventKey
- },
- getConditionValueDisplay() {
- if (!this.conditionValue) return '[未设置]'
- if (this.attrEnums.length > 0) {
- const enumItem = this.attrEnums.find(e => e.attrValue === this.conditionValue)
- if (enumItem) return enumItem.attrValueName
- }
- return this.conditionValue
- },
- getOperatorText(op) {
- const map = { '==': '等于', '!=': '不等于', '>': '大于', '>=': '大于等于', '<': '小于', '<=': '小于等于' }
- return map[op] || op
- },
- getTriggerModeText(mode) {
- const map = { 'ON_FIRST_MATCH': '触发(首次满足)', 'ON_CHANGE': '触发(值变化时)', 'ALWAYS': '触发(每次检查)' }
- return map[mode] || '触发'
- },
- getGroupLabel(groupName) {
- const labelMap = { 'Base': '基础信息', 'State': '状态信息', 'Protocol': '协议信息', 'Measure': '测量数据', 'Control': '控制参数', 'Default': '其他' }
- return labelMap[groupName] || groupName
- },
- getAttrTypeName(type) {
- const typeMap = { 'Value': '数值', 'String': '字符串', 'Enum': '枚举', 'Boolean': '布尔' }
- return typeMap[type] || type
- },
- getAttrTypeTag(type) {
- const tagMap = { 'Value': '', 'String': 'info', 'Enum': 'warning', 'Boolean': 'success' }
- return tagMap[type] || ''
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .trigger-config {
- .el-divider { margin: 16px 0 12px; }
- .form-tip { font-size: 12px; color: #909399; margin-top: 4px; }
- .unit-label { margin-left: 8px; color: #909399; font-size: 12px; }
- .attr-type-info {
- padding: 8px 12px;
- background: #f5f7fa;
- border-radius: 6px;
- display: flex;
- align-items: center;
- }
- .cron-shortcuts {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- .cron-tag { cursor: pointer; transition: all 0.2s; &:hover { transform: scale(1.05); } }
- }
- .cron-desc {
- font-size: 12px;
- color: #67c23a;
- padding: 8px 12px;
- background: #f0f9eb;
- border-radius: 4px;
- }
- .condition-preview {
- padding: 12px 14px;
- border-radius: 6px;
- font-size: 13px;
- line-height: 1.6;
- i { margin-right: 6px; }
- strong { color: #409eff; }
- &.success {
- background: linear-gradient(135deg, #f0f9eb 0%, #e8f8e0 100%);
- border-left: 3px solid #67c23a;
- color: #606266;
- }
- &.warning {
- background: linear-gradient(135deg, #fef0e5 0%, #fdf6ec 100%);
- border-left: 3px solid #e6a23c;
- color: #909399;
- }
- }
- }
- </style>
|