Browse Source

Merge branch 'dev' into v1.0.0 更新自动导览

tremble 2 years ago
parent
commit
500decb75b
37 changed files with 2721 additions and 1662 deletions
  1. 2 2
      .env
  2. 3 2
      .env.development
  3. 1 1
      .env.prod
  4. 442 6
      package-lock.json
  5. 2 0
      package.json
  6. 35 36
      public/index.html
  7. 206 231
      src/app.vue
  8. BIN
      src/assets/images/guide/novice_guide_button@3x.png
  9. BIN
      src/assets/images/guide/novice_guide_button_empty@2x.png
  10. BIN
      src/assets/images/guide/novice_guide_button_empty@3x.png
  11. BIN
      src/assets/images/guide/novice_guide_button_zh@2x.png
  12. BIN
      src/assets/images/guide/novice_guide_text@3x.png
  13. BIN
      src/assets/images/guide/novice_guide_text_en@2x.png
  14. BIN
      src/assets/images/icon/en.png
  15. BIN
      src/assets/images/icon/zh_CN.png
  16. BIN
      src/assets/images/icon/zh_HK.png
  17. 87 95
      src/components/Controls/Control.Mobile.vue
  18. 191 156
      src/components/Controls/Panel/Guide.vue
  19. 74 75
      src/components/Controls/Panel/Main.vue
  20. 819 678
      src/components/Information/View.Mobile.vue
  21. 30 28
      src/components/RTC/PageRtcLive.vue
  22. 13 7
      src/components/RTC/dialog/createdRoom.vue
  23. 9 6
      src/components/RTC/dialog/index.vue
  24. 6 4
      src/components/RTC/dialog/share.vue
  25. 11 7
      src/components/Tags/goods-list.vue
  26. 5 1
      src/components/Tags/waterfall.vue
  27. 3 2
      src/components/openVideo/index.vue
  28. 24 48
      src/components/shared/Guide.vue
  29. 73 0
      src/i18n/index.js
  30. 102 0
      src/locales/en.json
  31. 86 0
      src/locales/zh.json
  32. 102 0
      src/locales/zh_CN.json
  33. 102 0
      src/locales/zh_HK.json
  34. 14 5
      src/main.js
  35. 1 1
      src/store/index.js
  36. 276 269
      src/store/modules/tour.js
  37. 2 2
      src/views/Panel.vue

+ 2 - 2
.env

@@ -1,10 +1,10 @@
 
 # 场景资源地址
-VUE_APP_RESOURCE_URL=https://glp-vr.cdfmembers.com/
+VUE_APP_RESOURCE_URL=https://eurs3.4dkankan.com/
 # 静态资源地址
 VUE_APP_CDN_URL=https://4dkk.4dage.com/v4/www/
 # sdk文件地址
-VUE_APP_SDK_DIR=https://eurs3.4dkankan.com/v4/cdfg/sdk
+VUE_APP_SDK_DIR=https://eurs3.4dkankan.com/v4/cdfg/sdk-new
 
 # socket地址
 VUE_APP_SOCKET_URL=https://vr-admin.cdfmembers.com/

+ 3 - 2
.env.development

@@ -4,7 +4,7 @@ VUE_APP_RESOURCE_URL=https://eurs3.4dkankan.com/
 # 静态资源地址
 VUE_APP_CDN_URL=https://4dkk.4dage.com/v4/www/
 # sdk文件地址
-VUE_APP_SDK_DIR=https://eurs3.4dkankan.com/v4/cdfg/sdk
+VUE_APP_SDK_DIR=https://eurs3.4dkankan.com/v4/cdfg/sdk-new
 
 
 # socket地址
@@ -18,5 +18,6 @@ VUE_APP_STATIC_DIR=viewer
 VUE_APP_REGION_URL=aws
 
 # 接口请求地址
-VUE_APP_APIS_URL=http://192.168.0.38:8888/
+#VUE_APP_APIS_URL=http://192.168.0.38:8888/
+VUE_APP_APIS_URL=https://vr.cdfmembers.com/
 

+ 1 - 1
.env.prod

@@ -3,7 +3,7 @@ VUE_APP_RESOURCE_URL=https://glp-vr.cdfmembers.com/
 # 静态资源地址
 VUE_APP_CDN_URL=https://4dkk.4dage.com/v4/www/
 # sdk文件地址
-VUE_APP_SDK_DIR=https://eurs3.4dkankan.com/v4/cdfg/sdk
+VUE_APP_SDK_DIR=https://eurs3.4dkankan.com/v4/cdfg/sdk-new
 
 # 静态资源目录
 VUE_APP_STATIC_DIR=viewer

+ 442 - 6
package-lock.json

@@ -15,9 +15,11 @@
         "socket.io": "^4.5.1",
         "trtc-js-sdk": "^4.13.0",
         "vue": "^3.2.36",
+        "vue-i18n": "9",
         "vuex": "^4.0.2"
       },
       "devDependencies": {
+        "@intlify/vue-i18n-loader": "^4.2.0",
         "@vue/cli-plugin-babel": "~5.0.0",
         "@vue/cli-service": "~5.0.0",
         "@vue/compiler-sfc": "^3.0.0",
@@ -1707,6 +1709,130 @@
         "@hapi/hoek": "^9.0.0"
       }
     },
+    "node_modules/@intlify/bundle-utils": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-2.2.2.tgz",
+      "integrity": "sha512-vngkvlIVV8ZJoyC5VqMvqJd2nvsx+qMN7pQjPiPjOrVndeiR7Dlue0k86Q8FsFUzyksW3HJZZi833ldxwbFzTA==",
+      "dev": true,
+      "dependencies": {
+        "@intlify/message-compiler": "^9.1.0",
+        "@intlify/shared": "^9.1.0",
+        "jsonc-eslint-parser": "^1.0.1",
+        "source-map": "^0.6.1",
+        "yaml-eslint-parser": "^0.3.2"
+      },
+      "engines": {
+        "node": ">= 12"
+      },
+      "peerDependenciesMeta": {
+        "petite-vue-i18n": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@intlify/core-base": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.2.2.tgz",
+      "integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
+      "dependencies": {
+        "@intlify/devtools-if": "9.2.2",
+        "@intlify/message-compiler": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/devtools-if": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
+      "integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
+      "dependencies": {
+        "@intlify/shared": "9.2.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/message-compiler": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
+      "integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
+      "dependencies": {
+        "@intlify/shared": "9.2.2",
+        "source-map": "0.6.1"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/shared": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.2.2.tgz",
+      "integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/vue-devtools": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
+      "integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
+      "dependencies": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/vue-i18n-loader": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/@intlify/vue-i18n-loader/-/vue-i18n-loader-4.2.0.tgz",
+      "integrity": "sha512-d7aBmMNWJskcZPT5rJH4h2XHe/PwNoJUaY0PGla9g+NSD4B0UR8LBKrp126nlaUfA74Xt0FEGvzCfG9KdC9KoA==",
+      "dev": true,
+      "dependencies": {
+        "@intlify/bundle-utils": "^2.2.2",
+        "@intlify/shared": "^9.1.0",
+        "js-yaml": "^4.1.0",
+        "json5": "^2.2.0",
+        "loader-utils": "^2.0.0"
+      },
+      "engines": {
+        "node": ">= 12"
+      },
+      "peerDependencies": {
+        "petite-vue-i18n": "^9.1.0",
+        "vue": "^3.0.0",
+        "vue-i18n": "^9.1.0"
+      },
+      "peerDependenciesMeta": {
+        "petite-vue-i18n": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@intlify/vue-i18n-loader/node_modules/loader-utils": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.2.tgz",
+      "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+      "dev": true,
+      "dependencies": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^3.0.0",
+        "json5": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=8.9.0"
+      }
+    },
     "node_modules/@jridgewell/gen-mapping": {
       "version": "0.1.1",
       "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
@@ -2719,9 +2845,9 @@
       "dev": true
     },
     "node_modules/@vue/devtools-api": {
-      "version": "6.1.4",
-      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.1.4.tgz",
-      "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ=="
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.2.1.tgz",
+      "integrity": "sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ=="
     },
     "node_modules/@vue/reactivity": {
       "version": "3.2.36",
@@ -3008,6 +3134,15 @@
         "acorn": "^8"
       }
     },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
     "node_modules/acorn-walk": {
       "version": "8.2.0",
       "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.2.0.tgz",
@@ -3149,6 +3284,12 @@
       "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
       "dev": true
     },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
     "node_modules/array-flatten": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-2.1.2.tgz",
@@ -4852,6 +4993,53 @@
         "node": ">=8.0.0"
       }
     },
+    "node_modules/eslint-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-2.1.0.tgz",
+      "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+      "dev": true,
+      "dependencies": {
+        "eslint-visitor-keys": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+      "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/espree": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-6.2.1.tgz",
+      "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^7.1.1",
+        "acorn-jsx": "^5.2.0",
+        "eslint-visitor-keys": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/espree/node_modules/acorn": {
+      "version": "7.4.1",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/esrecurse": {
       "version": "4.3.0",
       "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -5949,6 +6137,18 @@
       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
       "dev": true
     },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
     "node_modules/jsesc": {
       "version": "2.5.2",
       "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz",
@@ -5991,6 +6191,34 @@
         "node": ">=6"
       }
     },
+    "node_modules/jsonc-eslint-parser": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz",
+      "integrity": "sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^7.4.1",
+        "eslint-utils": "^2.1.0",
+        "eslint-visitor-keys": "^1.3.0",
+        "espree": "^6.0.0",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/jsonc-eslint-parser/node_modules/acorn": {
+      "version": "7.4.1",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/jsonfile": {
       "version": "6.1.0",
       "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -9236,6 +9464,23 @@
       "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
       "dev": true
     },
+    "node_modules/vue-i18n": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.2.2.tgz",
+      "integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
+      "dependencies": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2",
+        "@vue/devtools-api": "^6.2.1"
+      },
+      "engines": {
+        "node": ">= 14"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
     "node_modules/vue-loader": {
       "version": "17.0.0",
       "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-17.0.0.tgz",
@@ -9924,6 +10169,17 @@
         "node": ">= 6"
       }
     },
+    "node_modules/yaml-eslint-parser": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmmirror.com/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz",
+      "integrity": "sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==",
+      "dev": true,
+      "dependencies": {
+        "eslint-visitor-keys": "^1.3.0",
+        "lodash": "^4.17.20",
+        "yaml": "^1.10.0"
+      }
+    },
     "node_modules/yargs": {
       "version": "16.2.0",
       "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz",
@@ -11130,6 +11386,87 @@
         "@hapi/hoek": "^9.0.0"
       }
     },
+    "@intlify/bundle-utils": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-2.2.2.tgz",
+      "integrity": "sha512-vngkvlIVV8ZJoyC5VqMvqJd2nvsx+qMN7pQjPiPjOrVndeiR7Dlue0k86Q8FsFUzyksW3HJZZi833ldxwbFzTA==",
+      "dev": true,
+      "requires": {
+        "@intlify/message-compiler": "^9.1.0",
+        "@intlify/shared": "^9.1.0",
+        "jsonc-eslint-parser": "^1.0.1",
+        "source-map": "^0.6.1",
+        "yaml-eslint-parser": "^0.3.2"
+      }
+    },
+    "@intlify/core-base": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.2.2.tgz",
+      "integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
+      "requires": {
+        "@intlify/devtools-if": "9.2.2",
+        "@intlify/message-compiler": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2"
+      }
+    },
+    "@intlify/devtools-if": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
+      "integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
+      "requires": {
+        "@intlify/shared": "9.2.2"
+      }
+    },
+    "@intlify/message-compiler": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
+      "integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
+      "requires": {
+        "@intlify/shared": "9.2.2",
+        "source-map": "0.6.1"
+      }
+    },
+    "@intlify/shared": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.2.2.tgz",
+      "integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q=="
+    },
+    "@intlify/vue-devtools": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
+      "integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
+      "requires": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2"
+      }
+    },
+    "@intlify/vue-i18n-loader": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/@intlify/vue-i18n-loader/-/vue-i18n-loader-4.2.0.tgz",
+      "integrity": "sha512-d7aBmMNWJskcZPT5rJH4h2XHe/PwNoJUaY0PGla9g+NSD4B0UR8LBKrp126nlaUfA74Xt0FEGvzCfG9KdC9KoA==",
+      "dev": true,
+      "requires": {
+        "@intlify/bundle-utils": "^2.2.2",
+        "@intlify/shared": "^9.1.0",
+        "js-yaml": "^4.1.0",
+        "json5": "^2.2.0",
+        "loader-utils": "^2.0.0"
+      },
+      "dependencies": {
+        "loader-utils": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.2.tgz",
+          "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+          "dev": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^2.1.2"
+          }
+        }
+      }
+    },
     "@jridgewell/gen-mapping": {
       "version": "0.1.1",
       "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
@@ -11986,9 +12323,9 @@
       }
     },
     "@vue/devtools-api": {
-      "version": "6.1.4",
-      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.1.4.tgz",
-      "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ=="
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.2.1.tgz",
+      "integrity": "sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ=="
     },
     "@vue/reactivity": {
       "version": "3.2.36",
@@ -12250,6 +12587,13 @@
       "dev": true,
       "requires": {}
     },
+    "acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "requires": {}
+    },
     "acorn-walk": {
       "version": "8.2.0",
       "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.2.0.tgz",
@@ -12359,6 +12703,12 @@
       "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
       "dev": true
     },
+    "argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
     "array-flatten": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-2.1.2.tgz",
@@ -13723,6 +14073,40 @@
         "estraverse": "^4.1.1"
       }
     },
+    "eslint-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-2.1.0.tgz",
+      "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^1.1.0"
+      }
+    },
+    "eslint-visitor-keys": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+      "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+      "dev": true
+    },
+    "espree": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-6.2.1.tgz",
+      "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+      "dev": true,
+      "requires": {
+        "acorn": "^7.1.1",
+        "acorn-jsx": "^5.2.0",
+        "eslint-visitor-keys": "^1.1.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "7.4.1",
+          "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+          "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+          "dev": true
+        }
+      }
+    },
     "esrecurse": {
       "version": "4.3.0",
       "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -14609,6 +14993,15 @@
       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
       "dev": true
     },
+    "js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "requires": {
+        "argparse": "^2.0.1"
+      }
+    },
     "jsesc": {
       "version": "2.5.2",
       "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz",
@@ -14639,6 +15032,27 @@
       "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
       "dev": true
     },
+    "jsonc-eslint-parser": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz",
+      "integrity": "sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==",
+      "dev": true,
+      "requires": {
+        "acorn": "^7.4.1",
+        "eslint-utils": "^2.1.0",
+        "eslint-visitor-keys": "^1.3.0",
+        "espree": "^6.0.0",
+        "semver": "^6.3.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "7.4.1",
+          "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+          "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+          "dev": true
+        }
+      }
+    },
     "jsonfile": {
       "version": "6.1.0",
       "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -17171,6 +17585,17 @@
       "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
       "dev": true
     },
+    "vue-i18n": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.2.2.tgz",
+      "integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
+      "requires": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2",
+        "@vue/devtools-api": "^6.2.1"
+      }
+    },
     "vue-loader": {
       "version": "17.0.0",
       "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-17.0.0.tgz",
@@ -17715,6 +18140,17 @@
       "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
       "dev": true
     },
