gemercheung %!s(int64=2) %!d(string=hai) anos
pai
achega
4987af2311
Modificáronse 41 ficheiros con 1778 adicións e 4107 borrados
  1. 1 0
      .env
  2. 4 3
      package.json
  3. 181 2
      pnpm-lock.yaml
  4. 2 0
      src/App.vue
  5. 2 1
      src/app.scss
  6. 0 1558
      src/components/RTC/PageRtcLive.vue
  7. 0 592
      src/components/RTC/Trtccom.vue
  8. 0 69
      src/components/RTC/chat/chat.vue
  9. 0 94
      src/components/RTC/dialog/checkBrowser.vue
  10. 0 357
      src/components/RTC/dialog/createdRoom.vue
  11. 0 143
      src/components/RTC/dialog/index.vue
  12. 0 169
      src/components/RTC/dialog/share.vue
  13. 0 151
      src/components/RTC/index.vue
  14. 0 79
      src/components/RTC/mini-platform.js
  15. 0 483
      src/components/RTC/paint/Draw.vue
  16. 0 308
      src/components/RTC/paint/index.vue
  17. 0 9
      src/components/RTC/socket/index.ts
  18. 0 79
      src/components/RTC/trtc/Device.vue
  19. 52 0
      src/components/basic/dialog/Alert.vue
  20. 58 0
      src/components/basic/dialog/Confirm.vue
  21. 17 0
      src/components/basic/dialog/Dialog-content.vue
  22. 28 0
      src/components/basic/dialog/Dialog.vue
  23. 74 0
      src/components/basic/dialog/Toast.vue
  24. 63 0
      src/components/basic/dialog/Window.vue
  25. 77 0
      src/components/basic/dialog/dialog.scss
  26. 117 0
      src/components/basic/dialog/index.ts
  27. 57 0
      src/components/basic/dialog/utils.ts
  28. 760 0
      src/components/basic/title.vue
  29. 20 1
      src/components/chatRoom/chat.vue
  30. 2 6
      src/components/chatRoom/controls/actions.ts
  31. 6 1
      src/components/chatRoom/index.vue
  32. 111 0
      src/components/chatRoom/sceneList.vue
  33. 24 0
      src/components/registerComponent.ts
  34. 58 0
      src/hooks/useTRTC.ts
  35. 3 1
      src/main.ts
  36. 15 0
      src/store/modules/app.ts
  37. 3 0
      src/store/modules/tour.ts
  38. 32 0
      src/utils/install.ts
  39. 0 0
      src/utils/rtc_socket.ts
  40. 7 0
      src/utils/typescript.ts
  41. 4 1
      vite.config.ts

+ 1 - 0
.env

@@ -10,3 +10,4 @@ VITE_APP_SOCKET_URL=wss://ws.4dkankan.com
 VITE_APP_STATIC_DIR=viewer
 # 接口请求地址
 VITE_APP_APIS_URL=https://test.4dkankan.com/
+VITE_USE_HTTPS=1

+ 4 - 3
package.json

@@ -22,15 +22,16 @@
     "@types/node": "^18.11.7",
     "@types/uuid": "^8.3.4",
     "@vitejs/plugin-vue": "^3.2.0",
+    "commitizen": "^4.2.5",
+    "cz-customizable": "^7.0.0",
     "sass": "^1.55.0",
     "typescript": "^4.8.4",
     "vite": "^3.2.0",
     "vite-plugin-html-env": "^1.2.7",
+    "vite-plugin-mkcert": "^1.10.1",
     "vite-plugin-windicss": "^1.8.8",
     "vue-tsc": "^1.0.9",
