Prechádzať zdrojové kódy

更新全景图版本

tremble 3 rokov pred
rodič
commit
f4079e8d28
100 zmenil súbory, kde vykonal 16671 pridanie a 8 odobranie
  1. 43 8
      pano/package-lock.json
  2. 2 0
      pano/package.json
  3. 1 0
      pano/public/index.html
  4. 4 0
      pano/public/pano/js/jquery-2.1.1.min.js
  5. 1 0
      pano/src/App.vue
  6. BIN
      pano/src/assets/images/icon/bigscene/auto.png
  7. BIN
      pano/src/assets/images/icon/bigscene/auto_active.png
  8. BIN
      pano/src/assets/images/icon/bigscene/change.png
  9. 28 0
      pano/src/assets/images/icon/bigscene/chat.svg
  10. 43 0
      pano/src/assets/images/icon/bigscene/collection.svg
  11. BIN
      pano/src/assets/images/icon/bigscene/dollhouse.png
  12. BIN
      pano/src/assets/images/icon/bigscene/dollhouse_active.png
  13. BIN
      pano/src/assets/images/icon/bigscene/firstfloor.png
  14. BIN
      pano/src/assets/images/icon/bigscene/floor.png
  15. BIN
      pano/src/assets/images/icon/bigscene/floor_active.png
  16. BIN
      pano/src/assets/images/icon/bigscene/fullscreen.png
  17. BIN
      pano/src/assets/images/icon/bigscene/home.png
  18. BIN
      pano/src/assets/images/icon/bigscene/inside.png
  19. BIN
      pano/src/assets/images/icon/bigscene/inside_active.png
  20. BIN
      pano/src/assets/images/icon/bigscene/like.png
  21. BIN
      pano/src/assets/images/icon/bigscene/like_1.png
  22. BIN
      pano/src/assets/images/icon/bigscene/like_2.png
  23. BIN
      pano/src/assets/images/icon/bigscene/like_2_1.png
  24. BIN
      pano/src/assets/images/icon/bigscene/like_active.png
  25. BIN
      pano/src/assets/images/icon/bigscene/likexuliezhen.png
  26. BIN
      pano/src/assets/images/icon/bigscene/liuyanban.png
  27. BIN
      pano/src/assets/images/icon/bigscene/menu.png
  28. BIN
      pano/src/assets/images/icon/bigscene/menu_active.png
  29. BIN
      pano/src/assets/images/icon/bigscene/musicoff.png
  30. BIN
      pano/src/assets/images/icon/bigscene/musicon.png
  31. BIN
      pano/src/assets/images/icon/bigscene/pause.png
  32. BIN
      pano/src/assets/images/icon/bigscene/play.png
  33. BIN
      pano/src/assets/images/icon/bigscene/renqi.png
  34. BIN
      pano/src/assets/images/icon/bigscene/screen.png
  35. BIN
      pano/src/assets/images/icon/bigscene/secondfloor.png
  36. BIN
      pano/src/assets/images/icon/bigscene/sublist/Q&A.png
  37. BIN
      pano/src/assets/images/icon/bigscene/sublist/Q&A_active.png
  38. BIN
      pano/src/assets/images/icon/bigscene/sublist/chat.png
  39. BIN
      pano/src/assets/images/icon/bigscene/sublist/chat_active.png
  40. BIN
      pano/src/assets/images/icon/bigscene/sublist/collection.png
  41. BIN
      pano/src/assets/images/icon/bigscene/sublist/collection_active.png
  42. BIN
      pano/src/assets/images/icon/bigscene/sublist/donate.png
  43. BIN
      pano/src/assets/images/icon/bigscene/sublist/donate_active.png
  44. BIN
      pano/src/assets/images/icon/bigscene/voice.png
  45. BIN
      pano/src/assets/images/icon/bigscene/voice_active.png
  46. BIN
      pano/src/assets/images/icon/close1.png
  47. BIN
      pano/src/assets/images/icon/danmu_close.png
  48. BIN
      pano/src/assets/images/icon/danmu_open.png
  49. BIN
      pano/src/assets/images/icon/danmu_top.png
  50. BIN
      pano/src/assets/images/icon/loading.gif
  51. BIN
      pano/src/assets/images/icon/star.png
  52. BIN
      pano/src/assets/images/icon/usericon.png
  53. BIN
      pano/src/assets/images/logo@2x.png
  54. BIN
      pano/src/assets/images/project/cancel.png
  55. BIN
      pano/src/assets/images/project/faguang.png
  56. BIN
      pano/src/assets/images/project/jin.png
  57. BIN
      pano/src/assets/images/project/jzdw.png
  58. BIN
      pano/src/assets/images/project/tong.png
  59. BIN
      pano/src/assets/images/project/yin.png
  60. BIN
      pano/src/assets/images/renqi@2x.png
  61. BIN
      pano/src/assets/logo.png
  62. 61 0
      pano/src/assets/style/reset.less
  63. 365 0
      pano/src/components/Danmaku.vue
  64. 193 0
      pano/src/components/Paging.vue
  65. 3266 0
      pano/src/components/emoji/biaoqing.js
  66. 3956 0
      pano/src/components/emoji/emoji_sorites.css
  67. 1 0
      pano/src/components/emoji/emoji_sorites.css.map
  68. 4747 0
      pano/src/components/emoji/emoji_sorites.less
  69. BIN
      pano/src/components/emoji/expression.png
  70. 257 0
      pano/src/components/emoji/index.vue
  71. BIN
      pano/src/components/emoji/placeholder.png
  72. 447 0
      pano/src/components/hotspot/audio.vue
  73. 85 0
      pano/src/components/hotspot/iframe.vue
  74. 187 0
      pano/src/components/hotspot/image.vue
  75. 31 0
      pano/src/components/hotspot/index.js
  76. 146 0
      pano/src/components/hotspot/index.vue
  77. 188 0
      pano/src/components/hotspot/model.vue
  78. 265 0
      pano/src/components/hotspot/multiple.vue
  79. 78 0
      pano/src/components/hotspot/title.vue
  80. 171 0
      pano/src/components/hotspot/video.vue
  81. 27 0
      pano/src/components/hotspot/yuange.js
  82. 244 0
      pano/src/components/hovercom/dati/data.js
  83. 403 0
      pano/src/components/hovercom/dati/index.vue
  84. 192 0
      pano/src/components/hovercom/donate/index.vue
  85. 49 0
      pano/src/components/hovercom/index.vue
  86. 393 0
      pano/src/components/hovercom/message/Message.vue
  87. 49 0
      pano/src/components/hovercom/message/index.vue
  88. 60 0
      pano/src/components/huizhang/huizhang.vue
  89. 49 0
      pano/src/components/huizhang/index.vue
  90. 36 0
      pano/src/components/jiangpai/index.vue
  91. 130 0
      pano/src/components/popupLayout/Alert.vue
  92. 45 0
      pano/src/components/popupLayout/Broadcast.vue
  93. 62 0
      pano/src/components/popupLayout/Confirm.vue
  94. 52 0
      pano/src/components/popupLayout/Loading.vue
  95. 188 0
      pano/src/components/popupLayout/Message.vue
  96. 50 0
      pano/src/components/popupLayout/Tips.vue
  97. 29 0
      pano/src/components/popupLayout/index.js
  98. 34 0
      pano/src/components/popupLayout/popup/index.vue
  99. 13 0
      pano/src/components/popupLayout/popup/manager.js
  100. 0 0
      pano/src/components/returnhome/index.vue

+ 43 - 8
pano/package-lock.json

@@ -8,8 +8,10 @@
       "name": "pano",
       "version": "0.1.0",
       "dependencies": {
+        "axios": "^0.26.0",
         "core-js": "^3.6.5",
         "vue": "^2.6.11",
+        "vue-chat-scroll": "^1.4.0",
         "vue-router": "^3.2.0"
       },
       "devDependencies": {
@@ -3123,6 +3125,14 @@
       "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
       "dev": true
     },
+    "node_modules/axios": {
+      "version": "0.26.0",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-0.26.0.tgz",
+      "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==",
+      "dependencies": {
+        "follow-redirects": "^1.14.8"
+      }
+    },
     "node_modules/babel-eslint": {
       "version": "10.1.0",
       "resolved": "https://registry.npmmirror.com/babel-eslint/-/babel-eslint-10.1.0.tgz",
@@ -6728,7 +6738,6 @@
       "version": "1.14.9",
       "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.14.9.tgz",
       "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
-      "dev": true,
       "engines": {
         "node": ">=4.0"
       },
@@ -13745,6 +13754,14 @@
       "resolved": "https://registry.npmmirror.com/vue/-/vue-2.6.14.tgz",
       "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ=="
     },
+    "node_modules/vue-chat-scroll": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/vue-chat-scroll/-/vue-chat-scroll-1.4.0.tgz",
+      "integrity": "sha512-taHcwJJadZwJR4C4t/fv+R9ZXXSrwrpQKP17La/ep5q7IyH5i3BvscgSpXwu7s8TPP9T9n5n3JDnO+vRsZ/mBQ==",
+      "peerDependencies": {
+        "vue": "^2.4.4"
+      }
+    },
     "node_modules/vue-eslint-parser": {
       "version": "7.11.0",
       "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-7.11.0.tgz",
@@ -16771,7 +16788,8 @@
       "version": "4.5.15",
       "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.15.tgz",
       "integrity": "sha512-fqap+4HN+w+InDxlA3hZTOGE0tzBTgXhKLoDydhywqgmhQ1D9JA6Feh94ze6tG8DsWX58/ujYUqA8jAz17FJtg==",
-      "dev": true
+      "dev": true,
+      "requires": {}
     },
     "@vue/cli-service": {
       "version": "4.5.15",
@@ -16908,7 +16926,8 @@
       "version": "1.1.2",
       "resolved": "https://registry.npmmirror.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz",
       "integrity": "sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==",
-      "dev": true
+      "dev": true,
+      "requires": {}
     },
     "@vue/web-component-wrapper": {
       "version": "1.3.0",
@@ -17123,7 +17142,8 @@
       "version": "5.3.2",
       "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
       "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
-      "dev": true
+      "dev": true,
+      "requires": {}
     },
     "acorn-walk": {
       "version": "7.2.0",
@@ -17153,13 +17173,15 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/ajv-errors/-/ajv-errors-1.0.1.tgz",
       "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
-      "dev": true
+      "dev": true,
+      "requires": {}
     },
     "ajv-keywords": {
       "version": "3.5.2",
       "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
       "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
-      "dev": true
+      "dev": true,
+      "requires": {}
     },
     "alphanum-sort": {
       "version": "1.0.2",
@@ -17419,6 +17441,14 @@
       "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
       "dev": true
     },
