447 lines
14 KiB
Vue
447 lines
14 KiB
Vue
<script setup lang="ts">
|
|
import type {
|
|
FooterMenuItem,
|
|
FooterData,
|
|
DevCiConfig,
|
|
} from '#layers/types/components/footer'
|
|
|
|
// Configuration
|
|
const runtimeConfig = useRuntimeConfig()
|
|
const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string
|
|
const multilingualFileName = 'STOVE_PUBTEMPLATE_homepage_brand_footer.json'
|
|
|
|
// Multilingual
|
|
const resultGetMultilingual = await useGetMultilingual({
|
|
baseApiUrl: dataResourcesUrl,
|
|
fileName: multilingualFileName,
|
|
})
|
|
const { tm, locale }: any = useI18n({
|
|
useScope: 'local',
|
|
messages: Object(resultGetMultilingual?.value?.multilingual),
|
|
})
|
|
|
|
// Footer_caution 값이 있고 빈 객체가 아닌지 체크
|
|
const hasCautionText = computed(() => {
|
|
const value = tm('Footer_caution')
|
|
|
|
// null, undefined 체크
|
|
if (value === null || value === undefined) {
|
|
return false
|
|
}
|
|
|
|
// 빈 객체 체크
|
|
if (
|
|
typeof value === 'object' &&
|
|
!Array.isArray(value) &&
|
|
Object.keys(value).length === 0
|
|
) {
|
|
return false
|
|
}
|
|
|
|
// 문자열로 변환하여 빈 문자열 또는 '{}' 문자열 체크
|
|
const stringValue = String(value).trim()
|
|
if (stringValue === '' || stringValue === '{}') {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
const gameDataStore = useGameDataStore()
|
|
const { gameData } = storeToRefs(gameDataStore)
|
|
// const path = ref<string>(`${staticUrl}/local/template/${gameData.value.s3_folder_name}`)
|
|
|
|
// 공통다국어 data
|
|
const footerLinks = computed((): FooterMenuItem[] => {
|
|
const menu = (tm as any)('Footer_Menu')
|
|
const menuItems = Array.isArray(menu) ? (menu as FooterMenuItem[]) : []
|
|
|
|
// ja일 때 fund_display_yn에 따라 id가 footerFund인 항목 처리
|
|
if (locale.value === 'ja') {
|
|
const fundDisplayYn = footerData.value?.fund_display_yn
|
|
const fundDisplayUrl = footerData.value?.fund_display_url
|
|
|
|
// id가 footerFund인 항목의 인덱스 찾기
|
|
const fundIndex = menuItems.findIndex(
|
|
(item: any) => item.id === 'footerFund'
|
|
)
|
|
|
|
// fund_display_yn이 'y'가 아니면 id가 footerFund인 항목 제거
|
|
if (!fundDisplayYn) {
|
|
return menuItems.filter((item: any) => item.id !== 'footerFund')
|
|
}
|
|
|
|
// fund_display_yn이 'y'이면 id가 footerFund인 항목의 url 설정
|
|
if (fundDisplayYn && fundIndex !== -1) {
|
|
const updatedMenuItems = [...menuItems]
|
|
updatedMenuItems[fundIndex] = {
|
|
...updatedMenuItems[fundIndex],
|
|
url: fundDisplayUrl || updatedMenuItems[fundIndex].url,
|
|
}
|
|
return updatedMenuItems
|
|
}
|
|
}
|
|
|
|
return menuItems
|
|
})
|
|
const footerData = ref(gameData.value?.footer_json as unknown as FooterData)
|
|
const setDevCi = ref<DevCiConfig>({
|
|
dev_ci_yn: gameData.value?.footer_dev_ci_img_yn as boolean,
|
|
dev_ci_img_path: gameData.value?.footer_dev_ci_img_path as string,
|
|
})
|
|
|
|
const useGameRating = computed<boolean>(() => {
|
|
return footerData.value.use_game_rating
|
|
})
|
|
|
|
///local/template/common/grades_age
|
|
const getGameRatingImage = computed((): { type: string; image: string } => {
|
|
// 안전하게 rating_type 값 확인
|
|
if (
|
|
!footerData.value?.game_rating_info?.rating_type ||
|
|
typeof footerData.value.game_rating_info.rating_type !== 'string'
|
|
) {
|
|
return { type: '', image: '' }
|
|
}
|
|
|
|
const contentInfo = footerData.value.game_rating_info.rating_type
|
|
const ratingType = contentInfo.trim()
|
|
|
|
// 빈 문자열인 경우 처리
|
|
if (!ratingType) {
|
|
return { type: '', image: '' }
|
|
}
|
|
|
|
const ageTypeMap: Record<string, string> = {
|
|
'6': 'Type6',
|
|
'12': 'Type12',
|
|
'15': 'Type15',
|
|
'18': 'Type18',
|
|
'19': 'Type19',
|
|
all: 'TypeAll',
|
|
e: 'TypeExempt',
|
|
}
|
|
|
|
const type = ageTypeMap[ratingType as keyof typeof ageTypeMap] || 'TypeTest'
|
|
return {
|
|
type,
|
|
image: formatPathHost(
|
|
`/images/common/grades_age/${locale.value}/${type}.svg`,
|
|
{
|
|
imageType: 'common',
|
|
}
|
|
),
|
|
}
|
|
})
|
|
|
|
const getContentInfoImage = computed((): string[] => {
|
|
// 안전하게 content_info 값 확인
|
|
if (
|
|
!footerData.value?.game_rating_info?.content_info ||
|
|
typeof footerData.value.game_rating_info.content_info !== 'string'
|
|
) {
|
|
return []
|
|
}
|
|
|
|
const contentInfo = footerData.value.game_rating_info.content_info.split(',')
|
|
contentInfo.pop()
|
|
|
|
const contentTypeMap: Record<string, string> = {
|
|
'1': 'Type-sexual',
|
|
'2': 'Type-violence',
|
|
'3': 'Type-fear',
|
|
'4': 'Type-inapposite',
|
|
'5': 'Type-drug',
|
|
'6': 'Type-crime',
|
|
'7': 'Type-speculation',
|
|
}
|
|
|
|
return contentInfo
|
|
.map(item => {
|
|
const type = contentTypeMap[item]
|
|
return type
|
|
? formatPathHost(`/images/common/grades_use/${type}.svg`, {
|
|
imageType: 'common',
|
|
})
|
|
: ''
|
|
})
|
|
.filter(Boolean)
|
|
})
|
|
|
|
const showAgeRating = ref<boolean>(false)
|
|
const toggleAgeRating = (): void => {
|
|
showAgeRating.value = !showAgeRating.value
|
|
}
|
|
|
|
const footerAgeRatingInfo = computed((): string[] => {
|
|
const info = (tm as any)('Footer_AgeRating_Info')
|
|
return Array.isArray(info) ? info : []
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<footer id="footer" ref="footerRef" class="relative px-5 sm:px-10">
|
|
<div
|
|
class="relative max-w-[1300px] mx-auto pt-4 pb-10 text-[13px] text-white/50"
|
|
>
|
|
<ClientOnly>
|
|
<div class="py-4 border-b border-white/10">
|
|
<ul
|
|
class="flex items-center flex-wrap gap-x-6 gap-y-2 text-[15px] tracking-[-0.5px]"
|
|
>
|
|
<li v-for="(footerMenuItem, index) in footerLinks" :key="index">
|
|
<NuxtLink
|
|
:to="footerMenuItem.url"
|
|
:target="footerMenuItem.target"
|
|
:class="[
|
|
footerMenuItem.active === 'y' && 'text-white/50',
|
|
index === 2 && 'text-white',
|
|
]"
|
|
>
|
|
{{ footerMenuItem.title }}
|
|
</NuxtLink>
|
|
</li>
|
|
<li v-if="useGameRating" class="relative">
|
|
<button @click="toggleAgeRating">
|
|
<em v-dompurify-html="tm('Footer_AgeRating')"></em>
|
|
</button>
|
|
<div
|
|
v-if="showAgeRating"
|
|
class="game-rating-card absolute bottom-6 left-1 md:left-1/2 md:-translate-x-1/2 bg-[#383838] rounded-lg border border-white/30 w-[340px] mx-auto z-10"
|
|
>
|
|
<!-- 헤더 -->
|
|
<div
|
|
class="px-6 py-4 rounded-t-lg flex justify-between items-center"
|
|
>
|
|
<h3 class="text-white text-base">
|
|
{{ tm('Footer_AgeRating') }}
|
|
</h3>
|
|
<button
|
|
class="text-white hover:text-gray-300 transition-colors"
|
|
@click="toggleAgeRating"
|
|
>
|
|
<svg
|
|
class="w-5 h-5"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M6 18L18 6M6 6l12 12"
|
|
></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 등급 아이콘 그리드 -->
|
|
|
|
<div class="px-6 pt-2 pb-6">
|
|
<!-- 아이콘은 52x60 사이즈 갭은 4px 4개씩 그리드 레이아웃 -->
|
|
<div
|
|
class="grid grid-cols-4 gap-[4px] mb-4 max-w-[220px] justify-start items-start"
|
|
>
|
|
<!-- 연령 등급 -->
|
|
<div class="text-center">
|
|
<img
|
|
:src="getGameRatingImage.image"
|
|
alt="게임이용등급안내"
|
|
class="w-full h-full object-contain"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-for="image in getContentInfoImage"
|
|
:key="image"
|
|
class="text-center"
|
|
>
|
|
<img
|
|
:src="image"
|
|
alt="게임이용등급안내"
|
|
class="w-full h-full object-contain"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 정보 테이블 -->
|
|
<div
|
|
v-if="locale === 'zh-tw'"
|
|
class="px-6 py-6 rounded-b-lg bg-[#292929]"
|
|
>
|
|
<div class="space-y-2">
|
|
<div
|
|
v-dompurify-html="tm('Footer_AgeRating_Caution')"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-else
|
|
class="px-6 py-6 rounded-b-lg bg-[#A31639]"
|
|
:class="`bg-${getGameRatingImage.type}`"
|
|
>
|
|
<div class="space-y-2">
|
|
<div class="flex flex-start border-b border-white/10 pb-2">
|
|
<span class="text-white text-sm flex-1">
|
|
{{ footerAgeRatingInfo[0] }}
|
|
</span>
|
|
<span class="text-white text-sm flex-1">
|
|
{{ footerData.game_rating_info.title }}
|
|
</span>
|
|
</div>
|
|
<div class="flex flex-start border-b border-white/10 pb-2">
|
|
<span class="text-white text-sm flex-1">
|
|
{{ footerAgeRatingInfo[1] }}
|
|
</span>
|
|
<span class="text-white text-sm flex-1">
|
|
{{ footerData.game_rating_info.rating_grade }}
|
|
</span>
|
|
</div>
|
|
<div class="flex flex-start border-b border-white/10 pb-2">
|
|
<span class="text-white text-sm flex-1">
|
|
{{ footerAgeRatingInfo[2] }}
|
|
</span>
|
|
<span class="text-white text-sm flex-1">
|
|
{{ footerData.game_rating_info.reg_no }}
|
|
</span>
|
|
</div>
|
|
<div class="flex flex-start border-b border-white/10 pb-2">
|
|
<span class="text-white text-sm flex-1">
|
|
{{ footerAgeRatingInfo[3] }}
|
|
</span>
|
|
<span class="text-white text-sm flex-1">
|
|
{{ footerData.game_rating_info.prod_date }}
|
|
</span>
|
|
</div>
|
|
<div class="flex flex-start">
|
|
<span class="text-white text-sm flex-1">
|
|
{{ footerAgeRatingInfo[4] }}
|
|
</span>
|
|
<span class="text-white text-sm flex-1">
|
|
{{ footerData.game_rating_info.rating_class_no }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div
|
|
class="flex flex-col items-start mt-4 gap-4 sm:gap-5 md:gap-6 md:flex-row md:justify-between"
|
|
>
|
|
<address class="hidden not-italic leading-5 sm:block">
|
|
<div
|
|
v-dompurify-html="tm('Footer_Address')"
|
|
class="text-[13px] [&_a]:cursor-pointer [&_a]:text-white/50 [&_a]:underline"
|
|
/>
|
|
</address>
|
|
<BlocksLanguageSwitcher
|
|
:language-order="tm('Footer_Language_Order')"
|
|
/>
|
|
</div>
|
|
|
|
<div v-if="hasCautionText" class="mt-5 hidden sm:block">
|
|
<div
|
|
v-dompurify-html="tm('Footer_caution')"
|
|
class="text-xs text-white/30"
|
|
/>
|
|
</div>
|
|
|
|
<div class="mt-5">
|
|
<span v-dompurify-html="tm('Footer_Copyright')"></span>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-7 mt-6 md:mt-6">
|
|
<span>
|
|
<a
|
|
:href="tm('Footer_Smilegate_Link')"
|
|
target="_blank"
|
|
class="smilegate"
|
|
>
|
|
<img
|
|
:src="
|
|
formatPathHost(`/images/common/logo_smilegate.png`, {
|
|
imageType: 'common',
|
|
})
|
|
"
|
|
alt="스마일게이트 로고"
|
|
class="w-auto h-auto"
|
|
/>
|
|
</a>
|
|
</span>
|
|
<span v-if="setDevCi.dev_ci_yn">
|
|
<a
|
|
v-if="footerData.use_dev_ci_url"
|
|
:href="footerData.dev_ci_url"
|
|
target="_blank"
|
|
class="block nx3"
|
|
>
|
|
<img
|
|
:src="
|
|
formatPathHost(`${setDevCi.dev_ci_img_path}`, {
|
|
imageType: 'game',
|
|
})
|
|
"
|
|
alt="CI"
|
|
class="block w-auto h-[22px]"
|
|
/>
|
|
</a>
|
|
|
|
<img
|
|
v-else
|
|
:src="
|
|
formatPathHost(`${setDevCi.dev_ci_img_path}`, {
|
|
imageType: 'game',
|
|
})
|
|
"
|
|
alt="CI"
|
|
class="block w-auto h-[22px] leading-none"
|
|
/>
|
|
</span>
|
|
</div>
|
|
</ClientOnly>
|
|
</div>
|
|
</footer>
|
|
</template>
|
|
|
|
<style scoped>
|
|
em {
|
|
font-style: normal;
|
|
}
|
|
|
|
.bg-Type6 {
|
|
background-color: #3d89a9;
|
|
}
|
|
|
|
.bg-Type12 {
|
|
background-color: #4369b1;
|
|
}
|
|
|
|
.bg-Type15 {
|
|
background-color: #f9b846;
|
|
}
|
|
|
|
.bg-Type18 {
|
|
background-color: #d92e22;
|
|
}
|
|
|
|
.bg-Type19 {
|
|
background-color: #a31639;
|
|
}
|
|
|
|
.bg-TypeAll {
|
|
background-color: #44a342;
|
|
}
|
|
|
|
.bg-TypeExempt {
|
|
background-color: #292929;
|
|
}
|
|
|
|
.bg-TypeTest {
|
|
background-color: #292929;
|
|
}
|
|
</style>
|