monitor-native.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. <template>
  2. <view class="mn-page">
  3. <view class="video-box">
  4. <video v-if="videoSrc" id="monitorVideo" class="video" :src="videoSrc" controls autoplay
  5. object-fit="contain" @error="onVideoError"></video>
  6. <view v-else class="video-mask" @click="startPlay">
  7. <image class="play-img" mode="widthFix"
  8. src="https://webstaticimg.oss-cn-hangzhou.aliyuncs.com/bigdata_app/img/wxplayer.png" />
  9. </view>
  10. </view>
  11. <view class="btn-box">
  12. <view class="side" @touchstart="ctrl(8)" @touchend="stopCtrl">
  13. <image class="side-img" mode="widthFix"
  14. src="https://s3.hnyfwlw.com/webstaticimg/bigdata_app/image/monitor/3.png" />
  15. </view>
  16. <view class="direc">
  17. <view class="pad top" @touchstart="ctrl(0)" @touchend="stopCtrl"></view>
  18. <view class="pad bottom" @touchstart="ctrl(1)" @touchend="stopCtrl"></view>
  19. <view class="pad left" @touchstart="ctrl(2)" @touchend="stopCtrl"></view>
  20. <view class="pad right" @touchstart="ctrl(3)" @touchend="stopCtrl"></view>
  21. <text class="photo yficonfont icon-paizhao-xianxing" @click="takePhoto"></text>
  22. </view>
  23. <view class="side" @touchstart="ctrl(9)" @touchend="stopCtrl">
  24. <image class="side-img" mode="widthFix"
  25. src="https://s3.hnyfwlw.com/webstaticimg/bigdata_app/image/monitor/2.png" />
  26. </view>
  27. </view>
  28. <view class="img-btn" @click="goImages">
  29. <image class="img-icon"
  30. src="https://webstaticimg.oss-cn-hangzhou.aliyuncs.com/bigdata_app/img/img_icon.png" />
  31. <text>{{ isDgp ? '查看光谱图片' : '查看图片' }}</text>
  32. </view>
  33. </view>
  34. </template>
  35. <script>
  36. const domin = 'https://wx.hnyfwlw.com'
  37. export default {
  38. name: 'monitor-native',
  39. props: {
  40. deviceId: {
  41. type: String,
  42. default: ''
  43. },
  44. isDgp: {
  45. type: Boolean,
  46. default: false
  47. }
  48. },
  49. data() {
  50. return {
  51. videoSrc: '',
  52. typeId: null,
  53. stopTimer: null
  54. }
  55. },
  56. computed: {
  57. token() {
  58. return uni.getStorageSync('session_key')
  59. }
  60. },
  61. methods: {
  62. request(method, data) {
  63. return new Promise((resolve, reject) => {
  64. uni.request({
  65. url: domin + '/api/api_gateway?method=' + method,
  66. method: 'POST',
  67. header: {
  68. 'Content-Type': 'application/x-www-form-urlencoded'
  69. },
  70. data: data,
  71. success: (res) => resolve(res.data || {}),
  72. fail: (err) => reject(err)
  73. })
  74. })
  75. },
  76. toast(title) {
  77. uni.showToast({
  78. title: title,
  79. icon: 'none'
  80. })
  81. },
  82. startPlay() {
  83. if (this.videoSrc) return
  84. const method = this.isDgp ? 'camera.camera_manage.multi_addr_camera' : 'camera.camera_manage.addr_camera'
  85. const data = this.isDgp ? {
  86. device_id: this.deviceId,
  87. token: this.token
  88. } : {
  89. device_id: this.deviceId,
  90. is_dahua_app: '1',
  91. token: this.token
  92. }
  93. uni.showLoading({
  94. title: '获取播放地址...',
  95. mask: true
  96. })
  97. this.request(method, data).then((res) => {
  98. uni.hideLoading()
  99. if (res.message == '') {
  100. let d = res.data
  101. if (typeof d === 'string') {
  102. try {
  103. d = JSON.parse(d)
  104. } catch (e) {
  105. d = null
  106. }
  107. }
  108. if (!d) return this.toast('播放地址解析失败')
  109. this.typeId = d.type_id
  110. let src = ''
  111. if (this.isDgp) {
  112. src = d.rtmp || d.hls || ''
  113. } else if (d.type_id == 2) {
  114. // 大华云联
  115. src = d.hls || d.hlsHd || d.rtsp || ''
  116. } else {
  117. src = d.hls || d.hlsHd || ''
  118. }
  119. if (!src) return this.toast('未获取到可播放的流地址')
  120. this.videoSrc = src
  121. } else {
  122. this.toast('获取播放地址失败: ' + res.message)
  123. }
  124. }).catch(() => {
  125. uni.hideLoading()
  126. this.toast('获取播放地址接口异常')
  127. })
  128. },
  129. ctrl(num) {
  130. const method = this.isDgp ? 'camera.camera_manage.multi_ctrl_camera' : 'camera.camera_manage.ctrl_camera'
  131. const data = this.isDgp ? {
  132. device_id: this.deviceId,
  133. ctrl: num,
  134. token: this.token
  135. } : {
  136. device_id: this.deviceId,
  137. ctrl: 'move',
  138. movenum: num,
  139. token: this.token
  140. }
  141. this.request(method, data).then(() => {
  142. this.toast('指令下发成功,请等待...')
  143. })
  144. },
  145. stopCtrl() {
  146. if (this.isDgp) {
  147. if (this.stopTimer) clearTimeout(this.stopTimer)
  148. this.stopTimer = setTimeout(() => {
  149. this.request('camera.camera_manage.mulit_stop_move', {
  150. device_id: this.deviceId,
  151. token: this.token
  152. })
  153. }, 3000)
  154. } else {
  155. this.request('camera.camera_manage.ctrl_camera', {
  156. device_id: this.deviceId,
  157. ctrl: 'stop',
  158. token: this.token
  159. })
  160. }
  161. },
  162. takePhoto() {
  163. let method, data
  164. if (this.isDgp) {
  165. method = 'camera.camera_manage.multi_camera_takephoto'
  166. data = {
  167. device_id: this.deviceId,
  168. token: this.token
  169. }
  170. } else if (this.typeId == 2) {
  171. // 大华云联:截取当前视频帧上传
  172. const ctx = uni.createVideoContext('monitorVideo', this)
  173. ctx.snapshot({
  174. success: (res) => this.uploadPic(res.tempImagePath),
  175. fail: () => this.toast('截取画面失败')
  176. })
  177. return
  178. } else {
  179. method = 'camera.camera_manage.camera_takephoto'
  180. data = {
  181. device_id: this.deviceId,
  182. token: this.token
  183. }
  184. }
  185. uni.showLoading({
  186. title: '拍照指令下发中...',
  187. mask: false
  188. })
  189. this.request(method, data).then((res) => {
  190. uni.hideLoading()
  191. this.toast(res.message == '' ? '拍照成功' : res.message)
  192. }).catch(() => {
  193. uni.hideLoading()
  194. this.toast('拍照接口异常')
  195. })
  196. },
  197. uploadPic(filePath) {
  198. uni.showLoading({
  199. title: '上传中...',
  200. mask: false
  201. })
  202. uni.uploadFile({
  203. url: domin + '/api/api_gateway?method=camera.camera_manage.save_camera_photo',
  204. filePath: filePath,
  205. name: 'img_file',
  206. formData: {
  207. device_id: this.deviceId,
  208. token: this.token
  209. },
  210. success: (res) => {
  211. uni.hideLoading()
  212. let message = ''
  213. try {
  214. message = (JSON.parse(res.data) || {}).message || ''
  215. } catch (e) {}
  216. this.toast(message == '' ? '拍照成功' : message)
  217. },
  218. fail: () => {
  219. uni.hideLoading()
  220. this.toast('上传失败')
  221. }
  222. })
  223. },
  224. goImages() {
  225. uni.navigateTo({
  226. url: this.isDgp ? '/pages/monitor/dgpImagelist?id=' + this.deviceId : '/pages/monitor/imagelist?id=' + this.deviceId
  227. })
  228. },
  229. onVideoError(e) {
  230. const msg = (e && e.detail && e.detail.errMsg) ? e.detail.errMsg : ''
  231. this.toast('视频播放失败' + (msg ? ': ' + msg : ''))
  232. }
  233. },
  234. beforeDestroy() {
  235. if (this.stopTimer) clearTimeout(this.stopTimer)
  236. }
  237. }
  238. </script>
  239. <style>
  240. @import '@/static/font/iconfont.css';
  241. .mn-page {
  242. background-color: #f3f3f3;
  243. min-height: 100vh;
  244. }
  245. .video-box {
  246. height: 300px;
  247. width: 100%;
  248. background: #000;
  249. overflow: hidden;
  250. position: relative;
  251. }
  252. .video {
  253. width: 100%;
  254. height: 300px;
  255. }
  256. .video-mask {
  257. position: absolute;
  258. top: 0;
  259. left: 0;
  260. right: 0;
  261. bottom: 0;
  262. display: flex;
  263. align-items: center;
  264. justify-content: center;
  265. }
  266. .play-img {
  267. width: 100px;
  268. height: 100px;
  269. }
  270. .btn-box {
  271. margin-top: 20px;
  272. display: flex;
  273. flex-direction: row;
  274. justify-content: space-around;
  275. align-items: center;
  276. }
  277. .side {
  278. flex: 1;
  279. }
  280. .side-img {
  281. width: 100%;
  282. }
  283. .direc {
  284. width: 150px;
  285. height: 150px;
  286. background: url('https://s3.hnyfwlw.com/webstaticimg/bigdata_app/image/monitor/1.png');
  287. background-size: 100% auto;
  288. background-repeat: no-repeat;
  289. position: relative;
  290. display: flex;
  291. justify-content: center;
  292. align-items: center;
  293. }
  294. .pad {
  295. width: 50px;
  296. height: 50px;
  297. position: absolute;
  298. }
  299. .top {
  300. top: 0;
  301. left: 50px;
  302. }
  303. .bottom {
  304. bottom: 0;
  305. left: 50px;
  306. }
  307. .left {
  308. left: 0;
  309. top: 50px;
  310. }
  311. .right {
  312. right: 0;
  313. top: 50px;
  314. }
  315. .photo {
  316. font-size: 30px;
  317. color: #4ec467;
  318. padding: 16px;
  319. }
  320. .img-btn {
  321. padding: 10px;
  322. display: flex;
  323. flex-direction: row;
  324. justify-content: center;
  325. align-items: center;
  326. color: #338cd9;
  327. font-size: 16px;
  328. }
  329. .img-icon {
  330. width: 20px;
  331. height: 20px;
  332. margin-right: 4px;
  333. }
  334. </style>