detail.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. <template>
  2. <div
  3. v-loading="videoLoading"
  4. class="detail"
  5. >
  6. <i
  7. class="el-icon-close closeDetail"
  8. @click="close"
  9. />
  10. <video
  11. ref="player"
  12. style="width: 100%"
  13. class="video"
  14. muted
  15. autoplay
  16. :poster="require('@/assets//video-post.png')"
  17. />
  18. <div class="detail-buttom">
  19. <el-row :gutter="16">
  20. <el-col :span="24">
  21. <div class="video-controls l-flex--row">
  22. <div
  23. v-show="settingBshow"
  24. class="settingB"
  25. >
  26. <div v-show="settingTab">
  27. <div
  28. v-for="(item, index) in setData"
  29. :key="index"
  30. class="settingT"
  31. @click="setClick(index)"
  32. >
  33. {{ item }} <i class="el-icon-arrow-right" />
  34. </div>
  35. </div>
  36. <div v-show="!settingTab">
  37. <div
  38. class="settingT settingsub"
  39. @click="setBack"
  40. >
  41. <i class="el-icon-arrow-left" />{{ setData[settingActive] }}
  42. </div>
  43. <div class="settingHeight">
  44. <div
  45. v-for="(item, index) in settingData[settingActive]"
  46. :key="index"
  47. class="settingT settingsub"
  48. @click="settingClick(index)"
  49. >
  50. <i :class="{ 'el-icon-check': item.active }" />
  51. {{ item.value }}
  52. </div>
  53. </div>
  54. </div>
  55. </div>
  56. <div class="l-flex__auto">
  57. <img
  58. :src="imgUrl.stop"
  59. class="stop"
  60. @click="stopbtn"
  61. >
  62. </div>
  63. <img
  64. :src="imgUrl.setting"
  65. class="setting"
  66. @click="settingShow"
  67. >
  68. <img
  69. :src="imgUrl.refresh"
  70. class="refresh"
  71. @click="refresh"
  72. >
  73. </div>
  74. </el-col>
  75. <el-col :span="8">
  76. <div class="o-detail">
  77. <div>
  78. <span class="o-detail_title">设备名称:</span><span>{{ detailobj.name }}</span>
  79. </div>
  80. <div>
  81. <span class="o-detail_title">ID:</span><span>{{ detailobj.id }}</span>
  82. </div>
  83. <div>
  84. <span class="o-detail_title">用户名:</span><span>{{ detailobj.username }}</span>
  85. </div>
  86. <div>
  87. <span class="o-detail_title">备注:</span><span>{{ detailobj.remark }}</span>
  88. </div>
  89. </div>
  90. </el-col>
  91. <el-col :span="16">
  92. <div class="o-detail">
  93. <div
  94. id="main"
  95. style="width: 100%; height: 200px"
  96. />
  97. <div class="choosedate">
  98. <div class="timeBtn">
  99. <span
  100. :class="{ active: active === 'hour' }"
  101. @click="timeType('hour')"
  102. >1小时</span>
  103. <span
  104. :class="{ active: active === 'day' }"
  105. @click="timeType('day')"
  106. >1天</span>
  107. </div>
  108. <el-date-picker
  109. v-model="datevalue"
  110. type="datetime"
  111. placeholder="选择日期时间"
  112. prefix-icon="el-icon-date"
  113. format="yyyy-MM-dd HH:mm"
  114. value-format="yyyy-MM-dd HH:mm:ss"
  115. @change="onDateTimeChange()"
  116. />
  117. </div>
  118. </div>
  119. </el-col>
  120. </el-row>
  121. </div>
  122. </div>
  123. </template>
  124. <script>
  125. import flvjs from 'flv.js'
  126. import * as echarts from 'echarts'
  127. import {
  128. getStatistic,
  129. getVideoinfo,
  130. getAvailableParam,
  131. setCamera
  132. } from '@/api/camera'
  133. const CAMERA_URL = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${process.env.VUE_APP_GATEWAY || location.host}${process.env.VUE_APP_CAMERA_PROXY}`
  134. export default {
  135. name: 'Detail',
  136. props: {
  137. detailobj: {
  138. type: Object,
  139. default () {
  140. return {}
  141. }
  142. }
  143. // ['']
  144. },
  145. data () {
  146. return {
  147. datevalue: new Date(),
  148. active: 'hour', // hour是小时,day是天
  149. imgUrl: {
  150. setting: require('@/assets/icon_setting.png'),
  151. stop: require('@/assets/icon_stop.png'),
  152. start: require('@/assets/icon_start.png'),
  153. refresh: require('@/assets/icon_refresh.png')
  154. },
  155. settingActive: 0,
  156. setData: ['分辨率', '帧率', '码流'],
  157. settingData: [[], [], []],
  158. settingTab: true,
  159. settingBshow: false,
  160. echartsData: '',
  161. player: null,
  162. availableParam: {},
  163. infoData: {},
  164. videoLoading: false,
  165. settingDatacopy: []
  166. }
  167. },
  168. created () {
  169. this.getAvailableParam()
  170. },
  171. mounted () {
  172. this.getflv()
  173. this.getStatistic(
  174. this.getStarttime(new Date(), 'now'),
  175. this.getStarttime(new Date())
  176. )
  177. },
  178. methods: {
  179. close () {
  180. this.destroyPlayer()
  181. this.$emit('closeDetail')
  182. },
  183. getflv () {
  184. if (flvjs.isSupported()) {
  185. // 创建一个flvjs实例
  186. this.player = flvjs.createPlayer({
  187. type: 'flv',
  188. isLive: true,
  189. // hasAudio: false,
  190. url: `${CAMERA_URL}/${this.detailobj.deviceId}?authorization=${this.$keycloak.token}`
  191. })
  192. this.player.on('error', (e) => {
  193. console.log(e)
  194. })
  195. // 将实例挂载到video元素上面
  196. this.player.attachMediaElement(this.$refs.player)
  197. try {
  198. // 开始运行加载 只要流地址正常 就可以在h5页面中播放出画面了
  199. this.player.load()
  200. this.player.play()
  201. this.videoLoading = false
  202. } catch (error) {
  203. console.log('连接websocker异常:' + error)
  204. return false
  205. }
  206. }
  207. },
  208. setCamera () {
  209. setCamera({
  210. deviceId: this.detailobj.deviceId,
  211. width: this.infoData.withHight.split('*')[0],
  212. hight: this.infoData.withHight.split('*')[1],
  213. bitRate: this.infoData.bitRate.slice(0, -4),
  214. frameRate: this.infoData.frameRate.slice(0, -3)
  215. }).then(() => {
  216. this.destroyPlayer()
  217. this.getflv()
  218. })
  219. },
  220. getAvailableParam () {
  221. getAvailableParam().then(({ data }) => {
  222. data.itemList.forEach((item) => {
  223. this.settingData[0].push({
  224. value: item.snWidth + '*' + item.snHight,
  225. active: false
  226. })
  227. })
  228. for (let i = 1; i < 26; i++) {
  229. this.settingData[1].push({ value: i + 'fps', active: false })
  230. this.settingDatacopy.push({ value: i + 'fps', active: false })
  231. }
  232. data.streamRateTypeList.forEach((item) => {
  233. this.settingData[2].push({ value: item + 'kb/s', active: false })
  234. })
  235. this.availableParam = data
  236. this.getVideoinfo(data)
  237. })
  238. },
  239. getVideoinfo (param) {
  240. getVideoinfo(this.detailobj.deviceId).then(({ data }) => {
  241. this.infoData = {
  242. withHight: data.width + '*' + data.hight,
  243. frameRate: data.frameRate + 'fps',
  244. bitRate: data.bitRate + 'kb/s'
  245. }
  246. this.dataInit(param, this.infoData)
  247. })
  248. },
  249. dataInit (param, data) {
  250. for (let i = 0; i < this.settingData[0].length; i++) {
  251. if (this.settingData[0][i].value === data.withHight) {
  252. this.settingData[0][i].active = true
  253. }
  254. }
  255. this.settingData[1] = this.settingDatacopy
  256. for (let i = 0; i < this.settingData[1].length; i++) {
  257. if (this.settingData[1][i].value === data.frameRate) {
  258. this.settingData[1][i].active = true
  259. } else {
  260. this.settingData[1][i].active = false
  261. }
  262. }
  263. for (let i = 0; i < this.settingData[2].length; i++) {
  264. if (this.settingData[2][i].value === data.bitRate) {
  265. this.settingData[2][i].active = true
  266. }
  267. }
  268. var findobj = param.itemList.find((item) => {
  269. return item.snWidth + '*' + item.snHight === data.withHight
  270. })
  271. this.settingData[1] = this.settingData[1].filter((item) => {
  272. var splceitem = item.value.slice(0, -3)
  273. return splceitem <= findobj.resolutionFpsMax
  274. })
  275. var findData = this.settingData[1].find((item) => {
  276. return item.active === true
  277. })
  278. this.infoData.frameRate = findData ? this.infoData.frameRate : (this.settingData[1].length + 'fps')
  279. if (!findData) {
  280. this.infoData.frameRate = this.settingData[1].length + 'fps'
  281. this.settingData[1][this.settingData[1].length].active = true
  282. }
  283. // this.settingData[1][this.settingData[1].length].value = this.infoData.frameRate
  284. this.settingData[2] = this.settingData[2].filter((item) => {
  285. var splceitem = item.value.slice(0, -4)
  286. return (
  287. splceitem >= findobj.minBitRateOptions &&
  288. splceitem <= findobj.maxBitRateOptions
  289. )
  290. })
  291. },
  292. destroyPlayer () {
  293. // this.player.pause();
  294. this.player.unload()
  295. this.player.detachMediaElement()
  296. this.player.destroy()
  297. this.player = null
  298. },
  299. stopbtn () {
  300. if (this.imgUrl.stop === this.imgUrl.start) {
  301. this.imgUrl.stop = require('@/assets/icon_stop.png')
  302. this.player.play()
  303. } else {
  304. this.imgUrl.stop = this.imgUrl.start
  305. this.player.pause()
  306. }
  307. },
  308. setClick (index) {
  309. this.settingActive = index
  310. this.settingTab = !this.settingTab
  311. },
  312. setBack () {
  313. this.settingTab = !this.settingTab
  314. },
  315. settingClick (index) {
  316. this.settingData[this.settingActive].forEach((element) => {
  317. element.active = false
  318. })
  319. this.settingData[this.settingActive][index].active = !this.settingData[this.settingActive][index].active
  320. var arr = JSON.stringify(this.infoData)
  321. // console.log(this.settingData)
  322. if (this.settingActive === 0) {
  323. if (
  324. this.infoData.withHight !==
  325. this.settingData[this.settingActive][index].value
  326. ) {
  327. this.infoData.withHight =
  328. this.settingData[this.settingActive][index].value
  329. this.dataInit(this.availableParam, this.infoData)
  330. }
  331. } else if (this.settingActive === 1) {
  332. this.infoData.frameRate =
  333. this.settingData[this.settingActive][index].value
  334. } else if (this.settingActive === 2) {
  335. this.infoData.bitRate =
  336. this.settingData[this.settingActive][index].value
  337. }
  338. if (!(arr === JSON.stringify(this.infoData))) {
  339. this.setCamera()
  340. }
  341. // console.log(this.infoData)
  342. },
  343. settingShow () {
  344. this.settingBshow = !this.settingBshow
  345. },
  346. refresh () {
  347. this.destroyPlayer()
  348. this.getflv()
  349. },
  350. timeType (type) {
  351. this.active = type
  352. var startTime
  353. if (this.datevalue.length) {
  354. startTime = this.datevalue
  355. } else {
  356. this.datevalue = new Date()
  357. startTime = this.getStarttime(new Date(), 'now')
  358. }
  359. this.getStatistic(startTime, this.getStarttime(startTime, type))
  360. },
  361. onDateTimeChange () {
  362. if (this.datevalue) {
  363. this.getStatistic(this.datevalue, this.getStarttime(this.datevalue, this.active))
  364. }
  365. },
  366. getStarttime (time, type) {
  367. var time1 = new Date(time)
  368. time1 = time1.getTime() // 转换为时间戳
  369. var onehour = 60 * 60 * 1000
  370. if (type === 'day') {
  371. onehour = 60 * 60 * 1000 * 24
  372. } else if (type === 'now') {
  373. onehour = 0
  374. }
  375. var startTime = time1 - onehour
  376. startTime = new Date(startTime).toLocaleString()
  377. startTime = startTime.split('/').join('-')
  378. var arr = startTime.split(' ')[0].split('-')
  379. for (let i = 0; i < arr.length; i++) {
  380. if (arr[i].length < 2) {
  381. arr[i] = '0' + arr[i]
  382. }
  383. }
  384. startTime = arr.join('-') + ' ' + startTime.split(' ')[1]
  385. return startTime
  386. },
  387. getStatistic (endTime, startTime) {
  388. getStatistic({
  389. deviceId: this.detailobj.deviceId,
  390. startTime: startTime,
  391. endTime: endTime,
  392. pageIndex: 1,
  393. pageSize: 10000
  394. }).then(({ data }) => {
  395. this.echartsData = data
  396. this.initEchart()
  397. })
  398. },
  399. getxdata (data) {
  400. var arr = []
  401. data.forEach((item) => {
  402. var time = item.eventTime.slice(11, 16)
  403. arr.push(time)
  404. })
  405. return arr
  406. },
  407. getydata (data) {
  408. var arr = []
  409. data.forEach((item) => {
  410. arr.push(item.insidePeopleNum)
  411. })
  412. return arr
  413. },
  414. initEchart () {
  415. var data = this.echartsData
  416. data = data.filter((item) => {
  417. return item.insidePeopleNum !== 0
  418. })
  419. var xdata = this.getxdata(data)
  420. var ydata = this.getydata(data)
  421. var chartDom = document.getElementById('main')
  422. var myChart = echarts.init(chartDom)
  423. var option
  424. option = {
  425. title: {
  426. text: '123',
  427. textStyle: {
  428. color: '#fff',
  429. fontWeight: 'bold'
  430. }
  431. // padding: [0, 30],
  432. },
  433. xAxis: {
  434. type: 'category',
  435. data: xdata,
  436. splitLine: {
  437. show: false
  438. },
  439. axisLine: {
  440. lineStyle: {
  441. color: '#4779BC'
  442. }
  443. },
  444. axisLabel: {
  445. color: '#A9CEFF'
  446. }
  447. },
  448. yAxis: {
  449. type: 'value',
  450. minInterval: 1,
  451. splitLine: {
  452. lineStyle: {
  453. color: '#4779BC',
  454. type: 'dashed'
  455. }
  456. },
  457. axisLine: {
  458. show: false,
  459. lineStyle: {
  460. color: '#4779BC'
  461. }
  462. },
  463. axisLabel: {
  464. color: '#A9CEFF'
  465. }
  466. },
  467. grid: {
  468. left: '30',
  469. right: '20',
  470. top: '40',
  471. bottom: '20'
  472. },
  473. series: [
  474. {
  475. data: ydata,
  476. type: 'bar',
  477. showBackground: true,
  478. backgroundStyle: {
  479. color: 'transparent'
  480. },
  481. itemStyle: {
  482. color: 'rgba(0, 191, 208, 0.5)'
  483. },
  484. select: {
  485. itemStyle: {
  486. color: 'rgb(0, 234, 255)'
  487. }
  488. }
  489. }
  490. ],
  491. tooltip: {
  492. formatter: '{b}<br />{c}'
  493. }
  494. }
  495. option && myChart.setOption(option)
  496. }
  497. }
  498. }
  499. </script>
  500. <style lang="scss" scoped>
  501. .detail {
  502. width: 100%;
  503. height: 100%;
  504. position: relative;
  505. .closeDetail {
  506. position: absolute;
  507. right: 20px;
  508. top: 20px;
  509. cursor: pointer;
  510. z-index: 2;
  511. font-size: 42px;
  512. color: #000000;
  513. }
  514. .video {
  515. width: 100%;
  516. height: 100vh;
  517. object-fit: contain;
  518. }
  519. .detail-buttom {
  520. position: absolute;
  521. bottom: 0;
  522. width: 100%;
  523. left: 0;
  524. padding: 16px;
  525. .o-detail {
  526. padding: 24px 24px 70px;
  527. background-color: rgba(0, 62, 144, 0.8);
  528. color: #fff;
  529. line-height: 36px;
  530. height: 238px;
  531. position: relative;
  532. &_title {
  533. width: 72px;
  534. display: inline-block;
  535. border-radius: 4px;
  536. }
  537. .choosedate {
  538. position: absolute;
  539. right: 20px;
  540. top: 10px;
  541. .timeBtn {
  542. display: inline-block;
  543. border-radius: 4px;
  544. background: #4478bc;
  545. height: 24px;
  546. line-height: 24px;
  547. margin-right: 16px;
  548. span {
  549. font-size: 14px;
  550. background: #4478bc;
  551. border-radius: 4px;
  552. padding: 0 7px;
  553. display: inline-block;
  554. cursor: pointer;
  555. width: 50px;
  556. text-align: center;
  557. }
  558. .active {
  559. background: #0096ff;
  560. }
  561. }
  562. }
  563. }
  564. .video-controls {
  565. background-color: rgba(0, 6, 13, 0.75);
  566. height: 48px;
  567. border-radius: 4px;
  568. margin-bottom: 16px;
  569. position: relative;
  570. img {
  571. cursor: pointer;
  572. }
  573. .stop {
  574. margin-left: 12px;
  575. }
  576. .setting {
  577. margin-right: 50px;
  578. }
  579. .refresh {
  580. margin-right: 38px;
  581. }
  582. .settingB {
  583. position: absolute;
  584. padding: 10px 0px;
  585. background-color: rgba($color: #000000, $alpha: 0.85);
  586. right: 134px;
  587. transform: translateX(50%);
  588. bottom: 48px;
  589. color: #fff;
  590. .settingT {
  591. padding: 0px 16px;
  592. height: 40px;
  593. line-height: 40px;
  594. width: 180px;
  595. cursor: pointer;
  596. position: relative;
  597. &:hover {
  598. background-color: rgba(69, 69, 69, 0.85);
  599. }
  600. i {
  601. position: absolute;
  602. right: 16px;
  603. top: 13px;
  604. }
  605. }
  606. .settingHeight {
  607. max-height: 250px;
  608. overflow: auto;
  609. }
  610. .settingsub {
  611. width: 140px;
  612. padding-left: 42px;
  613. i {
  614. position: absolute;
  615. left: 16px;
  616. }
  617. }
  618. }
  619. }
  620. }
  621. }
  622. video::-webkit-media-controls-fullscreen-button {
  623. display: none;
  624. }
  625. //所有控件
  626. video::-webkit-media-controls-enclosure {
  627. display: none;
  628. }
  629. </style>
  630. <style lang="scss">
  631. .choosedate {
  632. input {
  633. background-color: transparent;
  634. height: 24px;
  635. line-height: 24px;
  636. font-size: 14px;
  637. color: #fff;
  638. // padding: 0 10px !important;
  639. }
  640. .el-input__prefix {
  641. top: -2px;
  642. }
  643. }
  644. </style>