+    "yaml-eslint-parser": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmmirror.com/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz",
+      "integrity": "sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^1.3.0",
+        "lodash": "^4.17.20",
+        "yaml": "^1.10.0"
+      }
+    },
     "yargs": {
       "version": "16.2.0",
       "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz",

+ 2 - 0
package.json

@@ -16,9 +16,11 @@
     "socket.io": "^4.5.1",
     "trtc-js-sdk": "^4.13.0",
     "vue": "^3.2.36",
+    "vue-i18n": "9",
     "vuex": "^4.0.2"
   },
   "devDependencies": {
+    "@intlify/vue-i18n-loader": "^4.2.0",
     "@vue/cli-plugin-babel": "~5.0.0",
     "@vue/cli-service": "~5.0.0",
     "@vue/compiler-sfc": "^3.0.0",

+ 35 - 36
public/index.html

@@ -1,47 +1,46 @@
 <!DOCTYPE html>
 <html lang="">
-    <head>
-        <meta charset="utf-8" />
-        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
-        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
-        <link rel="icon" href="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/favicon.png" />
-        <link rel="stylesheet" href="//at.alicdn.com/t/font_2596172_ejg30arrayu.css" />
-        <link rel="stylesheet" href="//at.alicdn.com/t/font_3423899_m7c62apktz.css" />
-        <link rel="stylesheet" href="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/animate/animate.min.css" />
-        <link rel="stylesheet" href="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/swiper/swiper-bundle.min.css" />
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
+    <link rel="icon" href="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/favicon.png" />
+    <link rel="stylesheet" href="//at.alicdn.com/t/font_2596172_ejg30arrayu.css" />
+    <link rel="stylesheet" href="//at.alicdn.com/t/font_3423899_m7c62apktz.css" />
+    <link rel="stylesheet" href="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/animate/animate.min.css" />
+    <link rel="stylesheet" href="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/swiper/swiper-bundle.min.css" />
 
-        <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/mobile-detect.js"></script>
-        <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/flexible.min.js"></script>
-        <title>cdf澳門上葡京店</title>
-    </head>
+    <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/mobile-detect.js"></script>
+    <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/flexible.min.js"></script>
+    <title>cdf澳門上葡京店</title>
+  </head>
 
-    <body>
-        <noscript>
-            <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
-        </noscript>
-        <div id="app"></div>
-        <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/flv.min.js"></script>
-        <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/vconsole.js"></script>
-        <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/swiper/swiper-bundle.min.js"></script>
-        <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/socket.io.min.js"></script>
-        <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/zmjm.min.js"></script>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/flv.min.js"></script>
+    <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/vconsole.js"></script>
+    <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/swiper/swiper-bundle.min.js"></script>
+    <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/socket.io.min.js"></script>
+    <script src="<%= BASE_URL %><%= VUE_APP_STATIC_DIR %>/static/lib/zmjm.min.js"></script>
 
+    <script src="<%= VUE_APP_STATIC_DIR %>/static/lib/jweixin-1.6.0.js"></script>
 
-        <script src="<%= VUE_APP_STATIC_DIR %>/static/lib/jweixin-1.6.0.js"></script>
-        
-        <script src="<%= VUE_APP_SDK_DIR %>/kankan-sdk-deps.js?v=4.37.3-alpha.52"></script>
-        <script src="<%= VUE_APP_SDK_DIR %>/kankan-sdk.js?v=4.37.3-alpha.52"></script>
+    <script src="<%= VUE_APP_SDK_DIR %>/kankan-sdk-deps.js?v=4.37.3-alpha.81"></script>
+    <script src="<%= VUE_APP_SDK_DIR %>/kankan-sdk.js?v=4.37.3-alpha.80"></script>
 
-        <!-- <script src="https://4dkk.4dage.com/v4/www/sdk/kankan-sdk-deps.js?v=4.0.0-alpha.44"></script>
+    <!-- <script src="https://4dkk.4dage.com/v4/www/sdk/kankan-sdk-deps.js?v=4.0.0-alpha.44"></script>
         <script src="https://4dkk.4dage.com/v4/www/sdk/kankan-sdk.js?v=4.0.0-alpha.44"></script> -->
 
-        <!-- <script src="https://eurs3.4dkankan.com/v4/cdfg/sdk/kankan-sdk-deps.js?v=4.0.0-alpha.45"></script>
+    <!-- <script src="https://eurs3.4dkankan.com/v4/cdfg/sdk/kankan-sdk-deps.js?v=4.0.0-alpha.45"></script>
         <script src="https://eurs3.4dkankan.com/v4/cdfg/sdk/kankan-sdk.js?v=4.0.0-alpha.45"></script> -->
-        <!-- built files will be auto injected -->
-        <script>
-            if ((query = window.location.href.indexOf('vlog') != -1)) {
-                var vConsole = new window.VConsole()
-            }
-        </script>
-    </body>
+    <!-- built files will be auto injected -->
+    <script>
+      if ((query = window.location.href.indexOf('vlog') != -1)) {
+        var vConsole = new window.VConsole();
+      }
+    </script>
+  </body>
 </html>

+ 206 - 231
src/app.vue

@@ -8,63 +8,28 @@
     <template v-if="dataLoaded">
       <Information v-if="!isshoppingguide" />
       <Control />
-      <!-- <teleport v-if="refMiniMap && player.showWidgets" :to="refMiniMap">
-        <span :class="{ gudieDisabled: isshoppingguide && role != 'leader' }" class="button-switch" @click.stop="toggleMap">
-          <ui-icon type="show_map_collect"></ui-icon>
-        </span>
 
-        <p class="change" :class="{ gudieDisabled: isshoppingguide && role != 'leader' }" @click="changeMode('dollhouse', $event, 'focus3d')">
-          <ui-icon type="show_3d_normal"></ui-icon>
-          3D模型
-        </p>
-      </teleport> -->
       <teleport v-if="refMiniMap && player.showWidgets" :to="refMiniMap">
         <span :class="{ gudieDisabled: isshoppingguide && role != 'leader' }" class="button-switch" @click.stop="toggleMap">
           <ui-icon type="show_map_collect"></ui-icon>
         </span>
         <div v-if="controls.showDollhouse" :class="{ gudieDisabled: isshoppingguide && role != 'leader' }" class="change" @click="changeMode('dollhouse')">
           <ui-icon type="show_3d_normal"></ui-icon>
-          <span> 3D模型</span>
+          <span> {{ $t('mode.dollhouseModel') }}</span>
         </div>
       </teleport>
-      <!-- <template v-if="refMiniMap && player.showWidgets">
-        <div
-          :class="{ disabled: flying, gudieDisabled: isshoppingguide && role != 'leader' }"
-          v-show="mode != 'panorama'"
-          v-if="controls.showFloorplan && controls.showDollhouse"
-          class="tab-layer"
-        >
-          <div class="tabs" v-if="controls.showMap">
-            <span :class="{ active: mode === 'floorplan' }" @click="changeMode('floorplan', $event)">
-              <ui-icon :type="mode == 'floorplan' ? 'show_plane_selected' : 'show_plane_normal'"></ui-icon>
-              二維
-            </span>
-            <span :class="{ active: mode === 'dollhouse' }" @click="changeMode('dollhouse', $event)">
-              <ui-icon :type="mode == 'dollhouse' ? 'show_3d_selected' : 'show_3d_normal'"></ui-icon>
-
-              三維
-            </span>
-            <div class="background" ref="background"></div>
-          </div>
-        </div>
-      </template> -->
 
       <template v-if="refMiniMap && player.showWidgets">
-        <div
-          :class="{ disabled: flying, gudieDisabled: isshoppingguide && role != 'leader' }"
-          v-show="mode != 'panorama'"
-          v-if="controls.showFloorplan && controls.showDollhouse"
-          class="tab-layer"
-        >
+        <div :class="{ disabled: flying, gudieDisabled: isshoppingguide && role != 'leader' }" v-show="mode != 'panorama'" v-if="controls.showFloorplan && controls.showDollhouse" class="tab-layer">
           <div class="tabs" v-if="controls.showMap">
             <span :class="{ active: mode === 'floorplan' }" ref="floorplan_ref" @click="changeMode('floorplan', $event)">
               <ui-icon :type="mode == 'floorplan' ? 'show_plane_selected' : 'show_plane_normal'"></ui-icon>
-              二維
+              {{ $t('mode.floorplan') }}
             </span>
             <span :class="{ active: mode === 'dollhouse' }" ref="dollhouse_ref" @click="changeMode('dollhouse', $event)">
               <ui-icon :type="mode == 'dollhouse' ? 'show_3d_selected' : 'show_3d_normal'"></ui-icon>
 
-              三維
+              {{ $t('mode.dollhouse') }}
             </span>
             <div class="background" ref="background"></div>
           </div>
@@ -80,41 +45,45 @@
 </template>
 
 <script setup>
-import { useMusicPlayer } from "@/utils/sound";
+import { useMusicPlayer } from '@/utils/sound';
 // import UiTags from "@/components/Tags";
-import GoodsList from "@/components/Tags/goods-list.vue";
-import Treasure from "@/components/Tags/treasure.vue";
-import Waterfall from "@/components/Tags/waterfall.vue";
-
-import Information from "@/components/Information";
-import Control from "@/components/Controls/Control.Mobile.vue";
-import LoadingLogo from "@/components/shared/Loading.vue";
-import OpenVideo from "@/components/openVideo/";
-import Guide from "@/components/shared/Guide.vue";
-import { Dialog } from "@/global_components/";
-
-import { createApp } from "@/app";
-import { ref, onMounted, computed, nextTick, watch } from "vue";
-import { useStore } from "vuex";
-import browser from "@/utils/browser";
-import { useApp, getApp } from "@/app";
-import common from "@/utils/common";
-import { Cache } from "@/utils/index";
-import wxShare from "@/utils/wxshare";
-
-import * as apis from "@/apis/index.js";
+import GoodsList from '@/components/Tags/goods-list.vue';
+import Treasure from '@/components/Tags/treasure.vue';
+import Waterfall from '@/components/Tags/waterfall.vue';
+
+import Information from '@/components/Information';
+import Control from '@/components/Controls/Control.Mobile.vue';
+import LoadingLogo from '@/components/shared/Loading.vue';
+import OpenVideo from '@/components/openVideo/';
+import Guide from '@/components/shared/Guide.vue';
+import { Dialog } from '@/global_components/';
+
+import { createApp } from '@/app';
+import { ref, onMounted, computed, nextTick, watch } from 'vue';
+import { useStore } from 'vuex';
+import browser from '@/utils/browser';
+import { useApp, getApp } from '@/app';
+import common from '@/utils/common';
+import { useI18n, getLocale } from '@/i18n';
+import { Cache } from '@/utils/index';
+import wxShare from '@/utils/wxshare';
+
+import * as apis from '@/apis/index.js';
+
+const { t } = useI18n({ useScope: 'global' });
+
 const store = useStore();
 
 let jumpNewScene = (sceneFirstView) => {
   let url = window.location.href;
 
-  if (!browser.hasURLParam("pose")) {
+  if (!browser.hasURLParam('pose')) {
     url += `&${sceneFirstView.sceneview}`;
   } else {
-    url = browser.replaceQueryString(url, "pose", sceneFirstView.sceneview.replace("pose=", ""));
+    url = browser.replaceQueryString(url, 'pose', sceneFirstView.sceneview.replace('pose=', ''));
   }
 
-  url = browser.replaceQueryString(url, "m", sceneFirstView.num);
+  url = browser.replaceQueryString(url, 'm', sceneFirstView.num);
   return url;
 };
 
@@ -125,7 +94,7 @@ let visibilitychangeFn = () => {
 };
 
 let hashchangefn = () => {
-  if (window.location.hash.indexOf("#showpage") >= 0) {
+  if (window.location.hash.indexOf('#showpage') >= 0) {
     window.history.go(-1);
   }
 };
@@ -134,54 +103,54 @@ const musicPlayer = useMusicPlayer();
 
 let app = null;
 
-let tagid = browser.getURLParam("tagid");
+let tagid = browser.getURLParam('tagid');
 
-const role = computed(() => store.getters["rtc/role"]);
+const role = computed(() => store.getters['rtc/role']);
 
 const closetagtype = () => {
-  store.commit("tag/setTagClickType", {
-    type: "",
+  store.commit('tag/setTagClickType', {
+    type: '',
     data: {},
   });
 
   if (isshoppingguide.value) {
-    if (role.value == "leader") {
+    if (role.value == 'leader') {
       socket.value &&
-        socket.value.emit("action", {
-          type: "tagclose",
+        socket.value.emit('action', {
+          type: 'tagclose',
         });
     }
   }
 };
-const socket = computed(() => store.getters["rtc/socket"]);
+const socket = computed(() => store.getters['rtc/socket']);
 
 const tags = computed(() => {
-  return store.getters["tag/tags"] || [];
+  return store.getters['tag/tags'] || [];
 });
 
-const isshoppingguide = computed(() => store.getters["shoppingguide"]);
+const isshoppingguide = computed(() => store.getters['shoppingguide']);
 
-const player = computed(() => store.getters["player"]);
-const flying = computed(() => store.getters["flying"]);
-const metadata = computed(() => store.getters["scene/metadata"]);
+const player = computed(() => store.getters['player']);
+const flying = computed(() => store.getters['flying']);
+const metadata = computed(() => store.getters['scene/metadata']);
 const controls = computed(() => {
   return metadata.value.controls;
 });
-const mode = computed(() => store.getters["mode"]);
-const showNavigations = computed(() => store.getters["showNavigations"]);
+const mode = computed(() => store.getters['mode']);
+const showNavigations = computed(() => store.getters['showNavigations']);
 const scene$ = ref(null);
 const hadVideo = ref(true);
 
-if (!Cache.get("HIDENVIDEOEXPIRES")) {
-  if (browser.getURLParam("m") == "eur-KJ-z5ZEV22AeU" && browser.getURLParam("pose") == "pano:408,qua:-0.006,0.6299,0.0049,0.7766") {
-    Cache.set("HIDENVIDEOEXPIRES", "yes", 60 * 8 * 60);
+if (!Cache.get('HIDENVIDEOEXPIRES')) {
+  if (browser.getURLParam('m') == 'eur-KJ-z5ZEV22AeU' && browser.getURLParam('pose') == 'pano:408,qua:-0.006,0.6299,0.0049,0.7766') {
+    Cache.set('HIDENVIDEOEXPIRES', 'yes', 60 * 8 * 60);
     hadVideo.value = false;
   } else {
     hadVideo.value = true;
   }
 }
 
-if (browser.getURLParam("role")) {
+if (browser.getURLParam('role')) {
   hadVideo.value = true;
 }
 
@@ -191,11 +160,11 @@ const refMiniMap = ref(null);
 const isCollapse = ref(false);
 const background = ref(null);
 const resize = () => {
-  if (this.$refs.background && (this.mode == "floorplan" || this.mode == "dollhouse")) {
+  if (this.$refs.background && (this.mode == 'floorplan' || this.mode == 'dollhouse')) {
     this.$nextTick(() => {
-      let $active = $(this.$el).find(".tabs .active");
-      background.value.style.width = $active[0].getBoundingClientRect().width + "px";
-      background.value.style.left = $active.position().left + "px";
+      let $active = $(this.$el).find('.tabs .active');
+      background.value.style.width = $active[0].getBoundingClientRect().width + 'px';
+      background.value.style.left = $active.position().left + 'px';
     });
   }
 };
@@ -203,16 +172,16 @@ const resize = () => {
 watch(
   () => isshoppingguide.value,
   (val, old) => {
-    let $minmap = document.querySelector("[xui_min_map]");
+    let $minmap = document.querySelector('[xui_min_map]');
 
     if ($minmap) {
       setTimeout(async () => {
-        if (browser.getURLParam("role") == "customer") {
+        if (browser.getURLParam('role') == 'customer') {
           await nextTick();
           if (isshoppingguide.value) {
-            $minmap.classList.add("gudieDisabled");
+            $minmap.classList.add('gudieDisabled');
           } else {
-            $minmap.classList.remove("gudieDisabled");
+            $minmap.classList.remove('gudieDisabled');
             // wxShare({
             //   title: `cdf澳門上葡京店~`,
             //   desc: "cdf澳門上葡京店~",
@@ -233,12 +202,12 @@ watch(
   () => player.value.showMap,
   (val, old) => {
     if (!isCollapse.value) {
-      let $minmap = document.querySelector("[xui_min_map]");
+      let $minmap = document.querySelector('[xui_min_map]');
       if ($minmap) {
         if (val) {
-          $minmap.classList.remove("collapse");
+          $minmap.classList.remove('collapse');
         } else {
-          $minmap.classList.add("collapse");
+          $minmap.classList.add('collapse');
         }
       }
     }
@@ -250,12 +219,12 @@ watch(
 watch(
   () => player.value.showWidgets,
   (val, old) => {
-    let $minmap = document.querySelector("[xui_min_map]");
+    let $minmap = document.querySelector('[xui_min_map]');
     if ($minmap) {
       if (val) {
-        $minmap.classList.remove("collapse");
+        $minmap.classList.remove('collapse');
       } else {
-        $minmap.classList.add("collapse");
+        $minmap.classList.add('collapse');
       }
     }
   },
@@ -270,15 +239,15 @@ watch(
     console.log(val);
     let timer = setTimeout(() => {
       clearTimeout(timer);
-      if (val == "floorplan") {
+      if (val == 'floorplan') {
         if (floorplan_ref.value && floorplan_ref.value) {
-          background.value.style.width = floorplan_ref.value.getBoundingClientRect().width + "px";
-          background.value.style.left = floorplan_ref.value.offsetLeft + "px";
+          background.value.style.width = floorplan_ref.value.getBoundingClientRect().width + 'px';
+          background.value.style.left = floorplan_ref.value.offsetLeft + 'px';
         }
-      } else if (val == "dollhouse") {
+      } else if (val == 'dollhouse') {
         if (dollhouse_ref.value && dollhouse_ref.value) {
-          background.value.style.width = dollhouse_ref.value.getBoundingClientRect().width + "px";
-          background.value.style.left = dollhouse_ref.value.offsetLeft + "px";
+          background.value.style.width = dollhouse_ref.value.getBoundingClientRect().width + 'px';
+          background.value.style.left = dollhouse_ref.value.offsetLeft + 'px';
         }
       }
     }, 0);
@@ -292,20 +261,20 @@ const dollhouse_ref = ref(null);
 const changeMode = (name, e) => {
   if (e) {
     if (!flying.value) {
-      store.commit("setMode", name);
+      store.commit('setMode', name);
     }
   } else {
-    store.commit("setMode", name);
+    store.commit('setMode', name);
   }
 };
 const toggleMap = () => {
   isCollapse.value = !isCollapse.value;
-  let $minmap = document.querySelector("[xui_min_map]");
+  let $minmap = document.querySelector('[xui_min_map]');
   if ($minmap) {
     if (!isCollapse.value) {
-      $minmap.classList.remove("collapse");
+      $minmap.classList.remove('collapse');
     } else {
-      $minmap.classList.add("collapse");
+      $minmap.classList.add('collapse');
     }
   }
 };
@@ -313,13 +282,13 @@ const toggleMap = () => {
 const onClickTagInfo = (el) => {
   el.stopPropagation();
   let item = tags.value.find((item) => item.sid == el.target.dataset.id);
-  if (item.type == "commodity") {
+  if (item.type == 'commodity') {
     guideclicktag(item);
-    store.commit("tag/setTagClickType", {
-      type: "goodlist",
+    store.commit('tag/setTagClickType', {
+      type: 'goodlist',
       data: item,
     });
-  } else if (item.type == "link_scene") {
+  } else if (item.type == 'link_scene') {
     guideclicktag(item);
     let sceneFirstView = item.hotContent.sceneFirstView;
     window.location.href = jumpNewScene(sceneFirstView);
@@ -328,10 +297,10 @@ const onClickTagInfo = (el) => {
 
 const guideclicktag = (tag) => {
   if (isshoppingguide.value) {
-    if (role.value == "leader") {
+    if (role.value == 'leader') {
       socket.value &&
-        socket.value.emit("action", {
-          type: "tagclick",
+        socket.value.emit('action', {
+          type: 'tagclick',
           data: {
             sid: tag.sid,
           },
@@ -345,54 +314,53 @@ onMounted(async () => {
   apis.burying_point({ type: 0 });
 
   app = createApp({
-    num: browser.getURLParam("m"),
+    num: browser.getURLParam('m'),
     dom: scene$.value,
     mobile: true,
     isLoadTags: false,
-    sceneKind: "tiles",
+    sceneKind: 'tiles',
+    lang: getLocale(),
     scene: {
       markerOpacity: 1,
-      markerURL: "https://eurs3.4dkankan.com/cdf/file/43aa29799bfd472298a47cc6370b10cc.png",
-      pathEndColor: "#FF4641",
+      markerURL: 'https://eurs3.4dkankan.com/cdf/file/43aa29799bfd472298a47cc6370b10cc.png',
+      pathEndColor: '#FF4641',
     },
   });
-  app.use("MinMap", { theme: { camera_fillStyle: "#ED5D18" } });
-  app.use("Tag");
+  app.use('MinMap', { theme: { camera_fillStyle: '#ED5D18' } });
+  app.use('Tag');
   app
-    .use("TagView", {
+    .use('TagView', {
       render(data) {
-        if (data.type == "waterfall") {
+        if (data.type == 'waterfall') {
           return `<span class="tag-icon waterfall animate" style="background-image:url({{icon}})"></span>`;
-        } else if (data.type == "coupon") {
+        } else if (data.type == 'coupon') {
           return `<span class="tag-icon coupon animate" style="background-image:url({{icon}})"></span>`;
-        } else if (data.type == "applet_link") {
+        } else if (data.type == 'applet_link') {
           try {
             data.hotContent = JSON.parse(data.hotContent);
           } catch (error) {}
-          return `<span class="tag-icon applet_link animate" style="background-image:url(${
-            data.hotContent.liveIcon.src ? common.changeUrl(data.hotContent.liveIcon.src) : "{{icon}}"
-          })"></span>`;
-        } else if (data.type == "link_scene") {
+          return `<span class="tag-icon applet_link animate" style="background-image:url(${data.hotContent.liveIcon.src ? common.changeUrl(data.hotContent.liveIcon.src) : '{{icon}}'})"></span>`;
+        } else if (data.type == 'link_scene') {
           return `<span class="tag-icon animate" style="background-image:url({{icon}})"></span>
                       <div class="tag-body">
                         <div data-id="${data.sid}" class="tag-commodity tag-link_scene">
-                          <p class="tag-title">點擊前往下一個區域</p>
+                          <p class="tag-title">${t('common.goNext')}</p>
                         </div>
                       </div>
                   `;
-        } else if (data.type == "commodity") {
+        } else if (data.type == 'commodity') {
           let arr = data.products.map((item) => item.price);
           let priceMin = isFinite(Math.min.apply(null, arr)) ? Math.min.apply(null, arr) : 0;
           let priceMax = isFinite(Math.max.apply(null, arr)) ? Math.max.apply(null, arr) : 0;
           let price = priceMin == priceMax ? priceMax : `${priceMin}-${priceMax}`;
-          let range = `${data.products[0] ? data.products[0].symbol : "MOP$"} ${price}`;
+          let range = `${data.products[0] ? data.products[0].symbol : 'MOP$'} ${price}`;
           return `<span class="tag-icon animate" style="background-image:url({{icon}})"></span>
                       <div class="tag-body">
                         <div data-id="${data.sid}" class="tag-commodity">
-                          <div  style="background-image:url(${data.products[0] ? data.products[0].pic : ""})" class='tag-avatar'>
+                          <div  style="background-image:url(${data.products[0] ? data.products[0].pic : ''})" class='tag-avatar'>
                           </div>
                           <p class="tag-title">${data.title}</p>
-                          <p class="tag-info">${range} | 查看 ></p>
+                          <p class="tag-info">${range} | ${t('common.view')} ></p>
                         </div>
                       </div>
                   `;
@@ -402,43 +370,43 @@ onMounted(async () => {
       },
     })
     .then((view) => {
-      view.on("click", (e) => {
+      view.on('click', (e) => {
         var tag = e.data;
         // 聚焦當前點擊的熱點
         view.focus(tag.sid).then(() => {
-          if (tag.type == "coupon") {
+          if (tag.type == 'coupon') {
             try {
               if (isshoppingguide.value) {
                 return;
               }
-              document.querySelector(`[data-tag-id="${tag.sid}"] .tag-icon`).style.display = "none";
-              let hotcontent = typeof tag.hotContent == "string" ? JSON.parse(tag.hotContent) : tag.hotContent;
+              document.querySelector(`[data-tag-id="${tag.sid}"] .tag-icon`).style.display = 'none';
+              let hotcontent = typeof tag.hotContent == 'string' ? JSON.parse(tag.hotContent) : tag.hotContent;
               browser.openLink(
-                "/subPackage/pages/activity/activity?pageId=" + hotcontent.couponLink,
+                '/subPackage/pages/activity/activity?pageId=' + hotcontent.couponLink,
                 `https://m.cdfmembers.com/shop/600667208/showactivity?pageId=${hotcontent.couponLink}`,
                 `/pages/showactivity/showactivity?pageId=${hotcontent.couponLink}`
               );
               apis.burying_point({ type: 2 });
             } catch (error) {}
-          } else if (tag.type == "waterfall") {
-            store.commit("tag/setTagClickType", {
-              type: "waterfall",
+          } else if (tag.type == 'waterfall') {
+            store.commit('tag/setTagClickType', {
+              type: 'waterfall',
               data: tag,
             });
             guideclicktag(tag);
-          } else if (tag.type == "applet_link") {
+          } else if (tag.type == 'applet_link') {
             try {
               if (isshoppingguide.value) {
                 return;
               }
-              let hotcontent = typeof tag.hotContent == "string" ? JSON.parse(tag.hotContent) : tag.hotContent;
+              let hotcontent = typeof tag.hotContent == 'string' ? JSON.parse(tag.hotContent) : tag.hotContent;
               browser.openLink(
-                "/subPackage/pages/home/home?pageType=2&pageId=" + hotcontent.liveLink,
+                '/subPackage/pages/home/home?pageType=2&pageId=' + hotcontent.liveLink,
                 `https://m.cdfmembers.com/shop/600667208/showactivity?pageId=${hotcontent.liveLink}`,
                 `/pages/showactivity/showactivity?pageId=${hotcontent.liveLink}`
               );
             } catch (error) {}
-          } else if (tag.type == "link_scene") {
+          } else if (tag.type == 'link_scene') {
             guideclicktag(tag);
             let sceneFirstView = tag.hotContent.sceneFirstView;
             window.location.href = jumpNewScene(sceneFirstView);
@@ -446,18 +414,18 @@ onMounted(async () => {
         });
       });
 
-      view.on("focus", (e) => {
-        document.querySelectorAll("[xui_tags_view] >div").forEach((el) => {
-          if (el.getAttribute("data-tag-type") == "link_scene" || el.getAttribute("data-tag-type") == "commodity") {
-            el.querySelector(".tag-body").classList.remove("show");
-            el.style.zIndex = "auto";
+      view.on('focus', (e) => {
+        document.querySelectorAll('[xui_tags_view] >div').forEach((el) => {
+          if (el.getAttribute('data-tag-type') == 'link_scene' || el.getAttribute('data-tag-type') == 'commodity') {
+            el.querySelector('.tag-body').classList.remove('show');
+            el.style.zIndex = 'auto';
           }
         });
-        if (e.data.type == "commodity" || e.data.type == "link_scene") {
-          e.target.style.zIndex = "999";
-          e.target.querySelector(".tag-body").classList.add("show");
-          e.target.querySelector(".tag-commodity").removeEventListener("click", onClickTagInfo);
-          e.target.querySelector(".tag-commodity").addEventListener("click", onClickTagInfo);
+        if (e.data.type == 'commodity' || e.data.type == 'link_scene') {
+          e.target.style.zIndex = '999';
+          e.target.querySelector('.tag-body').classList.add('show');
+          e.target.querySelector('.tag-commodity').removeEventListener('click', onClickTagInfo);
+          e.target.querySelector('.tag-commodity').addEventListener('click', onClickTagInfo);
 
           if (tagid) {
             document.querySelector(`[data-id="${tagid}"]`) && document.querySelector(`[data-id="${tagid}"]`).click();
@@ -466,147 +434,154 @@ onMounted(async () => {
         }
       });
 
-      view.on("rendered", (e) => {
+      view.on('rendered', (e) => {
         tagid && view.focus(tagid);
       }); //dom渲染完成
     });
 
-  app.use("TourPlayer");
+  app.use('TourPlayer');
+
+  app.TourManager.on('loaded', (list) => {
+    store.commit('tour/loaded', list);
+    // app.TourManager.load(tours.value);
+  });
   // if (!hadVideo.value) {
   //   app.Scene.lock();
   // }
-  app.Scene.on("ready", () => {
+  app.Scene.on('ready', () => {
     show.value = true;
+    store.commit('SetPlayerOptions', {
+      lang: getLocale(),
+    });
     wxShare({
-      title: `cdf澳門上葡京店~`,
-      desc: "cdf澳門上葡京店~",
+      title: `${t('common.title')}~`,
+      desc: `${t('common.title')}~`,
       link: window.location.href,
-      imgUrl: "https://glp-vr.cdfmembers.com/cdf/file/91dd5305525f463286f03a31abd1c154.jpg",
+      imgUrl: 'https://glp-vr.cdfmembers.com/cdf/file/91dd5305525f463286f03a31abd1c154.jpg',
     });
   });
-  app.Scene.on("error", (data) => {
+  app.Scene.on('error', (data) => {
     switch (data.code) {
       case 5033:
-        Dialog.alert("该场景正在计算中,请稍后再试");
+        Dialog.alert(t('common.calculation'));
         break;
       case 5034:
-        Dialog.alert("服务端开小差,请稍后再试");
+        Dialog.alert(t('common.title'));
         break;
       case 5009:
-        Dialog.alert("服务端开小差,请稍后再试");
+        Dialog.alert(t('common.title'));
         break;
       case 5005:
-        Dialog.alert("服务端开小差,请稍后再试");
+        Dialog.alert(t('common.title'));
         break;
     }
   });
-  app.Scene.on("loaded", (pano) => {
-    refMiniMap.value = "[xui_min_map]";
-    store.commit("setFloorId", pano.floorIndex);
-    store.commit("rtc/setShowdaogou", true);
+  app.Scene.on('loaded', (pano) => {
+    refMiniMap.value = '[xui_min_map]';
+    store.commit('setFloorId', pano.floorIndex);
+    store.commit('rtc/setShowdaogou', true);
 
-    if (browser.getURLParam("roomId")) {
-      store.commit("showShoppingguide", true);
+    if (browser.getURLParam('roomId')) {
+      store.commit('showShoppingguide', true);
     } else {
-      if (!localStorage.getItem("user_guide")) {
+      if (!localStorage.getItem('user_guide')) {
         Dialog.confirm({
           showCloseIcon: false,
-          okText: "我知道了",
-          content:
-            "<span style='font-size: 16px; line-height: 1.5;'>開發者已遵守收集、使用最終用戶個人信息有關的所有可適用法律、政策和法規,保護用戶個人信息安全。<span/>",
-          title: "隱私條款:",
+          okText: t('common.know'),
+          content: "<span style='font-size: 16px; line-height: 1.5;'>" + t('common.notice') + '<span/>',
+          title: `${t('common.tips')}:`,
           single: true,
           func: (state) => {
-            if (state == "ok") {
-              localStorage.setItem("user_guide", Date.now());
+            if (state == 'ok') {
+              localStorage.setItem('user_guide', Date.now());
             }
           },
         });
       }
     }
 
-    app.resource.tags(`${process.env.VUE_APP_RESOURCE_URL}cdf/hot/${browser.getURLParam("m")}/hot.json?rnd=${Math.random()}`);
+    app.resource.tags(`${process.env.VUE_APP_RESOURCE_URL}cdf/hot/${browser.getURLParam('m')}/hot.json?rnd=${Math.random()}`);
     useMusicPlayer();
   });
-  app.Scene.on("panorama.videorenderer.resumerender", () => {
+  app.Scene.on('panorama.videorenderer.resumerender', () => {
     musicPlayer.pause(true);
   });
 
-  app.Scene.on("panorama.videorenderer.suspendrender", async () => {
+  app.Scene.on('panorama.videorenderer.suspendrender', async () => {
     let player = await getApp().TourManager.player;
     if (!player.isPlaying) {
       musicPlayer.resume();
     }
   });
-  app.store.on("metadata", (metadata) => {
-    store.commit("scene/load", metadata);
+  app.store.on('metadata', (metadata) => {
+    store.commit('scene/load', metadata);
     if (!metadata.controls.showMap) {
       app.MinMap.hide(true);
     }
     dataLoaded.value = true;
   });
-  app.store.on("tags", (tags) => {
-    store.commit("tag/load", tags);
+  app.store.on('tags', (tags) => {
+    store.commit('tag/load', tags);
   });
-  app.Camera.on("mode.beforeChange", ({ fromMode, toMode, floorIndex }) => {
+  app.Camera.on('mode.beforeChange', ({ fromMode, toMode, floorIndex }) => {
     if (fromMode) {
-      store.commit("setFlying", true);
+      store.commit('setFlying', true);
     }
   });
-  app.Camera.on("mode.afterChange", ({ toMode, floorIndex }) => {
-    store.commit("setFlying", false);
+  app.Camera.on('mode.afterChange', ({ toMode, floorIndex }) => {
+    store.commit('setFlying', false);
   });
-  app.Camera.on("flying.started", (pano) => {
-    store.commit("setFlying", true);
+  app.Camera.on('flying.started', (pano) => {
+    store.commit('setFlying', true);
   });
-  app.Camera.on("flying.ended", ({ targetPano }) => {
-    store.commit("setFlying", false);
-    store.commit("setPanoId", targetPano.id);
+  app.Camera.on('flying.ended', ({ targetPano }) => {
+    store.commit('setFlying', false);
+    store.commit('setPanoId', targetPano.id);
     if (app.Scene.isCurrentPanoHasVideo) {
       apis.burying_point({ type: 5 });
     }
   });
 
-  app.Camera.on("pano.chosen", (pano) => {
+  app.Camera.on('pano.chosen', (pano) => {
     apis.burying_point({ type: 4 });
   });
-  app.store.on("tour", async (tour) => {
-    app.TourManager.load(tour);
-    store.commit("tour/setData", {
-      tours: JSON.parse(
-        JSON.stringify(app.TourManager.tours, (key, val) => {
-          if (key === "audio") {
-            return null;
-          } else {
-            return val;
-          }
-        })
-      ),
-    });
-    store.commit("tour/setBackUp", {
-      tours: JSON.parse(
-        JSON.stringify(app.TourManager.tours, (key, val) => {
-          if (key === "audio") {
-            return null;
-          } else {
-            return val;
-          }
-        })
-      ),
-    });
-  });
-  app.store.on("floorcad", (floor) => store.commit("scene/loadFloorData", floor));
+  // app.store.on('tour', async (tour) => {
+  //   app.TourManager.load(tour);
+  //   store.commit('tour/setData', {
+  //     tours: JSON.parse(
+  //       JSON.stringify(app.TourManager.tours, (key, val) => {
+  //         if (key === 'audio') {
+  //           return null;
+  //         } else {
+  //           return val;
+  //         }
+  //       })
+  //     ),
+  //   });
+  //   store.commit('tour/setBackUp', {
+  //     tours: JSON.parse(
+  //       JSON.stringify(app.TourManager.tours, (key, val) => {
+  //         if (key === 'audio') {
+  //           return null;
+  //         } else {
+  //           return val;
+  //         }
+  //       })
+  //     ),
+  //   });
+  // });
+  app.store.on('floorcad', (floor) => store.commit('scene/loadFloorData', floor));
 
   app.render();
-  document.removeEventListener("visibilitychange", visibilitychangeFn);
-  document.addEventListener("visibilitychange", visibilitychangeFn);
+  document.removeEventListener('visibilitychange', visibilitychangeFn);
+  document.addEventListener('visibilitychange', visibilitychangeFn);
 
   if (browser.detectWeixin()) {
     //ios的ua中无miniProgram,但都有MicroMessenger(表示是微信浏览器)
     wx.miniProgram.getEnv((res) => {
       if (res.miniprogram) {
-        window.removeEventListener("hashchange", hashchangefn);
-        window.addEventListener("hashchange", hashchangefn);
+        window.removeEventListener('hashchange', hashchangefn);
+        window.addEventListener('hashchange', hashchangefn);
       }
     });
   }
@@ -696,7 +671,7 @@ onMounted(async () => {
       position: relative;
       margin-bottom: 30px;
       &::before {
-        content: "";
+        content: '';
         display: inline-block;
         left: 50%;
         transform: translateX(-50%);
@@ -758,7 +733,7 @@ onMounted(async () => {
     width: 84px !important;
     height: 84px !important;
     &::after {
-      content: "發現好禮";
+      content: '發現好禮';
       width: 100%;
       color: #ed5d18;
       position: absolute;
@@ -782,7 +757,7 @@ onMounted(async () => {
     position: relative;
     overflow: hidden;
     &::after {
-      content: "直播中";
+      content: '直播中';
       width: 100%;
       height: 20px;
       background: #ed5d18;

BIN
src/assets/images/guide/novice_guide_button@3x.png


BIN
src/assets/images/guide/novice_guide_button_empty@2x.png


BIN
src/assets/images/guide/novice_guide_button_empty@3x.png


BIN
src/assets/images/guide/novice_guide_button_zh@2x.png


BIN
src/assets/images/guide/novice_guide_text@3x.png


BIN
src/assets/images/guide/novice_guide_text_en@2x.png


BIN
src/assets/images/icon/en.png


BIN
src/assets/images/icon/zh_CN.png


BIN
src/assets/images/icon/zh_HK.png


+ 87 - 95
src/components/Controls/Control.Mobile.vue

@@ -1,97 +1,93 @@
 <template>
   <FloorSwitch />
   <transition mode="out-in">
-      <component class="limitwidth" :is="panelPage"></component>
+    <component class="limitwidth" :is="panelPage"></component>
   </transition>
-
-
 </template>
 
 <script setup>
-import { useStore } from "vuex";
-import { onMounted, watch, computed, ref, shallowRef, nextTick } from "vue";
-import { useApp, getApp } from "@/app";
-import FloorSwitch from "./FloorSwitch";
-
-import vMain from "./Panel/Main";
-import Guide from "./Panel/Guide";
+import { useStore } from 'vuex';
+import { onMounted, watch, computed, ref, shallowRef, nextTick } from 'vue';
+import { useApp, getApp } from '@/app';
+import FloorSwitch from './FloorSwitch';
 
-import guideShop from "../RTC/index";
+import vMain from './Panel/Main';
+import Guide from './Panel/Guide';
 
+import guideShop from '../RTC/index';
 
-import { Scrollbar, Dialog } from "@/global_components/";
-import common from "@/utils/common";
-import { useMusicPlayer } from "@/utils/sound";
+import { Scrollbar, Dialog } from '@/global_components/';
+import common from '@/utils/common';
+import { useMusicPlayer } from '@/utils/sound';
 
 const store = useStore();
 
 const musicPlayer = useMusicPlayer();
-const metadata = computed(() => store.getters["scene/metadata"]);
-const flying = computed(() => store.getters["flying"]);
+const metadata = computed(() => store.getters['scene/metadata']);
+const flying = computed(() => store.getters['flying']);
 const controls = computed(() => {
   return metadata.value.controls;
 });
-const player = computed(() => store.getters["player"]);
-const mode = computed(() => store.getters["mode"]);
+const player = computed(() => store.getters['player']);
+const mode = computed(() => store.getters['mode']);
 
 let timer = null;
 
 const panelPage = computed(() => {
-  let status = store.getters["tour/isPlay"] ? Guide :  vMain;
-  return store.getters["shoppingguide"] ? guideShop : status;
+  // let status = store.getters["tour/isPlay"] ? Guide :  vMain;
+  let status = store.getters['tour/isHome'] ? vMain : Guide;
+  return store.getters['shoppingguide'] ? guideShop : status;
 });
 
-
-
-const modeTips = ref("");
+const modeTips = ref('');
 const isCollapse = ref(false);
 const progressNum = ref(0);
 const isInit = ref(false);
-const tours = computed(() => store.getters["tour/tours"]);
+const tours = computed(() => store.getters['tour/tours']);
 
 const isSelect = ref(false);
 const labelTimer = ref(null);
-const showTours = computed(() => store.getters["tour/showTours"]);
+const showTours = computed(() => store.getters['tour/showTours']);
 const partId = computed(() => {
-  let id = store.getters["tour/partId"];
+  let id = store.getters['tour/partId'];
   return id;
 });
 
 const frameId = computed(() => {
-  let id = store.getters["tour/frameId"];
+  let id = store.getters['tour/frameId'];
   return id;
 });
 const isPlay = computed(() => {
-  let status = store.getters["tour/isPlay"];
-  let map = document.querySelector(".kankan-app div[xui_min_map]");
+  let status = store.getters['tour/isPlay'];
+  let map = document.querySelector('.kankan-app div[xui_min_map]');
   if (map) {
     if (status) {
-      map.classList.add("disabled");
+      map.classList.add('disabled');
     } else {
-      map.classList.remove("disabled");
+      map.classList.remove('disabled');
     }
   }
   return status;
 });
 const onModeSwitch = (mode) => {
   if (labelTimer.value) {
-    modeTips.value = "";
+    modeTips.value = '';
     clearTimeout(labelTimer.value);
   }
   modeTips.value = mode;
   labelTimer.value = setTimeout(() => {
-    modeTips.value = "";
+    modeTips.value = '';
   }, 2000);
   console.log(mode);
   // flyToNewMode(mode)
-  store.commit("setMode", mode);
+  store.commit('setMode', mode);
 };
 
 const onCollapse = () => {
   if (player.value.showToolbar == false) {
     isCollapse.value = false;
     let show = !player.value.showToolbar;
-    store.commit("SetPlayerOptions", {
+    store.commit('SetPlayerOptions', {
       showMore: false,
       showMap: show == false,
       showToolbar: show,
@@ -99,11 +95,10 @@ const onCollapse = () => {
     });
   } else {
     isCollapse.value = !isCollapse.value;
-    store.commit("tour/setData", { showTours: false });
+    store.commit('tour/setData', { showTours: false });
   }
 };
 
-
 const hanlderTourPartPlay = (time) => {
   if (!timer) {
     timer = KanKan.Animate.transitions.start((progress) => {
@@ -120,58 +115,58 @@ const cancelTimer = () => {
 
 const hanlderTour = async () => {
   let player = await getApp().TourManager.player;
-  player.on("play", (data) => {
-    musicPlayer.pause(true);
-    // if (tours.value.length > 1) {
-    //     let time = getPartTime(data.partId)
-    //     hanlderTourPartPlay(time)
-    // }
-  });
-  player.on("pause", (tours) => {
-    console.log("pause", player);
-    musicPlayer.resume();
-
-    progressNum.value = 0;
-    cancelTimer();
-    store.commit("tour/setData", { isPlay: false });
-  });
-  player.on("end", (tours) => {
-    musicPlayer.resume();
-    progressNum.value = 100;
-    store.commit("tour/setData", { isPlay: false });
-    cancelTimer();
-  });
+  // player.on("play", (data) => {
+  //   musicPlayer.pause(true);
+  //   // if (tours.value.length > 1) {
+  //   //     let time = getPartTime(data.partId)
+  //   //     hanlderTourPartPlay(time)
+  //   // }
+  // });
+  // player.on("pause", (tours) => {
+  //   console.log("pause", player);
+  //   musicPlayer.resume();
+
+  //   progressNum.value = 0;
+  //   cancelTimer();
+  //   store.commit("tour/setData", { isPlay: false });
+  // });
+  // player.on("end", (tours) => {
+  //   musicPlayer.resume();
+  //   progressNum.value = 100;
+  //   store.commit("tour/setData", { isPlay: false });
+  //   cancelTimer();
+  // });
   let currPartId = null;
   let currProgress = 0;
   let currFrames = 0;
-  player.on("progress", (data) => {
-    if (tours.value.length == 1) {
-      progressNum.value = data.progress * 100;
-    } else {
-      // let time = getPartTime(data.partId)
-
-      // hanlderTourPartPlay(time)
-
-      if (currPartId != data.partId) {
-        currPartId = data.partId;
-        currFrames = tours.value[data.partId].list.length;
-        currProgress = 0;
-      } else {
-        currProgress += data.progress / currFrames;
-        if (currProgress >= 100) {
-          currProgress = 100;
-        }
-
-        progressNum.value = currProgress;
-      }
-    }
+  //   player.on("progress", (data) => {
+  //     if (tours.value.length == 1) {
+  //       progressNum.value = data.progress * 100;
+  //     } else {
+  //       // let time = getPartTime(data.partId)
+
+  //       // hanlderTourPartPlay(time)
+
+  //       if (currPartId != data.partId) {
+  //         currPartId = data.partId;
+  //         currFrames = tours.value[data.partId].list.length;
+  //         currProgress = 0;
+  //       } else {
+  //         currProgress += data.progress / currFrames;
+  //         if (currProgress >= 100) {
+  //           currProgress = 100;
+  //         }
+
+  //         progressNum.value = currProgress;
+  //       }
+  //     }
 
-    store.commit("tour/setData", { partId: data.partId, frameId: data.frameId, isPlay: true });
-  });
+  //     store.commit("tour/setData", { partId: data.partId, frameId: data.frameId, isPlay: true });
+  //   });
 
-  // nextTick(() => {
-  //     editorMain.value = document.querySelector('.ui-editor-main')
-  // })
+  //   // nextTick(() => {
+  //   //     editorMain.value = document.querySelector('.ui-editor-main')
+  //   // })
 };
 const getPartTime = (partId) => {
   cancelTimer();
@@ -201,8 +196,7 @@ const getPartTime = (partId) => {
 const openTours = () => {
   // showTours.value = !showTours.value
 
-  store.commit("tour/setData", { showTours: !showTours.value });
- 
+  store.commit('tour/setData', { showTours: !showTours.value });
 };
 const changeFrame = async (type, id) => {
   if (flying.value || isSelect.value) {
@@ -222,7 +216,7 @@ const changeFrame = async (type, id) => {
     player.selectFrame(f_id).then(() => {
       isSelect.value = false;
     });
-    store.commit("tour/setData", {
+    store.commit('tour/setData', {
       frameId: f_id,
       partId: id,
     });
@@ -230,27 +224,26 @@ const changeFrame = async (type, id) => {
     player.selectFrame(id).then(() => {
       isSelect.value = false;
     });
-    store.commit("tour/setData", {
+    store.commit('tour/setData', {
       frameId: id,
     });
   }
-
 };
 const onClickHandler = async () => {
   if (isPlay.value) {
     let player = await getApp().TourManager.player;
-    player.pause();
+    // player.pause();
     musicPlayer.resume();
   }
 };
 onMounted(() => {
   useApp().then(async (sdk) => {
-    hanlderTour();
+    // hanlderTour();
   });
 
   nextTick(() => {
     let player = document.querySelector('.player[name="main"]');
-    player.addEventListener("touchstart", onClickHandler);
+    player.addEventListener('touchstart', onClickHandler);
   });
 });
 </script>
@@ -260,7 +253,7 @@ onMounted(() => {
   opacity: 1;
 }
 
-.limitwidth{
+.limitwidth {
   max-width: 500px;
   margin: 0 auto;
 }
@@ -301,7 +294,7 @@ onMounted(() => {
       width: 0.8rem;
       margin: 0 0.3rem;
       &::after {
-        content: "";
+        content: '';
         position: absolute;
         top: 50%;
         right: -0.3rem;
@@ -365,4 +358,3 @@ onMounted(() => {
   }
 }
 </style>
-

+ 191 - 156
src/components/Controls/Panel/Guide.vue

@@ -1,11 +1,12 @@
 <template>
   <div v-show="player.showWidgets" class="root-panel">
-    <div class="guide-panel" >
+    <div class="guide-panel" :class="{ disable: disable }">
       <div class="g-con">
-        
         <div class="swiper-container" id="sw-guide">
-          <ul class="swiper-wrapper"  v-if="tours.length > 1">
+          <!-- <ul class="swiper-wrapper" v-if="tours.length > 1"> -->
+          <ul class="swiper-wrapper">
             <li
+              @click.stop="changeItem('part', index)"
               class="swiper-slide"
               :style="`background-image:url(${i.frameId ? common.changeUrl(i.list[i.frameId].enter.cover) : common.changeUrl(i.list[0].enter.cover)});`"
               :class="{ active: isPlay && partId == index }"
@@ -17,8 +18,9 @@
               <span v-if="partId == index && progressNum > 0" class="bar" :style="{ '--w': progressNum + '%' }"></span>
             </li>
           </ul>
-          <ul class="swiper-wrapper" v-else>
+          <!-- <ul class="swiper-wrapper" v-else>
             <li
+              @click.stop="changeItem('frame', index)"
               class="swiper-slide"
               :style="`background-image:url(${common.changeUrl(i.enter.cover)});`"
               :class="{ active: isPlay && frameId == index }"
@@ -29,11 +31,17 @@
 
               <span v-if="frameId == index && progressNum > 0" class="bar" :style="{ '--w': progressNum + '%' }"></span>
             </li>
-          </ul>
+          </ul> -->
         </div>
-        <div class="back" @click.stop="playTour">
-          <ui-icon type="back"></ui-icon>
-          <div>返回</div>
+        <div class="back">
+          <div @click.stop="back">
+            <ui-icon type="back"></ui-icon>
+            <div>{{ $t('common.back') }}</div>
+          </div>
+          <div @click.stop="playTour">
+            <ui-icon :type="isPlay ? 'pause' : 'preview'"></ui-icon>
+            <div>{{ isPlay ? $t('common.pause') : $t('common.play') }}</div>
+          </div>
         </div>
       </div>
     </div>
@@ -41,111 +49,117 @@
 </template>
 
 <script setup>
-import { useStore } from "vuex";
-import common from "@/utils/common";
-import { onMounted, watch, computed, ref, nextTick } from "vue";
-import { useApp, getApp } from "@/app";
-import { useMusicPlayer } from "@/utils/sound";
-let timer = null
+import { useStore } from 'vuex';
+import common from '@/utils/common';
+import { onMounted, watch, computed, ref, nextTick } from 'vue';
+import { useApp, getApp } from '@/app';
+import { useMusicPlayer } from '@/utils/sound';
+
+import { useI18n, getLocale } from '@/i18n';
+const { t } = useI18n({ useScope: 'global' });
+
+let timer = null;
 
 const store = useStore();
 const musicPlayer = useMusicPlayer();
-const flying = computed(() => store.getters['flying'])
-const isSelect = ref(false)
+const isSelect = ref(false);
+const disable = ref(false);
 
 const isPlay = computed(() => {
-  let status = store.getters["tour/isPlay"];
-  let map = document.querySelector(".kankan-app div[xui_min_map]");
+  let status = store.getters['tour/isPlay'];
+  let map = document.querySelector('.kankan-app div[xui_min_map]');
   if (map) {
     if (status) {
-      map.classList.add("disabled");
+      map.classList.add('disabled');
     } else {
-      map.classList.remove("disabled");
+      map.classList.remove('disabled');
     }
   }
   return status;
 });
 
-const partId = computed(() => store.getters["tour/partId"]);
-const frameId = computed(() => store.getters["tour/frameId"]);
+const partId = computed(() => store.getters['tour/partId']);
+const frameId = computed(() => store.getters['tour/frameId']);
+const changeItem = async (type, id) => {
+  disable.value = true;
+  let player = await getApp().TourManager.player;
+  if (type == 'part') {
+    store.commit('tour/setData', {
+      frameId: 0,
+      partId: id,
+    });
+    progressNum.value = 0;
 
+    player.selectPart(id).then(() => {
+      disable.value = false;
+      if (!isPlay.value) {
+        return;
+      }
+      store.commit('tour/setData', {
+        isPlay: true,
+      });
+      player.play(id);
+    });
+  } else {
+    player.selectFrame(id).then(() => {
+      disable.value = false;
+
+      store.commit('tour/setData', {
+        frameId: id,
+      });
+    });
+  }
+  // mySwiper.slideTo(id);
+  // cancelTimer();
+  // store.commit('tour/setData', { isPlay: false });
+
+  // slideScroll()
+};
+
+const back = async () => {
+  let player = await getApp().TourManager.player;
+  if (isPlay.value) {
+    store.commit('tour/setData', { isPlay: false });
+    player.pause();
+  }
+  store.commit('tour/setData', {
+    partId: 0,
+    isHome: true,
+  });
+};
 const playTour = async () => {
+  progressNum.value = 0;
   let player = await getApp().TourManager.player;
   if (isPlay.value) {
-    store.commit("tour/setData", { isPlay: true });
+    store.commit('tour/setData', { isPlay: false });
     player.pause();
   } else {
-    store.commit("tour/setData", { isPlay: true });
+    store.commit('tour/setData', { isPlay: true });
     player.play(partId.value);
   }
 };
 
 const progressNum = ref(0);
 
-const metadata = computed(() => store.getters["scene/metadata"]);
+const metadata = computed(() => store.getters['scene/metadata']);
 
 const controls = computed(() => metadata.value.controls);
 
-const player = computed(() => store.getters["player"]);
-
-const tours = computed(() => store.getters["tour/tours"]);
-
-const menulist = ref([
-  {
-    icon: "customer_service",
-    name: "客服",
-  },
-  {
-    icon: "guided_shopping",
-    name: "一起逛",
-  },
-  {
-    icon: "shopping",
-    name: "購物",
-  },
-]);
-
-const categorylist = ref([
-  {
-    id: "all",
-    name: "全部",
-  },
-  {
-    id: "all",
-    name: "香化",
-  },
-  {
-    id: "all",
-    name: "香化",
-  },
-  {
-    id: "all",
-    name: "香化",
-  },
-  {
-    id: "all",
-    name: "香化",
-  },
-  {
-    id: "all",
-    name: "全部",
-  },
-  {
-    id: "all",
-    name: "全部",
-  },
-]);
+const player = computed(() => store.getters['player']);
 
-const brandlist = ref([]);
+const tours = computed(() => store.getters['tour/tours']);
 
+const brandlist = ref([]);
+let mySwiper = null;
 const brandScroll = () => {
   nextTick(() => {
     let t = setTimeout(() => {
       clearTimeout(t);
-      new Swiper("#sw-guide", {
+      mySwiper = new Swiper('#sw-guide', {
         freeMode: true,
-        slidesPerView: "auto",
+        slidesPerView: 'auto',
         spaceBetween: 6,
+
         on: {
           touchMove(swiper, e) {
             e.stopPropagation();
@@ -157,120 +171,137 @@ const brandScroll = () => {
   });
 };
 
-const changeFrame = async (type, id) => {
-    if (flying.value || isSelect.value) {
-        return
-    }
-    progressNum.value = 0
-    // recorder.selectFrame(id)
-    let player = await getApp().TourManager.player
-    // player.selectFrame(id)
-    isSelect.value = true
-    if (type == 1) {
-        player.selectPart(id)
-        let f_id = 0
-        if (tours.value[id].frameId) {
-            f_id = tours.value[id].frameId
-        }
-        player.selectFrame(f_id).then(() => {
-            isSelect.value = false
-        })
-        store.commit('tour/setData', {
-            frameId: f_id,
-            partId: id,
-        })
-    } else {
-        player.selectFrame(id).then(() => {
-            isSelect.value = false
-        })
-        store.commit('tour/setData', {
-            frameId: id,
-        })
-    }
-
-}
 const onClickHandler = async () => {
-    if (isPlay.value) {
-        let player = await getApp().TourManager.player
-        player.pause()
-        musicPlayer.resume()
-    }
-}
+  console.log('onClickHandler');
+  if (isPlay.value) {
+    let player = await getApp().TourManager.player;
+    player.pause();
+    musicPlayer.resume();
+  }
+};
 const cancelTimer = () => {
-    if (timer) {
-        KanKan.Animate.transitions.cancel(timer)
-        timer = null
-    }
-}
+  if (timer) {
+    KanKan.Animate.transitions.cancel(timer);
+    timer = null;
+  }
+};
 
 const hanlderTour = async () => {
   let player = await getApp().TourManager.player;
-  player.on("play", (data) => {
+  player.on('play', (data) => {
+    progressNum.value = 0;
     musicPlayer.pause(true);
+
     // if (tours.value.length > 1) {
     //     let time = getPartTime(data.partId)
     //     hanlderTourPartPlay(time)
     // }
   });
-  player.on("pause", (tours) => {
-    console.log("pause", player);
+  player.on('pause', (tours) => {
+    // console.log('pause', player);
     musicPlayer.resume();
-
+    currProgress = 0;
+    currFrameProgress = 0;
     progressNum.value = 0;
     cancelTimer();
-    store.commit("tour/setData", { isPlay: false });
+    store.commit('tour/setData', { isPlay: false });
   });
-  player.on("end", (tours) => {
+  player.on('end', (tours) => {
     musicPlayer.resume();
     progressNum.value = 100;
-    store.commit("tour/setData", { isPlay: false });
+    currProgress = 0;
+    currFrameProgress = 0;
+    store.commit('tour/setData', { isPlay: false });
     cancelTimer();
   });
   let currPartId = null;
+  let currFrameId = null;
   let currProgress = 0;
-  let currFrames = 0;
-  player.on("progress", (data) => {
-    if (tours.value.length == 1) {
-      progressNum.value = data.progress * 100;
-    } else {
-      // let time = getPartTime(data.partId)
+  let FrameLength = 0;
+  let currFrameProgress = 0;
+  // player.on('progress', (data) => {
+  //   // if (tours.value.length == 1) {
+  //   //   progressNum.value = data.progress * 100;
+  //   // } else {
 
-      // hanlderTourPartPlay(time)
+  //   if (currPartId != data.partId) {
+  //     mySwiper.slideTo(data.partId);
+  //     currPartId = data.partId;
+  //     FrameLength = Math.max(tours.value[data.partId].list.length - 1, 1);
+  //     currProgress = 0;
+  //   } else {
+  //     console.log(FrameLength);
+  //     currProgress += data.progress / FrameLength;
+  //     if (currProgress >= 100) {
+  //       currProgress = 100;
+  //     }
 
-      if (currPartId != data.partId) {
-        currPartId = data.partId;
-        currFrames = Math.max(tours.value[data.partId].list.length-1,1);
-        currProgress = 0;
+  //     console.log(currProgress);
+
+  //     progressNum.value = currProgress;
+  //   }
+  //   // }
+
+  //   store.commit('tour/setData', { partId: data.partId, frameId: data.frameId, isPlay: true });
+  // });
+
+  player.on('progress', (data) => {
+    // if (tours.value.length == 1) {
+    //     progressNum.value = data.progress * 100
+    // } else {
+    // let time = getPartTime(data.partId)
+
+    // hanlderTourPartPlay(time)
+
+    if (currPartId != data.partId) {
+      mySwiper.slideTo(data.partId);
+      currPartId = data.partId;
+      FrameLength = tours.value[data.partId].list.length;
+      currProgress = 0;
+      currFrameProgress = 0;
+    } else {
+      FrameLength = tours.value[data.partId].list.length;
+      let precent = (data.frameId + 1) / FrameLength;
+
+      if (currFrameId != data.frameId) {
+        currFrameId = data.frameId;
+        currFrameProgress = currProgress;
       } else {
-        console.log(currFrames);
-        currProgress += data.progress / currFrames;
-        if (currProgress >= 100) {
-          currProgress = 100;
-        }
+        currProgress = (data.progress / FrameLength) * 100 + currFrameProgress;
+      }
+      // currProgress = data.progress / FrameLength;
 
-        console.log(currProgress);
+      // console.log(currFrameProgress);
 
-        progressNum.value = currProgress;
+      if (currProgress >= 100) {
+        currProgress = 100;
       }
+
+      progressNum.value = currProgress;
     }
+    // }
 
-    store.commit("tour/setData", { partId: data.partId, frameId: data.frameId, isPlay: true });
+    store.commit('tour/setData', {
+      partId: data.partId,
+      frameId: data.frameId,
+      isPlay: true,
+    });
   });
 
   // nextTick(() => {
   //     editorMain.value = document.querySelector('.ui-editor-main')
   // })
 };
-
 onMounted(() => {
   useApp().then(async (sdk) => {
     hanlderTour();
     brandScroll();
   });
   nextTick(() => {
-        let player = document.querySelector('.player[name="main"]')
-        player.addEventListener('touchstart', onClickHandler)
-    })
+    let player = document.querySelector('.player[name="main"]');
+    player.addEventListener('touchstart', onClickHandler);
+    player.addEventListener('click', onClickHandler);
+  });
 });
 </script>
 
@@ -282,11 +313,15 @@ onMounted(() => {
   right: 0;
   z-index: 99;
   width: 100%;
-  background: rgba(0,0,0,0.3000);
-  border: 1px solid rgba(255,255,255,0.2000);
+  background: rgba(0, 0, 0, 0.3);
+  border: 1px solid rgba(255, 255, 255, 0.2);
   .guide-panel {
     width: 100%;
     position: relative;
+    &.disable {
+      opacity: 0.8;
+      pointer-events: none;
+    }
     .g-con {
       background: rgba(0, 0, 0, 0.3);
       border-radius: 4px;
@@ -307,12 +342,12 @@ onMounted(() => {
         display: flex;
         justify-content: center;
         align-items: center;
-        border-top: solid 1px rgba(255,255,255,0.2000);
+        border-top: solid 1px rgba(255, 255, 255, 0.2);
         height: 48px;
 
         > div {
           font-size: 14px;
-          margin-left: 10px;
+          margin-left: 20px;
         }
       }
       #sw-guide {
@@ -356,7 +391,7 @@ onMounted(() => {
                 left: 0;
                 width: 100%;
                 height: 100%;
-                content: "";
+                content: '';
                 display: inline-block;
                 background: rgba(24, 24, 24, 0.5);
               }
@@ -374,7 +409,7 @@ onMounted(() => {
                 left: 50%;
                 transform: translateX(-50%);
                 &::after {
-                  content: "";
+                  content: '';
                   width: var(--w);
                   background: var(--editor-main-color);
                   height: 100%;

+ 74 - 75
src/components/Controls/Panel/Main.vue

@@ -1,18 +1,17 @@
 <template>
   <div>
-              
-    <Panel v-show="player.showWidgets" :class="{disabled:!showdaogou}" :isOpen="isOpen">
+    <Panel v-show="player.showWidgets" :class="{ disabled: !showdaogou }" :isOpen="isOpen">
       <div @click="toggleOpen" class="menu color">
         <div class="logo">
           <img :src="require('@/assets/images/icon/logo.png')" alt="" />
-          <p>cdf澳門上葡京店</p>
+          <p v-if="player.lang != 'en'">{{$t('common.title') }}</p>
         </div>
         <div class="vline"></div>
-        <ul>
-          <!-- <li v-if="tours.length > 0">
+        <ul :class="player.lang" @touchmove.stop>
+          <li v-if="tours.length > 0">
             <ui-icon type="preview" @click.stop="playTour"></ui-icon>
-            <div>導覽</div>
-          </li> -->
+            <div>{{$t('common.tour')}}</div>
+          </li>
           <li @click.stop="onClickMenu(item)" v-for="(item, i) in menulist" :key="i">
             <ui-icon :type="item.icon"></ui-icon>
             <div>{{ item.name }}</div>
@@ -25,11 +24,11 @@
                 onClickMenu({
                   icon: 'guided_shopping',
                   id: 'guided_shopping',
-                  name: '一起逛',
+                  name: $t('common.mode'),
                 })
               "
             ></ui-icon>
-            <div>一起逛</div>
+            <div>{{ $t('common.mode') }}</div>
           </li>
 
           <li>
@@ -39,11 +38,11 @@
                 onClickMenu({
                   icon: 'shopping',
                   id: 'shopping',
-                  name: '購物',
+                  name: $t('common.shopping'),
                 })
               "
             ></ui-icon>
-            <div>購物</div>
+            <div>{{ $t('common.shopping') }}</div>
           </li>
         </ul>
       </div>
@@ -51,10 +50,10 @@
       <div class="toolbar color">
         <div class="navigation">
           <div class="h3">
-            <span>專櫃導航</span>
+            <span>{{ $t('common.guide') }}</span>
             <div @click="showShopList = true">
               <img :src="require(`@/assets/images/icon/search.svg`)" alt="" />
-              搜索專櫃
+              {{ $t('common.searchguide') }}
             </div>
           </div>
           <div class="swiper-container" id="sw-navigation">
@@ -86,19 +85,13 @@
                 @click.stop="
                   onClickCategory({
                     id: '',
-                    categoryName: '全部',
+                    categoryName: $t('common.all'),
                   })
                 "
               >
-                <div>全部</div>
+                <div>{{ $t('common.all') }}</div>
               </li>
-              <li
-                @click.stop="onClickCategory(item)"
-                :class="{ categoryactive: item.id == currentCategory.id }"
-                class="swiper-slide"
-                v-for="(item, i) in categorylist"
-                :key="i"
-              >
+              <li @click.stop="onClickCategory(item)" :class="{ categoryactive: item.id == currentCategory.id }" class="swiper-slide" v-for="(item, i) in categorylist" :key="i">
                 <div>{{ item.categoryName }}</div>
               </li>
             </ul>
@@ -109,13 +102,13 @@
     <teleport :to="`#app`">
       <div v-show="showShopList" class="shoplist">
         <div class="l-title">
-          專櫃列表 ({{ searchList.length }})
+          {{ $t('common.guidelist') }} ({{ searchList.length }})
           <ui-icon type="close" @click="showShopList = false" />
         </div>
 
         <div class="search">
           <img :src="require(`@/assets/images/icon/search.svg`)" alt="" />
-          <input v-model="searchKey" type="text" placeholder="專櫃搜索" />
+          <input v-model="searchKey" type="text" :placeholder="$t('common.guideSearch')" />
           <ui-icon class="close" v-if="searchKey.length > 0" @click.stop="searchKey = ''" type="state_f"></ui-icon>
         </div>
 
@@ -131,24 +124,26 @@
           </li>
         </ul>
 
-        <div class="noresult" v-else>暫無結果</div>
+        <div class="noresult" v-else>{{ $t('common.noResult') }}</div>
       </div>
     </teleport>
   </div>
 </template>
 
 <script setup>
-import { useStore } from "vuex";
-import { onMounted, watch, computed, ref, nextTick } from "vue";
-import Panel from "@/views/Panel.vue";
-import { useApp, getApp } from "@/app";
-import * as apis from "@/apis/index.js";
-import browser from "@/utils/browser";
+import { useStore } from 'vuex';
+import { onMounted, watch, computed, ref, nextTick } from 'vue';
+import Panel from '@/views/Panel.vue';
+import { useApp, getApp } from '@/app';
+import * as apis from '@/apis/index.js';
+import browser from '@/utils/browser';
+import { useI18n, getLocale } from '@/i18n';
+const { t } = useI18n({ useScope: 'global' });
 
 const store = useStore();
 
 const isOpen = ref(false);
-const searchKey = ref("");
+const searchKey = ref('');
 
 const showShopList = ref(false);
 
@@ -157,57 +152,57 @@ const toggleOpen = () => {
 };
 
 const currentCategory = ref({
-  id: "",
-  categoryName: "全部",
+  id: '',
+  categoryName: '全部',
 });
 
-const currentM = computed(() => browser.getURLParam("m"));
-const currentPose = computed(() => browser.getURLParam("pose"));
-const showdaogou = computed(() => store.getters["rtc/showdaogou"]);
+const currentM = computed(() => browser.getURLParam('m'));
+const currentPose = computed(() => browser.getURLParam('pose'));
+const showdaogou = computed(() => store.getters['rtc/showdaogou']);
 
 const isPlay = computed(() => {
-  let status = store.getters["tour/isPlay"];
-  let map = document.querySelector(".kankan-app div[xui_min_map]");
+  let status = store.getters['tour/isPlay'];
+  let map = document.querySelector('.kankan-app div[xui_min_map]');
   if (map) {
     if (status) {
-      map.classList.add("disabled");
+      map.classList.add('disabled');
     } else {
-      map.classList.remove("disabled");
+      map.classList.remove('disabled');
     }
   }
   return status;
 });
 
-const partId = computed(() => store.getters["tour/partId"]);
+const partId = computed(() => store.getters['tour/partId']);
 
 const playTour = async () => {
   let player = await getApp().TourManager.player;
   if (isPlay.value) {
-    store.commit("tour/setData", { isPlay: true });
+    store.commit('tour/setData', { isHome: true });
     player.pause();
   } else {
-    store.commit("tour/setData", { isPlay: true });
+    store.commit('tour/setData', { isHome: false });
     player.play(partId.value);
   }
 };
 
-const metadata = computed(() => store.getters["scene/metadata"]);
+const metadata = computed(() => store.getters['scene/metadata']);
 
-const player = computed(() => store.getters["player"]);
+const player = computed(() => store.getters['player']);
 
-const tours = computed(() => store.getters["tour/tours"]);
+const tours = computed(() => store.getters['tour/tours']);
 
 const menulist = computed(() => {
   let fff = [
     {
-      icon: "help",
-      id: "help",
-      name: "幫助",
+      icon: 'help',
+      id: 'help',
+      name: t('common.help'),
     },
     {
-      icon: "customer_service",
-      id: "kefu",
-      name: "客服",
+      icon: 'customer_service',
+      id: 'kefu',
+      name: t('common.kefu'),
     },
   ];
 
@@ -227,9 +222,9 @@ const brandScroll = () => {
   nextTick(() => {
     let t = setTimeout(() => {
       clearTimeout(t);
-      new Swiper("#sw-navigation", {
+      new Swiper('#sw-navigation', {
         freeMode: true,
-        slidesPerView: "auto",
+        slidesPerView: 'auto',
         centeredSlides: false,
         spaceBetween: 10,
         grid: {
@@ -243,9 +238,9 @@ const brandScroll = () => {
         },
       });
 
-      new Swiper("#sw-category", {
+      new Swiper('#sw-category', {
         freeMode: true,
-        slidesPerView: "auto",
+        slidesPerView: 'auto',
         spaceBetween: 10,
         on: {
           touchMove(swiper, e) {
@@ -259,20 +254,16 @@ const brandScroll = () => {
 };
 
 const onClickMenu = (item) => {
-  if (item.id == "kefu") {
+  if (item.id == 'kefu') {
     let mglink =
-      "https://webpage.qidian.qq.com/2/chat/h5/index.html?linkType=1&env=ol&kfuin=3009110132&fid=3655&key=9b4334768c39150ead3f23e11e5dc2e4&cate=7&source=0&isLBS=0&isCustomEntry=0&type=10&ftype=1&_type=wpa&qidian=true&_pid=kvrmvu.74cg11.l43qvbcu&translateSwitch=0&isSsc=0&roleValue=4&roleData=922223821";
-    window.open(mglink, "_blank");
-  } else if (item.id == "shopping") {
-    browser.openLink(
-      "/subPackage/pages/shoppingcart/shoppingcart",
-      "https://m.cdfmembers.com/shop/600667208/shoppingcart",
-      "/subPackage/pages/shoppingcart/shoppingcart"
-    );
-  } else if (item.id == "help") {
-    store.commit("showUserGuide", true);
-  } else if (item.id == "guided_shopping") {
-    store.commit("showShoppingguide", true);
+      'https://webpage.qidian.qq.com/2/chat/h5/index.html?linkType=1&env=ol&kfuin=3009110132&fid=3655&key=9b4334768c39150ead3f23e11e5dc2e4&cate=7&source=0&isLBS=0&isCustomEntry=0&type=10&ftype=1&_type=wpa&qidian=true&_pid=kvrmvu.74cg11.l43qvbcu&translateSwitch=0&isSsc=0&roleValue=4&roleData=922223821';
+    window.open(mglink, '_blank');
+  } else if (item.id == 'shopping') {
+    browser.openLink('/subPackage/pages/shoppingcart/shoppingcart', 'https://m.cdfmembers.com/shop/600667208/shoppingcart', '/subPackage/pages/shoppingcart/shoppingcart');
+  } else if (item.id == 'help') {
+    store.commit('showUserGuide', true);
+  } else if (item.id == 'guided_shopping') {
+    store.commit('showShoppingguide', true);
   }
 };
 
@@ -290,13 +281,13 @@ const onClickCategory = (item) => {
 const onClickShop = (item) => {
   let url = window.location.href;
 
-  if (!browser.hasURLParam("pose")) {
+  if (!browser.hasURLParam('pose')) {
     url += `&${item.inPosition}`;
   } else {
-    url = browser.replaceQueryString(url, "pose", item.inPosition.replace("pose=", ""));
+    url = browser.replaceQueryString(url, 'pose', item.inPosition.replace('pose=', ''));
   }
 
-  url = browser.replaceQueryString(url, "m", item.sceneUrl);
+  url = browser.replaceQueryString(url, 'm', item.sceneUrl);
   window.location.href = url;
 };
 
@@ -358,6 +349,8 @@ onMounted(() => {
   .logo {
     width: 90px;
     transform: translateY(-24%);
+    min-width: 90px;
+    flex-shrink: 0;
     > img {
       width: 100%;
       border-radius: 4px;
@@ -377,8 +370,9 @@ onMounted(() => {
     align-items: center;
     font-size: 0;
     justify-content: flex-end;
+    overflow-x: auto;
     > li {
-      margin-left: 0.5rem;
+      margin-left: 0.4rem;
       &:first-of-type {
         margin-left: 0;
       }
@@ -388,6 +382,11 @@ onMounted(() => {
       }
     }
   }
+
+  .en{
+    justify-content: flex-start;
+    width: calc(100% - 100px);
+  }
 }
 
 .toolbar {
@@ -434,7 +433,7 @@ onMounted(() => {
         position: absolute;
         right: 0;
         bottom: 0;
-        content: "";
+        content: '';
         display: inline-block;
         height: 100%;
         z-index: 99;

File diff suppressed because it is too large
+ 819 - 678
src/components/Information/View.Mobile.vue


+ 30 - 28
src/components/RTC/PageRtcLive.vue

@@ -2,7 +2,7 @@
   <div id="PageRtcLive">
     <div class="member_number">
       <div class="members"></div>
-      <span>{{ user_list.length }}觀看</span>
+      <span>{{ user_list.length }}{{$t('common.viewnum')}}</span>
     </div>
     <chat v-show="chatShow" :chatList="chatList" :user_info="user_info"></chat>
 
@@ -12,12 +12,12 @@
       <div v-if="connectStatus == 1" :class="{ disabled: !user_info.IsWords }" class="saySomething" @click="onFocus">
         <!-- <i class="speakIcon"
            :class="{'dis':!user_info.IsWords}"></i> -->
-        <span v-if="user_info.IsWords">說點什麼</span>
-        <span v-if="!user_info.IsWords">已被禁言</span>
+        <span v-if="user_info.IsWords">{{$t('common.saysomething')}}</span>
+        <span v-if="!user_info.IsWords">{{$t('common.cantsay')}}</span>
 
         <div class="disSpeakBtn" @click.stop="chatShow = !chatShow" :class="{ dis: !chatShow }"></div>
       </div>
-      <div style="text-align: right; width: 100%" v-if="connectStatus == 0">連接中...</div>
+      <div style="text-align: right; width: 100%" v-if="connectStatus == 0">{{$t('common.linking')}}</div>
       <div v-if="connectStatus == 1" class="contorl_btn">
         <div v-if="isBrushes && user_info.Role == 'leader'" @click="onDrawUndo" class="brushesBack" :class="{ disabled: !canUndo }"></div>
         <div v-if="user_info.Role == 'leader'" @click="onDraw(!isBrushes)" :class="{ brushesed: isBrushes }" class="brushes"></div>
@@ -33,8 +33,8 @@
     <div class="layer" v-if="showInput" @click="closeInput">
       <div class="inputBox" @click.stop>
         <div class="msgBox">
-          <input id="input_msg" type="text" maxlength="200" v-model.trim="text" :placeholder="`說點什麼~`" />
-          <span class="iconsend_icon" :class="{ disable: text == '' }" @click.stop="sendText">發送</span>
+          <input id="input_msg" type="text" maxlength="200" v-model.trim="text" :placeholder="`${$t('common.saysomething')}~`" />
+          <span class="iconsend_icon" :class="{ disable: text == '' }" @click.stop="sendText">{{$t('common.send')}}</span>
         </div>
       </div>
     </div>
@@ -45,7 +45,7 @@
         <div class="blurBox"></div>
         <div class="content">
           <div class="memberHeader">
-            <span> 成員管理({{ user_list.length }})</span>
+            <span> {{$t('common.member')}}({{ user_list.length }})</span>
             <i class="iconfont"></i>
           </div>
           <div class="memberList">
@@ -54,8 +54,8 @@
                 <div class="avatar">
                   <img :src="require('@/assets/images/rtcLive/avatar_small@2x.png')" alt="" />
                 </div>
-                <div class="name" v-if="user_info.Role == 'leader'">{{ user_info.Nickname }} (主持人,我)</div>
-                <div class="name" v-else>{{ user_info.Nickname }} ()</div>
+                <div class="name" v-if="user_info.Role == 'leader'">{{ user_info.Nickname }} ({{$t('common.leader')}},{{$t('common.me')}})</div>
+                <div class="name" v-else>{{ user_info.Nickname }} ({{$t('common.me')}})</div>
               </div>
 
               <div class="button" v-if="user_info.Role == 'leader'">
@@ -106,6 +106,9 @@ import Trtccom from "./Trtccom.vue";
 import browser from "@/utils/browser";
 import wxShare from "@/utils/wxshare";
 
+import { useI18n, getLocale } from '@/i18n'
+const { t } = useI18n({ useScope: 'global' })
+
 const emit = defineEmits(["openDialog", "closeSocket"]);
 
 const store = useStore();
@@ -146,7 +149,6 @@ let getUrl = (href, queryArr) => {
     if (!browser.hasURLParam(item.key)) {
       let ttt = href.split("index.html?");
       href = `${ttt[0]}index.html?${item.key}=${item.val}&${ttt[1]}`;
-      console.log(href, "------index.htmlindex.htmlindex.htmlindex.htmlindex.htmlindex.htmlindex.html----------");
     } else {
       href = browser.replaceQueryString(href, item.key, item.val);
     }
@@ -233,7 +235,7 @@ const userGetOut = (item, i) => {
 const setUserWords = (res) => {
   if (res.userId == user_info.value.UserId) {
     user_info.value.IsWords = res.words;
-    Dialog.toast({ content: !user_info.value.IsWords ? `主持人設置了禁言` : `主持人已解除禁言` });
+    Dialog.toast({ content: !user_info.value.IsWords ? t('common.setTaboo') : t('common.relieveTaboo') });
   }
 };
 
@@ -252,7 +254,7 @@ const setUserMuted = (res) => {
   if (res.userId) {
     if (res.userId == user_info.value.UserId && role.value == "customer") {
       user_info.value.IsMuted = res.muted;
-      Dialog.toast({ content: !user_info.value.IsMuted ? `主持人設置了開麥` : `主持人設置了靜音` });
+      Dialog.toast({ content: !user_info.value.IsMuted ?  t('common.openMic') : t('common.closeMic') });
       disableMic.value = res.muted;
       audioMuted.value = res.muted;
     }
@@ -282,7 +284,7 @@ const onAllMuted = (res) => {
     user_info.value.IsMuted = res.muted;
     item.IsMuted = res.muted;
     if (role.value == "customer") {
-      Dialog.toast({ content: !user_info.value.IsMuted ? `主持人設置了開麥` : `主持人設置了靜音` });
+      Dialog.toast({ content: !user_info.value.IsMuted ? t('common.openMic') : t('common.closeMic') });
       disableMic.value = res.muted;
       audioMuted.value = res.muted;
     }
@@ -302,8 +304,8 @@ const setUserJoin = async (res) => {
   // self.chekcLeaderInfo();
   let name = res.user.Nickname;
   if (res.user.Role == "leader") {
-    name = "主持人";
-    Dialog.toast({ content: `主持人進入房間` });
+    name = t('common.leader') ;
+    Dialog.toast({ content: t('common.leader')+t('common.inRoom') });
     socket.value.emit("action", { type: "user-init" });
   } 
   let data = {
@@ -311,7 +313,7 @@ const setUserJoin = async (res) => {
     mode: mode.value,
     Nickname: name,
     UserId: res.user.UserId,
-    text: "進入房間",
+    text: t('common.inRoom'),
   };
   chatList.value.push(data);
   await nextTick();
@@ -385,7 +387,7 @@ const sendText = async () => {
 const setReceiveMsg = async (res) => {
   console.log(res);
   if (res.role == "leader") {
-    res.Nickname = "主持人";
+    res.Nickname = t('common.leader');
   }
   chatList.value.push(res);
   await nextTick();
@@ -434,14 +436,14 @@ const onMemberLeave = async (res) => {
   let name = res.user.Nickname;
   if (res.user.Role == "leader") {
     name = "主持人";
-    Dialog.toast({ content: `主持人離開了房間` });
+    Dialog.toast({ content: t('common.leader') + t('common.leaveRoom') });
   }
   let data = {
     role: res.user.Role,
     mode: mode.value,
     Nickname: name,
     UserId: res.user.UserId,
-    text: "離開房間",
+    text: t('common.leaveRoom') ,
   };
   chatList.value.push(data);
   await nextTick();
@@ -458,7 +460,7 @@ const userCanSpeak = (item) => {
 const onGetOuT = (data) => {
   if (data.id == user_info.value.UserId) {
     emit("closeSocket");
-    Dialog.toast({ content: `您已被移除` });
+    Dialog.toast({ content: t('common.removed')});
   }
 };
 
@@ -499,7 +501,7 @@ const startFollow = (app) => {
     }, []);
 
     if (meblist.length > 5 && role.value == "customer") {
-      Dialog.toast({ content: `房間已滿員` });
+      Dialog.toast({ content: t('common.fullStarffed') });
       emit("closeSocket");
       return;
     }
@@ -549,8 +551,8 @@ const startFollow = (app) => {
 
     wxShare({
       donotconfig: true,
-      title: `【好友推薦】一起雲逛店吧~`,
-      desc: "【好友推薦】一起雲逛店吧~",
+      title: t('common.invitation'),
+      desc:  t('common.invitation'),
       link: shareLink.value,
       imgUrl: "https://glp-vr.cdfmembers.com/cdf/file/91dd5305525f463286f03a31abd1c154.jpg",
     });
@@ -561,7 +563,7 @@ const startFollow = (app) => {
         if (res.miniprogram) {
           wx.miniProgram.postMessage({
             data: {
-              title: "【好友推薦】一起雲逛店吧~",
+              title: t('common.invitation'),
               imageUrl: "https://glp-vr.cdfmembers.com/cdf/file/91dd5305525f463286f03a31abd1c154.jpg",
               h5Url: shareLink.value,
             },
@@ -602,7 +604,7 @@ const startFollow = (app) => {
   socket.value.on("action", (data) => {
     console.log(data,'=============');
     if (data.type == "error") {
-      Dialog.toast({ content: `房間未找到`, type: "error" });
+      Dialog.toast({ content: t('common.notFoundRoom'), type: "error" });
       emit("closeSocket");
     } else if (data.type == "danmumsg") {
       setReceiveMsg(data.data);
@@ -614,9 +616,9 @@ const startFollow = (app) => {
       onDraw(data.open);
       if (role.value == "customer") {
         if (data.open) {
-          Dialog.toast({ content: `主持人開啟畫筆` });
+          Dialog.toast({ content: t('common.leader')+t('common.openBrush')  });
         } else {
-          Dialog.toast({ content: `主持人關閉畫筆` });
+          Dialog.toast({ content:  t('common.leader')+t('common.closeBrush') });
         }
       }
     } else if (data.type == "user-join") {
@@ -632,7 +634,7 @@ const startFollow = (app) => {
       onMemberLeave(data);
     } else if (data.type == "leader-dismiss") {
       emit("closeSocket");
-      Dialog.toast({ content: `主持人已解散房間` });
+      Dialog.toast({ content: t('common.leader')+t('common.dismissRoom')  });
     } else if (data.type == "tagclick") {
       if (role.value == "customer") {
         let item = tags.value.find((item) => item.sid == data.data.sid);

+ 13 - 7
src/components/RTC/dialog/createdRoom.vue

@@ -3,15 +3,15 @@
     <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="dialog_title" v-if="role == 'leader'">{{ $t("common.createTour") }}</div>
+        <div class="dialog_title" v-else>{{ $t("common.joinTour") }}</div>
         <div class="user_name">
           <input
             class="input_name"
             maxlength="20"
             v-model.trim="userName"
             type="text"
-            :placeholder="role == 'leader' ? ' 請輸入發起人昵稱' : '請輸入您的昵稱'"
+            :placeholder="role == 'leader' ? $t('common.leadernickName') : $t('common.inputnickName')"
           />
           <span class="limitNum">{{ userName.length }}/20</span>
         </div>
@@ -19,14 +19,19 @@
           <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 class="created_cancel" @click="closeCreated">{{ $t("common.cancel") }}</div>
+          <div class="created_confirm" @click="createdConfirm">{{ $t("common.confirm") }}</div>
         </div>
       </div>
     </div>
   </div>
 </template>
 
+<script setup>
+import { useI18n, getLocale } from "@/i18n";
+const { t } = useI18n({ useScope: "global" });
+</script>
+
 <script>
 import { Dialog } from "@/global_components/";
 import browser from "@/utils/browser";
@@ -47,9 +52,10 @@ export default {
           title: "多人模式",
         },
       ],
-      store:useStore(),
+      store: useStore(),
       userName: "",
       roomId: browser.getURLParam("roomId"),
+      $t: this.t,
     };
   },
 
@@ -79,7 +85,7 @@ export default {
     },
     createdConfirm() {
       if (this.userName == "") {
-        Dialog.toast({ content: "請輸入您的昵稱", type: "error" });
+        Dialog.toast({ content: this.$t("common.inputnickName"), type: "error" });
         return;
       }
       let name = encodeURIComponent(this.userName);

+ 9 - 6
src/components/RTC/dialog/index.vue

@@ -3,11 +3,11 @@
     <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="dialog_title">{{ props.title || $t('common.wenxintips') }}</div>
+        <p class="dialog_desc">{{ props.desc ||  $t('common.isOver')}}</p>
         <div class="created_btn">
-          <div class="end_cancel" @click="endLiveCancel">取消</div>
-          <div class="end_confirm" @click="endLiveConfirm">確認</div>
+          <div class="end_cancel" @click="endLiveCancel">{{$t('common.cancel')}}</div>
+          <div class="end_confirm" @click="endLiveConfirm">{{$t('common.confirm')}}確認</div>
         </div>
       </div>
     </div>
@@ -18,9 +18,12 @@
 import browser from "@/utils/browser";
 import { onMounted, watch, defineProps, defineEmits, ref, computed } from "vue";
 import { useStore } from "vuex";
+import { useI18n, getLocale } from '@/i18n'
+
 const store = useStore();
 
 const emit = defineEmits(["closeDialog","confirmDialog"]);
+const { t } = useI18n({ useScope: 'global' })
 
 const role = ref(browser.urlHashValue("role"));
 const socket = computed(() => store.getters["rtc/socket"]);
@@ -29,11 +32,11 @@ const socket = computed(() => store.getters["rtc/socket"]);
 const props = defineProps({
   title: {
     type: String,
-    default: "溫馨提示",
+    default: '',
   },
   desc: {
     type: String,
-    default: "是否結束帶看?",
+    default: "",
   },
 });
 

+ 6 - 4
src/components/RTC/dialog/share.vue

@@ -3,7 +3,7 @@
     <div class="created_dialog">
       <div class="blurBox"></div>
       <div class="content">
-        <div class="dialog_title">{{ title }}</div>
+        <div class="dialog_title">{{ title || $t('share.shareLinkTips') }}</div>
         <div class="dialog_link">
           <p>
             {{ shareLink }}
@@ -11,8 +11,8 @@
         </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 class="created_cancel" @click="closeCreated">{{ $t('common.cancel')}}</div>
+          <div class="created_confirm"  ref="copylink$" :data-clipboard-text="shareLink" @click="createdConfirm">{{ $t('share.shareLink')}} 複製分享</div>
         </div>
       </div>
     </div>
@@ -23,13 +23,15 @@
 import { onMounted, watch, defineProps, defineEmits, ref, nextTick } from "vue";
 import ClipboardJS from 'clipboard'
 import { Dialog } from '@/global_components/'
+import { useI18n, getLocale } from '@/i18n'
+const { t } = useI18n({ useScope: 'global' })
 
 const emit = defineEmits(["closeDialog"]);
 
 const props = defineProps({
   title: {
     type: String,
-    default: "邀請好友",
+    default: ``,
   },
   shareLink: {
     type: String,

+ 11 - 7
src/components/Tags/goods-list.vue

@@ -22,7 +22,7 @@
                 </div>
                 <ul>
                   <li>
-                    <span>規格:</span>
+                    <span>{{$t('common.guige')}}</span>
                     <ul>
                       <li @click.stop="onClickSku(item)" :class="{ active: item.id == i.currentSku.id }" v-for="(item, idx) in i.skus" :key="idx">
                         <span v-if="item.properties[0]">
@@ -32,7 +32,7 @@
                     </ul>
                   </li>
                   <li>
-                    <span>數量:</span>
+                    <span>{{$t('common.shuliang')}}</span>
                     <div class="number">
                       <ui-icon
                         @click="
@@ -58,10 +58,10 @@
                 </ul>
               </div>
               <div class="goods-button">
-                <span v-if="i.saleChannel == 1" class="tipss">僅供線下門店購買</span>
+                <span v-if="i.saleChannel == 1" class="tipss">{{$t('common.onlyOffLine')}}</span>
                 <template v-else>
-                  <div @click.stop="viewDetail(i)">查看詳情</div>
-                  <div @click.stop="addCart(i)">加入購物車</div>
+                  <div @click.stop="viewDetail(i)">{{$t('common.viewDetail')}}</div>
+                  <div @click.stop="addCart(i)">{{$t('common.addCart')}}</div>
                 </template>
               </div>
             </div>
@@ -81,6 +81,10 @@ import { Loading, Dialog } from "@/global_components/";
 import browser from "@/utils/browser";
 import viewimg from "@/views/viewimg";
 
+import { useI18n, getLocale } from '@/i18n'
+const { t } = useI18n({ useScope: 'global' })
+
+
 // get_product_info
 const store = useStore();
 const isshoppingguide = computed(() => store.getters["shoppingguide"]);
@@ -135,7 +139,7 @@ const addCart = async (item) => {
   Loading.hide();
   if (result.code === 0) {
     apis.burying_point({ type: 7, productId: item.id });
-    return Dialog.toast({ content: `添加成功`, type: "success" });
+    return Dialog.toast({ content: t('common.addSuccess'), type: "success" });
   } else {
     let callbackUrl = `${window.location.origin}${window.location.pathname}?m=${browser.getURLParam("m")}&${getApp().Camera.getPoseUrlParams()}`;
 
@@ -160,7 +164,7 @@ const close = () => {
 
 const updateProductsById = async (idx) => {
   if (!tagclick.value.data.products[idx]) {
-    Dialog.toast({ content: `獲取商品詳情失敗,請稍後再試`, type: "error" });
+    Dialog.toast({ content: t('common.viewDetailFail'), type: "error" });
     close();
     return;
   }

+ 5 - 1
src/components/Tags/waterfall.vue

@@ -13,7 +13,7 @@
             <div class="wfavatar" :style="{ backgroundImage: `url(${item.pic})` }"></div>
             <div class="wfinfo">
               <p>{{ item.name }}</p>
-              <p>{{ item.symbol }} {{ item.price }} | 查看 ></p>
+              <p>{{ item.symbol }} {{ item.price }} | {{$t('common.view')}} ></p>
             </div>
           </li>
         </ul>
@@ -29,6 +29,10 @@ import { useStore } from "vuex";
 import browser from "@/utils/browser";
 import * as apis from "@/apis/index.js";
 
+
+import { useI18n, getLocale } from '@/i18n'
+const { t } = useI18n({ useScope: 'global' })
+
 const store = useStore();
 
 const tagclick = computed(() => store.getters["tag/tagClickType"]);

+ 3 - 2
src/components/openVideo/index.vue

@@ -3,7 +3,7 @@
     <div class="open-video">
       <!-- <div class="vmask"></div> -->
       <video x5-playsinline="true" playsinline="true" webkit-playsinline="true" class="video" ref="openvideo$" preload autoplay :src="videourl"></video>
-      <div v-show="videourl" @click.stop="countdown==0&&emit('close')" class="jump">{{countdown>0?countdown+'s 後可跳過視頻':'跳過'}}</div>
+      <div v-show="videourl" @click.stop="countdown==0&&emit('close')" class="jump">{{countdown>0?countdown+$t('common.jumpTips'):$t('common.jump')}}</div>
       <img v-show="videourl && bofanging" @click.stop="bofang" class="bofang" :src="require('@/assets/images/icon/bofang.png')" alt="" />
     </div>
   </transition>
@@ -11,7 +11,8 @@
 <script setup>
 import { ref, watch, defineEmits, computed, onMounted, nextTick, defineProps } from "vue";
 import * as apis from "@/apis/index.js";
-
+import { useI18n, getLocale } from '@/i18n'
+const { t } = useI18n({ useScope: 'global' })
 const openvideo$ = ref(null);
 
 const videourl = ref(null);

+ 24 - 48
src/components/shared/Guide.vue

@@ -1,7 +1,15 @@
 <template>
   <div v-if="show && isMobile" class="user-guide-overlay">
     <div class="user-guide-mobile">
-      <div class="zh">
+      <div class="zh en" v-if="player.lang == 'en'">
+        <div class="btn" @click="onSet"></div>
+      </div>
+
+      <div class="zh" v-else-if="player.lang == 'zh_CN'">
+        <div class="btn" @click="onSet"></div>
+      </div>
+
+      <div class="zh fan" v-else>
         <div class="btn" @click="onSet"></div>
       </div>
     </div>
@@ -12,10 +20,12 @@ import { onMounted, watch, computed, ref, nextTick } from "vue";
 import { useApp } from "@/app";
 import browser from "@/utils/browser";
 import { useStore } from "vuex";
+import { useI18n, getLocale } from '@/i18n'
 
 const isMobile = browser.isMobile();
 const show = computed(() => store.getters["player"].showUserGuide);
 const store = useStore();
+const player = computed(() => store.getters["player"]);
 
 const onSet = () => {
   store.commit("showUserGuide", false);
@@ -148,53 +158,19 @@ useApp().then((app) => {
     background-repeat: no-repeat;
   }
   .en {
-    width: 100%;
-    color: #fff;
-    ul,
-    li {
-      list-style: none;
-      padding: 0;
-      margin: 0;
-      width: 100%;
-    }
-    ul {
-      margin: 0;
-    }
-    li {
-      display: flex;
-      align-items: flex-start;
-      padding: 0.5rem 0;
-      &:first-child {
-        padding-top: 0;
-      }
-      i {
-        font-size: 1.32rem;
-      }
-      > div {
-        font-size: 0.4rem;
-        margin-left: 0.3rem;
-        :deep(span) {
-          font-size: 0.6rem;
-          color: #00c2c4;
-        }
-        :deep(div) {
-          font-size: 0.5rem;
-          margin-top: 0.1rem;
-          white-space: pre-line;
-          line-height: 1.3;
-        }
-      }
+    background-image: url(~@/assets/images/guide/novice_guide_text_en@2x.png);
+    .btn {
+      background-image: url(~@/assets/images/guide/novice_guide_button@2x.png);
     }
+  }
 
+  .fan {
+    background-image: url(~@/assets/images/guide/novice_guide_text@2x.png);
     .btn {
-      background-image: none;
-      color: #00c2c4;
-      line-height: 1.2rem;
-      text-align: center;
-      font-size: 0.52632rem;
-      background-image: url(~@/assets/images/guide/novice_guide_button_empty@2x.png);
+      background-image: url(~@/assets/images/guide/novice_guide_button@2x.png);
     }
   }
+
   .btn {
     position: absolute;
     bottom: -3.15789rem;
@@ -208,7 +184,7 @@ useApp().then((app) => {
     transform: translateX(-50%);
   }
 
-  .guide-tips{
+  .guide-tips {
     font-size: 16px;
     margin-top: 1rem;
     line-height: 1.5;
@@ -252,9 +228,9 @@ useApp().then((app) => {
   }
 }
 
-  @media (orientation: landscape) {
-    .user-guide-mobile {
-        overflow-y: auto;
-    }
+@media (orientation: landscape) {
+  .user-guide-mobile {
+    overflow-y: auto;
   }
+}
 </style>

+ 73 - 0
src/i18n/index.js

@@ -0,0 +1,73 @@
+import { nextTick } from 'vue'
+import { useI18n, createI18n } from 'vue-i18n'
+import browser from '@/utils/browser'
+
+export { useI18n }
+export const SUPPORT_LOCALES = ['zh_CN', 'en', 'zh_HK']
+
+
+export function getLocale() {
+    // 先不更新多语言
+    // let lang = browser.getURLParam('lang')
+    // if (!lang) {
+    //     lang = window.navigator.language || window.navigator.userLanguage || null
+    //     if (lang && /^en/.test(lang)) {
+    //         console.log('自动获取浏览器语言:' + lang)
+    //         lang = 'en'
+    //     }
+    //     else if (lang && /^zh_CN/.test(lang)) {
+    //         lang = 'zh_CN'
+    //     }
+    //     else {
+    //         lang = 'zh_HK'
+    //     }
+    // }
+    // return lang 
+    return 'zh_HK'
+}
+
+export function setupI18n(options = { locale: 'zh_HK' }) {
+    console.log(options.locale);
+    const i18n = createI18n(options)
+    // 先不更新多语言
+    // setI18nLanguage(i18n, options.locale)
+    setI18nLanguage(i18n, 'zh_HK')
+
+    return i18n
+}
+
+export function setI18nLanguage(i18n, locale) {
+    //先不更新多语言
+    locale = 'zh_HK'
+    if (i18n.mode === 'legacy') {
+        i18n.global.locale = locale
+    } else {
+        i18n.global.locale.value = locale
+    }
+    /**
+     * NOTE:
+     * If you need to specify the language setting for headers, such as the `fetch` API, set it here.
+     * The following is an example for axios.
+     *
+     * axios.defaults.headers.common['Accept-Language'] = locale
+     */
+
+    document.querySelector('html').setAttribute('lang', locale)
+}
+
+export async function loadLocaleMessages(i18n, locale) {
+    // load locale messages with dynamic import
+    // set locale and locale message
+
+    const messages = await import(/* webpackChunkName: "locale-[request]" */ `../locales/${locale}.json`)
+    i18n.global.setLocaleMessage(locale, messages.default)
+
+    return nextTick()
+}
+
+export default setupI18n({
+    globalInjection: true,
+    legacy: false,
+    locale: '',
+    fallbackLocale: 'zh_HK',
+})

+ 102 - 0
src/locales/en.json

@@ -0,0 +1,102 @@
+{
+  "common": {
+    "none": "None",
+    "confirm": "Confirm",
+    "cancel": "Cancel",
+    "tips": "Tips",
+    "hide": "Hide",
+    "show": "Show",
+    "review": "Preview",
+    "open": "Open",
+    "close": "Close",
+    "pause": "Pause",
+    "play": "Play",
+    "pauseTour": "Pause",
+    "playTour": "Play",
+    "passwordTips": "Please enter the password",
+    "passwordError": "“Password incorrect” error",
+    "tour": "Navigation",
+    "all": "All",
+    "model": "Dollhouse",
+    "title":"cdf Macau Grand Lisboa Palace Shop",
+    "mode":"Livestream",
+    "shopping":"Cart",
+    "guide":"Guide",
+    "guidelist":"List",
+    "noResult":"No results found",
+    "searchguide":"Search",
+    "help":"Help",
+    "kefu":"Agent",
+    "viewnum":"View",
+    "saysomething":"Comment...",
+    "cantsay":"Muted",
+    "back":"Back",
+    "linking":"Connecting...",
+    "send":"Send",
+    "member":"Participants",
+    "me":"Me",
+    "leader":"Host",
+    "setTaboo":"Mute all participants",
+    "relieveTaboo":"Unmute all participants",
+    "openMic":" You are unmuted by the host",
+    "closeMic":"You are muted by the host",
+    "inRoom":"Enter the breakout room",
+    "leaveRoom":"Exit the breakout room",
+    "removed":"You are removed by the host",
+    "fullStarffed":"The breakout room is full",
+    "notFoundRoom":"The breakout room cannot be found",
+    "openBrush":" The paintbrush is enabled by the host",
+    "closeBrush":"The paintbrush is disabled by the host",
+    "dismissRoom":"The breakout room is dismissed by the host",
+    "jumpTips":"Skip after Xs",
+    "jump":"Skip",
+
+    "goNext":"Click to navigate the next area  .",
+    "view":"View",
+    "calculation":"Current scene calculation still in progress, please try again later",
+    "error":"System Busy! Please try again later",
+    "know":"Got It",
+    "privacy":"Privacy Policies:",
+    "notice":"To preserve the security of user personal information, the developer has complied with all applicable laws, policies, and regulations regulating the collection and use of end-user personal information.",
+    "invitation":"【Friends Recommend】 Let's start a virtual walkthrough together!",
+    "inputnickName": "Please enter a nickname~",
+    "leadernickName": " Please enter an inviter's nickname",
+    "createTour": " Create a livestream",
+    "joinTour": " Join a livestream",
+    "addSuccess": "Successfully added",
+    "viewDetailFail": "Failed to get the product information; Please try again later",
+    "addCart": "Add to cart",
+    "viewDetail": "View details",
+    "onlyOffLine":"Offline shopping only",
+    "guige":"Specification:",
+    "shuliang":"Quantity:",
+    "guideSearch":"Search",
+    "isOver":"Are you sure to end the livestream?",
+    "wenxintips":"Tips"
+
+  },
+  "share": {
+    "wechat": "Wechat",
+    "friends": "Moments",
+    "qq": "QQ",
+    "facebook": "Facebook",
+    "whatsApp": "WhatsApp",
+    "copyLink": "Copy the link",
+    "shareLink": "Share the link",
+    "shareLinkTips": "Share with friends",
+    "vrMode": "VR Mode",
+    "fastCopy": "Fast copy",
+    "copySuccess":"Successfully copied"
+  },
+  "mode": {
+    "panorama": "Panorama",
+    "floorplan": "Floor plan",
+    "dollhouse": "Dollhouse",
+    "dollhouseModel": "3D Model",
+    "vr": "VR",
+    "music": "Music",
+    "fullScene": "Full screen",
+    "exitFullScene": "Exit full screen",
+    "rule": "Measurement Tools"
+  }
+}

+ 86 - 0
src/locales/zh.json

@@ -0,0 +1,86 @@
+{
+  "common": {
+    "none": "無",
+    "confirm": "確定",
+    "cancel": "取消",
+    "tips": "提示",
+    "hide": "隱藏",
+    "show": "顯示",
+    "review": "預覽",
+    "open": "開",
+    "close": "關",
+    "pause": "暫停",
+    "play": "播放",
+    "pauseTour": "暫停導覽",
+    "playTour": "播放導覽",
+    "passwordTips": "請輸入浏覽密碼",
+    "passwordError": "密碼錯誤",
+    "tour": "導覽",
+    "all": "全部",
+    "model": "三維模式",
+    "title": "cdf澳門上葡京店",
+    "mode": "一起逛",
+    "shopping": "購物",
+    "guide": "專櫃導航",
+    "guidelist": "專櫃列表",
+    "noResult": "暫無結果",
+    "searchguide": "搜索專櫃",
+    "help": "幫助",
+    "kefu": "客服",
+    "viewnum": "觀看",
+    "saysomething": "說點什麼",
+    "cantsay": "已被禁言",
+    "back": "返回",
+    "linking": "連接中...",
+    "send": "發送",
+    "member": "成員管理",
+    "me": "我",
+    "leader": "主持人",
+    "setTaboo": "主持人設置了禁言",
+    "relieveTaboo": "主持人已解除禁言",
+    "openMic": "主持人設置了開麥",
+    "closeMic": "主持人設置了靜音",
+    "inRoom": "進入房間",
+    "leaveRoom": "離開房間",
+    "removed": "您已被移除",
+    "fullStarffed": "房間已滿員",
+    "notFoundRoom": "房間未找到",
+    "openBrush": "開啟畫筆",
+    "closeBrush": "關閉畫筆",
+    "dismissRoom": "已解散房間",
+    "jumpTips": "s 後可跳過視頻",
+    "jump": "跳過",
+    "goNext": "點擊前往下一個區域",
+    "view": "查看",
+    "calculation": "该场景正在计算中,请稍后再试",
+    "error": "服务端开小差,请稍后再试",
+    "know": "我知道了",
+    "privacy": "隱私條款:",
+    "notice": "開發者已遵守收集、使用最終用戶個人信息有關的所有可適用法律、政策和法規,保護用戶個人信息安全。",
+    "invitation": "【好友推薦】一起雲逛店吧~"
+  },
+  "share": {
+    "wechat": "微信",
+    "friends": "朋友圈",
+    "qq": "QQ",
+    "facebook": "Facebook",
+    "whatsApp": "WhatsApp",
+    "copyLink": "複製鏈接",
+    "shareLink": "分享鏈接",
+    "shareLinkTips": "分享鏈接給好友",
+    "vrMode": "VR模式",
+    "fastCopy": "一鍵複製",
+    "copySuccess": "場景鏈接複製成功"
+  },
+  "mode": {
+    "panorama": "漫遊",
+    "floorplan": "平面",
+    "dollhouse": "三維",
+    "dollhouseModel": "3D模型",
+    "vr": "VR功能",
+    "music": "音樂",
+    "fullScene": "全屏",
+    "exitFullScene": "退出全屏",
+    "rule": "測量工具"
+  }
+}

+ 102 - 0
src/locales/zh_CN.json

@@ -0,0 +1,102 @@
+{
+  "common": {
+    "none": "无",
+    "confirm": "确定",
+    "cancel": "取消",
+    "tips": "提示",
+    "hide": "隐藏",
+    "show": "显示",
+    "review": "预览",
+    "open": "开",
+    "close": "关",
+    "pause": "暂停",
+    "play": "播放",
+    "pauseTour": "暂停导览",
+    "playTour": "播放导览",
+    "passwordTips": "请输入浏览密码",
+    "passwordError": "密码错误",
+    "tour": "导览",
+    "all": "全部",
+    "model": "三维模式",
+    "title":"cdf澳门上葡京店",
+    "mode":"一起逛",
+    "shopping":"购物",
+    "guide":"专柜导航",
+    "guidelist":"专柜列表",
+    "noResult":"暂无结果",
+    "searchguide":"搜索专柜",
+    "help":"帮助",
+    "kefu":"客服",
+    "viewnum":"观看",
+    "saysomething":"说点什么",
+    "cantsay":"已被禁言",
+    "back":"返回",
+    "linking":"连接中...",
+    "send":"发送",
+    "member":"成员管理",
+    "me":"我",
+    "leader":"主持人",
+    "setTaboo":"主持人设置了禁言",
+    "relieveTaboo":"主持人已解除禁言",
+    "openMic":"主持人设置了开麦",
+    "closeMic":"主持人设置了静音",
+    "inRoom":"进入房间",
+    "leaveRoom":"离开房间",
+    "removed":"您已被移除",
+    "fullStarffed":"房间已满员",
+    "notFoundRoom":"房间未找到",
+    "openBrush":"开启画笔",
+    "closeBrush":"关闭画笔",
+    "dismissRoom":"已解散房间",
+    "jumpTips":"s 后可跳过视频",
+    "jump":"跳过",
+
+    "goNext":"点击前往下一个区域",
+    "view":"查看",
+    "calculation":"该场景正在计算中,请稍后再试",
+    "error":"服务端开小差,请稍后再试",
+    "know":"我知道了",
+    "privacy":"隐私条款:",
+    "notice":"开发者已遵守收集、使用最终用户个人信息有关的所有可适用法律、政策和法规,保护用户个人信息安全。",
+    "invitation":"【好友推荐】一起云逛店吧~",
+    "inputnickName": "请输入您的昵称~",
+    "leadernickName": " 请输入发起人昵称",
+    "createTour": " 创建一起逛",
+    "joinTour": " 进入一起逛",
+    "addSuccess": "添加成功",
+    "viewDetailFail": "获取商品详情失败,请稍后再试",
+    "addCart": "加入购物车",
+    "viewDetail": "查看详情",
+    "onlyOffLine":"仅供线下门店购买",
+    "guige":"规格:",
+    "shuliang":"数量:",
+    "guideSearch":"专柜搜索",
+    "isOver":"是否结束带看?",
+    "wenxintips":"温馨提示"
+
+  },
+  "share": {
+    "wechat": "微信",
+    "friends": "朋友圈",
+    "qq": "QQ",
+    "facebook": "Facebook",
+    "whatsApp": "WhatsApp",
+    "copyLink": "複制分享",
+    "shareLink": "分享链接",
+    "shareLinkTips": "邀请好友",
+    "vrMode": "VR模式",
+    "fastCopy": "一键複制",
+    "copySuccess":"场景链接複制成功"
+  },
+  "mode": {
+    "panorama": "漫游",
+    "floorplan": "平面",
+    "dollhouse": "三维",
+    "dollhouseModel": "3D模型",
+    "vr": "VR功能",
+    "music": "音乐",
+    "fullScene": "全屏",
+    "exitFullScene": "退出全屏",
+    "rule": "测量工具"
+  }
+}

+ 102 - 0
src/locales/zh_HK.json

@@ -0,0 +1,102 @@
+{
+  "common": {
+    "none": "無",
+    "confirm": "確定",
+    "cancel": "取消",
+    "tips": "提示",
+    "hide": "隱藏",
+    "show": "顯示",
+    "review": "預覽",
+    "open": "開",
+    "close": "關",
+    "pause": "暫停",
+    "play": "播放",
+    "pauseTour": "暫停導覽",
+    "playTour": "播放導覽",
+    "passwordTips": "請輸入浏覽密碼",
+    "passwordError": "密碼錯誤",
+    "tour": "導覽",
+    "all": "全部",
+    "model": "三維模式",
+    "title":"cdf澳門上葡京店",
+    "mode":"一起逛",
+    "shopping":"購物",
+    "guide":"專櫃導航",
+    "guidelist":"專櫃列表",
+    "noResult":"暫無結果",
+    "searchguide":"搜索專櫃",
+    "help":"幫助",
+    "kefu":"客服",
+    "viewnum":"觀看",
+    "saysomething":"說點什麼",
+    "cantsay":"已被禁言",
+    "back":"返回",
+    "linking":"連接中...",
+    "send":"發送",
+    "member":"成員管理",
+    "me":"我",
+    "leader":"主持人",
+    "setTaboo":"主持人設置了禁言",
+    "relieveTaboo":"主持人已解除禁言",
+    "openMic":"主持人設置了開麥",
+    "closeMic":"主持人設置了靜音",
+    "inRoom":"進入房間",
+    "leaveRoom":"離開房間",
+    "removed":"您已被移除",
+    "fullStarffed":"房間已滿員",
+    "notFoundRoom":"房間未找到",
+    "openBrush":"開啟畫筆",
+    "closeBrush":"關閉畫筆",
+    "dismissRoom":"已解散房間",
+    "jumpTips":"s 後可跳過視頻",
+    "jump":"跳過",
+
+    "goNext":"點擊前往下一個區域",
+    "view":"查看",
+    "calculation":"该场景正在计算中,请稍后再试",
+    "error":"服务端开小差,请稍后再试",
+    "know":"我知道了",
+    "privacy":"隱私條款:",
+    "notice":"開發者已遵守收集、使用最終用戶個人信息有關的所有可適用法律、政策和法規,保護用戶個人信息安全。",
+    "invitation":"【好友推薦】一起雲逛店吧~",
+    "inputnickName": "請輸入您的昵稱~",
+    "leadernickName": " 請輸入發起人昵稱",
+    "createTour": " 創建一起逛",
+    "joinTour": " 進入一起逛",
+    "addSuccess": "添加成功",
+    "viewDetailFail": "獲取商品詳情失敗,請稍後再試",
+    "addCart": "加入購物車",
+    "viewDetail": "查看詳情",
+    "onlyOffLine":"僅供線下門店購買",
+    "guige":"規格:",
+    "shuliang":"數量:",
+    "guideSearch":"專櫃搜索",
+    "isOver":"是否結束帶看?",
+    "wenxintips":"溫馨提示"
+
+  },
+  "share": {
+    "wechat": "微信",
+    "friends": "朋友圈",
+    "qq": "QQ",
+    "facebook": "Facebook",
+    "whatsApp": "WhatsApp",
+    "copyLink": "複製分享",
+    "shareLink": "分享鏈接",
+    "shareLinkTips": "邀請好友",
+    "vrMode": "VR模式",
+    "fastCopy": "一鍵複製",
+    "copySuccess":"場景鏈接複製成功"
+  },
+  "mode": {
+    "panorama": "漫遊",
+    "floorplan": "平面",
+    "dollhouse": "三維",
+    "dollhouseModel": "3D模型",
+    "vr": "VR功能",
+    "music": "音樂",
+    "fullScene": "全屏",
+    "exitFullScene": "退出全屏",
+    "rule": "測量工具"
+  }
+}

+ 14 - 5
src/main.js

@@ -5,6 +5,7 @@ import { createApp } from "vue";
 import store from "./store";
 import App from "./app.vue";
 import browser from "@/utils/browser";
+import i18n, { getLocale, setI18nLanguage, loadLocaleMessages } from './i18n'
 
 if (!window.location.search) {
   window.location.href = "/index.html?m=eur-KJ-z5ZEV22AeU&pose=pano:408,qua:-0.006,0.6299,0.0049,0.7766";
@@ -12,10 +13,18 @@ if (!window.location.search) {
   let url = window.location.href + `&pose=pano:408,qua:-0.006,0.6299,0.0049,0.7766`;
   window.location.href = url;
 } else {
-  const app = createApp(App);
 
-  app.use(store);
-  app.use(Components);
-  app.directive("click-outside", ClickOutSide);
-  app.mount("#app");
+
+  const local = getLocale()
+  loadLocaleMessages(i18n, local).then(() => {
+    setI18nLanguage(i18n, local)
+    const app = createApp(App);
+    app.use(i18n);
+    app.use(store);
+    app.use(Components);
+    app.directive("click-outside", ClickOutSide);
+    app.mount("#app");
+  })
+
+
 }

+ 1 - 1
src/store/index.js

@@ -8,7 +8,7 @@ const store = createStore({
             flying: false,
             floorId: null,
             player: {
-                lang: 'zh',
+                lang: 'zh_HK',
                 showVR: false,
                 showMore: false,
                 showDescription: false,

+ 276 - 269
src/store/modules/tour.js

@@ -1,284 +1,291 @@
-import { getApp, getNum } from '@/app'
-import { base64ToBlob, convertBlob2File } from '@/utils/file'
+import { getApp, getNum } from '@/app';
+import { base64ToBlob, convertBlob2File } from '@/utils/file';
 export default {
-    namespaced: true,
-    state() {
-        return {
-            // showPath: false,
-            // showFrame: false,
-            toursList: [],
-            sourceList: null,
-            frameId: 0,
-            partId: 0,
-            tours: [],
-            musicList: [],
-            delList: [],
-            isPlay: false,
-            showTours: false,
-        }
+  namespaced: true,
+  state() {
+    return {
+      // showPath: false,
+      // showFrame: false,
+      toursList: [],
+      sourceList: null,
+      frameId: 0,
+      partId: 0,
+      tours: [],
+      musicList: [],
+      delList: [],
+      isPlay: false,
+      showTours: false,
+      isHome: true,
+    };
+  },
+  getters: {
+    tours: (state) => {
+      return state.tours;
     },
-    getters: {
-        tours: state => {
-            return state.tours
-        },
 
-        partId: state => {
-            return state.partId
-        },
-        frameId: state => {
-            return state.frameId
-        },
-        isPlay: state => {
-            return state.isPlay
-        },
-        showTours: state => {
-            return state.showTours
-        },
+    partId: (state) => {
+      return state.partId;
     },
-    mutations: {
-        setData(state, payload) {
-            if (payload) {
-                for (let key in payload) {
-                    state[key] = payload[key]
-                }
-            }
-        },
-        setBackUp(state) {
-            state.toursList = JSON.parse(JSON.stringify(state.tours))
-        },
-        update(state, tours) {
-            this.commit('enterEdit')
-            //如果删除片段和备份数据一样则退出编辑模式
-            let t = setTimeout(() => {
-                clearTimeout(t)
-                if (state.tours.length == 0 && state.toursList.length == 0) {
-                    this.commit('leaveEdit')
-                }
-            }, 0)
-        },
-        async setFrame(state, payload) {
-            const recordr = await getApp().TourManager.recorder
-            recordr.setFrame(state.frameId, payload)
-            this.commit('tour/update')
-        },
-        async setPart(state, payload) {
-            const recordr = await getApp().TourManager.recorder
-            let data = JSON.parse(JSON.stringify(payload))
-            for (let key in data) {
-                if (key === 'file') {
-                    delete data[key]
-                }
-            }
-            recordr.setPart(state.partId, data)
+    frameId: (state) => {
+      return state.frameId;
+    },
+    isPlay: (state) => {
+      return state.isPlay;
+    },
+    isHome: (state) => {
+      return state.isHome;
+    },
+    showTours: (state) => {
+      return state.showTours;
+    },
+  },
+  mutations: {
+    setData(state, payload) {
+      if (payload) {
+        for (let key in payload) {
+          state[key] = payload[key];
+        }
+      }
+    },
+    loaded(state, payload) {
+      state.tours = payload;
+    },
+    setBackUp(state) {
+      state.toursList = JSON.parse(JSON.stringify(state.tours));
+    },
+    update(state, tours) {
+      this.commit('enterEdit');
+      //如果删除片段和备份数据一样则退出编辑模式
+      let t = setTimeout(() => {
+        clearTimeout(t);
+        if (state.tours.length == 0 && state.toursList.length == 0) {
+          this.commit('leaveEdit');
+        }
+      }, 0);
+    },
+    async setFrame(state, payload) {
+      const recordr = await getApp().TourManager.recorder;
+      recordr.setFrame(state.frameId, payload);
+      this.commit('tour/update');
+    },
+    async setPart(state, payload) {
+      const recordr = await getApp().TourManager.recorder;
+      let data = JSON.parse(JSON.stringify(payload));
+      for (let key in data) {
+        if (key === 'file') {
+          delete data[key];
+        }
+      }
+      recordr.setPart(state.partId, data);
 
-            for (let key in payload) {
-                state.tours[state.partId][key] = payload[key]
-            }
+      for (let key in payload) {
+        state.tours[state.partId][key] = payload[key];
+      }
 
-            this.commit('tour/update')
-        },
-        getUploadMuscis(state, payload) {
-            let musics = state.tours.filter(item => {
-                console.log(item.file)
-                if (item.file) {
-                    return item
-                }
-            })
-            console.log(musics)
-            return musics
-        },
-        // setTime(state, time) {
-        //     // state.tours[state.partId].list[state.frameId].time = time * 1000
-        //     this.commit('tour/setFrame', { time: time * 1000 })
-        // },
-        delList(state, payload) {
-            let frameId = payload.frameId || 0
-            let list = state.tours[state.partId].list
-            let music = state.tours[state.partId].music
-            let name = state.tours[state.partId].list[frameId].enter.cove
-            switch (payload.type) {
-                case 'part':
-                    // if (music.indexOf('blob:') == -1 && music != '') {
-                    if (music != '') {
-                        let file = state.tours[state.partId].musicUrl ? state.tours[state.partId].musicUrl.split('/').pop().split('?').shift() : music.split('/').pop().split('?').shift()
-                        state.delList.push(file)
-                    }
-                    if (list.length > 0) {
-                        list.forEach((i, index) => {
-                            // if (i.enter.coverUrl || i.enter.cover.indexOf('data:image') == -1) {
-                            //     state.delList.push(i.enter.cover.split('/').pop().split('?').shift())
-                            // }
-                            let cover = i.enter.coverUrl ? i.enter.coverUrl.split('/').pop().split('?').shift() : i.enter.cover.split('/').pop().split('?').shift()
-                            state.delList.push(cover)
-                        })
-                    }
-                    console.log(state.delList)
-                    break
-                case 'frame':
-                    // if (name.indexOf('data:image') == -1) {
-                    //     state.delList.push(name.split('/').pop().split('?').shift())
-                    // }
-                    let cover = state.tours[state.partId].list[frameId].enter.coverUrl
-                        ? state.tours[state.partId].list[frameId].enter.coverUrl.split('/').pop().split('?').shift()
-                        : state.tours[state.partId].list[frameId].enter.cover.split('/').pop().split('?').shift()
-                    state.delList.push(cover)
-                    break
-                case 'music':
-                    // if (music.indexOf('blob:') == -1 && music != '') {
-                    //     state.delList.push(name.split('/').pop().split('?').shift())
-                    // }
-                    if (music != '') {
-                        let file = state.tours[state.partId].musicUrl ? state.tours[state.partId].musicUrl.split('/').pop().split('?').shift() : music.split('/').pop().split('?').shift()
-                        state.delList.push(file)
-                    }
-                    break
-                // case 'all':
-                //     for (var i = 0; i < state.tours.length; i++) {
-                //         let music = state.tours[i].music
-                //         if (music.indexOf('blob:') == -1 && music != '') {
-                //             state.delList.push(music.split('/').pop().split('?').shift())
-                //         }
-                //         state.tours[i].list.forEach((frame, index) => {
-                //             if (frame.enter.cover.indexOf('data:image') == -1) {
-                //                 state.delList.push(frame.enter.cover.split('/').pop().split('?').shift())
-                //             }
-                //         })
-                //     }
-                //     break
-            }
-            console.log(state.delList)
-        },
+      this.commit('tour/update');
+    },
+    getUploadMuscis(state, payload) {
+      let musics = state.tours.filter((item) => {
+        console.log(item.file);
+        if (item.file) {
+          return item;
+        }
+      });
+      console.log(musics);
+      return musics;
+    },
+    // setTime(state, time) {
+    //     // state.tours[state.partId].list[state.frameId].time = time * 1000
+    //     this.commit('tour/setFrame', { time: time * 1000 })
+    // },
+    delList(state, payload) {
+      let frameId = payload.frameId || 0;
+      let list = state.tours[state.partId].list;
+      let music = state.tours[state.partId].music;
+      let name = state.tours[state.partId].list[frameId].enter.cove;
+      switch (payload.type) {
+        case 'part':
+          // if (music.indexOf('blob:') == -1 && music != '') {
+          if (music != '') {
+            let file = state.tours[state.partId].musicUrl ? state.tours[state.partId].musicUrl.split('/').pop().split('?').shift() : music.split('/').pop().split('?').shift();
+            state.delList.push(file);
+          }
+          if (list.length > 0) {
+            list.forEach((i, index) => {
+              // if (i.enter.coverUrl || i.enter.cover.indexOf('data:image') == -1) {
+              //     state.delList.push(i.enter.cover.split('/').pop().split('?').shift())
+              // }
+              let cover = i.enter.coverUrl ? i.enter.coverUrl.split('/').pop().split('?').shift() : i.enter.cover.split('/').pop().split('?').shift();
+              state.delList.push(cover);
+            });
+          }
+          console.log(state.delList);
+          break;
+        case 'frame':
+          // if (name.indexOf('data:image') == -1) {
+          //     state.delList.push(name.split('/').pop().split('?').shift())
+          // }
+          let cover = state.tours[state.partId].list[frameId].enter.coverUrl
+            ? state.tours[state.partId].list[frameId].enter.coverUrl.split('/').pop().split('?').shift()
+            : state.tours[state.partId].list[frameId].enter.cover.split('/').pop().split('?').shift();
+          state.delList.push(cover);
+          break;
+        case 'music':
+          // if (music.indexOf('blob:') == -1 && music != '') {
+          //     state.delList.push(name.split('/').pop().split('?').shift())
+          // }
+          if (music != '') {
+            let file = state.tours[state.partId].musicUrl ? state.tours[state.partId].musicUrl.split('/').pop().split('?').shift() : music.split('/').pop().split('?').shift();
+            state.delList.push(file);
+          }
+          break;
+        // case 'all':
+        //     for (var i = 0; i < state.tours.length; i++) {
+        //         let music = state.tours[i].music
+        //         if (music.indexOf('blob:') == -1 && music != '') {
+        //             state.delList.push(music.split('/').pop().split('?').shift())
+        //         }
+        //         state.tours[i].list.forEach((frame, index) => {
+        //             if (frame.enter.cover.indexOf('data:image') == -1) {
+        //                 state.delList.push(frame.enter.cover.split('/').pop().split('?').shift())
+        //             }
+        //         })
+        //     }
+        //     break
+      }
+      console.log(state.delList);
+    },
 
-        cancel(state) {
-            state.tours = JSON.parse(JSON.stringify(state.toursList))
-            getApp().TourManager.load(JSON.parse(JSON.stringify(state.toursList))) //通知回滚
-            state.toursList = JSON.parse(JSON.stringify(state.tours))
-            this.commit('leaveEdit')
-        },
-        save(state, newdData) {
-            state.toursList = JSON.parse(JSON.stringify(newdData))
+    cancel(state) {
+      state.tours = JSON.parse(JSON.stringify(state.toursList));
+      getApp().TourManager.load(JSON.parse(JSON.stringify(state.toursList))); //通知回滚
+      state.toursList = JSON.parse(JSON.stringify(state.tours));
+      this.commit('leaveEdit');
+    },
+    save(state, newdData) {
+      state.toursList = JSON.parse(JSON.stringify(newdData));
 
-            for (let i = 0; i < state.tours.length; i++) {
-                state.tours[i].musicUrl = newdData[i].music
-                for (let j = 0; j < newdData[i].list.length; j++) {
-                    state.tours[i].list[j].coverUrl = newdData[i].list[j].cover
-                }
-            }
-            console.log(state.tours)
-            // state.tours = JSON.parse(JSON.stringify(newdData))
-            this.commit('leaveEdit')
-        },
+      for (let i = 0; i < state.tours.length; i++) {
+        state.tours[i].musicUrl = newdData[i].music;
+        for (let j = 0; j < newdData[i].list.length; j++) {
+          state.tours[i].list[j].coverUrl = newdData[i].list[j].cover;
+        }
+      }
+      console.log(state.tours);
+      // state.tours = JSON.parse(JSON.stringify(newdData))
+      this.commit('leaveEdit');
+    },
 
-        // filterFiles(state,list){
-        //   for()
-        // }
+    // filterFiles(state,list){
+    //   for()
+    // }
+  },
+  actions: {
+    delTours({ commit, state }, payload) {
+      // tour/delete
+      return getApp()
+        .remote_editor.tour_delete({ num: getNum() })
+        .then((res) => {
+          if (res.success) {
+            state.tours = [];
+            commit('save', []);
+            getApp().TourManager.load([]);
+          }
+          return res;
+        })
+        .catch((err) => {
+          console.log(err);
+          return err;
+        });
     },
-    actions: {
-        delTours({ commit, state }, payload) {
-            // tour/delete
-            return getApp()
-                .remote_editor.tour_delete({ num: getNum() })
-                .then(res => {
-                    if (res.success) {
-                        state.tours = []
-                        commit('save', [])
-                        getApp().TourManager.load([])
-                    }
-                    return res
-                })
-                .catch(err => {
-                    console.log(err)
-                    return err
-                })
-        },
-        async delFile({ commit, state }, payload) {
-            let params = {
-                num: getNum(),
-                bizType: 'tour',
-                fileNames: state.delList,
-            }
-            let res = await this.dispatch('delFiles', params)
-            console.log(res)
-            if (res.success) {
-                state.delList = []
-            }
-        },
-        async save({ commit, dispatch, state }, payload) {
-            if (state.delList.length > 0) {
-                dispatch('delFile')
+    async delFile({ commit, state }, payload) {
+      let params = {
+        num: getNum(),
+        bizType: 'tour',
+        fileNames: state.delList,
+      };
+      let res = await this.dispatch('delFiles', params);
+      console.log(res);
+      if (res.success) {
+        state.delList = [];
+      }
+    },
+    async save({ commit, dispatch, state }, payload) {
+      if (state.delList.length > 0) {
+        dispatch('delFile');
+      }
+      let recorder = await getApp().TourManager.recorder;
+      let exportFiles = recorder.exportFiles();
+      let files = [];
+      console.log(exportFiles);
+      exportFiles.map((item) => {
+        let obj = {};
+        if (item.type == 'base64') {
+          obj.file = convertBlob2File(base64ToBlob(item.file), item.name);
+          obj.filename = item.name;
+        } else if (item.type == 'file') {
+          state.tours.filter((i) => {
+            let fileId = item.name.split('.')[0].split('-')[2];
+            if (fileId == i.sid) {
+              obj.file = i.file;
+              obj.filename = item.name;
             }
-            let recorder = await getApp().TourManager.recorder
-            let exportFiles = recorder.exportFiles()
-            let files = []
-            console.log(exportFiles)
-            exportFiles.map(item => {
-                let obj = {}
-                if (item.type == 'base64') {
-                    obj.file = convertBlob2File(base64ToBlob(item.file), item.name)
-                    obj.filename = item.name
-                } else if (item.type == 'file') {
-                    state.tours.filter(i => {
-                        let fileId = item.name.split('.')[0].split('-')[2]
-                        if (fileId == i.sid) {
-                            obj.file = i.file
-                            obj.filename = item.name
-                        }
-                    })
-                }
-                files.push(obj)
-            })
-            console.log(files)
+          });
+        }
+        files.push(obj);
+      });
+      console.log(files);
 
-            let res = await this.dispatch('scene/upload_files', { bizType: 'tour', num: getNum(), type: 0, files: files })
-            if (res.success) {
-            }
-            let data = recorder.exportData()
-            // let audioFiles = files.filter(i => {
-            //     return i.type === 'file'
-            // })
-            // let imageFiles = files.filter(i => {
-            //     return i.type === 'base64'
-            // })
+      let res = await this.dispatch('scene/upload_files', { bizType: 'tour', num: getNum(), type: 0, files: files });
+      if (res.success) {
+      }
+      let data = recorder.exportData();
+      // let audioFiles = files.filter(i => {
+      //     return i.type === 'file'
+      // })
+      // let imageFiles = files.filter(i => {
+      //     return i.type === 'base64'
+      // })
 
-            // if (audioFiles.length > 0) {
-            //     for (let i = 0; i < audioFiles.length; i++) {
-            //         for (let j = 0; j < state.tours.length; j++) {
-            //             let fileId = audioFiles[i].name.split('.')[0].split('-')[2]
-            //             console.log(fileId)
-            //             if (fileId == state.tours[j].sid) {
-            //                 let res = await this.dispatch('scene/upload_files', { files: state.tours[j].file, bizType: 'tour', num: getNum(), type: 0, fileName: audioFiles[i].name })
-            //                 if (res.success) {
-            //                 }
-            //             }
-            //         }
-            //     }
-            // }
-            // if (imageFiles.length > 0) {
-            //     for (let i = 0; i < imageFiles.length; i++) {
-            //         let res = await this.dispatch('scene/upload_files', { base64: imageFiles[i].file, bizType: 'tour', num: getNum(), type: 0, fileName: imageFiles[i].name })
-            //         if (res.success) {
-            //         }
-            //     }
-            // }
+      // if (audioFiles.length > 0) {
+      //     for (let i = 0; i < audioFiles.length; i++) {
+      //         for (let j = 0; j < state.tours.length; j++) {
+      //             let fileId = audioFiles[i].name.split('.')[0].split('-')[2]
+      //             console.log(fileId)
+      //             if (fileId == state.tours[j].sid) {
+      //                 let res = await this.dispatch('scene/upload_files', { files: state.tours[j].file, bizType: 'tour', num: getNum(), type: 0, fileName: audioFiles[i].name })
+      //                 if (res.success) {
+      //                 }
+      //             }
+      //         }
+      //     }
+      // }
+      // if (imageFiles.length > 0) {
+      //     for (let i = 0; i < imageFiles.length; i++) {
+      //         let res = await this.dispatch('scene/upload_files', { base64: imageFiles[i].file, bizType: 'tour', num: getNum(), type: 0, fileName: imageFiles[i].name })
+      //         if (res.success) {
+      //         }
+      //     }
+      // }
 
-            console.log('data', data)
-            console.log('tours', state.tours)
-            let params = {
-                num: getNum(),
-                data: JSON.stringify(data),
-            }
-            return getApp()
-                .remote_editor.tour_save(params)
-                .then(res => {
-                    if (res.success) {
-                        commit('save', data)
-                    }
-                    return res
-                })
-                .catch(err => {
-                    console.log(err)
-                    return err
-                })
-        },
+      console.log('data', data);
+      console.log('tours', state.tours);
+      let params = {
+        num: getNum(),
+        data: JSON.stringify(data),
+      };
+      return getApp()
+        .remote_editor.tour_save(params)
+        .then((res) => {
+          if (res.success) {
+            commit('save', data);
+          }
+          return res;
+        })
+        .catch((err) => {
+          console.log(err);
+          return err;
+        });
     },
-}
+  },
+};

+ 2 - 2
src/views/Panel.vue

@@ -186,8 +186,8 @@ onMounted(() => {
 .panel {
   position: fixed;
   top: calc(100% - 90px);
-  left: 0.8rem;
-  right: 0.8rem;
+  left: 0.4rem;
+  right: 0.4rem;
   z-index: 22;
 }
 </style>