-    "windicss": "^3.5.6",
-    "commitizen": "^4.2.5",
-    "cz-customizable": "^7.0.0"
+    "windicss": "^3.5.6"
   },
   "config": {
     "commitizen": {

+ 181 - 2
pnpm-lock.yaml

@@ -16,6 +16,7 @@ specifiers:
   uuid: ^9.0.0
   vite: ^3.2.0
   vite-plugin-html-env: ^1.2.7
+  vite-plugin-mkcert: ^1.10.1
   vite-plugin-windicss: ^1.8.8
   vue: ^3.2.41
   vue-tsc: ^1.0.9
@@ -42,6 +43,7 @@ devDependencies:
   typescript: 4.8.4
   vite: 3.2.0_sass@1.55.0
   vite-plugin-html-env: 1.2.7_vite@3.2.0
+  vite-plugin-mkcert: 1.10.1_vite@3.2.0
   vite-plugin-windicss: 1.8.8_vite@3.2.0
   vue-tsc: 1.0.9_typescript@4.8.4
   windicss: 3.5.6
@@ -218,6 +220,122 @@ packages:
       fastq: 1.13.0
     dev: true
 
+  /@octokit/auth-token/3.0.2:
+    resolution: {integrity: sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@octokit/types': 8.0.0
+    dev: true
+
+  /@octokit/core/4.1.0:
+    resolution: {integrity: sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@octokit/auth-token': 3.0.2
+      '@octokit/graphql': 5.0.4
+      '@octokit/request': 6.2.2
+      '@octokit/request-error': 3.0.2
+      '@octokit/types': 8.0.0
+      before-after-hook: 2.2.3
+      universal-user-agent: 6.0.0
+    transitivePeerDependencies:
+      - encoding
+    dev: true
+
+  /@octokit/endpoint/7.0.3:
+    resolution: {integrity: sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@octokit/types': 8.0.0
+      is-plain-object: 5.0.0
+      universal-user-agent: 6.0.0
+    dev: true
+
+  /@octokit/graphql/5.0.4:
+    resolution: {integrity: sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@octokit/request': 6.2.2
+      '@octokit/types': 8.0.0
+      universal-user-agent: 6.0.0
+    transitivePeerDependencies:
+      - encoding
+    dev: true
+
+  /@octokit/openapi-types/14.0.0:
+    resolution: {integrity: sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==}
+    dev: true
+
+  /@octokit/plugin-paginate-rest/5.0.1_@octokit+core@4.1.0:
+    resolution: {integrity: sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==}
+    engines: {node: '>= 14'}
+    peerDependencies:
+      '@octokit/core': '>=4'
+    dependencies:
+      '@octokit/core': 4.1.0
+      '@octokit/types': 8.0.0
+    dev: true
+
+  /@octokit/plugin-request-log/1.0.4_@octokit+core@4.1.0:
+    resolution: {integrity: sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==}
+    peerDependencies:
+      '@octokit/core': '>=3'
+    dependencies:
+      '@octokit/core': 4.1.0
+    dev: true
+
+  /@octokit/plugin-rest-endpoint-methods/6.7.0_@octokit+core@4.1.0:
+    resolution: {integrity: sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==}
+    engines: {node: '>= 14'}
+    peerDependencies:
+      '@octokit/core': '>=3'
+    dependencies:
+      '@octokit/core': 4.1.0
+      '@octokit/types': 8.0.0
+      deprecation: 2.3.1
+    dev: true
+
+  /@octokit/request-error/3.0.2:
+    resolution: {integrity: sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@octokit/types': 8.0.0
+      deprecation: 2.3.1
+      once: 1.4.0
+    dev: true
+
+  /@octokit/request/6.2.2:
+    resolution: {integrity: sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@octokit/endpoint': 7.0.3
+      '@octokit/request-error': 3.0.2
+      '@octokit/types': 8.0.0
+      is-plain-object: 5.0.0
+      node-fetch: 2.6.7
+      universal-user-agent: 6.0.0
+    transitivePeerDependencies:
+      - encoding
+    dev: true
+
+  /@octokit/rest/19.0.5:
+    resolution: {integrity: sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@octokit/core': 4.1.0
+      '@octokit/plugin-paginate-rest': 5.0.1_@octokit+core@4.1.0
+      '@octokit/plugin-request-log': 1.0.4_@octokit+core@4.1.0
+      '@octokit/plugin-rest-endpoint-methods': 6.7.0_@octokit+core@4.1.0
+    transitivePeerDependencies:
+      - encoding
+    dev: true
+
+  /@octokit/types/8.0.0:
+    resolution: {integrity: sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==}
+    dependencies:
+      '@octokit/openapi-types': 14.0.0
+    dev: true
+
   /@tsconfig/node10/1.0.9:
     resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
     dev: true
@@ -494,6 +612,14 @@ packages:
     engines: {node: '>= 4.0.0'}
     dev: true
 
+  /axios/0.21.4_debug@4.3.4:
+    resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
+    dependencies:
+      follow-redirects: 1.15.2
+    transitivePeerDependencies:
+      - debug
+    dev: true
+
   /axios/1.1.3:
     resolution: {integrity: sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==}
     dependencies:
@@ -512,6 +638,10 @@ packages:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
     dev: true
 
+  /before-after-hook/2.2.3:
+    resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
+    dev: true
+
   /binary-extensions/2.2.0:
     resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
     engines: {node: '>=8'}
@@ -804,6 +934,10 @@ packages:
     resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==}
     dev: false
 
+  /deprecation/2.3.1:
+    resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==}
+    dev: true
+
   /detect-file/1.0.0:
     resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==}
     engines: {node: '>=0.10.0'}
@@ -1148,7 +1282,6 @@ packages:
     peerDependenciesMeta:
       debug:
         optional: true
-    dev: false
 
   /form-data/4.0.0:
     resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
@@ -1402,7 +1535,6 @@ packages:
   /is-plain-object/5.0.0:
     resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
     engines: {node: '>=0.10.0'}
-    dev: false
 
   /is-unicode-supported/0.1.0:
     resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
@@ -1585,6 +1717,18 @@ packages:
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     hasBin: true
 
+  /node-fetch/2.6.7:
+    resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
+    engines: {node: 4.x || >=6.0.0}
+    peerDependencies:
+      encoding: ^0.1.0
+    peerDependenciesMeta:
+      encoding:
+        optional: true
+    dependencies:
+      whatwg-url: 5.0.0
+    dev: true
+
   /normalize-path/3.0.0:
     resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
     engines: {node: '>=0.10.0'}
@@ -1985,6 +2129,10 @@ packages:
       is-number: 7.0.0
     dev: true
 
+  /tr46/0.0.3:
+    resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+    dev: true
+
   /trtc-js-sdk/4.14.6:
     resolution: {integrity: sha512-ERjHQMWY6rmVVAlNTIxaL1PPKjEfewZZDLcEvjDGyGmr0jLYkX3n579A3CfCThTtcBHHpDjsbI2Ac3xnAx1h6g==}
     dev: false
@@ -2039,6 +2187,10 @@ packages:
     engines: {node: '>=4.2.0'}
     hasBin: true
 
+  /universal-user-agent/6.0.0:
+    resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==}
+    dev: true
+
   /universalify/2.0.0:
     resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
     engines: {node: '>= 10.0.0'}
@@ -2081,6 +2233,22 @@ packages:
       vite: 3.2.0_sass@1.55.0
     dev: true
 
+  /vite-plugin-mkcert/1.10.1_vite@3.2.0:
+    resolution: {integrity: sha512-fNNC0z+AcBZExKedjWC7bWlDMf4+WZJqO/4aYf7C/vYY1dqYVOM+zowwTYV0xSx5ZQgplfangPkZk+RwdUlpBg==}
+    engines: {node: '>=v16.0.0'}
+    peerDependencies:
+      vite: '>=3'
+    dependencies:
+      '@octokit/rest': 19.0.5
+      axios: 0.21.4_debug@4.3.4
+      debug: 4.3.4
+      picocolors: 1.0.0
+      vite: 3.2.0_sass@1.55.0
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: true
+
   /vite-plugin-windicss/1.8.8_vite@3.2.0:
     resolution: {integrity: sha512-iyu+ZX0NmhNEUaLPv7xtC+EFRBpWMmw0nhd9a9upayfuNG/thwslKiQKmRB7U/dG0k/2oWLvPDvN/B9i7oRgSA==}
     peerDependencies:
@@ -2184,6 +2352,17 @@ packages:
       defaults: 1.0.4
     dev: true
 
+  /webidl-conversions/3.0.1:
+    resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+    dev: true
+
+  /whatwg-url/5.0.0:
+    resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+    dependencies:
+      tr46: 0.0.3
+      webidl-conversions: 3.0.1
+    dev: true
+
   /which/1.3.1:
     resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
     hasBin: true

+ 2 - 0
src/App.vue

@@ -10,6 +10,7 @@ import type { FloorsType } from "/@/store/modules/scene";
 import { useAppStore } from "./store/modules/app";
 import Guideline from "/@/components/basic/guide.vue";
 import ChatRoom from "/@/components/chatRoom/index.vue";
+import Title from "/@/components/basic/title.vue";
 import browser from "/@/utils/browser";
 
 const sceneStore = useSceneStore();
@@ -126,6 +127,7 @@ const changeMode = (name: string) => {
 <template>
   <LoadingLogo :thumb="true" />
   <!-- 引导页 -->
+  <Title />
   <Guideline />
   <div class="ui-view-layout" :class="{ show: show }">
     <div class="scene" ref="scene$"></div>

+ 2 - 1
src/app.scss

@@ -1,3 +1,4 @@
+@import "./components//basic/dialog/dialog.scss";
 .tab-layer {
   width: 100%;
   text-align: center;
@@ -159,4 +160,4 @@
       }
     }
   }
-}
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 1558
src/components/RTC/PageRtcLive.vue


+ 0 - 592
src/components/RTC/Trtccom.vue

@@ -1,592 +0,0 @@
-<template>
-  <div class="trtccom" v-if="show">
-    <Device @switchDevice="switchDevice" @canUseDevice="canUseDevice" />
-    <div
-      class="local"
-      :class="{ disabledlocal: role == 'customer' || !videoDeviceId }"
-      id="local"
-      v-if="isJoined"
-    >
-      <div class="micBox">
-        <img
-          v-if="muteAudioLeader"
-          :src="require('@/assets/images/rtcLive/mic_off@2x.png')"
-          alt=""
-        />
-        <i v-else class="speak_mic"></i>
-      </div>
-    </div>
-
-    <template v-if="isJoined && invitedRemoteStreams.length > 0">
-      <div
-        class="local"
-        :data-role="item.userId_"
-        :class="{
-          disabledlocal:
-            item.userId_.indexOf('customer') > -1 || !videoDeviceId,
-        }"
-        v-for="item in invitedRemoteStreams"
-        :id="item.userId_"
-        :key="item.userId_"
-      >
-        <div class="micBox">
-          <img
-            v-if="muteAudioLeader"
-            :src="require('@/assets/images/rtcLive/mic_off@2x.png')"
-            alt=""
-          />
-          <i v-else class="speak_mic"></i>
-        </div>
-      </div>
-    </template>
-
-    <div
-      class="videoBox userVideo"
-      v-show="props.videoMuted || muteVideoLeader"
-      v-if="false"
-    >
-      <img
-        :src="require('@/assets/images/rtcLive/avatar_small@2x.png')"
-        alt=""
-      />
-      <div class="micBox">
-        <img
-          v-if="muteAudioLeader"
-          :src="require('@/assets/images/rtcLive/mic_off@2x.png')"
-          alt=""
-        />
-        <i v-else class="speak_mic"></i>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import TRTC, { Client, LocalStream } from "trtc-js-sdk";
-import { Dialog } from "@/global_components/";
-import {
-  ref,
-  computed,
-  watch,
-  defineEmits,
-  defineProps,
-  nextTick,
-  onUnmounted,
-} from "vue";
-import Device from "./trtc/Device";
-import { useStore } from "vuex";
-import browser from "@/utils/browser";
-// import * as apis from "@/apis/index.js";
-
-const emit = defineEmits(["audioMuted", "videoMuted", "closeSocket"]);
-
-const store = useStore();
-
-const show = ref(false);
-const invitedRemoteStreams = ref([]);
-
-const role = ref(browser.getURLParam("role"));
-
-const muteAudioLeader = ref(false);
-const muteVideoLeader = ref(false);
-
-const isJoined = computed(() => store.getters["rtc/isJoined"]);
-const isPublished = computed(() => store.getters["rtc/isPublished"]);
-const videoDeviceId = computed(() => store.getters["rtc/videoDeviceId"]);
-
-const initParamsStates = computed(
-  () =>
-    !!(
-      store.getters["rtc/sdkAppId"] &&
-      store.getters["rtc/secretKey"] &&
-      store.getters["rtc/roomId"] &&
-      store.getters["rtc/userId"]
-    )
-);
-const userSig = computed(() => store.getters["rtc/userSig"]);
-let localClient = "";
-let localStream = "";
-let shareClient = "";
-
-const props = defineProps({
-  audioMuted: {
-    default: false,
-  },
-  videoMuted: {
-    default: false,
-  },
-});
-
-watch(
-  () => props.audioMuted,
-  () => {
-    if (props.audioMuted) {
-      localStream.muteAudio();
-    } else {
-      localStream.unmuteAudio();
-    }
-    if (role.value == "leader") {
-      muteAudioLeader.value = props.audioMuted;
-    }
-  }
-);
-
-watch(
-  () => props.videoMuted,
-  () => {
-    if (props.videoMuted) {
-      localStream.muteVideo();
-    } else {
-      localStream.unmuteVideo();
-    }
-  }
-);
-
-// watch(
-//   () => isJoined.value,
-//   () => {
-//     if (!isJoined.value) {
-//       handleLeave();
-//     }
-//   }
-// );
-
-TRTC.checkSystemRequirements().then((checkResult) => {
-  if (!checkResult.result) {
-    Dialog.toast({ content: `您的设备不支持音视频通讯`, type: "error" });
-  } else {
-    show.value = true;
-  }
-});
-
-async function createLocalStream() {
-  try {
-    localStream = TRTC.createStream({
-      userId: store.getters["rtc/userId"],
-      audio: true,
-      video: false,
-      microphoneId: store.getters["rtc/audioDeviceId"],
-    });
-
-    await localStream.initialize();
-
-    if(props.audioMuted){
-       localStream.muteAudio();
-    }
-
-  } catch (error) {
-    console.log(error, "createStream");
-  }
-}
-
-async function handleJoin() {
-  if (!initParamsStates.value) {
-    return;
-  }
-
-  try {
-    // let res = await apis.getSign({ userId: store.getters["rtc/userId"] });
-    localClient = TRTC.createClient({
-      mode: "rtc",
-      sdkAppId: parseInt(store.getters["rtc/sdkAppId"], 10),
-      userId: store.getters["rtc/userId"],
-      userSig: userSig.value,
-      useStringRoomId: true,
-      enableAutoPlayDialog: false,
-    });
-    installEventHandlers();
-
-    await localClient.join({ roomId: store.getters["rtc/roomId"] });
-    store.commit("rtc/setIsJoined", true);
-    // inviteLink.value = store.commit("rtc/createShareLink");
-  } catch (error) {
-    console.error(error, "error-----------");
-  }
-
-  await createLocalStream();
-  await handlePublish();
-  localStream
-    .play("local")
-    .then(() => {
-      // addLocalControlView();
-    })
-    .catch((e) => {
-      console.log(stream);
-    });
-
-  localStream.on("error", (error) => {
-    if (error.getCode() === 0x4043) {
-      // 自动播放受限导致播放失败,此时引导用户点击页面。
-      // 在点击事件的回调函数中,执行 stream.resume();
-      Dialog.confirm({
-        showCloseIcon: false,
-        okText: "确定",
-        content:
-          "<span style='font-size: 16px; line-height: 1.5;'>在用户与网页产生交互(例如点击、触摸页面等)之前,网页将被禁止播放带有声音的媒体。点击恢复播放<span/>",
-        title: "隐私条款:",
-        single: true,
-        func: (state) => {
-          if (state == "ok") {
-            localStream.resume();
-          }
-        },
-      });
-    }
-  });
-}
-
-async function handlePublish() {
-  if (!isJoined.value) {
-    return;
-  }
-  if (isPublished.value) {
-    return;
-  }
-  if (role.value != "leader") {
-    return;
-  }
-
-  try {
-    await localClient.publish(localStream);
-    store.commit("rtc/setIsPublished", true);
-  } catch (error) {
-    console.error(error, "---------------handlePublish--------------------");
-  }
-}
-
-async function handleStartShare() {
-  shareClient = new ShareClient({
-    sdkAppId: parseInt(store.getters["rtc/sdkAppId"], 10),
-    userId: `share${store.getters["rtc/userId"]}`,
-    roomId: store.getters["rtc/roomId"],
-    secretKey: store.getters["rtc/secretKey"],
-    useStringRoomId: true,
-  });
-  try {
-    await shareClient.join();
-    await shareClient.publish();
-    console.log("Start share screen success");
-    store.isShared = true;
-  } catch (error) {
-    console.error(`Start share error: ${error.message_}`);
-  }
-}
-
-async function handleUnpublish() {
-  if (!isJoined.value) {
-    return;
-  }
-  if (!isPublished.value) {
-    return;
-  }
-  try {
-    await localClient.unpublish(localStream);
-    store.commit("rtc/setIsPublished", false);
-  } catch (error) {
-    console.error(error, "-----------handleUnpublish--------------");
-  }
-}
-
-async function handleLeave() {
-  if (isPublished.value) {
-    await handleUnpublish();
-  }
-  try {
-    uninstallEventHandlers();
-    await localClient.leave();
-    localClient.destroy();
-    localClient = null;
-    invitedRemoteStreams.value.forEach((item) => {
-      item.stop();
-    });
-    invitedRemoteStreams.value = [];
-    store.commit("rtc/setVideoDeviceId", "");
-    store.commit("rtc/setAudioDeviceId", "");
-
-    if (localStream) {
-      localStream.stop();
-      localStream.close();
-      localStream = null;
-      console.log("有执行到这里-------------");
-    }
-  } catch (error) {
-    console.error(error, "-----------handleLeave--------------");
-  }
-}
-
-function installEventHandlers() {
-  if (!localClient) {
-    return;
-  }
-  localClient.on("error", handleError);
-  localClient.on("client-banned", handleBanned);
-  localClient.on("peer-join", handlePeerJoin);
-  localClient.on("peer-leave", handlePeerLeave);
-  localClient.on("stream-added", handleStreamAdded);
-  localClient.on("stream-subscribed", handleStreamSubscribed);
-  localClient.on("stream-removed", handleStreamRemoved);
-  localClient.on("stream-updated", handleStreamUpdated);
-  localClient.on("mute-video", handleMuteVideo);
-  localClient.on("mute-audio", handleMuteAudio);
-  localClient.on("unmute-video", handleUnmuteVideo);
-  localClient.on("unmute-audio", handleUnmuteAudio);
-}
-
-function uninstallEventHandlers() {
-  if (!localClient) {
-    return;
-  }
-  localClient.off("error", handleError);
-  localClient.off("error", handleError);
-  localClient.off("client-banned", handleBanned);
-  localClient.off("peer-join", handlePeerJoin);
-  localClient.off("peer-leave", handlePeerLeave);
-  localClient.off("stream-added", handleStreamAdded);
-  localClient.off("stream-subscribed", handleStreamSubscribed);
-  localClient.off("stream-removed", handleStreamRemoved);
-  localClient.off("stream-updated", handleStreamUpdated);
-  localClient.off("mute-video", handleMuteVideo);
-  localClient.off("mute-audio", handleMuteAudio);
-  localClient.off("unmute-video", handleUnmuteVideo);
-  localClient.off("unmute-audio", handleUnmuteAudio);
-}
-
-function handleMuteVideo(event) {
-  console.log(`[${event.userId}] mute video`);
-  if (event.userId.indexOf("leader") > -1) {
-    muteVideoLeader.value = true;
-  }
-}
-
-function handleMuteAudio(event) {
-  if (event.userId.indexOf("leader") > -1) {
-    muteAudioLeader.value = true;
-  }
-  console.log(event, `[] mute audio`);
-}
-
-function handleUnmuteVideo(event) {
-  console.log(`[${event.userId}] unmute video`);
-  if (event.userId.indexOf("leader") > -1) {
-    muteVideoLeader.value = false;
-  }
-}
-
-function handleUnmuteAudio(event) {
-  console.log(`[${event.userId}] unmute audio`);
-  if (event.userId.indexOf("leader") > -1) {
-    muteAudioLeader.value = false;
-  }
-}
-
-function handleError(error) {
-  console.log(`LocalClient error: ${error.message_}`);
-}
-
-function handleBanned(error) {
-  console.log(`Client has been banned for ${error.message_}`);
-}
-
-function handlePeerJoin(event) {
-  const { userId } = event;
-  if (userId !== "local-screen") {
-    console.log(`Peer Client [${userId}] joined`);
-  }
-}
-
-function handlePeerLeave(event) {
-  const { userId } = event;
-  if (userId !== "local-screen") {
-    console.log(`[${userId}] leave`);
-  }
-}
-
-function handleStreamAdded(event) {
-  const remoteStream = event.stream;
-  const id = remoteStream.getId();
-  const userId = remoteStream.getUserId();
-
-  console.log(remoteStream, "-------------remoteStream");
-
-  if (remoteStream.getUserId() === store.getters["rtc/userId"]) {
-    // don't need to screen shared by us
-    localClient.unsubscribe(remoteStream).catch((error) => {
-      console.info(`unsubscribe failed: ${error.message_}`);
-    });
-  } else {
-    console.log(
-      `remote stream added: [${userId}] ID: ${id} type: ${remoteStream.getType()}`
-    );
-    localClient.subscribe(remoteStream).catch((error) => {
-      console.info(`subscribe failed: ${error.message_}`);
-    });
-  }
-}
-
-async function handleStreamSubscribed(event) {
-  const remoteStream = event.stream;
-
-  if (remoteStream.userId_ == store.getters["rtc/userId"]) {
-    return;
-  }
-  console.info(
-    remoteStream.userId_,
-    store.getters["rtc/userId"],
-    "handleStreamSubscribedhandleStreamSubscribed.value"
-  );
-
-  if (
-    !invitedRemoteStreams.value.some(
-      (item) => item.userId_ == remoteStream.userId_
-    )
-  ) {
-    invitedRemoteStreams.value.push(remoteStream);
-  }
-
-  console.log(invitedRemoteStreams.value, "invitedRemoteStreams.value");
-
-  await nextTick();
-  setTimeout(() => {
-    console.log(remoteStream.userId_, "remoteStream.getId()");
-    remoteStream
-      .play(remoteStream.userId_)
-      .then(() => {
-        console.log(`RemoteStream play success`, 88888888888888888888);
-      })
-      .catch((error) => {
-        console.log(`RemoteStream play failed:  error: ${error.message_}`);
-      });
-  }, 100);
-
-  // const remoteStream = event.stream;
-  // const userId = remoteStream.getUserId();
-  // console.log(`RemoteStream subscribed: [${userId}]`);
-}
-
-function handleStreamRemoved(event) {
-  const remoteStream = event.stream;
-  const userId = remoteStream.getUserId();
-  console.log(`RemoteStream removed: [${userId}]`);
-}
-
-function handleStreamUpdated(event) {
-  const remoteStream = event.stream;
-  const userId = remoteStream.getUserId();
-  console.log(
-    `RemoteStream updated: [${userId}] audio:${remoteStream.hasAudio()} video:${remoteStream.hasVideo()}`
-  );
-}
-
-let switchDevice = async ({ videoId, audioId }) => {
-  console.log()
-  if (!isJoined.value) {
-    return;
-  }
-  if (videoId) {
-    try {
-      await localStream.switchDevice("video", videoId);
-    } catch (error) {}
-  }
-  if (audioId) {
-    try {
-      await localStream.switchDevice("audio", audioId);
-    } catch (error) {}
-  }
-};
-
-onUnmounted(() => {
-  handleLeave();
-});
-
-let canUseDevice = () => {
-  console.log("可用");
-  handleJoin();
-};
-</script>
-
-<style lang="scss" scoped>
-.trtccom {
-  .local {
-    width: 70px;
-    height: 70px;
-    position: fixed;
-    z-index: 9999;
-    top: 20px;
-    left: 20px;
-    border-radius: 50%;
-    overflow: hidden;
-    background: url(~@/assets/images/rtcLive/avatar_small@2x.png) center center
-      no-repeat;
-  }
-  .videoBox {
-    width: 72px;
-    height: 72px;
-    top: 19px;
-    left: 19px;
-    position: fixed;
-    z-index: 99999;
-    border-radius: 50%;
-    overflow: hidden;
-    .loadingTip {
-      position: absolute;
-      z-index: 101;
-      left: 0;
-      top: 0;
-      bottom: 0;
-      right: 0;
-      animation: Rotate 1.5s infinite;
-      @keyframes Rotate {
-        0% {
-          transform: rotate(0deg);
-        }
-        100% {
-          transform: rotate(360deg);
-        }
-      }
-    }
-    > img {
-      width: 100%;
-      height: 100%;
-    }
-  }
-  .micBox {
-    width: 100%;
-    height: 16px;
-    background: rgba(0, 0, 0, 0.3);
-    position: absolute;
-    left: 0;
-    bottom: 0;
-    z-index: 100;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    .speak_mic {
-      display: block;
-      background-size: 720px auto;
-      width: 12px;
-      height: 12px;
-      background-image: url(~@/assets/images/rtcLive/speed.png);
-      // width: 0.69rem;
-      // height: 0.69rem;
-      animation: myAnimation 3s steps(59) infinite;
-    }
-    > img {
-      width: 12px;
-    }
-  }
-  .disabledlocal {
-    opacity: 0 !important;
-    visibility: hidden !important;
-  }
-}
-
-@keyframes myAnimation {
-  0% {
-    background-position: 0px 0px;
-  }
-
-  100% {
-    background-position: -708px 0px;
-  }
-}
-</style>

+ 0 - 69
src/components/RTC/chat/chat.vue

@@ -1,69 +0,0 @@
-<template>
-  <div id="chat">
-    <div id="contents">
-      <div class="chat-item" v-for="(i, index) in chatList" :key="index">
-        <div class="chat-msg">
-          <!-- {{ user_info }} -->
-          <span :class="{ my: i.UserId == user_info.UserId }" class="chat-name"> {{ i.Nickname }} &nbsp;</span>
-          <span class="chat-content"> {{ i.text }}</span>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup type="ts">
-import { onMounted, watch, defineProps, defineEmits, ref, nextTick } from "vue";
-
-const props = defineProps({
-  user_info: {
-    type: Object,
-    default: {},
-  },
-  chatList: {
-    type: Array,
-    default: [],
-  },
-});
-</script>
-
-<style scoped lang="scss">
-#chat {
-  // width: 7.03rem;
-  width: 50%;
-  max-width: 4rem;
-  max-height: 4.58rem;
-  overflow: auto;
-  position: fixed;
-  left: 0.44rem;
-  bottom: 2.83rem;
-  .chat-item {
-  }
-  .chat-msg {
-    width: auto;
-    border-radius: 0.44rem;
-    background: rgba(0, 0, 0, 0.3);
-    padding: 0.17rem 0.28rem;
-    box-sizing: border-box;
-
-    display: inline-block;
-    // margin-bottom: 0.17rem;
-    margin-bottom: 0.1rem;
-    .chat-name {
-      color: #70bbff;
-      font-size: 0.28rem;
-      &.my {
-        color: #ff9a6a;
-      }
-    }
-    .chat-content {
-      color: #fff;
-      font-size: 0.28rem;
-      word-break: break-all;
-    }
-  }
-  &::-webkit-scrollbar {
-    display: none;
-  }
-}
-</style>

+ 0 - 94
src/components/RTC/dialog/checkBrowser.vue

@@ -1,94 +0,0 @@
-<template>
-  <div id="checkBrowser">
-    <i class="iconfont iconshow_cancel" @click="closeCheckBrowser"></i>
-    <p class="title">建議使用以下最新版本的瀏覽器用於通話</p>
-    <div class="browser_list">
-      <div class="item" v-for="i,index in browserList" :key="index">
-        <div class="browser_icon">
-          <img :src="$config.getStaticResource(`img/apps/rtcLive/${i.icon}.png`)" alt="">
-        </div>
-        <div class="browser_name">{{i.name}}</div>
-        <!-- <div class="browser_version ">{{i.version}}</div> -->
-      </div>
-    </div>
-  </div>
-</template>
-
-<script >
-export default {
-  data() {
-    return {
-      browserList: [
-        {
-          icon: "chrome",
-          name: "Chrome",
-          version: "60",
-        },
-        {
-          icon: "firefox",
-          name: "Firefox",
-          version: "55",
-        },
-        {
-          icon: "edge",
-          name: "Edge",
-          version: "40",
-        },
-        {
-          icon: "safari",
-          name: "Safari",
-          version: "11",
-        },
-      ],
-    };
-  },
-  components: {},
-  methods: {
-    closeCheckBrowser() {
-      this.$parent.showCheckBrowser = false;
-    },
-  },
-};
-</script>
-
-<style scoped lang="scss">
-#checkBrowser {
-  position: fixed;
-
-  top: 50%;
-  left: 50%;
-  transform: translate(-50%, -50%);
-  z-index: 1000;
-  background: rgba(0, 0, 0, 0.5);
-  padding: 1.11rem;
-  border-radius: 0.14rem;
-  text-align: center;
-  z-index: 10000;
-  .iconshow_cancel {
-    font-size: 0.42rem;
-    position: absolute;
-    right: 0.33rem;
-    top: 0.33rem;
-  }
-  .title {
-    font-size: 0.28rem;
-    color: #fff;
-  }
-  .browser_list {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    margin: 0.42rem 0 0 0;
-    .item {
-      img {
-        width: 1.39rem;
-        height: auto;
-      }
-      margin-right: 0.28rem;
-      &:last-of-type {
-        margin-right: 0;
-      }
-    }
-  }
-}
-</style>

