Browse Source

移除多余服务,增加loading,增加store字典,移除全局捕获,

Caner 1 year ago
parent
commit
19b6b3cc49

+ 1 - 1
README.md

@@ -1,6 +1,6 @@
 # 控制端 使用Tauri
 # 控制端 使用Tauri
 ```
 ```
-1.tauri 兼容性存在问题(可使用V6代替)
+1.tauri 兼容性存在问题
     webrtc 在mac m2 safari 16.1中 safari 无法正确播放opus格式音频
     webrtc 在mac m2 safari 16.1中 safari 无法正确播放opus格式音频
 ```
 ```
 ## runer
 ## runer

+ 7 - 8
src/App.vue

@@ -1,4 +1,5 @@
 <template>
 <template>
+  <loading v-if="show" />
   <n-config-provider
   <n-config-provider
     preflight-style-disabled
     preflight-style-disabled
     inline-theme-disabled
     inline-theme-disabled
@@ -12,9 +13,10 @@
   </n-config-provider>
   </n-config-provider>
 </template>
 </template>
 <script setup lang='ts'>
 <script setup lang='ts'>
-import { provide, watch } from 'vue'
+import { computed, provide, watch } from 'vue'
 import { zhCN, dateZhCN } from 'naive-ui'
 import { zhCN, dateZhCN } from 'naive-ui'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
+import loading from '@/components/loading.vue'
 import useStore from './store/index'
 import useStore from './store/index'
 import Theme from '@/assets/naive-theme'
 import Theme from '@/assets/naive-theme'
 import MqttService from '@/services/mqtt.service'
 import MqttService from '@/services/mqtt.service'
@@ -23,17 +25,14 @@ const mqtt = new MqttService()
 const store = useStore()
 const store = useStore()
 const router = useRouter()
 const router = useRouter()
 const themeOverrides = Theme
 const themeOverrides = Theme
-// router.push('/room')
+const show = computed(() => store.loading)
+
 watch(() => store.mqtt_message, (val) => {
 watch(() => store.mqtt_message, (val) => {
   console.log('顶级监听', val)
   console.log('顶级监听', val)
   if (val.type === 'connect') {
   if (val.type === 'connect') {
+    store.setLoading(true)
     router.push('/room')
     router.push('/room')
   }
   }
-  // mqtt断开连接
-  if (val.type === 'disconnect' || val.type === 'leave') {
-    router.push('/')
-    mqtt.disconnect()
-  }
 })
 })
 
 
 provide('MQTT', mqtt)
 provide('MQTT', mqtt)
@@ -57,7 +56,7 @@ body {
   min-width: 1300px;
   min-width: 1300px;
   min-height: 760px;
   min-height: 760px;
   overflow: hidden;
   overflow: hidden;
-  border-radius: 10px;
+  border-radius: 8px;
   background: transparent;
   background: transparent;
   position: relative;
   position: relative;
   border: none;
   border: none;

+ 5 - 5
src/components/battery.vue

@@ -31,7 +31,7 @@ const bgClass = computed(() => {
   } if (props.quantity >= 1) {
   } if (props.quantity >= 1) {
     return 'danger'
     return 'danger'
   }
   }
-  return 'danger'
+  return ''
 })
 })
 </script>
 </script>
 
 
