GBRecordDetail.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. <template>
  2. <div style="width: 100%">
  3. <div class="page-header" >
  4. <div class="page-title">
  5. <el-page-header @back="goBack" content="国标录像"></el-page-header>
  6. </div>
  7. </div>
  8. <el-container>
  9. <el-aside width="300px">
  10. <div class="record-list-box-box">
  11. <el-date-picker size="mini" v-model="chooseDate" type="date" value-format="yyyy-MM-dd" placeholder="日期" @change="dateChange()"></el-date-picker>
  12. <div class="record-list-box" v-loading="recordsLoading" :style="recordListStyle">
  13. <ul v-if="detailFiles.length >0" class="infinite-list record-list" >
  14. <li v-for="item in detailFiles" class="infinite-list-item record-list-item" >
  15. <el-tag v-if="chooseFile != item" @click="checkedFile(item)">
  16. <i class="el-icon-video-camera" ></i>
  17. {{ moment(item.startTime).format('HH:mm:ss')}}-{{ moment(item.endTime).format('HH:mm:ss')}}
  18. </el-tag>
  19. <el-tag v-if="chooseFile == item" type="danger" >
  20. <i class="el-icon-video-camera" ></i>
  21. {{ moment(item.startTime).format('HH:mm:ss')}}-{{ moment(item.endTime).format('HH:mm:ss')}}
  22. </el-tag>
  23. <i style="color: #409EFF;margin-left: 5px;" class="el-icon-download" @click="downloadRecord(item)" ></i>
  24. </li>
  25. </ul>
  26. </div>
  27. <div size="mini" v-if="detailFiles.length ==0" class="record-list-no-val" >暂无数据</div>
  28. </div>
  29. </el-aside>
  30. <el-main style="padding-bottom: 10px;">
  31. <div class="playBox" :style="playerStyle">
  32. <player ref="recordVideoPlayer"
  33. :videoUrl="videoUrl"
  34. :error="videoError"
  35. :message="videoError"
  36. :hasAudio="hasAudio"
  37. style="max-height: 100%"
  38. fluent autoplay live ></player>
  39. </div>
  40. <div class="player-option-box">
  41. <div>
  42. <el-button-group >
  43. <el-time-picker
  44. size="mini"
  45. is-range
  46. align="left"
  47. v-model="timeRange"
  48. value-format="yyyy-MM-dd HH:mm:ss"
  49. range-separator="至"
  50. start-placeholder="开始时间"
  51. end-placeholder="结束时间"
  52. @change="timePickerChange"
  53. placeholder="选择时间范围">
  54. </el-time-picker>
  55. </el-button-group>
  56. <el-button-group >
  57. <el-button size="mini" class="iconfont icon-zanting" title="开始" @click="gbPause()"></el-button>
  58. <el-button size="mini" class="iconfont icon-kaishi" title="暂停" @click="gbPlay()"></el-button>
  59. <el-dropdown size="mini" title="播放倍速" @command="gbScale">
  60. <el-button size="mini">
  61. 倍速 <i class="el-icon-arrow-down el-icon--right"></i>
  62. </el-button>
  63. <el-dropdown-menu slot="dropdown">
  64. <el-dropdown-item command="0.25">0.25倍速</el-dropdown-item>
  65. <el-dropdown-item command="0.5">0.5倍速</el-dropdown-item>
  66. <el-dropdown-item command="1.0">1倍速</el-dropdown-item>
  67. <el-dropdown-item command="2.0">2倍速</el-dropdown-item>
  68. <el-dropdown-item command="4.0">4倍速</el-dropdown-item>
  69. </el-dropdown-menu>
  70. </el-dropdown>
  71. <el-button size="mini" class="iconfont icon-xiazai1" title="下载选定录像" @click="downloadRecord()"></el-button>
  72. <el-button v-if="sliderMIn === 0 && sliderMax === 86400" size="mini" class="iconfont icon-slider" title="放大滑块" @click="setSliderFit()"></el-button>
  73. <el-button v-if="sliderMIn !== 0 || sliderMax !== 86400" size="mini" class="iconfont icon-slider-right" title="恢复滑块" @click="setSliderFit()"></el-button>
  74. </el-button-group>
  75. </div>
  76. <el-slider
  77. class="playtime-slider"
  78. v-model="playTime"
  79. id="playtimeSlider"
  80. :disabled="detailFiles.length === 0"
  81. :min="sliderMIn"
  82. :max="sliderMax"
  83. :range="true"
  84. :format-tooltip="playTimeFormat"
  85. @change="playTimeChange"
  86. :marks="playTimeSliderMarks">
  87. </el-slider>
  88. <div class="slider-val-box">
  89. <div class="slider-val" v-for="item of detailFiles" :style="'width:' + getDataWidth(item) + '%; left:' + getDataLeft(item) + '%'"></div>
  90. </div>
  91. </div>
  92. </el-main>
  93. </el-container>
  94. <recordDownload ref="recordDownload"></recordDownload>
  95. </div>
  96. </template>
  97. <script>
  98. import uiHeader from '../layout/UiHeader.vue'
  99. import player from './common/jessibuca.vue'
  100. import moment from 'moment'
  101. import recordDownload from './dialog/recordDownload.vue'
  102. export default {
  103. name: 'app',
  104. components: {
  105. uiHeader, player,recordDownload
  106. },
  107. data() {
  108. return {
  109. deviceId: this.$route.params.deviceId,
  110. channelId: this.$route.params.channelId,
  111. recordsLoading: false,
  112. streamId: "",
  113. hasAudio: false,
  114. detailFiles: [],
  115. chooseDate: null,
  116. videoUrl: null,
  117. chooseFile: null,
  118. streamInfo: null,
  119. app: null,
  120. mediaServerId: null,
  121. ssrc: null,
  122. sliderMIn: 0,
  123. sliderMax: 86400,
  124. autoPlay: true,
  125. taskUpdate: null,
  126. tabVal: "running",
  127. recordListStyle: {
  128. height: this.winHeight + "px",
  129. overflow: "auto",
  130. margin: "10px auto 10px auto"
  131. },
  132. playerStyle: {
  133. "margin": "0 auto 20px auto",
  134. "height": this.winHeight + "px",
  135. },
  136. winHeight: window.innerHeight - 240,
  137. playTime: null,
  138. timeRange: null,
  139. startTime: null,
  140. endTime: null,
  141. playTimeSliderMarks: {
  142. 0: "00:00",
  143. 3600: "01:00",
  144. 7200: "02:00",
  145. 10800: "03:00",
  146. 14400: "04:00",
  147. 18000: "05:00",
  148. 21600: "06:00",
  149. 25200: "07:00",
  150. 28800: "08:00",
  151. 32400: "09:00",
  152. 36000: "10:00",
  153. 39600: "11:00",
  154. 43200: "12:00",
  155. 46800: "13:00",
  156. 50400: "14:00",
  157. 54000: "15:00",
  158. 57600: "16:00",
  159. 61200: "17:00",
  160. 64800: "18:00",
  161. 68400: "19:00",
  162. 72000: "20:00",
  163. 75600: "21:00",
  164. 79200: "22:00",
  165. 82800: "23:00",
  166. 86400: "24:00",
  167. },
  168. };
  169. },
  170. computed: {
  171. },
  172. mounted() {
  173. this.recordListStyle.height = this.winHeight + "px";
  174. this.playerStyle["height"] = this.winHeight + "px";
  175. this.chooseDate = moment().format('YYYY-MM-DD')
  176. this.dateChange();
  177. },
  178. destroyed() {
  179. this.$destroy('recordVideoPlayer');
  180. },
  181. methods: {
  182. dateChange(){
  183. if (!this.chooseDate) {
  184. return;
  185. }
  186. this.setTime(this.chooseDate + " 00:00:00", this.chooseDate + " 23:59:59");
  187. this.recordsLoading = true;
  188. this.detailFiles = [];
  189. this.$axios.axios({
  190. method: 'get',
  191. url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' + this.endTime
  192. }).then((res)=>{
  193. this.recordsLoading = false;
  194. if(res.data.code === 0) {
  195. // 处理时间信息
  196. this.detailFiles = res.data.data.recordList;
  197. }else {
  198. this.$message({
  199. showClose: true,
  200. message: res.data.msg,
  201. type: "error",
  202. });
  203. }
  204. }).catch((e)=> {
  205. this.recordsLoading = false;
  206. // that.videoHistory.searchHistoryResult = falsificationData.recordData;
  207. });
  208. },
  209. moment: function (v) {
  210. return moment(v)
  211. },
  212. setTime: function (startTime, endTime){
  213. this.startTime = startTime;
  214. this.endTime = endTime;
  215. let start = (new Date(this.startTime).getTime() - new Date(this.chooseDate + " 00:00:00").getTime())/1000;
  216. let end = (new Date(this.endTime).getTime() - new Date(this.chooseDate + " 00:00:00").getTime())/1000;
  217. console.log(start)
  218. console.log(end)
  219. this.playTime = [start, end];
  220. this.timeRange = [startTime, endTime];
  221. },
  222. videoError: function (e) {
  223. console.log("播放器错误:" + JSON.stringify(e));
  224. },
  225. checkedFile(file){
  226. this.chooseFile = file;
  227. this.setTime(file.startTime, file.endTime);
  228. // 开始回放
  229. this.playRecord()
  230. },
  231. playRecord: function () {
  232. if (this.streamId !== "") {
  233. this.stopPlayRecord(()=> {
  234. this.streamId = "";
  235. this.playRecord();
  236. })
  237. } else {
  238. this.$axios.axios({
  239. method: 'get',
  240. url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' +
  241. this.endTime
  242. }).then((res)=> {
  243. if (res.data.code === 0) {
  244. this.streamInfo = res.data.data;
  245. this.app = this.streamInfo.app;
  246. this.streamId = this.streamInfo.stream;
  247. this.mediaServerId = this.streamInfo.mediaServerId;
  248. this.ssrc = this.streamInfo.ssrc;
  249. this.videoUrl = this.getUrlByStreamInfo();
  250. }else {
  251. this.$message({
  252. showClose: true,
  253. message: res.data.msg,
  254. type: "error",
  255. });
  256. }
  257. });
  258. }
  259. },
  260. gbPlay(){
  261. console.log('前端控制:播放');
  262. this.$axios.axios({
  263. method: 'get',
  264. url: '/api/playback/resume/' + this.streamId
  265. }).then((res)=> {
  266. this.$refs["recordVideoPlayer"].play(this.videoUrl)
  267. });
  268. },
  269. gbPause(){
  270. console.log('前端控制:暂停');
  271. this.$axios.axios({
  272. method: 'get',
  273. url: '/api/playback/pause/' + this.streamId
  274. }).then(function (res) {});
  275. },
  276. gbScale(command){
  277. console.log('前端控制:倍速 ' + command);
  278. this.$axios.axios({
  279. method: 'get',
  280. url: `/api/playback/speed/${this.streamId }/${command}`
  281. }).then(function (res) {});
  282. },
  283. downloadRecord: function (row) {
  284. if (!row) {
  285. let startTimeStr = moment(new Date(this.chooseDate + " 00:00:00").getTime() + this.playTime[0]*1000).format("YYYY-MM-DD HH:mm:ss");
  286. let endTimeStr = moment(new Date(this.chooseDate + " 00:00:00").getTime() + this.playTime[1]*1000).format("YYYY-MM-DD HH:mm:ss");
  287. console.log(startTimeStr);
  288. console.log(endTimeStr);
  289. row = {
  290. startTime: startTimeStr,
  291. endTime: endTimeStr
  292. }
  293. }
  294. if (this.streamId !== "") {
  295. this.stopPlayRecord(()=> {
  296. this.streamId = "";
  297. this.downloadRecord(row);
  298. })
  299. }else {
  300. this.$axios.axios({
  301. method: 'get',
  302. url: '/api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
  303. row.endTime + '&downloadSpeed=4'
  304. }).then( (res)=> {
  305. if (res.data.code === 0) {
  306. let streamInfo = res.data.data;
  307. this.$refs.recordDownload.openDialog(this.deviceId, this.channelId, streamInfo.app, streamInfo.stream, streamInfo.mediaServerId);
  308. }else {
  309. this.$message({
  310. showClose: true,
  311. message: res.data.msg,
  312. type: "error",
  313. });
  314. }
  315. });
  316. }
  317. },
  318. stopDownloadRecord: function (callback) {
  319. this.$refs["recordVideoPlayer"].pause();
  320. this.videoUrl = '';
  321. this.$axios.axios({
  322. method: 'get',
  323. url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId
  324. }).then((res)=> {
  325. if (callback) callback(res)
  326. });
  327. },
  328. stopPlayRecord: function (callback) {
  329. this.$refs["recordVideoPlayer"].pause();
  330. this.videoUrl = '';
  331. this.$axios.axios({
  332. method: 'get',
  333. url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
  334. }).then(function (res) {
  335. if (callback) callback()
  336. });
  337. },
  338. getDataWidth(item){
  339. let timeForFile = this.getTimeForFile(item);
  340. let result = (timeForFile[2])/((this.sliderMax - this.sliderMIn)*1000)
  341. return result*100
  342. },
  343. getDataLeft(item){
  344. let timeForFile = this.getTimeForFile(item);
  345. let differenceTime = timeForFile[0].getTime() - new Date(this.chooseDate + " 00:00:00").getTime()
  346. return parseFloat((differenceTime - this.sliderMIn * 1000)/((this.sliderMax - this.sliderMIn)*1000))*100 ;
  347. },
  348. getUrlByStreamInfo(){
  349. if (location.protocol === "https:") {
  350. this.videoUrl = this.streamInfo["wss_flv"]
  351. }else {
  352. this.videoUrl = this.streamInfo["ws_flv"]
  353. }
  354. return this.videoUrl;
  355. },
  356. timePickerChange: function (val){
  357. this.setTime(val[0], val[1])
  358. },
  359. playTimeChange(val){
  360. console.log(val)
  361. let startTimeStr = moment(new Date(this.chooseDate + " 00:00:00").getTime() + val[0]*1000).format("YYYY-MM-DD HH:mm:ss");
  362. let endTimeStr = moment(new Date(this.chooseDate + " 00:00:00").getTime() + val[1]*1000).format("YYYY-MM-DD HH:mm:ss");
  363. this.setTime(startTimeStr, endTimeStr)
  364. this.playRecord();
  365. },
  366. setSliderFit() {
  367. if (this.sliderMIn === 0 && this.sliderMax === 86400) {
  368. if (this.detailFiles.length > 0){
  369. let timeForFile = this.getTimeForFile(this.detailFiles[0]);
  370. let lastTimeForFile = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1]);
  371. let timeNum = timeForFile[0].getTime() - new Date(this.chooseDate + " " + "00:00:00").getTime()
  372. let lastTimeNum = lastTimeForFile[1].getTime() - new Date(this.chooseDate + " " + "00:00:00").getTime()
  373. this.playTime = parseInt(timeNum/1000)
  374. this.sliderMIn = parseInt(timeNum/1000 - timeNum/1000%(60*60))
  375. this.sliderMax = parseInt(lastTimeNum/1000 - lastTimeNum/1000%(60*60)) + 60*60
  376. this.playTime = [this.sliderMIn, this.sliderMax];
  377. }
  378. }else {
  379. this.sliderMIn = 0;
  380. this.sliderMax = 86400;
  381. }
  382. },
  383. getTimeForFile(file){
  384. let startTime = new Date(file.startTime);
  385. let endTime = new Date(file.endTime);
  386. return [startTime, endTime, endTime.getTime() - startTime.getTime()];
  387. },
  388. playTimeFormat(val){
  389. let h = parseInt(val/3600);
  390. let m = parseInt((val - h*3600)/60);
  391. let s = parseInt(val - h*3600 - m*60);
  392. let hStr = h;
  393. let mStr = m;
  394. let sStr = s;
  395. if (h < 10) {
  396. hStr = "0" + hStr;
  397. }
  398. if (m < 10) {
  399. mStr = "0" + mStr;s
  400. }
  401. if (s < 10) {
  402. sStr = "0" + sStr;
  403. }
  404. return hStr + ":" + mStr + ":" + sStr
  405. },
  406. goBack(){
  407. window.history.go(-1);
  408. }
  409. }
  410. };
  411. </script>
  412. <style>
  413. .el-slider__runway {
  414. background-color:rgba(206, 206, 206, 0.47) !important;
  415. }
  416. .el-slider__bar {
  417. background-color: rgba(153, 153, 153, 0) !important;
  418. }
  419. .playtime-slider {
  420. position: relative;
  421. z-index: 100;
  422. }
  423. .data-picker-true{
  424. }
  425. .data-picker-true:after{
  426. content: "";
  427. position: absolute;
  428. width: 4px;
  429. height: 4px;
  430. background-color: #606060;
  431. border-radius: 4px;
  432. left: 45%;
  433. top: 74%;
  434. }
  435. .data-picker-false{
  436. }
  437. .slider-val-box{
  438. height: 6px;
  439. position: relative;
  440. top: -22px;
  441. }
  442. .slider-val{
  443. height: 6px;
  444. background-color: #007CFF;
  445. position: absolute;
  446. }
  447. .record-list-box-box{
  448. width: 250px;
  449. float: left;
  450. }
  451. .record-list-box{
  452. overflow: auto;
  453. width: 220px;
  454. list-style: none;
  455. padding: 0;
  456. margin: 0;
  457. margin-top: 0px;
  458. padding: 1rem 0;
  459. background-color: #FFF;
  460. margin-top: 10px;
  461. }
  462. .record-list{
  463. list-style: none;
  464. padding: 0;
  465. margin: 0;
  466. background-color: #FFF;
  467. }
  468. .record-list-no-val {
  469. position: absolute;
  470. color: #9f9f9f;
  471. top: 50%;
  472. left: 110px;
  473. }
  474. .record-list-item{
  475. padding: 0;
  476. margin: 0;
  477. margin: 0.5rem 0;
  478. cursor: pointer;
  479. }
  480. .record-list-option {
  481. width: 10px;
  482. float: left;
  483. margin-top: 39px;
  484. }
  485. .player-option-box{
  486. padding: 0 20px;
  487. }
  488. </style>