+ 0 - 357
src/components/RTC/dialog/createdRoom.vue

@@ -1,357 +0,0 @@
-<template>
-  <div id="createdRoom" @click.stop>
-    <div class="created_dialog">
-      <div class="blurBox"></div>
-      <div class="content">
-        <div class="dialog_title" v-if="role == 'leader'">创建一起逛</div>
-        <div class="dialog_title" v-else>进入一起逛</div>
-        <div class="avatar-box" v-if="role == 'leader'" :style="`background-image:url(${avatar || defaultAvatar});`">
-          <input type="file" @change="changeFile($event)" accept=".jpg,.png" />
-          <div class="tips">更换</div>
-        </div>
-        <div class="user_name">
-          <input class="input_name" maxlength="20" v-model.trim="userName" type="text" :placeholder="role == 'leader' ? ' 请输入发起人昵称' : '请输入您的昵称'" />
-          <span class="limitNum">{{ userName.length }}/20</span>
-        </div>
-        <!-- <div v-if="role!='customer'" class="mode_btn">
-          <div @click="chooseMode(i.mode)" v-for="i,index in modeList" :key="index" :class="{ active: mode==i.mode }" class="mode">{{i.title}}</div>
-        </div> -->
-        <div class="created_btn">
-          <div class="created_cancel" @click="closeCreated">取消</div>
-          <div class="created_confirm" @click="createdConfirm">确认</div>
-        </div>
-      </div>
-    </div>
-    <Cropper v-bind="option" v-if="showCrop" @close="closeCrop" @ok="confirmCrop" />
-  </div>
-</template>
-
-<script lang="ts">
-import { Dialog } from '@/global_components/';
-import browser from '@/utils/browser';
-import { useStore } from 'vuex';
-import Cropper from '@/components/cropper/cropper.vue';
-export default {
-  data() {
-    return {
-      role: browser.getURLParam('role') || 'leader',
-      mode: browser.getURLParam('mode') || 2,
-      modeList: [
-        {
-          mode: 1,
-          title: '1V1',
-        },
-        {
-          mode: 2,
-          title: '多人模式',
-        },
-      ],
-      store: useStore(),
-      userName: '',
-      roomId: browser.getURLParam('roomId'),
-      showCrop: false,
-      // base64: null,
-      defaultAvatar: require('@/assets/images/avatar_default.png'),
-      // avatar: null,
-      option: {
-        // img: 'https://4dkk.4dage.com/scene_edit_data/KK-t-SfG2Xcb8QX/user/thumb-1k.jpg?_=1661768330305',
-        img: '',
-      },
-    };
-  },
-
-  mounted() {
-   
-  },
-  computed: {
-    avatar: function () {
-      return this.$store.getters['rtc/avatar']
-    },
-  },
-  components: { Cropper },
-  // created: {},
-  // mounted:{},
-  methods: {
-    changeFile(e) {
-      let file = e.target.files[0];
-
-      let blob = window.URL.createObjectURL(file);
-      console.log(blob);
-      this.option.img = blob;
-      this.openCrop();
-      e.target.value = '';
-    },
-    confirmCrop(base64) {
-      this.$store.commit('rtc/setAvatar', base64);
-    },
-    openCrop() {
-      this.showCrop = true;
-    },
-    closeCrop() {
-      this.showCrop = false;
-    },
-    getUrl(href, queryArr) {
-      queryArr.forEach((item) => {
-        if (!browser.hasURLParam(item.key)) {
-          let ttt = href.split('index.html?');
-          href = `${ttt[0]}index.html?${item.key}=${item.val}&${ttt[1]}`;
-        } else {
-          href = browser.replaceQueryString(href, item.key, item.val);
-        }
-      });
-
-      return href;
-    },
-
-    chooseMode(mode) {
-      this.mode = mode;
-    },
-    closeCreated() {
-      this.$emit('closeCreated');
-    },
-    createdConfirm() {
-      if (this.userName == '') {
-        Dialog.toast({ content: '请输入入您的昵称', type: 'error' });
-        return;
-      }
-      let name = encodeURIComponent(this.userName);
-      let hh = window.location.href;
-
-      if (this.role == 'customer') {
-        let tempUrl = this.getUrl(hh, [
-          {
-            key: 'mode',
-            val: this.mode,
-          },
-          {
-            key: 'name',
-            val: name,
-          },
-          {
-            key: 'role',
-            val: 'customer',
-          },
-          {
-            key: 'roomId',
-            val: this.roomId,
-          },
-        ]);
-        // history.replaceState(null, null, hh + "&mode=" + this.mode + "&name=" + name + "&role=customer&roomId=" + this.roomId);
-        history.replaceState(null, null, tempUrl);
-      } else {
-        let tempUrl = this.getUrl(hh, [
-          {
-            key: 'mode',
-            val: this.mode,
-          },
-          {
-            key: 'name',
-            val: name,
-          },
-          {
-            key: 'role',
-            val: 'leader',
-          },
-        ]);
-
-        // history.replaceState(null, null,hh + "&mode=" + this.mode + "&name=" + name + "&role=leader");
-        history.replaceState(null, null, tempUrl);
-        console.log(tempUrl);
-      }
-      this.store.commit('rtc/setRole', this.role);
-
-      this.$nextTick(() => {
-        this.$emit('createdConfirm');
-      });
-    },
-  },
-};
-</script>
-
-<style scoped lang="scss">
-#createdRoom {
-  width: 100vw;
-  height: 100%;
-  // background: rgba(0, 0, 0, 0.5);
-  background: transparent;
-  position: fixed;
-  left: 0;
-  top: 0;
-  z-index: 1000000;
-  // pointer-events: none;
-  .created_dialog {
-    width: 8.64rem;
-    min-height: 5rem;
-    // background: #ffffff;
-    pointer-events: auto;
-    position: absolute;
-    left: 50%;
-    top: 50%;
-    transform: translate(-50%, -50%);
-    // overflow: hidden;
-    border: 1px solid rgba(255, 255, 255, 0.1);
-    border-radius: 4px;
-
-    .blurBox {
-      position: absolute;
-      z-index: 1;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      background: rgba(0, 0, 0, 0.7);
-      filter: blur(1px);
-    }
-    .content {
-      position: relative;
-      z-index: 2;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-    }
-    .avatar-box {
-      width: 1.7067rem;
-      height: 1.7067rem;
-      margin: 0.56rem auto 0;
-      border: 1px #fff solid;
-      border-radius: 50%;
-      // background-image: url('@/assets/images/avatar_default.jpg');
-      background-size: 100%;
-      background-repeat: no-repeat;
-      position: relative;
-      overflow: hidden;
-      cursor: pointer;
-      &:hover {
-        border: 1px #ed5d18 solid;
-        .tips {
-          color: #ed5d18;
-        }
-      }
-      .tips {
-        width: 100%;
-        height: 0.5rem;
-        position: absolute;
-        background: rgba(0, 0, 0, 0.5);
-        bottom: 0;
-        left: 0;
-        text-align: center;
-        line-height: 0.5rem;
-        font-size: 0.22rem;
-      }
-      input {
-        width: 100%;
-        height: 100%;
-        opacity: 0;
-        position: relative;
-        z-index: 10;
-        cursor: pointer;
-      }
-    }
-    .dialog_title {
-      font-size: 0.39rem;
-      width: 100%;
-      height: 1.39rem;
-      padding: 0 0.56rem;
-      box-sizing: border-box;
-      font-size: 0.39rem;
-      color: #fff;
-      line-height: 1.39rem;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      white-space: nowrap;
-      border-bottom-style: solid;
-      border-bottom-width: 1px;
-      border-bottom-color: rgba(255, 255, 255, 0.1);
-    }
-    .user_name {
-      width: 100%;
-      height: 1.11rem;
-      padding: 0 0.56rem;
-      box-sizing: border-box;
-      font-size: 0.39rem;
-      line-height: 1.11rem;
-      margin: 0.56rem 0;
-      position: relative;
-      .limitNum {
-        position: absolute;
-        right: 0.64rem;
-        top: 50%;
-        transform: translateY(-50%);
-        font-size: 0.33rem;
-        color: #b9bdbc;
-      }
-      .input_name {
-        font-size: 0.39rem;
-        width: 100%;
-        height: 100%;
-        line-height: 1.11rem;
-        padding: 0 1.066667rem 0 0.28rem;
-        box-sizing: border-box;
-        background: rgba(0, 0, 0, 0.5);
-        border-radius: 4px;
-        color: #fff;
-        border: none;
-        outline: none;
-        &::placeholder {
-          color: rgba(255, 255, 255, 0.3);
-        }
-      }
-    }
-    .mode_btn {
-      width: 100%;
-      height: 1.11rem;
-      padding: 0 0.56rem;
-      box-sizing: border-box;
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      margin-bottom: 0.56rem;
-      > div.mode {
-        width: 3.61rem;
-        height: 100%;
-        border-radius: 0.65rem;
-        border: 0.03rem solid #fff;
-        color: #fff;
-        font-size: 0.39rem;
-        line-height: 1.11rem;
-        text-align: center;
-        box-sizing: border-box;
-        &.active {
-          color: #ed5d18;
-          border: 0.03rem solid #ed5d18;
-        }
-      }
-    }
-    .created_btn {
-      width: 100%;
-      height: 1.36rem;
-      border-top-style: solid;
-      border-top-width: 1px;
-      border-top-color: rgba(255, 255, 255, 0.1);
-      box-sizing: border-box;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 0.39rem;
-      > div {
-        width: 50%;
-        height: 1.36rem;
-        text-align: center;
-        line-height: 1.36rem;
-        font-size: 0.39rem;
-        box-sizing: border-box;
-        &.created_cancel {
-          color: #fff;
-          border-right-style: solid;
-          border-right-width: 1px;
-          border-right-color: rgba(255, 255, 255, 0.1);
-        }
-        &.created_confirm {
-          color: #ed5d18;
-        }
-      }
-    }
-  }
-}
-</style>

+ 0 - 143
src/components/RTC/dialog/index.vue

@@ -1,143 +0,0 @@
-<template>
-  <div id="dialog_index">
-    <div class="created_dialog">
-      <div class="blurBox"></div>
-      <div class="content">
-        <div class="dialog_title">{{ props.title }}</div>
-        <p class="dialog_desc">{{ props.desc }}</p>
-        <div class="created_btn">
-          <div class="end_cancel" @click="endLiveCancel">取消</div>
-          <div class="end_confirm" @click="endLiveConfirm">立即结束</div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup type="ts">
-import browser from "/@/utils/browser";
-import { onMounted, watch, defineProps, defineEmits, ref, computed } from "vue";
-import { useStore } from "vuex";
-const store = useStore();
-
-const emit = defineEmits(["closeDialog","confirmDialog"]);
-
-const role = ref(browser.urlHashValue("role"));
-const socket = computed(() => store.getters["rtc/socket"]);
-
-
-const props = defineProps({
-  title: {
-    type: String,
-    default: "温馨提示",
-  },
-  desc: {
-    type: String,
-    default: "是否结束带看?",
-  },
-});
-
-const endLiveCancel = () => {
-  emit("closeDialog");
-};
-
-const endLiveConfirm = () => {
-  // socket.value.emit("disconnect");
-  emit("confirmDialog");
-};
-</script>
-
-<style scoped lang="scss">
-#dialog_index {
-  width: 100vw;
-  height: 100%;
-  // background: rgba(0, 0, 0, 0.5);
-  position: fixed;
-  left: 0;
-  top: 0;
-  z-index: 100000;
-  pointer-events: none;
-  .created_dialog {
-    pointer-events: auto;
-    width: 8.64rem;
-    // min-height: 5rem;
-    // background: #ffffff;
-    border-radius: 8px;
-    position: absolute;
-    left: 50%;
-    top: 50%;
-    transform: translate(-50%, -50%);
-    border: 1px solid rgba(255, 255, 255, 0.1);
-    border-radius: 4px;
-    .blurBox {
-      position: absolute;
-      z-index: 1;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      background: rgba(0, 0, 0, 0.7);
-      filter: blur(1px);
-    }
-    .content {
-      position: relative;
-      z-index: 2;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-    }
-    .dialog_title {
-      width: 100%;
-      height: 1.39rem;
-      padding: 0 0.56rem;
-      box-sizing: border-box;
-      font-size: 0.39rem;
-      color: #fff;
-      line-height: 1.39rem;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      white-space: nowrap;
-      border-bottom-style: solid;
-      border-bottom-width: 1px;
-      border-bottom-color: rgba(255, 255, 255, 0.1);
-    }
-    .dialog_desc {
-      font-size: 0.42rem;
-      color: #fff;
-      padding: 0.56rem 0;
-      text-align: center;
-    }
-
-    .created_btn {
-      width: 100%;
-      height: 1.36rem;
-      border-top-style: solid;
-      border-top-width: 1px;
-      border-top-color: rgba(255, 255, 255, 0.1);
-      box-sizing: border-box;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 0.39rem;
-      > div {
-        width: 50%;
-        height: 1.36rem;
-        text-align: center;
-        line-height: 1.36rem;
-        font-size: 0.39rem;
-        box-sizing: border-box;
-        &.end_cancel {
-          color: #fff;
-          border-right-style: solid;
-          border-right-width: 1px;
-          border-right-color: rgba(255, 255, 255, 0.1);
-        }
-        &.end_confirm {
-          color: #ed5d18;
-        }
-      }
-    }
-  }
-}
-</style>

+ 0 - 169
src/components/RTC/dialog/share.vue