@@ -41,14 +41,14 @@ const bgClass = computed(() => {
   justify-content: center;
   justify-content: center;
   align-items: center;
   align-items: center;
   position: relative;
   position: relative;
-  color: white;
+  color: rgba(255, 255, 255, 0.5);
 
 
   &-panel {
   &-panel {
     box-sizing: border-box;
     box-sizing: border-box;
     width: 25px;
     width: 25px;
     height: 15px;
     height: 15px;
     position: relative;
     position: relative;
-    border: 2px solid #ccc;
+    border: 2px solid rgba(255, 255, 255, 0.5);
     padding: 1px;
     padding: 1px;
     border-radius: 3px;
     border-radius: 3px;
     margin-right: 5px;
     margin-right: 5px;
@@ -57,7 +57,7 @@ const bgClass = computed(() => {
       content: "";
       content: "";
       border-radius: 0 1px 1px 0;
       border-radius: 0 1px 1px 0;
       height: 8px;
       height: 8px;
-      background: #ccc;
+      background: rgba(255, 255, 255, 0.5);
       width: 3px;
       width: 3px;
       position: absolute;
       position: absolute;
       top: 50%;
       top: 50%;
@@ -73,7 +73,7 @@ const bgClass = computed(() => {
       width: 0%;
       width: 0%;
       left: 0;
       left: 0;
       top: 0;
       top: 0;
-      background: #fff;
+      background: rgba(255, 255, 255, 0.5);
     }
     }
   }
   }
 
 

+ 52 - 0
src/components/loading.vue

@@ -0,0 +1,52 @@
+<template>
+  <div class="bg">
+    <div class="loader" />
+  </div>
+</template>
+<style lang="scss" scoped>
+.bg {
+    width: 100vw;
+    height: 100vh;
+    position: fixed;
+    top: 0;
+    left: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: rgba(0, 0, 0, 0.5);
+    z-index: 9;
+    border-radius: 8px;
+    overflow: hidden;
+}
+
+.loader {
+
+    border: 5px solid #f3f3f3;
+    border-radius: 50%;
+    border-top: 5px solid #3498db;
+    width: 30px;
+    height: 30px;
+    -webkit-animation: spin 2s linear infinite;
+    animation: spin 2s linear infinite;
+}
+
+@-webkit-keyframes spin {
+    0% {
+        -webkit-transform: rotate(0deg);
+    }
+
+    100% {
+        -webkit-transform: rotate(360deg);
+    }
+}
+
+@keyframes spin {
+    0% {
+        transform: rotate(0deg);
+    }
+
+    100% {
+        transform: rotate(360deg);
+    }
+}
+</style>

+ 1 - 0
src/components/mic.vue

@@ -3,6 +3,7 @@
     name="mic"
     name="mic"
     :size="22"
     :size="22"
     :color="show ? '#00CED1' : 'rgba(255, 255, 255, 0.5)'"
     :color="show ? '#00CED1' : 'rgba(255, 255, 255, 0.5)'"
+    style="margin-left: 5px;"
     @click="change"
     @click="change"
   />
   />
 </template>
 </template>

+ 1 - 1
src/components/signal.vue

@@ -92,7 +92,7 @@ watch(() => props.signalValue, (v: number) => {
   }
   }
 
 
   .signal-default {
   .signal-default {
-    background: rgba(0, 0, 0, 0.3);
+    background: rgba(255, 255, 255, 0.5);
   }
   }
 
 
   .signal-red {
   .signal-red {

+ 11 - 2
src/components/topBar.vue

@@ -23,7 +23,7 @@
         name="close"
         name="close"
         :size="18"
         :size="18"
         color="#fff"
         color="#fff"
-        @click="appWindow.close()"
+        @click="close"
       />
       />
     </div>
     </div>
   </div>
   </div>
@@ -31,13 +31,22 @@
 
 
 <script setup lang='ts'>
 <script setup lang='ts'>
 import { Window } from '@tauri-apps/api/window'
 import { Window } from '@tauri-apps/api/window'
+import { useRoute } from 'vue-router'
+import useStore from '@/store'
 
 
 const appWindow = new Window('main')
 const appWindow = new Window('main')
-const props = withDefaults(defineProps<{
+withDefaults(defineProps<{
   showBtn?: boolean
   showBtn?: boolean
 }>(), {
 }>(), {
   showBtn: true
   showBtn: true
 })
 })
+const route = useRoute()
+const store = useStore()
+
+function close() {
+  if (route.path !== '/') store.setLoading(false); store.setMqttMessage({ type: 'disconnect' })
+  if (route.path === '/') appWindow.close()
+}
 </script>
 </script>
 <style lang="scss" scoped>
 <style lang="scss" scoped>
 .topBar {
 .topBar {

+ 1 - 21
src/main.ts

@@ -23,24 +23,4 @@ app.component('Icon', Icon)
 app.use(store)
 app.use(store)
   .use(naive)
   .use(naive)
   .use(router)
   .use(router)
-
-router.isReady().then(() => {
-  const vm = app.mount('#app')
-  app.config.errorHandler = (err: any) => {
-    if (err.reason === 401) {
-      router.replace('/')
-    } else {
-      console.log('错误', err)
-    }
-  }
-  window.addEventListener('unhandledrejection', (evt) => {
-    console.error(evt)
-    evt.preventDefault()
-    app.config.errorHandler?.(evt, vm, '')
-  })
-  window.addEventListener('error', (evt) => {
-    console.error(evt)
-    evt.preventDefault()
-    app.config.errorHandler?.(evt.error, vm, '')
-  })
-})
+  .mount('#app')

+ 1 - 2
src/pages/login/index.vue

@@ -66,9 +66,8 @@ const mqtt = inject('MQTT') as Any
 const name = ref(localStorage.getItem('NAME') || '')
 const name = ref(localStorage.getItem('NAME') || '')
 const room = ref(localStorage.getItem('ROOM') || '')
 const room = ref(localStorage.getItem('ROOM') || '')
 const url = ref(localStorage.getItem('URL') || '')
 const url = ref(localStorage.getItem('URL') || '')
-const dic = { leave: '对方离开房间', disconnect: '连接断开' }
 const disabled = computed(() => !name.value || !room.value || !url.value || !url.value.startsWith('mqtt'))
 const disabled = computed(() => !name.value || !room.value || !url.value || !url.value.startsWith('mqtt'))
-const err = computed(() => dic[store.mqtt_message.type])
+const err = computed(() => store.errorDic[store.mqtt_message.type])
 
 
 async function login() {
 async function login() {
   if (disabled.value) return
   if (disabled.value) return

+ 14 - 8
src/pages/room/index.vue

@@ -23,6 +23,7 @@
 import {
 import {
   ref, onMounted, inject, watch
   ref, onMounted, inject, watch
 } from 'vue'
 } from 'vue'
+import { useRouter } from 'vue-router'
 import topBar from '@/components/topBar.vue'
 import topBar from '@/components/topBar.vue'
 import signal from '@/components/signal.vue'
 import signal from '@/components/signal.vue'
 import battery from '@/components/battery.vue'
 import battery from '@/components/battery.vue'
@@ -31,36 +32,35 @@ import mic from '@/components/mic.vue'
 import WebRtcService from '@/services/webrtc.service'
 import WebRtcService from '@/services/webrtc.service'
 import useStore from '@/store/index'
 import useStore from '@/store/index'
 
 
-const signalValue = ref(5)
-const batteryValue = ref(50)
-const SpeedValue = ref(60)
+const signalValue = ref(0)
+const batteryValue = ref(0)
+const SpeedValue = ref(0)
 const conctrlNum = ref(0)
 const conctrlNum = ref(0)
 const remoteVideo = ref(null as unknown as HTMLVideoElement)
 const remoteVideo = ref(null as unknown as HTMLVideoElement)
 const RTC = new WebRtcService()
 const RTC = new WebRtcService()
 const store = useStore()
 const store = useStore()
 const mqtt = inject('MQTT') as Any
 const mqtt = inject('MQTT') as Any
+const router = useRouter()
 
 
 /**
 /**
  * 发送语音
  * 发送语音
  * @param blob blob
  * @param blob blob
  */
  */
-function sendAudio(blob:Blob) {
+function sendAudio(blob: Blob) {
   console.log('send audio', blob)
   console.log('send audio', blob)
 }
 }
 
 
 onMounted(() => RTC.initRTC(remoteVideo.value, (event) => {
 onMounted(() => RTC.initRTC(remoteVideo.value, (event) => {
   const { type, data } = event
   const { type, data } = event
   if (type === 'connected') {
   if (type === 'connected') {
+    store.setLoading(false)
     console.log('可以发送控制数据')
     console.log('可以发送控制数据')
   }
   }
   if (type === 'answer') {
   if (type === 'answer') {
     console.log('answer', data)
     console.log('answer', data)
     mqtt.send(JSON.stringify(data))
     mqtt.send(JSON.stringify(data))
   }
   }
-  if (type === 'disconnected') {
-    RTC.distory()
-    store.setMqttMessage({ type: 'disconnect' })
-  }
+  if (store.errorDic[type]) mqtt.send(data)
 }))
 }))
 
 
 watch(() => store.mqtt_message, async (val: { type: string, data?: RTCSessionDescriptionInit }) => {
 watch(() => store.mqtt_message, async (val: { type: string, data?: RTCSessionDescriptionInit }) => {
@@ -71,6 +71,12 @@ watch(() => store.mqtt_message, async (val: { type: string, data?: RTCSessionDes
     const answerd = await RTC.Peer?.createAnswer()
     const answerd = await RTC.Peer?.createAnswer()
     await RTC.Peer?.setLocalDescription(answerd)
     await RTC.Peer?.setLocalDescription(answerd)
   }
   }
+  // mqtt|webrtc ERROR
+  if (store.errorDic[val.type]) {
+    RTC.distory()
+    mqtt.disconnect()
+    router.push('/')
+  }
 })
 })
 
 
 </script>
 </script>

+ 2 - 5
src/services/mqtt.service.ts

@@ -2,14 +2,12 @@ import {
   connect, disconnect, listen, publish
   connect, disconnect, listen, publish
 } from '@kuyoonjo/tauri-plugin-mqtt'
 } from '@kuyoonjo/tauri-plugin-mqtt'
 import { Buffer } from 'buffer'
 import { Buffer } from 'buffer'
-import { injectable, Service } from './service'
 import useStore from '@/store'
 import useStore from '@/store'
 
 
 /**
 /**
  * MQTT服务
  * MQTT服务
  */
  */
-@injectable
-export default class MqttService extends Service {
+export default class MqttService {
   private mqtt_id = 'contrller'
   private mqtt_id = 'contrller'
 
 
   private client_channel = ''
   private client_channel = ''
@@ -19,7 +17,6 @@ export default class MqttService extends Service {
   private client_id = ''
   private client_id = ''
 
 
   constructor() {
   constructor() {
-    super()
     this.onListen()
     this.onListen()
   }
   }
 
 
@@ -93,7 +90,7 @@ export default class MqttService extends Service {
 
 
           this.store.setMqttMessage({ ...data })
           this.store.setMqttMessage({ ...data })
         } catch (error) {
         } catch (error) {
-          this.throw('mqtt消息解析失败')
+          console.log('mqtt消息解析失败')
         }
         }
       }
       }
     })
     })

+ 0 - 116
src/services/net.service.ts

@@ -1,116 +0,0 @@
-import { injectable, Service } from './service'
-
-/** api接口返回值类型 */
-declare type NetResult = {
-  success: boolean,
-  data: Any
-}
-
-/**
- * API网络请求服务
- */
-@injectable
-export default class NetService extends Service {
-  /**
-   * post方法请求接口
-   * @param url 接口地址
-   * @param params 接口参数
-   */
-  post(url: string, params: Any = {}, timeout = 6000): Promise<any> {
-    return this.fetch(url, { method: 'POST', body: JSON.stringify(params) }, timeout)
-  }
-
-  /**
-   * get请求api接口
-   * @param url 接口地址
-   */
-  get(url: string, timeout = 6000): Promise<NetResult> {
-    return this.fetch(url, { method: 'GET' }, timeout)
-  }
-
-  /**
-   * del请求api接口
-   * @param url 接口地址
-   */
-  del(url: string, timeout = 6000): Promise<NetResult> {
-    return this.fetch(url, { method: 'DELETE' }, timeout)
-  }
-
-  /**
-   * post方法请求接口
-   * @param url 接口地址
-   * @param params 接口参数
-   */
-  put(url: string, params: Any = {}, timeout = 6000): Promise<NetResult> {
-    return this.fetch(url, { method: 'PUT', body: JSON.stringify(params) }, timeout)
-  }
-
-  /**
-   * 下载文件
-   * @param url url|blob
-   * @param fileName
-   */
-  downloadFile(url: string | Blob, fileName: string) {
-    const download = (blob: Blob, fileName: string) => {
-      const a = document.createElement('a')
-      document.body.appendChild(a)
-      a.style.display = 'none'
-      // 使用获取到的blob对象创建的url
-      const url = window.URL.createObjectURL(blob)
-      a.href = url
-      // 指定下载的文件名
-      a.download = fileName
-      a.click()
-      document.body.removeChild(a)
-      // 移除blob对象的url
-      window.URL.revokeObjectURL(url)
-    }
-    return new Promise<void>((resolve, reject) => {
-      if (url instanceof Blob) {
-        download(url, fileName)
-        resolve()
-      } else {
-        fetch(url).then((res) => res.blob()).then((blob) => download(blob, fileName)).finally(() => resolve())
-      }
-    })
-  }
-
-  /** token 续期 */
-  async refresh(token: string) {
-    this.post('', { token }).then((res) => {
-      console.log('刷新token', res)
-    })
-  }
-
-  /**
-   * fetch
-   * @param url
-   * @param opt
-   * @param timeout 0 默认不超时
-   * @returns
-   */
-  private fetch(url: string, opt: RequestInit, timeout = 0): Promise<NetResult> {
-    return new Promise((resolve) => {
-      const controller = new AbortController()
-      const { signal } = controller
-      if (timeout) {
-        setTimeout(() => {
-          controller.abort()
-        }, timeout)
-      }
-      fetch(url, {
-        signal,
-        ...opt,
-        headers: opt.headers || { 'Content-Type': 'application/json' }
-      }).then((res) => {
-        if (res.status === 500) return { success: false, data: '请求错误' }
-        return res.json()
-      }).then((res) => {
-        console.log(8, res)
-        resolve({ data: res.data, success: res.data instanceof Object })
-      }).catch((er) => {
-        throw er
-      })
-    })
-  }
-}

+ 0 - 64
src/services/service.ts

@@ -1,64 +0,0 @@
-export class ServiceError extends Error {
-  code?: number
-
-  origin?: string
-
-  constructor(message: string, origin?: string, code?: number) {
-    super(message)
-    this.code = code
-    this.origin = origin
-    this.stack = `${this.message}\n${this.origin}\n${this.code || ''}`
-  }
-
-  toString() {
-    return this.message
-  }
-}
-
-export class Service {
-  throw(message: string, origin?: string, code?: number) {
-    throw new ServiceError(message, origin, code)
-  }
-}
-
-export function injectable<T extends { new(..._args: any[]): {} }> (Ctor: T) {
-  let instance!: any
-  return new Proxy(Ctor, {
-    construct(t, args) {
-      if (!instance) {
-        instance = new Ctor(args)
-        // console.log('instance ' + Ctor.name)
-      }
-      return instance
-    }
-  })
-}
-const runnerMap: { [key: string]: ((_res: any) => void)[] | undefined } = {}
-/**
-   * 互斥注解
-   * 用于保证某个方法同一时间只有单次调用
-   */
-export function mutex(target: any, property: string) {
-  const oriFn = target[property]
-  const funcKey = `${target.constructor.name}-${property}`
-  Object.defineProperty(target, property, {
-    async value(...args: any[]) {
-      const key = funcKey + JSON.stringify(args)
-      if (runnerMap[key]) {
-        return await new Promise((res) => {
-          runnerMap[key]?.push((result: any) => res(result))
-        })
-      }
-      runnerMap[key] = []
-
-      setTimeout(() => {
-        runnerMap[key] = undefined
-      }, 4000)
-      const res = await Reflect.apply(oriFn, this, args || [])
-      runnerMap[key]?.forEach((fn) => fn(res))
-      runnerMap[key] = undefined
-      return res
-    }
-  })
-  return target[property]
-}

+ 11 - 13
src/services/webrtc.service.ts

@@ -1,10 +1,8 @@
-import { injectable, Service } from './service'
-
 /**
 /**
  * WebRTC服务
  * WebRTC服务
  */
  */
-@injectable
-export default class WebRtcService extends Service {
+
+export default class WebRtcService {
   private ICE = [
   private ICE = [
     {
     {
       urls: [ 'stun:caner.top:3478' ]
       urls: [ 'stun:caner.top:3478' ]
@@ -18,11 +16,9 @@ export default class WebRtcService extends Service {
 
 
   public Peer: RTCPeerConnection | null = null
   public Peer: RTCPeerConnection | null = null
 
 
-  private sleep = (ms: number) => new Promise((res) => setTimeout(res, ms))
-
   initRTC(DOM: HTMLVideoElement, callBack: (event: Any) => void) {
   initRTC(DOM: HTMLVideoElement, callBack: (event: Any) => void) {
     try {
     try {
-      if (!DOM) { callBack({ type: 'disconnected' }); return }
+      if (!DOM) { callBack({ type: 'WebRtcIntError' }); return }
       console.log('start initRTC')
       console.log('start initRTC')
 
 
       // create Peer
       // create Peer
@@ -42,20 +38,21 @@ export default class WebRtcService extends Service {
       }
       }
 
 
       // listen track
       // listen track
-      this.Peer.ontrack = async (evt) => {
+      this.Peer.ontrack = (evt) => {
         console.log('track', evt)
         console.log('track', evt)
         DOM.srcObject = evt.streams[0]
         DOM.srcObject = evt.streams[0]
       }
       }
 
 
       // listen changestate·
       // listen changestate·
-      this.Peer.oniceconnectionstatechange = async () => {
+      this.Peer.oniceconnectionstatechange = () => {
         const state = this.Peer?.iceConnectionState
         const state = this.Peer?.iceConnectionState
         console.log('StateChange', state)
         console.log('StateChange', state)
-        if (state === 'failed' || state === 'disconnected' || state === 'closed') {
+        // P2P 连接失败
+        if (state === 'failed' || state === 'closed') {
           console.log('P2P通信失败')
           console.log('P2P通信失败')
-          callBack({ type: 'disconnected' })
+          this.distory()
+          callBack({ type: 'WebRtcFailed' })
         }
         }
-
         // ICE连接成功
         // ICE连接成功
         if (state === 'connected') {
         if (state === 'connected') {
           callBack({ type: 'connected', data: null })
           callBack({ type: 'connected', data: null })
@@ -65,7 +62,8 @@ export default class WebRtcService extends Service {
       console.log('RTC success')
       console.log('RTC success')
     } catch (error) {
     } catch (error) {
       console.log('RTC 初始化失败', error)
       console.log('RTC 初始化失败', error)
-      callBack({ type: 'disconnected' })
+      this.distory()
+      callBack({ type: 'WebRtcIntError' })
     }
     }
   }
   }
 
 

+ 11 - 1
src/store/index.ts

@@ -8,11 +8,21 @@ export interface MqttMessage {
 // id必填,且需要唯一
 // id必填,且需要唯一
 const useStore = defineStore('index', {
 const useStore = defineStore('index', {
   state: () => ({
   state: () => ({
-    mqtt_message: { type: '' }
+    loading: false,
+    mqtt_message: { type: '' },
+    errorDic: {
+      leave: '对方离开房间',
+      disconnect: '连接断开',
+      WebRtcIntError: 'webrtc初始化失败',
+      WebRtcFailed: 'P2P视频通信失败'
+    }
   }),
   }),
   actions: {
   actions: {
     setMqttMessage(data: MqttMessage) {
     setMqttMessage(data: MqttMessage) {
       this.mqtt_message = data
       this.mqtt_message = data
+    },
+    setLoading(data:boolean) {
+      this.loading = data
     }
     }
   }
   }
 })
 })