live.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. <template>
  2. <div id="devicePosition" style="width:100vw; height: 91vh">
  3. <el-container v-loading="loading" style="height: 91vh;" element-loading-text="拼命加载中">
  4. <el-aside width="300px" style="background-color: #ffffff">
  5. <DeviceTree :clickEvent="clickEvent" :contextMenuEvent="contextMenuEvent"></DeviceTree>
  6. </el-aside>
  7. <el-container>
  8. <el-header height="5vh" style="text-align: left;font-size: 17px;line-height:5vh">
  9. 分屏:
  10. <i class="el-icon-full-screen btn" :class="{active:spilt==1}" @click="spilt=1"/>
  11. <i class="el-icon-menu btn" :class="{active:spilt==4}" @click="spilt=4"/>
  12. <i class="el-icon-s-grid btn" :class="{active:spilt==9}" @click="spilt=9"/>
  13. </el-header>
  14. <el-main style="padding: 0;">
  15. <div style="width: 99%;height: 85vh;display: flex;flex-wrap: wrap;background-color: #000;">
  16. <div v-for="i in spilt" :key="i" class="play-box"
  17. :style="liveStyle" :class="{redborder:playerIdx == (i-1)}"
  18. @click="playerIdx = (i-1)">
  19. <div v-if="!videoUrl[i-1]" style="color: #ffffff;font-size: 30px;font-weight: bold;">{{ i }}</div>
  20. <player ref="player" v-else :videoUrl="videoUrl[i-1]" fluent autoplay @screenshot="shot"
  21. @destroy="destroy"/>
  22. </div>
  23. </div>
  24. </el-main>
  25. </el-container>
  26. </el-container>
  27. </div>
  28. </template>
  29. <script>
  30. import uiHeader from "../layout/UiHeader.vue";
  31. import player from './common/jessibuca.vue'
  32. import DeviceTree from './common/DeviceTree.vue'
  33. export default {
  34. name: "live",
  35. components: {
  36. uiHeader, player, DeviceTree
  37. },
  38. data() {
  39. return {
  40. videoUrl: [''],
  41. spilt: 1,//分屏
  42. playerIdx: 0,//激活播放器
  43. updateLooper: 0, //数据刷新轮训标志
  44. count: 15,
  45. total: 0,
  46. //channel
  47. loading: false
  48. };
  49. },
  50. mounted() {
  51. },
  52. created() {
  53. this.checkPlayByParam()
  54. },
  55. computed: {
  56. liveStyle() {
  57. let style = {width: '100%', height: '100%'}
  58. switch (this.spilt) {
  59. case 4:
  60. style = {width: '49%', height: '49%'}
  61. break
  62. case 9:
  63. style = {width: '32%', height: '32%'}
  64. break
  65. }
  66. this.$nextTick(() => {
  67. for (let i = 0; i < this.spilt; i++) {
  68. const player = this.$refs.player
  69. player && player[i] && player[i].updatePlayerDomSize()
  70. }
  71. })
  72. return style
  73. }
  74. },
  75. watch: {
  76. spilt(newValue) {
  77. console.log("切换画幅;" + newValue)
  78. let that = this
  79. for (let i = 1; i <= newValue; i++) {
  80. if (!that.$refs['player' + i]) {
  81. continue
  82. }
  83. this.$nextTick(() => {
  84. if (that.$refs['player' + i] instanceof Array) {
  85. that.$refs['player' + i][0].resize()
  86. } else {
  87. that.$refs['player' + i].resize()
  88. }
  89. })
  90. }
  91. window.localStorage.setItem('split', newValue)
  92. },
  93. '$route.fullPath': 'checkPlayByParam'
  94. },
  95. destroyed() {
  96. clearTimeout(this.updateLooper);
  97. },
  98. methods: {
  99. destroy(idx) {
  100. console.log(idx);
  101. this.clear(idx.substring(idx.length - 1))
  102. },
  103. clickEvent: function (device, data, isCatalog) {
  104. if (data.channelId && !isCatalog) {
  105. if (device.online === 0) {
  106. this.$message.error('设备离线!不允许点播');
  107. }else {
  108. this.sendDevicePush(data)
  109. }
  110. }
  111. },
  112. contextMenuEvent: function (device, event, data, isCatalog) {
  113. },
  114. //通知设备上传媒体流
  115. sendDevicePush: function (itemData) {
  116. // if (itemData.status === 0) {
  117. // this.$message.error('设备离线!');
  118. // return
  119. // }
  120. this.save(itemData)
  121. let deviceId = itemData.deviceId;
  122. // this.isLoging = true;
  123. let channelId = itemData.channelId;
  124. console.log("通知设备推流1:" + deviceId + " : " + channelId);
  125. let idxTmp = this.playerIdx
  126. let that = this;
  127. this.loading = true
  128. this.$axios({
  129. method: 'get',
  130. url: '/api/play/start/' + deviceId + '/' + channelId
  131. }).then(function (res) {
  132. if (res.data.code === 0 && res.data.data) {
  133. itemData.playUrl = res.data.data.httpsFlv
  134. that.setPlayUrl(res.data.data.ws_flv, idxTmp)
  135. } else {
  136. that.$message.error(res.data.msg);
  137. }
  138. }).catch(function (e) {
  139. }).finally(() => {
  140. that.loading = false
  141. });
  142. },
  143. setPlayUrl(url, idx) {
  144. this.$set(this.videoUrl, idx, url)
  145. let _this = this
  146. setTimeout(() => {
  147. window.localStorage.setItem('videoUrl', JSON.stringify(_this.videoUrl))
  148. }, 100)
  149. },
  150. checkPlayByParam() {
  151. let {deviceId, channelId} = this.$route.query
  152. if (deviceId && channelId) {
  153. this.sendDevicePush({deviceId, channelId})
  154. }
  155. },
  156. shot(e) {
  157. // console.log(e)
  158. // send({code:'image',data:e})
  159. var base64ToBlob = function (code) {
  160. let parts = code.split(';base64,');
  161. let contentType = parts[0].split(':')[1];
  162. let raw = window.atob(parts[1]);
  163. let rawLength = raw.length;
  164. let uInt8Array = new Uint8Array(rawLength);
  165. for (let i = 0; i < rawLength; ++i) {
  166. uInt8Array[i] = raw.charCodeAt(i);
  167. }
  168. return new Blob([uInt8Array], {
  169. type: contentType
  170. });
  171. };
  172. let aLink = document.createElement('a');
  173. let blob = base64ToBlob(e); //new Blob([content]);
  174. let evt = document.createEvent("HTMLEvents");
  175. evt.initEvent("click", true, true); //initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
  176. aLink.download = '截图';
  177. aLink.href = URL.createObjectURL(blob);
  178. aLink.click();
  179. },
  180. save(item) {
  181. let dataStr = window.localStorage.getItem('playData') || '[]'
  182. let data = JSON.parse(dataStr);
  183. data[this.playerIdx] = item
  184. window.localStorage.setItem('playData', JSON.stringify(data))
  185. },
  186. clear(idx) {
  187. let dataStr = window.localStorage.getItem('playData') || '[]'
  188. let data = JSON.parse(dataStr);
  189. data[idx - 1] = null;
  190. console.log(data);
  191. window.localStorage.setItem('playData', JSON.stringify(data))
  192. },
  193. }
  194. };
  195. </script>
  196. <style>
  197. .btn {
  198. margin: 0 10px;
  199. }
  200. .btn:hover {
  201. color: #409EFF;
  202. }
  203. .btn.active {
  204. color: #409EFF;
  205. }
  206. .redborder {
  207. border: 2px solid red !important;
  208. }
  209. .play-box {
  210. background-color: #000000;
  211. border: 2px solid #505050;
  212. display: flex;
  213. align-items: center;
  214. justify-content: center;
  215. }
  216. </style>
  217. <style>
  218. .videoList {
  219. display: flex;
  220. flex-wrap: wrap;
  221. align-content: flex-start;
  222. }
  223. .video-item {
  224. position: relative;
  225. width: 15rem;
  226. height: 10rem;
  227. margin-right: 1rem;
  228. background-color: #000000;
  229. }
  230. .video-item-img {
  231. position: absolute;
  232. top: 0;
  233. bottom: 0;
  234. left: 0;
  235. right: 0;
  236. margin: auto;
  237. width: 100%;
  238. height: 100%;
  239. }
  240. .video-item-img:after {
  241. content: "";
  242. display: inline-block;
  243. position: absolute;
  244. z-index: 2;
  245. top: 0;
  246. bottom: 0;
  247. left: 0;
  248. right: 0;
  249. margin: auto;
  250. width: 3rem;
  251. height: 3rem;
  252. background-image: url("../assets/loading.png");
  253. background-size: cover;
  254. background-color: #000000;
  255. }
  256. .video-item-title {
  257. position: absolute;
  258. bottom: 0;
  259. color: #000000;
  260. background-color: #ffffff;
  261. line-height: 1.5rem;
  262. padding: 0.3rem;
  263. width: 14.4rem;
  264. }
  265. .baidumap {
  266. width: 100%;
  267. height: 100%;
  268. border: none;
  269. position: absolute;
  270. left: 0;
  271. top: 0;
  272. right: 0;
  273. bottom: 0;
  274. margin: auto;
  275. }
  276. /* 去除百度地图版权那行字 和 百度logo */
  277. .baidumap > .BMap_cpyCtrl {
  278. display: none !important;
  279. }
  280. .baidumap > .anchorBL {
  281. display: none !important;
  282. }
  283. </style>