@@ -1,169 +0,0 @@
-<template>
-  <div id="dialog_index">
-    <div class="created_dialog">
-      <div class="blurBox"></div>
-      <div class="content">
-        <div class="dialog_title">{{ title }}</div>
-        <div class="dialog_link">
-          <p>
-            {{ shareLink }}
-          </p>
-        </div>
-
-        <div class="created_btn">
-          <div class="created_cancel" @click="closeCreated">取消</div>
-          <div class="created_confirm"  ref="copylink$" :data-clipboard-text="shareLink" @click="createdConfirm">复制分享</div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { onMounted, watch, defineProps, defineEmits, ref, nextTick } from "vue";
-import ClipboardJS from 'clipboard'
-import { Dialog } from '@/global_components/'
-
-const emit = defineEmits(["closeDialog"]);
-
-const props = defineProps({
-  title: {
-    type: String,
-    default: "邀请好友",
-  },
-  shareLink: {
-    type: String,
-    default: "",
-  },
-});
-const copylink$ = ref(null)
-
-const closeCreated = () => {
-  emit("closeDialog");
-};
-
-const createdConfirm = () => {
-  emit("closeDialog");
-};
-
-onMounted(() => {
-   nextTick(()=>{
-     new ClipboardJS(copylink$.value).on('success', function (e) {
-        e.clearSelection()
-        Dialog.toast({ content: '链接复制成功', type: 'success' })
-    })
-   })
-})
-
-</script>
-
-<style scoped lang="scss">
-#dialog_index {
-  width: 100vw;
-  height: 100%;
-  // background: rgba(0, 0, 0, 0.5);
-  position: fixed;
-  left: 0;
-  top: 0;
-  z-index: 100000;
-  pointer-events: none;
-  .created_dialog {
-    width: 8.64rem;
-    // min-height: 5rem;
-    // background: #ffffff;
-    border-radius: 8px;
-    position: absolute;
-    left: 50%;
-    top: 50%;
-    transform: translate(-50%, -50%);
-    pointer-events: auto;
-    border: 1px solid rgba(255, 255, 255, 0.1);
-    border-radius: 4px;
-    .blurBox {
-      position: absolute;
-      z-index: 1;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      background: rgba(0, 0, 0, 0.7);
-      filter: blur(1px);
-    }
-    .content {
-      position: relative;
-      z-index: 2;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-    }
-    .dialog_title {
-      width: 100%;
-      height: 1.39rem;
-      padding: 0 0.56rem;
-      box-sizing: border-box;
-      font-size: 0.39rem;
-      color: #fff;
-      line-height: 1.39rem;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      white-space: nowrap;
-      border-bottom-style: solid;
-      border-bottom-width: 1px;
-      border-bottom-color: rgba(255, 255, 255, 0.1);
-    }
-    .dialog_link {
-      width: 100%;
-      font-size: 0.39rem;
-      color: rgba(255, 255, 255, 0.5);
-      padding: 0.53rem 0.56rem;
-      box-sizing: border-box;
-      text-align: justify;
-      text-align: left;
-      > p {
-        background: rgba(0, 0, 0, 0.5);
-        padding: 0.15rem 0.28rem;
-        word-break: break-all;
-        word-wrap: break-word;
-        text-overflow: -o-ellipsis-lastline;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        display: -webkit-box;
-        -webkit-line-clamp: 2;
-        line-clamp: 2;
-        -webkit-box-orient: vertical;
-        line-height: 0.72rem;
-      }
-    }
-
-    .created_btn {
-      width: 100%;
-      height: 1.36rem;
-      border-top-style: solid;
-      border-top-width: 1px;
-      border-top-color: rgba(255, 255, 255, 0.1);
-      box-sizing: border-box;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      > div {
-        width: 50%;
-        height: 1.36rem;
-        text-align: center;
-        line-height: 1.36rem;
-        font-size: 0.39rem;
-        box-sizing: border-box;
-        &.created_cancel {
-          color: #fff;
-          border-right-style: solid;
-          border-right-width: 1px;
-          border-right-color: rgba(0, 0, 0, 0.05);
-        }
-        &.created_confirm {
-          color: #ed5d18;
-        }
-      }
-    }
-  }
-}
-</style>

+ 0 - 151
src/components/RTC/index.vue

@@ -1,151 +0,0 @@
-<template>
-  <transition mode="out-in">
-    <div>
-    
-      <dialogIndex @closeDialog="closeDialog" @confirmDialog="confirmDialog" v-if="dialog == 'dialogIndex'"></dialogIndex>
-      <dialogShare :shareLink="shareLink" @closeDialog="closeDialog" v-if="dialog == 'dialogShare'"></dialogShare>
-      <createdRoom v-if="showCreated" @closeCreated="closeCreated" @createdConfirm="createdConfirm()"></createdRoom>
-      <PageRtcLive @closeSocket="confirmDialog" @openDialog="openDialog" v-if="show"></PageRtcLive>
-    </div>
-  </transition>
-</template>
-
-<script setup>
-import PageRtcLive from './PageRtcLive';
-import { useMusicPlayer } from "@/utils/sound";
-// import Draw from "./paint/Draw";
-import createdRoom from './dialog/createdRoom';
-import dialogIndex from './dialog/index.vue';
-import dialogShare from './dialog/share.vue';
-import { Dialog } from '@/global_components/';
-import browser from '@/utils/browser';
-import { onMounted, watch, computed, ref, nextTick, watchEffect } from 'vue';
-import { useStore } from 'vuex';
-import { useApp, getApp } from '@/app';
-import { useCurrentMini } from './mini-platform'
-import { isMiniApp } from '@/env'
-import { leaveRoom } from '@/store/room'
-
-console.error('RTC create')
-const store = useStore();
-
-const shareLink = ref('');
-const dialog = ref('');
-const show = ref(false);
-const showPaint = ref(true);
-const showCreated = ref(false);
-const roomId = ref(browser.getURLParam('roomId'));
-const role = ref(browser.getURLParam('role'));
-const userName = ref(browser.getURLParam('name'));
-const socket = computed(() => store.getters['rtc/socket']);
-
-// const musicPlayer = useMusicPlayer();
-
-// useMusicPlayer().pause(true);
-
-const openDialog = (str, link) => {
-  shareLink.value = link;
-  dialog.value = str;
-};
-const closeDialog = (str, link) => {
-  dialog.value = '';
-  dialog.value = str;
-};
-
-const confirmDialog = async () => {
-  if (isMiniApp) {
-    useCurrentMini()?.leave()
-  }
-  await getApp().Connect.follow.exit();
-  await leaveRoom()
-  if (socket.value) {
-    if (browser.getURLParam('role') == 'leader') {
-      socket.value.emit('action', { type: 'leader-dismiss' });
-    }
-
-    setTimeout(() => {
-      socket.value.close();
-      store.commit('rtc/setSocket', null);
-    }, 0);
-  }
-
-  store.commit('rtc/setIsJoined', false);
-
-  store.commit('rtc/setVideoDeviceId', '');
-  store.commit('rtc/setAudioDeviceId', '');
-
-  let tempUrl = window.location.href;
-  ['mode', 'name', 'role','vruserId'].forEach((item) => {
-    tempUrl = browser.replaceQueryString(tempUrl, item, '');
-  });
-  // tempUrl = browser.replaceQueryString(tempUrl, 'role', 'customer')
-
-  history.replaceState(null, null, tempUrl);
-  store.commit('rtc/setRole', '');
-
-  store.commit('showShoppingguide', false);
-  dialog.value = '';
-
-  setTimeout(() => {
-    location.reload();
-  }, 100);
-};
-
-const closeCreated = (str, link) => {
-  store.commit('rtc/setAvatar', null);
-  store.commit('showShoppingguide', false);
-  showCreated.value = false;
-  //showguide();
-  if (browser.getURLParam('role')) {
-    let tempUrl = window.location.href;
-    ['mode', 'name', 'role', 'roomId', 'vruserId'].forEach((item) => {
-      tempUrl = browser.replaceQueryString(tempUrl, item, '');
-    });
-    history.replaceState(null, null, tempUrl);
-  }
-};
-
-const createdConfirm = (str, link) => {
-  showCreated.value = false;
-  show.value = true;
-  //showguide();
-};
-
-
-const showguide = () => {
-  if (!localStorage.getItem('user_guide')) {
-    Dialog.confirm({
-      showCloseIcon: false,
-      okText: '我知道了',
-      content: "<span style='font-size: 16px; line-height: 1.5;'>開發者已遵守收集、使用最終用戶個人信息有關的所有可適用法律、政策和法規,保護用戶個人信息安全。<span/>",
-      title: '隱私條款:',
-      single: true,
-      func: (state) => {
-        if (state == 'ok') {
-          localStorage.setItem('user_guide', Date.now());
-        }
-      },
-    });
-  }
-};
-
-onMounted(() => {
-  useApp().then(async (sdk) => {
-    await nextTick();
-    if (userName.value) {
-      createdConfirm();
-    } else {
-      showCreated.value = true;
-      // showguide()
-    }
-  });
-  // showCreated.value = true;
-
-});
-
-
-</script>
-
-<style lang="scss" scoped>
-
-</style>

+ 0 - 79
src/components/RTC/mini-platform.js

@@ -1,79 +0,0 @@
-import { useStore } from "vuex";
-import { watchEffect, watch, computed, nextTick } from 'vue'
-import browser from "@/utils/browser";
-
-let mini
-
-export const useCurrentMini = () => mini
-
-export const initialMini = (audioMuted) => {
-  const store = useStore();
-  const socket = computed(() => store.getters["rtc/socket"]);
-
-  let isStop = false
-  const stopUpdate = (cb) => {
-    isStop = true
-    cb()
-    nextTick(() => isStop = false)
-  }
-
-
-  watch(socket, (news, olds, onCleatup) => {
-    if (!socket.value) {
-      return;
-    }
-
-    const signalActions = {
-      openAudioCallback(isOpen) {
-        stopUpdate(() => {
-          audioMuted.value = !isOpen
-          console.log('更改')
-        })
-      }
-    }
-    
-    let stopMutedWatch
-    const joinCallback = data => {
-      console.log('join room')
-      store.commit("rtc/setAvatar", browser.getURLParam("avatar"));
-      stopMutedWatch && stopMutedWatch()
-      console.log('进入监听')
-      stopMutedWatch = watch(audioMuted, (audioMuted, oldAudioMuted) => {
-        if (!isStop && audioMuted !== oldAudioMuted) {
-          console.error(audioMuted, oldAudioMuted)
-          socket.value.emit('signal', { type: 'openAudio', payload: !audioMuted })
-          if (audioMuted) {
-            console.log('静音请求')
-          } else {
-            console.log('开启声音请求')
-          }
-        }
-      }, { immediate: true, flush: 'pre' })
-    }
-    const signalCallback = data => {
-      if (data.type in signalActions) {
-        signalActions[data.type](data.payload)
-      }
-    }
-
-    socket.value.on("join", joinCallback)
-    socket.value.on('signal', signalCallback)
-
-    onCleatup(() => {
-      stopMutedWatch && stopMutedWatch()
-      socket.value.off('join', joinCallback)
-      socket.value.on('signal', signalCallback)
-    })
-  }, { immediate: true })
-
-  mini = {
-    enterShare() {
-      socket.value.emit('signal', { type: 'enterShareMode' })
-    },
-    leave() {
-      socket.value.emit('signal', { type: 'onUnload' })
-    }
-  }
-
-  return mini
-}

+ 0 - 483
src/components/RTC/paint/Draw.vue

