Files
web-temp/layers/templates/FxPreregist01/index.vue

424 lines
12 KiB
Vue

<script setup lang="ts">
import { SplideSlide } from '@splidejs/vue-splide'
import { getComponentGroup, getComponentGroupAry } from '#layers/utils/dataUtil'
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
import type { OperateGroupItem } from '#layers/types/api/operateResources'
import type { Platform } from '#layers/types/components/button'
interface Props {
components: PageDataTemplateComponents
pageVerTmplSeq: number
}
const props = defineProps<Props>()
const runtimeConfig = useRuntimeConfig()
const device = useDevice()
const gameDataStore = useGameDataStore()
const pageDataStore = usePageDataStore()
const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string
const multilingualFileName = 'STOVE_PUBTEMPLATE_homepage_brand_preregist.json'
// Multilingual
const resultGetMultilingual = await useGetMultilingual({
baseApiUrl: dataResourcesUrl,
fileName: multilingualFileName,
})
const { tm, locale }: any = useI18n({
useScope: 'local',
messages: Object(resultGetMultilingual?.value?.multilingual),
})
const { getOperateResources } = useOperateResources()
const { gameData } = storeToRefs(gameDataStore)
const { pageData } = storeToRefs(pageDataStore)
// Constants
const COLOR_INDEX = { BACKGROUND: 0, TEXT: 1 } as const
const preregistModalRef = ref<{
handleOpenPreregist: () => Promise<void>
} | null>(null)
// Preregist Section
const preregistCode = computed(
() => getComponentGroup(props.components, 'eventKey')?.display?.text
)
const prdBackgroundData = computed(() =>
getComponentGroup(props.components, 'background')
)
const preMainTitleData = computed(() =>
getComponentGroup(props.components, 'mainTitle')
)
const preSubTitleData = computed(() =>
getComponentGroup(props.components, 'subTitle')
)
const preImgPreregistdData = computed(() =>
getComponentGroup(props.components, 'imgPreregistReward')
)
const preImgSnsData = computed(() =>
getComponentGroup(props.components, 'imgSnsReward')
)
const preDescriptionData = computed(() =>
getComponentGroup(props.components, 'description')
)
// SNS Buttons
const snsButtonsData = computed(() => {
const buttons = getComponentGroupAry(props.components, 'imgSnsButton')
const links = getComponentGroupAry(props.components, 'txtSnsLink')
if (!buttons?.length) return []
return buttons.map((button, index) => ({
image: button,
link: links?.[index]?.display?.text ?? '',
id: button.id ?? `sns-${index}`,
}))
})
// Button Colors
const buttonColors = computed(() => {
const colorData = getComponentGroupAry(
props.components,
'preregistButtonColor'
)
if (!colorData?.length)
return { backgroundColor: undefined, textColor: undefined }
return {
backgroundColor: getColorCode({
colorName: colorData[COLOR_INDEX.BACKGROUND]?.display?.color_name,
colorCode: colorData[COLOR_INDEX.BACKGROUND]?.display?.color_code,
}),
textColor: getColorCode({
colorName: colorData[COLOR_INDEX.TEXT]?.display?.color_name,
colorCode: colorData[COLOR_INDEX.TEXT]?.display?.color_code,
}),
}
})
// Reward Section
const accBackgroundData = computed(() =>
getComponentGroup(props.components, 'backgroundAccReward')
)
const accMainTitleData = computed(() =>
getComponentGroup(props.components, 'mainTitleAccReward')
)
const accSubTitleData = computed(() =>
getComponentGroup(props.components, 'subTitleAccReward')
)
const accRewardTitleData = computed(() =>
getComponentGroup(props.components, 'rewardTitleAccReward')
)
const accDescriptionData = computed(() =>
getComponentGroup(props.components, 'descriptionAccReward')
)
const accPaginationData = computed(() =>
getComponentGroupAry(props.components, 'pagination')
)
// Async Data - 리워드 완료 데이터
const { data: rewardCompletedData } = await useAsyncData(
`fx-preregist-resources-${pageData.value?.page_seq}-${pageData.value?.page_ver}-${props.pageVerTmplSeq}`,
async () => {
const { page_seq, page_ver } = pageData.value ?? {}
if (!page_seq || !page_ver) return []
try {
const operateGroupList = await getOperateResources({
pageSeq: page_seq,
pageVer: page_ver,
pageVerTmplSeq: props.pageVerTmplSeq,
langCode: locale.value,
})
return getComponentContainer(operateGroupList, 'isAccRewardCompleted', {
isGroup: true,
}) as OperateGroupItem[]
} catch (error) {
if (import.meta.dev) {
// eslint-disable-next-line no-console
console.error('[FxPreregist01] Failed to fetch reward data:', error)
}
return []
}
},
{
default: () => [],
server: false,
}
)
// Reward Images
const rewardImages = computed(() => {
const completeList = getComponentGroupAry(props.components, 'imgAccReward')
const incompleteList = getComponentGroupAry(
props.components,
'imgAccRewardIncomplete'
)
if (!completeList?.length || !incompleteList?.length) return []
return completeList.map((completeItem, index) => ({
id: completeItem.id ?? `reward-${index}`,
incomplete: incompleteList?.[index],
complete: completeItem,
flagType: rewardCompletedData.value?.[index]?.flag_type ?? 0,
}))
})
// Splide Options
const splideOptions = computed(() => {
const length = rewardImages.value.length
return {
type: 'slide' as const,
gap: 16,
arrows: false,
pagination: false,
destroy: true,
breakpoints: {
937: {
destroy: length < 5,
gap: 12,
padding: { left: 40, right: 40 },
},
[BREAKPOINTS.sm - 1]: {
padding: { left: 20, right: 20 },
},
723: {
destroy: length < 4,
},
561: {
destroy: false,
},
},
}
})
const isRunButtonVisible = (marketType?: Platform): boolean => {
if (device.isDesktop) return true
switch (marketType) {
case 'google_play':
return device.isAndroid
case 'app_store':
return device.isApple
default:
return false
}
}
// Handler
const handlePreregistClick = () => {
preregistModalRef.value?.handleOpenPreregist()
}
</script>
<template>
<div class="section-container">
<!-- Preregist Section -->
<section class="relative py-[80px] md:py-[120px]">
<WidgetsBackground
v-if="prdBackgroundData"
:resources-data="prdBackgroundData"
/>
<div class="content-standard">
<WidgetsMainTitle
v-if="preMainTitleData"
:resources-data="preMainTitleData"
class="title-xlg"
/>
<WidgetsSubTitle
v-if="preSubTitleData"
:resources-data="preSubTitleData"
class="title-sm mt-2"
/>
<div class="flex flex-col gap-4 mt-8 sm:flex-row">
<div
v-if="preImgPreregistdData"
class="max-w-[336px] md:max-w-[446px]"
>
<AtomsImg
:src="getResourceSrc(preImgPreregistdData)"
:alt="preImgPreregistdData?.display?.text"
loading="lazy"
decoding="async"
class="w-full h-full object-contain"
/>
</div>
<div
v-if="preImgSnsData"
class="relative max-w-[336px] md:max-w-[446px]"
>
<AtomsImg
:src="getResourceSrc(preImgSnsData)"
:alt="preImgSnsData?.display?.text"
loading="lazy"
decoding="async"
class="w-full h-full object-contain"
/>
<ul
v-if="snsButtonsData.length"
class="absolute bottom-[20px] left-0 w-full flex items-center justify-center gap-2 md:bottom-[24px] md:gap-3"
>
<li
v-for="btn in snsButtonsData"
:key="btn.id"
class="w-[48px] h-[40px] md:w-[72px] md:h-[56px]"
>
<a :href="btn.link" target="_blank" rel="noopener noreferrer">
<AtomsImg
:src="getResourceSrc(btn.image)"
:alt="btn.image?.display?.text"
/>
</a>
</li>
</ul>
</div>
</div>
<div class="flex gap-3 justify-center flex-wrap mt-8 md:gap-2.5">
<BlocksButtonLauncher
type="duplication"
platform="stove"
:background-color="buttonColors.backgroundColor"
:text-color="buttonColors.textColor"
@click="handlePreregistClick"
>
{{ tm('Preregist_Btn_Preegist') }}
</BlocksButtonLauncher>
<template v-if="gameData?.platform_type !== '1'">
<template
v-for="platform in getSupportedPlatforms('2', gameData?.os_type)"
:key="`preregist-${platform}`"
>
<BlocksButtonLauncher
v-if="isRunButtonVisible(platform as Platform)"
type="duplication"
:platform="platform as Platform"
:background-color="buttonColors.backgroundColor"
:text-color="buttonColors.textColor"
>
{{ tm('Preregist_Btn_Preegist') }}
</BlocksButtonLauncher>
</template>
</template>
</div>
<WidgetsDescription
v-if="preDescriptionData"
:resources-data="preDescriptionData"
class="mt-8"
/>
</div>
</section>
<!-- Reward Section -->
<section class="relative py-[80px] md:py-[120px]">
<WidgetsBackground
v-if="accBackgroundData"
:resources-data="accBackgroundData"
/>
<div class="content-standard">
<WidgetsMainTitle
v-if="accMainTitleData"
:resources-data="accMainTitleData"
class="title-xlg"
/>
<WidgetsSubTitle
v-if="accSubTitleData"
:resources-data="accSubTitleData"
class="title-sm mt-2"
/>
<WidgetsSubTitle
v-if="accRewardTitleData"
:resources-data="accRewardTitleData"
class="mt-[48px] text-[18px] font-[700] leading-[26px] tracking-[-0.54px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:mt-[72px] md:text-[24px] md:leading-[34px] md:tracking-[0.72px]"
/>
<div
v-if="rewardImages.length"
class="overflow-hidden w-[calc(100%+40px)] min-h-[228px] mt-6 mx-[-20px] sm:w-[calc(100%+80px)] sm:mx-[-40px] md:w-full md:min-h-[281px] md:mt-8 md:mx-auto"
>
<ClientOnly>
<ul
class="hidden md:flex justify-center md:mb-[20px]"
:style="getPaginationClass(accPaginationData)"
>
<li
v-for="(item, index) in rewardImages"
:key="item.id"
:class="[
'flex items-center',
{ 'is-completed': item.flagType === 2 },
]"
>
<span class="progress-bullet"></span>
<div
v-if="index < rewardImages.length - 1"
class="progress-bar"
></div>
</li>
</ul>
<BlocksSlideDefault v-bind="splideOptions">
<SplideSlide
v-for="item in rewardImages"
:key="item.id"
class="w-[162px] h-[228px] md:w-[172px] md:h-[245px]"
>
<AtomsImg
:src="
getResourceSrc(
item.flagType === 2 ? item.complete : item.incomplete
)
"
:alt="item.default?.display?.text"
loading="lazy"
decoding="async"
class="w-full h-full object-contain"
/>
</SplideSlide>
</BlocksSlideDefault>
</ClientOnly>
</div>
<WidgetsDescription
v-if="accDescriptionData"
:resources-data="accDescriptionData"
class="mt-6 md:mt-8"
/>
</div>
</section>
<WidgetsModalPreregist
ref="preregistModalRef"
:tm="tm"
:preregist-code="preregistCode"
/>
</div>
</template>
<style scoped>
.content-standard {
@apply max-w-[1024px] mx-auto;
}
.splide:not(.is-active):deep(.splide__list) {
@apply flex justify-center gap-3 md:gap-4;
}
.progress-bullet {
@apply block w-3 h-3 rounded-full transition-all duration-300;
background-color: var(--pagination-disabled);
}
.progress-bar {
@apply w-[180px] h-0.5 overflow-hidden;
background-color: var(--pagination-disabled);
}
.is-completed .progress-bullet,
.is-completed .progress-bar {
background-color: var(--pagination-active);
}
</style>