浏览代码

feat: 联动规则

lvkun 3 年之前
父节点
当前提交
b1a750c4c6
共有 5 个文件被更改,包括 1100 次插入7 次删除
  1. 7 0
      src/api/iot/rule.ts
  2. 10 1
      src/controller/iot/rule.ts
  3. 4 5
      src/pages/Iot/rule/linkRule.vue
  4. 1078 0
      src/pages/Iot/rule/linkRules.vue
  5. 1 1
      src/router/index.ts

+ 7 - 0
src/api/iot/rule.ts

@@ -104,6 +104,13 @@ export const addLink = (data: IOT.API.RULE.LinkRuleBody) => {
   })
 }
 
+export const getLinkPageById = (id: string) => {
+  return request<IOT.API.RULE.LinkRule>({
+    url: `/linkRule/${id}`,
+    method: 'GET'
+  })
+}
+
 export const getLinkPage = (params: {page: number, pageSize: number, ruleLabel?: string}) => {
   return request<IOT.API.RULE.LinkRule[]>({
     url: '/linkRule/page',

+ 10 - 1
src/controller/iot/rule.ts

@@ -1,4 +1,4 @@
-import { addForwardRule, addLink, delForward, delLink, getForwardById, getForwardRulePage, getLinkPage, updateForward, updateForwardStatus, updateLinkStatus } from '@/api/iot/rule'
+import { addForwardRule, addLink, delForward, delLink, getForwardById, getForwardRulePage, getLinkPage, getLinkPageById, updateForward, updateForwardStatus, updateLink, updateLinkStatus } from '@/api/iot/rule'
 import { SubjectEventEnum, SubjectResourceEnum } from '@/enum/common'
 import { message } from 'ant-design-vue'
 
@@ -64,8 +64,17 @@ export class RuleController {
     message.success('删除成功')
   }
 
+  static async updateLink (data: any) {
+    await updateLink(data)
+    message.success('修改成功')
+  }
+
   static async addLink (data: any) {
     await addLink(data)
     message.success('新增成功')
   }
+
+  static async getLinkById (id: string) {
+    return await getLinkPageById(id)
+  }
 }

+ 4 - 5
src/pages/Iot/rule/linkRule.vue

@@ -2,7 +2,6 @@
   <a-card>
     <a-row justify="space-between" >
       <a-col>
-
       </a-col>
       <a-col>
         <a-space>
@@ -104,7 +103,7 @@
                 >
                 <a-row :gutter="[8, 8]" >
                   <a-col>
-                        <!-- 选择条件 -->
+                  <!-- 选择条件 -->
                   <a-select
                     style="width: 170px"
                     v-model:value="item.conditionType"
@@ -407,7 +406,7 @@
 <script lang='ts' setup >
 import { FormItemProps } from '@/components/FormPro/index.vue'
 import { ModelAttrController, ModelCmdController, ModelController, RuleController } from '@/controller'
-import { computed, onMounted, reactive, ref, watch } from 'vue'
+import { onMounted, reactive, ref } from 'vue'
 import { DownOutlined } from '@ant-design/icons-vue'
 import SelectDevice from './components/selectDevice.vue'
 
@@ -511,7 +510,7 @@ const queryParamsState = reactive({
   ruleLabel: ''
 })
 
-const initConditionsData = {
+const initConditionsData = reactive({
   conditionType: 'DEVICE_DATA',
   modelId: '',
   deviceType: '',
@@ -528,7 +527,7 @@ const initConditionsData = {
   value: '',
   v1: '',
   v2: ''
-}
+})
 
 const initActionsData = {
   actionType: 'DEVICE_CMD',

+ 1078 - 0
src/pages/Iot/rule/linkRules.vue

@@ -0,0 +1,1078 @@
+<template>
+  <a-card>
+    <a-row justify="space-between" >
+      <a-col>
+      </a-col>
+      <a-col>
+        <a-space>
+          <a-button type="primary" @click="openModal('add', {})" >创建规则</a-button>
+        </a-space>
+      </a-col>
+    </a-row>
+    <a-table
+      style="margin-top: 20px;"
+      :columns="columns"
+      :data-source="state.dataSource"
+      :loading="state.loading"
+      :pagination="queryParamsState"
+      @change="changePage"
+    >
+    <template #bodyCell="{column, record}" >
+      <template v-if="column.key === 'status'" >
+        <a-switch
+          v-model:checked="record.status"
+          checked-children="运行中"
+          un-checked-children="已停止"
+          @click="changeStatus(record)"
+        />
+      </template>
+      <template  v-if="column.key === 'action'" >
+        <a-space>
+          <a @click="openModal('update', record)" >编辑</a>
+          <a-popconfirm
+                  title="确实要删除吗?"
+                  ok-text="确定"
+                  cancel-text="取消"
+                  @confirm="delLinkRule(record.id)"
+                >
+                  <a>删除</a>
+                </a-popconfirm>
+        </a-space>
+      </template>
+    </template>
+    </a-table>
+  </a-card>
+
+  <modal-pro
+    style="width: 1400px;"
+    label="创建转发规则"
+    :visible="state.visible"
+    @cancel="state.visible = false"
+    @ok="ok"
+  >
+    <div style="height: 750px;overflow: hidden;overflow-y: auto;">
+      <a-card
+        title="基本信息"
+        :bordered="false"
+      >
+        <a-form :labelCol="{span: 2}" :wapperCol="{span: 12}" >
+          <a-form-item label="规则名称"  v-bind="validateInfos.ruleLabel">
+            <a-input v-model:value="bodyParamsState.ruleLabel" ></a-input>
+          </a-form-item >
+          <a-form-item  label='规则描述'>
+            <a-textarea
+              v-model:value="bodyParamsState.ruleDescription"
+              placeholder="请输入规则描述"
+              :auto-size="{ minRows: 2, maxRows: 5 }"
+            />
+          </a-form-item>
+        </a-form>
+
+        <!-- <form-pro
+          :labelCol="{span: 2}"
+          :formProps="formProps"
+          ref="formProRef"
+        /> -->
+      </a-card>
+      <a-card
+        title="触发条件"
+        :bordered="false"
+      >
+        <a-row class="condition" >
+          <a-col span="12" >
+            需满足
+            <a-dropdown >
+              <a class="ant-dropdown-link" @click.prevent>
+                {{bodyParamsState.conditionLogic === 'AND' ? '全部' : '任意一个'}}
+                <DownOutlined />
+              </a>
+              <template #overlay>
+                <a-menu>
+                  <a-menu-item @click="changeConditionLogic('AND')" >
+                    <a >全部</a>
+                  </a-menu-item>
+                  <a-menu-item @click="changeConditionLogic('ALL')">
+                    <a >任意一个</a>
+                  </a-menu-item>
+                </a-menu>
+              </template>
+            </a-dropdown>
+            以下条件:
+          </a-col>
+          <a-col class="df" span="12" >
+            <a-button type="primary" @click="openFormVisible('conditions')">添加条件</a-button>
+          </a-col>
+          <a-col :span="24"  v-if="deviceDataSource.length" >
+            <a-table
+              style="width: 100%;margin-top: 10px;"
+              :columns="conditionColumns.DEVICE_DATA"
+              :data-source="deviceDataSource"
+              size="small"
+              :pagination="false"
+            >
+              <template #bodyCell="{column, record}" >
+                <template v-if="column.key === 'action'" >
+                  <a @click="delCondiTionsAndActions(record.id, 'conditions')" >
+                    删除
+                  </a>
+                </template>
+              </template>
+            </a-table>
+          </a-col>
+          <a-col :span="24" v-if="deviceSessionSource.length" >
+            <a-table
+              style="width: 100%;margin-top: 10px;"
+              :columns="conditionColumns.DEVICE_SESSION"
+              :data-source="deviceSessionSource"
+              size="small"
+              :pagination="false"
+            >
+              <template #bodyCell="{column, record}" >
+                <template v-if="column.key === 'sessionEventType'" >
+                {{record.sessionEventType === 'CONNECT' ? '连接' : '断开连接'  }}
+                </template>
+                <template v-if="column.key === 'action'" >
+                  <a @click="delCondiTionsAndActions(record.id, 'conditions')" >
+                    删除
+                  </a>
+                </template>
+              </template>
+            </a-table>
+          </a-col>
+          <a-col :span="24" v-if="dailyTimerSource.length" >
+            <a-table
+              style="width: 100%;margin-top: 10px;"
+              :columns="conditionColumns.DAILY_TIMER"
+              :data-source="dailyTimerSource"
+              size="small"
+              :pagination="false"
+            >
+              <template #bodyCell="{column, record}" >
+                <template v-if="column.key === 'dayOfWeek'" >
+                {{record.dayOfWeek.map(item => '周' + item).join(',') }}
+                </template>
+                <template v-if="column.key === 'time'" >
+                {{record.time}}
+                </template>
+                <template v-if="column.key === 'action'" >
+                  <a @click="delCondiTionsAndActions(record.id, 'conditions')" >
+                    删除
+                  </a>
+                </template>
+              </template>
+            </a-table>
+          </a-col>
+          <a-col  v-if="bodyParamsState.conditions.length === 0" class="content"  >尚未设置条件</a-col>
+        </a-row>
+      </a-card>
+      <a-card
+        title="执行动作"
+        :bordered="false"
+      >
+      <a-row  style="width: 100%;" >
+        <a-col :span="24" class="df" ><a-button type="primary" @click="openFormVisible('actions')" >添加动作</a-button></a-col>
+        <a-row style="width: 100%;" v-if="bodyParamsState.actions.length">
+          <a-col :span="24"  v-if="deviceCmdsource.length" >
+            <a-table
+              style="width: 100%;margin-top: 10px;"
+              :columns="actionsColumns.DEVICE_CMD"
+              :data-source="deviceCmdsource"
+              size="small"
+              :pagination="false"
+            >
+              <template #bodyCell="{column, record}" >
+                <template v-if="column.key === 'action'" >
+                  <a @click="delCondiTionsAndActions(record.id, 'actions')" >
+                    删除
+                  </a>
+                </template>
+              </template>
+            </a-table>
+          </a-col>
+          <a-col :span="24"  v-if="reportWarnSource.length" >
+            <a-table
+              style="width: 100%;margin-top: 10px;"
+              :columns="actionsColumns.REPORT_WARN"
+              :data-source="reportWarnSource"
+              size="small"
+              :pagination="false"
+            >
+              <template #bodyCell="{column, record}" >
+                <template v-if="column.key === 'action'" >
+                  <a @click="delCondiTionsAndActions(record.id, 'actions')" >
+                    删除
+                  </a>
+                </template>
+              </template>
+            </a-table>
+          </a-col>
+          <a-col :span="24"  v-if="resumeWarnSource.length" >
+            <a-table
+              style="width: 100%;margin-top: 10px;"
+              :columns="actionsColumns.RESUME_WARN"
+              :data-source="resumeWarnSource"
+              size="small"
+              :pagination="false"
+            >
+              <template #bodyCell="{column, record}" >
+                <template v-if="column.key === 'action'" >
+                  <a @click="delCondiTionsAndActions(record.id, 'actions')" >
+                    删除
+                  </a>
+                </template>
+              </template>
+            </a-table>
+          </a-col>
+          <a-col :span="24"  v-if="noticeSource.length" >
+            <a-table
+              style="width: 100%;margin-top: 10px;"
+              :columns="actionsColumns.NOTICE"
+              :data-source="noticeSource"
+              size="small"
+              :pagination="false"
+            >
+              <template #bodyCell="{column, record}" >
+                <template v-if="column.key === 'action'" >
+                  <a @click="delCondiTionsAndActions(record.id, 'actions')" >
+                    删除
+                  </a>
+                </template>
+              </template>
+            </a-table>
+          </a-col>
+        </a-row>
+        <a-col  class="content"  v-if="bodyParamsState.actions.length === 0"   >尚未设置动作</a-col>
+      </a-row>
+      </a-card>
+    </div>
+  </modal-pro>
+
+  <modal-pro
+    label="选择"
+    :visible="state.formVisible"
+    @cancel="state.formVisible = false"
+    @ok="selectConditionAndAction"
+    style="width: 700px;"
+  >
+    <div  style="width: 100%;" v-if="state.opraModel === 'conditions'" >
+      <a-row :gutter="[8, 8]" style="width: 100%;" >
+        <a-form
+           style="width: 100%;"
+          :label-col="{ span: 3 }"
+          :wrapper-col="{ span: 16 }"
+        >
+          <a-col>
+            <a-form-item label="触发条件" >
+              <a-select
+                style="width: 170px"
+                v-model:value="initConditionsData.conditionType"
+              >
+                <a-select-option
+                  v-for="itemType in conditionTypeList"
+                  :key="itemType.key"
+                  :value="itemType.key"
+                >
+                {{itemType.name}}
+                </a-select-option>
+              </a-select>
+            </a-form-item>
+            <span v-if="initConditionsData.conditionType === 'DEVICE_DATA'" >
+              <a-form-item label="产品" >
+              <a-select
+                style="width: 170px"
+                placeholder="请选择产品"
+                v-model:value="initConditionsData.modelId"
+              >
+                <a-select-option
+                  v-for="model in state.modelList"
+                  :key="model.id"
+                  :value="model.id"
+                >
+                  {{model.modelLabel}}
+                </a-select-option>
+              </a-select>
+            </a-form-item>
+            <a-form-item label="设备类型" >
+              <a-space>
+                <a-select
+                  style="width: 170px"
+                  placeholder="请选择设备"
+                  v-model:value="initConditionsData.deviceType"
+                >
+                  <a-select-option
+                    v-for="deviceItem in selectDeviceList"
+                    :key="deviceItem.key"
+                    :value="deviceItem.key"
+                  >
+                    {{deviceItem.name}}
+                  </a-select-option>
+                </a-select>
+                <a-tag color="blue" v-if="initConditionsData.deviceLabel" >{{initConditionsData.deviceLabel}}</a-tag>
+                <a-button
+                  type="primary"
+                  v-if="initConditionsData.deviceType === 'target'"
+                  @click="openDeviceModal('conditions')"
+                >
+                  {{initConditionsData.deviceId ? '重新选择' : '请选择设备'}}
+                </a-button>
+              </a-space>
+            </a-form-item>
+            <a-form-item label="属性key" >
+              <a-select
+                style="width: 170px"
+                placeholder="请选择属性key"
+                v-model:value="initConditionsData.attributeKey"
+              >
+                <a-select-option
+                  v-for="attrItem in state.attrList"
+                  :key="attrItem.attributeKey"
+                  :value="attrItem.attributeKey"
+                >
+                  {{attrItem.attributeKey}}
+                </a-select-option>
+              </a-select>
+            </a-form-item>
+            <a-form-item label="操作符" >
+              <a-select
+                style="width: 170px"
+                placeholder="请选择操作符"
+                v-model:value="initConditionsData.operator"
+              >
+                <a-select-option
+                  v-for="operaItem in operatorList"
+                  :key="operaItem"
+                  :value="operaItem"
+                >
+                  {{operaItem}}
+                </a-select-option>
+              </a-select>
+            </a-form-item>
+            <a-form-item label="触发值" >
+              <a-input
+                v-if="initConditionsData.operator !== 'BETWEEN'"
+                style="width: 170px;"
+                placeholder="请选择触发值"
+                v-model:value="initConditionsData.value"
+              ></a-input>
+              <a-input-group compact v-else >
+                <a-input placeholder="值1" v-model:value="initConditionsData.v1" style="width: 20%" />
+                <a-input placeholder="值2" v-model:value="initConditionsData.v2" style="width: 30%" />
+              </a-input-group>
+            </a-form-item>
+            </span>
+            <span v-else-if="initConditionsData.conditionType === 'DEVICE_SESSION'" >
+              <a-form-item label="产品">
+                <a-select
+                  style="width: 170px"
+                  placeholder="请选择产品"
+                  v-model:value="initConditionsData.modelId"
+                >
+                  <a-select-option
+                    v-for="model in state.modelList"
+                    :key="model.id"
+                    :value="model.id"
+                  >
+                    {{model.modelLabel}}
+                  </a-select-option>
+                </a-select>
+              </a-form-item>
+              <a-form-item label="设备类型" >
+                <a-space>
+                  <a-select
+                    style="width: 170px"
+                    placeholder="请选择设备"
+                    v-model:value="initConditionsData.deviceType"
+                  >
+                    <a-select-option
+                      v-for="deviceItem in selectDeviceList"
+                      :key="deviceItem.key"
+                      :value="deviceItem.key"
+                    >
+                      {{deviceItem.name}}
+                    </a-select-option>
+                  </a-select>
+                  <a-tag color="blue" v-if="initConditionsData.deviceLabel" >{{initConditionsData.deviceLabel}}</a-tag>
+                  <a-button
+                    type="primary"
+                    v-if="initConditionsData.deviceType === 'target'"
+                    @click="openDeviceModal('conditions')"
+                  >
+                    {{initConditionsData.deviceId ? '重新选择' : '请选择设备'}}
+                  </a-button>
+                </a-space>
+              </a-form-item>
+              <a-form-item label="是否连接" >
+                <a-select
+                  style="width: 170px"
+                  placeholder="请选择session"
+                  v-model:value="initConditionsData.sessionEventType"
+                >
+                  <a-select-option
+                    v-for="essionEventTypeItem in sessionEventTypeList"
+                    :key="essionEventTypeItem.key"
+                    :value="essionEventTypeItem.key"
+                  >
+                    {{essionEventTypeItem.name}}
+                  </a-select-option>
+                </a-select>
+              </a-form-item>
+            </span>
+            <span v-else-if="initConditionsData.conditionType === 'DAILY_TIMER'" >
+              <a-form-item label="时间" >
+                <a-checkbox-group v-model:value="initConditionsData.dayOfWeek" :options="dayOptions" />
+              </a-form-item>
+              <a-form-item label="日期" >
+                <a-time-picker v-model:value="initConditionsData.time" value-format="HH:mm:ss" />
+              </a-form-item>
+            </span>
+          </a-col>
+
+        </a-form>
+      </a-row>
+    </div>
+    <div v-else  style="width: 100%;" >
+      <a-row :gutter="[8, 8]" style="width: 100%;" >
+        <a-form
+           style="width: 100%;"
+          :label-col="{ span: 3 }"
+          :wrapper-col="{ span: 16 }"
+        >
+          <a-col>
+            <a-form-item label="触发动作" >
+              <a-select
+                style="width: 170px"
+                v-model:value="initActionsData.actionType"
+              >
+                  <a-select-option
+                    v-for="actionItem in actionTypeList"
+                    :key="actionItem.key"
+                    :value="actionItem.key"
+                  >
+                  {{actionItem.name}}
+                  </a-select-option>
+              </a-select>
+            </a-form-item>
+          </a-col>
+          <span v-if="initActionsData.actionType === 'DEVICE_CMD'" >
+            <a-col>
+            <a-form-item label="选择产品" >
+              <a-select
+                  style="width: 170px"
+                  placeholder="请选择产品"
+                  v-model:value="initActionsData.modelId"
+                >
+                <a-select-option
+                  v-for="model in state.modelList"
+                  :key="model.id"
+                  :value="model.id"
+                >
+                  {{model.modelLabel}}
+                </a-select-option>
+              </a-select>
+            </a-form-item>
+            <a-form-item label="设备类型" >
+              <a-space>
+                <a-select
+                  style="width: 170px"
+                  placeholder="请选择设备"
+                  v-model:value="initActionsData.deviceType"
+                >
+                  <a-select-option
+                    v-for="deviceItem in selectDeviceList"
+                    :key="deviceItem.key"
+                    :value="deviceItem.key"
+                  >
+                    {{deviceItem.name}}
+                  </a-select-option>
+                </a-select>
+                <a-tag color="blue" v-if="initActionsData.deviceLabel" >{{initActionsData.deviceLabel}}</a-tag>
+                <a-button
+                  type="primary"
+                  v-if="initActionsData.deviceType === 'target'"
+                  @click="openDeviceModal('actions')"
+                >
+                  {{initActionsData.deviceId ? '重新选择' : '请选择设备'}}
+                </a-button>
+              </a-space>
+            </a-form-item>
+            <a-form-item label="选择命令" >
+              <a-select
+                style="width: 170px;"
+                v-model:value="initActionsData.cmdId"
+              >
+                  <a-select-option
+                    v-for="cmdItem in state.cmdList"
+                    :key="cmdItem.id"
+                    :value="cmdItem.id"
+                  >
+                  {{cmdItem.cmdLabel}}
+                  </a-select-option>
+                </a-select>
+            </a-form-item>
+            <a-form-item label="命令参数" >
+                <div
+                  v-for="(item, index) in initActionsData.cmdParameters"
+                  :key="index"
+                  style="margin-bottom: 10px;"
+                >
+                  <a-input-group compact  >
+                    <a-input placeholder="key"  disabled v-model:value="item.paramLabel" style="width: 50%" />
+                    <a-input placeholder="value" v-model:value="item.dataUnit" style="width: 50%" />
+                  </a-input-group>
+                </div>
+            </a-form-item>
+            </a-col>
+          </span>
+          <span v-else-if="initActionsData.actionType === 'REPORT_WARN'">
+            <a-form-item label="设备类型" >
+              <a-space>
+                  <a-select
+                    style="width: 170px"
+                    placeholder="请选择设备"
+                    v-model:value="initActionsData.deviceType"
+                  >
+                    <a-select-option
+                      v-for="deviceItem in selectDeviceList"
+                      :key="deviceItem.key"
+                      :value="deviceItem.key"
+                    >
+                      {{deviceItem.name}}
+                    </a-select-option>
+                  </a-select>
+                  <a-tag color="blue" v-if="initActionsData.deviceLabel" >{{initActionsData.deviceLabel}}</a-tag>
+                  <a-button
+                    type="primary"
+                    v-if="initActionsData.deviceType === 'target'"
+                    @click="openDeviceModal('actions')"
+                  >
+                    {{initActionsData.deviceId ? '重新选择' : '请选择设备'}}
+                  </a-button>
+                </a-space>
+            </a-form-item>
+            <a-form-item  label="告警名称" >
+              <a-input v-model:value="initActionsData.warnLabel" ></a-input>
+            </a-form-item>
+            <a-form-item  label="告警描述" >
+              <a-input v-model:value="initActionsData.warnDescription" ></a-input>
+            </a-form-item>
+            <a-form-item  label="告警级别" >
+              <a-select  v-model:value="initActionsData.warnSeverity" >
+                <a-select-option
+                  v-for='warnItem in warnSeverityList'
+                  :key="warnItem.key"
+                  :value="warnItem.key"
+                >
+                  {{warnItem.name}}
+                </a-select-option>
+              </a-select>
+            </a-form-item>
+          </span>
+          <span v-else-if="initActionsData.actionType === 'RESUME_WARN'">
+            <a-form-item label="设备类型" >
+              <a-space>
+                  <a-select
+                    style="width: 170px"
+                    placeholder="请选择设备"
+                    v-model:value="initActionsData.deviceType"
+                  >
+                    <a-select-option
+                      v-for="deviceItem in selectDeviceList"
+                      :key="deviceItem.key"
+                      :value="deviceItem.key"
+                    >
+                      {{deviceItem.name}}
+                    </a-select-option>
+                  </a-select>
+                  <a-tag color="blue" v-if="initActionsData.deviceLabel" >{{initActionsData.deviceLabel}}</a-tag>
+                  <a-button
+                    type="primary"
+                    v-if="initActionsData.deviceType === 'target'"
+                    @click="openDeviceModal('actions')"
+                  >
+                    {{initActionsData.deviceId ? '重新选择' : '请选择设备'}}
+                  </a-button>
+                </a-space>
+            </a-form-item>
+            <a-form-item  label="告警名称" >
+              <a-input v-model:value="initActionsData.warnLabel" ></a-input>
+            </a-form-item>
+            <a-form-item  label="告警级别" >
+              <a-select v-model:value="initActionsData.warnSeverity" >
+                <a-select-option
+                  v-for='warnItem in warnSeverityList'
+                  :key="warnItem.key"
+                  :value="warnItem.key"
+                >
+                  {{warnItem.name}}
+                </a-select-option>
+              </a-select>
+            </a-form-item>
+          </span>
+          <span v-else-if="initActionsData.actionType === 'NOTICE'">
+            <a-form-item label="提示名称" >
+              <a-input v-model:value="initActionsData.noticeLabel" ></a-input>
+            </a-form-item>
+            <a-form-item label="提示描述" >
+              <a-input v-model:value="initActionsData.noticeDescription" ></a-input>
+            </a-form-item>
+            <a-form-item label="提示用户" >
+              <a-input v-model:value="initActionsData.userId" ></a-input>
+            </a-form-item>
+          </span>
+        </a-form>
+      </a-row>
+    </div>
+  </modal-pro>
+
+  <modal-pro
+    style="width: 700px"
+    label="选择设备"
+    :visible="state.deviceModalVisible"
+    @cancel="state.deviceModalVisible = false"
+    @ok="selectDevice"
+  >
+    <SelectDevice
+      ref="selectDeviceRef"
+    />
+  </modal-pro>
+</template>
+<script lang='ts' setup >
+import { FormItemProps } from '@/components/FormPro/index.vue'
+import { ModelAttrController, ModelCmdController, ModelController, RuleController } from '@/controller'
+import { computed, nextTick, onMounted, reactive, ref, watch, getCurrentInstance } from 'vue'
+import { DownOutlined, FacebookFilled } from '@ant-design/icons-vue'
+import SelectDevice from './components/selectDevice.vue'
+import { useId } from '@/hooks'
+import { Form } from 'ant-design-vue'
+
+const {
+  proxy: { $forceUpdate }
+}: any = getCurrentInstance()
+
+const useForm = Form.useForm
+
+const columns = [
+  {
+    title: '状态',
+    dataIndex: 'status',
+    key: 'status'
+  },
+  {
+    title: '规则ID',
+    dataIndex: 'id',
+    key: 'id'
+  },
+  {
+    title: '规则名称',
+    dataIndex: 'ruleLabel',
+    key: 'ruleLabel'
+  },
+  {
+    title: '规则描述',
+    dataIndex: 'ruleDescription',
+    key: 'ruleDescription'
+  },
+  {
+    title: '操作',
+    dataIndex: 'action',
+    key: 'action'
+  }
+]
+
+const formProps: FormItemProps[] = reactive([
+  {
+    label: '规则名称',
+    key: 'ruleLabel',
+    value: '',
+    type: 'input',
+    rules: true
+  },
+  {
+    label: '规则描述',
+    key: 'ruleDescription',
+    value: '',
+    type: 'textarea',
+    rules: false
+  }
+])
+
+const conditionTypeList = [
+  { key: 'DEVICE_DATA', name: '设备数据触发' },
+  { key: 'DEVICE_SESSION', name: '设备状态触发' },
+  { key: 'DAILY_TIMER', name: '周期时间条件' }
+]
+
+const selectDeviceList = [
+  { key: 'all', name: '全部设备' },
+  { key: 'target', name: '指定设备' }
+]
+
+const conditionColumns = reactive({
+  DEVICE_DATA: [
+    { title: '触发条件', dataIndex: 'conditionType' }, { title: '产品ID', dataIndex: 'modelId' },
+    { title: '属性key', dataIndex: 'attributeKey' }, { title: '操作符', dataIndex: 'operator' },
+    { title: '触发值', dataIndex: 'value' }, { title: '操作符', dataIndex: 'operator' }, { title: '操作', key: 'action' }
+  ],
+  DEVICE_SESSION: [
+    { title: '触发条件', dataIndex: 'conditionType' }, { title: '产品ID', dataIndex: 'modelId' },
+    { title: '设备类型', dataIndex: 'deviceType' }, { title: '设备名称', dataIndex: 'deviceLabel' },
+    { title: 'sessionEventType', dataIndex: 'attributeKey', key: 'sessionEventType' }, { title: '操作', key: 'action' }
+  ],
+  DAILY_TIMER: [
+    { title: '触发条件', dataIndex: 'conditionType' }, { title: '时间', dataIndex: 'time', key: 'time' },
+    { title: '日期', dataIndex: 'dayOfWeek', key: 'dayOfWeek' }, { title: '操作', key: 'action' }
+  ]
+})
+
+const actionsColumns = reactive({
+  DEVICE_CMD: [
+    { title: '触发动作', dataIndex: 'actionType' }, { title: '产品ID', dataIndex: 'modelId' },
+    { title: '设备id', dataIndex: 'deviceId' }, { title: '命令名称', dataIndex: 'cmdLabel' },
+    { title: '命令参数', dataIndex: 'cmdParameters' }, { title: '操作', dataIndex: 'action', key: 'action' }
+  ],
+  REPORT_WARN: [
+    { title: '触发动作', dataIndex: 'actionType' }, { title: '设备id', dataIndex: 'deviceId' },
+    { title: '告警名称', dataIndex: 'warnLabel' }, { title: '告警描述', dataIndex: 'warnDescription' },
+    { title: '告警级别', dataIndex: 'warnSeverity' }, { title: '操作', dataIndex: 'action', key: 'action' }
+  ],
+  RESUME_WARN: [
+    { title: '触发动作', dataIndex: 'actionType' }, { title: '告警名称', dataIndex: 'warnLabel' },
+    { title: '告警级别', dataIndex: 'warnSeverity' }, { title: '操作', dataIndex: 'action', key: 'action' }
+  ],
+  NOTICE: [
+    { title: '触发动作', dataIndex: 'actionType' }, { title: '提示名称', dataIndex: 'noticeLabel' },
+    { title: '提示描述', dataIndex: 'noticeDescription' }, { title: '提示用户', dataIndex: 'userId' },
+    { title: '操作', dataIndex: 'action', key: 'action' }
+  ]
+})
+
+const sessionEventTypeList = [
+  { key: 'CONNECT', name: '连接' },
+  { key: 'DISCONNECT', name: '断开连接' }
+]
+
+const dayOptions = [
+  { value: 1, label: '周一' },
+  { value: 2, label: '周二' },
+  { value: 3, label: '周三' },
+  { value: 4, label: '周四' },
+  { value: 5, label: '周五' },
+  { value: 6, label: '周六' },
+  { value: 7, label: '周日' }
+]
+
+const actionTypeList = [
+  { key: 'DEVICE_CMD', name: '设备命令' },
+  { key: 'REPORT_WARN', name: '上报告警' },
+  { key: 'RESUME_WARN', name: '恢复告警' },
+  { key: 'NOTICE', name: '通知' }
+]
+
+const warnSeverityList = [
+  { key: 'NOTICE', name: '提示' },
+  { key: 'MINOR', name: '次要' },
+  { key: 'MJAJOP', name: '重要' },
+  { key: 'EMERGENCY', name: '紧急' }
+]
+
+const operatorList = [
+  'EQ', 'NE', 'GT', 'GE', 'LT', 'LE', 'BETWEEN'
+]
+
+const selectDeviceRef = ref('')
+
+const formProRef = ref('')
+
+const queryParamsState = reactive({
+  page: 1,
+  pageSize: 10,
+  total: 0,
+  ruleLabel: ''
+})
+
+const _initConditionsData = {
+  conditionType: '',
+  modelId: '',
+  deviceType: '',
+  deviceLabel: '',
+  deviceId: '',
+  sessionEventType: 'CONNECT',
+  dayOfWeek: [],
+  time: '',
+  noticeLabel: '',
+  noticeDescription: '',
+  userId: '',
+  attributeKey: '',
+  operator: 'EQ',
+  value: '',
+  v1: '',
+  v2: ''
+}
+
+const initConditionsData = reactive({ ..._initConditionsData })
+
+const _initActionsData = {
+  actionType: '',
+  modelId: '',
+  deviceId: '',
+  deviceLabel: '',
+  deviceType: '',
+  cmdId: '',
+  cmdLabel: '',
+  cmdParameters: [],
+  warnLabel: '',
+  warnDescription: '',
+  warnSeverity: '',
+  noticeLabel: '',
+  noticeDescription: '',
+  userId: ''
+}
+
+const initActionsData = reactive({ ..._initActionsData })
+
+const bodyParamsState = reactive<{
+  id: string
+  conditions: [],
+  actions: [],
+  conditionLogic: string
+  ruleLabel: string
+  ruleDescription: string
+}>({
+  id: '',
+  ruleLabel: '',
+  ruleDescription: '',
+  conditionLogic: 'AND',
+  conditions: [],
+  actions: []
+})
+
+const state = reactive({
+  loading: false,
+  dataSource: [],
+  visible: false,
+  formVisible: false,
+  deviceModalVisible: false,
+  opraState: 'add',
+  modelList: [],
+  cmdList: [],
+  attrList: [],
+  opraModel: '',
+  opraIndex: 0, // 点击 条件 与 动作 时的下标
+  opraKey: ''
+})
+
+watch(
+  () => initConditionsData.conditionType,
+  () => {
+    Object.keys(initConditionsData).forEach(key => {
+      if (key !== 'conditionType') {
+        initConditionsData[key] = _initConditionsData[key]
+      }
+    })
+  }
+)
+
+watch(
+  () => initActionsData.actionType,
+  () => {
+    Object.keys(initActionsData).forEach(key => {
+      if (key !== 'actionType') {
+        initActionsData[key] = _initActionsData[key]
+      }
+    })
+  }
+)
+
+watch(
+  () => initConditionsData.modelId,
+  () => {
+    console.log('我触发吗')
+    getAttrList()
+  },
+  {
+    deep: true
+  }
+)
+watch(
+  () => initActionsData.modelId,
+  () => {
+    initActionsData.cmdParameters = []
+    initActionsData.cmdLabel = ''
+    initActionsData.cmdId = ''
+    getCmdList()
+  }
+)
+
+watch(
+  () => initActionsData.cmdId,
+  () => {
+    const cmdDetail = state.cmdList.find(item => item.id === initActionsData.cmdId)!
+    console.log('cmdDetail:', cmdDetail)
+
+    initActionsData.cmdParameters = cmdDetail.cmdParams
+    console.log(' initActionsData.cmdParameters:', initActionsData.cmdParameters, initActionsData.cmdId)
+
+    initActionsData.cmdLabel = cmdDetail.cmdLabel
+  }
+)
+
+const deviceSessionSource = computed(() => bodyParamsState.conditions.filter(item => item.conditionType === 'DEVICE_SESSION'))
+const deviceDataSource = computed(() => bodyParamsState.conditions.filter(item => item.conditionType === 'DEVICE_DATA'))
+const dailyTimerSource = computed(() => bodyParamsState.conditions.filter(item => item.conditionType === 'DAILY_TIMER'))
+const deviceCmdsource = computed(() => bodyParamsState.actions.filter(item => item.actionType === 'DEVICE_CMD'))
+const reportWarnSource = computed(() => bodyParamsState.actions.filter(item => item.actionType === 'REPORT_WARN'))
+const resumeWarnSource = computed(() => bodyParamsState.actions.filter(item => item.actionType === 'RESUME_WARN'))
+const noticeSource = computed(() => bodyParamsState.actions.filter(item => item.actionType === 'NOTICE'))
+
+const { resetFields, validate, validateInfos } = useForm(bodyParamsState, {
+  ruleLabel: [{ required: true, message: '请填写联动规则名称' }]
+})
+
+const openModal = (_opraState: 'add' | 'update', record: any) => {
+  state.visible = true
+  state.opraState = _opraState
+  getLinkPageById(record.id)
+}
+
+const ok = async () => {
+  validate().then(async () => {
+    bodyParamsState.conditions.forEach(item => {
+      if (item.operator === 'BETWEEN') {
+        item.value = `{${item.v1}, ${item.v2}}`
+      }
+    })
+
+    bodyParamsState.actions.forEach(item => {
+      if (item.cmdParameters && item.cmdParameters.length) {
+        const obj = {}
+        item.cmdParameters.forEach(item => {
+          obj[item.paramLabel] = item.dataUnit
+        })
+        item.cmdParameters = obj
+      }
+    })
+
+    state.opraState === 'add' ? await RuleController.addLink({ ...bodyParamsState }) : await RuleController.updateLink({ ...bodyParamsState })
+    state.visible = false
+    getLinkPage()
+  })
+}
+
+// 选择设备
+const selectDevice = () => {
+  const _device = selectDeviceRef.value.getSelectDevice()
+  state.deviceModalVisible = false
+  if (state.opraKey === 'conditions') {
+    initConditionsData.deviceLabel = _device.deviceLabelß
+    initConditionsData.deviceId = _device.id
+  } else {
+    initActionsData.deviceLabel = _device.deviceLabel
+    initActionsData.deviceId = _device.id
+  }
+}
+
+const changePage = ({ current }) => {
+  queryParamsState.page = current
+  getLinkPage()
+}
+
+// 打开选择设备弹窗
+const openDeviceModal = (key: 'conditions' | 'actions') => {
+  state.deviceModalVisible = true
+  state.opraKey = key
+}
+
+const openFormVisible = (model: 'conditions' | 'actions') => {
+  state.formVisible = true
+  state.opraModel = model
+}
+
+// 删除条件或则动作
+const delCondiTionsAndActions = (id: string, key: 'conditions' | 'actions') => {
+  const index = bodyParamsState[key].findIndex(item => item.id === id)
+  bodyParamsState[key].splice(index, 1)
+}
+
+const selectConditionAndAction = () => {
+  if (state.opraModel === 'conditions') {
+    bodyParamsState.conditions.push({ ...initConditionsData, id: useId() })
+  } else {
+    bodyParamsState.actions.push({ ...initActionsData, id: useId() })
+  }
+  state.formVisible = false
+}
+
+// 获取命令
+const getCmdList = async () => {
+  const { data } = await ModelCmdController.list({ modelId: initActionsData.modelId })
+  state.cmdList = data
+}
+
+// 获取属性
+const getAttrList = async () => {
+  const { data } = await ModelAttrController.list({ modelId: initConditionsData.modelId })
+  state.attrList = data
+}
+
+// 获取模型
+const getModelList = async () => {
+  const { data } = await ModelController.list()
+  state.modelList = data
+}
+
+const getLinkPageById = async (id: string) => {
+  const { data } = await RuleController.getLinkById(id)
+  console.log(data)
+  resetFields({
+    ruleLabel: data.ruleLabel,
+    ruleDescription: data.ruleDescription
+  })
+  bodyParamsState.conditionLogic = data.conditionLogic
+  bodyParamsState.actions = data.actions.map(item => {
+    return {
+      ...item,
+      id: useId()
+    }
+  })
+  bodyParamsState.conditions = data.conditions.map(item => {
+    return {
+      ...item,
+      id: useId()
+    }
+  })
+  $forceUpdate()
+  bodyParamsState.id = data.id
+}
+
+const getLinkPage = async () => {
+  state.loading = true
+  const { data, sum } = await RuleController.pageLink(queryParamsState)
+  state.dataSource = data
+  queryParamsState.total = sum
+  state.loading = false
+}
+
+onMounted(() => {
+  getLinkPage()
+  getModelList()
+})
+
+</script>
+<style lang='less' scoped >
+
+.content {
+  width: 100%;
+  height: 45px;
+  background-color: #F2F5FC;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  margin: 20px 0;
+}
+
+.df {
+  display: flex;
+  justify-content: end;
+}
+
+</style>

+ 1 - 1
src/router/index.ts

@@ -71,7 +71,7 @@ const routes: Array<ROUTER.RoutesProps> = [
           {
             path: '/rule/link',
             name: '联动规则',
-            component: () => import('@/pages/iot/rule/linkRule.vue')
+            component: () => import('@/pages/iot/rule/linkRules.vue')
           }
         ]
       },