@@ -1,483 +0,0 @@
-<template>
-  <div class="paint">
-    <canvas ref="canvas" v-show="show"></canvas>
-    <!-- <div class="toolbar" v-show="showPaint">
-      <ul>
-        <li @click="onDraw('drawStart')" v-show="show == false">
-          <i class="iconfont icontagging"></i>
-          <div>画笔</div>
-        </li>
-        <li @click="onDraw('drawUndo')" v-if="show" :class="{ disable: !canUndo }">
-          <i class="iconfont icon_cancel"></i>
-          <div>撤回</div>
-        </li>
-        <li @click="onDraw('drawStop')" v-if="show">
-          <i class="iconfont iconclose"></i>
-          <div>关闭</div>
-        </li>
-      </ul>
-    </div> -->
-  </div>
-</template>
-<script lang="ts">
-import { getRole, sendToH5 } from "../../../utils/rtc_socket";
-import { objects } from "/@/core/base";
-import math from "/@/core/util/math";
-import convertTool from "/@/core/util/convertTool";
-
-
-
-export default {
-  props: {
-    showPaint: Boolean,
-  },
-  data() {
-    return {
-      role: getRole(),
-      show: false,
-      canUndo: false,
-      colorA: "#02c8ae",
-      colorB: "#2e98fe",
-    };
-  },
-  watch: {
-    showPaint() {
-      if (this.showPaint) {
-        this.role = getRole();
-      }
-    },
-    show() {
-      this.$bus.emit("shop/header/disable", this.show);
-    },
-  },
-  created() {
-    this.$bus.on("shop/sync/action", (data) => {
-      if (data.type == "drawStart") {
-        this.show = true;
-        this.draw = [];
-        this.drawHistory = [];
-        this.$nextTick(() => {
-          this.onDrawStart();
-        });
-      } else if (data.type == "drawStop") {
-        this.show = false;
-        this.draw = null;
-        this.drawHistory = null;
-      } else if (data.type == "drawing") {
-        const draw = this.transformTo2d(data.data.drawing);
-        if (data.data.role != this.role) {
-          this.drawHistory.push(JSON.parse(JSON.stringify(draw)));
-          this.drawing(draw);
-        }
-      } else if (data.type == "drawUndo") {
-        this.drawUndo(data.data.role);
-      }
-    });
-  },
-  mounted() {
-    this.canvas = this.$refs.canvas;
-    this.context = this.canvas.getContext("2d");
-    this.canvas.onmousedown = (e) => {
-      if (!this.show || this.role != "leader") {
-        return;
-      }
-      e.preventDefault();
-      this.beginStroke({
-        x: e.clientX,
-        y: e.clientY,
-      });
-    };
-    this.canvas.onmouseup = (e) => {
-      if (!this.show || this.role != "leader") {
-        return;
-      }
-      e.preventDefault();
-      this.endStroke();
-    };
-    this.canvas.onmouseout = (e) => {
-      if (!this.show || this.role != "leader") {
-        return;
-      }
-      e.preventDefault();
-      this.endStroke();
-    };
-    this.canvas.onmousemove = (e) => {
-      if (!this.show || this.role != "leader") {
-        return;
-      }
-      e.preventDefault();
-      if (this._mouseDown) {
-        this.moveStroke({
-          x: e.clientX,
-          y: e.clientY,
-        });
-      }
-    };
-
-    let touch;
-
-    // 移动端触控
-    this.canvas.addEventListener("touchstart", (e) => {
-      if (!this.show || this.role != "leader") {
-        return;
-      }
-      e.preventDefault();
-      touch = e.touches[0];
-      this.beginStroke({
-        x: touch.pageX,
-        y: touch.pageY,
-      });
-    });
-    this.canvas.addEventListener("touchmove", (e) => {
-      if (!this.show || this.role != "leader") {
-        return;
-      }
-      e.preventDefault();
-      if (this._mouseDown) {
-        touch = e.touches[0];
-        this.moveStroke({
-          x: touch.pageX,
-          y: touch.pageY,
-        });
-      }
-    });
-
-    this.canvas.addEventListener("touchend", (e) => {
-      if (!this.show || this.role != "leader") {
-        return;
-      }
-      e.preventDefault();
-      this.endStroke();
-    });
-
-    this.mouse = new THREE.Vector2();
-  },
-  methods: {
-    /**
-     * 2d数据转3d数据
-     */
-    transformTo3d(draw) {
-      const data = [];
-      if (draw.length == 0) {
-        return [];
-      }
-      draw.forEach((item, index) => {
-        math.convertScreenPositionToNDC(item.x, item.y, this.mouse);
-
-        var intersect = .getMouseIntersect(
-          objects.player.camera,
-          [this.intersectPlane],
-          this.mouse
-        );
-
-        if (!intersect) {
-          console.error("no intersect ??");
-        } else {
-          item.pos3d = intersect.point;
-          data.push(item);
-        }
-      });
-
-      return data;
-    },
-    /**
-     * 3d数据转2d数据
-     */
-    transformTo2d(draw) {
-      const data = [];
-      draw.forEach((item) => {
-        var pos3d = new THREE.Vector3(item.pos3d.x, item.pos3d.y, item.pos3d.z);
-        var pos2d = convertTool.getPos2d(pos3d, objects.player.camera);
-        //delete item.pos3d;
-        item.x = pos2d.pos.x;
-        item.y = pos2d.pos.y;
-        data.push(item);
-      });
-      return data;
-    },
-    onDraw(type) {
-      if (type == "drawStart") {
-        this.show = true;
-        this.draw = [];
-        this.drawHistory = [];
-        this.$nextTick(() => {
-          this.onDrawStart();
-        });
-      } else if (type == "drawStop") {
-        this.show = false;
-        this.draw = null;
-        this.drawHistory = null;
-      } else if (type == "drawing") {
-        const draw = this.transformTo2d(data.content.drawing);
-        if (data.role != role) {
-          this.drawHistory.push(JSON.parse(JSON.stringify(draw)));
-          //this.drawing(draw);
-        }
-      } else if (type == "drawUndo") {
-        this.drawUndo(this.role);
-      }
-      sendToH5({
-        type,
-        data: {
-          role: this.role,
-        },
-      });
-    },
-    onPainting() {
-      const draw = this.transformTo3d(this.draw);
-      this.drawHistory.push(JSON.parse(JSON.stringify(draw)));
-      sendToH5({
-        type: "drawing",
-        data: {
-          drawing: draw,
-        },
-      });
-      this.draw = [];
-      this.canUndo = true;
-      this._endTime = 0;
-      this._mouseDown = false;
-      this._lastTimestamp = 0;
-      this.$emit("sendCanUndo", this.canUndo);
-    },
-    beginStroke(point) {
-      this._mouseDown = true;
-      this._lastTimestamp = Date.now();
-      this._lastPosition = this.windowToCanvas(point.x, point.y);
-      this.draw.push({
-        role: this.role,
-        width: 0,
-        x: this._lastPosition.x,
-        y: this._lastPosition.y,
-        t: 5, //this._lastTimestamp - this._endTime
-      });
-    },
-    onDrawStart() {
-      let dpr = window.devicePixelRatio || 1;
-      let rect = this.canvas.getBoundingClientRect();
-      this.ratio = 1; // window.innerWidth / 375;
-      this.canvas.width = rect.width * dpr;
-      this.canvas.height = rect.height * dpr;
-      this.context.scale(dpr, dpr);
-      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
-      this._endTime = 0;
-      this._mouseDown = false;
-      this._lastTimestamp = 0;
-      this._lastLineWidth = -1;
-      this._lastPosition = {
-        x: 0,
-        y: 0,
-      };
-
-      math.convertScreenPositionToNDC(0, 0, this.mouse); //取屏幕中点
-      var intersect = convertTool.getMouseIntersect(
-        objects.player.camera,
-        [objects.model.skybox, objects.sceneRenderer.scene.skyboxBG],
-        this.mouse
-      );
-      this.placeIntersectPlane(intersect && intersect.point);
-    },
-    moveStroke(point) {
-      let timestamp = Date.now();
-      let position = this.windowToCanvas(point.x, point.y);
-      let s = this.calcDistance(position, this._lastPosition);
-      let t = timestamp - this._lastTimestamp;
-      let lineWidth = this.calcLineWidth(t, s);
-
-      //draw
-      this.context.beginPath();
-      this.context.moveTo(this._lastPosition.x, this._lastPosition.y);
-      this.context.lineTo(position.x, position.y);
-
-      this.draw.push({
-        role: this.role,
-        width: lineWidth,
-        x: position.x,
-        y: position.y,
-        t: 5, //t
-      });
-
-      this.context.strokeStyle = this.colorA;
-      this.context.lineWidth = lineWidth;
-      this.context.lineCap = "round";
-      this.context.linJoin = "round";
-      this.context.stroke();
-
-      //每次过程结束时,将结束值赋给初始值,一直延续
-      this._lastPosition = position;
-      this._lastTimestamp = timestamp;
-      this._lastLineWidth = lineWidth;
-    },
-    endStroke() {
-      this.draw.push({
-        role: this.role,
-        width: 0,
-        x: this._lastPosition.x,
-        y: this._lastPosition.y,
-        t: 0,
-      });
-      this.onPainting();
-      this._mouseDown = false;
-      this._endTime = Date.now();
-    },
-    calcLineWidth(t, s) {
-      let v = s / t;
-      let resultLineWidth;
-
-      if (v <= 0.1) {
-        resultLineWidth = 6;
-      } else if (v >= 3) {
-        resultLineWidth = 2;
-      } else {
-        resultLineWidth = 6 - ((v - 0.1) / (3 - 0.1)) * (6 - 4);
-      }
-      if (this._lastLineWidth == -1) {
-        return resultLineWidth;
-      }
-      return (this._lastLineWidth * 2) / 3 + (resultLineWidth * 1) / 3;
-    },
-    calcDistance(pos1, pos2) {
-      return Math.sqrt(
-        (pos1.x - pos2.x) * (pos1.x - pos2.x) +
-          (pos1.y - pos2.y) * (pos1.y - pos2.y)
-      ); //通过起始结束坐标x,y值计算路程长度
-    },
-    windowToCanvas(x, y) {
-      var bbox = this.canvas.getBoundingClientRect(); //获取canvas的位置信息
-      return {
-        x: Math.round(x - bbox.left),
-        y: Math.round(y - bbox.top),
-      }; //返回当前鼠标相对于canvas的位置
-    },
-    drawing(draw) {
-      for (let i = 0; i < draw.length - 1; i++) {
-        draw[i].t &&
-          setTimeout(() => {
-            this.context.beginPath();
-            this.context.strokeStyle =
-              draw[i].role == this.role ? this.colorA : this.colorB;
-            this.context.moveTo(draw[i].x * this.ratio, draw[i].y * this.ratio);
-            this.context.lineTo(
-              draw[i + 1].x * this.ratio,
-              draw[i + 1].y * this.ratio
-            );
-            this.context.lineWidth = draw[i].width * this.ratio;
-            this.context.lineCap = "round";
-            this.context.linJoin = "round";
-            this.context.stroke();
-          }, 5);
-      }
-    },
-    drawUndo(sender) {
-      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
-      for (let i = this.drawHistory.length - 1; i >= 0; i--) {
-        if (this.drawHistory[i][0].role == sender) {
-          this.drawHistory.splice(i, 1);
-          break;
-        }
-      }
-      this.drawHistory.forEach((draw) => {
-        for (let i = 0; i < draw.length - 1; i++) {
-          if (draw[i].t) {
-            this.context.beginPath();
-            this.context.strokeStyle =
-              draw[i].role == this.role ? this.colorA : this.colorB;
-            this.context.moveTo(draw[i].x * this.ratio, draw[i].y * this.ratio);
-            this.context.lineTo(
-              draw[i + 1].x * this.ratio,
-              draw[i + 1].y * this.ratio
-            );
-            this.context.lineWidth = draw[i].width * this.ratio;
-            this.context.lineCap = "round";
-            this.context.linJoin = "round";
-            this.context.stroke();
-          }
-        }
-      });
-      this.canUndo = this.drawHistory.some((item) => item[0].role == this.role);
-    },
-    placeIntersectPlane(pos) {
-      //用于判断mesh拖拽移动距离的平面 需要和视线垂直,以保证遮住视野范围
-      if (!this.intersectPlane) {
-        var geo = new THREE.PlaneGeometry(8000, 80000, 1, 1);
-        //var geo = new THREE.PlaneGeometry(3,3,1,1);
-        this.intersectPlane = new THREE.Mesh(
-          geo,
-          new THREE.MeshBasicMaterial({
-            transparent: true,
-            wireframe: false,
-            opacity: 0,
-            side: THREE.DoubleSide,
-            depthTest: false,
-          })
-        );
-        this.intersectPlane.lookAt(new THREE.Vector3(0, 1, 0));
-        this.intersectPlane.name = "intersectPlane";
-        objects.model.add(this.intersectPlane);
-      }
-      if (pos) {
-        this.intersectPlane.position.copy(pos);
-        var cameraDir = objects.player.getDirection(
-          null,
-          objects.player.camera
-        ); //向里
-        this.intersectPlane.lookAt(pos.clone().add(cameraDir)); //看向相机
-      }
-    },
-  },
-};
-</script>
-<style lang="scss" scoped>
-.paint {
-  position: absolute;
-  left: 0;
-  top: 0;
-  width: 100%;
-  height: 100%;
-  z-index: 2000;
-  pointer-events: none !important;
-  canvas {
-    width: 100vw;
-    height: 100vh;
-    image-rendering: pixelated;
-    image-rendering: crisp-edges;
-    pointer-events: auto;
-    position: fixed;
-    top: 0;
-    left: 0;
-  }
-  .toolbar {
-    pointer-events: auto;
-    position: absolute;
-    right: 0.35rem;
-    bottom: 4.5rem;
-    padding: 0.4rem 0.2rem;
-    border-radius: 30px;
-    z-index: 100;
-    background-color: rgba(0, 0, 0, 0.3);
-    ul,
-    li {
-      margin: 0;
-      padding: 0;
-      list-style: none;
-    }
-    li {
-      padding: 0.3px;
-      text-align: center;
-      font-size: 14px;
-      margin-bottom: 0.5rem;
-      &:last-child {
-        margin-bottom: 0;
-      }
-      i {
-        font-size: 20px;
-        &.icon_cancel {
-          font-size: 22px;
-        }
-        &.iconclose {
-          font-size: 14px;
-        }
-      }
-    }
-  }
-}
-</style>

+ 0 - 308
src/components/RTC/paint/index.vue

@@ -1,308 +0,0 @@
-<template>
-    <div class="paint" v-show="showPaint && role == 'leader'">
-        <div class="toolbar">
-            <ul>
-                <li v-show="show == false" @click="start">
-                    <i class="iconfont iconkd_tagging"></i>
-                    <div>标记</div>
-                </li>
-                <li v-if="show" @click="end">
-                    <i class="iconfont iconclose"></i>
-                    <div>关闭</div>
-                </li>
-            </ul>
-        </div>
-    </div>
-</template>
-<script lang="ts">
-import { app } from "@/core"
-import { objects } from "@/core/base"
-import config from "@/config"
-import { getRole, sendToH5,sendToApp } from "../../../socket"
-import settings from "@/core/settings"
-import math from "@/core/util/math"
-import convertTool from "@/core/util/convertTool"
-import texture from "@/core/util/texture.js"
-import lerp from "@/core/util/lerp"
-import transitions from "@/core/util/transitions"
-import easing from "@/core/util/easing"
-
-let meshGroup = null //new THREE.Object3D()
-let circles = []
-const map = texture.load(config.getStaticResource("img/scene_tabele.png?v=1"), null, null, {
-    antialias: false,
-})
-
-export default {
-    props: {
-        showPaint: Boolean,
-    },
-    data() {
-        return {
-            role: getRole(),
-            show: false,
-            flags: [],
-            canUndo: false,
-            colorA: "#02c8ae",
-            colorB: "#2e98fe",
-        }
-    },
-    created() {
-        const self = this
-
-        if (meshGroup == null) {
-            meshGroup = new THREE.Object3D()
-            meshGroup.name = "shop-circles"
-            if (app.ready) {
-                app.model.add(meshGroup)
-            } else {
-                this.$bus.on("ready", function() {
-                    app.model.add(meshGroup)
-                })
-            }
-        }
-
-        function remove() {
-            setTimeout(() => {
-                self.flags.shift()
-                if (self.flags.length) {
-                    remove()
-                }
-                self.updateCirclesDrawing(self.flags)
-            }, 1000)
-        }
-
-        function syncAction(data) {
-            if (data.type == "flags") {
-                self.flags.push(data.data)
-                self.updateCirclesDrawing(self.flags)
-                remove()
-            }
-        }
-
-        this.$bus.on("shop/sync/action", syncAction)
-        this.$once("hook:beforeDestroy", () => {
-            this.$bus.off("shop/sync/action", syncAction)
-            this.show = false
-            this.flags = []
-            this.updateCirclesDrawing(this.flags)
-        })
-    },
-    watch: {
-        show() {
-            sendToApp('cart',!this.show)
-            this.$bus.emit("shop/header/disable", this.show)
-            setTimeout(() => {
-                app.play.control.noFly = this.show
-            }, 300);
-        },
-    },
-    mounted() {
-        var self = this
-        function draw(e) {
-            if (!self.show) {
-                return
-            }
-            const x = (e.touches[0] || e).clientX
-            const y = (e.touches[0] || e).clientY
-            const f = {
-                sid: Date.now(),
-                pos: self.transformTo3d({ x, y }),
-                color: self.colorA,
-            }
-            self.flags.push(f)
-            self.updateCirclesDrawing(self.flags)
-
-             setTimeout(() => {
-                self.flags.shift()
-                self.updateCirclesDrawing(self.flags)
-            }, 1000)
-
-            sendToH5({ type: "flags", data: f })
-           
-        }
-        const $player = document.getElementById("player")
-        $player.addEventListener("mousedown", draw)
-        $player.addEventListener("touchstart", draw)
-        this.role = getRole()
-    },
-    methods: {
-        start() {
-            this.show = true
-        },
-        undo() {
-            this.flags.pop()
-            this.updateCirclesDrawing(this.flags)
-        },
-        end() {
-            this.show = false
-            this.flags = []
-            this.updateCirclesDrawing(this.flags)
-        },
-        updateCirclesDrawing(flags) {
-            //呈现一系列circles  其中包含已经绘制好的
-
-            var dels = []
-            circles.forEach(circle => {
-                //检查是否有要删除的
-                var find = flags.find(info => circle.sid == info.sid)
-                if (!find) {
-                    dels.push(circle)
-                }
-            })
-            dels.forEach(circle => this.fadeCircle(circle))
-
-            flags.forEach(info => {
-                //再添加新的
-                var circle = meshGroup.children.find(circle => circle.sid == info.sid)
-                if (circle) {
-                    //如果要修改什么属性在这修改
-                } else {
-                    this.createCircle(info.sid, info.pos, info.color)
-                }
-            })
-        },
-
-        transformTo3d(pos2d) {
-            //获取三维坐标
-            const data = new THREE.Vector3()
-            const mouse = new THREE.Vector2()
-
-            math.convertScreenPositionToNDC(pos2d.x, pos2d.y, mouse)
-
-            var intersect = convertTool.getMouseIntersect(
-                objects.player.camera,
-                [objects.model.skybox],
-                mouse
-            )
-
-            if (!intersect) {
-                console.error("no intersect ??")
-            } else {
-                data.copy(intersect.point)
-                var dir = data
-                    .clone()
-                    .sub(objects.player.position)
-                    .normalize()
-                data.copy(objects.player.position.clone().add(dir))
-            }
-
-            return data
-        },
-        createCircle(sid, pos, color) {
-            //绘制新的圈
-
-            var circle = new THREE.Sprite(this.getMat(color))
-            circle.sid = sid
-            circle.position.copy(pos)
-            circle.quaternion.copy(objects.player.camera.quaternion)
-            circle.scale.set(0.05, 0.05, 0.05)
-            meshGroup.add(circle)
-            circles.push(circle)
-            /*var scale = math.getScaleForConstantSize({
-                width2d:100,
-                camera:objects.player.camera,
-                position: pos
-            })
-            bubble.scale.copy(scale)*/
-        },
-
-        fadeCircle(circle) {
-            //开始消失
-            //console.log("fadeCircle"+circle.sid)
-            var _duration = 800
-            transitions.start(
-                lerp.property(circle.material, "opacity", 0),
-                _duration,
-                () => {
-                    this.removeCircle(circle)
-                },
-                0,
-                easing[settings.transition.blendEasing],
-                "shop-circles-fade",
-                settings.freeze.shopCircle
-            )
-
-            //从列表中删除
-            var index = circles.indexOf(circle)
-            circles.splice(index, 1)
-        },
-
-        removeCircle(circle) {
-            //移除某个圈 <!-- sid -->
-            //var circle = meshGroup.children.find(circle=>circle.sid == sid);
-            //console.log("removeC " + circle.sid)
-            circle.material.dispose()
-            circle.parent.remove(circle)
-        },
-
-        getMat(color) {
-            /*var mat = this.materials[color]
-            if (mat) return mat
-            else {
-                mat = new THREE.SpriteMaterial({
-                    transparent: true,
-                    depthTest: false,
-                    map: map,
-                    side: THREE.DoubleSide,
-                    color: color,
-                })
-                this.materials[color] = mat
-                return mat
-            }*/
-            return new THREE.SpriteMaterial({
-                transparent: true,
-                depthTest: false,
-                map: map,
-                side: THREE.DoubleSide,
-                color: color,
-            })
-        },
-    },
-}
-</script>
-<style lang="scss" scoped>
-.paint {
-    position: absolute;
-    left: 0;
-    top: 0;
-    width: 100%;
-    height: 100%;
-    z-index: 99;
-    pointer-events: none !important;
-    .toolbar {
-        pointer-events: auto;
-        position: absolute;
-        right: 0.35rem;
-        bottom: 4.5rem;
-        padding: 0.4rem 0.2rem;
-        border-radius: 30px;
-        z-index: 100;
-        background-color: rgba(0, 0, 0, 0.3);
-        ul,
-        li {
-            margin: 0;
-            padding: 0;
-            list-style: none;
-        }
-        li {
-            padding: 0.3px;
-            text-align: center;
-            font-size: 14px;
-            margin-bottom: 0.5rem;
-            &:last-child {
-                margin-bottom: 0;
-            }
-            i {
-                font-size: 20px;
-                &.icon_cancel {
-                    font-size: 22px;
-                }
-                // &.iconclose {
-                //     font-size: 14px;
-                // }
-            }
-        }
-    }
-}
-</style>

