diff --git a/index.html b/index.html
index 2899d5b..4004f22 100644
--- a/index.html
+++ b/index.html
@@ -3,7 +3,7 @@
 <head>
     <meta charset="UTF-8"/>
     <link rel="icon" href="/favicon.ico"/>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0,user-scalable=no" id="viewport" />
     <title>Vite App</title>
     <style>
         ::-webkit-scrollbar {
diff --git a/src/App.vue b/src/App.vue
index d143eb7..0901a8d 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -6,107 +6,31 @@
       </keep-alive>
     </transition>
   </router-view>
-
-  <div class="call-float"
-       v-if="isSmall"
-       :style="callFloatStyle"
-       @touchmove="touchmove"
-       @touchend="touchend"
-       @click="isSmall = false">
-    <img src="@/assets/img/icon/message/chat/call-float.png" alt="">
-    <span>呼叫中</span>
-  </div>
-
-  <transition name="scale">
-    <div class="audio-call"
-         :style="isSmall ? callFloatStyle : {zIndex:10}"
-         :class="isSmall?'small':''"
-         v-if="isShowAudioCall">
-      <div class="float">
-        <div class="header">
-          <img @click="isSmall = true" src="@/assets/img/icon/message/chat/narrow.png" alt=""
-               class="left">
-          <div class="center">
-            <img src="@/assets/img/icon/avatar/2.png" alt="" class="avatar">
-            <span>等待对方接听...</span>
-          </div>
-          <div class="right">
-            <div class="option">
-              <img src="@/assets/img/icon/message/chat/disabled-camera.png" alt="">
-              <span>摄像头</span>
-            </div>
-            <div class="option">
-              <img src="@/assets/img/icon/message/chat/able-volume.png" alt="">
-              <span>免提</span>
-            </div>
-            <div class="option">
-              <back mode="light" img="back" class="shrink"/>
-              <!--              <img src="@/assets/img/icon/message/chat/narrow.png" alt="">-->
-            </div>
-          </div>
-        </div>
-        <img src="@/assets/img/icon/avatar/2.png" alt="" class="big-avatar">
-        <div class="footer">
-          <img @click="isShowAudioCall = false" src="@/assets/img/icon/message/chat/call-end.png">
-          <span>挂断</span>
-        </div>
-      </div>
-    </div>
-  </transition>
+  <Call/>
 </template>
 <script>
 /*
 * try {navigator.control.gesture(false);} catch (e) {} //UC浏览器关闭默认手势事件
 try {navigator.control.longpressMenu(false);} catch (e) {} //关闭长按弹出菜单
 * */
-import Mask from "./components/Mask";
 import {mapState} from "vuex";
 import routes from "./router/routes";
-import {inject} from "_vue@3.2.20@vue";
+import Call from "./components/Call";
 
 export default {
   name: 'App',
   components: {
-    Mask
+    Call
   },
   data() {
     return {
-      isSmall: false,
-      isShowAudioCall: false,
       transitionName: 'go',
-      callFloatTransitionTime: 0,
-      callFloatLeft: 15,
-      callFloatTop: 100,
-      height: 0,
-      width: 0,
-      mitt: inject('mitt'),
     }
   },
   computed: {
     ...mapState(['excludeRoutes']),
-    callFloatStyle() {
-      return {
-        'transition-duration': this.callFloatTransitionTime + 'ms',
-        left: this.callFloatLeft + 'px',
-        top: this.callFloatTop + 'px',
-      }
-    }
-  },
-  methods: {
-    touchmove(e) {
-      this.callFloatTransitionTime = 0
-      this.callFloatLeft = e.touches[0].pageX - 35
-      this.callFloatTop = e.touches[0].pageY - 40
-    },
-    touchend(e) {
-      this.callFloatTransitionTime = 300
-      if (this.callFloatLeft < this.width / 2) {
-        this.callFloatLeft = 15
-      } else {
-        this.callFloatLeft = this.width - 15 - 70
-      }
-    },
   },
+  methods: {},
   // watch $route 决定使用哪种过渡
   watch: {
     '$route'(to, from) {
@@ -124,15 +48,6 @@ export default {
     },
   },
   mounted() {
-    this.mitt.on('showAudioCall', () => {
-      if (this.isShowAudioCall) {
-        this.isSmall = false
-      } else {
-        this.isShowAudioCall = true
-      }
-    })
-    this.height = document.body.clientHeight
-    this.width = document.body.clientWidth
     // this.$store.dispatch('getFriends')
     try {
       navigator.control.gesture(false);
@@ -147,165 +62,9 @@ export default {
 }
 </script>
 
-<style>
-.scale-enter-active,
-.scale-leave-active {
-  transition: transform .2s ease;
-}
-
-.scale-enter-from,
-.scale-leave-to {
-  transform: scale(0);
-}
-</style>
-
 <style lang="less">
 @import "./assets/less/index";
 
-.call-float {
-  transition-property: all;
-  z-index: 9;
-  width: 7rem;
-  height: 8rem;
-  position: fixed;
-  top: 20vh;
-  left: @padding-page;
-  background: white;
-  display: flex;
-  align-items: center;
-  border-radius: .6rem;
-  justify-content: center;
-  flex-direction: column;
-  color: #14BF5F;
-  font-size: 1.2rem;
-
-  img {
-    width: 3rem;
-    margin-bottom: .2rem;
-  }
-}
-
-.audio-call {
-  color: white;
-  font-size: 1.2rem;
-  position: fixed;
-  z-index: 8;
-  top: 0;
-  left: 0;
-  width: 100vw;
-  height: 100vh;
-  //background: black;
-  background: linear-gradient(to bottom, #262626, black);
-  transition: all .3s;
-
-  .float {
-    transition: all .3s;
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: space-between;
-
-    > .header {
-      width: 100vw;
-      padding: @padding-page;
-      box-sizing: border-box;
-      display: flex;
-      align-items: flex-start;
-      justify-content: space-between;
-
-      img {
-        width: 2.4rem;
-        height: 2.4rem;
-      }
-
-      .center {
-        display: flex;
-        align-items: center;
-
-        img {
-          width: 2rem;
-          height: 2rem;
-          background: white;
-          padding: .2rem;
-          border-radius: 50%;
-        }
-
-        span {
-          margin-left: .5rem;
-        }
-      }
-
-      .right {
-        display: flex;
-        flex-direction: column;
-        font-size: 1rem;
-
-        .option {
-          margin-bottom: 2.4rem;
-          display: flex;
-          align-items: center;
-          flex-direction: column;
-
-          span {
-            margin-top: 1rem;
-          }
-        }
-
-        .shrink {
-          transform: rotate(90deg) scale(.6) !important;
-        }
-      }
-    }
-
-    .big-avatar {
-      position: absolute;
-      left: 50%;
-      top: 50%;
-      transform: translate3d(-50%, -50%, 0);
-      width: 10rem;
-      border-radius: 50%;
-    }
-
-    .footer {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      font-size: 1.2rem;
-      margin-bottom: 4rem;
-
-      img {
-        width: 5rem;
-        margin-bottom: .5rem;
-      }
-
-    }
-  }
-
-  &.small {
-    //opacity: 0;
-    width: 7rem;
-    height: 8rem;
-    position: fixed;
-    top: 20vh;
-    left: @padding-page;
-    border-radius: .6rem;
-
-    .float {
-      width: 7rem;
-      height: 8rem;
-      transform: scale(0);
-      overflow: hidden;
-    }
-  }
-
-}
-
-
 #app {
   height: 100%;
   width: 100%;
diff --git a/src/components/Call.vue b/src/components/Call.vue
new file mode 100644
index 0000000..5db170a
--- /dev/null
+++ b/src/components/Call.vue
@@ -0,0 +1,283 @@
+<template>
+  <div class="call-float"
+       v-if="call.isSmall"
+       :style="callFloatStyle"
+       @touchmove="touchmove"
+       @touchend="touchend"
+       @click="call.isSmall = false">
+    <img src="@/assets/img/icon/message/chat/call-float.png" alt="">
+    <span>呼叫中</span>
+  </div>
+
+  <transition name="scale">
+    <div class="audio-call"
+         :style="call.isSmall ? callFloatStyle : {zIndex:10}"
+         :class="call.isSmall?'small':''"
+         v-if="call.isShowAudioCall">
+      <div class="float">
+        <div class="header">
+          <div class="left">
+            <img @click="call.isSmall = true" src="@/assets/img/icon/message/chat/narrow.png" alt="">
+          </div>
+          <span class="center">等待对方接听...</span>
+          <div class="right">
+            <div class="option">
+              <img src="@/assets/img/icon/message/chat/disabled-camera.png" alt="">
+              <span>摄像头</span>
+            </div>
+            <div class="option">
+              <img src="@/assets/img/icon/message/chat/able-volume.png" alt="">
+              <span>免提</span>
+            </div>
+            <div class="option">
+              <back mode="light" img="back" class="shrink"/>
+              <!--              <img src="@/assets/img/icon/message/chat/narrow.png" alt="">-->
+            </div>
+          </div>
+        </div>
+        <img src="@/assets/img/icon/avatar/2.png" alt="" class="big-avatar">
+        <div class="footer">
+          <img @click="call.isShowAudioCall = false" src="@/assets/img/icon/message/chat/call-end.png">
+          <span>挂断</span>
+        </div>
+      </div>
+    </div>
+  </transition>
+</template>
+<script>
+import {inject} from "vue";
+
+export default {
+  name: "Call",
+  components: {},
+  props: {
+    modelValue: false
+  },
+  data() {
+    return {
+      mitt: inject('mitt'),
+      call: {
+        callFloatTransitionTime: 300,
+        callFloatLeft: 15,
+        callFloatTop: 100,
+        isSmall: false,
+        isShowAudioCall: false,
+      },
+      height: 0,
+      width: 0,
+    }
+  },
+  computed: {
+    callFloatStyle() {
+      return {
+        'transition-duration': this.call.callFloatTransitionTime + 'ms',
+        left: this.call.callFloatLeft + 'px',
+        top: this.call.callFloatTop + 'px',
+      }
+    }
+  },
+  watch: {},
+  created() {
+  },
+  methods: {
+    touchmove(e) {
+      this.call.callFloatTransitionTime = 0
+      this.call.callFloatLeft = e.touches[0].pageX - 35
+      this.call.callFloatTop = e.touches[0].pageY - 40
+    },
+    touchend(e) {
+      this.call.callFloatTransitionTime = 300
+      if (this.call.callFloatLeft < this.width / 2) {
+        this.call.callFloatLeft = 15
+      } else {
+        this.call.callFloatLeft = this.width - 15 - 70
+      }
+    },
+  },
+  mounted() {
+    this.mitt.on('showAudioCall', () => {
+      if (this.call.isShowAudioCall) {
+        this.call.isSmall = false
+      } else {
+        this.call.isShowAudioCall = true
+      }
+    })
+    this.height = document.body.clientHeight
+    this.width = document.body.clientWidth
+  }
+}
+</script>
+
+<style>
+.scale-enter-active,
+.scale-leave-active {
+  transition: transform .2s ease;
+}
+
+.scale-enter-from,
+.scale-leave-to {
+  transform: scale(0);
+}
+</style>
+
+<style scoped lang="less">
+@import "@/assets/less/index";
+
+.call-float {
+  transition-property: all;
+  z-index: 9;
+  width: 7rem;
+  height: 9rem;
+  position: fixed;
+  top: 20vh;
+  left: @padding-page;
+  background: white;
+  display: flex;
+  align-items: center;
+  border-radius: .6rem;
+  justify-content: center;
+  flex-direction: column;
+  color: #14BF5F;
+  font-size: 1.2rem;
+
+  img {
+    width: 3rem;
+    margin-bottom: .2rem;
+  }
+}
+
+.audio-call {
+  color: white;
+  position: fixed;
+  z-index: 8;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 100vh;
+  background: linear-gradient(to bottom, #262626, black);
+  transition: all .3s;
+  font-size: 1.2rem;
+
+  .float {
+    transition: all .3s;
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: space-between;
+
+    span {
+      transition: all .3s;
+    }
+
+    > .header {
+      transition: all .3s;
+      width: 100%;
+      padding: @padding-page;
+      box-sizing: border-box;
+      display: flex;
+      align-items: flex-start;
+      justify-content: space-between;
+
+      .left {
+        width: 12%;
+
+        img {
+          width: 50%;
+          max-width: 2.4rem;
+          max-height: 2.4rem;
+        }
+      }
+
+      .center {
+        width: 50%;
+        overflow: hidden;
+        text-align: center;
+        word-break: keep-all;
+        font-size: 1.4rem;
+      }
+
+      .right {
+        width: 12%;
+        max-width: 4rem;
+        display: flex;
+        flex-direction: column;
+
+        img {
+          width: 50%;
+          max-width: 2.4rem;
+          max-height: 2.4rem;
+        }
+
+        .option {
+          margin-bottom: 55%;
+          display: flex;
+          align-items: center;
+          flex-direction: column;
+          overflow: hidden;
+
+          span {
+            word-break: keep-all;
+            margin-top: 20%;
+            font-size: 1.2rem;
+          }
+        }
+
+        .shrink {
+          transform: rotate(90deg) scale(.6) !important;
+        }
+      }
+    }
+
+    .big-avatar {
+      position: absolute;
+      left: 50%;
+      top: 50%;
+      transform: translate3d(-50%, -50%, 0);
+      width: 25%;
+      max-width: 10rem;
+      border-radius: 50%;
+    }
+
+    .footer {
+      width: 100%;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      font-size: 1.2rem;
+      margin-bottom: 10%;
+
+      img {
+        width: 15%;
+        max-width: 5rem;
+        margin-bottom: 1%;
+      }
+
+    }
+  }
+
+  &.small {
+    width: 7rem;
+    height: 9rem;
+    position: fixed;
+    top: 20vh;
+    left: @padding-page;
+    border-radius: .6rem;
+
+    .float {
+      > .header {
+        padding: 0;
+      }
+
+      span {
+        transform: scale(.2);
+      }
+    }
+  }
+}
+
+</style>