Files
web-temp/layers/components/widgets/ButtonList.vue

212 lines
5.9 KiB
Vue

<script setup lang="ts">
import type {
PageDataResourceGroup,
PageDataResourceGroupBtnInfo,
} from '#layers/types/api/pageData'
import type { ButtonType } from '#layers/types/components/button'
/** 어드민 버튼 타입 */
const BUTTON_ACTION_TYPE = {
URL: 'URL',
RUN: 'RUN',
POP: 'POP',
DOWNLOAD: 'DOWNLOAD',
ANCHOR: 'ANCHOR',
MOV: 'MOV',
DEACTIVE: 'DEACTIVE',
} as const
const OS_TYPE = {
PC: 1,
} as const
interface Props {
resourcesData: PageDataResourceGroup[]
buttonSize?: string
}
const props = withDefaults(defineProps<Props>(), {
buttonSize: 'size-extra-small md:size-medium',
})
const { locale, tm } = useI18n()
const device = useDevice()
const modalStore = useModalStore()
const scrollStore = useScrollStore()
const breakpoints = useResponsiveBreakpoints()
const { sendLog } = useAnalytics()
const buttonList = computed<PageDataResourceGroup[]>(
() => props.resourcesData ?? []
)
/** 버튼 유형이 '시스템 버튼'인지 확인 */
const isSystemButton = (button: PageDataResourceGroup): boolean => {
return button.btn_info?.btn_category === 'system'
}
/** 버튼 타입이 '게임 실행'인지 확인 */
const isLauncherButton = (button: PageDataResourceGroup): boolean => {
return button.btn_info?.detail?.btn_type === BUTTON_ACTION_TYPE.RUN
}
/** 버튼 타입이 '비활성화'인지 확인 */
const isDisabled = (button: PageDataResourceGroup): boolean => {
return button.btn_info?.detail?.btn_type === BUTTON_ACTION_TYPE.DEACTIVE
}
const usesGameFont = (btnInfo?: PageDataResourceGroupBtnInfo): boolean => {
return btnInfo?.use_game_font === 1
}
const getButtonType = (btnInfo?: PageDataResourceGroupBtnInfo): ButtonType => {
const btnType = btnInfo?.detail?.btn_type
const target = btnInfo?.detail?.action?.link_target
if (btnType === BUTTON_ACTION_TYPE.URL && target) {
return target === '_blank' ? 'external' : 'internal'
}
if (btnType === BUTTON_ACTION_TYPE.DOWNLOAD) return 'download'
return 'action'
}
const isRunButtonVisible = (btnInfo: PageDataResourceGroupBtnInfo): boolean => {
if (breakpoints.value?.isDesktop) return true
const marketType = btnInfo?.detail?.market_type
switch (marketType) {
case 'pc':
return false
case 'google_play':
return device.isAndroid
case 'app_store':
return device.isApple
default:
return true
}
}
const openPopupModal = (detail: Record<string, any>) => {
modalStore.handleOpenContent({
contentTitle: detail?.title,
tabInfo: detail?.tab_info,
})
}
const scrollToAnchor = (detail: Record<string, any>) => {
scrollStore.scrollToAnchor(detail?.page_ver_tmpl_name_en ?? '')
}
const openYoutubeModal = (detail: Record<string, any>) => {
modalStore.handleOpenYoutube({ youtubeUrl: detail?.url ?? '' })
}
const downloadFile = async (detail: Record<string, any>) => {
const url = detail?.file_path ?? ''
const osType = detail?.os_type ?? 0
if (osType === OS_TYPE.PC && breakpoints.value?.isMobile) {
modalStore.handleOpenAlert({ contentText: tm('Alert_Download_PC') })
return
}
const fileUrl = formatPathHost(url)
try {
const blob = await $fetch<Blob>(fileUrl, {
method: 'GET',
responseType: 'blob',
timeout: 10000,
})
const blobUrl = URL.createObjectURL(blob)
const fileName = fileUrl.split('/').pop() ?? 'download'
const anchor = document.createElement('a')
anchor.href = blobUrl
anchor.download = fileName
document.body.appendChild(anchor)
anchor.click()
anchor.remove()
URL.revokeObjectURL(blobUrl)
modalStore.handleOpenAlert({ contentText: tm('Alert_Download_Success') })
} catch (err) {
console.error(err)
modalStore.handleOpenAlert({ contentText: tm('Alert_Download_Error') })
}
}
const buttonClickHandlers: Record<
string,
(detail: Record<string, any>) => void
> = {
[BUTTON_ACTION_TYPE.POP]: openPopupModal,
[BUTTON_ACTION_TYPE.ANCHOR]: scrollToAnchor,
[BUTTON_ACTION_TYPE.MOV]: openYoutubeModal,
[BUTTON_ACTION_TYPE.DOWNLOAD]: downloadFile,
}
const handleButtonClick = (button: PageDataResourceGroup) => {
sendLog(locale.value, button.tracking)
const btnDetail = button.btn_info?.detail
const btnType = btnDetail?.btn_type
const handler = buttonClickHandlers[btnType]
handler?.(btnDetail)
}
</script>
<template>
<div
v-if="buttonList.length"
v-motion-stagger
class="flex flex-wrap justify-center items-center gap-3 md:gap-4"
>
<template v-for="(button, index) in buttonList" :key="index">
<!-- 버튼 유형: 시스템 버튼 -->
<AtomsButton
v-if="isSystemButton(button)"
:type="getButtonType(button.btn_info)"
:size="buttonSize"
:href="button.btn_info?.detail?.action?.url"
:background-color="getColorCodeFromData(button.btn_info, 'btn')"
:text-color="getColorCodeFromData(button.btn_info, 'txt')"
:disabled="isDisabled(button)"
:use-gradient="true"
:use-game-font="usesGameFont(button.btn_info)"
@click="handleButtonClick(button)"
>
{{ button.btn_info?.txt_btn_name }}
</AtomsButton>
<!-- 버튼 유형: 이미지 버튼 + 타입: 게임 실행 -->
<BlocksButtonLauncher
v-else-if="
isLauncherButton(button) && isRunButtonVisible(button.btn_info!)
"
type="duplication"
:platform="button.btn_info?.detail?.market_type"
@click="handleButtonClick(button)"
>
{{ button.btn_info?.txt_btn_name }}
</BlocksButtonLauncher>
<!-- 버튼 유형: 이미지 버튼 + 타입: 기타 -->
<AtomsButtonImage
v-else-if="!isLauncherButton(button)"
:href="button.btn_info?.detail?.action?.url"
:background-image="formatPathHost(button.btn_info?.res_path)"
:alt="button.btn_info?.txt_btn_name"
:disabled="isDisabled(button)"
@click="handleButtonClick(button)"
/>
</template>
</div>
</template>