+ 0 - 9
src/components/RTC/socket/index.ts

@@ -1,9 +0,0 @@
-import browser from "/@/utils/browser"
-import uuid from 'uuid'
-
-export let userName = decodeURIComponent(browser.urlHashValue("name"))
-export let roomId = browser.urlHashValue("roomId") || uuid(24)
-export let userId = browser.urlHashValue("userId") || uuid(12)
-export let mode = browser.urlHashValue("mode") 
-export let role = browser.urlHashValue("role") || "leader" //customer 普通用户
-export let muted = false //默认不静音

+ 0 - 79
src/components/RTC/trtc/Device.vue

@@ -1,79 +0,0 @@
-<template>
-  <div></div>
-</template>
-
-<script lang="ts" setup>
-import { defineEmits, computed, ref } from "vue";
-import TRTC from "trtc-js-sdk";
-import { useStore } from "vuex";
-import { Dialog } from "@/global_components/";
-import browser from "@/utils/browser";
-
-const store = useStore();
-
-const videoDeviceId = computed(() => store.getters["rtc/videoDeviceId"]);
-const audioDeviceId = computed(() => store.getters["rtc/audioDeviceId"]);
-const role = ref(browser.getURLParam("role"));
-
-const emit = defineEmits(["switchDevice", "canUseDevice"]);
-
-const updateDevice = async () => {
-  console.log("updateDevice");
-  // const cameraItems = await TRTC.getCameras();
-  // cameraItems.forEach((item) => {
-  //   item.value = item.deviceId;
-  // });
-  const microphoneItems = await TRTC.getMicrophones();
-  microphoneItems.forEach((item) => {
-    item.value = item.deviceId;
-  });
-
-  store.commit("rtc/setDeviceList", {
-    cameraList: [],
-    microphoneList: microphoneItems,
-  });
-
-  // if (!videoDeviceId.value) {
-  //   if (cameraItems[0]) {
-  //     store.commit("rtc/setVideoDeviceId", cameraItems[0].deviceId);
-  //   } else {
-  //     Dialog.toast({ content: `无法获取您的摄像头权限`, type: "error" });
-  //   }
-  // }
-
-  if (!audioDeviceId.value) {
-    if (microphoneItems[0]) {
-      store.commit("rtc/setAudioDeviceId", microphoneItems[0].deviceId);
-    } else {
-      Dialog.toast({ content: `无法获取您的麦克风权限`, type: "error" });
-    }
-  }
-};
-
-let quxian = { audio: true };
-
-console.log(quxian, "--------quxian-------");
-
-navigator.mediaDevices
-  .getUserMedia(quxian)
-  .then((stream) => {
-    stream.getTracks().forEach((track) => {
-      track.stop();
-    });
-    updateDevice();
-    emit("canUseDevice", true);
-  })
-  .catch((error) => {
-    console.log(error, "error");
-    Dialog.toast({ content: `请授权您的麦克风权限`, type: "error" });
-  });
-
-navigator.mediaDevices.ondevicechange = updateDevice;
-
-const handleDeviceChange = () => {
-  emit("switchDevice", {
-    videoId: "",
-    audioId: store.audioDeviceId,
-  });
-};
-</script>

+ 52 - 0
src/components/basic/dialog/Alert.vue

@@ -0,0 +1,52 @@
+<template>
+  <ui-dialog>
+    <template v-slot:header>
+      <span>{{ title }}</span>
+      <i v-if="showCloseIcon" class="iconfont icon-close" @click="close"></i>
+    </template>
+    <span v-html="content"></span>
+    <template v-slot:footer v-if="showFooter">
+      <ui-button type="submit" @click="close">{{ okText }}</ui-button>
+    </template>
+  </ui-dialog>
+</template>
+<script lang="ts">
+import { defineComponent } from "vue";
+import { isFunction, omit } from "./utils";
+export default defineComponent({
+  name: "ui-alert",
+  props: {
+    showCloseIcon: {
+      type: Boolean,
+      default: true,
+    },
+    showFooter: {
+      type: Boolean,
+      default: true,
+    },
+    title: {
+      type: String,
+      default: "提示",
+    },
+    okText: {
+      type: String,
+      default: "确定",
+    },
+    func: Function,
+    content: String,
+    destroy: Function,
+  },
+  setup: function (props, ctx) {
+    const close = () => {
+      if (isFunction(props.func) && props.func() === false) {
+        return;
+      }
+      isFunction(props.destroy) && props.destroy();
+    };
+    return {
+      ...omit(props, "destroy", "func"),
+      close,
+    };
+  },
+});
+</script>

+ 58 - 0
src/components/basic/dialog/Confirm.vue

@@ -0,0 +1,58 @@
+<template>
+    <ui-dialog>
+        <template v-slot:header>
+            <span>{{ title }}</span>
+            <i class="iconfont icon-close" @click="close('no')"></i>
+        </template>
+        <template v-if="$slots.content">
+            <slot name="content" />
+        </template>
+        <template v-else>
+            <div class="message" v-html="content"></div>
+        </template>
+        <template v-slot:footer>
+            <ui-button v-if="!single" type="cancel" @click="close('no')">{{ noText }}</ui-button>
+            <ui-button type="primary" @click="close('ok')">{{ okText }}</ui-button>
+        </template>
+    </ui-dialog>
+</template>
+<script lang="ts">
+import { defineComponent } from 'vue'
+import { isFunction, omit } from './utils'
+export default defineComponent({
+    name: 'ui-confirm',
+    props: {
+        title: {
+            type: String,
+            default: '提示',
+        },
+        okText: {
+            type: String,
+            default: '确定',
+        },
+        noText: {
+            type: String,
+            default: '取消',
+        },
+        single: {
+            type: Boolean,
+            default: false,
+        },
+        func: Function,
+        content: String,
+        destroy: Function,
+    },
+    setup: function (props, ctx) {
+        const close = result => {
+            if (isFunction(props.func) && props.func(result) === false) {
+                return
+            }
+            isFunction(props.destroy) && props.destroy()
+        }
+        return {
+            ...omit(props, 'destroy', 'func'),
+            close,
+        }
+    },
+})
+</script>

+ 17 - 0
src/components/basic/dialog/Dialog-content.vue

@@ -0,0 +1,17 @@
+<template>
+  <div class="ui-dialog__box">
+      <header v-if="$slots.header">
+          <slot name="header"></slot>
+      </header>
+      <section>
+          <slot></slot>
+      </section>
+      <footer v-if="$slots.footer">
+          <slot name="footer"></slot>
+      </footer>
+  </div>
+</template>
+
+<script>
+export default { name: 'ui-dialog-content' }
+</script>

+ 28 - 0
src/components/basic/dialog/Dialog.vue

@@ -0,0 +1,28 @@
+<template>
+  <teleport to="body">
+    <div class="ui-dialog" :style="{ zIndex: zIndex }" v-if="show">
+      <dialog-content>
+        <template v-for="(slot, name) in $slots" v-slot:[name]="raw">
+          <slot :name="name" v-bind="raw" />
+        </template>
+      </dialog-content>
+    </div>
+  </teleport>
+</template>
+<script lang="ts">
+// @ts-nocheck
+import { defineComponent, ref } from "vue";
+import { getZIndex } from "./utils";
+import DialogContent from "./Dialog-content.vue";
+export default defineComponent({
+  name: "ui-dialog",
+  setup: function (props, ctx) {
+    const show = ref(true);
+    return {
+      show,
+      zIndex: getZIndex(),
+    };
+  },
+  components: { DialogContent },
+});
+</script>

+ 74 - 0
src/components/basic/dialog/Toast.vue

@@ -0,0 +1,74 @@
+<template>
+  <teleport to="body">
+    <transition name="slide-down" mode="out-in" appear>
+      <div class="ui-toast" :style="{ zIndex: zIndex }" v-if="show">
+        <div class="ui-toast__box" :class="[type]">
+          <i v-if="type !== 'fixed' && type" class="icon"></i>
+          <div class="ui-toast__msg" v-html="content"></div>
+          <i
+            class="iconfont icon-close close"
+            @click="close"
+            v-if="showClose"
+          ></i>
+        </div>
+      </div>
+    </transition>
+  </teleport>
+</template>
+<script>
+import { defineComponent, onMounted, nextTick, ref } from "vue";
+import { getZIndex } from "./utils";
+export default defineComponent({
+  name: "ui-toast",
+  props: {
+    type: String,
+    delay: Number,
+    content: String,
+    destroy: Function,
+    close: Function,
+    showClose: Boolean,
+  },
+  setup: function (props, ctx) {
+    const show = ref(true);
+    const close = () => {
+      show.value = false;
+      nextTick(() => {
+        if (typeof props.close == "function") {
+          props.close();
+        }
+        typeof props.destroy === "function" && props.destroy();
+      });
+    };
+
+    if (props.type !== "fixed") {
+      setTimeout(() => close(), props.delay || 3000);
+    }
+    return {
+      show,
+      type: props.type,
+      close,
+      content: props.content,
+      zIndex: getZIndex(),
+    };
+  },
+});
+</script>
+<style lang="scss" scoped>
+.slide-down-enter-active,
+.slide-down-leave-active {
+  will-change: transform;
+  transition: all 0.35s ease-in-out;
+}
+.slide-down-enter-from {
+  opacity: 0;
+  transform: translate3d(0, -100%, 0);
+}
+.slide-down-enter {
+  opacity: 1;
+  transform: translate3d(-50%, 100%, 0);
+}
+.slide-down-leave-active {
+  opacity: 0;
+  transform: translate3d(0, -100%, 0);
+}
+</style>

+ 63 - 0
src/components/basic/dialog/Window.vue

@@ -0,0 +1,63 @@
+<template>
+    <ui-dialog>
+        <template v-slot:header>
+            <span style="font-size: 16px">{{ title }}</span>
+            <i class="iconfont icon-close" @click="onNo" v-if="showCloseIcon"></i>
+        </template>
+        <template v-if="$slots.content">
+            <slot name="content" />
+        </template>
+        <template v-else>
+            {{ content }}
+        </template>
+        <template v-slot:footer>
+            <ui-button type="cancel" @click="onNo" v-if="showCancelButton">{{ noText }}</ui-button>
+            <ui-button :class="{ disabled: !canSubmit }" type="submit primary" @click="onOk">{{ okText }}</ui-button>
+        </template>
+    </ui-dialog>
+</template>
+<script>
+import { defineComponent } from 'vue'
+export default defineComponent({
+    name: 'ui-window',
+    props: {
+        title: {
+            type: String,
+            default: '提示'
+        },
+        okText: {
+            type: String,
+            default: '确定'
+        },
+        noText: {
+            type: String,
+            default: '取消'
+        },
+        showCloseIcon: {
+            type: Boolean,
+            default: true
+        },
+        showCancelButton: {
+            type: Boolean,
+            default: true
+        },
+        canSubmit: {
+            type: Boolean,
+            default: true
+        }
+    },
+    emits: ['ok', 'no'],
+    setup: function(props, ctx) {
+        const onNo = () => {
+            ctx.emit('no')
+        }
+        const onOk = () => {
+            ctx.emit('ok')
+        }
+        return {
+            onNo,
+            onOk
+        }
+    }
+})
+</script>

+ 77 - 0
src/components/basic/dialog/dialog.scss

