index.vue 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180
  1. <template>
  2. <div class="device">
  3. <el-card shadow="never" style="border: 0" v-show="showSearch">
  4. <el-form
  5. class="search-form"
  6. :model="queryParams"
  7. ref="queryFormRef"
  8. inline
  9. style="margin-bottom: -20px"
  10. >
  11. <el-form-item label="设备名称" prop="deviceName">
  12. <el-input
  13. v-model="queryParams.deviceName"
  14. placeholder="请输入设备名称"
  15. clearable
  16. size="default"
  17. @keyup.enter="handleQuery"
  18. style="width: 130px"
  19. />
  20. </el-form-item>
  21. <el-form-item label="所在部门" prop="deptId">
  22. <el-tree-select
  23. v-model="queryParams.deptId"
  24. :data="deptList"
  25. :props="defaultProps"
  26. check-strictly
  27. node-key="id"
  28. filterable
  29. placeholder="请选择所在部门"
  30. @keyup.enter="handleQuery"
  31. style="width: 200px;"
  32. size="default"
  33. />
  34. </el-form-item>
  35. <el-form-item label="设备编号" prop="serialNumber">
  36. <el-input
  37. v-model="queryParams.serialNumber"
  38. placeholder="请输入设备编号"
  39. clearable
  40. size="default"
  41. @keyup.enter="handleQuery"
  42. style="width: 130px"
  43. />
  44. </el-form-item>
  45. <el-form-item label="设备状态" prop="status">
  46. <el-select
  47. v-model="queryParams.status"
  48. placeholder="请选择设备状态"
  49. clearable
  50. size="default"
  51. style="width: 130px"
  52. >
  53. <el-option
  54. v-for="dict in getStrDictOptions(DICT_TYPE.VIDEO_DEVICE_STATUS)"
  55. :key="dict.value"
  56. :label="dict.label"
  57. :value="dict.value"
  58. />
  59. </el-select>
  60. </el-form-item>
  61. <el-form-item>
  62. <el-button type="primary" :icon="Search" size="default" @click="handleQuery"
  63. >查询</el-button
  64. >
  65. <el-button :icon="Refresh" size="default" @click="resetQuery">重置</el-button>
  66. </el-form-item>
  67. </el-form>
  68. </el-card>
  69. <el-card
  70. class="main-card border-0!"
  71. shadow="never"
  72. v-if="showType == 'list'"
  73. :style="showSearch ? '' : 'margin:0'"
  74. style="border: 0;"
  75. >
  76. <div class="card-toolbar mb8">
  77. <div>
  78. <el-dropdown class="mr10" @command="handleCommand">
  79. <el-button size="small" type="primary" :icon="Plus">
  80. 新增
  81. <el-icon class="el-icon--right"><arrow-down /></el-icon>
  82. </el-button>
  83. <template #dropdown>
  84. <el-dropdown-menu>
  85. <el-dropdown-item command="handleEditDevice">手动添加</el-dropdown-item>
  86. <el-dropdown-item command="handleBatchImport">批量导入</el-dropdown-item>
  87. </el-dropdown-menu>
  88. </template>
  89. </el-dropdown>
  90. <!-- <el-dropdown @command="handleCommand1">
  91. <el-button size="default" type="primary">
  92. 分配设备
  93. <el-icon class="el-icon--right"><arrow-down /></el-icon>
  94. </el-button>
  95. <template #dropdown>
  96. <el-dropdown-menu>
  97. <el-dropdown-item command="handleSelectAllot">选择分配</el-dropdown-item>
  98. <el-dropdown-item command="handleImportAllot">导入分配</el-dropdown-item>
  99. </el-dropdown-menu>
  100. </template>
  101. </el-dropdown>
  102. <el-button type="primary" size="default" @click="recycleDevice" style="margin-left: 10px"
  103. >回收设备</el-button
  104. > -->
  105. </div>
  106. <div>
  107. <el-radio-group class="float-right ml-10" plain v-model="showType">
  108. <el-radio-button label="card"
  109. ><el-icon><Menu /></el-icon
  110. ></el-radio-button>
  111. <el-radio-button label="list"
  112. ><el-icon><Fold /></el-icon
  113. ></el-radio-button>
  114. </el-radio-group>
  115. <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
  116. </div>
  117. </div>
  118. <el-table class="base-table" v-loading="loading" :data="deviceList" :border="false">
  119. <el-table-column
  120. label="图标"
  121. align="center"
  122. header-align="center"
  123. prop="deviceId"
  124. width="72"
  125. >
  126. <template #default="{ row }">
  127. <el-image
  128. style="width: 100%; height: auto"
  129. lazy
  130. :preview-src-list="[baseUrl + row.imgUrl]"
  131. :src="baseUrl + row.imgUrl"
  132. fit="cover"
  133. v-if="row.imgUrl != null && row.imgUrl != ''"
  134. />
  135. <el-image
  136. style="width: 100%; height: auto"
  137. :preview-src-list="[gatewayImage]"
  138. :src="gatewayImage"
  139. fit="cover"
  140. v-else-if="row.deviceType == 2"
  141. />
  142. <el-image
  143. style="width: 100%; height: auto"
  144. :preview-src-list="[videoImage]"
  145. :src="videoImage"
  146. fit="cover"
  147. v-else-if="row.deviceType == 3"
  148. />
  149. <el-image
  150. style="width: 100%; height: auto"
  151. :preview-src-list="[productImage]"
  152. :src="productImage"
  153. fit="cover"
  154. v-else
  155. />
  156. </template>
  157. </el-table-column>
  158. <el-table-column
  159. label="编号"
  160. align="center"
  161. header-align="center"
  162. prop="deviceId"
  163. width="50"
  164. />
  165. <el-table-column label="设备名称" prop="deviceName" min-width="180" />
  166. <el-table-column label="设备编号" align="center" prop="serialNumber" min-width="130" />
  167. <el-table-column label="所属产品" align="center" prop="productName" min-width="160" />
  168. <el-table-column label="协议" align="center" prop="transport" min-width="80" />
  169. <el-table-column label="通讯协议" align="center" prop="protocolCode" min-width="140" />
  170. <el-table-column label="子设备数" align="center" prop="subDeviceCount" width="80">
  171. <template #default="scope">
  172. {{ scope.row.subDeviceCount }}
  173. </template>
  174. </el-table-column>
  175. <el-table-column label="设备影子" align="center" prop="isShadow" width="80">
  176. <template #default="scope">
  177. <el-tag type="success" size="default" v-if="scope.row.isShadow == 1">启用</el-tag>
  178. <el-tag type="info" size="default" v-else>禁用</el-tag>
  179. </template>
  180. </el-table-column>
  181. <el-table-column label="状态" align="center" prop="status" width="80">
  182. <template #default="scope">
  183. <dict-tag :type="DICT_TYPE.VIDEO_DEVICE_STATUS" :value="scope.row.status" />
  184. </template>
  185. </el-table-column>
  186. <el-table-column label="信号" align="center" prop="rssi" width="60">
  187. <template #default="scope">
  188. <svg-icon v-if="scope.row.status == 3 && scope.row.rssi >= '-55'" icon-class="wifi_4" />
  189. <svg-icon
  190. v-else-if="scope.row.status == 3 && scope.row.rssi >= '-70' && scope.row.rssi < '-55'"
  191. icon-class="wifi_3"
  192. />
  193. <svg-icon
  194. v-else-if="scope.row.status == 3 && scope.row.rssi >= '-85' && scope.row.rssi < '-70'"
  195. icon-class="wifi_2"
  196. />
  197. <svg-icon
  198. v-else-if="
  199. scope.row.status == 3 && scope.row.rssi >= '-100' && scope.row.rssi < '-85'
  200. "
  201. icon-class="wifi_1"
  202. />
  203. <svg-icon v-else icon-class="wifi_0" />
  204. </template>
  205. </el-table-column>
  206. <el-table-column label="定位方式" align="center" prop="locationWay" width="100">
  207. <template #default="scope">
  208. <dict-tag :type="DICT_TYPE.VIDEO_CENTER_LOCATION_WAY" :value="scope.row.locationWay" />
  209. </template>
  210. </el-table-column>
  211. <el-table-column label="固件版本" align="center" prop="firmwareVersion" width="100">
  212. <template #default="scope">
  213. <el-tag size="small" type="info">Ver {{ scope.row.firmwareVersion }}</el-tag>
  214. </template>
  215. </el-table-column>
  216. <el-table-column label="激活时间" align="center" prop="activeTime" width="100">
  217. <template #default="scope">
  218. <span>{{ parseTime(scope.row.activeTime, '{y}-{m}-{d}') }}</span>
  219. </template>
  220. </el-table-column>
  221. <el-table-column label="创建时间" align="center" prop="createTime" width="100">
  222. <template #default="scope">
  223. <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
  224. </template>
  225. </el-table-column>
  226. <el-table-column
  227. label="操作"
  228. align="center"
  229. class-name="small-padding fixed-width"
  230. fixed="right"
  231. width="200"
  232. >
  233. <template #default="scope">
  234. <el-button
  235. type="danger"
  236. plain
  237. style="padding: 5px"
  238. :icon="Delete"
  239. @click="handleDelete(scope.row)"
  240. >删除</el-button
  241. >
  242. <el-button
  243. type="primary"
  244. plain
  245. style="padding: 5px"
  246. :icon="View"
  247. @click="handleEditDevice(scope.row)"
  248. >查看</el-button
  249. >
  250. <el-button
  251. type="primary"
  252. plain
  253. style="padding: 5px"
  254. @click="openSummaryDialog(scope.row)"
  255. v-if="form.deviceId != 0"
  256. >二维码</el-button
  257. >
  258. </template>
  259. </el-table-column>
  260. </el-table>
  261. <pagination
  262. v-show="total > 0"
  263. :total="total"
  264. v-model:page="queryParams.pageNum"
  265. v-model:limit="queryParams.pageSize"
  266. :page-sizes="[12, 24, 36, 60]"
  267. @pagination="getList"
  268. />
  269. </el-card>
  270. <el-card
  271. class="main-card"
  272. shadow="never"
  273. v-if="showType == 'card'"
  274. :style="showSearch ? '' : 'margin:0'"
  275. style="border: 0;"
  276. >
  277. <div class="card-toolbar mb8">
  278. <div>
  279. <el-dropdown class="mr10" @command="handleCommand">
  280. <el-button size="small" type="primary" :icon="Plus">
  281. 新增
  282. <el-icon class="el-icon--right"><arrow-down /></el-icon>
  283. </el-button>
  284. <template #dropdown>
  285. <el-dropdown-menu>
  286. <el-dropdown-item command="handleEditDevice">手动添加</el-dropdown-item>
  287. <el-dropdown-item command="handleBatchImport">批量导入</el-dropdown-item>
  288. </el-dropdown-menu>
  289. </template>
  290. </el-dropdown>
  291. <!-- <el-dropdown @command="handleCommand1">
  292. <el-button size="default" type="primary">
  293. 分配设备
  294. <el-icon class="el-icon--right"><arrow-down /></el-icon>
  295. </el-button>
  296. <template #dropdown>
  297. <el-dropdown-menu>
  298. <el-dropdown-item command="handleSelectAllot">选择分配</el-dropdown-item>
  299. <el-dropdown-item command="handleImportAllot">导入分配</el-dropdown-item>
  300. </el-dropdown-menu>
  301. </template>
  302. </el-dropdown> -->
  303. <!-- <el-button
  304. size="default"
  305. type="primary"
  306. @click="recycleDevice"
  307. style="margin-left: 10px"
  308. >回收设备</el-button
  309. > -->
  310. </div>
  311. <div>
  312. <el-radio-group class="float-right ml-10" plain v-model="showType">
  313. <el-radio-button label="card"
  314. ><el-icon><Menu /></el-icon
  315. ></el-radio-button>
  316. <el-radio-button label="list"
  317. ><el-icon><Fold /></el-icon
  318. ></el-radio-button>
  319. </el-radio-group>
  320. <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
  321. </div>
  322. </div>
  323. <el-row :gutter="30" v-loading="loading" style="flex-wrap: wrap">
  324. <el-col
  325. :xs="24"
  326. :sm="12"
  327. :md="8"
  328. :lg="6"
  329. :xl="6"
  330. v-for="(item, index) in deviceList"
  331. :key="index"
  332. style="margin-bottom: 30px; text-align: center"
  333. >
  334. <el-card :body-style="{ padding: '20px' }" shadow="hover" class="card-item" style="border: 0;">
  335. <el-row :gutter="10" justify="space-between">
  336. <el-col :span="18" style="text-align: left">
  337. <el-link
  338. type=""
  339. :underline="false"
  340. style="font-weight: bold; font-size: 16px; line-height: 32px"
  341. >
  342. <el-tooltip class="item" effect="dark" content="分享的设备" placement="top-start">
  343. <Icon
  344. icon="svg-icon:share"
  345. style="font-size: 20px; vertical-align: -6px"
  346. v-if="item.isOwner != 1"
  347. />
  348. </el-tooltip>
  349. <Icon icon="svg-icon:device" v-if="item.isOwner == 1" />
  350. <span class="card-item__title" @click="handleDeviceDetail(item)">{{
  351. item.deviceName
  352. }}</span>
  353. <dict-tag :type="DICT_TYPE.VIDEO_DEVICE_STATUS" :value="item.status" />
  354. </el-link>
  355. </el-col>
  356. <el-col :span="3" style="font-size: 20px; padding-top: 5px; cursor: pointer">
  357. <el-image
  358. style="width: 20px; height: 20px"
  359. :src="qrcode"
  360. :fit="fit"
  361. @click="openSummaryDialog(item)"
  362. />
  363. </el-col>
  364. <el-col :span="2">
  365. <div style="font-size: 28px; color: #ccc;">
  366. <Icon icon="svg-icon:wifi_4" style="font-size: 26px;" v-if="item.status == 3 && item.rssi >= '-55'" />
  367. <Icon
  368. style="font-size: 26px;"
  369. icon="svg-icon:wifi_3"
  370. v-else-if="item.status == 3 && item.rssi >= '-70' && item.rssi < '-55'"
  371. />
  372. <Icon
  373. style="font-size: 26px;"
  374. icon="svg-icon:wifi_2"
  375. v-else-if="item.status == 3 && item.rssi >= '-85' && item.rssi < '-70'"
  376. />
  377. <Icon
  378. style="font-size: 26px;"
  379. icon="svg-icon:wifi_1"
  380. v-else-if="item.status == 3 && item.rssi >= '-100' && item.rssi < '-85'"
  381. />
  382. <Icon icon="svg-icon:wifi_0" v-else style="font-size: 26px;" />
  383. </div>
  384. </el-col>
  385. </el-row>
  386. <el-row :gutter="10">
  387. <el-col :span="17">
  388. <div style="text-align: left; line-height: 40px; white-space: nowrap">
  389. <el-tag v-if="item.protocolCode" class="mr-5" type="primary" size="default">
  390. {{ item.protocolCode }}
  391. </el-tag>
  392. <el-tag v-if="item.transport" class="mr-5" type="primary" size="default">
  393. {{ item.transport }}
  394. </el-tag>
  395. </div>
  396. <el-descriptions :column="1" size="small" style="white-space: nowrap">
  397. <el-descriptions-item label="编号">
  398. <span class="font-primary">{{ item.serialNumber }}</span>
  399. </el-descriptions-item>
  400. <el-descriptions-item label="产品">
  401. {{ item.productName }}
  402. </el-descriptions-item>
  403. <el-descriptions-item label="创建时间">
  404. {{ parseTime(item.createTime, '{y}-{m}-{d}') }}
  405. </el-descriptions-item>
  406. <el-descriptions-item label="所在部门">
  407. {{ item.deptName || '无' }}
  408. </el-descriptions-item>
  409. <el-descriptions-item label="关联设备">
  410. {{ item.deviceCode || '无' }}
  411. </el-descriptions-item>
  412. </el-descriptions>
  413. </el-col>
  414. <el-col :span="7">
  415. <div style="margin-top: 10px">
  416. <el-image
  417. style="width: 80px; height: 80px; border-radius: 10px"
  418. lazy
  419. :preview-src-list="[baseUrl + item.imgUrl]"
  420. :src="baseUrl + item.imgUrl"
  421. fit="cover"
  422. v-if="item.imgUrl != null && item.imgUrl != ''"
  423. />
  424. <el-image
  425. style="width: 80px; height: 80px; border-radius: 10px"
  426. :preview-src-list="[gatewayImage]"
  427. :src="gatewayImage"
  428. fit="cover"
  429. v-else-if="item.deviceType == 2"
  430. />
  431. <el-image
  432. style="width: 80px; height: 80px; border-radius: 10px"
  433. :preview-src-list="[videoImage]"
  434. :src="videoImage"
  435. fit="cover"
  436. v-else-if="item.deviceType == 3"
  437. />
  438. <el-image
  439. style="width: 80px; height: 80px; border-radius: 10px"
  440. :preview-src-list="[productImage]"
  441. :src="productImage"
  442. fit="cover"
  443. v-else
  444. />
  445. </div>
  446. </el-col>
  447. </el-row>
  448. <div class="card-item__footer">
  449. <el-button class="delete-btn" size="small" @click="handleDelete(item)"
  450. >删除</el-button
  451. >
  452. <el-button class="detail-btn" size="small" @click="handleEditDevice(item, 'basic')"
  453. >查看详情</el-button
  454. >
  455. <el-button size="small" @click="handleRunDevice(item)">运行状态</el-button>
  456. </div>
  457. </el-card>
  458. </el-col>
  459. </el-row>
  460. <el-empty description="暂无数据,请添加设备" v-if="total == 0" />
  461. <pagination
  462. v-show="total > 0"
  463. :total="total"
  464. v-model:page="queryParams.pageNum"
  465. v-model:limit="queryParams.pageSize"
  466. :page-sizes="[12, 24, 36, 60]"
  467. @pagination="getList"
  468. />
  469. </el-card>
  470. <!-- 二维码 -->
  471. <el-dialog v-model="openSummary" width="300px" append-to-body>
  472. <div
  473. style="
  474. border: 1px solid #ccc;
  475. width: 220px;
  476. text-align: center;
  477. margin: 0 auto;
  478. margin-top: -15px;
  479. "
  480. >
  481. <Vue3NextQrcode :text="qrText" :size="200" />
  482. <div style="padding-bottom: 10px">设备二维码</div>
  483. </div>
  484. </el-dialog>
  485. <!-- 批量导入设备 -->
  486. <batchImport ref="batchImportRef" @save="saveDialog" />
  487. <!-- 导入分配 -->
  488. <!-- <allotImport ref="allotImportRef" @save="saveAllotDialog" /> -->
  489. <!-- 导入记录 -->
  490. <!-- <importRecord ref="importRecordRef" /> -->
  491. <!-- 设备回收记录 -->
  492. <!-- <recycleRecord ref="recycleRecordRef" /> -->
  493. <!-- 设备分配记录 -->
  494. <!-- <allotRecord ref="allotRecordRef" /> -->
  495. </div>
  496. </template>
  497. <script setup>
  498. import { ref, reactive, onMounted, onActivated, computed } from 'vue'
  499. import { useRouter, useRoute } from 'vue-router'
  500. import { useStore } from 'vuex'
  501. import { ElMessage, ElMessageBox } from 'element-plus'
  502. import { useUserStoreWithOut } from '@/store/modules/user'
  503. import {
  504. Search,
  505. Refresh,
  506. InfoFilled,
  507. Plus,
  508. Delete,
  509. View,
  510. ArrowDown,
  511. Menu,
  512. Fold
  513. } from '@element-plus/icons-vue'
  514. import { getCurrentInstance } from 'vue'
  515. import { listDeviceShort, delDevice } from '@/api/pms/video/device'
  516. import { delSipDeviceBySipId } from '@/api/pms/video/sipdevice'
  517. import Treeselect from '@riophae/vue-treeselect'
  518. import '@riophae/vue-treeselect/dist/vue-treeselect.css'
  519. import { parseTime } from '@/utils/dateUtil'
  520. import { Vue3NextQrcode } from 'vue3-next-qrcode'
  521. import 'vue3-next-qrcode/es/style.css'
  522. import batchImport from './batch-import-dialog.vue'
  523. // import allotImport from './allot-import-dialog.vue'
  524. // import importRecord from './import-record.vue'
  525. // import recycleRecord from './recycle-record.vue'
  526. // import allotRecord from './allot-record.vue'
  527. // 图片资源
  528. import gatewayImage from '@/assets/imgs/gateway.svg'
  529. import videoImage from '@/assets/imgs/video.svg'
  530. import productImage from '@/assets/imgs/product.svg'
  531. import qrcode from '@/assets/imgs/qrcode.png'
  532. import SvgIcon from '@/components/SvgIcon/index.vue'
  533. import { DICT_TYPE, getDictLabel, getStrDictOptions } from '@/utils/dict'
  534. import DeptTree from '@/views/system/user/DeptTree.vue'
  535. import { defaultProps, handleTree } from '@/utils/tree'
  536. import * as DeptApi from '@/api/system/dept'
  537. const { proxy } = getCurrentInstance()
  538. // Vue Router 和 Store
  539. const router = useRouter()
  540. const route = useRoute()
  541. const store = useStore()
  542. // Refs
  543. const queryFormRef = ref()
  544. const batchImportRef = ref()
  545. const allotImportRef = ref()
  546. const importRecordRef = ref()
  547. const recycleRecordRef = ref()
  548. const allotRecordRef = ref()
  549. // 数据响应式变量
  550. const qrText = ref('yanfan')
  551. const openSummary = ref(false)
  552. const showSearch = ref(true)
  553. const showType = ref('card')
  554. const loading = ref(true)
  555. const total = ref(0)
  556. const deviceList = ref([])
  557. const myGroupList = ref([])
  558. const uniqueId = ref('')
  559. // 基础URL
  560. const baseUrl = import.meta.env.VITE_BASE_URL
  561. // 查询参数
  562. const queryParams = reactive({
  563. pageNum: 1,
  564. pageSize: 12,
  565. showChild: true,
  566. deviceName: null,
  567. productId: null,
  568. groupId: null,
  569. productName: null,
  570. userId: null,
  571. userName: null,
  572. tenantId: null,
  573. tenantName: null,
  574. serialNumber: null,
  575. status: null,
  576. networkAddress: null,
  577. activeTime: null,
  578. deptId:null
  579. })
  580. // 表单参数
  581. const form = reactive({
  582. productId: 0,
  583. status: 1,
  584. locationWay: 1,
  585. firmwareVersion: 1.0,
  586. serialNumber: '',
  587. deviceType: 1,
  588. isSimulate: 0,
  589. deviceId: 0
  590. })
  591. // 批量导入参数
  592. const isSubDev = ref(false)
  593. const deptList = ref([])
  594. // 生命周期钩子
  595. onMounted(async() => {
  596. const time = route.query.t
  597. if (time != null && time != uniqueId.value) {
  598. uniqueId.value = time
  599. // 页码筛选
  600. let pageNum = route.query.pageNum
  601. if (pageNum != null) {
  602. queryParams.pageNum = Number(pageNum)
  603. }
  604. // 产品筛选
  605. let productId = route.query.productId
  606. if (productId != null) {
  607. queryParams.productId = Number(productId)
  608. queryParams.groupId = null
  609. queryParams.serialNumber = null
  610. }
  611. // 分组筛选
  612. let groupId = route.query.groupId
  613. if (groupId != null) {
  614. queryParams.groupId = Number(groupId)
  615. queryParams.productId = null
  616. queryParams.serialNumber = null
  617. }
  618. // 设备编号筛选
  619. let sn = route.query.sn
  620. if (sn != null) {
  621. queryParams.serialNumber = sn
  622. queryParams.productId = null
  623. queryParams.groupId = null
  624. }
  625. }
  626. getList()
  627. deptList.value = handleTree(await DeptApi.getSimpleDeptList())
  628. // 以后会用,暂时注释掉
  629. // connectMqtt()
  630. })
  631. onActivated(() => {
  632. const time = route.query.t
  633. if (time != null && time != uniqueId.value) {
  634. uniqueId.value = time
  635. // 页码筛选
  636. let pageNum = route.query.pageNum
  637. if (pageNum != null) {
  638. queryParams.pageNum = Number(pageNum)
  639. }
  640. // 产品筛选
  641. let productId = route.query.productId
  642. if (productId != null) {
  643. queryParams.productId = Number(productId)
  644. queryParams.groupId = null
  645. queryParams.serialNumber = null
  646. }
  647. // 分组筛选
  648. let groupId = route.query.groupId
  649. if (groupId != null) {
  650. queryParams.groupId = Number(groupId)
  651. queryParams.productId = null
  652. queryParams.serialNumber = null
  653. }
  654. // 设备编号筛选
  655. let sn = route.query.sn
  656. if (sn != null) {
  657. queryParams.serialNumber = sn
  658. queryParams.productId = null
  659. queryParams.groupId = null
  660. }
  661. getList()
  662. }
  663. })
  664. // 方法定义
  665. /* 连接Mqtt消息服务器 */
  666. async function connectMqtt() {
  667. if (proxy.$mqttTool.client == null) {
  668. await proxy.$mqttTool.connect()
  669. }
  670. mqttCallback()
  671. getList()
  672. }
  673. /* Mqtt回调处理 */
  674. function mqttCallback() {
  675. proxy.$mqttTool.client.on('message', (topic, message, buffer) => {
  676. let topics = topic.split('/')
  677. let productId = topics[1]
  678. let deviceNum = topics[2]
  679. message = JSON.parse(message.toString())
  680. if (!message) {
  681. return
  682. }
  683. if (topics[3] == 'status') {
  684. // 更新列表中设备的状态
  685. for (let i = 0; i < this.deviceList.length; i++) {
  686. if (this.deviceList[i].serialNumber == deviceNum) {
  687. this.deviceList[i].status = message.status
  688. this.deviceList[i].isShadow = message.isShadow
  689. this.deviceList[i].rssi = message.rssi
  690. return
  691. }
  692. }
  693. }
  694. })
  695. }
  696. // 新增设备更多操作触发
  697. function handleCommand(command) {
  698. switch (command) {
  699. case 'handleEditDevice':
  700. handleEditDevice(0)
  701. break
  702. case 'handleBatchImport':
  703. handleBatchImport()
  704. break
  705. default:
  706. break
  707. }
  708. }
  709. //批量导入设备
  710. function handleBatchImport() {
  711. if (batchImportRef.value) {
  712. batchImportRef.value.showDialog()
  713. batchImportRef.value.importForm.productId = null
  714. }
  715. }
  716. //导入分配设备
  717. function handleImportAllot() {
  718. if (allotImportRef.value) {
  719. allotImportRef.value.upload.importAllotDialog = true
  720. allotImportRef.value.allotForm.productId = null
  721. allotImportRef.value.allotForm.deptId = null
  722. }
  723. }
  724. // dialog 保存响应
  725. function saveDialog() {
  726. getList()
  727. }
  728. // dialog 保存响应
  729. function saveAllotDialog() {
  730. getList()
  731. }
  732. // 分配设备更多操作触发
  733. function handleCommand1(command) {
  734. switch (command) {
  735. case 'handleSelectAllot':
  736. handleSelectAllot()
  737. break
  738. case 'handleImportAllot':
  739. handleImportAllot()
  740. break
  741. default:
  742. break
  743. }
  744. }
  745. //跳转选择分配设备页面
  746. function handleSelectAllot() {
  747. router.push({
  748. path: '/iotdev/iot/device-select-allot'
  749. })
  750. }
  751. //跳转回收设备页面
  752. function recycleDevice() {
  753. router.push({
  754. path: '/iotdev/iot/device-recycle'
  755. })
  756. }
  757. //更多操作
  758. function handleCommandMore(command) {
  759. switch (command) {
  760. case 'importRecord':
  761. handleImportRecord()
  762. break
  763. case 'exportDevice':
  764. handleexportDevice()
  765. break
  766. case 'recycleRecord':
  767. handleRecycleRecord()
  768. break
  769. case 'allotRecord':
  770. handleAllotRecord()
  771. break
  772. default:
  773. break
  774. }
  775. }
  776. //导入记录
  777. function handleImportRecord() {
  778. if (importRecordRef.value) {
  779. importRecordRef.value.open = true
  780. }
  781. }
  782. //设备回收记录
  783. function handleRecycleRecord() {
  784. if (recycleRecordRef.value) {
  785. recycleRecordRef.value.open = true
  786. }
  787. }
  788. //设备分配记录
  789. function handleAllotRecord() {
  790. if (allotRecordRef.value) {
  791. allotRecordRef.value.open = true
  792. }
  793. }
  794. function openSummaryDialog(row) {
  795. let json = {
  796. type: 1, // 1=扫码关联设备
  797. deviceNumber: row.serialNumber,
  798. productId: row.productId,
  799. productName: row.productName
  800. }
  801. qrText.value = JSON.stringify(json)
  802. openSummary.value = true
  803. }
  804. /* 订阅消息 */
  805. function mqttSubscribe(list) {
  806. // 订阅当前页面设备状态和实时监测
  807. let topics = []
  808. for (let i = 0; i < list.length; i++) {
  809. let topicStatus = '/' + '+' + '/' + list[i].serialNumber + '/status/post'
  810. topics.push(topicStatus)
  811. }
  812. proxy.$mqttTool.subscribe(topics)
  813. }
  814. /** 查询设备分组列表 */
  815. const userStore = useUserStoreWithOut()
  816. /** 查询所有简短设备列表 */
  817. function getList() {
  818. loading.value = true
  819. queryParams.params = {}
  820. listDeviceShort(queryParams)
  821. .then((response) => {
  822. deviceList.value = response
  823. total.value = response.length
  824. // 订阅消息
  825. // if (deviceList.value && deviceList.value.length > 0) {
  826. // mqttSubscribe(deviceList.value)
  827. // }
  828. })
  829. .catch((e) => {
  830. console.error(e)
  831. })
  832. .finally(() => {
  833. loading.value = false
  834. })
  835. }
  836. /** 搜索按钮操作 */
  837. function handleQuery() {
  838. queryParams.pageNum = 1
  839. getList()
  840. }
  841. /** 重置按钮操作 */
  842. function resetQuery() {
  843. queryParams.productId = null
  844. queryParams.groupId = null
  845. queryParams.serialNumber = null
  846. if (queryFormRef.value) {
  847. queryFormRef.value.resetFields()
  848. }
  849. handleQuery()
  850. }
  851. // 点击名称查看
  852. function handleDeviceDetail(item) {
  853. handleEditDevice(item)
  854. }
  855. /** 修改按钮操作 */
  856. function handleEditDevice(row, activeName) {
  857. let deviceId = 0
  858. let isSubDevValue = 0
  859. if (row != 0) {
  860. deviceId = row.deviceId || 0 // 这里需要根据实际情况调整
  861. isSubDevValue = row.subDeviceCount > 0 ? 1 : 0
  862. }
  863. router.push({
  864. name: 'VideoCenterDeviceEdit',
  865. query: {
  866. deviceId: deviceId,
  867. isSubDev: isSubDevValue,
  868. pageNum: queryParams.pageNum,
  869. activeName: activeName
  870. }
  871. })
  872. }
  873. /** 运行状态按钮操作 */
  874. function handleRunDevice(row) {
  875. let deviceId = 0
  876. let isSubDevValue = 0
  877. if (row != 0) {
  878. deviceId = row.deviceId || 0 // 这里需要根据实际情况调整
  879. isSubDevValue = row.subDeviceCount > 0 ? 1 : 0
  880. }
  881. if (row.deviceType === 3) {
  882. router.push({
  883. path: '/videocenter/device/device-edit',
  884. query: {
  885. deviceId: deviceId,
  886. isSubDev: isSubDevValue,
  887. pageNum: queryParams.pageNum,
  888. activeName: 'sipChannel'
  889. }
  890. })
  891. } else {
  892. router.push({
  893. path: '/videocenter/device/device-edit',
  894. query: {
  895. deviceId: deviceId,
  896. isSubDev: isSubDevValue,
  897. pageNum: queryParams.pageNum,
  898. activeName: 'runningStatus'
  899. }
  900. })
  901. }
  902. }
  903. /** 删除按钮操作 */
  904. function handleDelete(row) {
  905. const deviceIds = row.deviceId || 0 // 这里需要根据实际情况调整
  906. ElMessageBox.confirm('是否确认删除设备编号为"' + deviceIds + '"的数据项?', '提示', {
  907. confirmButtonText: '确定',
  908. cancelButtonText: '取消',
  909. type: 'warning'
  910. })
  911. .then(() => {
  912. if (row.deviceType === 3) {
  913. delSipDeviceBySipId(row.serialNumber)
  914. }
  915. return delDevice(deviceIds)
  916. })
  917. .then(() => {
  918. getList()
  919. ElMessage.success('删除成功')
  920. })
  921. .catch(() => {})
  922. }
  923. /** 未启用设备影子*/
  924. function shadowUnEnable(device, thingsModel) {
  925. // 1-未激活,2-禁用,3-在线,4-离线
  926. if (device.status != 3 && device.isShadow == 0) {
  927. return true
  928. }
  929. if (thingsModel.isReadonly) {
  930. return true
  931. }
  932. return false
  933. }
  934. // 获取表格图片
  935. function getImg(row) {
  936. switch (row.deviceType) {
  937. case 2:
  938. return gatewayImage
  939. case 3:
  940. return videoImage
  941. default:
  942. return productImage
  943. }
  944. }
  945. </script>
  946. <style lang="scss" scoped>
  947. ::v-deep(.el-upload-dragger) {
  948. width: 510px;
  949. }
  950. .el-dropdown-menu__item {
  951. font-size: 12px;
  952. /* 设置字体大小 */
  953. }
  954. .font-primary {
  955. color: #0147eb;
  956. }
  957. .device {
  958. --font-grey: #606266;
  959. --success-color: #67c23b;
  960. padding: 20px;
  961. .search-form {
  962. .el-form-item {
  963. :deep(.el-form-item__label) {
  964. font-weight: normal;
  965. color: var(--font-grey);
  966. }
  967. }
  968. }
  969. .main-card.el-card {
  970. margin-top: 20px;
  971. padding-bottom: 50px;
  972. border: none;
  973. .card-toolbar {
  974. display: flex;
  975. justify-content: space-between;
  976. margin-bottom: 20px;
  977. .el-radio-button {
  978. ::deep(.el-radio-button__inner) {
  979. background-color: #f0f2f5;
  980. border-color: #fff;
  981. }
  982. &.is-active {
  983. ::deep(.el-radio-button__orig-radio:checked + .el-radio-button__inner) {
  984. background-color: #0147eb;
  985. border-color: #0147eb;
  986. }
  987. }
  988. }
  989. }
  990. .is-publish {
  991. font-size: 12px;
  992. white-space: nowrap;
  993. color: var(--success-color);
  994. &:before {
  995. margin-right: 4px;
  996. display: inline-block;
  997. width: 4px;
  998. height: 4px;
  999. border-radius: 2px;
  1000. vertical-align: 2px;
  1001. background: var(--success-color);
  1002. content: '';
  1003. }
  1004. }
  1005. .el-table {
  1006. :deep(.el-table__header th) {
  1007. color: #9a9da3;
  1008. border-color: #f6f8fa;
  1009. background-color: #f5f7fa;
  1010. }
  1011. :deep(.el-table__body .el-table__cell) {
  1012. color: #606266;
  1013. padding: 8px 0;
  1014. border-color: #f6f8fa;
  1015. .el-button {
  1016. border-color: transparent;
  1017. background: transparent;
  1018. &:hover {
  1019. &.el-button--danger {
  1020. color: #ff9999;
  1021. }
  1022. &.el-button--primary {
  1023. color: #91a8f8;
  1024. }
  1025. }
  1026. &.el-button {
  1027. margin-left: 2px;
  1028. }
  1029. }
  1030. }
  1031. }
  1032. .pagination-container {
  1033. :deep(.el-pagination) {
  1034. .el-pager li:not(.disabled).active {
  1035. background-color: #0147eb;
  1036. }
  1037. }
  1038. }
  1039. }
  1040. .card-item {
  1041. height: 100%;
  1042. border-radius: 4px;
  1043. background-image: linear-gradient(#e9f1fc, #fefefe);
  1044. :deep(.el-card__body) {
  1045. display: flex;
  1046. height: 100%;
  1047. flex-direction: column;
  1048. justify-content: space-between;
  1049. }
  1050. .card-item__title {
  1051. vertical-align: -2px;
  1052. margin-right: 0.5em;
  1053. font-size: 16px;
  1054. line-height: 32px;
  1055. color: black;
  1056. }
  1057. .el-descriptions {
  1058. :deep(.el-descriptions__body) {
  1059. background: transparent;
  1060. }
  1061. }
  1062. .card-item__footer {
  1063. margin-top: 1em;
  1064. display: flex;
  1065. justify-content: center;
  1066. padding: 0 5px;
  1067. .el-button {
  1068. background: #f5f7fa;
  1069. border-color: #f5f7fa;
  1070. &.detail-btn {
  1071. color: #0147eb;
  1072. }
  1073. &.delete-btn {
  1074. color: #ff6363;
  1075. }
  1076. &:hover {
  1077. border-color: #0147eb;
  1078. color: #0147eb;
  1079. }
  1080. }
  1081. }
  1082. }
  1083. }
  1084. </style>