fix. [SWV-866] 액션버튼 기능 개선 (이미지 타입 추가)

Made-with: Cursor
This commit is contained in:
clkim
2026-02-27 15:15:59 +09:00
parent 9208aae87f
commit fd83d3ae94
25 changed files with 308 additions and 143 deletions

View File

@@ -69,10 +69,9 @@
type="single"
platform="pc"
class="inspection-launcher"
:icon-component="AtomsIconsPlayRoundFill"
:icon-props="{ size: 16, color: '#332C2A' }"
>
{{ tm('Txt_Game_Start') }}
<AtomsIconsPlayRoundFill :size="16" color="#332C2A" />
</BlocksButtonLauncher>
</div>
</div>
@@ -358,6 +357,9 @@ definePageMeta({
.button-group:deep(.inspection-launcher.btn-base) {
@apply flex-1 bg-[var(--primary)] px-0 rounded md:rounded-lg;
}
.button-group:deep(.inspection-launcher) .text {
@apply flex items-center justify-center gap-[2px] mr-0 md:gap-[4px];
}
.button-group:deep(.inspection-launcher.btn-base .icon-platform) {
@apply hidden;
}

13
app/pages/teaser.vue Normal file
View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
definePageMeta({
layout: false, // 동적 레이아웃을 위해 기본 레이아웃 비활성화
middleware: ['inspection'],
})
// 진입 시 /home으로 리다이렉트
await navigateTo('/home', { replace: true })
</script>
<template>
<div />
</template>

View File

@@ -1,32 +1,45 @@
<script setup lang="ts">
interface props {
<<<<<<< HEAD
type?: 'button' | 'link'
to?: string
target?: '_self' | '_blank'
=======
type?: 'internal' | 'external' | 'action'
href?: string
>>>>>>> feature/20250228_SWV-866
backgroundColor?: string
srOnly?: string
}
const props = withDefaults(defineProps<props>(), {
<<<<<<< HEAD
type: 'button',
to: '',
target: '_self',
=======
type: 'action',
>>>>>>> feature/20250228_SWV-866
backgroundColor: '',
srOnly: '',
})
const componentTag = computed((): string => {
switch (props.type) {
case 'link':
case 'internal':
return 'AtomsLocaleLink'
case 'external':
return 'a'
default:
return 'button'
}
})
const componentProps = computed(() => {
switch (props.type) {
case 'link':
return { to: props.to, target: props.target }
case 'internal':
return { to: props.href, target: '_self' }
case 'external':
return { href: props.href, target: '_blank' }
default:
return {}
}

View File

@@ -0,0 +1,87 @@
<script setup lang="ts">
import type { ImageButtonType } from '#layers/types/components/button'
interface props {
type?: ImageButtonType
href?: string
backgroundImage: string
alt: string
disabled?: boolean
}
const props = withDefaults(defineProps<props>(), {
type: 'action',
disabled: false,
})
const componentTag = computed((): string => {
switch (props.type) {
case 'external':
return 'a'
case 'internal':
return 'AtomsLocaleLink'
default:
return 'button'
}
})
const componentProps = computed(() => {
if (props.type === 'external') {
return {
href: props.href,
target: '_blank',
}
}
if (props.type === 'internal') {
if (props.href) {
return {
to: props.href,
target: '_self',
}
}
return {}
}
return {}
})
const buttonStyle = computed(() => {
return {
backgroundImage: props.backgroundImage,
}
})
</script>
<template>
<component
:is="componentTag"
v-bind="{ ...componentProps }"
:class="['btn-base', { disabled: props.disabled }]"
:style="buttonStyle"
:disabled="props.disabled"
>
<img
v-if="props.backgroundImage"
:src="props.backgroundImage"
:alt="props.alt"
class="btn-bg"
/>
</component>
</template>
<style scoped>
.btn-base {
@apply overflow-hidden relative h-[48px] md:h-[64px] rounded-[8px] md:rounded-[10px] cursor-pointer
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0
hover:after:opacity-20;
}
.btn-base.disabled {
@apply cursor-default pointer-events-none
after:opacity-20 after:z-[2];
}
.btn-bg {
@apply w-full h-full object-contain;
}
</style>

View File

@@ -2,23 +2,23 @@
import type { TrackingObject } from '#layers/types/api/common'
interface Props {
category?: 'system' | 'image'
variant?: 'videoPlay' | 'videoPlayImg'
backgroundColor?: string
tracking: TrackingObject
}
const props = withDefaults(defineProps<Props>(), { category: 'system' })
const props = withDefaults(defineProps<Props>(), { variant: 'videoPlay' })
const { locale } = useI18n()
const { sendLog } = useAnalytics()
const buttonClasses = computed(() => [
'btn-play',
props.category === 'system' ? 'play-icon' : 'play-image',
props.variant === 'videoPlay' ? 'play-icon' : 'play-image',
])
const buttonStyle = computed(() =>
props.category === 'system' && props.backgroundColor
props.variant === 'videoPlay' && props.backgroundColor
? { backgroundColor: props.backgroundColor }
: {}
)
@@ -28,7 +28,7 @@ const onClick = () => sendLog(locale.value, props.tracking)
<template>
<button :class="buttonClasses" :style="buttonStyle" @click="onClick">
<span v-if="props.category === 'system'" class="icon">
<span v-if="props.variant === 'videoPlay'" class="icon">
<AtomsIconsArrowRightFill />
</span>
<span class="sr-only">Play</span>
@@ -37,12 +37,11 @@ const onClick = () => sendLog(locale.value, props.tracking)
<style scoped>
.btn-play {
@apply relative flex items-center justify-center;
@apply relative flex items-center justify-center rounded-full w-[60px] h-[60px] md:w-[80px] md:h-[80px];
}
.play-icon {
@apply w-[60px] h-[60px] md:w-[80px] md:h-[80px] rounded-full
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-[rgba(255,255,255,0.5)] before:rounded-full
@apply before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-[rgba(255,255,255,0.5)] before:rounded-full
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:rounded-[50%] after:opacity-0 after:transition-opacity after:duration-300 after:ease-in-out
hover:after:opacity-10;
}
@@ -54,16 +53,13 @@ const onClick = () => sendLog(locale.value, props.tracking)
}
.play-image {
@apply w-[69px] h-[69px] md:w-[110px] md:h-[110px];
@apply overflow-hidden;
}
.play-image::before {
@apply content-[''] absolute inset-0 z-0 bg-no-repeat bg-center bg-cover bg-[url(/images/common/btn_play/btn_default.png)] transition-opacity duration-300 ease-out;
@apply content-[''] absolute inset-0 z-0 rounded-full bg-no-repeat bg-center bg-cover bg-[url(/images/common/btn_play/btn_default_m.png)] md:bg-[url(/images/common/btn_play/btn_default.png)];
}
.play-image::after {
@apply content-[''] absolute inset-0 z-0 bg-no-repeat bg-center bg-cover bg-[url(/images/common/btn_play/btn_hover.png)] opacity-0 transition-opacity duration-300 ease-out;
}
.play-image:hover::before {
@apply opacity-0;
@apply content-[''] absolute inset-0 z-0 rounded-full bg-no-repeat bg-center bg-cover bg-[url(/images/common/btn_play/btn_hover_m.png)] md:bg-[url(/images/common/btn_play/btn_hover.png)] opacity-0 transition-opacity duration-300 ease-out;
}
.play-image:hover::after {
@apply opacity-100;

View File

@@ -5,12 +5,11 @@ interface props {
type?: ButtonType
size?: string
variant?: ButtonVariant
target?: '_self' | '_blank'
href?: string
backgroundColor?: string
textColor?: string
disabled?: boolean
gradient?: boolean
useGradient?: boolean
useGameFont?: boolean
}
@@ -21,7 +20,7 @@ const props = withDefaults(defineProps<props>(), {
target: '_self',
textColor: 'var(--alternative-02)',
disabled: false,
gradient: false,
useGradient: false,
useGameFont: false,
})
@@ -31,21 +30,20 @@ const { fontFamily } = storeToRefs(gameDataStore)
const componentTag = computed((): string => {
switch (props.type) {
case 'external':
case 'link':
return 'a'
case 'download':
return props.href ? 'a' : 'button'
case 'internal':
return props.href ? 'AtomsLocaleLink' : 'button'
return 'AtomsLocaleLink'
default:
return 'button'
}
})
const componentProps = computed(() => {
if (props.type === 'external' || props.type === 'link') {
if (props.type === 'external') {
return {
href: props.href,
target: props.target,
target: '_blank',
}
}
@@ -53,6 +51,7 @@ const componentProps = computed(() => {
if (props.href) {
return {
to: props.href,
target: '_self',
}
}
return {}
@@ -106,7 +105,8 @@ const textStyle = computed(() => {
:style="buttonStyle"
:disabled="props.disabled"
>
<i v-if="props.gradient" class="btn-gradient"></i>
<!-- 그라데이션 -->
<i v-if="props.useGradient" class="btn-gradient"></i>
<span class="btn-content" :style="textStyle">
<slot />
<AtomsIconsLongArrowRightLine

View File

@@ -14,8 +14,8 @@ const analytics = {
<template>
<AtomsButtonCircle
sr-only="home"
type="link"
to="/home"
type="internal"
href="/home"
class="btn-home"
background-color="rgb(0 0 0 / 0.2)"
@click="sendLog(locale, analytics)"

View File

@@ -1,29 +1,25 @@
<script setup lang="ts">
import type { CSSProperties, Component } from 'vue'
import type { CSSProperties } from 'vue'
import type { PlatformTransformType } from '#layers/types/api/gameData'
import type {
DownloadButtonType,
LauncherButtonType,
ButtonVariant,
Platform,
} from '#layers/types/components/button'
interface Props {
type?: DownloadButtonType
type?: LauncherButtonType
platform: Platform
variant?: ButtonVariant
backgroundColor?: string
textColor?: string
iconComponent?: Component
iconProps?: Record<string, any>
disabled?: boolean
useGameFont?: boolean
}
const props = withDefaults(defineProps<Props>(), {
type: 'default',
variant: 'filled',
disabled: false,
useGameFont: false,
})
const runtimeConfig = useRuntimeConfig()
@@ -33,7 +29,7 @@ const gameDataStore = useGameDataStore()
const modalStore = useModalStore()
const { isProcessing, validateLauncher } = useCheckGameStart()
const { gameName, platformType, osType, marketJson, fontFamily } =
const { gameName, platformType, osType, marketJson } =
storeToRefs(gameDataStore)
const PLATFORM_ICON_MAP: Record<Platform, string> = {
@@ -55,11 +51,6 @@ const componentTag = computed(() => {
}
return 'button'
})
const shouldShowPlatformIcon = computed(
() =>
(props.type === 'default' && props.variant !== 'custom') ||
props.type === 'single'
)
const shouldShowDownloadIcon = computed(
() =>
props.platform === 'pc' &&
@@ -91,9 +82,6 @@ const textStyle = computed<CSSProperties>(() => {
if (props.textColor) {
style.color = props.textColor
}
if (props.useGameFont && fontFamily.value) {
style.fontFamily = fontFamily.value
}
return style
})
@@ -158,24 +146,19 @@ const handleClick = () => {
:is="componentTag"
v-bind="$attrs"
:class="['btn-base', props.type, { 'no-text': !$slots.default }]"
:data-variant="props.variant"
:data-platform="props.platform"
:data-variant="props.variant"
:style="buttonStyle"
:disabled="disabled || isProcessing"
@click="handleClick"
>
<span class="btn-content">
<span v-if="shouldShowPlatformIcon" class="icon-platform">
<span v-if="props.type !== 'duplication'" class="icon-platform">
<component :is="platformIcon" />
</span>
<span class="text" :style="textStyle">
<slot />
</span>
<component
:is="props.iconComponent"
v-if="props.iconComponent"
v-bind="props.iconProps"
/>
<span v-if="shouldShowDownloadIcon" class="icon-download">
<AtomsIconsDownloadLine />
</span>
@@ -193,9 +176,6 @@ const handleClick = () => {
.icon-platform {
@apply w-5 h-5 mr-2 flex-shrink-0;
}
.icon-download {
@apply ml-auto pl-4;
}
.btn-base[data-variant='filled'] {
@apply bg-[#383838] text-[#ffffff]
@@ -208,6 +188,9 @@ const handleClick = () => {
@apply bg-white text-[#1F1F1F]
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-black/10 before:rounded-lg;
}
.btn-base[data-variant='outlined']:hover {
@apply before:border-[#999];
}
.btn-base[data-variant='outlined'][data-platform='app_store'] svg,
.btn-base[data-variant='outlined'][data-platform='pc'] svg,
.btn-base[data-variant='outlined'][data-platform='stove'] svg {
@@ -226,13 +209,15 @@ const handleClick = () => {
@apply line-clamp-2 text-[14px]
md:text-[16px];
}
.btn-base.default[data-variant='outlined'] .icon-download {
@apply border-black/10;
}
.btn-base.default[data-variant='outlined'] .icon-download svg {
@apply fill-[#1F1F1F];
}
.icon-download {
@apply ml-auto pl-4;
}
/* duplication */
.btn-base.duplication {

View File

@@ -2,7 +2,7 @@
import type { ColorObject, TrackingObject } from '#layers/types/api/common'
interface Props {
color: ColorObject
backgroundColor: ColorObject
}
const props = defineProps<Props>()
@@ -19,7 +19,7 @@ const analytics = {
const showBtn = computed(() => windowY.value > 0)
const backgroundColor = computed(
() => getColorCodeFromData(props.color, 'none') ?? 'var(--primary)'
() => getColorCodeFromData(props.backgroundColor, 'none') ?? 'var(--primary)'
)
const handleScrollToTop = () => {

View File

@@ -10,7 +10,7 @@ const props = defineProps<Props>()
const { locale } = useI18n()
const { sendLog } = useAnalytics()
const getArrowBgColor = (direction: 'prev' | 'next') => {
const getArrowBackgroundColor = (direction: 'prev' | 'next') => {
return (
getColorCodeFromData(
props.arrowsData?.[direction === 'prev' ? 0 : 1]?.display,
@@ -32,7 +32,7 @@ const handleArrowClick = (direction: 'prev' | 'next') => {
<AtomsButtonCircle
sr-only="Previous"
class="splide-arrow splide__arrow--prev"
:background-color="getArrowBgColor('prev')"
:background-color="getArrowBackgroundColor('prev')"
@click="handleArrowClick('prev')"
>
<AtomsIconsArrowRightLine color="#ffffff" />
@@ -40,7 +40,7 @@ const handleArrowClick = (direction: 'prev' | 'next') => {
<AtomsButtonCircle
sr-only="Next"
class="splide-arrow splide__arrow--next"
:background-color="getArrowBgColor('next')"
:background-color="getArrowBackgroundColor('next')"
@click="handleArrowClick('next')"
>
<AtomsIconsArrowRightLine color="#ffffff" />

View File

@@ -59,9 +59,8 @@ const handleCopy = async () => {
<template v-for="(item, key) in snsJson" :key="key">
<AtomsButtonCircle
v-if="item.use_yn === 1 && item.url"
type="link"
:to="item.url"
target="_blank"
type="external"
:href="item.url"
:class="['btn-sns', key]"
:sr-only="key"
@click="sendLog(locale, { ...analytics, click_item: key })"
@@ -79,6 +78,7 @@ const handleCopy = async () => {
</AtomsButtonCircle>
</template>
<AtomsButtonCircle
type="action"
class="btn-sns link"
sr-only="copy"
@click="handleCopy"

View File

@@ -586,6 +586,9 @@ onMounted(() => {
.btn-start:deep(.btn-base.default[data-variant='custom']) {
@apply w-full h-[48px] px-10 font-[700] text-[16px];
}
.btn-start:deep(.btn-base.default[data-variant='custom']) .icon-platform {
@apply hidden;
}
.btn-start .nav-2depth {
@apply left-[unset] right-[-40px];

View File

@@ -106,7 +106,7 @@ onMounted(() => {
<div v-if="isShowTopBtn" class="utile-wrap">
<BlocksButtonScrollTop
v-if="isShowTopBtn"
:color="pageData?.top_btn_color_json"
:background-color="pageData?.top_btn_color_json"
/>
</div>
</ClientOnly>

View File

@@ -5,6 +5,37 @@ import type {
} from '#layers/types/api/pageData'
import type { ButtonType } from '#layers/types/components/button'
/** 어드민 버튼 유형 (시스템 버튼 / 이미지 버튼) */
const BUTTON_CATEGORY = {
SYSTEM: 'SYSTEM', // 시스템 버튼
IMAGE: 'IMAGE', // 이미지 버튼
} as const
/** 어드민 버튼 타입 */
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
const MARKET_TYPE = {
PC: 'pc',
GOOGLE_PLAY: 'google_play',
APP_STORE: 'app_store',
} as const
const LINK_TARGET = {
BLANK: '_blank',
} as const
interface Props {
resourcesData: PageDataResourceGroup[]
buttonSize?: string
@@ -25,15 +56,35 @@ const buttonList = computed<PageDataResourceGroup[]>(
() => props.resourcesData ?? []
)
/** 버튼 유형이 '시스템 버튼'인지 확인 */
const isSystemButton = (button: PageDataResourceGroup): boolean => {
// [TODO] 어디민 개발 후 수정 필요
return button.btn_info?.btn_category === BUTTON_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 === 'URL' && target) {
return target === '_blank' ? 'external' : 'internal'
if (btnType === BUTTON_ACTION_TYPE.URL && target) {
return target === LINK_TARGET.BLANK ? 'external' : 'internal'
}
if (btnType === 'DOWNLOAD') return 'download'
if (btnType === BUTTON_ACTION_TYPE.DOWNLOAD) return 'download'
return 'action'
}
@@ -44,19 +95,37 @@ const isRunButtonVisible = (btnInfo: PageDataResourceGroupBtnInfo): boolean => {
const marketType = btnInfo?.detail?.market_type
switch (marketType) {
case 'pc':
case MARKET_TYPE.PC:
return false
case 'google_play':
case MARKET_TYPE.GOOGLE_PLAY:
return device.isAndroid
case 'app_store':
case MARKET_TYPE.APP_STORE:
return device.isApple
default:
return true
}
}
const downloadFile = async (url: string = '', osType: number = 0) => {
if (osType === 1 && breakpoints.value?.isMobile) {
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
}
@@ -64,25 +133,22 @@ const downloadFile = async (url: string = '', osType: number = 0) => {
const fileUrl = formatPathHost(url)
try {
const res = await $fetch<Blob>(fileUrl, {
const blob = await $fetch<Blob>(fileUrl, {
method: 'GET',
responseType: 'blob',
timeout: 10000,
})
const blob = res
const blobUrl = URL.createObjectURL(blob)
const fileName = fileUrl.split('/').pop() ?? 'download'
const pathPart = fileUrl.split('/').pop() ?? 'download'
const a = document.createElement('a')
const anchor = document.createElement('a')
anchor.href = blobUrl
anchor.download = fileName
document.body.appendChild(anchor)
anchor.click()
anchor.remove()
a.href = blobUrl
a.download = pathPart
document.body.appendChild(a)
a.click()
a.remove()
// 메모리 정리
URL.revokeObjectURL(blobUrl)
modalStore.handleOpenAlert({ contentText: tm('Alert_Download_Success') })
@@ -92,30 +158,24 @@ const downloadFile = async (url: string = '', osType: number = 0) => {
}
}
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
switch (btnDetail?.btn_type) {
case 'POP':
modalStore.handleOpenContent({
contentTitle: btnDetail?.title,
tabInfo: btnDetail?.tab_info,
})
return
case 'ANCHOR':
scrollStore.scrollToAnchor(btnDetail?.page_ver_tmpl_name_en ?? '')
return
case 'MOV':
modalStore.handleOpenYoutube({ youtubeUrl: btnDetail.url ?? '' })
return
case 'DOWNLOAD':
downloadFile(btnDetail?.file_path, btnDetail?.os_type)
return
default:
return
}
const handler = buttonClickHandlers[btnType]
handler?.(btnDetail)
}
</script>
@@ -126,35 +186,46 @@ const handleButtonClick = (button: PageDataResourceGroup) => {
class="flex flex-wrap justify-center items-center gap-3 md:gap-4"
>
<template v-for="(button, index) in buttonList" :key="index">
<template v-if="button.btn_info?.detail?.btn_type === 'RUN'">
<BlocksButtonLauncher
v-if="isRunButtonVisible(button.btn_info)"
type="duplication"
:platform="button.btn_info?.detail?.market_type"
:background-color="getColorCodeFromData(button.btn_info, 'btn')"
:text-color="getColorCodeFromData(button.btn_info, 'txt')"
:use-game-font="button.btn_info?.use_game_font === 1"
@click="handleButtonClick(button)"
>
{{ button.btn_info?.txt_btn_name }}
</BlocksButtonLauncher>
</template>
<!-- 버튼 유형: 시스템 버튼 -->
<AtomsButton
v-else
v-if="isSystemButton(button)"
:type="getButtonType(button.btn_info)"
:size="buttonSize"
:href="button.btn_info?.detail?.action?.url"
:target="button.btn_info?.detail?.action?.link_target"
:background-color="getColorCodeFromData(button.btn_info, 'btn')"
:text-color="getColorCodeFromData(button.btn_info, 'txt')"
:disabled="button.btn_info?.detail?.btn_type === 'DEACTIVE'"
:gradient="true"
:use-game-font="button.btn_info?.use_game_font === 1"
: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"
:background-color="getColorCodeFromData(button.btn_info, 'btn')"
:text-color="getColorCodeFromData(button.btn_info, 'txt')"
@click="handleButtonClick(button)"
>
{{ button.btn_info?.txt_btn_name }}
</BlocksButtonLauncher>
<!-- 버튼 유형: 이미지 버튼 + 타입: 기타 -->
<!-- [TODO] api 개발 후 수정 필요 (background-image, alt 에 연결 필요) -->
<AtomsButtonImage
v-else-if="!isSystemButton(button) && !isLauncherButton(button)"
:href="button.btn_info?.detail?.action?.url"
:background-image="'/images/test.png'"
alt="test"
:disabled="isDisabled(button)"
@click="handleButtonClick(button)"
/>
</template>
</div>
</template>

View File

@@ -2,7 +2,7 @@
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
interface Props {
category?: 'system' | 'image'
variant?: 'videoPlay' | 'videoPlayImg'
resourcesData: PageDataResourceGroup
}
@@ -26,7 +26,7 @@ const handleVideoPlayClick = () => {
<template>
<AtomsButtonPlay
v-motion-stagger
:category="props.category"
:variant="props.variant"
:background-color="backgroundColor"
:tracking="props.resourcesData.tracking"
@click="handleVideoPlayClick"

View File

@@ -444,12 +444,11 @@ const handleMoveFocus = (target: 'pc' | 'mobile') => {
</p>
<AtomsButton
type="link"
type="external"
size="size-small"
background-color="#383838"
text-color="#FFFFFF"
class="w-full px-0"
target="_blank"
class="btn-download"
rel="noopener noreferrer"
:href="tm(`Download_Driver_${driver.driverCode}_Url`)"
@click="handleSendLog(`다운로드_${driver.driverText}`)"
@@ -527,12 +526,10 @@ table td {
md:h-[80px] md:py-[14px] md:px-[20px] md:text-[16px] md:leading-[26px] md:tracking-[-0.48px];
}
/* 플랫폼별 다운로드 Mobile Overflow Visible 처리 */
.splide :deep(.splide__track) {
overflow: visible !important;
.btn-download {
@apply w-full px-0;
}
::v-deep([data-platform='stove']) .icon-platform {
display: none !important;
.btn-download:deep(.icon-external) {
@apply hidden;
}
</style>

View File

@@ -20,7 +20,7 @@ const descriptionData = computed(() =>
getComponentGroup(props.components, 'description')
)
const videoPlayData = computed(() =>
getComponentGroup(props.components, 'videoPlay')
getComponentGroup(props.components, 'videoPlayImg')
)
// [TODO] api 수정 후 사용
// const videoPlayData = computed(() =>
@@ -51,7 +51,7 @@ const buttonListData = computed(() =>
/>
<WidgetsVideoPlay
v-if="videoPlayData"
category="image"
variant="videoPlayImg"
:resources-data="videoPlayData"
/>
<WidgetsButtonList

View File

@@ -105,7 +105,7 @@ const slideItemSize = {
/>
<WidgetsVideoPlay
v-if="videoPlayData"
category="image"
variant="videoPlayImg"
:resources-data="videoPlayData"
/>
<WidgetsSlideCenterHighlight

View File

@@ -86,6 +86,7 @@ export interface PageDataResourceGroupResPath {
}
export interface PageDataResourceGroupBtnInfo extends ColorObject {
btn_category: string
txt_btn_name: string
detail: Record<string, any>
disabled?: boolean

View File

@@ -1,11 +1,8 @@
export type ButtonType =
| 'internal'
| 'external'
| 'download'
| 'action'
| 'link'
export type ButtonType = 'internal' | 'external' | 'download' | 'action'
export type DownloadButtonType = 'default' | 'single' | 'duplication'
export type ImageButtonType = 'internal' | 'external' | 'action'
export type LauncherButtonType = 'default' | 'duplication' | 'single'
export type ButtonSize = 'large' | 'medium' | 'small' | 'extra-small'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
public/images/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB