index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. <template>
  2. <div class="app-container">
  3. <div class="filter-container">
  4. <div class="filter-left">
  5. <div class="filter-item">
  6. 设备id:
  7. <el-input
  8. v-model="query.deviceId"
  9. placeholder="请输入设备id"
  10. style="width: 150px"
  11. ></el-input>
  12. </div>
  13. <div class="filter-item">
  14. 通道id:
  15. <el-input
  16. v-model="query.id"
  17. placeholder="请输入通道id"
  18. style="width: 150px"
  19. ></el-input>
  20. </div>
  21. <div class="filter-item">
  22. 通道名称:
  23. <el-input
  24. v-model="query.name"
  25. placeholder="请输入通道名称"
  26. style="width: 150px"
  27. ></el-input>
  28. </div>
  29. <div class="filter-item">
  30. 通道状态:
  31. <el-select
  32. v-model="query.status"
  33. placeholder="请选择通道状态"
  34. clearable
  35. size="small"
  36. style="width: 150px"
  37. >
  38. <el-option
  39. v-for="dict in statusOptions"
  40. :key="dict.dictValue"
  41. :label="dict.dictLabel"
  42. :value="dict.dictValue"
  43. />
  44. </el-select>
  45. </div>
  46. <div class="filter-item">
  47. 通道类型:
  48. <el-select
  49. v-model="query.cameraType"
  50. placeholder="请选择通道类型"
  51. clearable
  52. size="small"
  53. style="width: 150px"
  54. >
  55. <el-option
  56. v-for="dict in cameraTypeOptions"
  57. :key="dict.dictValue"
  58. :label="dict.dictLabel"
  59. :value="dict.dictValue"
  60. />
  61. </el-select>
  62. </div>
  63. <el-button
  64. type="primary"
  65. icon="el-icon-search"
  66. size="mini"
  67. @click="getData()"
  68. >搜索</el-button
  69. >
  70. <el-button type="primary" icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  71. <el-button
  72. type="primary"
  73. class="search-button"
  74. @click="getDhDeviceListNew()"
  75. >同步摄像头信息</el-button
  76. >
  77. </div>
  78. <div class="handle-button-right">
  79. </div>
  80. </div>
  81. <!-- <el-divider></el-divider> -->
  82. <div ref="table">
  83. <el-table
  84. :data="tableData"
  85. style="width: 100%;margin-top:10px"
  86. row-key="id"
  87. :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
  88. v-loading="loading"
  89. :max-height="tableHeight"
  90. >
  91. <el-table-column prop="{deviceName}" label="设备名称" show-overflow-tooltip>
  92. <template slot-scope="scope">
  93. {{scope.row.deviceName}}
  94. </template>
  95. </el-table-column>
  96. <el-table-column prop="deviceId" label="设备Id" show-overflow-tooltip></el-table-column>
  97. <el-table-column prop="id" label="通道Id" show-overflow-tooltip></el-table-column>
  98. <el-table-column prop="name" label="通道名称" show-overflow-tooltip></el-table-column>
  99. <el-table-column prop="status" label="状态" show-overflow-tooltip>
  100. <template slot-scope="scope">
  101. {{scope.row.status == "0" ? "未知" : scope.row.status == "1" ? "在线" : scope.row.status == "2" ? "离线" : ""}}
  102. </template>
  103. </el-table-column>
  104. <el-table-column prop="type" label="设备类型" show-overflow-tooltip>
  105. <template slot-scope="scope">
  106. {{
  107. scope.row.type == "1" ? "编码单元" : scope.row.type == "2" ? "解码单元" :
  108. scope.row.type == "3" ? "报警输入单元" : scope.row.type == "4" ? "报警输出单元" :
  109. scope.row.type == "5" ? "电视墙输入单元" : scope.row.type == "6" ? "电视墙输出单元" :
  110. scope.row.type == "7" ? "门禁单元" : scope.row.type == "8" ? "对讲单元" :
  111. scope.row.type == "10" ? "动环单元" : scope.row.type == "14" ? "闸道单元" :
  112. scope.row.type == "15" ? "LED 单元" : scope.row.type == "16" ? "周界单元" : ""
  113. }}
  114. </template>
  115. </el-table-column>
  116. <el-table-column prop="manufacturer" label="品牌" show-overflow-tooltip>
  117. <template slot-scope="scope">
  118. {{scope.row.manufacturer == "0" ? "未知" : scope.row.manufacturer == "1" ? "大华" :scope.row.manufacturer == "2" ? "海康" : ""}}
  119. </template>
  120. </el-table-column>
  121. <el-table-column prop="cameraType" label="通道类型" show-overflow-tooltip>
  122. <template slot-scope="scope">
  123. {{scope.row.cameraType == "1" ? "枪机" : scope.row.cameraType == "2" ? "球机"
  124. : scope.row.cameraType == "3" ? "半球" : scope.row.cameraType == "4" ? "证据通道" :""}}
  125. </template>
  126. </el-table-column>
  127. <el-table-column prop="longitude" label="经度" show-overflow-tooltip></el-table-column>
  128. <el-table-column prop="latitude" label="维度" show-overflow-tooltip></el-table-column>
  129. <el-table-column prop="address" label="安装位置" show-overflow-tooltip></el-table-column>
  130. <el-table-column label="操作" width="190" fixed="right">
  131. <template slot-scope="scope">
  132. <el-button
  133. icon="el-icon-video-play"
  134. style="color: #406ce5"
  135. @click="play(scope.row)"
  136. >播放</el-button
  137. >
  138. <el-button
  139. icon="el-icon-video-play"
  140. style="color: #406ce5"
  141. @click="Hplay(scope.row)"
  142. >回放</el-button
  143. >
  144. </template>
  145. </el-table-column>
  146. </el-table>
  147. </div>
  148. <el-pagination
  149. background
  150. @current-change="handleCurrentChange"
  151. :page-sizes="[10, 15, 20, 30]"
  152. @size-change="handleSizeChange"
  153. :current-page="query.pageNo"
  154. :page-size="query.pageSize"
  155. layout="sizes,prev, pager, next"
  156. :total="totalCount"
  157. >
  158. </el-pagination>
  159. <el-dialog title="回放时间选择" :visible.sync="dialogFormVisible3" width="3000">
  160. <div class="block">
  161. <el-date-picker
  162. @change="dateData"
  163. value-format="yyyy-MM-dd HH:mm:ss"
  164. v-model="value"
  165. range-separator
  166. type="datetimerange"
  167. align="center"
  168. start-placeholder="开始日期"
  169. end-placeholder="结束日期"
  170. class="picker"
  171. :default-time="['00:00:00', '23:59:59']">
  172. </el-date-picker>
  173. <el-button
  174. v-if="dateDataJlStatus"
  175. size="small"
  176. type="primary"
  177. class="search-button pickerBut"
  178. @click="dateDataJl" >确定</el-button>
  179. </div>
  180. </el-dialog>
  181. <!-- 本地注册 -->
  182. <el-dialog :title="title" :visible.sync="dialogFormVisible">
  183. <el-form v-for="(from, index) in from_data" :key="index">
  184. <el-form-item
  185. :label="from.label"
  186. :label-width="formLabelWidth"
  187. v-if="from.bool == true"
  188. >
  189. <el-select
  190. @change="select_Value(index)"
  191. v-model="from.value"
  192. placeholder="请选择"
  193. style="width: 220px"
  194. >
  195. <el-option
  196. v-for="item in from.array"
  197. :key="item.value"
  198. :label="item.label"
  199. :value="item.value"
  200. >
  201. </el-option>
  202. </el-select>
  203. </el-form-item>
  204. </el-form>
  205. <div slot="footer" class="dialog-footer">
  206. <el-button
  207. @click="dialogFormVisible = false"
  208. style="background: #f6f6f6"
  209. >取 消</el-button
  210. >
  211. <el-button type="primary" @click="registerLocalDevice()">{{
  212. this.title
  213. }}</el-button>
  214. </div>
  215. </el-dialog>
  216. <el-dialog :visible.sync="dialogTable2" @close='closeDialog'>
  217. <template slot="title">
  218. <div
  219. class="titleZise"
  220. style="font-size: 15px; color: #484848; font-weight: 700"
  221. >
  222. {{ this.cameraNo }}
  223. </div>
  224. </template>
  225. <video
  226. class="hlsVideo monitor-height"
  227. ref="hlsVideo"
  228. style="width: 100%"
  229. controls
  230. preload="true"
  231. muted
  232. ></video>
  233. </el-dialog>
  234. </div>
  235. </template>
  236. <script>
  237. // 引入导出Excel表格依赖
  238. import FileSaver from "file-saver";
  239. import XLSX from "xlsx";
  240. import Hls from "hls.js";
  241. import { getLocalChannel, getDhDeviceListNew } from "@/api/device/camera";
  242. export default {
  243. name: "javascriptthree",
  244. data() {
  245. return {
  246. // 状态数据字典
  247. statusOptions: [
  248. {
  249. dictLabel:"未知",
  250. dictValue:0
  251. },{
  252. dictLabel:"在线",
  253. dictValue:1
  254. },{
  255. dictLabel:"离线",
  256. dictValue:2
  257. }
  258. ],
  259. cameraTypeOptions:[
  260. {
  261. dictLabel:"枪机",
  262. dictValue:1
  263. },{
  264. dictLabel:"球机",
  265. dictValue:2
  266. },{
  267. dictLabel:"半球",
  268. dictValue:3
  269. },{
  270. dictLabel:"证据通道",
  271. dictValue:4
  272. }
  273. ],
  274. listQuery:{
  275. deviceCode:undefined,
  276. deviceName:undefined,
  277. dwtype:undefined,
  278. pageNo:1,
  279. pageSize:15,
  280. sort:undefined,
  281. },
  282. dialogWidth: "60%",
  283. dialogWidth1: "70%",
  284. formLabelWidth: "130px",
  285. //input输入框--设备id
  286. device_id: "",
  287. //input输入框--设备名称
  288. device_name: "",
  289. dialogTable: false,
  290. gridData: [],
  291. gridData1: [],
  292. dialogTable1: false,
  293. cameraNo: "",
  294. dialogTable2: false,
  295. hls: "",
  296. //动态列表信息存储
  297. tableData:[],
  298. cols: [],
  299. //列表总数存储
  300. totalCount: 0,
  301. // //列表起始页
  302. // pageNo: 1,
  303. // //列表每页长度
  304. // pageSize: 15,
  305. //本地注册弹窗
  306. dialogFormVisible: false,
  307. //公共title (本地注册)
  308. title: "",
  309. //from表单提交
  310. from_data: [],
  311. //本地注册列表单条信息存储
  312. row: "",
  313. status2:false,
  314. value: '',
  315. dialogFormVisible3:false,
  316. starTime:undefined,
  317. endTime:undefined,
  318. hrow:undefined,
  319. loading:true,
  320. query:{
  321. id:undefined,
  322. deviceId:undefined,
  323. uuid:undefined,
  324. cameraType:undefined,
  325. name:undefined,
  326. status:undefined,
  327. pageNo:1,
  328. pageSize:15,
  329. unitType:1,
  330. },
  331. dateDataJlStatus:false,//回放记录时间button状态
  332. //表格自适应高度
  333. tableHeight:undefined,
  334. };
  335. },
  336. mounted() {
  337. // table高度
  338. if(window.innerWidth <1920){
  339. this.tableHeight = window.innerHeight - 300
  340. }else{
  341. this.tableHeight = window.innerHeight - 295
  342. }
  343. // 监听窗口大小变化
  344. let self = this;
  345. window.onresize = function() {
  346. self.tableHeight = window.innerHeight - 300
  347. }
  348. },
  349. created() {
  350. this.$once("hook:beforeDestroy", () => {
  351. this.destroyHls();
  352. });
  353. this.getData();
  354. },
  355. methods: {
  356. /** 重置按钮操作 */
  357. resetQuery() {
  358. this.query = {
  359. id:undefined,
  360. deviceId:undefined,
  361. uuid:undefined,
  362. cameraType:undefined,
  363. name:undefined,
  364. status:undefined,
  365. pageNo:1,
  366. pageSize:15,
  367. unitType:1,
  368. },
  369. this.handleQuery();
  370. },
  371. /** 搜索按钮操作 */
  372. handleQuery() {
  373. this.getData();
  374. },
  375. //同步信息
  376. getDhDeviceListNew(){
  377. getDhDeviceListNew().then((res)=>{
  378. this.$message.success("数据同步请求已发送,请稍后刷新查看")
  379. })
  380. },
  381. //回放时间选项
  382. dateData(val){
  383. this.starTime = new Date(val[0]).getTime()
  384. this.endTime = new Date(val[1]).getTime()
  385. this.dialogFormVisible3 = false
  386. this.playH(this.starTime/1000,this.endTime/1000)
  387. },
  388. //记录回放时间选项
  389. dateDataJl(){
  390. this.dialogFormVisible3 = false
  391. this.playH(this.starTime/1000,this.endTime/1000)
  392. },
  393. //数据初始化
  394. getData(){
  395. let arr = []
  396. getLocalChannel(this.query).then(Response =>{
  397. for(let i = 0; i<Response.data.pageList.length; i++){
  398. arr[i] = {}
  399. arr[i] = Response.data.pageList[i]
  400. }
  401. this.tableData = arr
  402. this.totalCount = Response.data.totalCount
  403. this.loading = false
  404. this.status2 = true
  405. })
  406. },
  407. Select() {
  408. this.Select_DeviceList();
  409. },
  410. Select_DeviceList(data) {
  411. var that = this;
  412. that.cols = [
  413. {
  414. property: "id",
  415. label: "设备id",
  416. width: "",
  417. },
  418. // {
  419. // property:"uuid",
  420. // label:"设备唯一编码",
  421. // width:""
  422. // },
  423. {
  424. property: "name",
  425. label: "设备名称",
  426. width: "",
  427. },
  428. {
  429. property: "deviceIp",
  430. label: "设备IP",
  431. width: "",
  432. },
  433. {
  434. property: "manufacturer",
  435. label: "厂商类型",
  436. width: "",
  437. },
  438. {
  439. property: "unitnum",
  440. label: "单元数目",
  441. width: "",
  442. },
  443. {
  444. property: "devicePort",
  445. label: "设备端口",
  446. width: "",
  447. },
  448. {
  449. property: "registerStatus",
  450. label: "本地注册",
  451. width: "",
  452. },
  453. {
  454. property: "status",
  455. label: "设备状态",
  456. width: "",
  457. },
  458. ];
  459. //获取视频设备列表
  460. api.getLocalDeviceList(data).then((request) => {
  461. that.tableData = [];
  462. that.totalCount = request.totalCount;
  463. for (let i = 0; i < request.pageList.length; i++) {
  464. var pageList = request.pageList[i];
  465. that.tableData.push(pageList);
  466. pageList.manufacturer == 1
  467. ? (pageList.manufacturer = "大华")
  468. : pageList.manufacturer == 2
  469. ? (pageList.manufacturer = "海康")
  470. : (pageList.manufacturer = "未知");
  471. pageList.status == 1
  472. ? (pageList.status = "在线")
  473. : pageList.status == 2
  474. ? (pageList.status = "离线")
  475. : "";
  476. }
  477. });
  478. },
  479. //分页查询
  480. handleCurrentChange(val) {
  481. this.query.pageNo = val;
  482. this.getData();
  483. },
  484. //分页查询
  485. handleSizeChange(val) {
  486. this.query.pageSize = val;
  487. this.getData();
  488. },
  489. handleEdit(index, row) {
  490. var that = this;
  491. that.title = "本地注册";
  492. that.row = row;
  493. that.from_data = [
  494. {
  495. prop: "buildUuid",
  496. label: "建筑名称:",
  497. value: "",
  498. array: [],
  499. bool: true,
  500. },
  501. {
  502. prop: "floorUuid",
  503. label: "楼层名称:",
  504. value: "",
  505. array: [],
  506. bool: false,
  507. },
  508. // { prop: "roomUuid", label: "房间名称:", value: "" ,array: [],bool: false},
  509. {prop: "deviceUuid", label: "楼层ID:", value: "" ,array: [] , bool: false}
  510. ];
  511. api
  512. .queryBuild({
  513. buildAddr: "",
  514. buildName: "",
  515. latitude: "",
  516. longitude: "",
  517. pageNo: "",
  518. pageSize: "",
  519. })
  520. .then((requset) => {
  521. for (let i = 0; i < requset.pageList.length; i++) {
  522. var pageList = requset.pageList[i];
  523. that.from_data[0].array.push({
  524. label: pageList.buildName,
  525. value: pageList.buildUuid,
  526. });
  527. }
  528. });
  529. that.dialogFormVisible = true;
  530. },
  531. select_Value(index) {
  532. var that = this;
  533. if (index == 0) {
  534. that.from_data[1].array = [];
  535. that.from_data[1].value = "";
  536. api
  537. .queryFloor({
  538. floorBuildUuid: that.from_data[index].value,
  539. floorName: "",
  540. pageNo: "",
  541. pageSize: "",
  542. })
  543. .then((requset) => {
  544. var requset = requset.pageList;
  545. if (requset.length > 0) {
  546. that.from_data[1].bool = true;
  547. for (let i = 0; i < requset.length; i++) {
  548. var pageList = requset[i];
  549. that.from_data[1].array.push({
  550. label: pageList.floorName,
  551. value: pageList.floorUuid,
  552. });
  553. }
  554. } else {
  555. that.from_data[1].bool = false;
  556. }
  557. });
  558. api.queryThingsjFloorId().then((requset)=>{
  559. if (requset.data.length > 0) {
  560. that.from_data[2].bool = true;
  561. for (let i = 0; i < requset.data.length; i++) {
  562. var pageList = requset.data[i];
  563. that.from_data[2].array.push({
  564. label: pageList.thingjsFloorId,
  565. value: pageList.thingjsFloorId,
  566. });
  567. }
  568. }else{
  569. that.from_data[2].bool = false;
  570. }
  571. })
  572. }
  573. },
  574. registerLocalDevice() {
  575. var that = this;
  576. var row = that.row;
  577. var from = that.from_data;
  578. api
  579. .registerLocalDevice({
  580. buildUuid: from[0].value,
  581. deviceIp: row.deviceIp,
  582. devicePort: row.devicePort,
  583. floorUuid: from[1].value,
  584. thingjsFloor: from[2].value,
  585. nvrIp: row.nvrIp,
  586. nvrPort: row.nvrPort,
  587. deviceName:row.name,
  588. status: row.registerStatus === "已注册" ? 0 : 1,
  589. })
  590. .then((requset) => {
  591. this.dialogFormVisible = false
  592. this.Select();
  593. });
  594. },
  595. //定义导出Excel表格事件
  596. exportExcel() {
  597. /* 从表生成工作簿对象 */
  598. var wb = XLSX.utils.table_to_book(document.querySelector("#out-table"));
  599. /* 获取二进制字符串作为输出 */
  600. var wbout = XLSX.write(wb, {
  601. bookType: "xlsx",
  602. bookSST: true,
  603. type: "array",
  604. });
  605. try {
  606. FileSaver.saveAs(
  607. //Blob 对象表示一个不可变、原始数据的类文件对象。
  608. //Blob 表示的不一定是JavaScript原生格式的数据。
  609. //File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
  610. //返回一个新创建的 Blob 对象,其内容由参数中给定的数组串联组成。
  611. new Blob([wbout], { type: "application/octet-stream" }),
  612. //设置导出文件名称
  613. "sheetjs.xlsx"
  614. );
  615. } catch (e) {
  616. if (typeof console !== "undefined") console.log(e, wbout);
  617. }
  618. return wbout;
  619. },
  620. //播放按钮
  621. play(row) {
  622. this.destroyHls()
  623. let url = "http://" + row.nvr_ip + ":" + row.hls_port + "/live/cameraid/" + row.deviceId + "%24" + row.id.split("$")[(row.id.split("$")).length-1] + "/substream/1.m3u8"
  624. this.loadVideoFn(url)
  625. },
  626. //回放url
  627. playH(start,end){
  628. this.destroyHls()
  629. let row = this.hrow
  630. let url = "http://" + row.nvr_ip + ":" + row.hls_port + "/vod/device/cameraid/" + row.deviceId + "%24" + row.id.split("$")[(row.id.split("$")).length-1] + "/substream/1/recordtype/1/totallength/" + Number(end-start) + "/begintime/" + start + "/endtime/" + end + ".m3u8"
  631. this.loadVideoFn(url)
  632. },
  633. destroyHls: function () {
  634. if (this.hls) {
  635. this.$refs.hlsVideo.pause();
  636. this.hls.destroy();
  637. this.hls = null;
  638. }
  639. },
  640. loadVideoFn(url) {
  641. this.dialogTable2 = true
  642. setTimeout(() =>{
  643. if (Hls.isSupported()) {
  644. this.hls = new Hls();
  645. this.hls.loadSource(url);
  646. this.hls.attachMedia(this.$refs.hlsVideo);
  647. this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
  648. console.log("加载成功");
  649. this.$refs.hlsVideo.play();
  650. });
  651. this.hls.on(Hls.Events.ERROR, (event, data) => {
  652. // console.log(event, data);
  653. // 监听出错事件
  654. console.log("加载失败");
  655. });
  656. }
  657. },1000)
  658. },
  659. //设备列表序号
  660. indexMethod(index) {
  661. return index + 1;
  662. },
  663. //回放视频
  664. Hplay(row){
  665. this.dialogFormVisible3 = true
  666. if(this.endTime){
  667. this.dateDataJlStatus = true
  668. }else{
  669. this.dateDataJlStatus = false
  670. }
  671. this.hrow = row
  672. },
  673. //关闭弹框
  674. closeDialog(){
  675. this.destroyHls()
  676. },
  677. },
  678. };
  679. </script>
  680. <style>
  681. .el-dialog__headerbtn{
  682. top:3px;
  683. }
  684. .el-range-editor.el-input__inner{
  685. width:80%;
  686. margin:0 10% !important;
  687. }
  688. .pickerBut{
  689. width:10%;display: inline-block;
  690. margin:20px 45% 0;
  691. }
  692. </style>