HotspotDetail.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. <template>
  2. <div class="hotspot-detail">
  3. <div class="wrapper">
  4. <!-- <button
  5. v-show="descForShow"
  6. class="desc"
  7. @click="isShowDesc = true"
  8. >
  9. <img
  10. src="@/assets/images/desc.png"
  11. alt="详情"
  12. draggable="false"
  13. >
  14. </button> -->
  15. <h1
  16. :title="titleForShow"
  17. v-html="titleForShow"
  18. />
  19. <button
  20. class="close"
  21. @click="$emit('close')"
  22. >
  23. <img
  24. src="@/assets/images/close.png"
  25. alt="关闭"
  26. draggable="false"
  27. >
  28. </button>
  29. <div
  30. v-if="typesForShow[currentTabIdx].name === '视频'"
  31. class="swiper-wrapper-mine video-wrap"
  32. >
  33. <div
  34. class="swiper-root swiper-root"
  35. >
  36. <div
  37. class="swiper-wrapper"
  38. >
  39. <div
  40. v-for="(item, index) in typesForShow[currentTabIdx].list"
  41. :key="index"
  42. class="swiper-slide"
  43. >
  44. <video
  45. ref="video"
  46. :src="`https://4dkk.4dage.com/scene_edit_data/${sceneCode}/user/${item.url}`"
  47. controls
  48. controlslist="nodownload"
  49. disablePictureInPicture
  50. />
  51. </div>
  52. </div>
  53. <div class="swiper-pagination">
  54. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ typesForShow[currentTabIdx].list.length }}</span>
  55. </div>
  56. <div class="swiper-button-prev" />
  57. <div class="swiper-button-next" />
  58. </div>
  59. </div>
  60. <div
  61. v-if="typesForShow[currentTabIdx].name === '模型'"
  62. class="swiper-wrapper-mine model-wrap"
  63. >
  64. <div
  65. class="swiper-root"
  66. >
  67. <div
  68. class="swiper-wrapper"
  69. >
  70. <iframe
  71. v-for="(item, index) in typesForShow[currentTabIdx].list"
  72. :key="index"
  73. :src="item.url"
  74. frameborder="0"
  75. class="swiper-slide"
  76. />
  77. </div>
  78. <!-- <div class="swiper-pagination">
  79. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ typesForShow[currentTabIdx].list.length }}</span>
  80. </div> -->
  81. <div class="swiper-button-prev" />
  82. <div class="swiper-button-next" />
  83. </div>
  84. </div>
  85. <div
  86. v-show="typesForShow[currentTabIdx] && typesForShow[currentTabIdx].name === '音频'"
  87. class="swiper-wrapper-mine audio-wrap"
  88. >
  89. <div
  90. class="swiper-root"
  91. >
  92. <div
  93. class="swiper-wrapper"
  94. >
  95. <Audio
  96. v-for="(item, index) in typesForShow[currentTabIdx].list"
  97. ref="audio-comp"
  98. :key="index"
  99. class="audio-custom swiper-slide"
  100. :audio-src="`https://4dkk.4dage.com/scene_edit_data/${sceneCode}/user/${item.url}`"
  101. />
  102. </div>
  103. <!-- <div class="swiper-pagination">
  104. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ typesForShow[currentTabIdx].list.length }}</span>
  105. </div> -->
  106. <div class="swiper-button-prev" />
  107. <div class="swiper-button-next" />
  108. </div>
  109. </div>
  110. <div
  111. v-show="typesForShow[currentTabIdx] && typesForShow[currentTabIdx].name === '图片'"
  112. class="swiper-wrapper-mine image-wrap"
  113. >
  114. <div
  115. class="swiper-root"
  116. >
  117. <div
  118. v-viewer="{
  119. button: true,
  120. navbar: false,
  121. title: false,
  122. toolbar: false,
  123. tooltip: false,
  124. movable: true,
  125. zoomable: true,
  126. rotatable: true,
  127. scalable: true,
  128. transition: false,
  129. fullscreen: false,
  130. keyboard: true,
  131. loop: false,
  132. }"
  133. class="swiper-wrapper"
  134. >
  135. <img
  136. v-for="(item, index) in typesForShow[currentTabIdx].list"
  137. :key="index"
  138. v-lazy="`https://4dkk.4dage.com/scene_edit_data/${sceneCode}/user/${item.url}`"
  139. class="swiper-slide"
  140. alt=""
  141. draggable="false"
  142. >
  143. </div>
  144. <!-- <div class="swiper-pagination">
  145. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ typesForShow[currentTabIdx].list.length }}</span>
  146. </div> -->
  147. <div class="swiper-button-prev" />
  148. <div class="swiper-button-next" />
  149. </div>
  150. </div>
  151. <p class="desc">
  152. {{ descForShow }}
  153. </p>
  154. <!-- <div class="type-tabbar">
  155. <button
  156. v-for="(typeItem, index) in typesForShow"
  157. :key="typeItem.name"
  158. class="tab"
  159. :class="{
  160. active: currentTabIdx === index
  161. }"
  162. @click="currentTabIdx = index"
  163. >
  164. <img
  165. :src="currentTabIdx !== index ? typeItem.icon : typeItem.iconActive"
  166. alt=""
  167. draggable="false"
  168. >
  169. <div>{{ typeItem.name + ' ' + typeItem.list.length }}</div>
  170. </button>
  171. </div> -->
  172. <!-- <MyDesc
  173. v-show="isShowDesc"
  174. :title="titleForShow"
  175. :desc="descForShow"
  176. @close="isShowDesc = false"
  177. /> -->
  178. </div>
  179. </div>
  180. </template>
  181. <script>
  182. import Swiper from 'swiper/swiper-bundle.esm.js'
  183. import 'swiper/swiper-bundle.css'
  184. import Audio from "@/components/Audio.vue"
  185. // import MyDesc from "@/components/HotspotDetailDesc.vue"
  186. export default {
  187. components: {
  188. Audio,
  189. // MyDesc,
  190. },
  191. props: {
  192. hotspotList: {
  193. type: Array,
  194. required: true,
  195. },
  196. sceneCode: {
  197. type: String,
  198. required: true,
  199. }
  200. },
  201. data() {
  202. return {
  203. currentTabIdx: 0,
  204. currentSlideIdx: 0,
  205. isShowDesc: false,
  206. }
  207. },
  208. computed: {
  209. types() {
  210. const ret = [
  211. {
  212. key: 'video',
  213. name: '视频',
  214. icon: require('@/assets/images/swkk/video.png'),
  215. iconActive: require('@/assets/images/swkk/video_1.png'),
  216. list: [
  217. // {
  218. // title: 'title',
  219. // url: 'asdf',
  220. // desc: 'aslf,.dsngkdjlg .,zdskgj. s/lkdsf',
  221. // },
  222. ]
  223. },
  224. {
  225. key: 'link',
  226. name: '模型',
  227. icon: require('@/assets/images/swkk/model.png'),
  228. iconActive: require('@/assets/images/swkk/model_1.png'),
  229. list: [
  230. // {
  231. // title: 'title',
  232. // url: 'asdf',
  233. // desc: 'aslf,.dsngkdjlg .,zdskgj. s/lkdsf',
  234. // },
  235. ]
  236. },
  237. {
  238. key: 'audio',
  239. name: '音频',
  240. icon: require('@/assets/images/swkk/audio.png'),
  241. iconActive: require('@/assets/images/swkk/audio_1.png'),
  242. list: [
  243. // {
  244. // title: 'title',
  245. // url: require('@/assets/images/close.png'),
  246. // desc: 'aslf,.dsngkdjlg .,zdskgj. s/lkdsf',
  247. // },
  248. // {
  249. // title: 'title2',
  250. // url: require('@/assets/images/curvy-line.png'),
  251. // desc: '看来是关键时刻就是雷锋精神是,送到了方式的方式。',
  252. // },
  253. ]
  254. },
  255. {
  256. key: 'image',
  257. name: '图片',
  258. icon: require('@/assets/images/swkk/pic.png'),
  259. iconActive: require('@/assets/images/swkk/pic_1.png'),
  260. list: [
  261. // {
  262. // title: 'title',
  263. // url: require('@/assets/images/close.png'),
  264. // desc: 'aslf,.dsngkdjlg .,zdskgj. s/lkdsf',
  265. // },
  266. // {
  267. // title: 'title2',
  268. // url: require('@/assets/images/curvy-line.png'),
  269. // desc: '看来是关键时刻就是雷锋精神是,送到了方式的方式。',
  270. // },
  271. ]
  272. },
  273. ]
  274. for (const hotspot of this.hotspotList) {
  275. const targetTypeItem = ret.find((item) => {
  276. return item.key === hotspot.type
  277. })
  278. for (const mediaItem of hotspot.media[hotspot.type]) {
  279. targetTypeItem.list.push({
  280. title: /* hotspot.content || */hotspot.title.split('&')[0],
  281. url: mediaItem.src,
  282. desc: hotspot.content,
  283. })
  284. }
  285. }
  286. return ret
  287. },
  288. titleForShow() {
  289. try {
  290. return this.typesForShow[this.currentTabIdx].list[this.currentSlideIdx].title
  291. } catch (error) {
  292. return ''
  293. }
  294. },
  295. descForShow() {
  296. try {
  297. return this.typesForShow[this.currentTabIdx].list[this.currentSlideIdx].desc
  298. } catch (error) {
  299. return ''
  300. }
  301. },
  302. typesForShow() {
  303. return this.types.filter((item) => {
  304. return item.list.length
  305. })
  306. },
  307. },
  308. watch: {
  309. currentTabIdx: {
  310. handler(vNew) {
  311. this.currentSlideIdx = 0
  312. this.$nextTick(() => {
  313. const that = this
  314. const swiper = new Swiper('.swiper-root', {
  315. // If we need pagination
  316. // pagination: {
  317. // el: '.swiper-pagination',
  318. // },
  319. // Navigation arrows
  320. navigation: {
  321. nextEl: '.swiper-button-next',
  322. prevEl: '.swiper-button-prev',
  323. },
  324. on: {
  325. afterInit: function (e) {
  326. if (that.typesForShow[vNew].key === 'video') {
  327. that.$nextTick(() => {
  328. that.$refs.video[0].play()
  329. })
  330. }
  331. if (that.typesForShow[vNew].key === 'audio') {
  332. that.$nextTick(() => {
  333. that.$refs['audio-comp'][0].play()
  334. })
  335. }
  336. },
  337. slideChange: function(e) {
  338. that.currentSlideIdx = e.activeIndex
  339. if (that.typesForShow[vNew].key === 'video') {
  340. for (let index = 0; index < that.$refs.video.length; index++) {
  341. if (index !== that.currentSlideIdx) {
  342. that.$refs.video[index].pause()
  343. } else {
  344. that.$refs.video[index].play()
  345. }
  346. }
  347. }
  348. if (that.typesForShow[vNew].key === 'audio') {
  349. for (let index = 0; index < that.$refs['audio-comp'].length; index++) {
  350. if (index !== that.currentSlideIdx) {
  351. that.$refs['audio-comp'][index].pause()
  352. } else {
  353. that.$refs['audio-comp'][index].play()
  354. }
  355. }
  356. }
  357. }
  358. }
  359. })
  360. })
  361. },
  362. immediate: true,
  363. },
  364. },
  365. mounted() {
  366. this.mustMute()
  367. },
  368. beforeDestroy() {
  369. this.cancelMustMute()
  370. },
  371. methods: {
  372. ...globalMapMutations([
  373. 'mustMute',
  374. 'cancelMustMute',
  375. ])
  376. }
  377. }
  378. </script>
  379. <style lang="less" scoped>
  380. .hotspot-detail {
  381. position: absolute;
  382. top: 0;
  383. left: 0;
  384. width: 100%;
  385. height: 100%;
  386. background: rgba(20, 20, 20, 0.70);
  387. backdrop-filter: blur(4px);
  388. > .wrapper {
  389. position: absolute;
  390. left: 50%;
  391. top: 50%;
  392. transform: translate(-50%, -50%);
  393. width: 100%;
  394. height: 100%;
  395. max-width: 1329px;
  396. max-height: 848px;
  397. background: #E5DFCD;
  398. border-top: solid 8px #A10E0C;
  399. border-bottom: solid 8px #A10E0C;
  400. padding: 28px 100px;
  401. > button.desc {
  402. position: absolute;
  403. top: 3.2rem;
  404. left: 1.25rem;
  405. width: 5rem;
  406. height: 5rem;
  407. > img {
  408. width: 100%;
  409. height: 100%;
  410. }
  411. }
  412. > h1 {
  413. text-align: center;
  414. font-size: 32px;
  415. font-family: Source Han Sans CN-Regular, Source Han Sans CN;
  416. font-weight: 400;
  417. color: #A10E0C;
  418. overflow: hidden;
  419. white-space: pre;
  420. text-overflow: ellipsis;
  421. margin-bottom: 28px;
  422. }
  423. > button.close {
  424. position: absolute;
  425. top: 29px;
  426. right: 37px;
  427. width: 28px;
  428. height: 28px;
  429. > img {
  430. width: 100%;
  431. height: 100%;
  432. }
  433. }
  434. .swiper-wrapper-mine {
  435. height: calc(100% - 32px - 28px - 22px - 72px);
  436. display: flex;
  437. justify-content: center;
  438. align-items: center;
  439. position: relative;
  440. margin-bottom: 22px;
  441. .swiper-root {
  442. overflow: hidden;
  443. height: 100%;
  444. width: 100%;
  445. .swiper-wrapper {
  446. }
  447. .swiper-pagination {
  448. position: absolute;
  449. top: calc(100% + 1em);
  450. left: 50%;
  451. transform: translateX(-50%);
  452. font-size: 1.33rem;
  453. font-family: Inter-Regular, Inter;
  454. color: #666;
  455. .cur {
  456. color: #930909;
  457. }
  458. }
  459. .swiper-button-prev {
  460. left: calc(-1.67rem - 1.83rem);
  461. width: 1.83rem;
  462. height: 3.58rem;
  463. background-image: url(@/assets/images/arrow-left.png);
  464. background-size: contain;
  465. &::after {
  466. content: '';
  467. }
  468. }
  469. .swiper-button-next {
  470. right: calc(-1.67rem - 1.83rem);
  471. width: 1.83rem;
  472. height: 3.58rem;
  473. background-image: url(@/assets/images/arrow-right.png);
  474. background-size: contain;
  475. &::after {
  476. content: '';
  477. }
  478. }
  479. }
  480. }
  481. .swiper-wrapper-mine.video-wrap {
  482. height: calc(31.5rem * 0.85);
  483. width: calc(43.5rem * 0.85);
  484. position: absolute;
  485. left: 50%;
  486. top: 50%;
  487. transform: translate(-50%, -70%);
  488. .swiper-root {
  489. .swiper-wrapper {
  490. .swiper-slide {
  491. background-image: url(@/assets/images/swkk/hotspot-video-bg.png);
  492. background-size: contain;
  493. background-repeat: no-repeat;
  494. padding: 5.67rem 2.21rem 1.88rem 1.8rem;
  495. > video {
  496. width: 100%;
  497. height: 100%;
  498. }
  499. }
  500. }
  501. }
  502. }
  503. .swiper-wrapper-mine.model-wrap {
  504. .swiper-root {
  505. .swiper-wrapper {
  506. }
  507. }
  508. }
  509. .swiper-wrapper-mine.audio-wrap {
  510. width: calc(100% - 1.67rem * 2 - 1.83rem * 2 - 1.67rem * 2);
  511. height: 30rem;
  512. position: absolute;
  513. left: 50%;
  514. top: 50%;
  515. transform: translate(-50%, -70%);
  516. .swiper-root {
  517. width: 100%;
  518. .swiper-wrapper {
  519. }
  520. }
  521. }
  522. .swiper-wrapper-mine.image-wrap {
  523. .swiper-root {
  524. .swiper-wrapper {
  525. > img {
  526. object-fit: contain;
  527. }
  528. }
  529. }
  530. }
  531. > p.desc {
  532. font-size: 16px;
  533. color: #494140;
  534. line-height: 19px;
  535. height: 72px;
  536. overflow: auto;
  537. padding-right: 6px;
  538. }
  539. > .type-tabbar {
  540. position: absolute;
  541. bottom: 11.9%;
  542. left: 0;
  543. width: 100%;
  544. display: flex;
  545. justify-content: center;
  546. > button {
  547. margin-right: 2.08rem;
  548. &:last-child {
  549. margin-right: initial;
  550. }
  551. > img {
  552. width: 5rem;
  553. height: 5rem;
  554. margin-bottom: 0.42rem;
  555. }
  556. > div {
  557. font-size: 1.25rem;
  558. color: #666666;
  559. }
  560. &.active {
  561. > div {
  562. color: #930909;
  563. }
  564. }
  565. }
  566. }
  567. }
  568. ::-webkit-scrollbar { width: 2px; height: 0; } /*宽度是对垂直滚动条而言,高度是对水平滚动条而言*/
  569. ::-webkit-scrollbar-thumb {
  570. background: #958D76;
  571. }
  572. }
  573. </style>