fix. 공통 스타일 분리
This commit is contained in:
@@ -5,6 +5,24 @@
|
||||
}
|
||||
|
||||
.section-content {
|
||||
@apply relative h-full flex flex-col items-center justify-center gap-4 md:gap-5;
|
||||
@apply relative h-full flex flex-col items-center justify-center gap-4 text-center px-[20px] sm:px-[40px] md:gap-5;
|
||||
}
|
||||
|
||||
/* Title Utility Classes */
|
||||
.title-lg {
|
||||
@apply line-clamp-3 text-[24px] font-[700] leading-[34px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:text-[50px] md:leading-[70px];
|
||||
}
|
||||
.title-md {
|
||||
@apply line-clamp-2 text-[20px] font-[700] leading-[30px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:line-clamp-1 md:text-[42px] md:leading-[56px];
|
||||
}
|
||||
.title-sm {
|
||||
@apply line-clamp-2 text-[16px] font-[500] leading-[24px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:line-clamp-1 md:text-[24px] md:leading-[34px];
|
||||
}
|
||||
.title-xs {
|
||||
}
|
||||
|
||||
/* Description Utility Classes */
|
||||
.description-lg {
|
||||
@apply line-clamp-3 text-[15px] font-[400] leading-[24px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:text-[20px] md:leading-[30px];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ const props = defineProps<Props>()
|
||||
<style scoped>
|
||||
.card-news {
|
||||
@apply overflow-hidden relative flex items-center justify-center h-full rounded-lg
|
||||
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full
|
||||
after:border after:border-white/10 after:rounded-lg;
|
||||
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full
|
||||
after:border after:border-white/10 after:rounded-lg;
|
||||
}
|
||||
.card-image {
|
||||
@apply transition-transform duration-300 w-full h-full object-cover;
|
||||
|
||||
@@ -83,5 +83,6 @@ const hasImage = computed(() => {
|
||||
v-else-if="displayText"
|
||||
v-dompurify-html="sanitizedContent"
|
||||
:style="textStyles"
|
||||
class="block"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -16,7 +16,7 @@ const props = defineProps<Props>()
|
||||
|
||||
const mainRef = ref<InstanceType<typeof Splide> | null>(null)
|
||||
const thumbsRef = ref<InstanceType<typeof Splide> | null>(null)
|
||||
const isPlaying = ref<boolean>(false)
|
||||
const playingSlideIndex = ref<number | null>(null)
|
||||
|
||||
const mainOptions = computed<Options>(() => ({
|
||||
type: 'fade',
|
||||
@@ -26,6 +26,7 @@ const mainOptions = computed<Options>(() => ({
|
||||
speed: 600,
|
||||
arrows: false,
|
||||
pagination: false,
|
||||
drag: false,
|
||||
}))
|
||||
const thumbOptions = computed<Options>(() => ({
|
||||
type: 'slide',
|
||||
@@ -45,9 +46,8 @@ const thumbOptions = computed<Options>(() => ({
|
||||
},
|
||||
}))
|
||||
|
||||
// 비디오 클릭 핸들러
|
||||
const handleVideoClick = () => {
|
||||
isPlaying.value = true
|
||||
const handleVideoClick = (index: number) => {
|
||||
playingSlideIndex.value = index
|
||||
}
|
||||
|
||||
let mainInst: SplideType | null = null
|
||||
@@ -58,6 +58,10 @@ onMounted(() => {
|
||||
thumbsInst = thumbsRef.value?.splide ?? null
|
||||
if (mainInst && thumbsInst) {
|
||||
mainInst.sync(thumbsInst)
|
||||
|
||||
mainInst.on('moved', () => {
|
||||
playingSlideIndex.value = null
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -76,28 +80,28 @@ onBeforeUnmount(() => {
|
||||
:key="item.set_order || index"
|
||||
class="main-slide"
|
||||
>
|
||||
<template v-if="!isPlaying">
|
||||
<img
|
||||
:src="getMediaImgSrc(item.media)"
|
||||
alt="main image"
|
||||
class="slide-image"
|
||||
/>
|
||||
<AtomsButtonPlay
|
||||
v-if="getMediaType(item.media) === 'video'"
|
||||
:resources-data="videoPlay"
|
||||
class="btn-play"
|
||||
@click="handleVideoClick()"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<iframe
|
||||
:src="getYouTubeEmbedUrl(getMediaText(item.media))"
|
||||
class="absolute top-0 left-0 w-full h-full"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen
|
||||
/>
|
||||
</template>
|
||||
<img
|
||||
:src="getMediaImgSrc(item.media)"
|
||||
alt="main image"
|
||||
class="slide-image"
|
||||
:class="{ 'opacity-0': playingSlideIndex === index }"
|
||||
/>
|
||||
<AtomsButtonPlay
|
||||
v-if="
|
||||
getMediaType(item.media) === 'video' && playingSlideIndex !== index
|
||||
"
|
||||
:resources-data="videoPlay"
|
||||
class="btn-play"
|
||||
@click="handleVideoClick(index)"
|
||||
/>
|
||||
<iframe
|
||||
v-if="playingSlideIndex === index"
|
||||
:src="getYouTubeEmbedUrl(getMediaText(item.media), true)"
|
||||
class="absolute top-0 left-0 w-full h-full"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen
|
||||
/>
|
||||
</SplideSlide>
|
||||
</Splide>
|
||||
|
||||
@@ -122,14 +126,14 @@ onBeforeUnmount(() => {
|
||||
/* 비디오 iframe 전환 애니메이션 */
|
||||
|
||||
.thumbnail-carousel {
|
||||
@apply w-full px-[20px] sm:px-[40px] md:max-w-[1024px];
|
||||
@apply w-full md:max-w-[944px];
|
||||
}
|
||||
|
||||
.main-splide {
|
||||
@apply overflow-hidden mx-auto rounded-lg border border-white/10 shadow-[0_4px_20px_0_rgba(0,0,0,0.5)];
|
||||
}
|
||||
.main-slide {
|
||||
@apply aspect-[16/9];
|
||||
@apply relative aspect-[16/9];
|
||||
}
|
||||
.slide-image {
|
||||
@apply w-full h-full object-cover;
|
||||
|
||||
@@ -4,11 +4,11 @@ import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
||||
|
||||
interface Props {
|
||||
resourcesData: PageDataResourceGroup
|
||||
gradient?: boolean
|
||||
gradient?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
gradient: false,
|
||||
gradient: '',
|
||||
})
|
||||
|
||||
const resPath = computed(() => {
|
||||
@@ -72,7 +72,7 @@ const posterSrc = computed(() => {
|
||||
<!-- 그라디언트 오버레이 (gradient가 true일 때만) -->
|
||||
<div
|
||||
v-if="props.gradient"
|
||||
class="absolute bottom-0 left-0 right-0 h-[342px] md:h-[720px] bg-gradient-to-b from-[#100d0f]/0 to-[#100d0f]"
|
||||
:class="`absolute bottom-0 left-0 right-0 ${props.gradient}`"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
import { getHeader } from 'h3'
|
||||
import { useRuntimeConfig, useRequestEvent } from 'nuxt/app'
|
||||
|
||||
/**
|
||||
* 게임 별칭을 추출하는 유틸리티 함수
|
||||
* @param host 호스트 문자열
|
||||
* @param baseDomain 기본 도메인
|
||||
* @returns 게임 별칭 또는 빈 문자열
|
||||
*/
|
||||
const extractGameAliasFromHost = (host: string, baseDomain: string): string => {
|
||||
if (!host || !host.includes(baseDomain)) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const subdomain = host.split(':')[0]
|
||||
return subdomain && subdomain !== 'www' ? subdomain : ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 서버 사이드에서 게임 별칭을 가져오는 함수
|
||||
* @param baseDomain 기본 도메인
|
||||
* @returns 게임 별칭 또는 빈 문자열
|
||||
*/
|
||||
const getGameAliasFromServer = (baseDomain: string): string => {
|
||||
try {
|
||||
const event = useRequestEvent()
|
||||
|
||||
if (!event) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// 미들웨어에서 설정한 gameAlias가 있다면 우선 사용
|
||||
if (event.context.gameAlias) {
|
||||
return event.context.gameAlias
|
||||
}
|
||||
|
||||
const host = getHeader(event, 'host') || ''
|
||||
return extractGameAliasFromHost(host, baseDomain)
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('useGetGameAlias server error:', error)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 클라이언트 사이드에서 게임 별칭을 가져오는 함수
|
||||
* @param baseDomain 기본 도메인
|
||||
* @returns 게임 별칭 또는 빈 문자열
|
||||
*/
|
||||
const getGameAliasFromClient = (baseDomain: string): string => {
|
||||
try {
|
||||
const host = window.location.host
|
||||
return extractGameAliasFromHost(host, baseDomain)
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('useGetGameAlias client error:', error)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 게임 별칭을 가져오는 컴포저블 함수
|
||||
* 서버와 클라이언트 환경에서 모두 동작
|
||||
* @returns 게임 별칭 문자열
|
||||
*/
|
||||
export const useGetGameAlias = (): string => {
|
||||
const config = useRuntimeConfig()
|
||||
const baseDomain = (config.public.baseDomain || '.onstove.com') as string
|
||||
|
||||
if (import.meta.client) {
|
||||
return getGameAliasFromClient(baseDomain)
|
||||
}
|
||||
|
||||
return getGameAliasFromServer(baseDomain)
|
||||
}
|
||||
@@ -38,9 +38,8 @@ export default defineNuxtRouteMiddleware(async (to, _from) => {
|
||||
})) as PageDataResponse | null
|
||||
|
||||
if (response?.code === 0 && 'value' in response) {
|
||||
const cleanData = JSON.parse(JSON.stringify(response.value))
|
||||
|
||||
store.setPageData(cleanData)
|
||||
store.setPageData(response.value)
|
||||
console.log('🚀 ~ cleanData:', response.value)
|
||||
} else {
|
||||
store.clearPageData()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import {
|
||||
defineEventHandler,
|
||||
getRequestURL,
|
||||
} from 'h3'
|
||||
|
||||
import { ssrGetFinalLocale } from '../../utils/localeUtil'
|
||||
import type { GameDataResponse } from '../../types/api/gameData'
|
||||
|
||||
export default defineEventHandler(async event => {
|
||||
const url = getRequestURL(event)
|
||||
@@ -59,9 +59,9 @@ export default defineEventHandler(async event => {
|
||||
lang_code: langCode,
|
||||
}
|
||||
|
||||
const response = await $fetch(apiUrl, {
|
||||
const response = (await $fetch(apiUrl, {
|
||||
query: queryParams,
|
||||
})
|
||||
})) as GameDataResponse | null
|
||||
|
||||
const gaId = (response as any).value?.ga_code
|
||||
|
||||
@@ -70,11 +70,8 @@ export default defineEventHandler(async event => {
|
||||
event.context.googleAnalyticsId = gaId
|
||||
}
|
||||
|
||||
// 타입 단언을 사용하여 response의 타입 오류를 해결
|
||||
const res = response as { code?: number; value?: unknown }
|
||||
|
||||
if (res?.code === 0 && res && typeof res === 'object' && 'value' in res) {
|
||||
event.context.gameData = res.value
|
||||
if (response?.code === 0 && 'value' in response) {
|
||||
event.context.gameData = response.value
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('gameData load error:', error)
|
||||
|
||||
@@ -27,7 +27,7 @@ const videoPlayData = computed(() =>
|
||||
<WidgetsMainTitle
|
||||
v-if="mainTitleData"
|
||||
:resources-data="mainTitleData"
|
||||
class="main-title"
|
||||
class="title-sm"
|
||||
/>
|
||||
<BlocksSlideThumbnail
|
||||
:slide-item-list="slideThumbnailData"
|
||||
@@ -36,11 +36,3 @@ const videoPlayData = computed(() =>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.main-title {
|
||||
@apply text-center text-[16px] font-medium leading-[24px] tracking-[-0.48px] md:text-[24px] md:leading-[34px] md:tracking-[-0.72px];
|
||||
text-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
|
||||
font-family: 'Spoqa Han Sans Neo', sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -30,7 +30,7 @@ const buttonListData = computed(() =>
|
||||
<WidgetsBackground
|
||||
v-if="backgroundData"
|
||||
:resources-data="backgroundData"
|
||||
:gradient="true"
|
||||
gradient="h-[342px] bg-[linear-gradient(180deg,rgba(16,13,15,0)_0%,#100D0F_90%)] md:h-[720px]"
|
||||
/>
|
||||
<div class="section-content">
|
||||
<WidgetsMainTitle
|
||||
|
||||
@@ -30,23 +30,21 @@ const props = defineProps<Props>()
|
||||
v-if="hasComponentGroup(item, 'background')"
|
||||
:resources-data="getComponentGroup(item, 'background')"
|
||||
/>
|
||||
<div
|
||||
class="relative h-full flex flex-col items-center justify-center gap-[14px] text-center md:gap-5"
|
||||
>
|
||||
<div class="section-content">
|
||||
<WidgetsSubTitle
|
||||
v-if="hasComponentGroup(item, 'subTitle')"
|
||||
:resources-data="getComponentGroup(item, 'subTitle')"
|
||||
class="line-clamp-2 text-[16px] font-[500] leading-[24px] md:line-clamp-1 md:text-[24px] md:leading-[34px]"
|
||||
class="title-sm"
|
||||
/>
|
||||
<WidgetsMainTitle
|
||||
v-if="hasComponentGroup(item, 'mainTitle')"
|
||||
:resources-data="getComponentGroup(item, 'mainTitle')"
|
||||
class="line-clamp-3 text-[24px] font-[700] leading-[34px] md:text-[50px] md:leading-[70px]"
|
||||
class="title-lg"
|
||||
/>
|
||||
<WidgetsDescription
|
||||
v-if="hasComponentGroup(item, 'description')"
|
||||
:resources-data="getComponentGroup(item, 'description')"
|
||||
class="line-clamp-3 text-[15px] font-[400] leading-[24px] md:text-[20px] md:leading-[30px]"
|
||||
class="description-lg"
|
||||
/>
|
||||
<WidgetsButtonList
|
||||
v-if="hasComponentGroup(item, 'buttonList')"
|
||||
|
||||
Reference in New Issue
Block a user