+    "axios": {
+      "version": "0.26.0",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-0.26.0.tgz",
+      "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==",
+      "requires": {
+        "follow-redirects": "^1.14.8"
+      }
+    },
     "babel-eslint": {
       "version": "10.1.0",
       "resolved": "https://registry.npmmirror.com/babel-eslint/-/babel-eslint-10.1.0.tgz",
@@ -20438,8 +20468,7 @@
     "follow-redirects": {
       "version": "1.14.9",
       "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.14.9.tgz",
-      "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
-      "dev": true
+      "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
     },
     "for-in": {
       "version": "1.0.2",
@@ -26271,6 +26300,12 @@
       "resolved": "https://registry.npmmirror.com/vue/-/vue-2.6.14.tgz",
       "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ=="
     },
+    "vue-chat-scroll": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/vue-chat-scroll/-/vue-chat-scroll-1.4.0.tgz",
+      "integrity": "sha512-taHcwJJadZwJR4C4t/fv+R9ZXXSrwrpQKP17La/ep5q7IyH5i3BvscgSpXwu7s8TPP9T9n5n3JDnO+vRsZ/mBQ==",
+      "requires": {}
+    },
     "vue-eslint-parser": {
       "version": "7.11.0",
       "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-7.11.0.tgz",

+ 2 - 0
pano/package.json

@@ -8,8 +8,10 @@
     "lint": "vue-cli-service lint"
   },
   "dependencies": {
+    "axios": "^0.26.0",
     "core-js": "^3.6.5",
     "vue": "^2.6.11",
+    "vue-chat-scroll": "^1.4.0",
     "vue-router": "^3.2.0"
   },
   "devDependencies": {

+ 1 - 0
pano/public/index.html

@@ -8,6 +8,7 @@
     <title>中国人民解放军总医院数字史馆</title>
   </head>
   <body>
+    <script src="<%= VUE_APP_STATIC_DIR %>/js/jquery-2.1.1.min.js" class="build keep"></script>
     <script src="<%= VUE_APP_STATIC_DIR %>/tour.js"></script>
     <div id="app"></div>
     <!-- built files will be auto injected -->

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 4 - 0
pano/public/pano/js/jquery-2.1.1.min.js


+ 1 - 0
pano/src/App.vue

@@ -21,5 +21,6 @@ html,body{
 #app{
   width: 100%;
   height: 100%;
+  text-align: center;
 }
 </style>

BIN
pano/src/assets/images/icon/bigscene/auto.png


BIN
pano/src/assets/images/icon/bigscene/auto_active.png


BIN
pano/src/assets/images/icon/bigscene/change.png


+ 28 - 0
pano/src/assets/images/icon/bigscene/chat.svg

@@ -0,0 +1,28 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="93.5" height="68.19" viewBox="0 0 93.5 68.19">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#248570"/>
+      <stop offset="1" stop-color="#4ab19b"/>
+    </linearGradient>
+    <filter id="路径_16256">
+      <feOffset dx="1" input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="3" result="blur"/>
+      <feFlood flood-color="#b0eee3" result="color"/>
+      <feComposite operator="out" in="SourceGraphic" in2="blur"/>
+      <feComposite operator="in" in="color"/>
+      <feComposite operator="in" in2="SourceGraphic"/>
+    </filter>
+  </defs>
+  <g id="chat" transform="translate(-2742.185 -2470.043)">
+    <path id="联合_1" data-name="联合 1" d="M4155.185,1017.733a3,3,0,0,1-3-3v-34a3,3,0,0,1,3-3h13v-10a2,2,0,0,1,2-2h29a2,2,0,0,1,2,2v10h13a3,3,0,0,1,3,3v34a3,3,0,0,1-3,3Zm42-40v-8h-25v8Z" transform="translate(-1382 1520)" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" fill="url(#linear-gradient)"/>
+    <g data-type="innerShadowGroup">
+      <path id="路径_16256-2" data-name="路径 16256" d="M2805.846,2470.043h-57.076c-3.637,0-6.585,2.551-6.585,5.7v41.632c0,3.147,2.948,5.7,6.585,5.7H2759.7l-.519,6.649,18.953-6.649h27.716c3.637,0,6.586-2.551,6.586-5.7v-41.632C2812.432,2472.594,2809.483,2470.043,2805.846,2470.043Z" fill="#338e7f"/>
+      <g transform="matrix(1, 0, 0, 1, 2742.19, 2470.04)" filter="url(#路径_16256)">
+        <path id="路径_16256-3" data-name="路径 16256" d="M2805.846,2470.043h-57.076c-3.637,0-6.585,2.551-6.585,5.7v41.632c0,3.147,2.948,5.7,6.585,5.7H2759.7l-.519,6.649,18.953-6.649h27.716c3.637,0,6.586-2.551,6.586-5.7v-41.632C2812.432,2472.594,2809.483,2470.043,2805.846,2470.043Z" transform="translate(-2742.19 -2470.04)" fill="#fff"/>
+      </g>
+    </g>
+    <rect id="矩形_488" data-name="矩形 488" width="47.807" height="3.659" rx="1.829" transform="translate(2754.625 2483.947)" fill="#fff"/>
+    <rect id="矩形_489" data-name="矩形 489" width="31.221" height="3.659" rx="1.829" transform="translate(2754.625 2497.85)" fill="#fff"/>
+    <path id="联合_3" data-name="联合 3" d="M1924.5,345v-4h-4v-3h4v-4h3v4h4v3h-4v4Z" transform="translate(898.685 2187.732)" fill="#fff"/>
+  </g>
+</svg>

+ 43 - 0
pano/src/assets/images/icon/bigscene/collection.svg

@@ -0,0 +1,43 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="87.261" height="80.919" viewBox="0 0 87.261 80.919">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#248570"/>
+      <stop offset="1" stop-color="#4ab19b"/>
+    </linearGradient>
+    <filter id="路径_28037">
+      <feOffset dx="1" input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="3" result="blur"/>
+      <feFlood flood-color="#b0eee3" result="color"/>
+      <feComposite operator="out" in="SourceGraphic" in2="blur"/>
+      <feComposite operator="in" in="color"/>
+      <feComposite operator="in" in2="SourceGraphic"/>
+    </filter>
+    <linearGradient id="linear-gradient-2" x1="0.336" y1="2.677" x2="0.629" y2="-0.144" xlink:href="#linear-gradient"/>
+    <filter id="矩形_1654">
+      <feOffset dx="1" input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="3" result="blur-2"/>
+      <feFlood flood-color="#b0eee3" result="color-2"/>
+      <feComposite operator="out" in="SourceGraphic" in2="blur-2"/>
+      <feComposite operator="in" in="color-2"/>
+      <feComposite operator="in" in2="SourceGraphic"/>
+    </filter>
+  </defs>
+  <g id="collection" transform="translate(-1065.837 -1753.053)">
+    <path id="路径_28036" data-name="路径 28036" d="M1151.59,1806.171c8.042,8.489,12.447,21.346,10.808,34.828a44.815,44.815,0,0,1-2.156,9.325l-9.23-1.532a45.434,45.434,0,0,0,1.2-6.226,43.83,43.83,0,0,0-8.448-32.063Z" transform="translate(-22.799 -16.503)" fill="url(#linear-gradient)"/>
+    <g data-type="innerShadowGroup">
+      <path id="路径_28037-2" data-name="路径 28037" d="M1151.59,1806.171c8.042,8.489,12.447,21.346,10.808,34.828a44.815,44.815,0,0,1-2.156,9.325l-9.23-1.532a45.434,45.434,0,0,0,1.2-6.226,43.83,43.83,0,0,0-8.448-32.063Z" transform="translate(-25.799 -16.352)" fill="#fff"/>
+      <g transform="matrix(1, 0, 0, 1, 1065.84, 1753.05)" filter="url(#路径_28037)">
+        <path id="路径_28037-3" data-name="路径 28037" d="M1151.59,1806.171c8.042,8.489,12.447,21.346,10.808,34.828a44.815,44.815,0,0,1-2.156,9.325l-9.23-1.532a45.434,45.434,0,0,0,1.2-6.226,43.83,43.83,0,0,0-8.448-32.063Z" transform="translate(-1091.64 -1769.4)" fill="#fff"/>
+      </g>
+    </g>
+    <rect id="矩形_1653" data-name="矩形 1653" width="63.08" height="18.996" transform="translate(1076.961 1798.058) rotate(-45)" fill="url(#linear-gradient-2)"/>
+    <g data-type="innerShadowGroup">
+      <rect id="矩形_1654-2" data-name="矩形 1654" width="50.894" height="32.314" transform="translate(1076.56 1789.041) rotate(-45)" fill="#fff"/>
+      <g transform="matrix(1, 0, 0, 1, 1065.84, 1753.05)" filter="url(#矩形_1654)">
+        <rect id="矩形_1654-3" data-name="矩形 1654" width="50.894" height="32.314" transform="translate(10.72 35.99) rotate(-45)" fill="#fff"/>
+      </g>
+    </g>
+    <circle id="椭圆_242" data-name="椭圆 242" cx="7.885" cy="7.885" r="7.885" transform="translate(1113.505 1781.934)" fill="#fff"/>
+    <rect id="矩形_1655" data-name="矩形 1655" width="87.261" height="2.509" rx="1.254" transform="translate(1065.837 1831.395)" fill="#fff"/>
+  </g>
+</svg>

BIN
pano/src/assets/images/icon/bigscene/dollhouse.png


BIN
pano/src/assets/images/icon/bigscene/dollhouse_active.png


BIN
pano/src/assets/images/icon/bigscene/firstfloor.png


BIN
pano/src/assets/images/icon/bigscene/floor.png


BIN
pano/src/assets/images/icon/bigscene/floor_active.png


BIN
pano/src/assets/images/icon/bigscene/fullscreen.png


BIN
pano/src/assets/images/icon/bigscene/home.png


BIN
pano/src/assets/images/icon/bigscene/inside.png


BIN
pano/src/assets/images/icon/bigscene/inside_active.png


BIN
pano/src/assets/images/icon/bigscene/like.png


BIN
pano/src/assets/images/icon/bigscene/like_1.png


BIN
pano/src/assets/images/icon/bigscene/like_2.png


BIN
pano/src/assets/images/icon/bigscene/like_2_1.png


BIN
pano/src/assets/images/icon/bigscene/like_active.png


BIN
pano/src/assets/images/icon/bigscene/likexuliezhen.png


BIN
pano/src/assets/images/icon/bigscene/liuyanban.png


BIN
pano/src/assets/images/icon/bigscene/menu.png


BIN
pano/src/assets/images/icon/bigscene/menu_active.png


BIN
pano/src/assets/images/icon/bigscene/musicoff.png


BIN
pano/src/assets/images/icon/bigscene/musicon.png


BIN
pano/src/assets/images/icon/bigscene/pause.png


BIN
pano/src/assets/images/icon/bigscene/play.png


BIN
pano/src/assets/images/icon/bigscene/renqi.png


BIN
pano/src/assets/images/icon/bigscene/screen.png


BIN
pano/src/assets/images/icon/bigscene/secondfloor.png


BIN
pano/src/assets/images/icon/bigscene/sublist/Q&A.png


BIN
pano/src/assets/images/icon/bigscene/sublist/Q&A_active.png


BIN
pano/src/assets/images/icon/bigscene/sublist/chat.png


BIN
pano/src/assets/images/icon/bigscene/sublist/chat_active.png


BIN
pano/src/assets/images/icon/bigscene/sublist/collection.png


BIN
pano/src/assets/images/icon/bigscene/sublist/collection_active.png


BIN
pano/src/assets/images/icon/bigscene/sublist/donate.png


BIN
pano/src/assets/images/icon/bigscene/sublist/donate_active.png


BIN
pano/src/assets/images/icon/bigscene/voice.png


BIN
pano/src/assets/images/icon/bigscene/voice_active.png


BIN
pano/src/assets/images/icon/close1.png


BIN
pano/src/assets/images/icon/danmu_close.png


BIN
pano/src/assets/images/icon/danmu_open.png


BIN
pano/src/assets/images/icon/danmu_top.png


BIN
pano/src/assets/images/icon/loading.gif


BIN
pano/src/assets/images/icon/star.png


BIN
pano/src/assets/images/icon/usericon.png


BIN
pano/src/assets/images/logo@2x.png


BIN
pano/src/assets/images/project/cancel.png


BIN
pano/src/assets/images/project/faguang.png


BIN
pano/src/assets/images/project/jin.png


BIN
pano/src/assets/images/project/jzdw.png


BIN
pano/src/assets/images/project/tong.png


BIN
pano/src/assets/images/project/yin.png


BIN
pano/src/assets/images/renqi@2x.png


BIN
pano/src/assets/logo.png


+ 61 - 0
pano/src/assets/style/reset.less

@@ -40,3 +40,64 @@ a{text-decoration: none;}
   background-color: #9f9f9f;
   -webkit-border-radius: 4px;
 }
+
+
+
+.brightness{
+  &::before{
+    content: '';
+    position: absolute;
+    top: 0; 
+    bottom: 0;
+    left: 0; 
+    right: 0;
+    z-index: -1;
+    background-repeat: no-repeat;
+    background-position: center;
+    background-color:rgba(0,0,0,0.7);
+    font-size: 0;
+  }
+}
+
+
+.brightnessW,.brightnessBigW{
+  &::before{
+    content: '';
+    position: absolute;
+    top: 0; 
+    bottom: 0;
+    left: 0; 
+    right: 0;
+    z-index: -1;
+    background-repeat: no-repeat;
+    background-position: center;
+    background-color:rgba(255,255,255,0.7);
+    font-size: 0;
+  }
+}
+
+@supports (backdrop-filter: brightness(60%)) {
+  .brightness{
+    &::before{
+      content: '';
+      backdrop-filter: blur(10px) brightness(60%)!important;
+      background-color: rgba(0,0,0,0)!important;
+    }
+  }  
+
+  .brightnessW{
+    &::before{
+      content: '';
+      backdrop-filter: blur(10px) brightness(70%)!important;
+      background-color: rgba(255,255,255, 0.14)!important;
+    }
+  }  
+
+  .brightnessBigW{
+    &::before{
+      content: '';
+      backdrop-filter: blur(2px) brightness(90%) !important;
+      background-color: rgba(255,255,255, 0.14)!important;
+    }
+  }  
+}

+ 365 - 0
pano/src/components/Danmaku.vue

@@ -0,0 +1,365 @@
+<template>
+  <div class="danmaku-area " @keydown.stop="keydownstop" >
+    <div
+      class="danmaku-container"
+      v-chat-scroll
+      v-if="isShowList"
+    >
+      <!-- <ul class=""> -->
+      <transition-group  appear tag="ul" class="danmaku-list" name="dm">
+        <li
+          class="danmaku-list-item"
+          v-for="(item,i) in showDanmakuData"
+          :title="item"
+          :key="i"
+        >
+          <span v-html="item"></span>
+        </li>
+      </transition-group>
+    </div>
+    <div class="input-container" v-if="!isMobile">
+        <input maxlength="20" @keyup.enter="leaveMsg" @click.stop="toggleSelectMenu" v-model="danmu" type="text" placeholder="请选择弹幕内发送吧~" class="send-choices">
+      <div class="send-btn-container">
+        <img
+          @click="hideList"
+          class="show-icon"
+          :src="showIcon || ''"
+          v-if="isShowList"
+        />
+        <img
+          @click="showList"
+          class="close-icon"
+          :src="closeIcon || ''"
+          v-if="!isShowList"
+        />
+        <button class="send-btn" @click="leaveMsg">发送</button>
+      </div>
+      <ul class="show-list" v-show="isShowSelectList">
+        <li
+          class="list-item"
+          :title="item"
+          v-for="(item, key) in quotes"
+          :key="key"
+          @click="sendDanmaku(key)"
+          v-html="item"
+        >
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script>
+
+import { saveBarrage } from "@/config/api";
+
+export default {
+  name: "danmaku",
+  props: {
+    quotes: {
+      type: Array,
+      default: function() {
+        return [
+        ];
+      },
+    },
+    danmuArr: {
+      type: Array,
+      default: function() {
+        return [
+        ];
+      },
+    },
+    showIcon: {
+      type: String,
+      required: true,
+    },
+    closeIcon: {
+      type: String,
+      required: true,
+    },
+    arrowIcon: {
+      type: String,
+      required: true,
+    },
+    limit: {
+      type: Number,
+      required: false,
+      default: 5000,
+    },
+  },
+  watch: {
+    isNotInputAction: function() {},
+  },
+
+  data() {
+    return {
+      danmu:'',
+      showDanmakuData: [],
+      isShowList: true,
+      isShowSelectList: false,
+      isNotInputAction: false,
+      timer: null,
+      autoIndex: 0,
+      isMobile: /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)
+    };
+  },
+  methods: {
+    keydownstop(e){
+      e.stopPropagation();
+      return 
+    },
+    toggleSelectMenu() {
+      this.isShowSelectList = !this.isShowSelectList;
+    },
+    showList() {
+      this.isShowList = true;
+    },
+    hideList() {
+      this.isShowList = false;
+    },
+     sendDanmaku(index) {
+      const text = this.quotes[index];
+      this.danmu = text
+      this.leaveMsg()
+    },
+    sendDanmakuSelf(text){
+      if (this.showDanmakuData.length <= this.limit) {
+        this.showDanmakuData.push(text);
+      }
+      this.isShowSelectList = false;
+      this.isNotInputAction = true;
+      this.danmu=''
+    },
+    async leaveMsg(){
+      let tmp = this.danmu.trim()
+      if (!tmp) {
+        return alert('弹幕不能为空')
+      }
+      saveBarrage({
+        content: tmp,
+      },res=>{
+        if (res.code==0) {
+          this.sendDanmakuSelf(tmp)
+        }
+      })
+    },
+    autoPopAnimation() {
+      if (!this.isNotInputAction) {
+        this.timer = setInterval(() => {
+          if (this.autoIndex <= this.danmuArr.length) {
+            if (this.danmuArr[this.autoIndex]) {
+              this.showDanmakuData.push(this.danmuArr[this.autoIndex]);
+              this.autoIndex++;
+            } else {
+              clearInterval(this.timer);
+            }
+          }
+        }, 2000);
+      }
+    },
+  },
+  mounted() {
+    this.autoPopAnimation();
+  },
+};
+</script>
+
+
+<style lang="less" scoped>
+
+
+.danmaku-area {
+  position: relative;
+  margin: 0;
+  pointer-events: auto;
+}
+.danmaku-container {
+  max-width: 340px;
+  max-height: 288px;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
+
+/* .hidedanmu{
+  visibility: hidden;
+  pointer-events: none;
+} */
+
+.danmaku-list {
+  text-decoration: none;
+  max-width: 342px;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+  margin: 0;
+  padding: 0;
+}
+.danmaku-list li.danmaku-list-item {
+  margin-bottom: 10px;
+  padding: 0;
+  display: block;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  text-align: left;
+}
+.danmaku-list li.danmaku-list-item > span {
+  padding: 0 20px;
+  line-height: 36px;
+  font-size: 14px;
+  display: inline-block;
+  margin: 0;
+  color: #fff;
+  border-radius: 20px;
+  background: rgba(80, 80, 80, 0.6);
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  text-align: left;
+  max-width: 290px;
+}
+.danmaku-list li.danmaku-list-item:nth-last-child(6) {
+  /* visibility: hidden; */
+  animation: fadeout 0.3s linear;
+}
+
+.input-container {
+  background: rgba(80, 80, 80, 0.6);
+  border-radius: 25px;
+  max-width: 350px;
+  height: 48px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  position: relative;
+  padding: 10px;
+}
+.input-container  {
+  >span,>input{
+    cursor: pointer;
+    font-size: 14px;
+    height: 48px;
+    text-align: left;
+    line-height: 48px;
+    padding-left: 10px;
+    background:none;  
+    outline:none;  
+    border:none;
+    color: #fff;
+    &::placeholder{
+      color: #ccc;
+      
+    }
+  }
+
+}
+.input-container > * {
+  cursor: pointer;
+}
+.arrow-icon {
+  margin-right: 8px;
+  width: 18px;
+}
+
+.send-btn {
+  background: #B8A38C;
+  color: #fff;
+  padding: 5px 15px;
+  border: 10px;
+  outline: 0;
+  font-size: 16px;
+  cursor: pointer;
+  border-radius: 15px;
+  margin-left: 5px;
+}
+.send-btn-container {
+  display: flex;
+  align-items: center;
+  
+}
+.send-btn-container .show-icon,
+.send-btn-container .close-icon {
+  width: 24px;
+  margin-right: 4px;
+}
+.input-container .show-list {
+  text-decoration: none;
+  position: absolute;
+  left: 0;
+  bottom: 64px;
+  background: rgba(80, 80, 80, 1);
+  width: 340px;
+  color: #fff;
+  border-radius: 10px;
+  max-height: 200px;
+  overflow-x: hidden;
+  overflow-y: scroll;
+  margin: 0;
+  padding: 0;
+}
+.input-container .show-list .list-item {
+  text-align: left;
+  font-size: 14px;
+  line-height: 36px;
+  text-decoration: none;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  color: #fff;
+  max-width: 100%;
+  padding: 0 10px;
+  overflow: hidden;
+}
+.input-container .show-list .list-item:hover {
+    background: #B8A38C; 
+}
+
+.dm-enter {
+  opacity: 0;
+  transform: translateY(20px);
+}
+
+.dm-leave-to {
+  opacity: 0;
+  transform: translateY(-50px);
+}
+
+.dm-enter-active,
+.dm-leave-active {
+  transition: all 0.6s ease;
+}
+
+.dm-move {
+  transition: all 0.6s ease;
+}
+
+.dm-leave-active {
+  position: absolute;
+}
+
+
+@keyframes fadeout {
+  0% {
+    opacity: 1;
+    transform: translateY(10px);
+  }
+  50% {
+    opacity: 0.7;
+    transform: translateY(3px);
+  }
+  100% {
+    opacity: 0.2;
+    transform: translateY(0px);
+  }
+}
+
+@media only screen and (max-width: 500px), (max-height: 487px) {
+  .danmaku-list li.danmaku-list-item{
+    margin-bottom: 5px;
+  }
+  .danmaku-list li.danmaku-list-item > span{
+    border-radius: 3px;
+    border: 0;
+  }
+}
+</style>

+ 193 - 0
pano/src/components/Paging.vue

@@ -0,0 +1,193 @@
+<template>
+  <div class="layerpage" :class="cls">
+    <!-- <span class="total">共{{total}}条</span> -->
+    <div class="number">
+      <span @click="current = current - 1">上一页</span>
+
+      <template v-if="currMin > min">
+        <span @click="current = min">{{min}}</span>
+        <span @click="current -= 3" v-if="currMin - 1 > min">…</span>
+      </template>
+
+      <span 
+        v-for="index in numbers" 
+        :key="index" 
+        :class="{active: index === current}" 
+        @click="current = index">
+        {{index}}
+      </span>
+
+
+      <template v-if="currMax < max">
+        <span @click="current += 3" v-if="currMax + 1 < max">…</span>
+        <span @click="current = max">{{max}}</span>
+      </template>
+
+      <span @click="current = current + 1">下一页</span>
+    </div>
+
+    <div class="topage" v-if="false">
+      <span>到第</span>
+      <input
+        type="text"
+        v-model="inputval"
+        @keyup.enter="ev => current = Number(inputval)"
+      >
+      <span>页</span>
+
+      <span @click="ev => current = Number(inputval)" class="comfirm">确定</span>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: ['paging','cls'],
+  data() {
+    return {
+      ...this.paging,
+      inputval:''
+    }
+  },
+  computed: {
+    min() {
+      return 1
+    },
+    max() {
+      return Math.ceil(this.total / this.pageSize)
+    },
+    routineMin() {
+      return this.current - Math.ceil(this.showSize / 2)
+    },
+    routineMax() {
+      return this.current + Math.floor(this.showSize / 2)
+    },
+    currMin() {
+      let c = this.max - this.routineMax
+
+      if (this.routineMin <= this.min) {
+        return this.min
+      } else if (c >= 0) {
+        return this.routineMin
+      } else if (this.routineMin + c <= this.min) {
+        return this.min
+      } else {
+        return this.routineMin + c
+      }
+    },
+    currMax() {
+      let c = this.min - this.routineMin
+
+      if (this.routineMax >= this.max) {
+        return this.max
+      } else if (c <= 0) {
+        return this.routineMax
+      } else if (this.routineMax + c >= this.max) {
+        return this.max
+      } else {
+        return this.routineMax + c
+      }
+    },
+    numbers() {
+      let total = this.currMax - this.currMin
+      let numbers = []
+
+      if (total === 0) {
+        numbers.push(1)
+      } else {
+        for (let i = 0; i <= total; i++) {
+          numbers.push(this.currMin + i)
+        }
+      }
+      return numbers
+    }
+  },
+  watch: {
+    current(val, old) {
+      if (isNaN(this.current)) {
+        return this.current = old
+      } else if (this.current < this.min) {
+        return this.current = this.min
+      } else if (this.current > this.max) {
+        return this.current = this.max
+      }
+      this.$emit('changeCurrent', this.current)
+    },
+   
+    paging() {
+        Object.keys(this.paging).forEach(k => this[k] = this.paging[k])
+    },
+    'paging.total':function () {
+        Object.keys(this.paging).forEach(k => this[k] = this.paging[k])
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+
+  .layerpage {
+    span {
+      font-size: 16px;
+      display: inline-block;
+      color: #434343;
+      vertical-align: middle;
+      margin: 0 4px;
+
+      &.active {
+        color: #B8A38C;
+      }
+    }
+    .number {
+      span {
+        margin: 0;
+        cursor: pointer;
+        border-radius: 50%;
+        width: 40px;
+        height: 40px;
+        vertical-align: middle;
+        line-height: 40px;
+        text-align: center;
+        &:last-child,
+        &:first-child {
+          border-radius: 20px;
+          width: 80px;
+        }
+        &:hover {
+          color: #B8A38C;
+        }
+      }
+    }
+    div {
+      display: inline-block;
+    }
+
+    .topage {
+      margin-left: 14px;
+      > input {
+        background: none;
+        border: 1px solid #999999;
+        width: 40px;
+        height: 40px;
+        padding: 0 10px;
+        border-radius: 50%;
+        vertical-align: middle;
+        text-align: center;
+        color: #999999;
+      }
+      > span {
+        margin: 0 10px;
+      }
+      .comfirm {
+        cursor: pointer;
+        border-radius: 20px;
+        width: 70px;
+        height: 40px;
+        vertical-align: middle;
+        line-height: 40px;
+        text-align: center;
+      }
+    }
+  }
+  
+</style>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 3266 - 0
pano/src/components/emoji/biaoqing.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 3956 - 0
pano/src/components/emoji/emoji_sorites.css


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 0
pano/src/components/emoji/emoji_sorites.css.map


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 4747 - 0
pano/src/components/emoji/emoji_sorites.less


BIN
pano/src/components/emoji/expression.png


+ 257 - 0
pano/src/components/emoji/index.vue

@@ -0,0 +1,257 @@
+<template>
+  <div>
+    <div class="biaoqingContent" @click.stop>
+      <img
+        class="expression"
+        @click.stop="show = !show"
+        src="./expression.png"
+      />
+      <!--      表情选择框-->
+      <div class="box"  v-clickoutside="clickoutside" v-if="show" @click.stop>
+        <div class="left">
+          <ul>
+            <li
+              v-for="(item, index) in biaoqingList"
+              :key="index"
+              :class="['cursor', biaoqingActive === index ? 'active' : '']"
+            >
+              <div @click="bqNameChange(index, item.iconArr)">
+                {{ item.name }}
+              </div>
+            </li>
+          </ul>
+        </div>
+        <div class="right">
+          <button
+            v-for="(i, index) in rightList"
+            :key="index"
+            class="emoji"
+            @click.stop="insertHtmlAtCaret(i.icon)"
+          >
+          <span>{{i.icon}}</span>
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+// 定义最后光标对象
+
+import { biaoqingArr } from "./biaoqing.js";
+export default {
+  props: {
+    //表情框宽度
+    width: {
+      type: String,
+      default: "50%",
+    },
+    //表情框高度
+    height: {
+      type: String,
+      default: "200px",
+    },
+    //表情框ID
+    id: {
+      type: String,
+      default: "text",
+    },
+    //内容
+    value: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      lastEditRange: "",
+      content: "",
+      show: false,
+      biaoqingList: biaoqingArr,
+      biaoqingActive: 0,
+      rightList: [],
+      allList: [],
+    };
+  },
+  watch: {
+    value() {
+      this.valueChange();
+    },
+  },
+  methods: {
+    clickoutside(){
+      this.show = false
+    },
+    valueChange() {
+      if (!this.value) return;
+      let str = this.value;
+      let newStr = this.forArr(this.forArr(str), "className");
+      document.getElementById(this.id).innerHTML = newStr;
+    },
+    //替换emoji 表情图片
+    forArr(str, _type) {
+      for (let j = 0; j < this.allList.length; j++) {
+        let val = this.allList[j];
+        let reg = _type === "className" ? `alt${val.className}` : val.icon;
+        if (_type === "className") {
+          str = str.replace(new RegExp(reg, "g"), val.icon);
+        } else {
+          str = str.replace(
+            new RegExp(reg, "g"),
+            `<img style="vertical-align: sub;" src="http://tkeasyemoji.oss-cn-shanghai.aliyuncs.com/images/placeholder.png" class="${val.className}" alt="alt${val.className}">`
+          );
+        }
+      }
+      return str;
+    },
+    // 表情title点击事件
+    bqNameChange(index, arr) {
+      this.biaoqingActive = index;
+      this.rightList = arr;
+    },
+    contentClick() {
+      // 获取选定对象
+      var selection = getSelection();
+      // 设置最后光标对象
+      this.lastEditRange = selection.getRangeAt(0);
+    },
+    //表情插入文本框
+    insertHtmlAtCaret(icon) {
+      this.$emit('select',icon)
+      this.show = false
+    },
+  },
+
+  created() {
+    this.rightList = biaoqingArr[0].iconArr;
+    biaoqingArr.forEach((e) => {
+      this.allList = this.allList.concat(e.iconArr);
+    });
+  },
+  mounted() {
+    this.valueChange();
+  },
+  components: {},
+};
+</script>
+<style>
+@import "./emoji_sorites.css";
+</style>
+<style lang="less" scoped>
+.contentBox {
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  padding: 10px;
+  font-size: 16px;
+  position: relative;
+}
+.biaoqingContent {
+  padding: 5px;
+  width: 30px;
+  margin: 5px auto;
+  position: relative;
+  .expression {
+    width: 20px;
+    margin: 5px 0;
+    cursor: pointer;
+  }
+
+  .box {
+    border: 1px solid #73a8f9;
+    width:400px;
+    min-height: 400px;
+    box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.2);
+    background: #fff;
+    bottom: 100%;
+    z-index: 999;
+    left: 0;
+    display: flex;
+    position: absolute;
+  }
+  .left {
+    width: 90px;
+    height: 100%;
+    display: table-cell;
+    border-right: 1px solid #ebebeb;
+    padding: 4px;
+
+    ul {
+      padding: 0;
+      margin: 0;
+      text-align: center;
+      list-style-type: none;
+
+      li {
+        height: 30px;
+        line-height: 30px;
+        cursor: pointer;
+        font-size: 14px;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        color: #000;
+        button {
+          border: none;
+          background: none;
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+
+    .active {
+      background-color: #3171d1;
+      color: #000;
+      border-radius: 4px;
+
+      button {
+        color: #000;
+      }
+    }
+  }
+  .right {
+    flex: 1;
+    padding-left: 10px;
+    padding-top: 15px;
+    overflow: auto;
+    text-align: left;
+    button {
+      border: none;
+      padding: 0;
+      cursor: pointer;
+      width: 32px;
+      height: 32px;
+    }
+
+    .emoji {
+      display: inline-block;
+      padding: 3px;
+      border: 1px solid transparent;
+      cursor: pointer;
+      background: #fff;
+      color: #000;
+      &:hover {
+        height: 32px;
+        background-color: #ddded8;
+        border: 1px solid #b3c1fd;
+        border-radius: 4px;
+      }
+    }
+  }
+  //样式一
+  .box0 {
+  }
+}
+
+@media screen and (max-width: 600px) {
+  .biaoqingContent {
+      .box {
+        width: 80vw;
+        right: -10vw;
+        max-height: 26vh;
+        min-height: unset;
+        overflow-y: auto;
+      }
+  }
+}
+</style>

BIN
pano/src/components/emoji/placeholder.png


+ 447 - 0
pano/src/components/hotspot/audio.vue

@@ -0,0 +1,447 @@
+<template>
+  <div class="images brightnessBigW">
+    <audio
+      id="audioTag"
+      class="noshow"
+      autoplay
+      :src="g_dealUrl(hotspot.backgroundMusic)"
+    ></audio>
+    <div class="jiantou brightnessBigW"></div>
+    <div class="title">{{ hotspot.title }}</div>
+    <div class="qline">
+      <img :src="require(`@/assets/images/icon/star.png`)" alt="" />
+    </div>
+    <div class="audiotu">
+      <img class="cd" :class="{cdactive:isPlay}" :src="require(`@/assets/images/project/CD.png`)" alt="" />
+      <img
+        class="hk"
+        @click="bofang"
+        :class="{ hkactive: !isPlay }"
+        :src="require(`@/assets/images/project/huakuai.png`)"
+        alt=""
+      />
+    </div>
+    <div class="img-con">
+      <template v-if="hotspot.title == '院歌'">
+        <ul>
+          <li v-for="(item,i) in geciList" :key="i"> 
+            <span :class="{acitve:singleidx>idx}" v-for="(ii,idx) in item.txt" :key="idx">
+              {{ii}}
+            </span>
+          </li>
+        </ul>
+      </template>
+      <p v-else>{{ hotspot.content }}</p>
+    </div>
+
+    <div class="qline rline">
+      <img :src="require(`@/assets/images/icon/star.png`)" alt="" />
+    </div>
+    <div class="audiocon">
+      <div class="adcon">
+        <img
+          @click="bofang"
+          :src="
+            require(`@/assets/images/project/${
+              isPlay ? 'zanting' : 'bofang'
+            }.png`)
+          "
+          alt=""
+        />
+        <div class="bar">
+          <div class="activeLine" @click="seekTime"></div>
+          <div :style="{ width: currentPosi + '%' }" class="dot"></div>
+        </div>
+        <div class="time">
+          <span>{{ time }}</span
+          ><span> / {{ allTime }}</span>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { data } from "./yuange";
+export default {
+  props: ["hotspot"],
+  data() {
+    let tmp = Object.keys(data).map(item=>{
+        return {
+          miao:item,
+          txt:data[item]
+        }
+      })
+    return {
+      active: 0,
+      time: 0,
+      i_audio: "",
+      isPlay: false,
+      currentPosi: 0,
+      allTime: 0,
+      geci: tmp,
+      activeGeci:0,
+      singleidx: 0,
+      timer:null
+    };
+  },
+  computed:{
+    geciList(){
+      let tmp = Object.keys(data).map(item=>{
+        return {
+          miao:item,
+          txt:data[item]
+        }
+      })
+
+      let active = tmp.splice(Math.max(this.activeGeci-1,0), 3)
+      return active
+    }
+  },
+  methods: {
+    bofang() {
+      if (this.i_audio.paused) {
+        this.i_audio.play();
+        this.isPlay = true;
+        this.timer&&clearInterval(this.timer)
+        this.timer = setInterval(() => {
+          this.singleidx += 1
+        }, 350);
+      } else {
+        this.i_audio.pause();
+        this.isPlay = false;
+        this.timer&&clearInterval(this.timer)
+      }
+    },
+    transTime(time) {
+      var duration = parseInt(time);
+      var minute = parseInt(duration / 60);
+      var sec = (duration % 60) + "";
+      var isM0 = ":";
+      if (minute == 0) {
+        minute = "00";
+      } else if (minute < 10) {
+        minute = "0" + minute;
+      }
+      if (sec.length == 1) {
+        sec = "0" + sec;
+      }
+      return minute + isM0 + sec;
+    },
+    updateProgress() {
+
+      this.geci.forEach((item,i)=>{
+        if (item.miao <= Math.floor(this.i_audio.currentTime)) {
+          this.activeGeci = i
+        }
+      })
+
+      this.currentPosi =
+        (this.i_audio.currentTime / this.i_audio.duration) * 100;
+      this.time = this.transTime(this.i_audio.currentTime);
+    },
+
+    audioEnded() {
+      this.i_audio.currentTime = 0;
+      this.i_audio.pause();
+      this.isPlay = false;
+      this.activeGeci=0
+      this.singleidx=0
+      this.timer&&clearInterval(this.timer)
+    },
+
+    seekTime(e) {
+      var rate = e.offsetX / e.target.clientWidth;
+      this.i_audio.currentTime = this.i_audio.duration * rate;
+      this.updateProgress();
+    },
+  },
+  watch:{
+    activeGeci(newVal,oldVal){
+      if (newVal) {
+        this.singleidx = 0
+        this.timer&&clearInterval(this.timer)
+        this.timer = setInterval(() => {
+          this.singleidx += 1
+        }, 350);
+      }
+
+    }
+  },
+  beforeDestroy(){
+    this.i_audio = null
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.i_audio = $("#audioTag")[0];
+      $("#audioTag").on("loadedmetadata", (e) => {
+        this.time = this.transTime(e.currentTarget.duration);
+        this.allTime = this.transTime(e.currentTarget.duration);
+        this.i_audio.play();
+        this.isPlay = true;
+      });
+
+      document.addEventListener(
+        "WeixinJSBridgeReady",
+        function() {
+          this.i_audio.play();
+        },
+        false
+      );
+
+      $("#audioTag").on("timeupdate", () => {
+        this.updateProgress();
+      });
+
+      $("#audioTag").on("timeupdate", () => {
+        this.updateProgress();
+      });
+
+      $("#audioTag").on("ended", () => {
+        this.audioEnded();
+      });
+    });
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.images {
+  width: 320px;
+  height: 480px;
+  text-align: center;
+  margin: 0 auto;
+  position: fixed;
+  top: 60%;
+  left: 32%;
+  transform: translate(-50%, -50%);
+  border-radius: 5px;
+  border-top: 5px solid #ccaf8f;
+  border-bottom: 5px solid #ccaf8f;
+  background-color: rgba(3, 51, 48, .7);
+  color: #ffe0a6;
+  padding: 20px 24px;
+  text-align: left;
+    &::before{
+      content: '';
+      backdrop-filter: blur(2px) brightness(90%)!important;
+      background-color: rgba(255,255,255, 0.14)!important;
+    }
+    .jiantou{
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      left: 100%;
+      width: 130px;
+      height: 100px;
+      background-color: rgba(3, 51, 48, .7);
+      clip-path: polygon(0 58%, 0% 100%, 100% 0%);
+      &::before{
+        content: '';
+        backdrop-filter: blur(2px) brightness(90%)!important;
+        background-color: rgba(255,255,255, 0.14)!important;
+      }
+    }
+  .title {
+    display: inline-block;
+    font-weight: bold;
+    font-size: 20px;
+    margin: 0 auto;
+    width: 100%;
+    text-align: center;
+  }
+
+  .qline {
+    width: 100%;
+    height: 1px;
+    margin: 22px 0 10px;
+    position: relative;
+    background: linear-gradient(
+      90deg,
+      #fcf5d3 0%,
+      #fcf5d3 48%,
+      rgba(255, 255, 255, 0) 100%
+    );
+    > img {
+      position: absolute;
+      left: -10px;
+      width: 32px;
+      top: -12px;
+    }
+  }
+
+  .rline {
+    margin: 0 0 16px;
+    background: linear-gradient(
+      -90deg,
+      #fcf5d3 0%,
+      #fcf5d3 48%,
+      rgba(255, 255, 255, 0) 100%
+    );
+    > img {
+      position: absolute;
+      right: -10px;
+      left: unset;
+      width: 32px;
+      top: -12px;
+    }
+  }
+
+  .audiotu {
+    text-align: center;
+    position: relative;
+    .cd {
+      width: 70%;
+      transition: 0.3s ease all;
+      animation: stoprotate 1s linear forwards;
+    }
+    .hk {
+      position: absolute;
+      top: 10px;
+      left: 10px;
+      z-index: 99;
+      cursor: pointer;
+      transition: 0.3s ease transform;
+      transform-origin: left;
+      width: 120px;
+    }
+    .hkactive {
+      transform: rotate(-30deg);
+    }
+
+    .cdactive{
+      animation: rotate360 5s linear infinite;
+    }
+  }
+  .img-con {
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    position: relative;
+    > p {
+      line-height: 2;
+      max-width: 1000px;
+      max-height: 100px;
+      margin: 10px 0;
+      text-align: justify;
+      overflow-y: auto;
+      &::-webkit-scrollbar {
+        width: 0;
+      }
+    }
+    >ul{
+      width:100%;
+      >li{
+        line-height: 1.5;
+        margin: 10px 0;
+        text-align: left;
+        transition: .3s ease all;
+        &:first-of-type,&:last-of-type{
+          >span{
+            color: rgba(255, 224, 166, 0.4)!important;
+          }
+        }
+        >span{
+          font-size: 14px;
+          display: inline-block;
+          color: rgba(255, 255, 255, 1);
+          transition: .4s ease all;
+          &.acitve{
+            color: rgba(255, 224, 166, 1);
+          }
+        }
+      }
+    }
+  }
+  @color: rgba(20, 62, 58, 0.5);
+  .audiocon {
+    position: relative;
+    width: 100%;
+    height: 40px;
+    .adcon {
+      width: 100%;
+      margin: 0 auto;
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      .bar {
+        position: relative;
+        background: none;
+        display: flex;
+        overflow: visible;
+        height: 4px;
+        border-radius: 4px;
+        margin: 0 10px 0 16px;
+        flex: auto;
+        .activeLine {
+          cursor: pointer;
+          height: 100%;
+          overflow: hidden;
+          position: absolute;
+          background-color: rgba(252, 245, 211, .25);
+          top: 50%;
+          transform: translateY(-50%);
+          border-radius: 12px;
+          width: 100%;
+        }
+
+        .dot {
+          display: inline-block;
+          position: absolute;
+          z-index: 999;
+          background-color: rgba(226, 197, 154, .7);
+          transform: translateY(-50%);
+          top: 50%;
+          left: 0;
+          height: 100%;
+          cursor: pointer;
+          pointer-events: none;
+        }
+      }
+
+      > img {
+        margin: 0 0px;
+        cursor: pointer;
+        width: 30px;
+      }
+
+      .time {
+        width: 100px;
+        text-align: center;
+        margin: 0 auto;
+        > span {
+          font-size: 14px;
+          &:first-of-type {
+            color: #fff;
+          }
+
+          &:last-of-type {
+            color: #fff;
+          }
+        }
+      }
+    }
+  }
+}
+
+.close {
+  position: absolute;
+  top: 40px;
+  right: 50px;
+  width: 20px;
+  height: 20px;
+  cursor: pointer;
+  z-index: 9999999;
+}
+.noshow {
+  position: fixed;
+  top: -100%;
+  left: -100%;
+  opacity: 0 !important;
+  pointer-events: none !important;
+}
+</style>

+ 85 - 0
pano/src/components/hotspot/iframe.vue

@@ -0,0 +1,85 @@
+<template>
+  <div class="images">
+    <div class="img-con">
+        <iframe :src="g_dealUrl(hotspot.iframe[active]||hotspot.model[active])" frameborder="0"></iframe>
+    </div>
+    <div class="title" v-html="hotspot.title"></div>
+    <div class="desc" v-if="hotspot.content" v-html="hotspot.content"></div>
+  </div>
+</template>
+
+<script>
+export default {
+  props:['hotspot'],
+  data(){
+    return {
+      active:0
+    }
+  },
+  mounted(){
+    console.log(this.hotspot);
+  },
+  methods:{
+  }
+}
+</script>
+<style lang="less" scoped>
+.noshow{
+  opacity: 0!important;;
+  pointer-events: none!important;;
+}
+
+.images{
+  width: 1410px;
+  height: 800px;
+  text-align: center;
+  margin: 0 auto;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  border-radius: 5px;
+  text-align: left;
+  .img-con{
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    position: relative;
+    padding-bottom: 40px;
+    height: calc(100% - 130px);
+    >iframe{
+      width: 100%;
+      height: 100%;
+    }
+  }
+  .title {
+    font-weight: bold;
+    font-size: 30px;
+    width: 100%;
+    padding: 0 50px;
+    text-align: center;
+    margin-bottom: 10px;
+  }
+  .desc{
+    margin: 0 auto;
+    width: 70%;
+    color: #fff;
+    text-align: justify;
+    line-height: 1.8;
+    max-height: 120px;
+    overflow-y: auto;
+    font-size: 16px;
+    padding: 0;
+  }
+}
+
+ .close{
+    position: absolute;
+    top: 20px;
+    right: 40px;
+    width: 20px;
+    height: 20px;
+    cursor: pointer;
+    z-index: 9999999;
+  }
+</style>

+ 187 - 0
pano/src/components/hotspot/image.vue

@@ -0,0 +1,187 @@
+<template>
+    <div class="images" >
+      <div class="img-con">
+        <span v-if="hotspot.images.length>1" @click="handlePage('prev')" >
+          <img :src="require('@/assets/images/project/left.png')" alt="">
+
+        </span>
+        <img v-viewer :src="g_dealUrl(hotspot.images[active])" alt="">
+        <span v-if="hotspot.images.length>1" @click="handlePage('next')">
+          <img :src="require('@/assets/images/project/right.png')" alt="">
+        </span>
+        <ul class="pagna" v-if="hotspot.images.length>1">
+          <li v-for="(item,i) in hotspot.images" :class="{active:i==active}" :key="i"></li>
+        </ul>
+      </div>
+        <div class="title" v-html="hotspot.title"></div>
+        <div class="desc" v-html="handleContent(hotspot.contents[active]||hotspot.contents[0],16)">
+      </div>
+    </div>
+
+    
+
+</template>
+
+<script>
+
+export default {
+
+  props:['hotspot'],
+  data(){
+    return {
+      active:0,
+      to:null,
+      mbactive:0
+    }
+  },
+  watch:{
+    active(){
+      setTimeout(() => {
+          this.$showLoading();
+      });
+
+      if (this.to) {
+        clearTimeout(this.to)
+        this.to = null
+      }
+      this.to = setTimeout(() => {
+        this.$hideLoading();
+      },1500);
+    }
+  },
+  beforeDestroy(){
+      this.$hideLoading();
+  },
+  methods:{
+    handlePage(type){
+    if (type==='next') {
+      console.log(this.hotspot.images.length);
+      if (this.active>=this.hotspot.images.length - 1) {
+          this.active = 0
+          return
+      }
+      this.active += 1
+    }
+    else{
+      if (this.active==0) {
+          this.active = this.hotspot.images.length - 1
+          return
+      }
+      this.active -= 1
+    }
+    
+  },
+  },
+  computed:{
+   
+  },
+  mounted() {
+    this.$showLoading();
+    if (this.TO) {
+      clearTimeout(this.TO)
+      this.TO = null
+    }
+    this.TO = setTimeout(() => {
+      this.$hideLoading();
+    },800);
+  },
+}
+</script>
+
+<style lang="less" scoped>
+.noshow{
+  opacity: 0!important;;
+  pointer-events: none!important;;
+}
+
+.images{
+  width: 1410px;
+  height: 760px;
+  text-align: center;
+  margin: 0 auto;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  border-radius: 5px;
+  text-align: left;
+  color: #fff;
+
+  .img-con{
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    position: relative;
+    padding-bottom: 40px;
+    >span{
+      @juli:60px;
+      display: inline-block;
+      padding: 0 30px;
+      box-sizing: content-box;
+      cursor: pointer;
+      position: absolute;
+      left: @juli;
+      top: 50%;
+      transform: translateY(-50%);
+      >img{
+        width: 20px;
+      }
+      &:last-of-type{
+        left: unset;
+        right: @juli;
+      }
+    }
+    >img{
+      max-width: 100%;
+      max-height: 520px;
+      cursor: pointer;
+    }
+    .pagna{
+      position: absolute;
+      bottom: 0;
+      text-align: center;
+      z-index: 999;
+      >li{
+        width: 10px;
+        border-radius: 50%;
+        height: 10px;
+        background: rgba(51, 143, 123, .24);
+        display: inline-block;
+        margin: 0 4px;
+        &.active{
+          background: #338F7B;
+        }
+      }
+    }
+  }
+  .title {
+    font-weight: bold;
+    font-size: 30px;
+    width: 100%;
+    padding: 0 50px;
+    margin-bottom: 10px;
+    text-align: center;
+  }
+.desc{
+    margin: 0 auto;
+    width: 70%;
+    color: #fff;
+    text-align: justify;
+    line-height: 1.8;
+    max-height: 120px;
+    overflow-y: auto;
+    font-size: 16px;
+    padding-right: 10px;
+  }
+}
+
+ .close{
+    position: fixed;
+    top: 40px;
+    right: 50px;
+    width: 20px;
+    height: 20px;
+    cursor: pointer;
+    z-index: 9999999;
+  }
+</style>

+ 31 - 0
pano/src/components/hotspot/index.js

@@ -0,0 +1,31 @@
+import Vue from 'vue'
+import UIHotspot from './index.vue'
+
+
+let Hotspot = Vue.extend(UIHotspot)
+
+
+let hotspotInstance = ''
+export function $showHotspot(data={}) {
+    if (hotspotInstance) {
+        return
+    }
+    hotspotInstance = new Hotspot({
+        data
+    }).$mount()
+
+    document.body.appendChild(hotspotInstance.$el)
+
+    Vue.nextTick(() => {
+        hotspotInstance.show = true
+    })
+}
+
+export function $hideHotspot() {
+    if (hotspotInstance) {
+        document.body.removeChild(hotspotInstance.$el)
+        hotspotInstance = ''
+    }
+}
+
+

+ 146 - 0
pano/src/components/hotspot/index.vue

@@ -0,0 +1,146 @@
+<template>
+  <div class="hotspotcon" @click="closeAudio" :class="{brightness:active != 'vAudio'}">
+    <component @close='close' :count="count" @click.native.stop :is="active" :hotspot="hotspot">
+    </component>
+
+    <img @click="close" class="close" :src="require('@/assets/images/project/cancel.png')" alt="">
+
+  </div>
+</template>
+
+<script>
+import audio from './audio.vue';
+import image from './image.vue';
+import video from './video.vue';
+import title from './title.vue';
+import iframe from './iframe.vue';
+import model from './model.vue';
+
+import multiple from './multiple.vue';
+
+
+let iconArr = [
+  { name: "视频", key: "video",  id: "vVideo", img: "video-icon", display: false },
+  { name: "网页", key: "iframe", id: "vIframe", img: "iframe-icon", display: false },
+  { name: "图片", key: "images", id: "vImage", img: "img-icon", display: false },
+  { name: "模型", key: "model",  id: "vModel", img: "model-icon", display: false },
+  { name: "音频", key: "backgroundMusic",  id: "vAudio", img: "model-icon", display: false },
+  { name: "介绍", key: "title", id: "vTitle", img: "txt-icon", display: false }
+];
+
+export default {
+  components:{
+    vAudio:audio,
+    vImage:image,
+    vIframe:iframe,
+    vModel:model,
+    vTitle:title,
+    vVideo:video,
+    vMultiple:multiple,
+
+  },
+  methods:{
+    closeAudio(){
+     if (this.active == 'vAudio') {
+        this.close()
+      }
+    },
+    close(){
+      
+      this.$hideHotspot()
+      
+       window.parent.postMessage(
+            {
+              source: "closeHotspot",
+              data: true,
+            },
+            "*"
+          );
+      g_currentHot.closePopup && g_currentHot.closePopup()
+      
+      if(g_bgAudio && g_bgAudio.pauseByHot){
+        window.manage.switchBgmState(true);
+      }
+    }
+  },
+  mounted(){
+    this.count = []
+    iconArr.forEach(item => {
+      if (this.hotspot[item.key]) {
+        this.active = !this.active ? item.id : this.active;
+        if (item.key=='images' || item.key == 'iframe' || item.key == 'model') {
+          this.count.push(item.key)
+        }
+      }
+    });
+
+    // this.hotspot = Object.replaceAll( this.hotspot, this.is_bendi?"https://super.4dage.com/":"", "" );
+
+ 
+
+    console.log(this.hotspot);
+     this.hotspot.contents = this.hotspot.content
+            ? this.hotspot.content.split("(4dage)")
+          : [];
+    
+    !this.active && (this.active = 'vTitle')
+      if (this.hotspot.title=='院歌') {
+        this.active = 'vAudio'
+      }
+
+
+      if (this.count.length>1) {
+        this.active = 'vMultiple'
+      }
+
+    console.log(this.count);
+
+    console.log(this.active);
+  },
+  data(){
+    return {
+      active:'',
+      count:[]
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.hotspotcon{
+  position: fixed;
+  z-index: 99999;
+  width: 100%;
+  height: 100%;
+  left: 0;
+  top: 0;
+  .close{
+    position: fixed;
+    top: 40px;
+    right: 50px;
+    width: 54px;
+    height: 54px;
+    cursor: pointer;
+    z-index: 9999999;
+  }
+  .ifrclose{
+    top: 100px;
+  }
+}
+
+@media only screen and (max-width: 487px) {
+.hotspotcon{
+  .close{
+    position: fixed;
+    top: unset;
+    right: unset;
+    bottom: 4%;
+    transform: translateX(-50%);
+    left: 50%;
+    width: 50px;
+    height: 50px;
+    cursor: pointer;
+  }
+}
+}
+</style>

+ 188 - 0
pano/src/components/hotspot/model.vue

@@ -0,0 +1,188 @@
+<template>
+  <div class="images">
+    <div class="img-con">
+      <span v-if="hotspot.model.length > 1" @click="handlePage('prev')">
+        <img :src="require('@/assets/images/project/left.png')" alt="" />
+      </span>
+      <iframe
+        :src="g_dealUrl(hotspot.model[active])"
+        frameborder="0"
+      ></iframe>
+      <span v-if="hotspot.model.length > 1" @click="handlePage('next')">
+        <img :src="require('@/assets/images/project/right.png')" alt="" />
+      </span>
+      <ul class="pagna" v-if="hotspot.model.length > 1">
+        <li
+          v-for="(item, i) in hotspot.model"
+          :class="{ active: i == active }"
+          :key="i"
+        ></li>
+      </ul>
+    </div>
+    <div class="title" v-html="hotspot.title"></div>
+    <div
+      class="desc"
+      v-html="
+        handleContent(hotspot.contents[active] || hotspot.contents[0], 16)
+      "
+    ></div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: ["hotspot"],
+  data() {
+    return {
+      active: 0,
+      to: null,
+      mbactive: 0,
+    };
+  },
+  watch: {
+    active() {
+      setTimeout(() => {
+        this.$showLoading();
+      });
+
+      if (this.to) {
+        clearTimeout(this.to);
+        this.to = null;
+      }
+      this.to = setTimeout(() => {
+        this.$hideLoading();
+      }, 1500);
+    },
+  },
+  beforeDestroy() {
+    this.$hideLoading();
+  },
+  methods: {
+    handlePage(type) {
+      if (type === "next") {
+        console.log(this.hotspot.model.length);
+        if (this.active >= this.hotspot.model.length - 1) {
+          this.active = 0;
+          return;
+        }
+        this.active += 1;
+      } else {
+        if (this.active == 0) {
+          this.active = this.hotspot.model.length - 1;
+          return;
+        }
+        this.active -= 1;
+      }
+    },
+  },
+  computed: {},
+  mounted() {
+    this.$showLoading();
+    if (this.TO) {
+      clearTimeout(this.TO);
+      this.TO = null;
+    }
+    this.TO = setTimeout(() => {
+      this.$hideLoading();
+    }, 800);
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.noshow{
+  opacity: 0!important;;
+  pointer-events: none!important;;
+}
+
+.images{
+  width: 1410px;
+  height: 760px;
+  text-align: center;
+  margin: 0 auto;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  border-radius: 5px;
+  text-align: left;
+  color: #fff;
+
+  .img-con{
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    position: relative;
+    padding-bottom: 40px;
+    height: calc(100% - 130px);
+    >span{
+      @juli:60px;
+      display: inline-block;
+      padding: 0 30px;
+      box-sizing: content-box;
+      cursor: pointer;
+      position: absolute;
+      left: @juli;
+      top: 50%;
+      transform: translateY(-50%);
+      >img{
+        width: 20px;
+      }
+      &:last-of-type{
+        left: unset;
+        right: @juli;
+      }
+    }
+    >iframe{
+      width: 100%;
+      height: 100%;
+    }
+    .pagna{
+      position: absolute;
+      bottom: 0;
+      text-align: center;
+      z-index: 999;
+      >li{
+        width: 10px;
+        border-radius: 50%;
+        height: 10px;
+        background: rgba(51, 143, 123, .24);
+        display: inline-block;
+        margin: 0 4px;
+        &.active{
+          background: #338F7B;
+        }
+      }
+    }
+  }
+  .title {
+    font-weight: bold;
+    font-size: 30px;
+    width: 100%;
+    padding: 0 50px;
+    margin-bottom: 10px;
+    text-align: center;
+  }
+.desc{
+    margin: 0 auto;
+    width: 70%;
+    color: #fff;
+    text-align: justify;
+    line-height: 1.8;
+    max-height: 120px;
+    overflow-y: auto;
+    font-size: 16px;
+    padding-right: 10px;
+  }
+}
+
+ .close{
+    position: fixed;
+    top: 40px;
+    right: 50px;
+    width: 20px;
+    height: 20px;
+    cursor: pointer;
+    z-index: 9999999;
+  }
+</style>

+ 265 - 0
pano/src/components/hotspot/multiple.vue

@@ -0,0 +1,265 @@
+<template>
+  <div class="multiple">
+    <div class="images">
+      <div class="img-con">
+        <span v-if="hotspot[type].length > 1" @click="handlePage('prev')">
+          <img :src="require('@/assets/images/project/left.png')" alt="" />
+        </span>
+        <img v-if="type == 'images'" v-viewer :src="g_dealUrl(hotspot[type][active])" alt="" />
+
+
+        <iframe
+          v-else
+          :src="g_dealUrl(hotspot[type][active])"
+          frameborder="0"
+        ></iframe>
+
+        <span v-if="hotspot[type].length > 1" @click="handlePage('next')">
+          <img :src="require('@/assets/images/project/right.png')" alt="" />
+        </span>
+        <ul class="pagna" v-if="hotspot[type].length > 1">
+          <li
+            v-for="(item, i) in hotspot[type]"
+            :class="{ active: i == active }"
+            :key="i"
+          ></li>
+        </ul>
+      </div>
+      <div class="title" v-html="hotspot.title"></div>
+      <div
+        class="desc"
+        v-html="
+          handleContent(hotspot.contents[contentactive] || hotspot.contents[0], 16)
+        "
+      ></div>
+    </div>
+
+    <ul class="btnlist">
+      <li @click="type = item.id" :class="{liactive:type == item.id}" v-for="(item,i) in btnlist" :key="i">
+        <span>{{item.title}}</span>
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script>
+
+let btntype = {
+  images:{
+    id:'images',
+    title:'图片',
+    img:''
+  },
+  iframe:{
+    id:'iframe',
+    title:'电子书',
+    img:''
+  },
+  model:{
+    id:'model',
+    title:'模型',
+    img:''
+  }
+}
+
+export default {
+  props: ["hotspot",'count'],
+  data() {
+    let btnlist = this.count.map(item=>{
+        return btntype[item]
+      })
+
+    return {
+      active: 0,
+      to: null,
+      contentactive:0,
+      type:this.count[0],
+      mbactive: 0,
+      btnlist
+    };
+  },
+ 
+  watch: {
+    type(newVal){
+      if(newVal == this.count[0]){
+        this.contentactive = 0
+      }
+      else{
+        this.contentactive += this.hotspot[this.type].length
+      }
+      this.active = 0
+    },
+    active() {
+      setTimeout(() => {
+        this.$showLoading();
+      });
+
+      if (this.to) {
+        clearTimeout(this.to);
+        this.to = null;
+      }
+      this.to = setTimeout(() => {
+        this.$hideLoading();
+      }, 1500);
+    },
+  },
+  beforeDestroy() {
+    this.$hideLoading();
+  },
+  methods: {
+    handlePage(type) {
+      if (type === "next") {
+        if (this.active >= this.hotspot[this.type].length - 1) {
+          this.active = 0;
+          return;
+        }
+
+        
+        this.active += 1;
+        this.contentactive += 1;
+      } else {
+        if (this.active == 0) {
+          this.active = this.hotspot[this.type].length - 1;
+          return;
+        }
+        this.active -= 1;
+        this.contentactive -= 1;
+
+      }
+    },
+  },
+  mounted() {
+    this.$showLoading();
+    if (this.TO) {
+      clearTimeout(this.TO);
+      this.TO = null;
+    }
+    this.TO = setTimeout(() => {
+      this.$hideLoading();
+    }, 800);
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.noshow {
+  opacity: 0 !important;
+  pointer-events: none !important;
+}
+.multiple {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  .images {
+    width: 1410px;
+    height: 760px;
+    text-align: center;
+    margin: 0 auto;
+    position: fixed;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    border-radius: 5px;
+    text-align: left;
+    color: #fff;
+
+    .img-con {
+      display: flex;
+      justify-content: space-around;
+      align-items: center;
+      position: relative;
+      padding-bottom: 40px;
+      height: calc(100% - 130px);
+
+      > span {
+        @juli: 60px;
+        display: inline-block;
+        padding: 0 30px;
+        box-sizing: content-box;
+        cursor: pointer;
+        position: absolute;
+        left: @juli;
+        top: 50%;
+        transform: translateY(-50%);
+        > img {
+          width: 20px;
+        }
+        &:last-of-type {
+          left: unset;
+          right: @juli;
+        }
+      }
+      > img {
+        max-width: 82%;
+        max-height: 520px;
+        cursor: pointer;
+      }
+
+      >iframe{
+        width: 100%;
+        height: 100%;
+      }
+      .pagna {
+        position: absolute;
+        bottom: 0;
+        text-align: center;
+        z-index: 999;
+        > li {
+          width: 10px;
+          border-radius: 50%;
+          height: 10px;
+          background: rgba(51, 143, 123, 0.24);
+          display: inline-block;
+          margin: 0 4px;
+          &.active {
+            background: #338f7b;
+          }
+        }
+      }
+    }
+    .title {
+      font-weight: bold;
+      font-size: 30px;
+      width: 100%;
+      padding: 0 50px;
+      margin-bottom: 10px;
+      text-align: center;
+    }
+    .desc {
+      margin: 0 auto;
+      width: 70%;
+      color: #fff;
+      text-align: justify;
+      line-height: 1.8;
+      max-height: 120px;
+      overflow-y: auto;
+      font-size: 16px;
+      padding-right: 10px;
+    }
+  }
+
+  .btnlist{
+    position: absolute;
+    right: 50px;
+    bottom: 50px;
+    display: inline-block;
+
+    @color: #e2c59a;
+    >li{
+      display: inline-block;
+      padding: 10px 20px;
+      min-width: 150px;
+      border-radius: 4px;
+      margin-left: 20px;
+      text-align: center;
+      color: @color;
+      border: 1px solid @color;
+      cursor: pointer;
+      &.liactive{
+        background: @color;
+        color: #fff;
+      }
+    }
+  }
+}
+</style>

+ 78 - 0
pano/src/components/hotspot/title.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="donate" @click.stop>
+    <p class="qtitle">{{hotspot.title}}</p>
+    <div class="qline">
+      <img :src="require(`@/assets/images/icon/star.png`)" alt="" />
+    </div>
+
+    <div class="txtcon">
+      <p v-html="hotspot.content"></p>
+    </div>
+   
+  </div>
+</template>
+
+<script>
+
+export default {
+  props:['hotspot'],
+};
+</script>
+
+<style lang="less" scoped>
+.donate {
+  width: 988px;
+  margin: 0 auto;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  z-index: 99;
+  background-color: rgba(3, 51, 48, 0.5);
+  color: #fff;
+  padding: 36px 34px;
+  border-top: 8px solid #ccaf8f;
+  border-bottom: 8px solid #ccaf8f;
+  background-repeat: no-repeat;
+  background-position: right bottom;
+  background-size: 100% auto;
+  .qtitle {
+    width: 100%;
+    height: 30px;
+    font-size: 30px;
+    font-weight: bold;
+    line-height: 38px;
+    color: #FFE0A6;
+    text-align: center;
+  }
+  .qline {
+    width: 100%;
+    height: 1px;
+    margin: 26px 0;
+    position: relative;
+    background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #FFFFFF 48%, rgba(255, 255, 255, 0) 100%);
+    > img {
+      position: absolute;
+      left: 50%;
+      transform: translateX(-50%);
+      width: 32px;
+      top: -12px;
+    }
+  }
+
+  .txtcon{
+    background-color: rgba(255, 255, 255, 0.7);
+    padding: 20px;
+    color: #626260;
+    p{
+      font-size: 18px;
+      line-height: 1.5;
+    }
+  }
+
+
+
+
+
+}
+</style>

+ 171 - 0
pano/src/components/hotspot/video.vue

@@ -0,0 +1,171 @@
+<template>
+  <div class="images">
+    <div class="img-con">
+      <span @click="active -= 1" :class="{ noshow: active <= 0 }">上一张</span>
+      <video controlslist="nodownload noremoteplayback"
+              disablePictureInPicture
+              controls
+              autoplay
+              v-show="isvideoshow"
+              @canplaythrough="isvideoshow = true"
+              @canplay="isvideoshow = true">
+        <source :src="g_dealUrl(hotspot.video[active].url)" type="video/mp4" />
+      </video>
+      <span
+        @click="active += 1"
+        :class="{ noshow: active >= hotspot.video.length - 1 }"
+        >下一张</span
+      >
+      <ul class="pagna" v-if="hotspot.video.length>1">
+        <li
+          v-for="(item, i) in hotspot.video"
+          :class="{ active: i == active }"
+          :key="i"
+        ></li>
+      </ul>
+    </div>
+    <div class="title">{{ hotspot.title }}</div>
+    <div class="desc" v-if="hotspot.content" v-html="hotspot.content"></div>
+
+  </div>
+ 
+</template>
+
+<script>
+import { directive } from "vue-awesome-swiper";
+// import style (<= Swiper 5.x)
+import "swiper/css/swiper.css";
+
+export default {
+  directives: {
+    swiper: directive,
+  },
+  props: ["hotspot"],
+  data() {
+    return {
+      active: 0,
+      isvideoshow:false,
+      TO:null
+    };
+  },
+  watch:{
+    isvideoshow:{
+      handler:function (newVal){
+        if (newVal) {
+          this.$hideLoading();
+        }
+      }
+    }
+  },
+  mounted() {
+    this.$showLoading();
+       if (this.TO) {
+        clearTimeout(this.TO)
+        this.TO = null
+      }
+      this.TO = setTimeout(() => {
+        this.$hideLoading();
+      },10*1000);
+  },
+ 
+};
+</script>
+
+<style lang="less" scoped>
+.noshow{
+  opacity: 0!important;;
+  pointer-events: none!important;;
+}
+
+.images{
+  width: 1410px;
+  height: 760px;
+  text-align: center;
+  margin: 0 auto;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  border-radius: 5px;
+  text-align: left;
+
+  .img-con{
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    position: relative;
+    padding-bottom: 40px;
+    >span{
+      @juli:60px;
+      display: inline-block;
+      padding: 0 30px;
+      box-sizing: content-box;
+      cursor: pointer;
+      position: absolute;
+      left: @juli;
+      top: 50%;
+      transform: translateY(-50%);
+      >img{
+        width: 20px;
+      }
+      &:last-of-type{
+        left: unset;
+        right: @juli;
+      }
+    }
+    >video{
+      max-width: 100%;
+      max-height: 520px;
+      cursor: pointer;
+      margin-top: 50px;
+    }
+    .pagna{
+      position: absolute;
+      bottom: 0;
+      text-align: center;
+      z-index: 999;
+      >li{
+        width: 10px;
+        border-radius: 50%;
+        height: 10px;
+        background: rgba(51, 143, 123, .24);
+        display: inline-block;
+        margin: 0 4px;
+        &.active{
+          background: #338F7B;
+        
+        }
+      }
+    }
+  }
+.title {
+    font-weight: bold;
+    font-size: 30px;
+    text-align: center;
+    width: 100%;
+    padding: 0 50px;
+    margin-bottom: 10px;
+  }
+    .desc{
+    margin: 0 auto;
+    width: 70%;
+    color: #fff;
+    text-align: justify;
+    line-height: 1.8;
+    max-height: 120px;
+    overflow-y: auto;
+    font-size: 16px;
+    padding: 0;
+  }
+}
+
+ .close{
+    position: absolute;
+    top: 40px;
+    right: 50px;
+    width: 20px;
+    height: 20px;
+    cursor: pointer;
+    z-index: 9999999;
+  }
+</style>

+ 27 - 0
pano/src/components/hotspot/yuange.js

@@ -0,0 +1,27 @@
+const data = {
+  0:'',
+  8:'我们是人民的子弟兵,在硝烟中守护安宁', //10代表歌词开始的秒数
+  17:'我们是光荣的白衣天使,在春风里绽放笑容',
+  24:'救死扶伤的行列里,我们是排头兵',
+  32:'圣洁的无影灯下走过,走过无悔的人生',
+  40:'用爱心浇灌生命之树常青',
+  45:'用奉献书写神圣历史使命',
+  50:'红十字星座镌刻着我们的姓名',
+  58:'八一军旗上飞扬着我们的光荣',
+  67:'忠诚信仰,血脉传承,追求卓越攀高峰',
+  73:'忠诚信仰,血脉传承,追求卓越攀高峰',
+  90:'我们是人民的子弟兵,在硝烟中守护安宁', //10代表歌词开始的秒数
+  98:'我们是光荣的白衣天使,在春风里绽放笑容',
+  105:'救死扶伤的行列里,我们是排头兵',
+  112:'圣洁的无影灯下走过,走过无悔的人生',
+  121:'用爱心浇灌生命之树常青',
+  126:'用奉献书写神圣历史使命',
+  131:'红十字星座镌刻着我们的姓名',
+  139:'八一军旗上飞扬着我们的光荣',
+  147:'忠诚信仰,血脉传承,追求卓越攀高峰',
+  154:'忠诚信仰,血脉传承,追求卓越攀高峰',
+  169:'',
+
+}
+
+export { data };

+ 244 - 0
pano/src/components/hovercom/dati/data.js

@@ -0,0 +1,244 @@
+let data = [
+  {
+    id: 1,
+    question: "解放军总医院的院训是什么?",
+    answer: [
+      {
+        val: "A",
+        title: "敬业、忠诚、厚德、创新",
+      },
+      {
+        val: "B",
+        title: "忠诚、敬业、创新、厚德",
+      },
+      {
+        val: "C",
+        title: "忠诚、厚德、创新、敬业",
+      },
+      {
+        val: "D",
+        title: "忠诚、敬业、厚德、创新",
+      },
+    ],
+    correct: "D",
+  },
+
+  {
+    id: 2,
+    question: "关于修建解放军总医院提出 “先准备、后建设”的国家领导人是谁?",
+    answer: [
+      {
+        val: "A",
+        title: "周恩来",
+      },
+      {
+        val: "B",
+        title: "毛泽东",
+      },
+      {
+        val: "C",
+        title: "朱德",
+      },
+      {
+        val: "D",
+        title: "聂荣臻",
+      },
+    ],
+    correct: "B",
+  },
+
+  {
+    id: 3,
+    question: "军委直属机关医院改称中国人民解放军第三〇一医院的时间是?",
+    answer: [
+      {
+        val: "A",
+        title: "1954年7月2日",
+      },
+      {
+        val: "B",
+        title: "1955年7月1日",
+      },
+      {
+        val: "C",
+        title: "1953年8月5日",
+      },
+      {
+        val: "D",
+        title: "1954年8月4日",
+      },
+    ],
+    correct: "D",
+  },
+
+  {
+    id: 4,
+    question: "解放军总医院首任院长是谁?",
+    answer: [
+      {
+        val: "A",
+        title: "靳来川",
+      },
+      {
+        val: "B",
+        title: "白崇友",
+      },
+      {
+        val: "C",
+        title: "蒲荣钦",
+      },
+      {
+        val: "D",
+        title: "涂通今",
+      },
+    ],
+    correct: "A",
+  },
+
+  {
+    id: 5,
+    question: "20世纪90年代,解放军总医院哪位专家当选为中国工程院首批院士?",
+    answer: [
+      {
+        val: "A",
+        title: "黄志强",
+      },
+      {
+        val: "B",
+        title: "盛志勇",
+      },
+      {
+        val: "C",
+        title: "姜泗长",
+      },
+      {
+        val: "D",
+        title: "卢世璧",
+      },
+    ],
+    correct: "C",
+  },
+
+  {
+    id: 6,
+    question:
+      "解放军总医院2018年,解放军总医院转隶联勤保障部队的日期是?的院训是什么?",
+    answer: [
+      {
+        val: "A",
+        title: "12月28日",
+      },
+      {
+        val: "B",
+        title: "12月31日",
+      },
+      {
+        val: "C",
+        title: "12月30日",
+      },
+      {
+        val: "D",
+        title: "12月29日",
+      },
+    ],
+    correct: "B",
+  },
+
+  {
+    id: 7,
+    question: "解放军总医院共有几个医学中心、几个医疗区?",
+    answer: [
+      {
+        val: "A",
+        title: "8、5",
+      },
+      {
+        val: "B",
+        title: "9、6",
+      },
+      {
+        val: "C",
+        title: "14 、5",
+      },
+      {
+        val: "D",
+        title: "10、8",
+      },
+    ],
+    correct: "A",
+  },
+
+  {
+    id: 8,
+    question: "解放军总医院共有多少位院士?",
+    answer: [
+      {
+        val: "A",
+        title: "12",
+      },
+      {
+        val: "B",
+        title: "10",
+      },
+      {
+        val: "C",
+        title: "11",
+      },
+      {
+        val: "D",
+        title: "9",
+      },
+    ],
+    correct: "A",
+  },
+
+  {
+    id: 9,
+    question: "解放军总医院共有多少个临床医学部?",
+    answer: [
+      {
+        val: "A",
+        title: "8",
+      },
+      {
+        val: "B",
+        title: "21",
+      },
+      {
+        val: "C",
+        title: "17",
+      },
+      {
+        val: "D",
+        title: "25",
+      },
+    ],
+    correct: "B",
+  },
+
+  {
+    id: 10,
+    question: "解放军总医院获得过几项国家科技进步一等奖?",
+    answer: [
+      {
+        val: "A",
+        title: "9",
+      },
+      {
+        val: "B",
+        title: "14",
+      },
+      {
+        val: "C",
+        title: "12",
+      },
+      {
+        val: "D",
+        title: "15",
+      },
+    ],
+    correct: "C",
+  },
+];
+
+export { data };

+ 403 - 0
pano/src/components/hovercom/dati/index.vue

@@ -0,0 +1,403 @@
+<template>
+  <div class="datiing" @click.stop>
+    <p class="qtitle">学史力行</p>
+    <div class="qline">
+      <img :src="require(`@/assets/images/icon/star.png`)" alt="" />
+    </div>
+    <template v-if="datiing">
+      <div class="dati">
+        <p class="title">{{currentIdx+1 + '、' + currentTimu.question || '-' }}</p>
+        <ul>
+          <li
+            :class="{
+              active: timuselect == item.val && !isSubmit,
+              dui: item.val == currentTimu.correct && isSubmit,
+              cuo:
+                item.val == timuselect &&
+                item.val != currentTimu.correct &&
+                isSubmit,
+              normal: timuselect == item.val && !isSubmit,
+            }"
+            v-for="(item, i) in currentTimu.answer"
+            @click="dati(item)"
+            :key="i"
+          >
+            <div>
+              <span></span>
+            </div>
+            <span>{{ "ABCD"[i] }}、{{ item.name }}</span>
+          </li>
+        </ul>
+      </div>
+      <div class="dati_btn">
+        <span class="active" @click="handleNext">{{
+          canNext ? "下一题" : "确认"
+        }}</span>
+      </div>
+    </template>
+
+    <template v-else>
+      <div class="dati">
+        <div class="success" >
+          
+        <template  v-if="correctRate==100">
+          <img :src="require(`@/assets/images/project/jin.png`)" alt="" />
+          <jiangpai class="jiangpai"/>
+          <span>恭喜您完成挑战!</span>
+          <p class="scess">您的得分:{{correctRate}}分</p>
+        </template>
+
+         <template v-else-if="correctRate>=60&&correctRate<=90">
+          <img :src="require(`@/assets/images/project/yin.png`)" alt="" />
+          <jiangpai class="jiangpai"/>
+          <span  class="yinspan">恭喜您完成挑战!</span>
+          <p class="yin">您的得分:{{correctRate}}分</p>
+        </template>
+
+        <template v-else>
+          <img :src="require(`@/assets/images/project/tong.png`)" alt="" />
+          <jiangpai class="jiangpai"/>
+          <span class="tongspan">不要灰心,再接再厉!</span>
+          <p class="tong">您的得分:{{correctRate}}分</p>
+        </template>
+
+        </div>
+        <!-- <div class="fail" v-else>
+          <img :src="require(`@/assets/images/project/blue.png`)" alt="" />
+          <p>不要灰心,再接再厉!</p>
+        </div> -->
+      </div>
+      <div class="dati_btn">
+        <span class="active" @click="restart">再来一次</span>
+        <span @click="$emit('close')">完成</span>
+      </div>
+    </template>
+
+    <img
+      @click="$emit('close')"
+      class="close"
+      :src="require('@/assets/images/icon/close1.png')"
+      alt=""
+    />
+  </div>
+</template>
+
+<script>
+// import { data } from "./data";
+import jiangpai from "@/components/jiangpai";
+import { getQuestionGroupList } from "@/config/api";
+
+export default {
+  components:{jiangpai},
+  data() {
+    return {
+      timu: [],
+      datiing:true,
+      currentIdx: 0,
+      timuselect: "",
+      isSubmit: false,
+      canNext: false,
+      correctNum: 0,
+      correctRate: 0,
+    };
+  },
+  computed: {
+    currentTimu() {
+      return this.timu[this.currentIdx] || {};
+    },
+  },
+  watch: {
+    currentIdx(newVal) {
+      if (newVal >= this.timu.length) {
+        this.correctRate = Math.floor(
+          (this.correctNum / this.timu.length) * 100
+        );
+        this.datiing = false
+      }
+    },
+  },
+  methods: {
+    dati(item) {
+      if (this.canNext) {
+        return;
+      }
+      this.timuselect = item.val;
+    },
+    restart(){
+      this.datiing = true;
+      this.currentIdx = 0;
+      this.correctNum= 0;
+      this.correctRate= 0;
+    },
+    reset() {
+      this.canNext = false;
+      this.isSubmit = false;
+      this.timuselect = "";
+
+    },
+    handleNext() {
+      if(!this.canNext){
+        this.$bus.$emit("changexlzmode",this.timuselect == this.currentTimu.correct?"Right":'Wrong')
+      }
+      if (this.timuselect == this.currentTimu.correct || this.canNext) {
+        if (this.timuselect == this.currentTimu.correct) {
+          this.correctNum++;
+        }
+
+       
+        this.reset();
+        this.currentIdx++;
+      } else {
+        this.isSubmit = true;
+        this.canNext = true;
+      }
+    },
+    getDetail() {
+      getQuestionGroupList({}, (res) => {
+        if (res.code==0) {
+          res.data = res.data.map((item) => {
+            item.answer = JSON.parse(item.answer);
+            return item;
+          });
+          this.timu = res.data;
+        }
+
+      });
+    },
+  },
+  mounted() {
+    this.getDetail()
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.datiing {
+  width: 750px;
+  margin: 0 auto;
+  position: fixed;
+  height: 520px;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  z-index: 99;
+  background: rgba(3, 51, 48, 0.5);
+  color: #fff;
+  padding: 30px 36px 0;
+  border-top: 8px solid #ccaf8f;
+  border-bottom: 8px solid #ccaf8f;
+  .qtitle {
+    margin-top: 6px;
+    text-align: center;
+    font-size: 28px;
+    font-weight: bold;
+    line-height: 38px;
+    color: #ffe0a6;
+  }
+  .qline {
+    width: 100%;
+    height: 1px;
+    margin: 26px 0;
+    position: relative;
+    background: linear-gradient(
+      90deg,
+      rgba(255, 255, 255, 0) 0%,
+      #ffffff 0%,
+      #ffffff 48%,
+      rgba(255, 255, 255, 0) 100%
+    );
+    > img {
+      position: absolute;
+      left: -10px;
+      width: 32px;
+      top: -12px;
+    }
+  }
+  .dati {
+    width: 100%;
+    height: 64%;
+    overflow-y: auto;
+    margin: 0 auto;
+    padding: 48px 38px 0;
+    background-color: rgba(255, 255, 255, 0.7);
+    color: #626260;
+      .title{
+        text-align: left;
+        line-height: 1.5;
+        font-weight: bold;
+        font-size: 18px;
+      }
+      >ul{
+        display: inline-block;
+        width: 100%;
+        padding: 0 15px;
+        >li{
+          display: flex;
+          align-items: center;
+          font-size: 18px;
+          margin: 20px 0;
+          cursor: pointer;
+          >div{
+            width: 22px;
+            height:22px;
+            border: 1px solid #626260;
+            border-radius: 50%;
+            position: relative;
+            >span,>img{
+              position: absolute;
+              top: 50%;
+              left: 50%;
+              transform: translate(-50%,-50%);
+            }
+          
+            >span{
+              width: 14px;
+              height: 14px;
+              border-radius: 50%;
+            }
+          }
+          
+          >span{
+            font-size: 16px;
+            display: inline-block;
+            margin-left: 20px;
+          }
+
+          &.active{
+            >div{
+              color: #B8A38C;
+              border: 1px solid #B8A38C;
+              >span{
+                width: 14px;
+                height: 14px;
+                background: #B8A38C;
+                border-radius: 50%;
+
+              }
+            }
+            >span{
+              color: #B8A38C;
+            }
+          }
+        }
+        .dui{
+          color: #34A78E;
+          >div{
+            border: 1px solid #34A78E;
+            >span{
+              background: #34A78E;
+            }
+          }
+          >span{
+            color: #34A78E;
+          }
+        }
+        .cuo{
+          color: #E45560;
+          >div{
+            border: 1px solid #E45560;
+            >span{
+              background: #E45560;
+            }
+          }
+          >span{
+            color: #E45560;
+          }
+        }
+      }
+    .success{
+      position: relative;
+      width: 224px;
+      margin: 0 auto 0;
+      >img{
+        width: 100%;
+        z-index: 1;
+        position: relative;
+      }
+      >span{
+        width: 100%;
+        bottom: 40px;
+        position: absolute;
+        left: 50%;
+        transform: translateX(-47%);
+        font-size: 18px;
+        color: #215A4A;
+        display: inline-block;
+        font-weight: bold;
+        z-index: 2;
+      }
+
+      .yinspan{
+        color: #626260;
+      }
+
+      .tongspan{
+        color: #FFE0A6;
+      }
+
+      .scess{
+        color: #AF924C;
+      }
+      .yin{
+        color: #828282;
+      }
+      .tong{
+        color: #8F5E49;
+      }
+
+      .jiangpai{
+        position: absolute;
+        top: -120px;
+        left: -50%;
+      }
+    }
+    
+    .fail{
+      width: 235px;
+      margin: 20px auto;
+      >p{
+        font-size: 19px;
+        font-weight: bold;
+        margin: 10px 0;
+        color: #626260;
+      }
+
+    }
+    >p{
+      font-size: 14px;
+    }
+   
+  }
+
+
+  .dati_btn {
+    text-align: center;
+    > span {
+      display: inline-block;
+      color: #FCF5D3;
+      font-size: 18px;
+      line-height: 35px;
+      height: 35px;
+      cursor: pointer;
+      width: 106px;
+      margin: 20px;
+      border: 1px solid #FCF5D3;
+      &.active {
+        color: #fff;
+        background: #ccaf8f;
+        border-color: #ccaf8f;
+      }
+    }
+  }
+
+  .close {
+    position: absolute;
+    top: 45px;
+    right: 38px;
+    width: 20px;
+    height: 20px;
+    cursor: pointer;
+    z-index: 9999999;
+  }
+}
+</style>

+ 192 - 0
pano/src/components/hovercom/donate/index.vue

@@ -0,0 +1,192 @@
+<template>
+  <div
+    class="donate"
+    @click.stop
+    :style="{
+      backgroundImage: `url(${require(`@/assets/images/project/jzdw.png`)})`,
+    }"
+  >
+    <p class="qtitle">公益捐赠</p>
+    <div class="qline">
+      <img :src="require(`@/assets/images/icon/star.png`)" alt="" />
+    </div>
+    <p>感谢您的捐赠,稍后会有工作人员与您联系!</p>
+    <ul>
+      <li>
+        <input
+          v-model="name"
+          maxlength="20"
+          type="text"
+          placeholder="请填写您的姓名"
+        />
+      </li>
+      <li>
+        <input
+          v-model="phone"
+          maxlength="20"
+          type="text"
+          oninput="value=value.replace(/[^\d\-]/g,'')"
+          placeholder="请填写您的电话号码"
+        />
+      </li>
+      <li>
+        <textarea
+          v-model="description"
+          maxlength="200"
+          cols="30"
+          placeholder="请填写捐赠说明"
+          rows="10"
+        ></textarea>
+      </li>
+    </ul>
+
+    <div class="dati_btn">
+      <span @click="submit">提 交</span>
+    </div>
+    <img
+      @click="$emit('close')"
+      class="close"
+      :src="require('@/assets/images/icon/close1.png')"
+      alt=""
+    />
+  </div>
+</template>
+
+<script>
+import { donate } from "@/config/api";
+
+export default {
+  data() {
+    return {
+      description: "",
+      name: "",
+      phone: "",
+    };
+  },
+  methods: {
+    submit() {
+      let { description, name, phone } = this;
+      if(!description.trim()|| !name.trim()|| !phone.trim()){
+        return alert('请补充完整信息')
+      }
+      donate(
+        {
+          description,
+          name,
+          phone,
+        },
+        (res) => {
+          if (res.code == 0) {
+            alert("信息已提交,稍后会有工作人员与您联系!");
+            this.$emit("close");
+          }
+        }
+      );
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.donate {
+  width: 585px;
+  margin: 0 auto;
+  min-height: 736px;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  z-index: 99;
+  background-color: rgba(3, 51, 48, 0.5);
+  color: #fff;
+  padding: 36px 80px 0;
+  border-top: 8px solid #ccaf8f;
+  border-bottom: 8px solid #ccaf8f;
+  background-repeat: no-repeat;
+  background-position: right bottom;
+  background-size: 100% auto;
+  .qtitle {
+    width: 100%;
+    height: 30px;
+    font-size: 28px;
+    font-weight: bold;
+    line-height: 38px;
+    color: #ffe0a6;
+    text-align: center;
+  }
+  .qline {
+    width: 100%;
+    height: 1px;
+    margin: 26px 0;
+    position: relative;
+    background: linear-gradient(
+      90deg,
+      rgba(255, 255, 255, 0) 0%,
+      #ffffff 48%,
+      rgba(255, 255, 255, 0) 100%
+    );
+    > img {
+      position: absolute;
+      left: 50%;
+      transform: translateX(-50%);
+      width: 32px;
+      top: -12px;
+    }
+  }
+
+  > p {
+    color: #eff2f1;
+  }
+
+  > ul {
+    > li {
+      background-color: rgba(255, 255, 255, 0.7);
+      margin: 10px auto;
+      text-align: left;
+      border-radius: 2px;
+      overflow: hidden;
+      > input,
+      > textarea {
+        background: none;
+        padding: 0 10px;
+        color: #626260;
+        line-height: 1.5;
+        height: 52px;
+        width: 100%;
+        border: none;
+        &::placeholder {
+          color: #626260;
+        }
+      }
+      > textarea {
+        padding: 10px;
+        height: 180px;
+      }
+    }
+  }
+
+  .dati_btn {
+    > span {
+      display: inline-block;
+      color: #fff;
+      font-size: 18px;
+      line-height: 45px;
+      height: 45px;
+      cursor: pointer;
+      width: 168px;
+      margin: 20px auto;
+      background: #ccaf8f;
+    }
+  }
+
+  .close {
+    position: absolute;
+    top: 40px;
+    right: 36px;
+    width: 24px;
+    height: 24px;
+    cursor: pointer;
+    z-index: 9999999;
+  }
+}
+</style>

+ 49 - 0
pano/src/components/hovercom/index.vue

@@ -0,0 +1,49 @@
+<template>
+  <div class="hovercon" @click.stop @keydown.stop :class="{hovershow:showType,brightnessBigW:showType}">
+      <template v-if="showType">
+        <dati v-if="showType=='Q&A'" @close="close"/>
+        <donate v-else-if="showType=='donate'" @close="close"/>
+        <message v-else @close="close"/>
+      </template>
+  </div>
+</template>
+
+<script>
+import message from "./message";
+import donate from "./donate";
+import dati from "./dati";
+
+export default {
+  props:['showType'],
+  components: {
+    message,
+    donate,
+    dati,
+  },
+  methods:{
+    close(){
+      this.$emit('close')
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.hovercon{
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    transition: .3s ease all;
+    pointer-events: none;
+    background: rgba(0, 0, 0, .0);
+    z-index: 999;
+    text-align: center;
+  }
+  .hovershow{
+    background: rgba(0, 0, 0, .3);
+    pointer-events: auto;
+  }
+
+</style>

+ 393 - 0
pano/src/components/hovercom/message/Message.vue

@@ -0,0 +1,393 @@
+<template>
+  <div class="message-body">
+    <div class="title">
+      <span>留言板</span>
+    </div>
+    <div class="message">
+      <div class="con">
+
+        <ul>
+          <li v-for="(item, i) in message" :key="i">
+            <img
+              :src="require(`@/assets/images/icon/usericon.png`)"
+              alt=""
+            />
+            <div>
+              <p>{{item.name||'匿名用户'}}</p>
+              <p>{{item.content}}</p>
+            </div>
+            <p>{{item.createTime}}</p>
+
+          </li>
+        </ul>
+
+        <Paging class="paging" v-if="paging.total>0" @changeCurrent="handleCurrent" :paging="paging" />
+        
+        <span class="totalmsg">共{{paging.total}}条留言</span>
+      </div>
+
+      <div class="vtextarea">
+        <textarea onkeypress="if(event.keyCode==13) return false;" v-model="msg" rows="5" maxlength="500" cols="20" placeholder="请输入留言......"></textarea>
+        <emoji class="emoji" @select="selectEmoji" :type="0" id="biaoqing1"/>
+        <!-- <span class="maxlength">{{ msg.length }}/100</span> -->
+      </div>
+
+      <div class="submitcon">
+        <ul>
+          <li :class="{active:item.id == select}" @click="select=item.id" v-for="(item,i) in types" :key="i">
+            <span></span>
+            <span class="name">{{item.name}}</span>
+          </li>
+        </ul>
+
+        <div class="ipp">
+          <input v-model="nickname" maxlength="20" :class="{disable:select=='none'}" :disabled="select=='none'" type="text" >
+        </div>
+        <div class="btnww" @click="leaveMsg">提交</div>
+      </div>
+  
+    </div>
+    <img @click="$emit('close')" class="close" :src="require('@/assets/images/icon/close1.png')" alt="">
+  </div>
+</template>
+
+<script>
+import { getMsg,saveMsg } from "@/config/api";
+import emoji from "@/components/emoji";
+import Paging from '@/components/Paging'
+
+export default {
+  components:{emoji,Paging},
+  data() {
+    return {
+      lock:false,
+      types:[{
+        name:'匿名发表',
+        id:'none'
+      },{
+        name:'您的昵称',
+        id:'nickname'
+      }],
+      select:'none',
+      nickname:'',
+      msg: "",
+      message: [],
+      paging: {
+        pageSize: 8,
+        pageNum: 1,
+        total: 0,
+        showSize: 2,
+        current: 1,
+      },
+    };
+  },
+  watch: {
+   'paging.current':{
+     deep:true,
+     handler:function () {
+      this.getMessage()
+     }
+   }
+  },
+  mounted() {
+    this.getMessage()
+  },
+  methods: {
+     async getMessage(){
+      getMsg({
+        pageNum: this.paging.current,
+        pageSize: this.paging.pageSize,
+      },result=>{
+        this.message = result.data.list
+        this.paging.total= result.data.total
+      })
+    },
+    selectEmoji(data){
+      if (this.msg.length<500) {
+        this.msg += data
+      }
+    },
+    handleCurrent(data){
+      this.paging.current = data
+    },
+
+    async leaveMsg() {
+      let tmp = this.msg.trim();
+      if (!tmp) {
+        return alert("留言不能为空");
+      }
+
+      if (this.select == "nickname") {
+        if (!this.nickname.trim()) {
+          return alert("昵称不能为空");
+        }
+      }
+
+
+      if (this.lock) {
+        return         
+      }
+      
+      this.lock = true
+      saveMsg({
+          content: tmp,
+          name: this.select == "nickname"?this.nickname:'匿名用户'
+        },
+        (res) => {
+          this.lock = false
+          if (res.code == 0) {
+            this.msg = "";
+            this.nickname = ""
+            alert("留言成功");
+            this.getMessage();
+          }else{
+            alert("留言失败,请稍后再试");
+          }
+        }
+      );
+    }
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.message-body {
+  width: 1120px;
+  margin: 0 auto;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  z-index: 99;
+  background: rgba(3, 51, 48, 0.5);
+  color: #fff;
+  padding: 30px 70px;
+  border-top: 8px solid  #CCAF8F;
+  border-bottom: 8px solid  #CCAF8F;
+  .title {
+    margin: 0 auto 37px;
+    position: relative;
+    > span {
+      display: inline-block;
+      height: 30px;
+      font-size: 30px;
+      font-weight: bold;
+      line-height: 38px;
+      color: #FFE0A6;
+    }
+  }
+  .message {
+    height: 100%;
+
+    .con {
+      width: 100%;
+      height: 397px;
+      position: relative;
+      box-sizing: content-box;
+      background-color: #fff;
+      color: #434343;
+   
+      > ul {
+        height: calc(100% - 60px);
+        overflow-y: auto;
+        margin-top: 20px;
+        padding: 0 20px 20px;
+        > li {
+          display: flex;
+          justify-content: space-between;
+          border-bottom: 1px dashed #CFC5C5;
+          padding: 14px 0;
+          @wh: 50px;
+          &:last-of-type {
+            border-bottom: none;
+          }
+          > img {
+            width: @wh;
+            height: @wh;
+            flex-shrink: 0;
+            border-radius: 50%;
+          }
+          > div {
+            text-align: left;
+            flex: 10;
+            margin-left: 20px;
+            > p {
+              &:first-of-type {
+                font-size: 24px;
+                margin-bottom: 10px;
+              }
+              &:last-of-type {
+                font-size: 14px;
+              }
+            }
+          }
+          > p {
+            font-size: 14px;
+            margin-top: 10px;
+            color: #D6D6D6;
+            flex-shrink: 0;
+          }
+        }
+      }
+
+      .paging{
+        margin-top: 10px;
+      }
+      .totalmsg{
+        position: absolute;
+        bottom: 20px;
+        right: 20px;
+      }
+      > div {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+
+        .button {
+          position: relative;
+          width: 150px;
+          &:first-of-type {
+            margin-right: 50px;
+          }
+          > span {
+            position: absolute;
+            left: -220px;
+            top: 50%;
+            transform: translateY(-50%);
+            display: flex;
+            align-items: center;
+            > img {
+              width: 24px;
+              height: 24px;
+              margin-right: 8px;
+            }
+          }
+        }
+      }
+    }
+    .vtextarea{
+        width: 100%;
+        margin: 20px auto 0;
+        position: relative;
+        color: #626260;
+        background: rgba(255, 255, 255, .7);
+        padding:10px 20px 40px;
+        box-sizing: border-box;
+        >textarea{
+          outline:none;
+          width: 100%;
+          border: none;
+          font-size: 16px;
+          background: none;
+          &::placeholder{
+            color: #626260;
+          }
+        }
+        .emoji{
+          position: absolute;
+          bottom: -6px;
+          left: 18px;
+        }
+        .maxlength{
+          position: absolute;
+          bottom: 10px;
+          right: 18px;
+        }
+      }
+      
+    .submitcon{
+      margin-top: 30px;
+      display: flex;
+      align-items: center;
+      position: relative;
+      color: #FCF5D3;
+      >ul{
+        display: flex;
+        align-items: center;
+        >li{
+          display: flex;
+          margin-right: 20px;
+          align-items: center;
+          cursor: pointer;
+          >span{
+            display: inline-block;
+            &:first-of-type{
+              position: relative;
+              border-radius: 50%;
+              width: 30px;
+              height: 30px;
+              border: 1px solid #FCF5D3;
+              border-radius: 50%;
+            }
+            &:last-of-type{
+              margin-left: 10px;
+            }
+          }
+          &.active{
+           >span{
+              &:first-of-type{
+                &::before{
+                  width: 20px;
+                  height: 20px;
+                  display: inline-block;
+                  content: '';
+                  border-radius: 50%;
+                  background: #FCF5D3;
+                  position: absolute;
+                  top: 50%;
+                  left: 50%;
+                  transform: translate(-50%,-50%);
+                }
+            }
+           }
+          }
+        }
+      }
+      .ipp{
+        width: 280px;
+        display: inline-block;
+        position: relative;
+        border-radius: 5px;
+        height: 30px;
+        line-height: 30px;
+        text-align: left;
+        padding: 0 6px;
+        border: 1px solid #FCF5D3;
+        font-size: 0;
+        >input{
+          background: none;
+          border: none;
+          user-select: none;
+          height: 30px;
+          line-height: 30px;
+          width: 100%;
+          color: #fff;
+          font-size: 14px;
+          vertical-align: middle;
+        }
+      }
+      .btnww{
+        width: 106px;
+        height: 35px;
+        line-height: 35px;
+        background: #CCAF8F;
+        text-align: center;
+        border-radius: 0px;
+        position: absolute;
+        right: 0;
+        color: #fff;
+        cursor: pointer;
+      }
+    }
+  }
+  .close{
+    position: absolute;
+    top: 40px;
+    right: 50px;
+    width: 24px;
+    height: 24px;
+    cursor: pointer;
+    z-index: 9999999;
+  }
+}
+</style>

+ 49 - 0
pano/src/components/hovercom/message/index.vue

@@ -0,0 +1,49 @@
+<template>
+  <div class="msg-con" :class="{msgshow:isshow}">
+      <Message @close="close"/>
+  </div>
+</template>
+
+<script>
+import Message from "./Message";
+
+export default {
+  components: {
+    Message,
+  },
+  data(){
+    return {
+      isshow:false
+    }
+  },
+  mounted() {
+    setTimeout(() => {
+      this.isshow = true
+    });
+  },
+  methods:{
+    close(){
+      this.isshow = false
+      setTimeout(() => {
+        this.$emit('close')
+      }, 300);
+   
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.msg-con {
+  position: relative;
+  color: #fff;
+  height: 100%;
+  width: 100%;
+  opacity: 0;
+  transition: opacity .3s ease;
+}
+
+.msgshow{
+  opacity: 1;
+}
+</style>

+ 60 - 0
pano/src/components/huizhang/huizhang.vue

@@ -0,0 +1,60 @@
+<template>
+  <div class="huizcon">
+    <div class="success">
+      <img :src="require(`@/assets/images/project/jin.png`)" alt="" />
+
+      <jiangpai class="jiangpai"/>
+      <span>您是第{{visit||'-'}}位参观者!</span>
+    </div>
+    <p>达成成就:累计游览3分钟</p>
+    <p>感谢您对解放军总医院数字史馆的支持!</p>
+  </div>
+</template>
+
+<script>
+import jiangpai from "@/components/jiangpai";
+
+export default {
+  props:['visit'],
+  components:{jiangpai}
+}
+</script>
+
+<style lang="less" scoped>
+.huizcon{
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+    .success{
+      position: relative;
+      width: 310px;
+      margin: 0 auto 30px;
+      >img{
+        width: 100%;
+      }
+      >span{
+        width: 100%;
+        bottom: 28px;
+        position: absolute;
+        left: 50%;
+        transform: translateX(-48%);
+        font-size: 20px;
+        color: #215A4A;
+        display: inline-block;
+        font-weight: bold;
+      }
+      .jiangpai{
+        position: absolute;
+        top: -110px;
+        left: -24%;
+        z-index: -1;
+      }
+    }
+
+    >p{
+      font-size: 20px;
+      margin-top: 6px;
+    }
+}
+</style>

+ 49 - 0
pano/src/components/huizhang/index.vue

@@ -0,0 +1,49 @@
+<template>
+  <div class="hovercon" @click.stop @keydown.stop :class="{hovershow:showhuizhang,brightness:showhuizhang}" >
+    <template v-if="showhuizhang">
+      <huizhang :visit='visit' />
+      <img @click="$emit('close')" class="close" :src="require('@/assets/images/project/cancel.png')" alt="">
+    </template>
+  </div>
+</template>
+
+<script>
+import huizhang from './huizhang.vue';
+
+
+export default {
+  props: ['showhuizhang','visit'],
+  components:{
+    huizhang
+  }
+}
+
+</script>
+
+<style lang="less" scoped>
+.hovercon{
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    transition: .3s ease all;
+    pointer-events: none;
+    background: rgba(0, 0, 0, .0);
+  }
+    .close{
+    position: fixed;
+    top: 40px;
+    right: 50px;
+    width: 54px;
+    height: 54px;
+    cursor: pointer;
+    z-index: 9999999;
+  }
+.hovershow{
+  background: rgba(0, 0, 0, .3);
+  pointer-events: auto;
+  z-index: 999999;
+}
+
+</style>

+ 36 - 0
pano/src/components/jiangpai/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <div class="jiangpai">
+    <img :src="require(`@/assets/images/project/faguang.png`)" alt="" />
+  </div>
+</template>
+
+<script>
+export default {
+  
+}
+</script>
+
+<style lang="less" scoped>
+.jiangpai{
+  position: relative;
+  width: 450px;
+  height: 350px;
+  overflow: hidden;
+  z-index: 0;
+  transform: scale(0.8);
+  >img{
+    position: absolute;
+    width: 21600px;
+    height: 350px;
+    left: 0;
+    top: 0;
+    animation: faguang 2s steps(24) infinite;
+  }
+}
+
+@keyframes faguang {
+  100% {
+    left: -21600px;
+  }
+}
+</style>

+ 130 - 0
pano/src/components/popupLayout/Alert.vue

@@ -0,0 +1,130 @@
+<template>
+  <popup ref="Message" :show="show">
+    <div class="ui-message">
+      <div class="ui-message-header">
+        <img @click="onClose" :src="`${$cdn}images/img_guestbook_close@2x.png`" alt="" />
+        <p>{{title}}</p>
+      </div>
+      <div class="ui-message-main">
+        <!-- <img :src="`${$cdn}images/img_guestbook_${icon}@2x.png`" alt="" /> -->
+        <span>{{tips}}</span>
+      </div>
+    </div>
+  </popup>
+</template>
+<script>
+import Popup from "./popup";
+export default {
+  name: "ui-alert",
+  components: {
+    Popup,
+  },
+  data() {
+    return {
+      show: false,
+      duration: 0,
+      title: "您的关注,会让我们做得更好!",
+      tips: "留言成功!",
+      content: "",
+      okText: "确定",
+      ok: null,
+      icon:'success'
+    };
+  },
+  methods: {
+    onOk() {
+      if (this.ok && this.ok(this) === false) {
+        return;
+      }
+      this.onClose();
+    },
+    onClose() {
+      setTimeout(() => {
+        this.show = false;
+        document.body.removeChild(this.$el);
+        this.$destroy();
+      }, this.duration);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+@import "../../assets/style/globalVars.less";
+
+@liH: 50px;
+
+textarea::placeholder {
+  color: #382e2c;
+}
+
+.ui-message {
+  width: 400px;
+  height: 240px;
+  position: relative;
+  background: #f5ede2;
+  padding: 20px 50px;
+  .ui-message-header {
+    text-align: center;
+    margin: 0 auto;
+    p {
+      color: @theme;
+      font-size: 20px;
+      font-weight: bold;
+    }
+    img{
+        position: absolute;
+        right: 6px;
+        top: 6px;
+        width: 24px;
+        height: 24px;
+        cursor: pointer;
+    }
+  }
+  .ui-message-main {
+      text-align: center;
+      width: 100%;
+      position: absolute;
+      top: 55%;
+      left: 50%;
+      transform: translate(-50%,-50%);
+        >span{
+            display: inline-block;
+            vertical-align: middle;
+            font-size: 14px;
+        }
+        img{
+            vertical-align: middle;
+            width: 52px;
+            height: 52px;
+            margin-right: 12px;
+        }
+  }
+  .ui-message-footer {
+    text-align: center;
+    margin-top: 20px;
+    .ui-button {
+      display: inline-block;
+      cursor: pointer;
+      font-size: 14px;
+      width: 104px;
+      height: 38px;
+      line-height: 38px;
+      border: none;
+      background: url("@{cdn}img_guestbook_btnbg_normal@2x.png") no-repeat
+        center center;
+      background-size: 100% 100%;
+      color: #382e2c;
+      &:first-of-type {
+        margin-right: 30px;
+      }
+    }
+    .submit {
+      background: url("@{cdn}img_guestbook_btnbg_first@2x.png") no-repeat center
+        center;
+      background-size: 100% 100%;
+      color: #f5ede2;
+    }
+  }
+}
+</style>

+ 45 - 0
pano/src/components/popupLayout/Broadcast.vue

@@ -0,0 +1,45 @@
+<template>
+  <div class="wrapper">
+      <showCollection @hideSlide="data=>{showSidebar = data}" class="ui-broadcast" :zhuti="zhuti" :item="item"></showCollection>
+  </div>
+</template>
+<script>
+import showCollection from '@/components/showCollection'
+import { getDetailById } from "@/config/api";
+
+export default {
+  components:{
+    showCollection
+  },
+  data(){
+    return {
+      showSidebar:false
+    };
+  },
+  methods:{
+    handleItem(data){
+      this.item = data
+    },
+    getCollectionDetail(){
+      getDetailById("goods", {id:this.item.id}, (res) => {
+        this.item = res.data
+      });
+    }
+  },
+  mounted(){
+    this.getCollectionDetail()
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.ui-broadcast {
+  width: 100%;
+  height: 100%;
+  position: fixed;
+  z-index: 99999;
+  top: 0;
+  left: 0;
+  background: rgba(48, 48, 48, 0.8);
+}
+</style>

+ 62 - 0
pano/src/components/popupLayout/Confirm.vue

@@ -0,0 +1,62 @@
+<template>
+    <popup ref="Message" :show="show">
+        <div class="ui-message ui-message-confirm">
+            <div class="ui-message-header">
+                <span>{{title}}</span>
+                <span @click="onNo">
+                    <i class="iconfont icon_close"></i>
+                </span>
+            </div>
+            <div class="ui-message-main">
+                <div class="ui-message-icon"></div>
+                <div class="ui-message-title">{{tips}}</div>
+                <div class="ui-message-content" v-html="content"></div>
+            </div>
+            <div class="ui-message-footer">
+                <button class="ui-button submit" @click="onOk">{{okText}}</button>
+                <button class="ui-button cancel" @click="onNo">{{noText}}</button>
+            </div>
+        </div>
+    </popup>
+</template>
+<script>
+import Popup from "./popup";
+export default {
+    name: "ui-confirm",
+    components: {
+        Popup
+    },
+    data() {
+        return {
+            show: false,
+            duration: 0,
+            title: "提示",
+            tips: "",
+            content: "",
+            okText: "确定",
+            noText: "放弃",
+            ok: null,
+            no: null
+        };
+    },
+    methods: {
+        onOk() {
+            if (this.ok && this.ok(this) === false) {
+                return;
+            }
+            this.onClose();
+        },
+        onNo() {
+            this.no && this.no();
+            this.onClose();
+        },
+        onClose() {
+            setTimeout(() => {
+                this.show = false;
+                document.body.removeChild(this.$el);
+                this.$destroy();
+            }, this.duration);
+        }
+    }
+};
+</script>

+ 52 - 0
pano/src/components/popupLayout/Loading.vue

@@ -0,0 +1,52 @@
+<template>
+    <div class="ui-loading" >
+      <div class="ui-con">
+        <div class="ld-txt">
+           <img :src="require('@/assets/images/icon/loading.gif')" />
+           <p>加载中</p>
+        </div>
+      </div>
+    </div>
+</template>
+<script>
+export default {
+    name: "ui-loading",
+    data() {
+        return {
+           showBg:false
+        };
+    },
+};
+</script>
+
+<style lang="less" scoped>
+.ui-loading{
+  width: 100%;
+  height: 100%;
+  position: fixed;
+  z-index: 100860;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  background-color: rgba(0, 0, 0, 0.2);
+  background-size: cover;
+  background-position: center;
+  .ui-con{
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%,-50%);
+    text-align: center;
+    .ld-txt{
+      font-size: 18px;
+      >img{
+        width: 100px;
+      }
+      >p{
+        font-size: 12px;
+        color: rgba(255, 255, 255, 0.7);
+      }
+    }
+  }
+}
+</style>

+ 188 - 0
pano/src/components/popupLayout/Message.vue

@@ -0,0 +1,188 @@
+<template>
+  <popup ref="Message" :show="show">
+    <div class="ui-message">
+      <div class="ui-message-header">
+        <img :src="`${$cdn}images/icon-story.png`" alt />
+        <p>欢迎留下您的宝贵意见</p>
+      </div>
+      <div class="ui-message-main">
+        <ul>
+          <li>
+            <span class="require">您的称呼</span>
+            <input v-model="userName" type="text" />
+          </li>
+          <li>
+            <span>手机号码(选填)</span>
+            <input v-model="phone" type="text" />
+          </li>
+          <li>
+            <span>邮箱地址(选填)</span>
+            <input v-model="email" type="text" />
+          </li>
+          <li>
+            <span class="require">留言内容</span>
+            <textarea v-model="message" placeholder="请填写您的留言内容"></textarea>
+          </li>
+        </ul>
+      </div>
+      <div class="ui-message-footer">
+        <div class="ui-button submit" @click="onOk">{{ okText }}</div>
+        <div class="ui-button cancel" @click="onNo">{{ noText }}</div>
+      </div>
+    </div>
+  </popup>
+</template>
+<script>
+import Popup from "./popup";
+export default {
+  name: "ui-confirm",
+  components: {
+    Popup
+  },
+  data() {
+    return {
+      show: false,
+      content: "",
+      okText: "提交",
+      noText: "取消",
+      duration: 0,
+      ok: null,
+      no: null,
+      email: "",
+      message: "",
+      phone: "",
+      userName: ""
+    };
+  },
+  methods: {
+    async onOk() {
+      let { email, message, phone, userName } = this;
+      let res = await this.$http({
+        method: "post",
+        data: {
+          email,
+          message,
+          phone,
+          userName
+        },
+        url: `/api/web/save/msg`
+      });
+
+
+      if (res.code !== 0) {
+        return this.$alert({
+          title: '提示',
+          tips: res.msg,
+          icon:''  
+        })
+      }
+
+      this.$alert();
+      this.onClose();
+
+    },
+    onNo() {
+      this.no && this.no();
+      this.onClose();
+    },
+    onClose() {
+      setTimeout(() => {
+        this.show = false;
+        document.body.removeChild(this.$el);
+        this.$destroy();
+      }, this.duration);
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+@import "../../assets/style/globalVars.less";
+
+@liH: 50px;
+
+textarea::placeholder {
+  color: #382e2c;
+}
+
+.ui-message {
+  width: 640px;
+  position: relative;
+  background: #f5ede2;
+  padding: 20px 50px;
+  .ui-message-header {
+    text-align: center;
+    margin: 0 auto;
+    p {
+      color: @theme;
+      font-size: 30px;
+      font-weight: bold;
+    }
+  }
+  .ui-message-main {
+    ul {
+      li {
+        font-size: 14px;
+        color: #382e2c;
+        border-bottom: 1px solid rgba(56, 46, 44, 0.16);
+        height: @liH;
+        line-height: @liH;
+        > input {
+          border: none;
+          background: none;
+          margin-left: 20px;
+        }
+        > textarea {
+          border: none;
+          background: none;
+          height: 150px;
+          width: 100%;
+        }
+        &:last-of-type {
+          height: auto;
+        }
+      }
+      .require {
+        &::after {
+          content: "*";
+          color: #9d362f;
+        }
+      }
+    }
+  }
+  .ui-message-footer {
+    text-align: center;
+    margin-top: 20px;
+    .ui-button {
+      display: inline-block;
+      cursor: pointer;
+      font-size: 14px;
+      width: 104px;
+      height: 38px;
+      line-height: 38px;
+      border: none;
+      background: url("@{cdn}img_guestbook_btnbg_normal@2x.png") no-repeat
+        center center;
+      background-size: 100% 100%;
+      color: #382e2c;
+      &:first-of-type {
+        margin-right: 30px;
+      }
+    }
+    .submit {
+      background: url("@{cdn}img_guestbook_btnbg_first@2x.png") no-repeat center
+        center;
+      background-size: 100% 100%;
+      color: #f5ede2;
+    }
+  }
+}
+
+@media screen and (max-width: 500px) {
+  .ui-message {
+    width: 90%;
+    border-radius: 8px;
+    padding: 20px;
+  }
+}
+</style>

+ 50 - 0
pano/src/components/popupLayout/Tips.vue

@@ -0,0 +1,50 @@
+<template>
+    <transition
+        appear
+        name="custom-classes-transition"
+        enter-active-class="animated fadeIn faster"
+        leave-active-class="animated fadeOut faster"
+    >
+        <popup v-if="show" :is-pass="true">
+            <div class="ui-message-tips" v-html="content"></div>
+        </popup>
+    </transition>
+</template>
+<script>
+import Popup from "./popup";
+export default {
+    name: "ui-tips",
+    components: {
+        Popup
+    },
+    data() {
+        return {
+            show: false,
+            duration: 1000,
+            content: ""
+        };
+    },
+    mounted() {
+        setTimeout(() => {
+            this.show = false;
+            this.$nextTick(function() {
+                document.body.removeChild(this.$el);
+                this.$destroy(true);
+            });
+        }, this.duration);
+    }
+};
+</script>
+<style lang="less" scoped>
+.ui-message-tips {
+    position: absolute;
+    left: 50%;
+    top: 40%;
+    color: #fff;
+    background-color: rgba(0, 0, 0, 0.8);
+    padding: 20px 50px;
+    transform: translate(-40%, -50%);
+    border-radius: 10px;
+    box-shadow: 0 0px 35px rgba(0, 0, 0, 0.3);
+}
+</style>

+ 29 - 0
pano/src/components/popupLayout/index.js

@@ -0,0 +1,29 @@
+import Vue from 'vue'
+import UILoading from './Loading.vue'
+
+const Loading = Vue.extend(UILoading)
+
+
+let loadingInstance = ''
+export function $showLoading(data={}) {
+    if (loadingInstance) {
+        return
+    }
+    loadingInstance = new Loading({
+        data
+    }).$mount()
+
+    document.body.appendChild(loadingInstance.$el)
+
+    Vue.nextTick(() => {
+        loadingInstance.show = true
+    })
+}
+
+export function $hideLoading() {
+    if (loadingInstance) {
+        document.body.removeChild(loadingInstance.$el)
+        loadingInstance = ''
+    }
+}
+

+ 34 - 0
pano/src/components/popupLayout/popup/index.vue

@@ -0,0 +1,34 @@
+<template>
+    <div class="v-popup-layer" :style="{'z-index':zIndex,'pointer-events':isPass?'none':'all'}">
+        <slot></slot>
+    </div>
+</template>
+<script>
+import { nextIndex } from "./manager";
+export default {
+    name: "v-popup-layer",
+    props: {
+        isPass: Boolean
+    },
+    data() {
+        return {
+            zIndex: nextIndex()
+        };
+    }
+};
+</script>
+<style lang="less" scoped>
+.v-popup-layer {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-color: rgba(0, 0, 0, 0.3);
+    overflow: hidden;
+    color: #000;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+</style>

+ 13 - 0
pano/src/components/popupLayout/popup/manager.js

@@ -0,0 +1,13 @@
+let zIndex = 999
+
+export function nextIndex(){
+    return ++zIndex
+}
+
+export function currIndex(){
+    return zIndex
+}
+
+export function prevIndex(){
+    return zIndex-1
+}

+ 0 - 0
pano/src/components/returnhome/index.vue


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov