linkRules.vue 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225
  1. <template>
  2. <StatisticsTemplate
  3. title="规则统计"
  4. :list="state.linkCount"
  5. />
  6. <a-card style="margin-top: 20px;" >
  7. <a-row justify="space-between" >
  8. <a-col :span="12" >
  9. <a-space>
  10. <a-input allowClear placeholder="请填写规则名称" v-model:value="queryParamsState.ruleLabel" ></a-input>
  11. <a-button type="primary" @click="getLinkPage()" >搜索</a-button>
  12. </a-space>
  13. </a-col>
  14. <a-col>
  15. <a-space>
  16. <a-button type="primary" @click="openModal('add', {})" >创建规则</a-button>
  17. </a-space>
  18. </a-col>
  19. </a-row>
  20. <a-table
  21. style="margin-top: 20px;"
  22. :columns="columns"
  23. :data-source="state.dataSource"
  24. :loading="state.loading"
  25. :pagination="queryParamsState"
  26. @change="changePage"
  27. >
  28. <template #bodyCell="{column, record}" >
  29. <template v-if="column.key === 'id'" >
  30. <a @click="openModal('preview', record)" >{{record.id}}</a>
  31. </template>
  32. <template v-if="column.key === 'status'" >
  33. <a-switch
  34. v-model:checked="record.status"
  35. checked-children="运行中"
  36. un-checked-children="已停止"
  37. @click="changeStatus(record)"
  38. />
  39. </template>
  40. <template v-if="column.key === 'action'" >
  41. <a-space>
  42. <a @click="openModalDebug(record.id)" >调试</a>
  43. <a @click="openModal('preview', record)" >详情</a>
  44. <a @click="openModal('update', record)" >编辑</a>
  45. <a-popconfirm
  46. title="确实要删除吗?"
  47. ok-text="确定"
  48. cancel-text="取消"
  49. @confirm="delLinkRule(record.id)"
  50. >
  51. <a>删除</a>
  52. </a-popconfirm>
  53. </a-space>
  54. </template>
  55. </template>
  56. </a-table>
  57. </a-card>
  58. <modal-pro
  59. style="width: 1000px;"
  60. :label=" state.opraState !== 'preview' ? '创建联动规则' : '查看详情'"
  61. :open="state.visible"
  62. destroyOnClose
  63. @cancel="state.visible = false"
  64. @ok="ok"
  65. >
  66. <div style="height: 500px;overflow: hidden; overflow-y: auto;">
  67. <a-card
  68. title="基本信息"
  69. :bordered="false"
  70. >
  71. <a-form :labelCol="{span: 2}" :wapperCol="{span: 12}" >
  72. <a-form-item label="规则名称" v-bind="validateInfos.ruleLabel">
  73. <span v-if=" state.opraState === 'preview'" >{{bodyParamsState.ruleLabel}}</span>
  74. <a-input allowClear v-else v-model:value="bodyParamsState.ruleLabel" ></a-input>
  75. </a-form-item >
  76. <a-form-item label='规则描述'>
  77. <span v-if=" state.opraState === 'preview'" >{{bodyParamsState.ruleDescription}}</span>
  78. <a-textarea
  79. v-else
  80. v-model:value="bodyParamsState.ruleDescription"
  81. placeholder="请输入规则描述"
  82. :auto-size="{ minRows: 2, maxRows: 5 }"
  83. />
  84. </a-form-item>
  85. </a-form>
  86. </a-card>
  87. <a-card
  88. title="触发条件"
  89. :bordered="false"
  90. >
  91. <a-row class="condition" >
  92. <a-col span="12" >
  93. 需满足
  94. <span v-if="state.opraState === 'preview'">{{bodyParamsState.conditionLogic}}</span>
  95. <a-dropdown v-else>
  96. <a class="ant-dropdown-link" @click.prevent>
  97. {{bodyParamsState.conditionLogic === 'AND' ? '全部' : '任意一个'}}
  98. <DownOutlined />
  99. </a>
  100. <template #overlay>
  101. <a-menu>
  102. <a-menu-item @click="changeConditionLogic('AND')" >
  103. <a >全部</a>
  104. </a-menu-item>
  105. <a-menu-item @click="changeConditionLogic('OR')">
  106. <a >任意一个</a>
  107. </a-menu-item>
  108. </a-menu>
  109. </template>
  110. </a-dropdown>
  111. 以下条件:
  112. </a-col>
  113. <a-col class="df" span="12" >
  114. <a-button v-if="state.opraState !== 'preview'" type="primary" @click="openFormVisible('conditions')">添加条件</a-button>
  115. </a-col>
  116. <a-col :span="24" v-if="deviceDataSource.length" >
  117. <a-table
  118. style="width: 100%;margin-top: 10px;"
  119. :columns="state.opraState !== 'preview' ? conditionColumns.DEVICE_DATA : conditionColumns.DEVICE_DATA.slice(0, conditionColumns.DEVICE_DATA.length - 1)"
  120. :data-source="deviceDataSource"
  121. size="small"
  122. :pagination="false"
  123. >
  124. <template #bodyCell="{column, record}" >
  125. <template v-if="column.key === 'action'" >
  126. <a @click="delCondiTionsAndActions(record.id, 'conditions')" >
  127. 删除
  128. </a>
  129. </template>
  130. </template>
  131. </a-table>
  132. </a-col>
  133. <a-col :span="24" v-if="deviceSessionSource.length" >
  134. <a-table
  135. style="width: 100%;margin-top: 10px;"
  136. :columns="state.opraState !== 'preview' ? conditionColumns.DEVICE_SESSION : conditionColumns.DEVICE_SESSION.slice(0, conditionColumns.DEVICE_SESSION.length - 1)"
  137. :data-source="deviceSessionSource"
  138. size="small"
  139. :pagination="false"
  140. >
  141. <template #bodyCell="{column, record}" >
  142. <template v-if="column.key === 'sessionEventType'" >
  143. {{record.sessionEventType === 'CONNECT' ? '连接' : '断开连接' }}
  144. </template>
  145. <template v-if="column.key === 'action'" >
  146. <a @click="delCondiTionsAndActions(record.id, 'conditions')" >
  147. 删除
  148. </a>
  149. </template>
  150. </template>
  151. </a-table>
  152. </a-col>
  153. <a-col :span="24" v-if="dailyTimerSource.length" >
  154. <a-table
  155. style="width: 100%;margin-top: 10px;"
  156. :columns="state.opraState !== 'preview' ? conditionColumns.DAILY_TIMER : conditionColumns.DAILY_TIMER.slice(0, conditionColumns.DAILY_TIMER.length - 1)"
  157. :data-source="dailyTimerSource"
  158. size="small"
  159. :pagination="false"
  160. >
  161. <template #bodyCell="{column, record}" >
  162. <template v-if="column.key === 'dayOfWeek'" >
  163. {{record.dayOfWeek.map(item => '周' + item).join(',') }}
  164. </template>
  165. <template v-if="column.key === 'time'" >
  166. {{record.time}}
  167. </template>
  168. <template v-if="column.key === 'action'" >
  169. <a @click="delCondiTionsAndActions(record.id, 'conditions')" >
  170. 删除
  171. </a>
  172. </template>
  173. </template>
  174. </a-table>
  175. </a-col>
  176. <a-col v-if="bodyParamsState.conditions.length === 0" class="content" >尚未设置条件</a-col>
  177. </a-row>
  178. </a-card>
  179. <a-card
  180. title="执行动作"
  181. :bordered="false"
  182. >
  183. <a-row style="width: 100%;" >
  184. <a-col :span="24" class="df" ><a-button v-if="state.opraState !== 'preview'" type="primary" @click="openFormVisible('actions')" >添加动作</a-button></a-col>
  185. <a-row style="width: 100%;" v-if="bodyParamsState.actions.length">
  186. <a-col :span="24" v-if="deviceCmdsource.length" >
  187. <a-table
  188. style="width: 100%;margin-top: 10px;"
  189. :columns="state.opraState !== 'preview' ? actionsColumns.DEVICE_CMD : actionsColumns.DEVICE_CMD.slice(0, actionsColumns.DEVICE_CMD.length - 1)"
  190. :data-source="deviceCmdsource"
  191. size="small"
  192. :pagination="false"
  193. >
  194. <template #bodyCell="{column, record}" >
  195. <template v-if="column.key === 'cmdParameters'" >
  196. <a-tooltip color="white" :overlayStyle="{width: '800px'}" >
  197. <template #title>
  198. <a-textarea
  199. :bordered="false"
  200. style="width: 600px;"
  201. :auto-size="{ minRows: 5, maxRows: 15 }"
  202. :value="JSON.stringify(record.cmdParameters, null, '\t')"
  203. >
  204. </a-textarea>
  205. </template>
  206. <div style="width: 100px">{{record.cmdParameters}}</div>
  207. </a-tooltip>
  208. </template>
  209. <template v-if="column.key === 'action'" >
  210. <a @click="delCondiTionsAndActions(record.id, 'actions')" >
  211. 删除
  212. </a>
  213. </template>
  214. </template>
  215. </a-table>
  216. </a-col>
  217. <a-col :span="24" v-if="reportWarnSource.length" >
  218. <a-table
  219. style="width: 100%;margin-top: 10px;"
  220. :columns="state.opraState !== 'preview' ? actionsColumns.REPORT_WARN : actionsColumns.REPORT_WARN.slice(0, actionsColumns.REPORT_WARN.length - 1)"
  221. :data-source="reportWarnSource"
  222. size="small"
  223. :pagination="false"
  224. >
  225. <template #bodyCell="{column, record}" >
  226. <template v-if="column.key === 'action'" >
  227. <a @click="delCondiTionsAndActions(record.id, 'actions')" >
  228. 删除
  229. </a>
  230. </template>
  231. </template>
  232. </a-table>
  233. </a-col>
  234. <a-col :span="24" v-if="resumeWarnSource.length" >
  235. <a-table
  236. style="width: 100%;margin-top: 10px;"
  237. :columns="state.opraState !== 'preview' ? actionsColumns.RESUME_WARN : actionsColumns.RESUME_WARN.slice(0, actionsColumns.RESUME_WARN.length - 1)"
  238. :data-source="resumeWarnSource"
  239. size="small"
  240. :pagination="false"
  241. >
  242. <template #bodyCell="{column, record}" >
  243. <template v-if="column.key === 'action'" >
  244. <a @click="delCondiTionsAndActions(record.id, 'actions')" >
  245. 删除
  246. </a>
  247. </template>
  248. </template>
  249. </a-table>
  250. </a-col>
  251. <a-col :span="24" v-if="noticeSource.length" >
  252. <a-table
  253. style="width: 100%;margin-top: 10px;"
  254. :columns="state.opraState !== 'preview' ? actionsColumns.NOTICE : actionsColumns.NOTICE.slice(0, actionsColumns.NOTICE.length - 1)"
  255. :data-source="noticeSource"
  256. size="small"
  257. :pagination="false"
  258. >
  259. <template #bodyCell="{column, record}" >
  260. <template v-if="column.key === 'action'" >
  261. <a @click="delCondiTionsAndActions(record.id, 'actions')" >
  262. 删除
  263. </a>
  264. </template>
  265. </template>
  266. </a-table>
  267. </a-col>
  268. </a-row>
  269. <a-col class="content" v-if="bodyParamsState.actions.length === 0" >尚未设置动作</a-col>
  270. </a-row>
  271. </a-card>
  272. </div>
  273. </modal-pro>
  274. <modal-pro
  275. label="选择"
  276. :open="state.formVisible"
  277. @cancel="state.formVisible = false"
  278. destroyOnClose
  279. @ok="() => {
  280. if (fnDispatchCount === 0 ) {
  281. fnDispatchCount++
  282. selectConditionAndAction()
  283. }
  284. }"
  285. style="width: 700px;"
  286. zIndex="1001"
  287. >
  288. <div style="width: 100%;" v-if="state.opraModel === 'conditions'" >
  289. <a-row :gutter="[8, 8]" style="width: 100%;" >
  290. <a-form
  291. style="width: 100%;"
  292. :label-col="{ span: 3 }"
  293. :wrapper-col="{ span: 16 }"
  294. >
  295. <a-col>
  296. <a-form-item label="触发条件" >
  297. <a-select
  298. allowClear
  299. style="width: 170px"
  300. v-model:value="initConditionsData.conditionType"
  301. >
  302. <a-select-option
  303. v-for="itemType in conditionTypeList"
  304. :key="itemType.key"
  305. :value="itemType.key"
  306. >
  307. {{itemType.name}}
  308. </a-select-option>
  309. </a-select>
  310. </a-form-item>
  311. <span v-if="initConditionsData.conditionType === 'DEVICE_DATA'" >
  312. <a-form-item label="产品" >
  313. <a-select
  314. allowClear
  315. style="width: 170px"
  316. placeholder="请选择产品"
  317. v-model:value="initConditionsData.modelId"
  318. >
  319. <a-select-option
  320. v-for="model in state.modelList"
  321. :key="model.id"
  322. :value="model.id"
  323. >
  324. {{model.modelLabel}}
  325. </a-select-option>
  326. </a-select>
  327. </a-form-item>
  328. <a-form-item label="设备类型" >
  329. <a-space>
  330. <a-select
  331. allowClear
  332. style="width: 170px"
  333. placeholder="请选择设备"
  334. v-model:value="initConditionsData.deviceType"
  335. >
  336. <a-select-option
  337. v-for="deviceItem in selectDeviceList"
  338. :key="deviceItem.key"
  339. :value="deviceItem.key"
  340. >
  341. {{deviceItem.name}}
  342. </a-select-option>
  343. </a-select>
  344. <a-tag color="blue" v-if="initConditionsData.deviceLabel" >{{initConditionsData.deviceLabel}}</a-tag>
  345. <a-button
  346. type="primary"
  347. v-if="initConditionsData.deviceType === 'target'"
  348. @click="openDeviceModal('conditions')"
  349. >
  350. {{initConditionsData.deviceId ? '重新选择' : '请选择设备'}}
  351. </a-button>
  352. </a-space>
  353. </a-form-item>
  354. <a-form-item label="属性key" >
  355. <a-select
  356. allowClear
  357. style="width: 170px"
  358. placeholder="请选择属性key"
  359. v-model:value="initConditionsData.attributeKey"
  360. >
  361. <a-select-option
  362. v-for="attrItem in state.attrList"
  363. :key="attrItem.attributeKey"
  364. :value="attrItem.attributeKey"
  365. >
  366. {{attrItem.attributeKey}}
  367. </a-select-option>
  368. </a-select>
  369. </a-form-item>
  370. <a-form-item label="操作符" >
  371. <a-space>
  372. <a-select
  373. allowClear
  374. style="width: 170px"
  375. placeholder="请选择操作符"
  376. v-model:value="initConditionsData.operator"
  377. >
  378. <a-select-option
  379. v-for="operaItem in operatorList"
  380. :key="operaItem.operator"
  381. :value="operaItem.operator"
  382. >
  383. {{operaItem.label}}
  384. </a-select-option>
  385. </a-select>
  386. </a-space>
  387. </a-form-item>
  388. <a-form-item label="触发值" >
  389. <a-space>
  390. <a-input
  391. allowClear
  392. v-if="initConditionsData.operator !== 'BETWEEN'"
  393. style="width: 170px;"
  394. placeholder="请选择触发值"
  395. v-model:value="initConditionsData.value"
  396. ></a-input>
  397. <a-input-group compact v-else >
  398. <a-input allowClear placeholder="值1" v-model:value="initConditionsData.v1" style="width: 20%" />
  399. <a-input allowClear placeholder="值2" v-model:value="initConditionsData.v2" style="width: 30%" />
  400. </a-input-group>
  401. <a-tooltip>
  402. <template #title>字符串类型的值必须增加 单引号,区间范围 使用示范,{1,100}</template>
  403. <question-circle-outlined />
  404. </a-tooltip>
  405. </a-space>
  406. </a-form-item>
  407. </span>
  408. <span v-else-if="initConditionsData.conditionType === 'DEVICE_SESSION'" >
  409. <a-form-item label="产品">
  410. <a-select
  411. allowClear
  412. style="width: 170px"
  413. placeholder="请选择产品"
  414. v-model:value="initConditionsData.modelId"
  415. >
  416. <a-select-option
  417. v-for="model in state.modelList"
  418. :key="model.id"
  419. :value="model.id"
  420. >
  421. {{model.modelLabel}}
  422. </a-select-option>
  423. </a-select>
  424. </a-form-item>
  425. <a-form-item label="设备类型" >
  426. <a-space>
  427. <a-select
  428. allowClear
  429. style="width: 170px"
  430. placeholder="请选择设备"
  431. v-model:value="initConditionsData.deviceType"
  432. >
  433. <a-select-option
  434. v-for="deviceItem in selectDeviceList"
  435. :key="deviceItem.key"
  436. :value="deviceItem.key"
  437. >
  438. {{deviceItem.name}}
  439. </a-select-option>
  440. </a-select>
  441. <a-tag color="blue" v-if="initConditionsData.deviceLabel" >{{initConditionsData.deviceLabel}}</a-tag>
  442. <a-button
  443. type="primary"
  444. v-if="initConditionsData.deviceType === 'target'"
  445. @click="openDeviceModal('conditions')"
  446. >
  447. {{initConditionsData.deviceId ? '重新选择' : '请选择设备'}}
  448. </a-button>
  449. </a-space>
  450. </a-form-item>
  451. <a-form-item label="是否连接" >
  452. <a-select
  453. allowClear
  454. style="width: 170px"
  455. placeholder="请选择session"
  456. v-model:value="initConditionsData.sessionEventType"
  457. >
  458. <a-select-option
  459. v-for="essionEventTypeItem in sessionEventTypeList"
  460. :key="essionEventTypeItem.key"
  461. :value="essionEventTypeItem.key"
  462. >
  463. {{essionEventTypeItem.name}}
  464. </a-select-option>
  465. </a-select>
  466. </a-form-item>
  467. </span>
  468. <span v-else-if="initConditionsData.conditionType === 'DAILY_TIMER'" >
  469. <a-form-item label="时间" >
  470. <a-checkbox-group v-model:value="initConditionsData.dayOfWeek" :options="dayOptions" />
  471. </a-form-item>
  472. <a-form-item label="日期" >
  473. <a-time-picker v-model:value="initConditionsData.time" value-format="HH:mm:ss" />
  474. </a-form-item>
  475. </span>
  476. </a-col>
  477. </a-form>
  478. </a-row>
  479. </div>
  480. <div v-else style="width: 100%;" >
  481. <a-row :gutter="[8, 8]" style="width: 100%;" >
  482. <a-form
  483. style="width: 100%;"
  484. :label-col="{ span: 3 }"
  485. :wrapper-col="{ span: 16 }"
  486. >
  487. <a-col>
  488. <a-form-item label="触发动作" >
  489. <a-select
  490. allowClear
  491. style="width: 170px"
  492. v-model:value="initActionsData.actionType"
  493. >
  494. <a-select-option
  495. v-for="actionItem in actionTypeList"
  496. :key="actionItem.key"
  497. :value="actionItem.key"
  498. >
  499. {{actionItem.name}}
  500. </a-select-option>
  501. </a-select>
  502. </a-form-item>
  503. </a-col>
  504. <span v-if="initActionsData.actionType === 'DEVICE_CMD'" >
  505. <a-col>
  506. <a-form-item label="选择产品" >
  507. <a-select
  508. allowClear
  509. style="width: 170px"
  510. placeholder="请选择产品"
  511. v-model:value="initActionsData.modelId"
  512. >
  513. <a-select-option
  514. v-for="model in state.modelList"
  515. :key="model.id"
  516. :value="model.id"
  517. >
  518. {{model.modelLabel}}
  519. </a-select-option>
  520. </a-select>
  521. </a-form-item>
  522. <a-form-item label="设备类型" >
  523. <a-space>
  524. <a-select
  525. allowClear
  526. style="width: 170px"
  527. placeholder="请选择设备"
  528. v-model:value="initActionsData.deviceType"
  529. >
  530. <a-select-option
  531. v-for="deviceItem in selectDeviceList"
  532. :key="deviceItem.key"
  533. :value="deviceItem.key"
  534. >
  535. {{deviceItem.name}}
  536. </a-select-option>
  537. </a-select>
  538. <a-tag color="blue" v-if="initActionsData.deviceLabel" >{{initActionsData.deviceLabel}}</a-tag>
  539. <a-button
  540. type="primary"
  541. v-if="initActionsData.deviceType === 'target'"
  542. @click="openDeviceModal('actions')"
  543. >
  544. {{initActionsData.deviceId ? '重新选择' : '请选择设备'}}
  545. </a-button>
  546. </a-space>
  547. </a-form-item>
  548. <a-form-item label="选择命令" >
  549. <a-select
  550. allowClear
  551. style="width: 170px;"
  552. v-model:value="initActionsData.cmdId"
  553. >
  554. <a-select-option
  555. v-for="cmdItem in state.cmdList"
  556. :key="cmdItem.id"
  557. :value="cmdItem.id"
  558. >
  559. {{cmdItem.cmdLabel}}
  560. </a-select-option>
  561. </a-select>
  562. </a-form-item>
  563. <a-form-item label="命令参数" >
  564. <div
  565. v-for="(item, index) in initActionsData.cmdParameters"
  566. :key="index"
  567. style="margin-bottom: 10px;"
  568. >
  569. <a-input-group compact >
  570. <a-input allowClear placeholder="key" disabled v-model:value="item.paramLabel" style="width: 50%" />
  571. <a-input allowClear placeholder="value" v-model:value="item.dataUnit" style="width: 50%" />
  572. </a-input-group>
  573. </div>
  574. </a-form-item>
  575. </a-col>
  576. </span>
  577. <span v-else-if="initActionsData.actionType === 'REPORT_WARN'">
  578. <a-form-item label="设备类型" >
  579. <a-space>
  580. <a-select
  581. allowClear
  582. style="width: 170px"
  583. placeholder="请选择设备"
  584. v-model:value="initActionsData.deviceType"
  585. >
  586. <a-select-option
  587. v-for="deviceItem in selectDeviceList"
  588. :key="deviceItem.key"
  589. :value="deviceItem.key"
  590. >
  591. {{deviceItem.name}}
  592. </a-select-option>
  593. </a-select>
  594. <a-tag color="blue" v-if="initActionsData.deviceLabel" >{{initActionsData.deviceLabel}}</a-tag>
  595. <a-button
  596. type="primary"
  597. v-if="initActionsData.deviceType === 'target'"
  598. @click="openDeviceModal('actions')"
  599. >
  600. {{initActionsData.deviceId ? '重新选择' : '请选择设备'}}
  601. </a-button>
  602. </a-space>
  603. </a-form-item>
  604. <a-form-item label="告警名称" >
  605. <a-input allowClear v-model:value="initActionsData.warnLabel" ></a-input>
  606. </a-form-item>
  607. <a-form-item label="告警描述" >
  608. <a-input allowClear v-model:value="initActionsData.warnDescription" ></a-input>
  609. </a-form-item>
  610. <a-form-item label="告警级别" >
  611. <a-select allowClear v-model:value="initActionsData.warnSeverity" >
  612. <a-select-option
  613. v-for='warnItem in warnSeverityList'
  614. :key="warnItem.key"
  615. :value="warnItem.key"
  616. >
  617. {{warnItem.name}}
  618. </a-select-option>
  619. </a-select>
  620. </a-form-item>
  621. </span>
  622. <span v-else-if="initActionsData.actionType === 'RESUME_WARN'">
  623. <a-form-item label="设备类型" >
  624. <a-space>
  625. <a-select
  626. allowClear
  627. style="width: 170px"
  628. placeholder="请选择设备"
  629. v-model:value="initActionsData.deviceType"
  630. >
  631. <a-select-option
  632. v-for="deviceItem in selectDeviceList"
  633. :key="deviceItem.key"
  634. :value="deviceItem.key"
  635. >
  636. {{deviceItem.name}}
  637. </a-select-option>
  638. </a-select>
  639. <a-tag color="blue" v-if="initActionsData.deviceLabel" >{{initActionsData.deviceLabel}}</a-tag>
  640. <a-button
  641. type="primary"
  642. v-if="initActionsData.deviceType === 'target'"
  643. @click="openDeviceModal('actions')"
  644. >
  645. {{initActionsData.deviceId ? '重新选择' : '请选择设备'}}
  646. </a-button>
  647. </a-space>
  648. </a-form-item>
  649. <a-form-item label="告警名称" >
  650. <a-input allowClear v-model:value="initActionsData.warnLabel" ></a-input>
  651. </a-form-item>
  652. <a-form-item label="告警级别" >
  653. <a-select allowClear v-model:value="initActionsData.warnSeverity" >
  654. <a-select-option
  655. v-for='warnItem in warnSeverityList'
  656. :key="warnItem.key"
  657. :value="warnItem.key"
  658. >
  659. {{warnItem.name}}
  660. </a-select-option>
  661. </a-select>
  662. </a-form-item>
  663. </span>
  664. <span v-else-if="initActionsData.actionType === 'NOTICE'">
  665. <a-form-item label="提示名称" >
  666. <a-input allowClear v-model:value="initActionsData.noticeLabel" ></a-input>
  667. </a-form-item>
  668. <a-form-item label="提示描述" >
  669. <a-input allowClear v-model:value="initActionsData.noticeDescription" ></a-input>
  670. </a-form-item>
  671. <a-form-item label="提示用户" >
  672. <a-input allowClear v-model:value="initActionsData.userId" ></a-input>
  673. </a-form-item>
  674. </span>
  675. </a-form>
  676. </a-row>
  677. </div>
  678. </modal-pro>
  679. <modal-pro
  680. style="width: 900px;height: 500px;"
  681. label="选择设备"
  682. :open="state.deviceModalVisible"
  683. @cancel="state.deviceModalVisible = false"
  684. @ok="selectDevice"
  685. destroyOnClose
  686. zIndex="1003"
  687. >
  688. <SelectDevice
  689. ref="selectDeviceRef"
  690. />
  691. </modal-pro>
  692. <modal-pro
  693. label="调试"
  694. :open="state.testVisble"
  695. @cancel="state.testVisble = false"
  696. @ok="dispatchDebug"
  697. style="width: 640px;"
  698. destroyOnClose
  699. >
  700. <TestDialog ref="testDialogRef"/>
  701. </modal-pro>
  702. </template>
  703. <script lang='ts' setup >
  704. import { FormItemProps } from '@/components/FormPro/index.vue'
  705. import { ModelAttrController, ModelCmdController, ModelController, RuleController } from '@/controller'
  706. import { computed, onMounted, reactive, ref, watch, getCurrentInstance } from 'vue'
  707. import { DownOutlined, QuestionCircleOutlined } from '@ant-design/icons-vue'
  708. import SelectDevice from './components/selectDevice.vue'
  709. import { useId, useSchedulerOnce } from '@/hooks'
  710. import { Form } from 'ant-design-vue'
  711. import TestDialog from './components/testDialog.vue'
  712. import StatisticsTemplate from '@/components/StatisticsTemplate/index.vue'
  713. const {
  714. proxy: { $forceUpdate }
  715. }: any = getCurrentInstance()
  716. const useForm = Form.useForm
  717. const fnDispatchCount = ref(0)
  718. const columns = [
  719. {
  720. title: '状态',
  721. dataIndex: 'status',
  722. key: 'status'
  723. },
  724. {
  725. title: '规则ID',
  726. dataIndex: 'id',
  727. key: 'id'
  728. },
  729. {
  730. title: '规则名称',
  731. dataIndex: 'ruleLabel',
  732. key: 'ruleLabel'
  733. },
  734. {
  735. title: '规则描述',
  736. dataIndex: 'ruleDescription',
  737. key: 'ruleDescription'
  738. },
  739. {
  740. title: '操作',
  741. dataIndex: 'action',
  742. key: 'action'
  743. }
  744. ]
  745. const formProps: FormItemProps[] = reactive([
  746. {
  747. label: '规则名称',
  748. key: 'ruleLabel',
  749. value: '',
  750. type: 'input',
  751. rules: true
  752. },
  753. {
  754. label: '规则描述',
  755. key: 'ruleDescription',
  756. value: '',
  757. type: 'textarea',
  758. rules: false
  759. }
  760. ])
  761. const conditionTypeList = [
  762. { key: 'DEVICE_DATA', name: '设备数据触发' },
  763. { key: 'DEVICE_SESSION', name: '设备状态触发' },
  764. { key: 'DAILY_TIMER', name: '周期时间条件' }
  765. ]
  766. const selectDeviceList = [
  767. { key: 'all', name: '全部设备' },
  768. { key: 'target', name: '指定设备' }
  769. ]
  770. const conditionColumns = reactive({
  771. DEVICE_DATA: [
  772. { title: '触发条件', dataIndex: 'conditionType' }, { title: '产品ID', dataIndex: 'modelId' },
  773. { title: '属性key', dataIndex: 'attributeKey' }, { title: '操作符', dataIndex: 'operator' },
  774. { title: '触发值', dataIndex: 'value' }, { title: '操作符', dataIndex: 'operator' }, { title: '操作', key: 'action' }
  775. ],
  776. DEVICE_SESSION: [
  777. { title: '触发条件', dataIndex: 'conditionType' }, { title: '产品ID', dataIndex: 'modelId' },
  778. { title: '设备类型', dataIndex: 'deviceType' }, { title: '设备名称', dataIndex: 'deviceLabel' },
  779. { title: 'sessionEventType', dataIndex: 'attributeKey', key: 'sessionEventType' }, { title: '操作', key: 'action' }
  780. ],
  781. DAILY_TIMER: [
  782. { title: '触发条件', dataIndex: 'conditionType' }, { title: '时间', dataIndex: 'time', key: 'time' },
  783. { title: '日期', dataIndex: 'dayOfWeek', key: 'dayOfWeek' }, { title: '操作', key: 'action' }
  784. ]
  785. })
  786. const actionsColumns = reactive({
  787. DEVICE_CMD: [
  788. { title: '触发动作', dataIndex: 'actionType' }, { title: '产品ID', dataIndex: 'modelId' },
  789. { title: '设备id', dataIndex: 'deviceId' }, { title: '命令名称', dataIndex: 'cmdLabel' },
  790. { title: '命令参数', key: 'cmdParameters', dataIndex: 'cmdParameters', ellipsis: true, width: 120 }, { title: '操作', dataIndex: 'action', key: 'action' }
  791. ],
  792. REPORT_WARN: [
  793. { title: '触发动作', dataIndex: 'actionType' }, { title: '设备id', dataIndex: 'deviceId' },
  794. { title: '告警名称', dataIndex: 'warnLabel' }, { title: '告警描述', dataIndex: 'warnDescription' },
  795. { title: '告警级别', dataIndex: 'warnSeverity' }, { title: '操作', dataIndex: 'action', key: 'action' }
  796. ],
  797. RESUME_WARN: [
  798. { title: '触发动作', dataIndex: 'actionType' }, { title: '告警名称', dataIndex: 'warnLabel' },
  799. { title: '告警级别', dataIndex: 'warnSeverity' }, { title: '操作', dataIndex: 'action', key: 'action' }
  800. ],
  801. NOTICE: [
  802. { title: '触发动作', dataIndex: 'actionType' }, { title: '提示名称', dataIndex: 'noticeLabel' },
  803. { title: '提示描述', dataIndex: 'noticeDescription' }, { title: '提示用户', dataIndex: 'userId' },
  804. { title: '操作', dataIndex: 'action', key: 'action' }
  805. ]
  806. })
  807. const sessionEventTypeList = [
  808. { key: 'CONNECT', name: '连接' },
  809. { key: 'DISCONNECT', name: '断开连接' }
  810. ]
  811. const dayOptions = [
  812. { value: 1, label: '周一' },
  813. { value: 2, label: '周二' },
  814. { value: 3, label: '周三' },
  815. { value: 4, label: '周四' },
  816. { value: 5, label: '周五' },
  817. { value: 6, label: '周六' },
  818. { value: 7, label: '周日' }
  819. ]
  820. const actionTypeList = [
  821. { key: 'DEVICE_CMD', name: '设备命令' },
  822. { key: 'REPORT_WARN', name: '上报告警' },
  823. { key: 'RESUME_WARN', name: '恢复告警' },
  824. { key: 'NOTICE', name: '通知' },
  825. { key: 'DEVICE_MSG', name: '设备消息' },
  826. { key: 'DEVICE_DISCONNECT', name: '设备断开连接' }
  827. ]
  828. const warnSeverityList = [
  829. { key: 'NOTICE', name: '提示' },
  830. { key: 'MINOR', name: '次要' },
  831. { key: 'MJAJOP', name: '重要' },
  832. { key: 'EMERGENCY', name: '紧急' }
  833. ]
  834. // const operatorList = ['EQ', 'NE', 'GT', 'GE', 'LT', 'LE', 'BETWEEN']
  835. const operatorList = [
  836. { operator: 'EQ', label: '等于' },
  837. { operator: 'NE', label: '不等于' },
  838. { operator: 'GT', label: '大于' },
  839. { operator: 'GE', label: '大于或等于' },
  840. { operator: 'LT', label: '小于' },
  841. { operator: 'LE', label: '小于或等于' },
  842. { operator: 'BETWEEN', label: '两者之间' }
  843. ]
  844. const selectDeviceRef = ref('')
  845. const formProRef = ref('')
  846. const queryParamsState = reactive({
  847. page: 1,
  848. pageSize: 10,
  849. total: 0,
  850. ruleLabel: ''
  851. })
  852. const _initConditionsData = {
  853. conditionType: '',
  854. modelId: '',
  855. deviceType: '',
  856. deviceLabel: '',
  857. deviceId: '',
  858. sessionEventType: 'CONNECT',
  859. dayOfWeek: [],
  860. time: '',
  861. noticeLabel: '',
  862. noticeDescription: '',
  863. userId: '',
  864. attributeKey: '',
  865. operator: 'EQ',
  866. value: '',
  867. v1: '',
  868. v2: ''
  869. }
  870. const initConditionsData = reactive({ ..._initConditionsData })
  871. const _initActionsData = {
  872. actionType: '',
  873. modelId: '',
  874. deviceId: '',
  875. deviceLabel: '',
  876. deviceType: '',
  877. cmdId: '',
  878. cmdLabel: '',
  879. cmdParameters: [],
  880. warnLabel: '',
  881. warnDescription: '',
  882. warnSeverity: '',
  883. noticeLabel: '',
  884. noticeDescription: '',
  885. userId: ''
  886. }
  887. const initActionsData = reactive({ ..._initActionsData })
  888. const bodyParamsState = reactive<{
  889. id: string
  890. conditions: [],
  891. actions: [],
  892. conditionLogic: string
  893. ruleLabel: string
  894. ruleDescription: string
  895. }>({
  896. id: '',
  897. ruleLabel: '',
  898. ruleDescription: '',
  899. conditionLogic: 'AND',
  900. conditions: [],
  901. actions: []
  902. })
  903. const state = reactive({
  904. loading: false,
  905. dataSource: [],
  906. visible: false,
  907. formVisible: false,
  908. deviceModalVisible: false,
  909. opraState: 'add',
  910. modelList: [],
  911. cmdList: [],
  912. attrList: [],
  913. opraModel: '',
  914. opraIndex: 0, // 点击 条件 与 动作 时的下标
  915. opraKey: '',
  916. testVisble: false,
  917. linkCount: []
  918. })
  919. watch(
  920. () => initConditionsData.conditionType,
  921. () => {
  922. Object.keys(initConditionsData).forEach(key => {
  923. if (key !== 'conditionType') {
  924. initConditionsData[key] = _initConditionsData[key]
  925. }
  926. })
  927. }
  928. )
  929. watch(
  930. () => initActionsData.actionType,
  931. () => {
  932. Object.keys(initActionsData).forEach(key => {
  933. if (key !== 'actionType') {
  934. initActionsData[key] = _initActionsData[key]
  935. }
  936. })
  937. }
  938. )
  939. watch(
  940. () => initConditionsData.modelId,
  941. () => {
  942. console.log('我触发吗')
  943. getAttrList()
  944. },
  945. {
  946. deep: true
  947. }
  948. )
  949. watch(
  950. () => initActionsData.modelId,
  951. () => {
  952. initActionsData.cmdParameters = []
  953. initActionsData.cmdLabel = ''
  954. initActionsData.cmdId = ''
  955. getCmdList()
  956. }
  957. )
  958. watch(
  959. () => initActionsData.cmdId,
  960. () => {
  961. const cmdDetail = state.cmdList.find(item => item.id === initActionsData.cmdId)!
  962. console.log('cmdDetail:', cmdDetail)
  963. initActionsData.cmdParameters = cmdDetail.cmdParams
  964. console.log(' initActionsData.cmdParameters:', initActionsData.cmdParameters, initActionsData.cmdId)
  965. initActionsData.cmdLabel = cmdDetail.cmdLabel
  966. }
  967. )
  968. const deviceSessionSource = computed(() => bodyParamsState.conditions.filter(item => item.conditionType === 'DEVICE_SESSION'))
  969. const deviceDataSource = computed(() => bodyParamsState.conditions.filter(item => item.conditionType === 'DEVICE_DATA'))
  970. const dailyTimerSource = computed(() => bodyParamsState.conditions.filter(item => item.conditionType === 'DAILY_TIMER'))
  971. const deviceCmdsource = computed(() => bodyParamsState.actions.filter(item => item.actionType === 'DEVICE_CMD'))
  972. const reportWarnSource = computed(() => bodyParamsState.actions.filter(item => item.actionType === 'REPORT_WARN'))
  973. const resumeWarnSource = computed(() => bodyParamsState.actions.filter(item => item.actionType === 'RESUME_WARN'))
  974. const noticeSource = computed(() => bodyParamsState.actions.filter(item => item.actionType === 'NOTICE'))
  975. const { resetFields, validate, validateInfos } = useForm(bodyParamsState, {
  976. ruleLabel: [{ required: true, message: '请填写联动规则名称' }]
  977. })
  978. // 获取统计规则
  979. const getCount = async () => {
  980. const { data } = await RuleController.linkCount()
  981. state.linkCount = Object.keys(data).map(key => {
  982. const item = RuleController.linkStatisticMap.get(key)
  983. return {
  984. ...item,
  985. value: data[key]
  986. }
  987. })
  988. }
  989. const changeStatus = async (record) => RuleController.updateLinkStatus({ id: record.id, status: record.status })
  990. const openModalDebug = (id: string) => {
  991. state.testVisble = true
  992. bodyParamsState.id = id
  993. }
  994. const changeConditionLogic = (record: string) => {
  995. bodyParamsState.conditionLogic = record
  996. }
  997. const testDialogRef = ref('')
  998. // 开始调试
  999. const dispatchDebug = async () => {
  1000. const value = await testDialogRef.value.getValue()
  1001. await RuleController.linkDebug({
  1002. ...value,
  1003. ruleId: bodyParamsState.id
  1004. })
  1005. state.testVisble = false
  1006. }
  1007. // const delForwardRule = async (id: string) => {
  1008. // await RuleController.delForwardRule(id)
  1009. // getForwardList()
  1010. // }
  1011. const openModal = (_opraState: 'add' | 'update' | 'preview', record: any) => {
  1012. resetFields({
  1013. ruleLabel: '',
  1014. ruleDescription: ''
  1015. })
  1016. bodyParamsState.conditionLogic = 'AND'
  1017. bodyParamsState.actions = []
  1018. bodyParamsState.conditions = []
  1019. bodyParamsState.id = ''
  1020. state.visible = true
  1021. state.opraState = _opraState
  1022. if (record.id) {
  1023. getLinkPageById(record.id)
  1024. }
  1025. }
  1026. const ok = async () => {
  1027. validate().then(async () => {
  1028. bodyParamsState.conditions.forEach(item => {
  1029. if (item.operator === 'BETWEEN') {
  1030. item.value = `{${item.v1}, ${item.v2}}`
  1031. }
  1032. })
  1033. bodyParamsState.actions.forEach(item => {
  1034. if (item.cmdParameters && item.cmdParameters.length) {
  1035. const obj = {}
  1036. item.cmdParameters.forEach(item => {
  1037. obj[item.paramLabel] = item.dataUnit
  1038. })
  1039. item.cmdParameters = obj
  1040. }
  1041. })
  1042. state.opraState === 'add' ? await RuleController.addLink({ ...bodyParamsState }) : await RuleController.updateLink({ ...bodyParamsState })
  1043. state.visible = false
  1044. getLinkPage()
  1045. })
  1046. }
  1047. const delLinkRule = async (id: string) => {
  1048. await RuleController.delLink(id)
  1049. getLinkPage()
  1050. }
  1051. // 选择设备
  1052. const selectDevice = () => {
  1053. const _device = selectDeviceRef.value.getSelectDevice()
  1054. state.deviceModalVisible = false
  1055. if (state.opraKey === 'conditions') {
  1056. initConditionsData.deviceLabel = _device.deviceLabel
  1057. initConditionsData.deviceId = _device.id
  1058. } else {
  1059. initActionsData.deviceLabel = _device.deviceLabel
  1060. initActionsData.deviceId = _device.id
  1061. }
  1062. }
  1063. const changePage = ({ current }) => {
  1064. queryParamsState.page = current
  1065. getLinkPage()
  1066. }
  1067. // 打开选择设备弹窗
  1068. const openDeviceModal = (key: 'conditions' | 'actions') => {
  1069. state.deviceModalVisible = true
  1070. state.opraKey = key
  1071. }
  1072. const openFormVisible = (model: 'conditions' | 'actions') => {
  1073. state.formVisible = true
  1074. state.opraModel = model
  1075. }
  1076. // 删除条件或则动作
  1077. const delCondiTionsAndActions = (id: string, key: 'conditions' | 'actions') => {
  1078. const index = bodyParamsState[key].findIndex(item => item.id === id)
  1079. bodyParamsState[key].splice(index, 1)
  1080. }
  1081. function selectConditionAndAction () {
  1082. if (state.opraModel === 'conditions') {
  1083. bodyParamsState.conditions.push({ ...JSON.parse(JSON.stringify(initConditionsData)), id: useId() })
  1084. } else {
  1085. bodyParamsState.actions.push({ ...initActionsData, id: useId() })
  1086. }
  1087. console.log('bodyParamsState.conditions:', bodyParamsState.conditions)
  1088. state.formVisible = false
  1089. useSchedulerOnce(() => {
  1090. fnDispatchCount.value = 0
  1091. }, 500)
  1092. }
  1093. // 获取命令
  1094. const getCmdList = async () => {
  1095. const { data } = await ModelCmdController.list({ modelId: initActionsData.modelId })
  1096. state.cmdList = data
  1097. }
  1098. // 获取属性
  1099. const getAttrList = async () => {
  1100. const { data } = await ModelAttrController.list({ modelId: initConditionsData.modelId })
  1101. state.attrList = data
  1102. }
  1103. // 获取模型
  1104. const getModelList = async () => {
  1105. const { data } = await ModelController.list()
  1106. state.modelList = data
  1107. }
  1108. const getLinkPageById = async (id: string) => {
  1109. const { data } = await RuleController.getLinkById(id)
  1110. resetFields({
  1111. ruleLabel: data.ruleLabel,
  1112. ruleDescription: data.ruleDescription
  1113. })
  1114. bodyParamsState.conditionLogic = data.conditionLogic
  1115. bodyParamsState.actions = data.actions.map(item => {
  1116. return {
  1117. ...item,
  1118. id: useId()
  1119. }
  1120. })
  1121. bodyParamsState.conditions = data.conditions.map(item => {
  1122. return {
  1123. ...item,
  1124. id: useId()
  1125. }
  1126. })
  1127. $forceUpdate()
  1128. bodyParamsState.id = data.id
  1129. }
  1130. const getLinkPage = async () => {
  1131. state.loading = true
  1132. const { data, sum } = await RuleController.pageLink(queryParamsState)
  1133. state.dataSource = data
  1134. queryParamsState.total = sum
  1135. state.loading = false
  1136. }
  1137. onMounted(() => {
  1138. getLinkPage()
  1139. getModelList()
  1140. getCount()
  1141. })
  1142. </script>
  1143. <style lang='less' scoped >
  1144. .content {
  1145. width: 100%;
  1146. height: 45px;
  1147. background-color: #F2F5FC;
  1148. display: flex;
  1149. justify-content: center;
  1150. align-items: center;
  1151. margin: 20px 0;
  1152. }
  1153. .df {
  1154. display: flex;
  1155. justify-content: end;
  1156. }
  1157. </style>