mobile.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. <template>
  2. <div>
  3. <el-dialog v-el-drag-dialog :visible.sync="showMobile" title="" :close-on-click-modal="false" :close-on-press-escape="false" :custom-class="mobileStyle" @close="close">
  4. <div slot="title" class="cfff fs14">
  5. <span class="ml20">{{ mobileName }}</span>
  6. <el-dropdown placement="bottom" class="fr mr100 oln" trigger="hover" @command="handleCommand" @visible-change="handleVisble">
  7. <span class="fs14 cfff">{{ bitRateText }}<i :class="caretIcon" /></span>
  8. <el-dropdown-menu slot="dropdown" class="mobile-dropdown-menu">
  9. <el-dropdown-item :class="bitRateStyle('1638400')" command="1638400">自动</el-dropdown-item>
  10. <el-dropdown-item :class="bitRateStyle('491520')" command="491520">极速</el-dropdown-item>
  11. <el-dropdown-item :class="bitRateStyle('1392640')" command="1392640">标清</el-dropdown-item>
  12. <el-dropdown-item :class="bitRateStyle('2785280')" command="2785280">高清</el-dropdown-item>
  13. </el-dropdown-menu>
  14. </el-dropdown>
  15. </div>
  16. <div class="frns">
  17. <div class="mobile-audio">
  18. <video
  19. ref="player"
  20. width="372px"
  21. :controls="false"
  22. autoplay
  23. @play="ready"
  24. @error="error"
  25. @pause="pause"
  26. @mousedown="handleMouseDown"
  27. @mousemove="handleMouseMove"
  28. @mouseup="handleMouseUp"
  29. @mouseleave="handleMouseLeave"
  30. />
  31. <audio
  32. ref="audioPlayer"
  33. width="372px"
  34. controls
  35. autoplay
  36. poster="@/assets/equipment/loader-thumb.jpg"
  37. />
  38. <div v-if="isMask" class="mask">
  39. <i v-if="isLoading" class="el-icon-loading" />
  40. <img v-if="isError" class="w180h148" src="@/assets/equipment/guzhang_lixian_pic.png" alt="">
  41. <div class="c999 fs12 mt15">{{ maskText }}</div>
  42. </div>
  43. </div>
  44. <div class="mobile-sidebar">
  45. <i :class="volumeUpIcon" @click="handleRoutine(24)" />
  46. <i :class="volumeDownIcon" @click="handleRoutine(25)" />
  47. <el-popover placement="bottom-start" popper-class="nav-popover" width="109" trigger="hover">
  48. <div class="tac ptb10 c333 fs14">重启</div>
  49. <i slot="reference" :class="shutDownIcon" @click="reboot" />
  50. </el-popover>
  51. <el-popover placement="bottom-start" popper-class="nav-popover" width="109" trigger="hover">
  52. <div class="tac ptb10 c333 fs14">恢复出厂设置</div>
  53. <i slot="reference" :class="restartIcon" @click="recovery" />
  54. </el-popover>
  55. </div>
  56. </div>
  57. <div class="mobile-bottombar">
  58. <i :class="menuIcon" @click="handleRoutine(187)" />
  59. <i :class="homeIcon" @click="handleRoutine(3)" />
  60. <i :class="backIcon" @click="handleRoutine(4)" />
  61. </div>
  62. </el-dialog>
  63. </div>
  64. </template>
  65. <script>
  66. import elDragDialog from '@/directive/el-drag-dialog'
  67. import JMuxer from 'jmuxer'
  68. import { getcardInfoBySn, rebootBatch, recovery, getcardStatus } from '@/api/mobile'
  69. import { ExexuteMouseDown, ExexuteMouseMove, ExexuteMouseUp, ExexuteKeyDown, ExexutePixel, ExexuteCloseServer, CheckScreenDirection, RequestIFrame, ConfigChannel } from '@/utils/control'
  70. import { keycodeMode } from '@/utils/config'
  71. const VIDEO_WIDTH = 371
  72. const VIDEO_HEIGHT = 660
  73. export default {
  74. name: 'DragDialogDemo',
  75. directives: { elDragDialog },
  76. props: {
  77. sn: {
  78. type: Array,
  79. default: () => {
  80. return []
  81. }
  82. },
  83. mobileName: {
  84. type: String,
  85. default: ''
  86. },
  87. mobileId: {
  88. type: Number,
  89. default: 0
  90. }
  91. },
  92. data() {
  93. return {
  94. showMobile: true,
  95. fpsCount: 0,
  96. timeCount: 0,
  97. isFeed: true,
  98. curTime: new Date().getTime(),
  99. requestTime: new Date().getTime(),
  100. jmuxer: null,
  101. audioMuxer: null,
  102. ip: '', // 设备ip
  103. port: '', // 设备端口号
  104. size: '', // 设备分辨率
  105. bitRate: '', // 设备清晰度
  106. path: 'ws://192.168.11.66:9101', // websocket地址
  107. socket: null, // websocket类
  108. isDrag: false,
  109. isLoading: true, // 加载中
  110. isMask: true, // 遮罩层
  111. maskText: '', // 加载文字
  112. isError: false, // 是否设备故障或者离线
  113. visble: false, // 下拉菜单是否展开
  114. isRotate: false, // 是否横屏
  115. isFlag: false
  116. }
  117. },
  118. computed: {
  119. bitRateStyle() {
  120. return (num) => {
  121. return this.bitRate === num ? 'pl20 bgcf3f6ff c515ff0 fs14' : 'pl20 c333 fs14'
  122. }
  123. },
  124. bitRateText() {
  125. /**
  126. * 极速 | 491520,60KB/S
  127. * 标清 | 1392640 ,170KB/S
  128. * 自动 | 1638400 ,200KB/S
  129. * 高清 | 2785280 ,340KB/S
  130. */
  131. const option = {
  132. '1638400': '自动',
  133. '491520': '极速',
  134. '1392640': '标清',
  135. '2785280': '高清'
  136. }
  137. return option[this.bitRate]
  138. },
  139. volumeUpIcon() {
  140. return this.isLoading || this.isError ? 'icon-volume-up disabled mt50' : 'icon-volume-up mt50'
  141. },
  142. volumeDownIcon() {
  143. return this.isLoading || this.isError ? 'icon-volume-down disabled mt40' : 'icon-volume-down mt40'
  144. },
  145. shutDownIcon() {
  146. return this.isLoading || this.isError ? 'icon-shutdown disabled mt226ormt90' : 'icon-shutdown mt226ormt90'
  147. },
  148. restartIcon() {
  149. return this.isLoading || this.isError ? 'icon-restart disabled mt40' : 'icon-restart mt40'
  150. },
  151. menuIcon() {
  152. return this.isLoading || this.isError ? 'icon-background disabled' : 'icon-background'
  153. },
  154. homeIcon() {
  155. return this.isLoading || this.isError ? 'icon-index disabled mlr64ormlr160' : 'icon-index mlr64ormlr160'
  156. },
  157. backIcon() {
  158. return this.isLoading || this.isError ? 'icon-back disabled' : 'icon-back'
  159. },
  160. caretIcon() {
  161. return this.visble ? 'el-icon-caret-top ml5' : 'el-icon-caret-bottom ml5'
  162. },
  163. mobileStyle() {
  164. return this.isRotate ? 'mobile-dialog rotate' : 'mobile-dialog'
  165. }
  166. },
  167. created() {
  168. this.getcardInfoBySn()
  169. },
  170. mounted() {
  171. document.addEventListener('visibilitychange', this.audioPlay)
  172. document.addEventListener('keydown', this.handleKeyDown)
  173. this.$nextTick(() => {
  174. this.jmuxer = new JMuxer({
  175. node: this.$refs.player,
  176. flushingTime: 15,
  177. fps: 30,
  178. mode: 'video',
  179. debug: false
  180. })
  181. this.audioMuxer = new JMuxer({
  182. node: this.$refs.audioPlayer,
  183. flushingTime: 1,
  184. clearBuffer: true,
  185. fps: 43, // 可以不选
  186. mode: 'audio',
  187. debug: false
  188. })
  189. })
  190. this.curTime = new Date().getTime()
  191. },
  192. destroyed() {
  193. document.removeEventListener('visibilitychange', this.audioPlay)
  194. document.removeEventListener('keydown', this.handleKeyDown)
  195. this.socket.send(ExexuteCloseServer(this.sn.join(','))) // 销毁监听
  196. this.socket.close()
  197. },
  198. methods: {
  199. handleVisble(flag) {
  200. this.visble = flag
  201. },
  202. // 设备恢复出厂设置
  203. recovery() {
  204. if (this.isLoading || this.isError) {
  205. this._message.warning('操作过于频繁,请稍后再试')
  206. return
  207. }
  208. this.$confirm('<div class="c666 fs18 mb10">确认要恢复出厂设置吗?</div><div class="c999 fs14">恢复出厂后将恢复到初始设置并清除所有数据,恢复出厂过程/n中设备将不可操作。</div>', '恢复出厂', {
  209. dangerouslyUseHTMLString: true,
  210. confirmButtonText: '确认',
  211. cancelButtonText: '取消',
  212. customClass: 'cloud-phone-message-box', // 自定义消息样式类名
  213. confirmButtonClass: 'cloud-phone-confirm-btn', // 自定义确认按钮样式类名
  214. cancelButtonClass: 'cloud-phone-cancel-btn' // 自定义取消按钮样式类名
  215. }).then(() => {
  216. recovery({ sns: this.sn.join(',') }).then(res => {
  217. if (res.status === 0) {
  218. this.$alert('<div class="c666 fs18 mb10">恢复出厂设置指令已发送</div><div class="c999 fs14">若恢复出厂设置失败可重试</div>', '恢复出厂', {
  219. dangerouslyUseHTMLString: true,
  220. confirmButtonText: '确认',
  221. customClass: 'cloud-phone-message-box',
  222. confirmButtonClass: 'cloud-phone-confirm-btn', // 自定义确认按钮样式类名
  223. callback: action => {
  224. this.isMask = true
  225. this.isLoading = true
  226. this.maskText = '设备恢复出厂设置中...'
  227. this._send()
  228. }
  229. })
  230. } else {
  231. this._message.error('恢复出厂设置指令发送失败,请稍后再试')
  232. }
  233. })
  234. }).catch(() => {})
  235. },
  236. // 设备重启
  237. reboot() {
  238. if (this.isLoading || this.isError) {
  239. this._message.warning('操作过于频繁,请稍后再试')
  240. return
  241. }
  242. this.$confirm('<div class="c666 fs18 mb10">确认要重启设备吗?</div><div class="c999 fs14">重启后将完全关闭后台进程,重启过程中设备将不可操作。</div>', '重启', {
  243. dangerouslyUseHTMLString: true,
  244. confirmButtonText: '确认',
  245. cancelButtonText: '取消',
  246. customClass: 'cloud-phone-message-box', // 自定义消息样式类名
  247. confirmButtonClass: 'cloud-phone-confirm-btn', // 自定义确认按钮样式类名
  248. cancelButtonClass: 'cloud-phone-cancel-btn' // 自定义取消按钮样式类名
  249. }).then(() => {
  250. rebootBatch({ sns: this.sn.join(',') }).then(res => {
  251. if (res.status === 0) {
  252. this.$alert('<div class="c666 fs18 mb10">重启指令已发送</div><div class="c999 fs14">若重启失败可重试</div>', '重启', {
  253. dangerouslyUseHTMLString: true,
  254. confirmButtonText: '确认',
  255. customClass: 'cloud-phone-message-box',
  256. confirmButtonClass: 'cloud-phone-confirm-btn', // 自定义确认按钮样式类名
  257. callback: action => {
  258. this.isMask = true
  259. this.isLoading = true
  260. this.maskText = '设备重启中...'
  261. this._send()
  262. }
  263. })
  264. } else {
  265. this._message.error('重启指令发送失败,请稍后再试')
  266. }
  267. })
  268. }).catch(() => {})
  269. },
  270. async _send() {
  271. const state = await this.getcardStatus()
  272. if (state === '1') {
  273. this.socket.send(ExexuteCloseServer(this.sn.join(','))) // 销毁监听
  274. this.socket.close()
  275. this.init()
  276. this.isLoading = false
  277. this.isMask = false
  278. this.maskText = ''
  279. } else {
  280. setTimeout(() => {
  281. this._send()
  282. }, 3000)
  283. }
  284. },
  285. async getcardStatus() {
  286. const res = await getcardStatus({ sns: this.sn.join(',') }).then(res => { return res })
  287. let state = '0'
  288. if (res.status === 0) {
  289. state = res.data[0].state
  290. }
  291. return state
  292. },
  293. // 设置像素
  294. handleCommand(command) {
  295. if (this.isLoading || this.isError) {
  296. return
  297. }
  298. this.bitRate = command
  299. const buffer = ExexutePixel(command, this.sn.join(','))
  300. this.socket.send(buffer)
  301. },
  302. // 音量加减、home键、back事件(keyCode的值分别表示为25减音量,24加音量,4为返回键,3为home键,187为切换菜单)
  303. handleRoutine(code) {
  304. if (this.isLoading || this.isError) {
  305. return
  306. }
  307. const buffer = ExexuteKeyDown(code, this.sn.join(','))
  308. this.socket.send(buffer)
  309. },
  310. // 鼠标点击事件
  311. handleMouseDown(event) {
  312. if (this.isLoading || this.isError) {
  313. return
  314. }
  315. if (event.button === 0) {
  316. var posX = this.isRotate ? event.offsetY / VIDEO_HEIGHT * 1920 * 1.0 : event.offsetX / VIDEO_WIDTH * 1080 * 1.0
  317. var posY = this.isRotate ? (VIDEO_WIDTH - event.offsetX) / VIDEO_WIDTH * 1080 * 1.0 : event.offsetY / VIDEO_HEIGHT * 1920 * 1.0
  318. var buffer = ExexuteMouseDown(posX.toString(), posY.toString(), this.sn.join(','))
  319. this.socket.send(buffer)
  320. this.isDrag = true
  321. }
  322. },
  323. // 鼠标移开事件
  324. handleMouseLeave(event) {
  325. if (this.isLoading || this.isError) {
  326. return
  327. }
  328. this.isDrag = false
  329. var posX = this.isRotate ? event.offsetY / VIDEO_HEIGHT * 1920 * 1.0 : event.offsetX / VIDEO_WIDTH * 1080 * 1.0
  330. var posY = this.isRotate ? (VIDEO_WIDTH - event.offsetX) / VIDEO_WIDTH * 1080 * 1.0 : event.offsetY / VIDEO_HEIGHT * 1920 * 1.0
  331. var buffer = ExexuteMouseUp(posX.toString(), posY.toString(), this.sn.join(','))
  332. this.socket.send(buffer)
  333. },
  334. // 鼠标移动事件
  335. handleMouseMove(event) {
  336. if (this.isLoading || this.isError) {
  337. return
  338. }
  339. if (this.isDrag && event.button === 0) {
  340. var posX = this.isRotate ? event.offsetY / VIDEO_HEIGHT * 1920 * 1.0 : event.offsetX / VIDEO_WIDTH * 1080 * 1.0
  341. var posY = this.isRotate ? (VIDEO_WIDTH - event.offsetX) / VIDEO_WIDTH * 1080 * 1.0 : event.offsetY / VIDEO_HEIGHT * 1920 * 1.0
  342. var buffer = ExexuteMouseMove(posX.toString(), posY.toString(), this.sn.join(','))
  343. this.socket.send(buffer)
  344. }
  345. },
  346. // 鼠标离开事件
  347. handleMouseUp(event) {
  348. if (this.isLoading || this.isError) {
  349. return
  350. }
  351. this.isDrag = false
  352. var posX = this.isRotate ? event.offsetY / VIDEO_HEIGHT * 1920 * 1.0 : event.offsetX / VIDEO_WIDTH * 1080 * 1.0
  353. var posY = this.isRotate ? (VIDEO_WIDTH - event.offsetX) / VIDEO_WIDTH * 1080 * 1.0 : event.offsetY / VIDEO_HEIGHT * 1920 * 1.0
  354. var buffer = ExexuteMouseUp(posX.toString(), posY.toString(), this.sn.join(','))
  355. this.socket.send(buffer)
  356. },
  357. // 键盘输入事件
  358. handleKeyDown(event) {
  359. if (this.isLoading || this.isError) {
  360. return
  361. }
  362. var buffer = ExexuteKeyDown(keycodeMode[event.keyCode] || event.keyCode, this.sn.join(','))
  363. this.socket.send(buffer)
  364. },
  365. // websocket初始化
  366. init() {
  367. this.socket = new WebSocket(this.path) // 实例化socket
  368. this.socket.binaryType = 'arraybuffer'
  369. this.socket.onopen = this.open // 监听socket连接
  370. this.socket.onerror = this.onerror // 监听socket错误信息
  371. this.socket.onmessage = this.getMessage // 监听socket消息
  372. },
  373. // websocket连接成功回调
  374. open() {
  375. this.isMask = false
  376. this.isError = false
  377. this.socket.send(ConfigChannel(this.sn))
  378. this.socket.onmessage = this.getMessage
  379. },
  380. // websocket连接失败回调
  381. onerror() {
  382. console.log('websocket连接失败')
  383. this.isLoading = false
  384. this.isError = true
  385. this.maskText = '设备故障或离线'
  386. },
  387. getMessage(event) {
  388. const data = this.parse(event.data) // 分离音视频数据
  389. const audioData = {
  390. audio: data.audio,
  391. video: null,
  392. duration: data.duration
  393. }
  394. // const videoData = {
  395. // audio: null,
  396. // video: data.video,
  397. // duration: data.duration
  398. // }
  399. if (this.$refs.audioPlayer && this.$refs.audioPlayer.readyState === 2) {
  400. const playPromise = this.$refs.audioPlayer.play()
  401. if (playPromise !== undefined) {
  402. playPromise.then(() => {
  403. this.$refs.audioPlayer.play()
  404. }).catch(() => {})
  405. }
  406. }
  407. if (data.audio != null) { // 喂音频
  408. this.audioMuxer.feed(audioData)
  409. }
  410. if (data.video != null && this.isFeed) { // 喂视频
  411. this.jmuxer.feed(data)
  412. }
  413. if (data.video) {
  414. if (new Date().getTime() - this.curTime >= 1000) {
  415. this.fpsCount = 0
  416. this.curTime = new Date().getTime()
  417. } else {
  418. this.fpsCount++
  419. }
  420. }
  421. },
  422. // 弹窗关闭
  423. close() {
  424. this.$emit('closeDialog', 'showMobile')
  425. },
  426. // 根据sn获取设备获取清晰度、端口号和ip
  427. getcardInfoBySn() {
  428. var list = []
  429. list.push(this.mobileId)
  430. getcardInfoBySn({ mobileIdList: list }).then(res => {
  431. if (res.status === 0) {
  432. this.ip = res.data[0].ip
  433. this.port = res.data[0].port
  434. this.size = res.data[0].size
  435. this.bitRate = res.data[0].bitRate === '0' ? '1638400' : res.data.bitRate
  436. this.path = `ws://${res.data[0].ip}:${res.data[0].websocketPort.toString()}`
  437. //console.log(this.path)
  438. this.init()
  439. }
  440. })
  441. },
  442. pause() {
  443. this.isFeed = false
  444. },
  445. audioPlay() {
  446. if (document.visibilityState === 'visible') {
  447. this.isFlag = true
  448. this.socket.send(RequestIFrame(this.sn.join(',')))
  449. } else {
  450. this.isFlag = false
  451. this.isFeed = false
  452. this.$refs.player.pause()
  453. }
  454. },
  455. ready() {
  456. this.isLoading = false
  457. },
  458. error() {
  459. this.isMask = true
  460. this.isError = true
  461. },
  462. parse(data) {
  463. // var input = new Uint8Array(data)
  464. // var dv = new DataView(input.buffer)
  465. // var duration = dv.getUint16(0, true) // 获取duration
  466. // var audioLength = dv.getUint16(2, true)
  467. // var audio
  468. // var video
  469. // if (audioLength === 0) {
  470. // video = input.subarray(4)
  471. // } else {
  472. // audio = input.subarray(4, (audioLength + 4))
  473. // video = input.subarray(audioLength + 4)
  474. // }
  475. // return {
  476. // audio: audio,
  477. // video: video,
  478. // duration: duration
  479. // }
  480. var input = new Uint8Array(data)
  481. var duration
  482. var video
  483. var audio
  484. if (input[0] === 0 && input[1] === 0 && input[2] === 0 && input[3] === 1) {
  485. video = input
  486. duration = 24
  487. var nalType = input[4] & 0x1f
  488. if (nalType === 0x05 && this.isFlag) { // 策略, 找到sps、sps、或I帧,才继续渲染
  489. this.isFeed = true
  490. }
  491. } else if (input[0] === 0xff) {
  492. audio = input
  493. duration = 24
  494. } else if (input[23] === 0x0b) {
  495. this.$alert('<div class="c666 fs18 mb10">设备' + this.mobileName + '已在其它端受控</div>', '提示', {
  496. dangerouslyUseHTMLString: true,
  497. confirmButtonText: '确认',
  498. customClass: 'cloud-phone-message-box',
  499. confirmButtonClass: 'cloud-phone-confirm-btn', // 自定义确认按钮样式类名
  500. callback: action => {
  501. this.showMobile = false
  502. }
  503. })
  504. } else if (input[0] === 0x68 && input[23] === 0x05) {
  505. var state = CheckScreenDirection(input.slice(24, 24 + 8))
  506. if (state === 1) {
  507. this.isRotate = false
  508. } else {
  509. this.isRotate = true
  510. }
  511. }
  512. return {
  513. audio: audio,
  514. video: video,
  515. duration: duration
  516. }
  517. }
  518. }
  519. }
  520. </script>
  521. <style lang="scss">
  522. .ml111 {margin-left: 111px;}.mt50{margin-top: 50px;}.mt40{margin-top: 40px;}
  523. .w180h148 {width: 180px;height: 148px;}.mr100{margin-right: 100px;}
  524. .mobile-dialog {
  525. width: 417px;
  526. height: 740px;
  527. background: #141414;
  528. box-shadow: 0px 0px 30px 0px rgba(0, 0, 0, 0.5);
  529. border-radius: 0;
  530. &.rotate {
  531. width: 705px;
  532. height: 452px;
  533. .el-dialog__body {
  534. padding: 0;
  535. .mt226ormt90{
  536. margin-top: 90px;
  537. }
  538. .mlr64ormlr160{
  539. margin-left: 160px;
  540. margin-right: 160px;
  541. }
  542. .mobile-audio {
  543. width: 660px;
  544. height: 372px;
  545. position: relative;
  546. video {
  547. transform:rotate(-90deg);
  548. position: absolute;
  549. top: -145px;
  550. left: 145px;
  551. }
  552. .mask {
  553. width: 100%;
  554. height: 100%;
  555. background-color: #fff;
  556. position: absolute;
  557. top: 0;
  558. left: 0;
  559. display: flex;
  560. align-items: center;
  561. justify-content: center;
  562. flex-direction: column;
  563. }
  564. }
  565. .mobile-sidebar {
  566. width: 45px;
  567. height: 372px;
  568. display: flex;
  569. flex-direction: column;
  570. align-items: center;
  571. }
  572. .mobile-bottombar {
  573. width: 100%;
  574. height: 40px;
  575. display: flex;
  576. align-items: center;
  577. justify-content: center;
  578. }
  579. }
  580. }
  581. .el-dialog__header {
  582. height: 40px;
  583. line-height: 40px;
  584. padding: 0;
  585. .el-dialog__headerbtn {
  586. top: 13px;
  587. right: 15px;
  588. font-size: 14px;
  589. .el-dialog__close {
  590. color: #fff;
  591. font-weight: 800;
  592. }
  593. }
  594. }
  595. .el-dialog__body {
  596. padding: 0;
  597. .mt226ormt90 {
  598. margin-top: 226px;
  599. }
  600. .mlr64ormlr160{
  601. margin-left: 64px;
  602. margin-right: 64px;
  603. }
  604. .mobile-audio {
  605. width: 371px;
  606. height: 660px;
  607. position: relative;
  608. .mask {
  609. width: 100%;
  610. height: 100%;
  611. background-color: #fff;
  612. position: absolute;
  613. top: 0;
  614. left: 0;
  615. display: flex;
  616. align-items: center;
  617. justify-content: center;
  618. flex-direction: column;
  619. }
  620. }
  621. .mobile-sidebar {
  622. width: 46px;
  623. height: 660px;
  624. display: flex;
  625. flex-direction: column;
  626. align-items: center;
  627. }
  628. .mobile-bottombar {
  629. width: 100%;
  630. height: 40px;
  631. display: flex;
  632. align-items: center;
  633. justify-content: center;
  634. }
  635. }
  636. }
  637. .mobile-dropdown-menu {
  638. width: 110px;
  639. padding: 5px 0;
  640. border: none;
  641. box-shadow: 0px 0px 30px 0px rgba(0, 0, 0, 0.3);
  642. .popper__arrow {
  643. border-width: 10px;
  644. border-bottom-color: #fff !important;
  645. top: -8px !important;
  646. left: 60px !important;
  647. }
  648. .el-dropdown-menu__item {
  649. height: 40px;
  650. line-height: 40px;
  651. &:hover {
  652. background-color: #F3F6FF;
  653. color: #333;
  654. }
  655. }
  656. }
  657. audio {
  658. display: none;
  659. }
  660. </style>