HotspotDetail.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. <template>
  2. <div
  3. class="hotspot-detail"
  4. >
  5. <button
  6. v-show="descForShow"
  7. class="desc"
  8. @click="isShowDesc = true"
  9. >
  10. <img
  11. src="@/assets/images/desc.png"
  12. alt="详情"
  13. draggable="false"
  14. >
  15. </button>
  16. <h1 v-html="titleForShow" />
  17. <button
  18. class="close"
  19. @click="$emit('close')"
  20. >
  21. <img
  22. src="@/assets/images/close.png"
  23. alt="关闭"
  24. draggable="false"
  25. >
  26. </button>
  27. <div
  28. v-if="typesForShow[currentTabIdx].key === 'video'"
  29. class="swiper-wrapper-mine video-wrap"
  30. >
  31. <div
  32. class="swiper-root swiper-root"
  33. >
  34. <div
  35. class="swiper-wrapper"
  36. >
  37. <div
  38. v-for="(item, index) in typesForShow[currentTabIdx].list"
  39. :key="index"
  40. class="swiper-slide"
  41. >
  42. <video
  43. ref="video"
  44. :src="`${$swkkHotspotVideoAudioPath}${sceneCode}/user/${item.url}`"
  45. controls
  46. controlslist="nodownload"
  47. disablePictureInPicture
  48. />
  49. </div>
  50. </div>
  51. <div class="swiper-pagination">
  52. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ typesForShow[currentTabIdx].list.length }}</span>
  53. </div>
  54. <div class="swiper-button-prev" />
  55. <div class="swiper-button-next" />
  56. </div>
  57. </div>
  58. <div
  59. v-if="typesForShow[currentTabIdx].key === 'link'"
  60. class="swiper-wrapper-mine model-wrap"
  61. >
  62. <div
  63. class="swiper-root"
  64. >
  65. <div
  66. class="swiper-wrapper"
  67. >
  68. <iframe
  69. v-for="(item, index) in typesForShow[currentTabIdx].list"
  70. :key="index"
  71. :src="item.url"
  72. frameborder="0"
  73. class="swiper-slide"
  74. />
  75. </div>
  76. <div class="swiper-pagination">
  77. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ typesForShow[currentTabIdx].list.length }}</span>
  78. </div>
  79. <div class="swiper-button-prev" />
  80. <div class="swiper-button-next" />
  81. </div>
  82. </div>
  83. <div
  84. v-if="typesForShow[currentTabIdx] && typesForShow[currentTabIdx].key === 'audio'"
  85. class="swiper-wrapper-mine audio-wrap"
  86. >
  87. <div
  88. class="swiper-root"
  89. >
  90. <div
  91. class="swiper-wrapper"
  92. >
  93. <Audio
  94. v-for="(item, index) in typesForShow[currentTabIdx].list"
  95. ref="audio-comp"
  96. :key="index"
  97. class="audio-custom swiper-slide"
  98. :audio-src="`${$swkkHotspotVideoAudioPath}${sceneCode}/user/${item.url}`"
  99. />
  100. </div>
  101. <div class="swiper-pagination">
  102. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ typesForShow[currentTabIdx].list.length }}</span>
  103. </div>
  104. <div class="swiper-button-prev" />
  105. <div class="swiper-button-next" />
  106. </div>
  107. </div>
  108. <div
  109. v-if="typesForShow[currentTabIdx] && typesForShow[currentTabIdx].key === 'image'"
  110. class="swiper-wrapper-mine image-wrap"
  111. >
  112. <div
  113. class="swiper-root"
  114. >
  115. <div
  116. v-viewer="{
  117. button: true,
  118. navbar: false,
  119. title: false,
  120. toolbar: false,
  121. tooltip: false,
  122. movable: true,
  123. zoomable: true,
  124. rotatable: true,
  125. scalable: true,
  126. transition: false,
  127. fullscreen: false,
  128. keyboard: true,
  129. loop: false,
  130. }"
  131. class="swiper-wrapper"
  132. >
  133. <img
  134. v-for="(item, index) in typesForShow[currentTabIdx].list"
  135. :key="index"
  136. v-lazy="item.url"
  137. class="swiper-slide"
  138. alt=""
  139. draggable="false"
  140. >
  141. </div>
  142. <div class="swiper-pagination">
  143. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ typesForShow[currentTabIdx].list.length }}</span>
  144. </div>
  145. <div class="swiper-button-prev" />
  146. <div class="swiper-button-next" />
  147. </div>
  148. </div>
  149. <div
  150. v-if="typesForShow[currentTabIdx] && typesForShow[currentTabIdx].key === '2d'"
  151. class="swiper-wrapper-mine d2-wrap"
  152. >
  153. <div
  154. class="swiper-root"
  155. >
  156. <div
  157. v-viewer="{
  158. button: true,
  159. navbar: false,
  160. title: false,
  161. toolbar: false,
  162. tooltip: false,
  163. movable: true,
  164. zoomable: true,
  165. rotatable: true,
  166. scalable: true,
  167. transition: false,
  168. fullscreen: false,
  169. keyboard: true,
  170. loop: false,
  171. }"
  172. class="swiper-wrapper"
  173. >
  174. <img
  175. v-for="(item, index) in typesForShow[currentTabIdx].list"
  176. :key="index"
  177. v-lazy="item.url"
  178. class="swiper-slide"
  179. alt=""
  180. draggable="false"
  181. >
  182. </div>
  183. <div class="swiper-pagination">
  184. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ typesForShow[currentTabIdx].list.length }}</span>
  185. </div>
  186. <div class="swiper-button-prev" />
  187. <div class="swiper-button-next" />
  188. </div>
  189. </div>
  190. <div
  191. v-if="typesForShow[currentTabIdx] && typesForShow[currentTabIdx].key === 'article'"
  192. class="swiper-wrapper-mine article-wrap"
  193. >
  194. <div
  195. class="swiper-root"
  196. >
  197. <div
  198. v-viewer="{
  199. button: true,
  200. navbar: false,
  201. title: false,
  202. toolbar: false,
  203. tooltip: false,
  204. movable: true,
  205. zoomable: true,
  206. rotatable: true,
  207. scalable: true,
  208. transition: false,
  209. fullscreen: false,
  210. keyboard: true,
  211. loop: false,
  212. }"
  213. class="swiper-wrapper"
  214. >
  215. <img
  216. v-for="(item, index) in typesForShow[currentTabIdx].list"
  217. :key="index"
  218. v-lazy="item.url"
  219. class="swiper-slide"
  220. alt=""
  221. draggable="false"
  222. >
  223. </div>
  224. <div class="swiper-pagination">
  225. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ typesForShow[currentTabIdx].list.length }}</span>
  226. </div>
  227. <div class="swiper-button-prev" />
  228. <div class="swiper-button-next" />
  229. </div>
  230. </div>
  231. <div class="type-tabbar">
  232. <button
  233. v-for="(typeItem, index) in typesForShow"
  234. :key="typeItem.name"
  235. class="tab"
  236. :class="{
  237. active: currentTabIdx === index
  238. }"
  239. @click="currentTabIdx = index"
  240. >
  241. <button
  242. v-if="typeItem.key === 'image' && typesForShow[currentTabIdx].key === 'image'"
  243. class="expand-list-btn"
  244. >
  245. <img
  246. v-show="!isShowImgList"
  247. class=""
  248. src="@/assets/images/arrow-up.png"
  249. alt=""
  250. draggable="false"
  251. @click="isShowImgList = true"
  252. >
  253. <img
  254. v-show="isShowImgList"
  255. class=""
  256. src="@/assets/images/arrow-down.png"
  257. alt=""
  258. draggable="false"
  259. @click="isShowImgList = false"
  260. >
  261. </button>
  262. <img
  263. :src="currentTabIdx !== index ? typeItem.icon : typeItem.iconActive"
  264. alt=""
  265. draggable="false"
  266. >
  267. <div>{{ typeItem.name + ' ' + typeItem.list.length }}</div>
  268. </button>
  269. </div>
  270. <MyDesc
  271. v-show="isShowDesc"
  272. :title="titleForShow"
  273. :desc="descForShow"
  274. @close="isShowDesc = false"
  275. />
  276. <HotspotDetailImgList
  277. v-show="isShowImgList"
  278. class="img-list"
  279. :img-list="types[3].list"
  280. :active-index="currentSlideIdx"
  281. @close="isShowImgList = false"
  282. @selectItem="onSelectImageItem"
  283. />
  284. </div>
  285. </template>
  286. <script>
  287. import Swiper from 'swiper/swiper-bundle.esm.js'
  288. import 'swiper/swiper-bundle.css'
  289. import Audio from "@/components/Audio.vue"
  290. import MyDesc from "@/components/HotspotDetailDesc.vue"
  291. import { hotData as hotspotDataListFromLocal } from "@/assets/data/swkkHotspotData.js"
  292. import HotspotDetailImgList from "@/components/HotspotDetailImgList"
  293. export default {
  294. components: {
  295. Audio,
  296. MyDesc,
  297. HotspotDetailImgList,
  298. },
  299. props: {
  300. hotspotList: {
  301. type: Array,
  302. required: true,
  303. },
  304. sceneCode: {
  305. type: String,
  306. required: true,
  307. },
  308. id: {
  309. type: String,
  310. required: true
  311. }
  312. },
  313. data() {
  314. return {
  315. currentTabIdx: 0,
  316. currentSlideIdx: 0,
  317. isShowDesc: false,
  318. isShowImgList: false,
  319. swiper: null,
  320. }
  321. },
  322. computed: {
  323. types() {
  324. const ret = [
  325. {
  326. key: 'video',
  327. name: '视频',
  328. icon: require('@/assets/images/swkk/video.png'),
  329. iconActive: require('@/assets/images/swkk/video_1.png'),
  330. list: [
  331. // {
  332. // title: 'title',
  333. // url: 'asdf',
  334. // desc: 'aslf,.dsngkdjlg .,zdskgj. s/lkdsf',
  335. // },
  336. ]
  337. },
  338. {
  339. key: 'link',
  340. name: '模型',
  341. icon: require('@/assets/images/swkk/model.png'),
  342. iconActive: require('@/assets/images/swkk/model_1.png'),
  343. list: [
  344. // {
  345. // title: 'title',
  346. // url: 'asdf',
  347. // desc: 'aslf,.dsngkdjlg .,zdskgj. s/lkdsf',
  348. // },
  349. ]
  350. },
  351. {
  352. key: 'audio',
  353. name: '音频',
  354. icon: require('@/assets/images/swkk/audio.png'),
  355. iconActive: require('@/assets/images/swkk/audio_1.png'),
  356. list: [
  357. // {
  358. // title: 'title',
  359. // url: require('@/assets/images/close.png'),
  360. // desc: 'aslf,.dsngkdjlg .,zdskgj. s/lkdsf',
  361. // },
  362. // {
  363. // title: 'title2',
  364. // url: require('@/assets/images/curvy-line.png'),
  365. // desc: '看来是关键时刻就是雷锋精神是,送到了方式的方式。',
  366. // },
  367. ]
  368. },
  369. {
  370. key: 'image',
  371. name: '图片',
  372. icon: require('@/assets/images/swkk/pic.png'),
  373. iconActive: require('@/assets/images/swkk/pic_1.png'),
  374. list: [
  375. ]
  376. },
  377. {
  378. key: '2d',
  379. name: '二维',
  380. icon: require('@/assets/images/swkk/pic.png'),
  381. iconActive: require('@/assets/images/swkk/pic_1.png'),
  382. list: [
  383. ]
  384. },
  385. {
  386. key: 'article',
  387. name: '文章',
  388. icon: require('@/assets/images/swkk/pic.png'),
  389. iconActive: require('@/assets/images/swkk/pic_1.png'),
  390. list: [
  391. ]
  392. },
  393. ]
  394. for (const hotspot of this.hotspotList) {
  395. if (hotspot.type === 'audio' || hotspot.type === 'video' || hotspot.type === 'link') {
  396. const targetTypeItem = ret.find((item) => {
  397. return item.key === hotspot.type
  398. })
  399. for (const mediaItem of hotspot.media[hotspot.type]) {
  400. targetTypeItem.list.push({
  401. title: /* hotspot.content || */hotspot.title.split('&')[0],
  402. url: mediaItem.src,
  403. desc: hotspot.content,
  404. })
  405. }
  406. }
  407. }
  408. const hotspotDataFromLocal = hotspotDataListFromLocal.find((item) => {
  409. return item.hotId === this.id
  410. })
  411. if (hotspotDataFromLocal) {
  412. for (const imgItem of hotspotDataFromLocal.img || []) {
  413. ret[3].list.push({
  414. title: imgItem.name,
  415. url: `${process.env.VUE_APP_SWKK_PATH}${this.id}/img/${imgItem.id}`,
  416. desc: imgItem.content,
  417. })
  418. }
  419. for (const item2d of hotspotDataFromLocal['2d'] || []) {
  420. ret[4].list.push({
  421. title: item2d.name,
  422. url: `${process.env.VUE_APP_SWKK_PATH}${this.id}/2d/${item2d.id}`,
  423. desc: item2d.content,
  424. })
  425. }
  426. for (const articleItem of hotspotDataFromLocal.article || []) {
  427. ret[5].list.push({
  428. title: articleItem.name,
  429. url: `${process.env.VUE_APP_SWKK_PATH}${this.id}/article/${articleItem.id}`,
  430. desc: articleItem.content,
  431. })
  432. }
  433. }
  434. console.log(ret)
  435. return ret
  436. },
  437. titleForShow() {
  438. try {
  439. return this.typesForShow[this.currentTabIdx].list[this.currentSlideIdx].title
  440. } catch (error) {
  441. return ''
  442. }
  443. },
  444. descForShow() {
  445. try {
  446. return this.typesForShow[this.currentTabIdx].list[this.currentSlideIdx].desc
  447. } catch (error) {
  448. return ''
  449. }
  450. },
  451. typesForShow() {
  452. return this.types.filter((item) => {
  453. return item.list.length
  454. })
  455. },
  456. },
  457. watch: {
  458. currentTabIdx: {
  459. handler(vNew) {
  460. this.currentSlideIdx = 0
  461. this.$nextTick(() => {
  462. const that = this
  463. this.swiper = new Swiper('.swiper-root', {
  464. // If we need pagination
  465. // pagination: {
  466. // el: '.swiper-pagination',
  467. // },
  468. // Navigation arrows
  469. navigation: {
  470. nextEl: '.swiper-button-next',
  471. prevEl: '.swiper-button-prev',
  472. },
  473. on: {
  474. afterInit: function (e) {
  475. if (that.typesForShow[vNew].key === 'video') {
  476. that.$nextTick(() => {
  477. that.$refs.video[0].play()
  478. })
  479. }
  480. if (that.typesForShow[vNew].key === 'audio') {
  481. that.$nextTick(() => {
  482. that.$refs['audio-comp'][0].play()
  483. })
  484. }
  485. },
  486. slideChange: function(e) {
  487. that.currentSlideIdx = e.activeIndex
  488. if (that.typesForShow[vNew].key === 'video') {
  489. for (let index = 0; index < that.$refs.video.length; index++) {
  490. if (index !== that.currentSlideIdx) {
  491. that.$refs.video[index].pause()
  492. } else {
  493. that.$refs.video[index].play()
  494. }
  495. }
  496. }
  497. if (that.typesForShow[vNew].key === 'audio') {
  498. for (let index = 0; index < that.$refs['audio-comp'].length; index++) {
  499. if (index !== that.currentSlideIdx) {
  500. that.$refs['audio-comp'][index].pause()
  501. } else {
  502. that.$refs['audio-comp'][index].play()
  503. }
  504. }
  505. }
  506. }
  507. }
  508. })
  509. })
  510. },
  511. immediate: true,
  512. },
  513. },
  514. mounted() {
  515. this.mustMute()
  516. },
  517. beforeDestroy() {
  518. this.cancelMustMute()
  519. },
  520. methods: {
  521. ...globalMapMutations([
  522. 'mustMute',
  523. 'cancelMustMute',
  524. ]),
  525. onSelectImageItem(index) {
  526. this.isShowImgList = false
  527. this.swiper.slideTo(index, 500)
  528. }
  529. }
  530. }
  531. </script>
  532. <style lang="less" scoped>
  533. .hotspot-detail {
  534. position: absolute;
  535. top: 0;
  536. left: 0;
  537. width: 100%;
  538. height: 100%;
  539. background: rgba(255,251,245,0.9);
  540. backdrop-filter: blur(4px);
  541. z-index: 2;
  542. > button.desc {
  543. position: absolute;
  544. top: 3.2rem;
  545. left: 1.25rem;
  546. width: 5rem;
  547. height: 5rem;
  548. > img {
  549. width: 100%;
  550. height: 100%;
  551. }
  552. }
  553. > h1 {
  554. position: absolute;
  555. top: 5rem;
  556. left: 10.46rem;
  557. right: 10.46rem;
  558. text-align: center;
  559. font-size: 2rem;
  560. font-weight: bold;
  561. color: #930909;
  562. line-height: 2.34rem;
  563. }
  564. > button.close {
  565. position: absolute;
  566. top: 3.2rem;
  567. right: 1.25rem;
  568. width: 5rem;
  569. height: 5rem;
  570. > img {
  571. width: 100%;
  572. height: 100%;
  573. }
  574. }
  575. .swiper-wrapper-mine {
  576. top: 11rem;
  577. width: calc(100% - 1.67rem * 2 - 1.83rem * 2 - 1.67rem * 2);
  578. margin: 0 auto;
  579. height: 60%;
  580. display: flex;
  581. justify-content: center;
  582. align-items: center;
  583. position: relative;
  584. .swiper-root {
  585. overflow: hidden;
  586. height: 100%;
  587. .swiper-wrapper {
  588. }
  589. // .swiper-pagination {
  590. // top: 100%;
  591. // bottom: initial;
  592. // height: 0.42rem;
  593. // /deep/.swiper-pagination-bullet {
  594. // width: 5.34rem;
  595. // height: 0.42rem;
  596. // background-color: #999999;
  597. // margin-right: 0.79rem;
  598. // // background-color: #930909;
  599. // border-radius: initial;
  600. // }
  601. // /deep/.swiper-pagination-bullet.swiper-pagination-bullet-active {
  602. // background-color: #930909;
  603. // }
  604. // }
  605. .swiper-pagination {
  606. position: absolute;
  607. top: calc(100% + 1em);
  608. left: 50%;
  609. transform: translateX(-50%);
  610. font-size: 1.33rem;
  611. font-family: Inter-Regular, Inter;
  612. color: #666;
  613. .cur {
  614. color: #930909;
  615. }
  616. }
  617. .swiper-button-prev {
  618. left: calc(-1.67rem - 1.83rem);
  619. width: 1.83rem;
  620. height: 3.58rem;
  621. background-image: url(@/assets/images/arrow-left.png);
  622. background-size: contain;
  623. &::after {
  624. content: '';
  625. }
  626. }
  627. .swiper-button-next {
  628. right: calc(-1.67rem - 1.83rem);
  629. width: 1.83rem;
  630. height: 3.58rem;
  631. background-image: url(@/assets/images/arrow-right.png);
  632. background-size: contain;
  633. &::after {
  634. content: '';
  635. }
  636. }
  637. }
  638. }
  639. .swiper-wrapper-mine.video-wrap {
  640. height: calc(31.5rem * 0.85);
  641. width: calc(43.5rem * 0.85);
  642. position: absolute;
  643. left: 50%;
  644. top: 50%;
  645. transform: translate(-50%, -70%);
  646. .swiper-root {
  647. .swiper-wrapper {
  648. .swiper-slide {
  649. background-image: url(@/assets/images/swkk/hotspot-video-bg.png);
  650. background-size: contain;
  651. background-repeat: no-repeat;
  652. padding: 5.67rem 2.21rem 1.88rem 1.8rem;
  653. > video {
  654. width: 100%;
  655. height: 100%;
  656. }
  657. }
  658. }
  659. }
  660. }
  661. .swiper-wrapper-mine.model-wrap {
  662. .swiper-root {
  663. .swiper-wrapper {
  664. }
  665. }
  666. }
  667. .swiper-wrapper-mine.audio-wrap {
  668. width: calc(100% - 1.67rem * 2 - 1.83rem * 2 - 1.67rem * 2);
  669. height: 30rem;
  670. position: absolute;
  671. left: 50%;
  672. top: 50%;
  673. transform: translate(-50%, -70%);
  674. .swiper-root {
  675. width: 100%;
  676. .swiper-wrapper {
  677. }
  678. }
  679. }
  680. .swiper-wrapper-mine.image-wrap, .swiper-wrapper-mine.d2-wrap, .swiper-wrapper-mine.article-wrap {
  681. .swiper-root {
  682. .swiper-wrapper {
  683. > img {
  684. object-fit: contain;
  685. }
  686. }
  687. }
  688. }
  689. > .type-tabbar {
  690. position: absolute;
  691. bottom: 11.9%;
  692. left: 0;
  693. width: 100%;
  694. display: flex;
  695. justify-content: center;
  696. > button {
  697. margin-right: 2.08rem;
  698. position: relative;
  699. &:last-child {
  700. margin-right: initial;
  701. }
  702. > button.expand-list-btn {
  703. position: absolute;
  704. left: 50%;
  705. top: -3.5rem;
  706. transform: translateX(-50%);
  707. width: 3.5rem;
  708. padding: 0.5rem;
  709. > img {
  710. width: 100%;
  711. }
  712. }
  713. > img {
  714. width: 5rem;
  715. height: 5rem;
  716. margin-bottom: 0.42rem;
  717. }
  718. > div {
  719. font-size: 1.25rem;
  720. color: #666666;
  721. }
  722. &.active {
  723. > div {
  724. color: #930909;
  725. }
  726. }
  727. }
  728. }
  729. }
  730. </style>