TreasureDetail.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. <template>
  2. <div class="treasure-detail">
  3. <Teleport to="body">
  4. <TreasureShare
  5. v-if="isShowShare"
  6. :info="rawData.value?.entity"
  7. @close="isShowShare = false"
  8. />
  9. </Teleport>
  10. <button
  11. class="back"
  12. @click="$router.go(-1)"
  13. >
  14. <img
  15. class=""
  16. src="@/assets/images/icon_back.png"
  17. alt=""
  18. draggable="false"
  19. >
  20. </button>
  21. <h1>{{ rawData.value?.entity?.name }}</h1>
  22. <div class="tab-bar">
  23. <button
  24. v-if="modelUrlList?.length"
  25. class="model"
  26. :class="{
  27. active: activeTabIndx === 0
  28. }"
  29. @click="activeTabIndx = 0"
  30. >
  31. <span>模型</span>
  32. </button>
  33. <button
  34. v-if="videoUrlList?.length"
  35. class="video"
  36. :class="{
  37. active: activeTabIndx === 1
  38. }"
  39. @click="activeTabIndx = 1"
  40. >
  41. <span>视频</span>
  42. </button>
  43. <button
  44. v-if="imageUrlList?.length"
  45. class="image"
  46. :class="{
  47. active: activeTabIndx === 2
  48. }"
  49. @click="activeTabIndx = 2"
  50. >
  51. <span>图片</span>
  52. </button>
  53. </div>
  54. <div
  55. class="main-wrapper"
  56. >
  57. <div
  58. class="swiper-root"
  59. >
  60. <div
  61. v-if="activeTabIndx === 0"
  62. class="swiper-wrapper"
  63. >
  64. <iframe
  65. v-for="(item) in modelUrlList"
  66. :key="item.id"
  67. :src="`${prefix}/web-model/index.html#/relic-detail?model-path=${encodeURIComponent(item.filePath)}`"
  68. frameborder="0"
  69. class="swiper-slide"
  70. />
  71. </div>
  72. <div
  73. v-if="activeTabIndx === 1"
  74. class="swiper-wrapper"
  75. >
  76. <video
  77. v-for="(item) in videoUrlList"
  78. :key="item.id"
  79. :src="item.filePath"
  80. class="swiper-slide"
  81. />
  82. </div>
  83. <div
  84. v-if="activeTabIndx === 2"
  85. class="swiper-wrapper"
  86. >
  87. <img
  88. v-for="(item) in imageUrlList"
  89. :key="item.id"
  90. draggable="false"
  91. :src="`${prefix}/${item.filePath}`"
  92. class="swiper-slide"
  93. >
  94. </div>
  95. <!-- <div class="swiper-pagination">
  96. <span class="cur">{{ currentSlideIdx + 1 }}</span> / <span>{{ list.length }}</span>
  97. </div> -->
  98. </div>
  99. <!-- <p class="main">
  100. 厂家:江南造船 尺寸:模型:长1230mm,宽700mm,高2300mm
  101. </p> -->
  102. <p class="desc">
  103. {{ rawData.value?.entity?.description }}
  104. </p>
  105. </div>
  106. <div class="swiper-button-prev" />
  107. <div class="swiper-button-next" />
  108. <menu>
  109. <button @click="isShowShare = true">
  110. <img
  111. class=""
  112. src="@/assets/images/icon_share.png"
  113. alt=""
  114. draggable="false"
  115. >
  116. </button>
  117. <button
  118. v-if="hasMusic"
  119. @click="isMusicOn = !isMusicOn"
  120. >
  121. <img
  122. class=""
  123. :src="require(`@/assets/images/icon_music_${isMusicOn ? 'on' : 'off'}.png`)"
  124. alt=""
  125. draggable="false"
  126. >
  127. </button>
  128. <button
  129. v-if="canRecord && activeTabIndx === 0"
  130. @click="$router.push({name: 'RecordView', query: {
  131. url: encodeURI(`${prefix}/web-model/index.html#/relic-detail?model-path=${encodeURIComponent(modelUrlList[activeSwiperItemIndex].filePath)}`)
  132. }})"
  133. >
  134. <img
  135. class=""
  136. src="@/assets/images/icon_record.png"
  137. alt=""
  138. draggable="false"
  139. >
  140. </button>
  141. <button @click="fullScreenStatus = !fullScreenStatus">
  142. <img
  143. class=""
  144. :src="require(`@/assets/images/icon_full_screen_${fullScreenStatus ? 'off' : 'on'}.png`)"
  145. alt=""
  146. draggable="false"
  147. >
  148. </button>
  149. </menu>
  150. <audio
  151. v-if="hasMusic"
  152. ref="bgAudio"
  153. :src="musicUrl"
  154. style="display: none;"
  155. />
  156. </div>
  157. </template>
  158. <script>
  159. import {
  160. computed,
  161. nextTick,
  162. onMounted,
  163. reactive,
  164. ref,
  165. toRefs,
  166. watch,
  167. } from 'vue'
  168. import { useRoute } from "vue-router"
  169. import TreasureShare from "@/components/TreasureShare.vue"
  170. import Swiper from 'swiper/bundle'
  171. import 'swiper/css/bundle'
  172. export default {
  173. components: {
  174. TreasureShare
  175. },
  176. setup () {
  177. const prefix = process.env.VUE_APP_API_ORIGIN
  178. /**
  179. * 路由
  180. */
  181. const route = useRoute()
  182. /**
  183. * tab bar
  184. */
  185. const activeTabIndx = ref(0)
  186. /**
  187. * swiper 相关
  188. */
  189. let swiper = null
  190. const activeSwiperItemIndex = ref(0)
  191. function initSwiper() {
  192. swiper = new Swiper('.swiper-root', {
  193. // pagination: {
  194. // el: '.swiper-pagination',
  195. // },
  196. // Navigation arrows
  197. navigation: {
  198. nextEl: '.swiper-button-next',
  199. prevEl: '.swiper-button-prev',
  200. },
  201. on: {
  202. afterInit: function (e) {
  203. activeSwiperItemIndex.value = e.activeIndex
  204. },
  205. slideChange: function(e) {
  206. activeSwiperItemIndex.value = e.activeIndex
  207. }
  208. }
  209. })
  210. }
  211. onMounted(initSwiper)
  212. watch(activeTabIndx, (newV) => {
  213. swiper.destroy()
  214. nextTick(() => {
  215. initSwiper()
  216. })
  217. })
  218. /**
  219. * 分享
  220. */
  221. const isShowShare = ref(false)
  222. /**
  223. * 背景音乐相关
  224. */
  225. const bgAudio = ref(null)
  226. const hasMusic = ref(false)
  227. const musicUrl = ref(require('@/assets/mock/02.mp3'))
  228. const isMusicOn = ref(false)
  229. watch(isMusicOn, (newV) => {
  230. console.log('change!', newV)
  231. if (newV) {
  232. bgAudio.value.play()
  233. } else {
  234. bgAudio.value.pause()
  235. }
  236. })
  237. /**
  238. * 录屏相关
  239. */
  240. const canRecord = ref(navigator?.mediaDevices?.getDisplayMedia)
  241. /**
  242. * 全屏相关
  243. */
  244. const fullScreenStatus = ref(false)
  245. watch(fullScreenStatus, (newVal) => {
  246. if (newVal) {
  247. const el = document.querySelector('.treasure-detail')
  248. if (el) {
  249. utils.requestFullScreen(el)
  250. } else {
  251. console.error('没有找到组件根元素!')
  252. }
  253. } else {
  254. utils.exitFullScreen()
  255. }
  256. })
  257. /**
  258. * 展示数据
  259. */
  260. const rawData = reactive({
  261. value: null,
  262. })
  263. const modelUrlList = reactive([])
  264. const videoUrlList = reactive([])
  265. const imageUrlList = reactive([])
  266. api.getTreasureDetail(route.query.id).then((res) => {
  267. rawData.value = res
  268. for (const iterator of rawData.value.file) {
  269. switch (iterator.type) {
  270. case 'model':
  271. modelUrlList.push(iterator)
  272. break
  273. case 'video':
  274. videoUrlList.push(iterator)
  275. break
  276. case 'img':
  277. imageUrlList.push(iterator)
  278. break
  279. default:
  280. break
  281. }
  282. }
  283. })
  284. return {
  285. prefix,
  286. encodeURIComponent,
  287. activeSwiperItemIndex,
  288. activeTabIndx,
  289. bgAudio,
  290. canRecord,
  291. fullScreenStatus,
  292. hasMusic,
  293. imageUrlList,
  294. isMusicOn,
  295. isShowShare,
  296. modelUrlList,
  297. musicUrl,
  298. rawData,
  299. videoUrlList,
  300. }
  301. },
  302. }
  303. </script>
  304. <style lang="less" scoped>
  305. .treasure-detail {
  306. width: 100%;
  307. height: 100%;
  308. overflow: auto;
  309. position: relative;
  310. >button.back {
  311. position: absolute;
  312. top: 51px;
  313. left: 67px;
  314. width: 40px;
  315. height: 40px;
  316. >img {
  317. width: 100%;
  318. height: 100%;
  319. }
  320. }
  321. >h1 {
  322. position: absolute;
  323. top: 36px;
  324. left: 50%;
  325. transform: translateX(-50%);
  326. font-size: 36px;
  327. font-family: Source Han Sans CN-Bold, Source Han Sans CN;
  328. font-weight: bold;
  329. color: #FFFFFF;
  330. }
  331. >.tab-bar {
  332. position: absolute;
  333. top: 108px;
  334. left: 50%;
  335. transform: translateX(-50%);
  336. >button {
  337. margin-right: 50px;
  338. width: 80px;
  339. height: 36px;
  340. background: rgba(255,255,255,0.2);
  341. border-radius: 18px;
  342. border: 1px solid #fff;
  343. font-size: 16px;
  344. font-family: Source Han Sans CN-Regular, Source Han Sans CN;
  345. font-weight: 400;
  346. color: #fff;
  347. opacity: 0.7;
  348. &:last-of-type {
  349. margin-right: initial;
  350. }
  351. &.active {
  352. background: rgba(255,255,255,0.2);
  353. border: 1px solid #DBC386;
  354. color: #DBC386;
  355. opacity: initial;
  356. }
  357. }
  358. }
  359. >.main-wrapper {
  360. position: absolute;
  361. left: 50%;
  362. top: 50%;
  363. transform: translate(-50%, -50%);
  364. height: calc(100% - 144px - 128px - 40px);
  365. width: 80%;
  366. max-width: 1164px;
  367. .swiper-root {
  368. height: calc(100% - 160px);
  369. width: 100%;
  370. position: relative;
  371. overflow: hidden;
  372. .swiper-wrapper {
  373. height: 100%;
  374. margin-left: auto;
  375. margin-right: auto;
  376. .swiper-slide {
  377. user-select: none;
  378. object-fit: contain;
  379. }
  380. }
  381. // .swiper-pagination {
  382. // position: absolute;
  383. // bottom: 10px;
  384. // left: 50%;
  385. // transform: translateX(-50%);
  386. // font-size: 1.33rem;
  387. // font-family: Inter-Regular, Inter;
  388. // color: #666;
  389. // .cur {
  390. // color: #930909;
  391. // }
  392. // }
  393. }
  394. >p.main {
  395. margin-top: 20px;
  396. font-size: 20px;
  397. font-family: Source Han Sans CN-Regular, Source Han Sans CN;
  398. font-weight: 400;
  399. color: #FFFFFF;
  400. overflow: hidden;
  401. white-space: pre;
  402. text-overflow: ellipsis;
  403. margin-bottom: 22px;
  404. text-align: center;
  405. }
  406. >p.desc {
  407. height: 108px;
  408. width: 100%;
  409. max-width: 1164px;
  410. text-align: center;
  411. font-size: 20px;
  412. line-height: 23px;
  413. font-family: Source Han Sans CN-Regular, Source Han Sans CN;
  414. font-weight: 400;
  415. color: #FFFFFF;
  416. overflow: auto;
  417. opacity: 0.8;
  418. margin-left: auto;
  419. margin-right: auto;
  420. padding-left: 10px;
  421. margin-top: 20px;
  422. height: 150px;
  423. &::-webkit-scrollbar { background: transparent; width: 4px; } /*宽度是对垂直滚动条而言,高度是对水平滚动条而言*/
  424. &::-webkit-scrollbar-thumb {
  425. background: rgba(220, 231, 240, 0.2);
  426. border-radius: 2px;
  427. }
  428. }
  429. }
  430. .swiper-button-prev {
  431. left: 44px;
  432. width: 44px;
  433. height: 44px;
  434. background-image: url(@/assets/images/arrow-left-2.png);
  435. background-size: contain;
  436. &::after {
  437. content: '';
  438. }
  439. }
  440. .swiper-button-next {
  441. right: 44px;
  442. width: 44px;
  443. height: 44px;
  444. background-image: url(@/assets/images/arrow-right-2.png);
  445. background-size: contain;
  446. &::after {
  447. content: '';
  448. }
  449. }
  450. >menu {
  451. position: absolute;
  452. right: 44px;
  453. bottom: 130px;
  454. > button {
  455. display: block;
  456. margin-bottom: 15px;
  457. width: 44px;
  458. height: 44px;
  459. > img {
  460. width: 100%;
  461. height: 100%;
  462. }
  463. }
  464. }
  465. }
  466. </style>