@@ -0,0 +1,77 @@
+.ui-dialog {
+    position: fixed;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+    color: #fff;
+    background-color: rgba($color: #000000, $alpha: 0.3);
+    backdrop-filter: blur(1px);
+}
+.ui-dialog__box {
+    position: relative;
+    display: inline-block;
+    min-width: 400px;
+    min-height: 100px;
+    background-color: rgba($color: #1a1a1a, $alpha: 0.8);
+    box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.7);
+    border-radius: 4px;
+    border: 1px solid #000000;
+    backdrop-filter: blur(4px);
+    &::after {
+        content: '';
+        position: absolute;
+        left: 1px;
+        right: 1px;
+        bottom: 1px;
+        top: 1px;
+        border: 1px solid rgba($color: #fff, $alpha: 0.1);
+        border-radius: 4px;
+        z-index: 0;
+        pointer-events: none;
+    }
+    header {
+        color: #999999;
+        padding: 0 20px;
+        height: 60px;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        border-bottom: solid 1px rgba($color: #ffffff, $alpha: 0.16);
+        font-weight: bold;
+        i {
+            cursor: pointer;
+        }
+    }
+    section {
+        padding: 40px 20px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        .message {
+            text-align: center;
+            line-height: 1.7;
+        }
+    }
+    footer {
+        padding: 20px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        border-top: solid 1px rgba($color: #ffffff, $alpha: 0.16);
+        button {
+            width: 105px;
+            margin-left: 10px;
+            margin-right: 10px;
+        }
+    }
+}
+[is-mobile]{
+  .ui-dialog__box{
+
+  } 
+} 

+ 117 - 0
src/components/basic/dialog/index.ts

@@ -0,0 +1,117 @@
+// @ts-nocheck 
+import Dialog from "./Dialog.vue";
+import Window from "./Window.vue";
+import Toast from "./Toast.vue";
+import Alert from "./Alert.vue";
+import Confirm from "./Confirm.vue";
+import DialogContent from "./Dialog-content.vue";
+import { createVNode, render } from 'vue'
+
+
+function mount(component, { props, children, element, app } = {}) {
+  let el = element;
+  let vNode = createVNode(component, props, children);
+
+  if (app && app._context) vNode.appContext = app._context;
+  if (el) render(vNode, el);
+  else if (typeof document !== "undefined") {
+    render(vNode, (el = document.createElement("div")));
+  }
+
+  const destroy = () => {
+    if (el) render(null, el);
+    el = null;
+    vNode = null;
+  };
+
+  return { vNode, destroy, el };
+}
+// const Dialog =window
+
+Dialog.use = function use(app) {
+
+  Dialog.toast = function (options) {
+    if (typeof options == "string") {
+      options = {
+        content: options,
+      };
+    }
+
+    const { destroy, vNode, el } = mount(Toast, {
+      app,
+      props: {
+        ...options,
+        destroy,
+      },
+    });
+
+    if (!Dialog.toast._destroys) {
+      Dialog.toast._destroys = [];
+    }
+    Dialog.toast._destroys.push(destroy);
+
+    return {
+      hide: function () {
+        let destroy = null;
+        while ((destroy = Dialog.toast._destroys.shift()) && destroy) {
+          destroy();
+        }
+      }.bind(this),
+    };
+  };
+  Dialog.toast.hide = function () {
+    if (Dialog.toast._destroys && Dialog.toast._destroys.length) {
+      let destroy = Dialog.toast._destroys.pop();
+      destroy && destroy();
+    }
+  };
+  Dialog.alert = function (options) {
+    if (typeof options == "string") {
+      options = {
+        content: options,
+      };
+    }
+
+    const { destroy } = mount(Alert, {
+      app,
+      props: { ...options, destroy: () => destroy() },
+    });
+
+    this.alert.hide = function () {
+      destroy();
+    };
+
+    return this.alert;
+  };
+
+  Dialog.confirm = function (options) {
+    if (typeof options == "string") {
+      options = {
+        content: options,
+      };
+    }
+
+    let promise;
+    if (!options.func) {
+      promise = new Promise((resolve) => {
+        options.func = (result) => resolve(result === "ok");
+      });
+    }
+
+    const { destroy } = mount(Confirm, {
+      app,
+      props: { ...options, destroy: () => destroy() },
+    });
+
+    this.confirm.hide = function () {
+      destroy();
+    };
+
+    return promise || this.confirm;
+  };
+};
+console.log('Dialog', Dialog)
+
+export { Window, Toast, Alert, Confirm, DialogContent };
+
+export default Dialog;

+ 57 - 0
src/components/basic/dialog/utils.ts

@@ -0,0 +1,57 @@
+// @ts-nocheck 
+let zindex = 10000
+import { createVNode, render } from 'vue'
+export const getZIndex = (): number => ++zindex
+
+export function mount(component, { props, children, element, app } = {}) {
+    let el = element
+    let vNode = createVNode(component, props, children)
+
+    if (app && app._context) vNode.appContext = app._context
+    if (el) render(vNode, el)
+    else if (typeof document !== 'undefined') {
+        render(vNode, (el = document.createElement('div')))
+    }
+
+    const destroy = () => {
+        if (el) render(null, el)
+        el = null
+        vNode = null
+    }
+
+    return { vNode, destroy, el }
+}
+
+export function setup(...Components) {
+    Components.forEach(Component => {
+        Component.install = function (app) {
+            Component.use && Component.use(app)
+            app.component(Component.name, Component)
+        }
+    })
+
+    return Components
+}
+export const objectToString = Object.prototype.toString
+export const toTypeString = value => objectToString.call(value)
+// 获取制定对象的类型比如toRawType(1) Number
+export const toRawType = value => toTypeString(value).slice(8, -1)
+
+/**
+ * 判断是否函数
+ * @param {any} target 参数对象
+ */
+export const isFunction = target => toRawType(target) === 'Function'
+
+/**
+* 获取忽略指定属性的对象
+* @param {Object} obj 源对象
+* @param  {...any} props 忽略属性
+*/
+export function omit(obj, ...props) {
+    const result = { ...obj }
+    props.forEach(function (prop) {
+        delete result[prop]
+    })
+    return result
+}

+ 760 - 0
src/components/basic/title.vue

@@ -0,0 +1,760 @@
+<template>
+  <div class="header" @touchmove.prevent>
+    <div class="left" :class="{ show: true }">
+      <div v-show="!isPanoramaMode" class="back-pano" @click="onChangeMode">
+        <i
+          class="iconfont ui-kankan-icon icon tip-h-center tip-v-bottom icon-show_back iconzm-show_back"
+        />
+      </div>
+      <div v-show="isPanoramaMode" class="back" @click="onBack">
+        <i
+          class="iconfont ui-kankan-icon icon tip-h-center tip-v-bottom icon-_back iconzm-_back"
+        />
+      </div>
+    </div>
+    <div
+      class="title"
+      :class="{
+        up: player.showDescription,
+        dark: !isPanoramaMode,
+        empty: !description,
+      }"
+      @click="onShowDescription"
+      v-show="player.showWidgets"
+    >
+      <div>
+        <span>
+          {{ metadata.title }}
+          <i class="iconfont icon-pull-down"></i>
+        </span>
+      </div>
+    </div>
+    <div class="right" v-show="player.showWidgets" @click="onShowMore">
+      <i class="iconfont icon-show_more_share"></i>
+    </div>
+    <transition
+      appear
+      name="custom-classes-transition"
+      enter-active-class="animated fadeInUp short faster"
+      leave-active-class="animated fadeOutDown short faster"
+    >
+      <div
+        class="content"
+        :class="{ dark: !isPanoramaMode }"
+        v-if="player.showDescription"
+        @click="onShowDescription"
+      >
+        <div>
+          <div v-html="description"></div>
+        </div>
+      </div>
+    </transition>
+    <teleport to="body">
+      <div class="url-share" v-show="showCopy" @click="showCopy = false">
+        <img :src="shareImage" alt="" />
+      </div>
+    </teleport>
+  </div>
+</template>
+<script setup lang="ts">
+// import ClipboardJS from "clipboard";
+import browser from "/@/utils/browser";
+// import { useStore } from "vuex";
+// import { Dialog } from "@/global_components/";
+// import { useMusicPlayer } from "@/utils/sound";
+import { onMounted, watch, computed, ref, nextTick } from "vue";
+import shareImage from "/@/assets/share.png";
+import { useAppStore } from "/@/store/modules/app";
+import { useSceneStore } from "/@/store/modules/scene";
+// import { useApp, getApp } from "@/app";
+const appStore = useAppStore();
+const sceneStore = useSceneStore();
+let share_url = browser.getURLParam("share_url");
+if (share_url) {
+  share_url = decodeURIComponent(share_url);
+} else {
+  share_url = `${location.origin}${location.pathname}?m=${browser.getURLParam(
+    "m"
+  )}`;
+}
+// const musicPlayer = useMusicPlayer()
+// const store = useStore();
+const metadata = computed(() => sceneStore.metadata);
+const controls = computed(() => {
+  return sceneStore.metadata.controls;
+});
+const showMusicPlaying = ref(false);
+const showMusic = computed(() => sceneStore.metadata.music);
+const player = computed(() => appStore.player);
+
+const copy$ = ref(null);
+const copyLink = ref(share_url);
+const showCopy = ref(false);
+const showShare = ref(false);
+const isMusicPlaying = ref(false);
+
+const mode = computed(() => appStore.mode);
+const isPanoramaMode = computed(() => appStore.mode === "panorama");
+const description = computed(() => metadata.value.description);
+watch(
+  () => showCopy.value,
+  (_) => {
+    appStore.setPlayerOptions({
+      showMap: !showCopy.value,
+      showToolbar: !showCopy.value,
+    });
+  },
+  {
+    deep: true,
+  }
+);
+watch(
+  () => showShare.value,
+  (_) => {
+    appStore.setPlayerOptions({
+      showMap: !showShare.value,
+      showToolbar: !showShare.value,
+    });
+  },
+  {
+    deep: true,
+  }
+);
+
+onMounted(() => {
+  // new ClipboardJS(copy$.value.$el).on('success', function (e) {
+  //     e.clearSelection()
+  //     showCopy.value = false
+  //     Dialog.toast({ content: '场景链接复制成功', type: 'success' })
+  // })
+
+  nextTick(() => {
+    document.querySelector(".player")?.addEventListener("touchstart", () => {
+      if (player.value.showMore) {
+        appStore.setPlayerOptions({
+          showMore: false,
+          showMap: true,
+          showToolbar: true,
+        });
+        // store.commit("SetPlayerOptions", {
+        //   showMore: false,
+        //   showMap: true,
+        //   showToolbar: true,
+        // });
+      } else if (player.value.showDescription) {
+        appStore.setPlayerOptions({
+          showDescription: false,
+          showMap: true,
+          showToolbar: true,
+        });
+      }
+    });
+
+    (window as any).Back = () => {
+      onBack();
+    };
+    // musicPlayer.on('play', () => (showMusicPlaying.value = true))
+    // musicPlayer.on('pause', () => (showMusicPlaying.value = false))
+  });
+});
+const onBack = () => {
+  player.value.showVR && appStore.showVR();
+};
+const onShowMore = () => {
+  showCopy.value = true;
+  // let show = !player.value.showMore
+  // store.commit('SetPlayerOptions', {
+  //     showMore: show,
+  //     showMap: show == false,
+  //     showToolbar: show == false,
+  //     showDescription: false,
+  // })
+};
+const onShowDescription = () => {
+  let show = !player.value.showDescription;
+
+  appStore.setPlayerOptions({
+    showMore: false,
+    showMap: show == false,
+    showToolbar: show == false,
+    showDescription: show,
+  });
+};
+const onMusicClick = () => {
+  showMusicPlaying.value ? musicPlayer.pause() : musicPlayer.play();
+};
+const onMenuClick = (name) => {
+  appStore.setPlayerOptions({
+    showMore: false,
+    showDescription: false,
+    showMap: true,
+    showToolbar: true,
+  });
+
+  nextTick(() => {
+    if (name == "music") {
+      onMusicClick();
+    } else if (name == "share") {
+      showCopy.value = true;
+    } else if (name === "measure") {
+      this.$bus.emit("measure/Handle", "start");
+    } else if (name == "vr") {
+      store.commit("showVR");
+    }
+  });
+};
+const onShare = (name) => {
+  if (name == "copy") {
+    showShare.value = false;
+    this.$nextTick(() => {
+      showCopy.value = true;
+    });
+  }
+};
+const onChangeMode = () => {
+  //   store.commit("setMode", "panorama");
+  appStore.setMode("panorama");
+};
+</script>
+
+<style lang="scss" scoped>
+.disable {
+  opacity: 1;
+}
+.header {
+  position: absolute;
+  top: 0.3rem;
+  left: 0;
+  height: 40px;
+  width: 100%;
+  z-index: 101;
+  color: #fff;
+  display: flex;
+  align-items: center;
+  text-shadow: 0px 0px 4px rgba(0, 0, 0, 0.4);
+  &.app {
+    top: 1rem;
+  }
+  .left {
+    width: 1rem;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    &.show {
+      visibility: visible;
+      pointer-events: auto;
+      .back {
+        visibility: visible;
+      }
+    }
+    .back {
+      width: 0.78rem;
+      height: 0.78rem;
+      border-radius: 50%;
+      background-color: rgba(0, 0, 0, 0.3);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      visibility: hidden;
+      position: relative;
+      i {
+        font-size: 0.3rem;
+        width: auto;
+        position: static;
+      }
+    }
+    .back-pano {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      // padding-left: 15px;
+      position: relative;
+      i {
+        font-size: 0.6rem;
+        position: static;
+      }
+    }
+  }
+  .right {
+    position: relative;
+    width: 1.28rem;
+    height: 100%;
+    padding-right: 30px;
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+    z-index: 11;
+    > i {
+      font-size: 0.45rem;
+      color: rgba(255, 255, 255, 0.88);
+    }
+    > div {
+      position: absolute;
+      display: flex;
+      flex-direction: column;
+      right: 10px;
+      top: 1.3rem;
+      padding: 4px 0.42105rem;
+      background-color: rgba(0, 0, 0, 0.5);
+      border-radius: 5px;
+      &::after {
+        content: "";
+        position: absolute;
+        top: -5px;
+        right: 0.3rem;
+        width: 0;
+        height: 0;
+        border-width: 0 5px 5px;
+        border-style: solid;
+        border-color: transparent transparent rgba(0, 0, 0, 0.5);
+      }
+      &.flex {
+        li {
+          span {
+            flex: 1;
+            width: auto;
+            // min-width: 2.8rem;
+          }
+        }
+      }
+      li {
+        position: relative;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        // margin-top: 0.3rem;
+        font-size: 0;
+        // height: .5333rem;
+        padding: 0.1333rem 0 0.2667rem;
+        i {
+          // position: absolute;
+          // left: 0;
+          // top: 0;
+          // font-size: 0.45rem;
+          font-size: 0.3733rem;
+          margin-right: 0.4rem;
+        }
+        b {
+          position: absolute;
+          left: 0.32rem;
+          top: 0.33rem;
+          width: 7px;
+          height: 7px;
+          border-radius: 50%;
+          background-color: #00c2c4;
+          i {
+            color: #fff;
+            font-size: 12px;
+            transform: scale(0.3, 0.3);
+            top: -4px;
+            left: -3px;
+            position: absolute;
+          }
+        }
+        span {
+          width: 1.89474rem;
+          white-space: nowrap;
+          text-align: left;
+          // padding-bottom: 0.3rem;
+          // margin-left: 0.75rem;
+          font-size: 0.3733rem;
+          // text-indent: 0.2rem;
+          // padding-right: 0.2rem;
+        }
+      }
+
+      .home {
+        border-top: solid 1px rgba(255, 255, 255, 0.4);
+        a {
+          width: 1.89474rem;
+          margin-top: 0.3rem;
+          margin-bottom: 0.1rem;
+          margin-left: auto;
+          margin-right: auto;
+          display: block;
+          text-decoration: none;
+          overflow: hidden;
+
+          img {
+            width: 100%;
+            outline: none;
+            border: none;
+          }
+        }
+      }
+    }
+  }
+  .title {
+    display: flex;
+    flex: 1;
+    width: 100%;
+    height: 100%;
+    font-size: 14px;
+    letter-spacing: 1px;
+    align-items: center;
+    justify-content: center;
+    > div {
+      display: flex;
+      transition: background 0.3s ease, min-width 0.3s ease,
+        border-radius 0.3s ease;
+      align-items: center;
+      justify-content: center;
+      padding-right: 12px;
+      padding-left: 12px;
+      white-space: nowrap;
+      position: absolute;
+      height: 100%;
+      min-width: 100%;
+      overflow: visible;
+      pointer-events: none;
+      background: linear-gradient(
+        90deg,
+        transparent,
+        rgba(0, 0, 0, 0.2) 29%,
+        rgba(0, 0, 0, 0.2) 69%,
+        transparent
+      );
+    }
+    span {
+      display: inline-block;
+      position: relative;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      max-width: 7rem;
+      display: inline-block;
+      padding-right: 20px;
+      i {
+        position: absolute;
+        right: 0;
+        color: #fff;
+        font-size: 10px;
+        top: 50%;
+        -webkit-transform: translateY(-50%);
+        transform: translateY(-50%);
+        vertical-align: bottom;
+        line-height: normal;
+      }
+    }
+
+    &.up {
+      i {
+        transform: translateY(-50%) rotate(180deg);
+      }
+      > div {
+        min-width: 0;
+        //position: static;
+        flex-shrink: 0;
+        background-color: rgba(0, 0, 0, 0.5);
+        border-radius: 1.15789rem;
+      }
+      &.dark {
+        > div {
+          background-color: rgba(0, 0, 0, 0.8);
+        }
+      }
+    }
+
+    &.empty {
+      i {
+        display: none;
+      }
+    }
+  }
+  .content {
+    position: absolute;
+    top: 1.2rem;
+    left: 0.92105rem;
+    right: 0.92105rem;
+    padding: 10px;
+    background: rgba(0, 0, 0, 0.5);
+    border-radius: 5px;
+    font-size: 0.36842rem;
+    text-align: center;
+    &.dark {
+      background: rgba(0, 0, 0, 0.8);
+    }
+    > div {
+      display: inline-block;
+      text-align: left;
+      letter-spacing: 1px;
+      word-break: break-all;
+      white-space: normal;
+      line-height: 1.5;
+      // h4 {
+      //     margin: 0;
+      //     padding: 0;
+      //     margin-bottom: 0.28rem;
+      //     font-size: 0.43rem;
+      //     padding-left: 0.18789rem;
+      //     width: 100%;
+      //     position: relative;
+      //     &::before {
+      //         content: "";
+      //         position: absolute;
+      //         left: 0;
+      //         top: 50%;
+      //         height: 80%;
+      //         transform: translateY(-50%);
+      //         width: 2px;
+      //         background-color: var(--editor-main-color);
+      //     }
+      // }
+
+      :deep(p) {
+        word-break: break-word;
+      }
+
+      :deep(a) {
+        color: var(--editor-main-color);
+      }
+    }
+    // &::after {
+    //     content: "";
+    //     position: absolute;
+    //     top: -6px;
+    //     left: 50%;
+    //     margin-left: -3px;
+    //     width: 0;
+    //     height: 0;
+    //     border-width: 0 7px 6px;
+    //     border-style: solid;
+    //     border-color: transparent transparent rgba(0, 0, 0, 0.5);
+    // }
+  }
+
+  .app-share {
+    position: fixed;
+    left: 0;
+    bottom: 0;
+    width: 100%;
+    background-color: #fff;
+    color: var(--editor-main-color);
+    text-shadow: none;
+    border-radius: 0.146667rem 0.146667rem 0px 0px;
+    i {
+      // font-size: 2rem;
+      font-size: 1rem;
+      margin: 0.666667rem 0 0.106667rem;
+      display: inline-block;
+    }
+    ul {
+      // display: flex;
+      // justify-content: space-around;
+      overflow: hidden;
+      div {
+        text-align: center;
+        // margin-top: -0.3rem;
+        font-size: 0.34rem;
+      }
+      li {
+        width: 33.3%;
+        float: left;
+        // padding-bottom: 0.5rem;
+        overflow: hidden;
+        text-align: center;
+        // i{
+        //   font-size: 2rem;
+        // }
+      }
+      &.flex {
+        display: flex;
+        li {
+          float: none;
+        }
+      }
+    }
+    > div {
+      height: 1.293333rem;
+      font-size: 16px;
+      text-align: center;
+      // padding: 0.5rem;
+      border-top: solid 1px #eeeeee;
+      margin-top: 0.466667rem;
+      line-height: 1.293333rem;
+    }
+  }
+}
+.url-share {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 99999;
+  background-color: rgba(0, 0, 0, 0.8);
+  > img {
+    position: absolute;
+    width: 61.6%;
+    top: 12px;
+    right: 6.4%;
+  }
+
+  > div {
+    position: absolute;
+    left: 0.6rem;
+    right: 0.6rem;
+    top: 50vh;
+    transform: translateY(-50%);
+    border-radius: 4px;
+    background-color: rgba(0, 0, 0, 0.5);
+    padding: 0.31579rem;
+    font-size: 0.36842rem;
+    .tips {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      h4 {
+        margin: 0;
+        font-size: 0.42105rem;
+      }
+      i {
+        font-size: 0.6rem;
+      }
+    }
+    .url {
+      display: -webkit-box;
+      color: #fff;
+      text-align: center;
+      padding: 0.26316rem;
+      width: 100%;
+      height: 1.8rem;
+      margin: 0.7rem 0rem;
+      border-radius: 0.07rem;
+      line-height: 1.99;
+      font-size: 0.36842rem;
+      background-color: rgba(0, 0, 0, 0.35);
+      overflow: hidden;
+      text-overflow: ellipsis;
+      word-break: break-all;
+      word-wrap: break-word;
+      -webkit-line-clamp: 2; //在第幾行加省略號
+      -webkit-box-orient: vertical;
+    }
+    .btns {
+      display: flex;
+      justify-content: space-between;
+      button {
+        font-size: 0.36842rem;
+        width: 47%;
+        height: 1.05263rem;
+        border-radius: 1.05263rem;
+        &.submit {
+          background: #00c2c4;
+        }
+      }
+    }
+  }
+}
+@media (orientation: landscape) {
+  .header {
+    top: 0.2rem;
+    height: 0.7rem;
+    &.app {
+      top: 0.2rem;
+    }
+    .left {
+      .back-pano {
+        i {
+          font-size: 0.5rem;
+        }
+      }
+    }
+    .right {
+      > i {
+        font-size: 0.5rem;
+      }
+      > div {
+        right: 10px;
+        top: 0.75rem;
+        padding: 4px 0.2rem;
+        &::after {
+          right: 0.23rem;
+        }
+        li {
+          margin-top: 0.15rem;
+          i {
+            font-size: 0.3rem;
+          }
+          b {
+            left: 0.2rem;
+            top: 0.15rem;
+          }
+          span {
+            padding-bottom: 0.15rem;
+            margin-left: 0.5rem;
+            font-size: 0.25rem;
+          }
+        }
+
+        .home {
+          a {
+            margin-top: 0.15rem;
+            margin-bottom: 0rem;
+            width: 1.4rem;
+          }
+        }
+      }
+    }
+    .title {
+      font-size: 0.3rem;
+    }
+    .content {
+      top: 1rem;
+      left: 2rem;
+      right: 2rem;
+      padding: 10px;
+
+      > div {
+        max-height: 3rem;
+        font-size: 0.25rem;
+        line-height: 1.5;
+      }
+    }
+  }
+}
+
+// ipad 橫屏
+@media only screen and (min-device-width: 768px) and (orientation: landscape) {
+  .header {
+    height: 0.8rem;
+    .title {
+      i {
+        margin-bottom: 0;
+      }
+    }
+  }
+}
+// @media only screen and (min-device-width: 768px) and (orientation: portrait) {
+
+// }
+</style>
+<style lang="scss">
+.animated.short {
+  &.faster {
+    animation-duration: 0.3s;
+  }
+  @keyframes fadeInUp {
+    0% {
+      opacity: 0;
+      transform: translate3d(0, 1rem, 0);
+    }
+    to {
+      opacity: 1;
+      transform: translateZ(0);
+    }
+  }
+  @keyframes fadeOutDown {
+    0% {
+      opacity: 1;
+    }
+    to {
+      opacity: 0;
+      transform: translate3d(0, 1rem, 0);
+    }
+  }
+}
+</style>

+ 20 - 1
src/components/chatRoom/chat.vue

@@ -20,7 +20,7 @@
 <script setup lang="ts">
 // import { propTypes } from "/@/utils/propTypes";
 
-import { computed } from "vue";
+import { computed, watch } from "vue";
 import { ChatContentType } from "/@/store/modules/rtc";
 import { propTypes } from "/@/utils/propTypes";
 const props = defineProps({
@@ -34,6 +34,25 @@ const props = defineProps({
 const isMe = computed(
   () => (currentUser: string) => props.currentUser === currentUser
 );
+
+const chatAutoScroll = () => {
+  let el = document.getElementById("chat");
+  let client_h = document.getElementById("chat")?.clientHeight;
+  let all = document.getElementById("contents")?.clientHeight;
+  // console.log("chatAutoScroll", client_h, all);
+  el?.scrollTo(0, (client_h || 0) + (all || 0));
+};
+
+watch(
+  () => props.chatList,
+  (_) => {
+    chatAutoScroll();
+  },
+  {
+    deep: true,
+    immediate: true,
+  }
+);
 </script>
 
 <style scoped lang="scss">

+ 2 - 6
src/components/chatRoom/controls/actions.ts

@@ -38,9 +38,6 @@ export function handleActions({ data, type, members }: ActionDataType) {
 
         case 'ask-currentscene':
             break;
-
-        case 'changeScene':
-            break;
         default:
             break;
     }
@@ -56,8 +53,7 @@ function handleDanMumSg(data: ChatContentType) {
     rtcStore.addToChatList(data)
 }
 // 
-function handleUserJoin(members: UserInfoType[]) {
+function handleUserJoin(members?: UserInfoType[]) {
     const rtcStore = useRtcStore();
-    debugger
-    rtcStore.setMemberList(members)
+    members && rtcStore.setMemberList(members)
 }

+ 6 - 1
src/components/chatRoom/index.vue

@@ -86,6 +86,8 @@ import { computed, nextTick, onMounted, ref, unref } from "vue";
 import { getApp, useApp } from "/@/hooks/userApp";
 import { initSocketEvent } from "./roomControl";
 import { createSocket } from "/@/hooks/userSocket";
+import { useRtcSdk } from "/@/hooks/useTRTC";
+
 import browser from "/@/utils/browser";
 import { useRtcStore } from "/@/store/modules/rtc";
 import type { SocketParams, RoleType } from "/@/store/modules/rtc";
@@ -130,7 +132,10 @@ rtcStore.setSocketParams(initParams);
 
 const socket = createSocket();
 initSocketEvent(socket);
-
+const { createRTCSocket } = useRtcSdk();
+(async () => {
+  await createRTCSocket();
+})();
 // onMounted(() => {
 //   const app = getApp();
 // });

+ 111 - 0
src/components/chatRoom/sceneList.vue

@@ -0,0 +1,111 @@
+<template>
+  <div class="scene-list">
+    <div class="header">
+      <p>场景列表({{ sceneList.length }})</p>
+      <span class="icon" @click="emit('close')">
+        <Icon type="cross@2x" />
+      </span>
+    </div>
+    <div class="content">
+      <div class="sign" v-for="scene in sceneList" :key="scene.id" @click="emit('changeScene', scene)" :class="{active: currentScene === scene}">
+        <div class="info">
+          <img :src="scene.thumb">
+          <p>{{ scene.sceneName }}</p>
+        </div>
+        <span class="icon">
+          <Icon type="arrow@2x" v-if="currentScene !== scene" />
+        </span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { sceneList } from '@/store/room'
+import Icon from '@/components/icon/index.vue'
+import { currentScene } from '@/store/room'
+
+const emit = defineEmits(['close', 'changeScene']);
+</script>
+
+<style scoped lang="scss">
+.scene-list {
+  max-height: 80%;
+  background: rgba(0,0,0,0.8);
+  border-radius: 10px 10px 0px 0px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  display: flex;
+  flex-direction: column;
+  z-index: 99999;
+  position: absolute;
+}
+
+.header {
+  flex: none;
+  text-align: center;
+  height: 46px;
+  line-height: 46px;
+  border-bottom: 1px solid rgba(255,255,255,.1);
+
+  p {
+    font-size: 16px;
+    font-weight: 500;
+    color: #FFFFFF;
+  }
+
+  .icon {
+    position: absolute;
+    right: 0;
+    top: 0;
+    font-size: 14px;
+    padding: 0 20px;
+  }
+}
+
+.content {
+  flex: 1;
+  overflow-y: auto;
+  padding: 11px;
+
+  .sign {
+    height: 60px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 18px;
+
+    .info {
+      flex: 1;
+      display: flex;
+      align-items: center;
+
+      img {
+        flex: none;
+        width: 60px;
+        height: 60px;
+        border-radius: 4px;
+      }
+
+      p {
+        font-size: 14px;
+        font-weight: 400;
+        color: #FFFFFF;
+        line-height: 20px;
+        margin-left: 10px;
+      }
+    }
+
+    &.active .info p {
+      color: #ed6155;
+    }
+
+    .icon {
+      flex: none;
+      font-size: 10px;
+      margin-left: 20px;
+    }
+  }
+}
+</style>

+ 24 - 0
src/components/registerComponent.ts

@@ -0,0 +1,24 @@
+
+import Dialog from '/@/components/basic/dialog'
+import type { App } from 'vue'
+
+function setup(...Components) {
+    Components.forEach(Component => {
+        Component.install = function (app: App) {
+            Component.use && Component.use(app)
+            app.component(Component.name, Component)
+        }
+    })
+    return Components
+}
+
+const registerComponent = setup(Dialog)
+console.log('registerComponent', registerComponent)
+export default {
+    install: (app: App) => {
+        registerComponent.forEach(component => {
+            component.install(app)
+        })
+    }
+}
+

+ 58 - 0
src/hooks/useTRTC.ts

@@ -0,0 +1,58 @@
+
+import consola from "consola";
+import TRTC from "trtc-js-sdk";
+import { useRtcStore } from "/@/store/modules/rtc";
+import Dialog from '/@/components/basic/dialog'
+export const updateDevice = async () => {
+    console.log("updateDevice");
+    const microphoneItems = await TRTC.getMicrophones();
+    microphoneItems.forEach((item) => {
+        console.log('item', item)
+    });
+}
+
+
+const checkoutIsExistAudioInput = async () => {
+    const result = await TRTC.checkSystemRequirements()
+    console.log('result', result)
+
+}
+
+// interface UseRtcSdkType {
+//     createRTCSocket: () => Promise<void>
+// }
+
+async function createRTCSocket(): Promise<void> {
+    try {
+        console.log('Dialog', Dialog)
+        Dialog.alert("该场景正在计算中,请稍后再试");
+        const rtcStore = useRtcStore()
+        await checkoutIsExistAudioInput();
+        const microphoneItems = await TRTC.getMicrophones();
+        // debugger
+        if (microphoneItems?.length) {
+            const localStream = TRTC.createStream({
+                userId: rtcStore.userId,
+                audio: true,
+                video: false,
+                // microphoneId: store.getters["rtc/audioDeviceId"],
+                microphoneId: rtcStore.audioDeviceId
+            });
+            console.log('localStream', localStream)
+
+        }
+    } catch (error) {
+        consola.error({
+            tag: 'createRTCSocket',
+            message: error
+        })
+    }
+}
+
+export function useRtcSdk() {
+    return {
+        createRTCSocket
+    }
+}
+
+

+ 3 - 1
src/main.ts

@@ -6,6 +6,8 @@ import 'virtual:windi-utilities.css';
 import "/@/assets/theme.editor.scss";
 import { setupStore } from '/@/store';
 import App from './App.vue'
+import registerComponent from '/@/components/registerComponent'
+
 // import Components from "./global_components";
 
 async function bootstrap() {
@@ -13,7 +15,7 @@ async function bootstrap() {
 
     // Configure store
     setupStore(app);
-    // app.use(Components)
+    app.use(registerComponent)
 
     app.mount('#app');
 }

+ 15 - 0
src/store/modules/app.ts

@@ -1,4 +1,5 @@
 import { defineStore } from 'pinia';
+import { useTourStore } from './tour';
 import { getApp, useApp } from '/@/hooks/userApp';
 
 
@@ -99,6 +100,20 @@ export const useAppStore = defineStore({
         setPanoId(id: string) {
             this.panoId = id
         },
+        setPlayerOptions(payload) {
+            for (let key in payload) {
+                this.player[key] = payload[key]
+                if (key == 'showSplit') {
+                    this.player.showWidgets = !payload[key]
+                }
+            }
+            if (!this.player.showToolbar || this.player.showVR) {
+                const tourStore = useTourStore()
+                // debugger
+                // this.commit('tour/setData', { showTours: false })
+                // tourStore.setData({ showTours: false })
+            }
+        }
     }
 
 })

+ 3 - 0
src/store/modules/tour.ts

@@ -37,6 +37,9 @@ export const useTourStore = defineStore({
         },
         async delFile(): Promise<void> {
 
+        },
+        setData(): void {
+
         }
     }
 

+ 32 - 0
src/utils/install.ts

@@ -0,0 +1,32 @@
+import type { App } from 'vue'
+import type { SFCWithInstall, SFCInstallWithContext } from './typescript'
+
+// 组件
+export const withInstall = <T, E extends Record<string, any>>(
+    main: T,
+    extra?: E
+) => {
+    // 定义 install 属性
+    ; (main as SFCWithInstall<T>).install = (app): void => {
+        for (const comp of [main, ...Object.values(extra ?? {})]) {
+            app.component(comp.name, comp)
+        }
+    }
+
+    if (extra) {
+        for (const [key, comp] of Object.entries(extra)) {
+            ; (main as any)[key] = comp
+        }
+    }
+    return main as SFCWithInstall<T> & E
+}
+
+// 函数
+export const withInstallFunction = <T>(fn: T, name: string) => {
+    ; (fn as SFCWithInstall<T>).install = (app: App) => {
+        ; (fn as SFCInstallWithContext<T>)._context = app._context
+        app.config.globalProperties[name] = fn
+    }
+
+    return fn as SFCInstallWithContext<T>
+}

+ 0 - 0
src/utils/rtc_socket.ts


+ 7 - 0
src/utils/typescript.ts

@@ -0,0 +1,7 @@
+import type { AppContext, Plugin } from 'vue'
+
+export type SFCWithInstall<T> = T & Plugin
+
+export type SFCInstallWithContext<T> = SFCWithInstall<T> & {
+    _context: AppContext | null
+}

+ 4 - 1
vite.config.ts

@@ -2,8 +2,9 @@ import { defineConfig, loadEnv } from 'vite'
 import vue from '@vitejs/plugin-vue'
 import VitePluginHtmlEnv from 'vite-plugin-html-env'
 import { resolve } from 'path'
-import WindiCSS from "vite-plugin-windicss";  
+import WindiCSS from "vite-plugin-windicss";
 import consola from 'consola'
+import mkcert from "vite-plugin-mkcert";
 
 function pathResolve(dir: string) {
   return resolve(process.cwd(), '.', dir);
@@ -12,6 +13,7 @@ function pathResolve(dir: string) {
 // https://vitejs.dev/config/
 export default ({ mode }) => defineConfig({
   plugins: [
+    mkcert(),
     vue(),
     WindiCSS(),
     VitePluginHtmlEnv({
@@ -29,6 +31,7 @@ export default ({ mode }) => defineConfig({
 
   },
   server: {
+    https: Number(loadEnv(mode, process.cwd()).VITE_USE_HTTPS) == 1,
     host: true,
     headers: {
       "Cache-Control": "no-store",