123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777 |
- <template>
- <div class="collect-detail-container">
- <!-- 顶部固定导航 -->
- <div class="top-navigation">
- <!-- 返回按钮 -->
- <div class="back-button" @click="goBack">
- <img src="@/assets/img/icon_back.png" alt="返回" />
- </div>
- <!-- 自定义标签页导航 -->
- <div class="custom-tabs">
- <div
- class="tab-item"
- :class="{ active: activeTab === 'model' }"
- @click="switchTab('model')"
- >
- 模型
- </div>
- <div
- class="tab-item"
- :class="{ active: activeTab === 'image' }"
- @click="switchTab('image')"
- >
- 图片
- </div>
- </div>
- </div>
- <!-- 内容展示区域 -->
- <div class="content-display" :class="{ zoomed: isZoomed }">
- <!-- 轮播展示区域 -->
- <div class="carousel-viewer">
- <!-- 模型展示容器 -->
- <div v-show="activeTab === 'model'" class="carousel-container">
- <!-- 模型直接展示,不使用轮播 -->
- <div class="model-container" v-if="artifactData && artifactData.modelFiles && artifactData.modelFiles.length > 0">
- <div class="model-item">
- <iframe
- id="ifr"
- :src="`https://sit-huyaobangjng.4dage.com/modelLoad/model.html?m=${modelList[0]?.src}`"
- frameborder="0"
- width="100%"
- height="100%"
- allowfullscreen
- ></iframe>
- <!-- <iframe
- id="ifr"
- :src="`${modelList[0]?.src}`"
- frameborder="0"
- width="100%"
- height="100%"
- allowfullscreen
- ></iframe> -->
- </div>
- </div>
- <div v-else class="empty-state">
- <div class="empty-content">
- <div class="empty-text">暂无3D模型数据</div>
- </div>
- </div>
- </div>
- <!-- 图片轮播容器 -->
- <div v-show="activeTab === 'image'" class="carousel-container">
- <div class="carousel-container-image" v-if="artifactData && artifactData.imageFiles && artifactData.imageFiles.length > 0">
- <el-carousel
- height="100%"
- arrow="always"
- ref="imageCarouselRef"
- @change="handleImageCarouselChange"
- :autoplay="autoplayEnabled"
- :interval="4000"
- :pause-on-hover="false"
- >
- <el-carousel-item
- v-for="(item, index) in imageList"
- :key="index"
- >
- <div class="image-item">
- <img :src="item.src" :alt="item.alt" class="carousel-image" />
- </div>
- </el-carousel-item>
- </el-carousel>
- </div>
- <div v-else class="empty-state">
- <div class="empty-content">
- <div class="empty-text">暂无图片数据</div>
- </div>
- </div>
- </div>
- <!-- 放大镜图标 -->
- <div v-if="activeTab === 'model' && artifactData && artifactData.modelFiles && artifactData.modelFiles.length > 0" class="zoom-icon" @click="toggleZoom">
- <el-icon style="color: #7C6444" :size="24">
- <ZoomIn v-if="!isZoomed" />
- <ZoomOut v-else />
- </el-icon>
- </div>
- </div>
- </div>
- <!-- 底部固定信息卡片 -->
- <div class="artifact-info-card" :class="{ hidden: isZoomed }">
- <div class="card-content">
- <div class="artifact-title">{{ getFieldValue(artifactData, 'title') }}</div>
- <!-- 信息表格 -->
- <div class="info-table">
- <div class="info-row">
- <div class="info-label-container" v-if="getFieldValue(artifactData, 'level')">
- <div class="info-label">
- <span class="info-label-text">级别</span>
- <span>{{ getFieldValue(artifactData, 'level') }}</span>
- </div>
- <div class="bottom-img"></div>
- </div>
- <div class="info-label-container" v-if="getFieldValue(artifactData, 'era')">
- <div class="info-label">
- <span class="info-label-text">年代</span>
- <span>{{ getFieldValue(artifactData, 'era') }}</span>
- </div>
- <div class="bottom-img"></div>
- </div>
- </div>
- <div class="info-row">
- <div class="info-label-container" v-if="getFieldValue(artifactData, 'category')">
- <div class="info-label">
- <span class="info-label-text">类别</span>
- <span>{{ getFieldValue(artifactData, 'category') }}</span>
- </div>
- <div class="bottom-img"></div>
- </div>
- <div class="info-label-container" v-if="getFieldValue(artifactData, 'texture')">
- <div class="info-label">
- <span class="info-label-text">材质</span>
- <span>{{ getFieldValue(artifactData, 'texture') }}</span>
- </div>
- <div class="bottom-img"></div>
- </div>
- </div>
- </div>
- <div class="artifact-size" v-if="getFieldValue(artifactData, 'size')">
- {{ getFieldValue(artifactData, 'size') }}
- </div>
- <div class="divider">
- <span class="divider-text">藏品描述</span>
- </div>
- <div class="artifact-description">
- {{ getFieldValue(artifactData, 'intro') }}
- </div>
- </div>
- </div>
- <!-- 加载状态 -->
- <div v-if="loading" class="loading-overlay">
- <div class="loading-text">加载中...</div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, nextTick } from 'vue'
- import { useRouter, useRoute } from 'vue-router'
- import getBookCountApi from '@/api'
- import { ZoomIn, ZoomOut } from '@element-plus/icons-vue'
- const modelUrl = import.meta.env.VITE_MODEL_URL
- const router = useRouter()
- const route = useRoute()
- const activeTab = ref('model')
- const loading = ref(false)
- const artifactData = ref({})
- const isZoomed = ref(false)
- const isFrom = ref('')
- const autoplayEnabled = ref(true)
- const imageCarouselRef = ref(null)
- const currentImageIndex = ref(1)
- const modelList = ref([])
- const imageList = ref([])
- const imgUrl = import.meta.env.VITE_COS_BASE_URL
- // 获取字段值的通用方法,优先获取带B后缀的字段
- const getFieldValue = (item, fieldName) => {
- return item[fieldName]
- }
- // 初始化模型和图片列表
- const initializeLists = () => {
- if (!artifactData.value) {
- modelList.value = []
- imageList.value = []
- return
- }
- // 初始化模型列表
- const modelFiles = artifactData.value.modelFiles || []
- console.log('modelFiles', modelFiles)
- modelList.value = modelFiles.map((modelFile, index) => ({
- type: 'model',
- src: `${modelUrl}${modelFile}`,
- // src: `${modelFile}`,
- alt: `${artifactData.value.title || artifactData.value.name} 3D模型${index + 1}`
- }))
- // 初始化图片列表
- const imgFiles = artifactData.value.imageFiles || []
- imageList.value = imgFiles.map((imgFile, index) => ({
- type: 'image',
- src: `${imgUrl}${imgFile}`,
- alt: `${artifactData.value.title || artifactData.value.name} 图片${index + 1}`
- }))
- }
- // 返回上一页
- const goBack = () => {
- // 如果当前URL包含preview=1参数,则在返回时保持该参数
- router.replace('/collectpage')
- }
- const getModelList = (arr) => {
- // return ['https://4dscene.4dage.com/culturalrelics/HYBJNG/Model.html?m=hybjn006']
- if(arr){
- return arr.filter((item) => {return item.indexOf('.4dage') != -1})
- } else {
- return []
- }
- }
- // 获取文物详情数据
- const getArtifactDetail = async () => {
- let id
- // 从route.query获取id和type
- const routeParams = route.query
- id = routeParams.id
- isFrom.value = routeParams.isFrom
- loading.value = true
- try {
- const response = await getBookCountApi.getArtifactDetailApi(id)
- console.log('文物详情数据:', response)
- if (response) {
- // 处理返回的详情数据,与collectDetails.vue保持一致
- artifactData.value = {
- id: response.id,
- title: response.name || response.title,
- name: response.name || response.title,
- remark: response.description || response.desc || '暂无描述',
- era: response.era || response.agetype || '近现代',
- category: response.texturetype1 || '其他',
- level: response.level || response.grade || '未定级',
- texture: response.material || response.texture || '其他',
- size: response.size || response.dimensions,
- imageFiles: response.imgFiles || [],
- modelFile: response.modelFiles && response.modelFiles.length > 0 ? response.modelFiles[0] : null,
- modelFiles: getModelList(response.modelFiles) || [],
- hasImage: response.hasImage !== false,
- hasModel: response.hasModel !== false
- }
- // 初始化列表
- initializeLists()
- }
- } catch (error) {
- console.error('获取文物详情失败:', error)
- } finally {
- loading.value = false
- }
- }
- // 图片轮播切换事件
- const handleImageCarouselChange = (index) => {
- currentImageIndex.value = imageList.value.length > 0 ? index + 1 : 0
- }
- // 切换标签页
- const switchTab = (tab) => {
- activeTab.value = tab
- if (isZoomed.value) {
- toggleZoom()
- }
- // 重置图片轮播索引
- if (tab === 'image') {
- currentImageIndex.value = imageList.value.length > 0 ? 1 : 0
- }
- if (activeTab.value === 'image') {
- nextTick(() => {
- slideBanner()
- })
- }
- }
- // 切换放大缩小状态
- const toggleZoom = () => {
- isZoomed.value = !isZoomed.value
- if (activeTab.value === 'model') {
- const dom = document.querySelector('#ifr')
- if (dom && dom.contentWindow) {
- if (isZoomed.value) {
- dom.contentWindow.webview.zoomIn()
- } else {
- dom.contentWindow.webview.zoomOut()
- }
- }
- }
- }
- // 重新启动自动播放
- const restartAutoplay = () => {
- // 通过动态控制autoplay属性来重新启动自动播放
- autoplayEnabled.value = false
- nextTick(() => {
- autoplayEnabled.value = true
- })
- }
- // 滑动切换
- const slideBanner = () => {
- // 等待DOM更新后再添加事件监听
- nextTick(() => {
- const box = document.querySelector('.el-carousel__container')
- if (!box) return
- let startPoint = 0
- let stopPoint = 0
- let isDragging = false
- // 重置坐标
- const resetPoint = () => {
- startPoint = 0
- stopPoint = 0
- isDragging = false
- }
- // 处理滑动切换逻辑
- const handleSlideChange = () => {
- if (stopPoint === 0 || startPoint - stopPoint === 0) {
- resetPoint()
- return
- }
- // 只有图片才支持轮播切换
- if (activeTab.value === 'image') {
- const currentCarouselRef = imageCarouselRef.value
- if (currentCarouselRef) {
- if (startPoint - stopPoint > 0) {
- // 向左滑动,下一张
- currentCarouselRef.next()
- } else {
- // 向右滑动,上一张
- currentCarouselRef.prev()
- }
- }
- }
- resetPoint()
- }
- // 移除之前的事件监听器(避免重复绑定)
- box.removeEventListener('touchstart', handleTouchStart)
- box.removeEventListener('touchmove', handleTouchMove)
- box.removeEventListener('touchend', handleTouchEnd)
- box.removeEventListener('mousedown', handleMouseDown)
- box.removeEventListener('mousemove', handleMouseMove)
- box.removeEventListener('mouseup', handleMouseUp)
- box.removeEventListener('mouseleave', handleMouseLeave)
- // === 触摸事件处理 ===
- // 手指按下
- function handleTouchStart(e) {
- startPoint = e.changedTouches[0].pageX
- }
- // 手指滑动
- function handleTouchMove(e) {
- stopPoint = e.changedTouches[0].pageX
- }
- // 手指抬起
- function handleTouchEnd() {
- handleSlideChange()
- // 重新启动自动播放
- restartAutoplay()
- }
- // === 鼠标事件处理 ===
- // 鼠标按下
- function handleMouseDown(e) {
- isDragging = true
- startPoint = e.pageX
- box.style.cursor = 'grabbing'
- // 阻止默认的拖拽行为
- e.preventDefault()
- }
- // 鼠标移动
- function handleMouseMove(e) {
- if (!isDragging) return
- stopPoint = e.pageX
- // 阻止默认行为
- e.preventDefault()
- }
- // 鼠标抬起
- function handleMouseUp() {
- if (!isDragging) return
- box.style.cursor = 'grab'
- handleSlideChange()
- // 重新启动自动播放
- restartAutoplay()
- }
- // 鼠标离开容器
- function handleMouseLeave() {
- if (!isDragging) return
- box.style.cursor = 'grab'
- handleSlideChange()
- // 重新启动自动播放
- restartAutoplay()
- }
- // 添加触摸事件监听器
- box.addEventListener('touchstart', handleTouchStart)
- box.addEventListener('touchmove', handleTouchMove)
- box.addEventListener('touchend', handleTouchEnd)
- // 添加鼠标事件监听器
- box.addEventListener('mousedown', handleMouseDown)
- box.addEventListener('mousemove', handleMouseMove)
- box.addEventListener('mouseup', handleMouseUp)
- box.addEventListener('mouseleave', handleMouseLeave)
- // 设置初始鼠标样式
- box.style.cursor = 'grab'
- })
- }
- // 初始化
- onMounted(() => {
- getArtifactDetail()
- })
- </script>
- <style lang="scss" scoped>
- .collect-detail-container {
- position: relative;
- height: 100vh;
- background: #fff;
- background-size: cover;
- overflow: hidden;
- }
- .top-navigation {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- display: flex;
- align-items: center;
- padding: 20px;
- z-index: 100;
- }
- .back-button {
- width: 30px;
- height: 30px;
- border-radius: 50%;
- cursor: pointer;
- margin-right: 20px;
- img {
- width: 30px;
- height: 30px;
- }
- }
- .custom-tabs {
- flex: 1;
- display: flex;
- justify-content: center;
- max-width: 200px;
- margin: 0 auto;
- .tab-item {
- width: 72px;
- text-align: center;
- color: #ffffff;
- font-size: 16px;
- cursor: pointer;
- transition: all 0.3s ease;
- position: relative;
- border-bottom: 2px solid transparent;
- &.active {
- color: #ffffff;
- font-weight: bold;
- border-bottom-color: #A99271;
- }
- &:hover:not(.active) {
- color: rgba(245, 245, 220, 1);
- }
- }
- }
- .content-display {
- height: 49%;
- background-color: #D1BB9E;
- transition: height 0.3s ease;
- &.zoomed {
- height: 100%;
- }
- }
- .content-area {
- margin: 20px 0;
- display: flex;
- justify-content: center;
- }
- .carousel-viewer {
- position: relative;
- width: 100%;
- height: 100%;
- .empty-state{
- width: 100%;
- height: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- .empty-content{
- text-align: center;
- color: #fff;
- }
- }
- }
- .carousel-container {
- height: 100%;
- position: relative;
- overflow: hidden;
- .carousel-container-model{
- height: 100%;
- }
- .carousel-container-image{
- height: 100%;
- }
- .el-carousel {
- height: 100%;
- .el-carousel__container {
- height: 100%;
- }
- .el-carousel__item {
- display: flex;
- justify-content: center;
- align-items: center;
- height: 100%;
- }
- /*指示器按钮*/
- :deep(.el-carousel__button) {
- width: 10px;
- height: 10px;
- border: none;
- border-radius: 50%;
- background-color: rgba(255, 255, 255, 0.5);
- }
- /*指示器激活按钮*/
- :deep(.is-active) {
- .el-carousel__button {
- background-color: #fff;
- }
- }
- }
- :deep(.el-carousel__indicators--horizontal){
- max-width: 300px;
- background: rgba(0,0,0,0.2);
- text-align: center;
- bottom: 16px;
- border-radius: 50px;
- padding: 0px 4px 0px 8px;
- .el-carousel__indicator--horizontal{
- padding: 4px 4px 0 0;
- }
- }
- .model-container {
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .model-item, .image-item {
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- iframe {
- width: 100%;
- height: 100%;
- }
- img {
- max-width: 100%;
- max-height: 100%;
- object-fit: contain;
- }
- }
- .carousel-image {
- max-width: 100%;
- max-height: 100%;
- object-fit: contain;
- }
- }
- .zoom-icon {
- position: absolute;
- bottom: 36px;
- right: 16px;
- cursor: pointer;
- transition: all 0.3s ease;
- }
- .artifact-info-card {
- position: absolute;
- top: 44%;
- width: 90%;
- height: 47%;
- background: #F4EFE9;
- border-radius: 15px 15px 0 0;
- padding: 20px;
- margin: 20px 0;
- overflow: auto;
- transition:
- opacity 0.3s ease,
- visibility 0.3s ease;
- &.hidden {
- opacity: 0;
- visibility: hidden;
- }
- .artifact-title {
- font-size: 20px;
- font-weight: bold;
- color: #7C6444;
- padding-bottom: 15px;
- margin-bottom: 24px;
- border-bottom: 1px solid #B49D7E;
- }
- .info-table {
- background: #F4EFE9;
- border-radius: 8px;
- margin-bottom: 15px;
- .info-row {
- display: flex;
- justify-content: space-between;
- margin-bottom: 15px;
- gap: 40px;
- .info-label-container {
- flex: 1;
- display: flex;
- flex-direction: column;
- .info-label {
- display: flex;
- justify-content: space-between;
- font-size: 16px;
- color: #464646;
- margin-bottom: 4px;
- .info-label-text{
- font-size: 16px;
- color: #D1BB9E;
- font-weight: bold;
- }
- }
- .bottom-img {
- width: 100%;
- height: 2px;
- background: url('@/assets/img/line.png') no-repeat center center;
- background-size: 100% 100%;
- }
- }
- &:last-child {
- margin-bottom: 0;
- }
- }
- }
- .artifact-size {
- font-size: 14px;
- color: #584735;
- margin-bottom: 15px;
- }
- .divider {
- width: 100%;
- margin: 24px 0;
- display: flex;
- align-items: center;
- .divider-text{
- font-size: 16px;
- color: #D1BB9E;
- font-weight: bold;
- }
- }
- .artifact-description {
- font-size: 13px;
- line-height: 1.6;
- color: #584735;
- text-align: justify;
- }
- }
- @media (max-width: 480px) {
- .tab-navigation {
- max-width: 280px;
- margin: 50px auto 15px;
- .tab-item {
- padding: 8px 15px;
- font-size: 13px;
- }
- }
- .artifact-info {
- padding: 15px;
- .artifact-title {
- font-size: 18px;
- }
- .artifact-description {
- font-size: 12px;
- }
- }
- }
- .no-video {
- height: 96%;
- display: flex;
- justify-content: center;
- align-items: center;
- .no-video-text {
- color: #fff;
- }
- }
- </style>
|