| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- <template>
- <div :class="['grid-container']">
- <div
- v-for="(item, index) in list"
- :key="index"
- :class="['item', itemStyleType ]"
- :style="{
- height: height,
- backgroundImage: itemStyleType !== 'ugly-box' ?
- `url(${playAudio ? (item.playing ? Pause : Play) : (item.thumbnail || item.image || AppCofig.defaultImage)})` :
- undefined,
- }"
- :tabindex="1"
- @click="handleClick(item)"
- >
- <img
- v-if="itemStyleType === 'ugly-box'"
- :src="item.thumbnail || item.image || AppCofig.defaultImage"
- alt=""
- />
- <div class="title">{{ item.title }}</div>
- </div>
- <audio ref="audio" />
- </div>
- </template>
- <script setup lang="ts">
- import { nextTick, onMounted, ref, type PropType } from 'vue';
- import Play from '@/assets/images/Home/Play.png';
- import Pause from '@/assets/images/Home/Pause.png';
- import AppCofig from '@/common/config/AppCofig';
- import Swal from 'sweetalert2';
- interface Item {
- image: string;
- thumbnail?: string;
- title: string;
- audio?: string;
- playing?: boolean;
- }
- const props = defineProps({
- list: {
- type: Array as PropType<Item[]>,
- required: true,
- default: () => [],
- },
- height: {
- type: String,
- default: undefined,
- },
- defaultImage: {
- type: String,
- default: undefined,
- },
- playAudio: {
- type: Boolean,
- default: false,
- },
- itemStyleType: {
- type: String,
- default: 'main-card shadow',
- },
- });
- const emit = defineEmits(['itemClick']);
- const audio = ref<HTMLAudioElement>();
- onMounted(() => {
- nextTick(() => {
- if (!audio.value)
- return;
- audio.value.onended = () => {
- props.list.forEach(p => p.playing = false);
- };
- })
- });
- function handleClick(item: Item) {
- if (props.playAudio && audio.value) {
- if (item.audio) {
- if (item.playing) {
- audio.value.pause();
- item.playing = false;
- } else {
- audio.value.pause();
- props.list.forEach(p => p.playing = false);
- audio.value.src = item.audio;
- audio.value.play().catch(() => {
- Swal.fire({
- icon: 'error',
- title: '播放失败',
- });
- item.playing = false;
- });
- item.playing = true;
- }
- }
- return;
- }
- emit('itemClick', item)
- }
- </script>
- <style lang="scss" scoped>
- .grid-container {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- grid-template-rows: repeat(4, 1fr);
- row-gap: 2.5vh;
- column-gap: 1.2vw;
- width: 100%;
- .item {
- position: relative;
- background-size: cover;
- background-position: center;
- }
- .ugly-box {
- position: relative;
- background-size: 100% 100%;
- background-image: url('@/assets/images/Home/UglyItemBackground.png');
- padding: 20px;
- cursor: pointer;
- user-select: none;
- img {
- border-radius: 5px 25px 5px 25px;
- box-shadow: 0 3px 6px rgba(0, 0, 0, 0.56), -3px 0 6px rgba(0, 0, 0, 0.26);
- width: 100%;
- height: calc(100% - 20px);
- object-fit: cover;
- }
- .title {
- background: transparent;
- }
- &:hover {
- transform: scale(1.05);
- }
- &:focus {
- outline: 4px solid var(--color-primary);
- }
- }
- .title {
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
- text-align: center;
- color: var(--color-text-light);
- font-size: 0.8rem;
- padding: 8px 0;
- background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
- }
- }
- </style>
|