提交 5a1f83a4 authored 作者: 龙菲's avatar 龙菲

布展模板

上级 6bf04840
<template>
<audio
ref="audio"
@pause="onPause"
@play="onPlay"
@timeupdate="onTimeupdate"
@loadedmetadata="onLoadedmetadata"
:src="url"
controls="controls"
></audio>
</template>
<script>
// 将整数转换成 时:分:秒的格式
function realFormatSecond(second) {
var secondType = typeof second;
if (secondType === "number" || secondType === "string") {
second = parseInt(second);
var hours = Math.floor(second / 3600);
second = second - hours * 3600;
var mimute = Math.floor(second / 60);
second = second - mimute * 60;
return (
hours + ":" + ("0" + mimute).slice(-2) + ":" + ("0" + second).slice(-2)
);
} else {
return "0:00:00";
}
}
export default {
name: "AudioPlayer",
props: {
url: String,
},
data() {
return {
audio: {
// 该字段是音频是否处于播放状态的属性
playing: false,
// 音频当前播放时长
currentTime: 0,
// 音频最大播放时长
maxTime: 0,
},
};
},
methods: {
// 控制音频的播放与暂停
startPlayOrPause() {
return this.audio.playing ? this.pause() : this.play();
},
// 播放音频
play() {
this.$refs.audio.play();
},
// 暂停音频
pause() {
this.$refs.audio.pause();
},
// 当音频播放
onPlay() {
this.audio.playing = true;
},
// 当音频暂停
onPause() {
this.audio.playing = false;
},
// 当timeupdate事件大概每秒一次,用来更新音频流的当前播放时间
onTimeupdate(res) {
this.audio.currentTime = res.target.currentTime;
},
// 当加载语音流元数据完成后,会触发该事件的回调函数
// 语音元数据主要是语音的长度之类的数据
onLoadedmetadata(res) {
console.log("loadedmetadata");
console.log(res);
this.audio.maxTime = parseInt(res.target.duration);
},
},
};
</script>
<style>
</style>
<template>
<div
class="container wow animate__animated animate__fadeInUp"
v-if="url"
>
<div class="img">
<img
:src="url"
v-if="url"
width="100%"
class="img"
lazy
/>
<img
v-else
src="@/assets/404_images/no-pic.png"
alt=""
width="100%"
height="100%"
class="img"
/>
</div>
<div class="desc">
<span class="name">{{ title }}</span>
</div>
</div>
</template>
<script>
export default {
name: "CulturalRelicCard",
props: {
url: {
type: String,
default: "",
},
title: {
type: String,
default: "",
},
},
methods: {},
};
</script>
<style lang='scss' scoped>
$blue: #2069c4;
.container {
border: 1px solid #f1f1f1;
height: 300px;
position: relative;
display: flex;
flex-direction: column;
cursor: pointer;
&:hover {
// img {
// transform: scale(1.2);
// }
.desc {
background-color: $blue;
color: #fff;
}
}
.img {
background-color: #f8f8f8;
height: 240px;
cursor: pointer;
transition: all 0.5s ease;
overflow: hidden;
img {
height: 100%;
object-fit: contain;
// width: auto;
}
}
.desc {
padding: 16px;
display: flex;
justify-content: center;
transition: all 0.5s ease;
flex: 1;
}
}
</style>
\ No newline at end of file
<template>
<audio
ref="audio"
@pause="onPause"
@play="onPlay"
@timeupdate="onTimeupdate"
@loadedmetadata="onLoadedmetadata"
:src="url"
controls="controls"
></audio>
</template>
<script>
// 将整数转换成 时:分:秒的格式
function realFormatSecond(second) {
var secondType = typeof second;
if (secondType === "number" || secondType === "string") {
second = parseInt(second);
var hours = Math.floor(second / 3600);
second = second - hours * 3600;
var mimute = Math.floor(second / 60);
second = second - mimute * 60;
return (
hours + ":" + ("0" + mimute).slice(-2) + ":" + ("0" + second).slice(-2)
);
} else {
return "0:00:00";
}
}
export default {
name: "AudioPlayer",
props: {
url: String,
},
data() {
return {
audio: {
// 该字段是音频是否处于播放状态的属性
playing: false,
// 音频当前播放时长
currentTime: 0,
// 音频最大播放时长
maxTime: 0,
},
};
},
methods: {
// 控制音频的播放与暂停
startPlayOrPause() {
return this.audio.playing ? this.pause() : this.play();
},
// 播放音频
play() {
this.$refs.audio.play();
},
// 暂停音频
pause() {
this.$refs.audio.pause();
},
// 当音频播放
onPlay() {
this.audio.playing = true;
},
// 当音频暂停
onPause() {
this.audio.playing = false;
},
// 当timeupdate事件大概每秒一次,用来更新音频流的当前播放时间
onTimeupdate(res) {
this.audio.currentTime = res.target.currentTime;
},
// 当加载语音流元数据完成后,会触发该事件的回调函数
// 语音元数据主要是语音的长度之类的数据
onLoadedmetadata(res) {
console.log("loadedmetadata");
console.log(res);
this.audio.maxTime = parseInt(res.target.duration);
},
},
};
</script>
<style>
</style>
<!-- -->
<template>
<div class="custom-title">
{{ text }}
</div>
</template>
<script>
export default {
name: "CustomTitle",
props: {
text: {
type: String,
default: "标题",
},
},
data() {
return {};
},
mounted() {},
methods: {},
};
</script>
<style lang="scss" scoped>
.custom-title {
width: 100%;
padding: 8px;
font-size: 18px;
border-bottom: 1px solid rgba($color: #000000, $alpha: 0.3);
margin-bottom: 32px;
}
</style>
<!-- -->
<template>
<div class="footer">
<span>贵州省文化和旅游厅博物馆处版权所有</span>
<span>中国知网提供技术支持</span>
</div>
</template>
<script>
export default {
name: "Footer",
};
</script>
<style lang="scss" scoped>
.footer{
display: flex;
height: 200px;
padding: 0 24px;
justify-content: center;
align-items: center;
background-color: #2069C4;
color: #fff;
// position: fixed;
// bottom: 0;
// left: 0;
// right: 0;
span{
margin-right: 16px;
}
}
</style>
<!-- -->
<template>
<el-menu
class="sidebar-el-menu"
default-active=""
@open="handleOpen"
@close="handleClose"
:default-openeds="[items[0].euId]"
>
<!-- 遍历菜单 -->
<template v-for="(item) in items">
<!-- 含有子菜单 -->
<template v-if="item.children">
<!-- 第一层 含有子菜单菜单 -->
<el-submenu :index="item.euId" :key="item.title">
<template slot="title">
<div slot="title" @click="handleClick(item)">{{ item.title }}</div>
</template>
<menu-list :items="item.children" v-bind="$attrs" v-on="$listeners"></menu-list
><!--递归调用-->
</el-submenu>
</template>
<!-- 第一层 不含子菜单 -->
<template v-else>
<el-menu-item :index="item.euId" :key="item.title">
<div slot="title" @click="handleClick(item)">{{ item.title }}</div>
</el-menu-item>
</template>
</template>
</el-menu>
</template>
<script>
export default {
name: "MenuList",
props: {
items: Array,
isCollapse: Boolean,
},
inheritAttrs:false,
methods: {
handleOpen(key, keyPath) {
},
handleClose(key, keyPath) {
// console.log(key, keyPath);
},
handleClick(item){
// console.log(item.title);
this.$emit('open',item)
}
},
};
</script>
<style lang="scss" scoped>
</style>
<template>
<div class="nav" id="navbar">
<div class="container">
<div class="logo-container">
<img src="@/assets/imgs/home/logo.png" />
</div>
<div class="right">
<div class="tabs">
<div class="tab-item" v-for="(item, index) in pages" :key="index">
<a>{{ item.name }} </a>
</div>
</div>
<div class="tools">
<div class="wrapper">
<el-button round>
<i class="el-icon-user"></i>
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "NavBar",
data() {
return {
pages: [
{
name: "首页",
path: "/",
},
{
name: "精品展",
path: "/boutique",
},
{
name: "虚拟展厅",
path: "/virtual",
},
{
name: "展览",
path: "/display",
},
{
name: "文物展",
path: "/culturalRelic",
},
{
name: "博物馆",
path: "/museum",
},
],
};
},
};
</script>
<style lang="scss" scoped>
.tab-item {
a {
color: #fff;
text-decoration: none;
}
}
.home-nav {
position: absolute;
top: 0;
left: 0;
z-index: 9;
background-color: rgba(#000, 0.25) !important;
}
.header-fixed {
position: fixed !important;
background-color: #2069c4 !important;
z-index: 99;
top: 0;
}
.nav {
height: 100px;
width: 100%;
transition: all 0.5s ease;
background-color: #2069c4;
box-shadow: rgba(0, 0, 0, 0.3) 0 1px 5px 0px;
.container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 70px 0 100px;
overflow: hidden;
.logo-container {
width: 380px;
height: 74px;
}
.right {
display: flex;
height: 100%;
align-items: center;
.tabs {
display: flex;
margin-right: 80px;
min-width: 512px;
height: 100%;
.tab-item {
margin-right: 70px;
color: #fff;
display: flex;
align-items: center;
font-family: "Alibaba-PuHuiTi";
position: relative;
height: 100%;
text-align: center;
a {
font-size: 18px;
white-space: nowrap;
transition: all 0.5s ease;
}
&:hover {
a {
// color: #182f68;
color: #fff8a3;
text-shadow: 0 1px 2px #9fafcb, 1px 0px 2px #9fafcb;
}
}
.router-link-exact-active {
// color: #182f68;
color: #fff8a3;
text-shadow: 0 1px 2px #9fafcb, 1px 0px 2px #9fafcb;
}
}
}
}
}
}
::v-deep .tools {
.el-button {
background: transparent;
border-color: #fff;
color: #fff;
i {
font-size: 16px;
}
}
}
</style>
<template>
<div>
<div class="operations">
<span class="operation-item" v-if="like">
<svg-icon
icon-class="like"
:class="loveCountStatus ? 'like' : ''"
></svg-icon>
<span>{{ loveCount }}</span>
</span>
<span class="operation-item" v-if="collect">
<svg-icon
icon-class="collect"
:class="collectCountStatus ? 'collect' : ''"
></svg-icon>
<span>{{ collectCount }}</span>
</span>
<span v-if="share" class="operation-item">
<svg-icon icon-class="share"></svg-icon>
</span>
</div>
</div>
</template>
<script>
export default {
name: "ReaderOperations",
props: {
// 是否需要点赞,默认需要
like: {
type: Boolean,
default: true,
},
// 是否需要收藏,默认需要
collect: {
type: Boolean,
default: true,
},
// 是否需要分享,默认需要
share: {
type: Boolean,
default: true,
},
// 点赞数量
loveCount: {
type: String,
default: "0",
},
// 收藏数量
collectCount: {
type: String,
default: "0",
},
// 点赞状态
loveCountStatus: {
type: Boolean,
default: false,
},
// 收藏状态
collectCountStatus: {
type: Boolean,
default: false,
},
sourceId: {
type: String,
default: "",
},
// 来源类型 biz_cultural_relic-文物;biz_exhibition-布展;biz_elite_exhibition-精品展
sourceType: {
type: String,
default: "",
},
title: {
type: String,
default: "",
},
},
data() {
return {
loginDialogVisible: false,
shareDialogVisible: false,
curLink: "", //当前地址栏的链接
};
},
};
</script>
<style lang="scss" scoped>
.operations {
margin-top: 22px;
display: flex;
// justify-content: space-between;
.operation-item {
display: flex;
align-items: center;
margin-right: 32px;
cursor: pointer;
}
.svg-icon {
margin-right: 16px;
font-size: 36px;
color: #61666d;
}
.like {
color: #d4237a;
}
.collect {
color: #2069c4;
}
}
</style>
\ No newline at end of file
......@@ -59,6 +59,9 @@
<template v-else-if="item.prop == 'fileSize'">
<slot name="fileSize" :scope="scope.row"></slot>
</template>
<template v-else-if="item.prop == 'themeType'">
<slot name="themeType" :scope="scope.row"></slot>
</template>
<span v-else>{{ scope.row[item.prop] }}</span>
</template>
</el-table-column>
......
<template>
<div class="video-container" @click="hanleClick">
<video :src="url" class="video-dom" ref="video"></video>
<div class="modal" :style="{ opacity: opacity }">
<div class="play-btn">
<i class="el-icon-caret-right"></i>
</div>
<div class="blur"></div>
</div>
</div>
</template>
<script>
export default {
name: "Video",
props: {
url: {
default: "",
type: String,
},
},
data() {
return {
isPlaying: false,
opacity: 1,
};
},
methods: {
// 播放视频
play() {
this.$refs.video.play();
},
// 暂停视频
pause() {
this.$refs.video.pause();
},
hanleClick() {
this.isPlaying = !this.isPlaying;
if (this.isPlaying) {
this.play();
this.opacity = 0;
} else {
this.pause();
this.opacity = 1;
}
},
},
};
</script>
<style scoped lang='scss'>
.video-container {
position: relative;
height: 100%;
.video-dom {
height: auto;
width: 100%;
}
.modal {
width: 100%;
height: 100%;
background-color: rgba($color: #000000, $alpha: 0.5);
display: flex;
justify-content: center;
align-items: center;
position: absolute;
left: 0;
bottom: 0;
transition: all 0.5s ease;
position: absolute;
z-index: 9;
.play-btn {
width: 100px;
height: 70px;
border: 2px solid #892325;
// background-color: #fff;
border-radius: 20px;
display: flex;
justify-content: center;
align-items: center;
color: #892325;
position: absolute;
z-index: 1;
i {
font-size: 50px;
z-index: 1;
}
}
}
.blur {
width: 100px;
height: 70px;
background-color: #fff;
position: absolute;
left: 50%;
bottom: 50%;
transform: translate(-50%, 50%);
filter: blur(26px);
z-index: 0;
}
}
</style>
\ No newline at end of file
......@@ -297,15 +297,9 @@ export default {
},
];
}
if (this.dialogForm.imagesVo) {
this.images = this.dialogForm.imagesVo;
}
if (this.dialogForm.videosVo) {
this.videos = this.dialogForm.videosVo;
}
if (this.dialogForm.audiosVo) {
this.audios = this.dialogForm.audiosVo;
}
this.images = this.dialogForm.imagesVo || [];
this.videos = this.dialogForm.videosVo || [];
this.audios = this.dialogForm.audiosVo || [];
// 编辑
// 回填状态
......@@ -479,7 +473,7 @@ export default {
});
}
});
// return
// return
let unitIds = [];
let unitData = [...this.$refs["exhibitionUnits"].getUnitData()];
// debugger
......
<template>
<el-dialog
:visible.sync="dialogVisible"
width="50%"
width="90%"
style="height: 98%"
:before-close="handleClose"
top="5vh"
......@@ -12,47 +12,12 @@
<div class="label">{{ title }}</div>
</div>
<div class="dialog-content">
<el-main style="height: 100%">
<el-row v-if="videos.length == 1" style="height: 100%">
<el-col :span="24" style="height: 100%" class="video-container">
<video
:src="videos[0]"
style="height: auto; width: 100%"
controls
muted
loop
></video>
</el-col>
</el-row>
<el-row v-if="videos.length > 1" style="height: 100%" :gutter="16">
<template v-for="(item, index) in videos">
<el-col
:span="16"
:key="item"
v-if="index == 0"
style="height: 100%"
class="video-container"
>
<video
:src="videos[0]"
style="height: auto; width: 100%"
controls
loop
></video>
</el-col>
<el-col :span="8" :key="item" v-else>
<video
:src="videos[index]"
style="height: auto; width: 100%"
class="video-container"
controls
loop
></video>
</el-col>
</template>
</el-row>
</el-main>
<NormalStyle
:displayDetail="displayDetail"
v-if="displayDetail.themeType == '1'"
/>
<div v-else-if="displayDetail.themeType == '2'">中国风主题</div>
<div v-else-if="displayDetail.themeType == '3'">红色主题</div>
<div class="dialog-footer">
<el-button type="primary" @click="handleClose">关闭</el-button>
</div>
......@@ -61,43 +26,45 @@
</template>
<script>
import NormalStyle from "./templates/NormalStyle.vue";
import ChStyle from "./templates/ChStyle.vue";
export default {
name: "PreviewDialog",
components: {},
components: {
NormalStyle,
ChStyle,
},
props: {
visible: {
type: Boolean,
default: false,
},
videos: {
type: Array,
default: () => [],
displayDetail: {
type: Object,
default: () => ({}),
},
},
computed: {
dialogVisible: {
get: function () {
return this.visible;
},
set: function () {},
},
title() {
return "查看视频";
return "预览——" + this.displayDetail.title;
},
},
dicts: [],
data() {
return {};
watch: {
visible: {
handler: function (value) {
this.dialogVisible = value;
},
immediate: true,
deep: true,
},
},
async created() {
// console.log(this.videos);
data() {
return {
dialogVisible: false,
};
},
methods: {
// 取消编辑
cancelForm() {
this.$emit("handleClose");
},
methods: {
handleClose(done) {
this.$emit("handleClose");
},
......
<template>
<div class="display-detail">
<div class="content" id="content">
<div
class="content-item sliders"
ref="imgs"
v-if="displayDetail.imagesVo && displayDetail.imagesVo.length > 0"
>
<div class="view-box">
<div
class="slide-group"
:style="{
width: sildeGroupWidth,
left: slideGroupleft + 'px',
}"
>
<div
class="slide-item"
v-for="(item, index) in displayDetail.imagesVo"
:key="index"
>
<img :src="$getFullUrl(item.url)" alt="" />
<div class="modal"></div>
</div>
</div>
</div>
<div class="modal-layer">
<div class="left-layer"></div>
<div class="center-img" @mousewheel="handleImgsSlide">
<!-- <div class="border-box"></div> -->
<img src="@/assets/imgs/display/slider-box.png" alt="" />
</div>
<div class="right-layer"></div>
</div>
</div>
<div class="content-item intro wow animate__animated animate__fadeInUp">
<div class="wrapper">
<div class="title">{{ displayDetail.title }}</div>
<div class="desc-and-tools">
<div class="desc">
<el-row>
<el-col class="item">
<div class="label">关键词:</div>
<div class="value">{{ displayDetail.keyword }}</div>
</el-col>
<el-col class="item" v-if="dicts.display_type">
<div class="label">展览类型:</div>
<div class="value">
{{ dicts.display_type[displayDetail.type] }}
</div>
</el-col>
</el-row>
<el-row>
<el-col class="item" v-if="dicts.display_character">
<div class="label">展览性质:</div>
<div class="value">
{{
dicts.display_character[displayDetail.displayCharacter]
}}
</div>
</el-col>
<el-col class="item">
<div class="label">展览单位:</div>
<div class="value">{{ displayDetail.deptName }}</div>
</el-col>
</el-row>
</div>
<div class="tools">
<ReaderOperations
:loveCount="displayDetail.loveCount"
:loveCountStatus="Boolean(displayDetail.loveCountStatus)"
:collectCount="displayDetail.collectCount"
:collectCountStatus="Boolean(displayDetail.collectCountStatus)"
:sourceId="displayDetail.exhibitionId"
:title="displayDetail.title"
:sourceType="'biz_exhibition'"
@reload="loadDetail"
/>
</div>
</div>
<div class="divider"></div>
<div class="intro-and-video">
<el-row :gutter="70">
<el-col
class="intro-container"
:span="
displayDetail.videosVo && displayDetail.videosVo.length > 0
? 12
: 24
"
>
<div class="intro-title">展览简介</div>
<div class="intro-content">
{{ displayDetail.intro }}
</div>
</el-col>
<el-col
:span="12"
v-if="
displayDetail.videosVo && displayDetail.videosVo.length > 0
"
class="videos"
>
<el-carousel :interval="4000" type="card" height="220px">
<el-carousel-item
v-for="item in displayDetail.videosVo"
:key="item.fileId"
>
<Video :url="$getFullUrl(item.url)" />
</el-carousel-item>
</el-carousel>
</el-col>
</el-row>
</div>
<div
class="audio"
:style="{ animationPlayState: audioPlaying ? 'running' : 'paused' }"
@click="handleClickAudio"
v-if="displayDetail.audiosVo && displayDetail.audiosVo.length > 0"
>
<img src="@/assets/imgs/display/audio.png" alt="" />
<AudioPlayer
style="display: none"
:url="$getFullUrl(displayDetail.audiosVo[0].url)"
ref="AudioPlayer"
/>
</div>
</div>
</div>
<div class="content-item units">
<div class="wrapper">
<div class="custom-title">
<div class="custom-title-prefix">
<img src="@/assets/imgs/display/custom-title.png" alt="" />
</div>
<div class="custom-title-text">展览单元</div>
<div class="custom-title-suffix">
<img src="@/assets/imgs/display/custom-title.png" alt="" />
</div>
</div>
<div class="units-content">
<ul>
<li
v-for="(item, index) in displayDetail.exhibitionUnits"
:key="index"
>
<div class="unit-title">{{ item.title }}</div>
<div class="unit-intro">{{ item.intro }}</div>
<div
class="unit-imgs"
v-if="item.imagesVo && item.imagesVo.length > 0"
>
<div
class="img-item"
v-for="(i, idx) in item.imagesVo"
:key="idx"
>
<img :src="$getFullUrl(i.url)" alt="" />
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="content-item lts">
<div class="wrapper">
<div class="custom-title">
<div class="custom-title-prefix">
<img src="@/assets/imgs/display/custom-title.png" alt="" />
</div>
<div class="custom-title-text">关联文献</div>
<div class="custom-title-suffix">
<img src="@/assets/imgs/display/custom-title.png" alt="" />
</div>
</div>
</div>
<div
class="lts-content"
v-if="
displayDetail.literatureVo && displayDetail.literatureVo.length > 0
"
>
<div class="wrapper">
<el-table
:data="displayDetail.literatureVo"
:header-cell-style="{
background: '#eeeeee',
color: '#333',
}"
:row-style="tableRowStyle"
>
<el-table-column
prop="name"
label="名称"
align="center"
></el-table-column>
<el-table-column
prop="authors"
label="作者"
align="center"
></el-table-column>
<el-table-column
prop="date"
label="出版时间"
align="center"
></el-table-column>
<el-table-column
align="center"
prop="source"
label="出版所在刊物"
></el-table-column>
<el-table-column label="阅读" align="center">
<template slot-scope="scope">
<div class="pdf-img" @click="handleViewLt(scope.row)">
<img src="@/assets/imgs/display/pdf-icon.png" />
</div>
</template>
</el-table-column>
</el-table>
<!-- <div class="title">
</div>
<div class="lts-item"></div> -->
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import AudioPlayer from "@/components/AudioPlayer";
import ReaderOperations from "@/components/ReaderOperations";
import Video from "@/components/Video";
import { mapGetters } from "vuex";
import { previewFile } from "@/utils/file";
export default {
components: {
AudioPlayer,
ReaderOperations,
Video,
"el-image-viewer": () =>
import("element-ui/packages/image/src/image-viewer"),
},
props: {
displayDetail: {
type: Object,
default: () => ({}),
},
},
data() {
return {
options: [
{
value: "name",
label: "文物名称",
},
],
selectValue: "",
keyword: "",
// displayDetail: {},
slideImageWidth: 0, //单个图片的宽度
sildeGroupWidth: 0, //整个轮播的宽度
slideGroupleft: 0, //整个轮播距离父亲盒子的距离(中间view区域)
imgViewerVisible: false,
relateRelics: [],
imgsDom: null,
moveLeft: true,
moveRight: true,
audioPlaying: true,
};
},
computed: {
...mapGetters(["dicts"]),
},
async mounted() {
await this.$store.dispatch("dict/getDictList", [
"display_type",
"display_character",
]);
this.loadDetail();
},
destroyed() {
//同时在destroyed回调中移除监听:
if (navigator.userAgent.toLowerCase().indexOf("firefox") >= 0) {
//firefox支持onmousewheel
removeEventListener("DOMMouseScroll", firefoxListenFun);
}
},
methods: {
async loadDetail() {
if (Object.keys(this.displayDetail).length > 0) {
processUnit(this.displayDetail.exhibitionUnits);
function processUnit(list) {
for (let o of list || []) {
if (o.children) {
if (o.children.length == 0) {
o.children = null;
} else {
processUnit(o.children);
}
}
}
}
this.$nextTick(() => {
this.loadWidth();
// if (
// this.displayDetail.videosVo &&
// this.displayDetail.videosVo.length > 0
// ) {
// this.$message.info("正在播放当前文物讲解音频,点击按钮可关闭");
// this.$refs.AudioPlayer.play();
// }
});
}
},
loadWidth() {
if (
this.displayDetail.imagesVo &&
this.displayDetail.imagesVo.length > 0
) {
let slideImage = document.getElementsByClassName("slide-item")[0];
this.slideImageWidth = slideImage.offsetWidth;
this.sildeGroupWidth =
this.slideImageWidth * this.displayDetail.imagesVo.length;
this.slideGroupleft = -3 * this.slideImageWidth;
}
},
handleImgsSlide(e) {
this.addImgsEventListener(e);
this.throttle(this.pageUpOrDown, 500)(e);
},
addImgsEventListener(e) {
e = e || window.event;
if (navigator.userAgent.toLowerCase().indexOf("msie") >= 0) {
// IE
e.returnValue = false;
} else {
// Chrome
e.preventDefault();
}
if (navigator.userAgent.toLowerCase().indexOf("firefox") >= 0) {
//firefox支持onmousewheel
addEventListener("DOMMouseScroll", this.firefoxListenFun, false);
}
},
firefoxListenFun(e) {
var obj = e.target;
var onmousewheel;
while (obj) {
onmousewheel = obj.getAttribute("onmousewheel") || obj.onmousewheel;
if (onmousewheel) break;
if (obj.tagName == "BODY") break;
obj = obj.parentNode;
}
if (onmousewheel) {
if (e.preventDefault) e.preventDefault(); //禁止页面滚动
if (typeof obj.onmousewheel != "function") {
//将onmousewheel转换成function
eval("window._tmpFun = function(event){" + onmousewheel + "}");
obj.onmousewheel = window._tmpFun;
window._tmpFun = null;
}
// 不直接执行是因为若onmousewheel(e)运行时间较长的话,会导致锁定滚动失效,使用setTimeout可避免
setTimeout(function () {
obj.onmousewheel(e);
}, 1);
}
},
pageUpOrDown(e) {
let detail = e.wheelDelta || e.detail || e.wheelDeltaY;
let direction = detail > 0 ? "left" : "right";
if (direction == "left" && this.moveLeft) {
this.slideGroupleft += this.slideImageWidth;
this.moveRight = true;
}
if (direction == "right" && this.moveRight) {
this.slideGroupleft += this.slideImageWidth * -1;
this.moveLeft = true;
}
if (this.slideGroupleft == 0) {
this.moveLeft = false;
this.moveRight = true;
}
if (
this.slideGroupleft ==
4 * this.slideImageWidth - this.sildeGroupWidth
) {
this.moveRight = false;
this.moveLeft = true;
}
},
//节流函数
throttle(fn, gapTime) {
let _this = this;
return function () {
let _nowTime = +new Date();
if (_nowTime - _this._lastTime > gapTime || !_this._lastTime) {
fn(...arguments); // 函数可以带参数
_this._lastTime = _nowTime;
}
};
},
// 关联文献的行样式调整
tableRowStyle({ row, rowIndex }) {
if (rowIndex % 2 == 0) {
return {
background: "#f9f9f9 !important",
};
} else {
return {
background: "#fff !important",
};
}
},
// 预览关联文献
handleViewLt(item) {
previewFile(item.files[0].url, item.files[0].name);
},
// 点击音频
handleClickAudio() {
this.audioPlaying = !this.audioPlaying;
if (this.audioPlaying) {
this.$refs["AudioPlayer"].play();
// console.log(this.$refs['audioContainer']);
} else {
this.$refs["AudioPlayer"].pause();
}
},
},
};
</script>
<style lang="scss" scoped>
// 中国风主题样式
/**公共样式开始 */
.wrapper {
width: 1200px;
}
.custom-title {
display: flex;
align-items: center;
.custom-title-prefix,
.custom-title-suffix {
width: 27px;
height: 27px;
margin: 15px;
img {
width: 100%;
height: 100%;
}
}
.custom-title-text {
font-size: 40px;
font-family: KaiTi;
font-weight: 400;
color: #0f0f0f;
line-height: 114px;
}
}
/**公共样式结束 */
/**样式开始 */
.display-detail {
overflow-x: hidden;
.content {
width: 100%;
overflow-y: hidden;
.content-item {
width: 100%;
}
/**轮播图 */
.sliders {
height: 434px;
// margin: 52px 0 84px 0;
margin-top: 52px;
display: flex;
justify-content: center;
position: relative;
.view-box {
position: relative;
width: 1200px;
.slide-group {
display: flex;
position: absolute;
height: 100%;
transition: all 0.8s cubic-bezier(0.25, 0.1, 0.25, 1);
/**保证能够最开始刚好有四格卡在中间*/
// transform: translateX(-900px);
.slide-item {
width: 300px;
height: 100%;
position: relative;
img {
width: 300px;
height: 100%;
object-fit: cover;
// filter: grayscale(14);
}
}
}
}
.modal-layer {
position: absolute;
display: flex;
width: 100%;
height: 100%;
justify-content: center;
.center-img {
width: 1200px;
transform: translateY(-25px);
position: relative;
filter: drop-shadow(2px 4px 6px #f8f8f8);
img {
transform: translateX(-10px);
}
}
.left-layer,
.right-layer {
flex: 1;
height: 100%;
background-color: rgba(#f8f8f8, 0.8);
}
}
}
/**展览信息 */
.intro {
margin-top: 84px;
display: flex;
justify-content: center;
.wrapper {
background: url("~@/assets/imgs/display/intro-bg.png");
background-repeat: no-repeat;
background-size: 100% 100%;
padding: 60px;
position: relative;
.title {
font-size: 62px;
font-family: KaiTi;
font-weight: 400;
color: #0f0f0f;
margin-bottom: 60px;
}
.desc-and-tools {
display: flex;
justify-content: space-between;
.desc {
display: flex;
.item {
display: flex;
.label,
.value {
font-size: 18px;
font-weight: 300;
color: #0f0f0f;
line-height: 36px;
}
}
}
}
.divider {
width: 100%;
height: 2px;
border: 1px dashed #b1b0af;
margin: 60px 0;
}
.intro-and-video {
.intro-container {
.intro-title {
font-size: 40px;
font-family: KaiTi;
font-weight: 400;
color: #0f0f0f;
margin-bottom: 43px;
position: relative;
&::after {
content: "";
display: inline-block;
width: 60px;
height: 2px;
background-color: #caad91;
position: absolute;
left: 0;
bottom: -12px;
}
}
.intro-content {
font-size: 16px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #444444;
text-indent: 32px;
line-height: 28px;
}
}
.videos {
position: relative;
::v-deep {
.el-carousel {
height: 100%;
width: 100%;
.el-carousel__container {
height: 100%;
display: flex;
justify-content: center;
}
.el-carousel__item {
width: 100%;
transform: translateX(0) !important;
}
}
}
}
}
.audio {
position: absolute;
right: -100px;
top: 34px;
cursor: pointer;
animation: audioRotate 8s linear infinite;
transform-origin: center center;
border: 1px solid #892325;
border-radius: 50%;
// padding: 10px;
width: 60px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
img {
width: 28px;
height: 28px;
}
}
}
}
@keyframes audioRotate {
0% {
transform: rotateZ(0deg);
}
100% {
transform: rotateZ(360deg);
}
}
@-webkit-keyframes audioRotate {
0% {
transform: rotateZ(0deg);
}
100% {
transform: rotateZ(360deg);
}
}
.animation-play-paused {
animation-play-state: paused;
-webkit-animation-play-state: paused; /* Safari 和 Chrome */
}
/**展览单元 */
.units {
margin-top: 94px;
display: flex;
flex-direction: column;
align-items: center;
.units-content {
ul {
li {
margin-bottom: 20px;
.unit-title {
font-size: 36px;
font-family: KaiTi;
font-weight: 400;
color: #4e392c;
line-height: 83px;
display: flex;
justify-content: center;
}
.unit-intro {
font-size: 16px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #444444;
text-indent: 36px;
margin-bottom: 10px;
}
.unit-imgs {
width: 100%;
.img-item {
width: 100%;
img {
width: 100%;
}
}
}
}
}
}
}
/**关联文献 */
.lts {
// background-color: aquamarine;
display: flex;
flex-direction: column;
align-items: center;
margin-top: 100px;
.lts-content {
display: flex;
justify-content: center;
width: 100%;
height: 569px;
background-image: url('~@/assets/imgs/display/lts-bg.png');
}
}
}
}
</style>
\ No newline at end of file
<!-- -->
<template>
<div class="display-detail">
<div class="wrapper">
<div class="back">
<el-button type="text" icon="el-icon-arrow-left">返回上页</el-button>
</div>
<el-row class="detail-container" :gutter="30">
<el-col class="cr-images" :span="16">
<el-carousel
indicator-position="outside"
:autoplay="false"
height="600px"
>
<el-carousel-item
v-for="(item, index) in displayDetail.imagesVo"
:key="index"
>
<el-image
style="width: 100%; height: 100%"
:src="$getFullUrl(item.url)"
fit="contain"
></el-image>
</el-carousel-item>
</el-carousel>
<div class="enlarge" @click="handelPreviewImages">
<img src="@/assets/imgs/enlarge-s.png" alt="" />
</div>
</el-col>
<el-col class="relic-info" :span="8">
<div class="info-title">
<div
style="
display: flex;
align-items: center;
justify-content: space-between;
"
>
<h4>
{{ displayDetail.title }}
</h4>
<span class="view-container">
<svg-icon icon-class="view" class="view-svg-icon"></svg-icon>
<span class="view-text">{{ displayDetail.browseCount }}</span>
</span>
</div>
<AudioPlayer
:url="$getFullUrl(displayDetail.audiosVo[0].url)"
ref="AudioPlayer"
v-if="displayDetail.audiosVo && displayDetail.audiosVo.length > 0"
/>
</div>
<div class="info-body">
<div class="basic-info">
<div class="body-item">
<span class="label">关键词</span>
<span class="value">{{ displayDetail.keyword }}</span>
</div>
<div class="body-item">
<span class="label">展览类型</span>
<span class="value">{{
dicts.display_type[displayDetail.type]
}}</span>
</div>
<div class="body-item">
<span class="label">展览性质</span>
<span class="value">{{
dicts.display_character[displayDetail.displayCharacter]
}}</span>
</div>
<div class="body-item">
<span class="label">展览单位</span>
<span class="value">{{ displayDetail.deptName }}</span>
</div>
</div>
</div>
<ReaderOperations
:loveCount="displayDetail.loveCount"
:loveCountStatus="Boolean(displayDetail.loveCountStatus)"
:collectCount="displayDetail.collectCount"
:collectCountStatus="Boolean(displayDetail.collectCountStatus)"
:sourceId="displayDetail.exhibitionId"
:title="displayDetail.title"
:sourceType="'biz_exhibition'"
@reload="loadDetail"
/>
</el-col>
</el-row>
<div class="margin-bottom-32">
<CustomTitle text="展览介绍" />
<div class="intro text-indent">
{{ displayDetail.intro }}
</div>
</div>
<div
class="margin-bottom-32"
v-if="
displayDetail.exhibitionUnits &&
displayDetail.exhibitionUnits.length > 0
"
>
<CustomTitle text="展览单元" />
<el-row :gutter="20">
<el-col :span="8">
<!-- 只能单开 unique-opened -->
<menu-list
:items="displayDetail.exhibitionUnits"
:isCollapse="false"
@open="handleOpenUnit"
></menu-list>
</el-col>
<el-col :span="16">
<el-card>
<div slot="header" class="clearfix">
<span>单元介绍</span>
</div>
<div class="unit-content">
<div class="intro text-indent" v-if="curUnit.intro">
{{ curUnit.intro }}
</div>
<div
class="images"
v-if="curUnit.imagesVo && curUnit.imagesVo.length > 0"
>
<el-row :gutter="20">
<el-col
:span="24 / curUnit.imagesVo.length"
v-for="item in curUnit.imagesVo"
:key="item.euId"
class="margin-bottom-32"
>
<img
:src="$getFullUrl(item.url)"
alt=""
srcset=""
width="100%"
/>
<div class="desc flex-center">
{{ item.name }}
</div>
</el-col>
</el-row>
</div>
<div
class="videos"
v-if="curUnit.videosVo && curUnit.videosVo.length > 0"
>
<el-row :gutter="20">
<el-col
:span="24 / curUnit.videosVo.length"
v-for="item in curUnit.videosVo"
:key="item.euId"
>
<Video :url="$getFullUrl(item.url)" />
</el-col>
</el-row>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<div
class="margin-bottom-32"
v-if="
displayDetail.culturalRelicVo &&
displayDetail.culturalRelicVo.length > 0
"
>
<CustomTitle text="关联文物" />
<el-row :gutter="20">
<el-col
:span="6"
v-for="(item, index) in displayDetail.culturalRelicVo"
:key="index"
>
<Card :title="item.name" :url="$getFullUrl(item.faceImageUrl)"
/></el-col>
</el-row>
</div>
<div
class="relate-video margin-bottom-32"
v-if="displayDetail.videosVo && displayDetail.videosVo.length > 0"
>
<CustomTitle text="关联视频" />
<el-carousel :interval="4000" type="card" height="400px">
<el-carousel-item
v-for="item in displayDetail.videosVo"
:key="item.fileId"
>
<div class="video-container">
<div class="video-box">
<Video :url="$getFullUrl(item.url)" />
</div>
<div class="info-box">
<h4 class="name">{{ item.name.split(".")[0] }}</h4>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
<div class="relate-book margin-bottom-32">
<CustomTitle text="相关文献" />
<div
class="book-item"
v-for="(item, index) in displayDetail.literatureVo"
:key="index"
>
<span class="mr-20">{{ item.name }}</span>
<span class="mr-20">{{ item.author }}</span>
<span>{{ item.source }}</span>
</div>
</div>
<!-- <div class="relate-cultual-relic margin-bottom-32">
<CustomTitle text="关联文物" />
<div class="display-group">
<div
class="display-item"
v-for="(item, index) in relateRelics"
:key="index"
@click="handleToDetail(item.exhibitionId)"
>
<img :src="item.faceImageUrl" width="100%" :alt="item.title" />
<div class="display-desc">
<div class="desc-title">
<h4>{{ item.name }}</h4>
</div>
</div>
</div>
</div>
</div> -->
</div>
<el-image-viewer
v-if="imgViewerVisible"
:on-close="closeImgViewer"
:url-list="imgList"
/>
</div>
</template>
<script>
import AudioPlayer from "@/components/AudioPlayer";
import CustomTitle from "@/components/CustomTitle";
import ReaderOperations from "@/components/ReaderOperations";
import Card from "@/components/Card";
import Video from "@/components/Video";
// import QRCode from "qrcodejs2";
import MenuList from "@/components/MenuList";
import { mapGetters } from "vuex";
export default {
components: {
CustomTitle,
AudioPlayer,
ReaderOperations,
MenuList,
Video,
Card,
"el-image-viewer": () =>
import("element-ui/packages/image/src/image-viewer"),
},
props: {
displayDetail: {
type: Object,
default: () => ({}),
},
},
data() {
return {
options: [
{
value: "name",
label: "文物名称",
},
],
selectValue: "",
keyword: "",
displayDetail: {},
slideImageWidth: "",
imgViewerVisible: false,
relateRelics: [],
curUnit: [],
};
},
computed: {
...mapGetters(["dicts"]),
},
mounted() {
// this.creatQrCode();
this.loadDetail();
},
methods: {
creatQrCode() {
this.$nextTick(() => {
var qrcode = new QRCode(this.$refs.qrCodeUrl, {
text: "http://www.gzmuseum.com/", // 需要转换为二维码的内容
width: 100,
height: 100,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H,
});
});
},
loadDetail() {
if (Object.keys(this.displayDetail).length > 0) {
if (
this.displayDetail.exhibitionUnits &&
this.displayDetail.exhibitionUnits.length > 0
) {
this.curUnit = this.displayDetail.exhibitionUnits[0];
}
processUnit(this.displayDetail.exhibitionUnits);
function processUnit(list) {
for (let o of list || []) {
if (o.children) {
if (o.children.length == 0) {
o.children = null;
} else {
processUnit(o.children);
}
}
}
}
}
},
closeImgViewer() {
this.imgViewerVisible = false;
},
handelPreviewImages() {
this.imgViewerVisible = true;
this.imgList = this.displayDetail.imagesVo.map((item) =>
this.$getFullUrl(item.url)
);
},
handleOpenUnit(item) {
this.curUnit = item;
},
},
};
</script>
<style lang="scss" scoped>
.mr-20 {
margin-right: 20px;
}
$label: #9f9c9a;
.search {
width: 1200px;
margin: auto;
margin-bottom: 32px;
}
.wrapper {
width: 1200px;
margin: 60px auto;
background-color: #fff;
padding: 60px;
.back {
font-size: 18px;
font-weight: bold;
margin-bottom: 40px;
.el-button {
color: $label;
}
}
.detail-container {
margin-bottom: 32px;
// display: flex;
img {
max-width: 100%;
height: auto;
}
.cr-images {
width: 700px;
// margin-right: 32px;
position: relative;
.el-image {
background-color: #f5f5f9;
}
.enlarge {
position: absolute;
bottom: 60px;
right: 40px;
display: flex;
z-index: 9;
background-color: #c1925b;
width: 40px;
height: 40px;
border-radius: 12px;
display: flex;
justify-content: center;
align-items: center;
margin-right: 10px;
cursor: pointer;
img {
width: 24px;
height: 24px;
}
}
}
.relic-info {
.info-title {
display: flex;
// align-items: center;
flex-direction: column;
margin-bottom: 40px;
.play {
margin-left: 10px;
font-size: 32px;
cursor: pointer;
}
}
.info-body {
display: flex;
justify-content: space-between;
align-items: flex-end;
.basic-info {
.body-item {
display: flex;
.label {
display: block;
width: 80px;
margin-right: 26px;
margin-bottom: 10px;
font-weight: bold;
color: #9f9c9a;
}
}
}
}
.view-3d {
margin-top: 32px;
padding: 6px 10px;
width: 100%;
background-color: #c1925b;
border-radius: 48px;
display: flex;
justify-content: center;
align-items: center;
color: white;
cursor: pointer;
img {
margin-right: 10px;
}
}
}
.title-container {
display: flex;
justify-content: center;
position: relative;
.operation {
position: absolute;
right: 0;
display: flex;
align-items: center;
height: 100%;
}
}
.content {
text-indent: 32px;
}
}
.relate-book {
.book-item {
margin-bottom: 10px;
}
}
.relate-cultual-relic {
.display-group {
margin-top: 24px;
display: flex;
flex-flow: row wrap;
.display-item {
width: 282px;
margin-bottom: 32px;
border-radius: 16px;
border: 1px solid rgba(0, 0, 0, 0.1);
// padding: 0 0 32px 0;
background-color: #fff;
margin-right: 10px;
cursor: pointer;
img {
border-radius: 16px 16px 0 0;
}
.display-desc {
padding: 0 16px;
.desc-content {
text-indent: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
}
.display-source {
margin-top: 8px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
}
}
}
}
.relate-video {
.video-container {
display: flex;
flex-direction: column;
align-items: center;
.video-box {
display: flex;
justify-content: center;
border: 1px solid #000;
video {
width: 800px;
}
}
.info-box {
width: 800px;
display: flex;
justify-content: space-between;
}
}
}
}
.text-indent {
text-indent: 28px;
}
.margin-bottom-32 {
margin-bottom: 32px;
}
</style>
export const themeTypeOptions = [{
label: "中国风",
label: "默认主题",
value: "1"
},
{
label: "模板主题二",
label: "中国风",
value: "2"
},
{
label: "模板主题三",
label: "红色主题",
value: "3"
},
]
export const themeTypeCode = {
1: '默认主题',
2: '中国风',
3: '红色主题'
}
......@@ -32,36 +32,21 @@
</template>
<template v-slot:faceImageUrl="data">
<img
:src="$getFullUrl(data.scope.faceImagePressUrl || data.scope.faceImageUrl)"
:src="
$getFullUrl(data.scope.faceImagePressUrl || data.scope.faceImageUrl)
"
alt="查看大图"
v-if="$getFullUrl(data.scope.faceImagePressUrl || data.scope.faceImageUrl)"
v-if="
$getFullUrl(data.scope.faceImagePressUrl || data.scope.faceImageUrl)
"
style="cursor: pointer"
width="100px"
@click="handelPreviewImages(data.scope.faceImageUrl)"
/>
</template>
<template v-slot:images="data">
<span
v-if="$getFullUrl(data.scope.images)"
style="color: #409eff; cursor: pointer"
@click="handelPreviewImages($getFullUrl(data.scope.images))"
>查看大图</span
>
</template>
<template v-slot:videos="data">
<span
v-if="$getFullUrl(data.scope.videos)"
style="color: #409eff; cursor: pointer"
@click="handelPreviewVideos($getFullUrl(data.scope.videos))"
>查看视频</span
>
<template v-slot:themeType="data">
{{ themeTypeCode(data.scope.themeType) }}
</template>
<template v-slot:audios="data">
<span v-if="data.scope.videos" style="color: #409eff; cursor: pointer"
>查看音频</span
>
</template>
<template v-slot:operates="scope">
<TableOperation
:operations="tableOperations"
......@@ -89,9 +74,10 @@
@refresh="loadData"
/>
<PreviewDialog
v-if="Object.keys(curPreviewObj).length > 0"
:visible="previewDialogVisible"
:displayDetail="curPreviewObj"
@handleClose="handleClosePreviewDialog"
:videos="previewVideos"
/>
<CopyDialog
:visible="copyDialogVisible"
......@@ -117,6 +103,7 @@ import PreviewDialog from "./components/PreviewDialog";
import CopyDialog from "./components/CopyDialog";
import SearchBar from "@/components/SearchBar";
import { mapGetters } from "vuex";
import { themeTypeCode } from "./contants";
export default {
components: {
TablePage,
......@@ -167,7 +154,7 @@ export default {
form: {
title: "", //标题
type: "", // 类别(待定)--枚举值(社会、生活等)
displayCharacter: '1', //展览性质(精品展2、布展1、文物展3)--此处填写布展类别
displayCharacter: "1", //展览性质(精品展2、布展1、文物展3)--此处填写布展类别
keyword: "", // 关键词
deptId: "", //展览单位id--暂填入用户自己的单位
regionCode: "", // 所在地域--暂填入用户自己的地区
......@@ -179,8 +166,8 @@ export default {
faceImage: "", // 封面(图片1张)
images: "", //展览图片
videos: "", //展览视频
audios: "",//展览音频
exhibitionUnits:[]//布展单元
audios: "", //展览音频
exhibitionUnits: [], //布展单元
},
loading: false,
imgViewerVisible: false,
......@@ -188,6 +175,7 @@ export default {
copyDialogVisible: false,
previewVideos: [],
displayTypes: {},
curPreviewObj: {}, //当前预览的对象
};
},
computed: {
......@@ -236,7 +224,7 @@ export default {
console.log("reset");
},
// 加载表格数据
// 加载表格数据
async loadData() {
var params = {
page: this.list.current,
......@@ -249,11 +237,12 @@ export default {
}
},
async handleOperation(value, row) {
// console.log("handleOperation", value, row);
switch (value.type) {
case "add":
this.editDialogVisible = true;
case "view":
this.previewDialogVisible = true;
this.curPreviewObj = row;
break;
case "edit":
const { exhibitionId } = row;
......@@ -314,15 +303,6 @@ export default {
this.imgList = [images];
}
},
// 预览视频和音频
handelPreviewVideos(videos) {
this.previewDialogVisible = true;
if (videos.indexOf(",") != -1) {
this.previewVideos = videos.split(",");
} else {
this.previewVideos = [videos];
}
},
handleClosePreviewDialog() {
this.previewDialogVisible = false;
......@@ -342,6 +322,10 @@ export default {
this.editDialogVisible = true;
console.log("this.form", this.form);
},
themeTypeCode(code) {
return themeTypeCode[code];
},
},
};
</script>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论