Merge branch 'feature/202501107-all' into feature/20251001-gil
This commit is contained in:
@@ -19,4 +19,14 @@
|
||||
@apply px-6 h-10 text-sm rounded
|
||||
before:rounded after:rounded;
|
||||
}
|
||||
|
||||
/* Icon Size Classes */
|
||||
.size-large .icon,
|
||||
.size-medium .icon {
|
||||
@apply w-5 h-5;
|
||||
}
|
||||
.size-small .icon,
|
||||
.size-extra-small .icon {
|
||||
@apply w-4 h-4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ const props = withDefaults(defineProps<props>(), {
|
||||
|
||||
const componentTag = computed((): string => {
|
||||
switch (props.type) {
|
||||
case 'link':
|
||||
case 'download':
|
||||
case 'external':
|
||||
return 'a'
|
||||
@@ -66,13 +67,16 @@ const componentProps = computed(() => {
|
||||
>
|
||||
<span class="btn-content">
|
||||
<slot />
|
||||
<AtomsIconsLongArrowRightLine v-if="props.type === 'internal'" />
|
||||
<AtomsIconsLongArrowRightLine
|
||||
v-if="props.type === 'internal'"
|
||||
class="icon"
|
||||
/>
|
||||
<AtomsIconsWebLinkLine
|
||||
v-if="props.type === 'external'"
|
||||
size="24"
|
||||
color="#ebebeb"
|
||||
class="icon"
|
||||
/>
|
||||
<AtomsIconsDownloadLine v-if="props.type === 'download'" />
|
||||
<AtomsIconsDownloadLine v-if="props.type === 'download'" class="icon" />
|
||||
</span>
|
||||
</component>
|
||||
</template>
|
||||
@@ -94,4 +98,7 @@ const componentProps = computed(() => {
|
||||
.btn-base .btn-content {
|
||||
@apply relative flex items-center gap-1 z-[1];
|
||||
}
|
||||
.btn-base.size-extra-small .btn-content {
|
||||
@apply gap-0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@ interface Props {
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 24,
|
||||
size: 20,
|
||||
color: '#EBEBEB',
|
||||
})
|
||||
</script>
|
||||
@@ -15,16 +15,14 @@ withDefaults(defineProps<Props>(), {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
:fill="color"
|
||||
>
|
||||
<path
|
||||
d="M10.75 3.25L10.75 12.2322L6.88391 8.36611C6.39576 7.87796 5.6043 7.87796 5.11615 8.36611C4.62799 8.85427 4.62799 9.64573 5.11615 10.1339L11.1161 16.1339C11.3506 16.3683 11.6685 16.5 12 16.5C12.3316 16.5 12.6495 16.3683 12.8839 16.1339L18.8839 10.1339C19.3721 9.64573 19.3721 8.85427 18.8839 8.36611C18.3958 7.87796 17.6043 7.87796 17.1161 8.36611L13.25 12.2322L13.25 3.25C13.25 2.55964 12.6904 2 12 2C11.3097 2 10.75 2.55964 10.75 3.25Z"
|
||||
:fill="color"
|
||||
d="M8.9585 2.70829L8.9585 10.1935L5.73673 6.97172C5.32993 6.56493 4.67039 6.56493 4.26359 6.97172C3.85679 7.37852 3.85679 8.03807 4.26359 8.44486L9.26359 13.4449C9.45894 13.6402 9.72389 13.75 10.0002 13.75C10.2764 13.75 10.5414 13.6402 10.7367 13.4449L15.7367 8.44486C16.1435 8.03806 16.1435 7.37852 15.7367 6.97172C15.3299 6.56493 14.6704 6.56493 14.2636 6.97172L11.0418 10.1935L11.0418 2.70829C11.0418 2.133 10.5755 1.66663 10.0002 1.66663C9.42487 1.66663 8.9585 2.133 8.9585 2.70829Z"
|
||||
/>
|
||||
<path
|
||||
d="M20 21C20.6904 21 21.25 20.4404 21.25 19.75L21.25 17.75C21.25 17.0596 20.6904 16.5 20 16.5C19.3097 16.5 18.75 17.0596 18.75 17.75L18.75 18.5L5.25003 18.5L5.25003 17.75C5.25003 17.0596 4.69039 16.5 4.00003 16.5C3.30967 16.5 2.75003 17.0596 2.75003 17.75L2.75003 19.75C2.75003 20.4404 3.30967 21 4.00003 21L20 21Z"
|
||||
:fill="color"
|
||||
d="M16.6668 17.5C17.2421 17.5 17.7085 17.0336 17.7085 16.4583L17.7085 14.7916C17.7085 14.2163 17.2421 13.75 16.6668 13.75C16.0915 13.75 15.6252 14.2163 15.6252 14.7916L15.6252 15.4166L4.37516 15.4166L4.37516 14.7916C4.37516 14.2163 3.90879 13.75 3.33349 13.75C2.7582 13.75 2.29183 14.2163 2.29183 14.7916L2.29183 16.4583C2.29183 17.0336 2.7582 17.5 3.33349 17.5L16.6668 17.5Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
28
layers/components/atoms/icons/LogoApple.vue
Normal file
28
layers/components/atoms/icons/LogoApple.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 20,
|
||||
color: '#ffffff',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 36 36"
|
||||
:fill="color"
|
||||
>
|
||||
<path
|
||||
d="M22.0265 6.77515C22.9737 5.57867 23.6003 3.90666 23.4254 2.25C22.0702 2.31136 20.4381 3.20105 19.4618 4.39753C18.5874 5.45595 17.8297 7.15864 18.0337 8.78462C19.5346 8.90734 21.0793 7.97163 22.0265 6.77515Z"
|
||||
/>
|
||||
<path
|
||||
d="M24.1784 9.38534L23.8464 9.35745C22.5604 9.25719 21.3837 9.62957 20.3897 10.0069L19.3814 10.3981C18.917 10.5722 18.5149 10.6972 18.1869 10.6972C17.8232 10.6972 17.3973 10.5679 16.9256 10.3916L15.7142 9.92036C14.9662 9.64193 14.1546 9.40031 13.3246 9.42125C10.8217 9.45315 8.526 10.8727 7.23469 13.1216C4.63612 17.6354 6.56512 24.3024 9.09991 27.9708L9.53965 28.5946L9.84388 29.0098L10.1578 29.4174C11.1678 30.689 12.3337 31.7497 13.755 31.703C14.523 31.6702 15.0938 31.4589 15.6575 31.2216L16.1434 31.0155C16.8008 30.7416 17.5202 30.4909 18.5855 30.4909C19.511 30.4909 20.1636 30.6964 20.7491 30.9348L21.4811 31.246C22.0469 31.481 22.6338 31.6749 23.4478 31.6552C25.1351 31.6284 26.305 30.3637 27.3637 28.8914L27.6635 28.466L28.1083 27.8128C28.254 27.5914 28.3903 27.3714 28.5175 27.1552L28.76 26.7285C28.7984 26.6584 28.8359 26.5889 28.8723 26.5201L29.0796 26.1159C29.1123 26.0501 29.144 25.9851 29.1747 25.921L29.3482 25.5483L29.4998 25.2015L29.6302 24.8848L29.7873 24.4749L29.9 24.155L30 23.8398L29.8843 23.7918L29.5934 23.6516L29.2859 23.4809L29.0483 23.3338L28.7907 23.1589C27.6392 22.3393 26.0767 20.7067 26.0464 17.8746C26.0241 15.2734 27.512 13.6173 28.4644 12.8256L28.6993 12.6386C28.736 12.6106 28.7714 12.5843 28.8052 12.5597L29.0641 12.3809L29.2348 12.2763C28.0657 10.5643 26.4786 9.86607 25.2717 9.57188L24.9218 9.49514L24.5996 9.43898L24.3097 9.39975C24.2644 9.39442 24.2206 9.38964 24.1784 9.38534Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
44
layers/components/atoms/icons/LogoGoogle.vue
Normal file
44
layers/components/atoms/icons/LogoGoogle.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 20,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 33 33"
|
||||
fill="none"
|
||||
>
|
||||
<g clip-path="url(#clip0_6093_172)">
|
||||
<path
|
||||
d="M16.2529 15.8401L3.54785 29.1226C3.96035 30.5251 5.28035 31.5976 6.84785 31.5976C7.50785 31.5976 8.08535 31.4326 8.58035 31.1026L22.9354 22.9351L16.2529 15.8401Z"
|
||||
fill="#EA4335"
|
||||
/>
|
||||
<path
|
||||
d="M29.1224 13.53L22.9349 9.98254L16.0049 16.0875L23.0174 22.935L29.2049 19.47C30.2774 18.8925 31.0199 17.7375 31.0199 16.5C30.9374 15.2625 30.1949 14.1075 29.1224 13.53Z"
|
||||
fill="#FBBC04"
|
||||
/>
|
||||
<path
|
||||
d="M3.54734 3.87769C3.46484 4.12519 3.46484 4.45519 3.46484 4.78519V28.2977C3.46484 28.6277 3.46484 28.8752 3.54734 29.2052L16.7473 16.2527L3.54734 3.87769Z"
|
||||
fill="#4285F4"
|
||||
/>
|
||||
<path
|
||||
d="M16.3354 16.5001L22.9354 9.98259L8.66285 1.89759C8.16785 1.56759 7.50785 1.40259 6.84785 1.40259C5.28035 1.40259 3.87785 2.47509 3.54785 3.87759L16.3354 16.5001Z"
|
||||
fill="#34A853"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_6093_172">
|
||||
<rect width="33" height="33" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
27
layers/components/atoms/icons/LogoStove.vue
Normal file
27
layers/components/atoms/icons/LogoStove.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 20,
|
||||
color: '#ffffff',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 17 17"
|
||||
:fill="color"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M16.6667 8.33333C16.6667 12.9357 12.9357 16.6667 8.33333 16.6667C3.73096 16.6667 0 12.9357 0 8.33333C0 3.73096 3.73096 0 8.33333 0C12.9357 0 16.6667 3.73096 16.6667 8.33333ZM7.55393 6.8783L7.55605 6.88009C7.76948 7.06425 8.19603 7.17448 8.69379 7.30311C9.90868 7.61706 11.5478 8.04063 11.5478 9.91641C11.5478 11.6738 10.068 12.5 8.23015 12.5C6.95157 12.5 5.1192 11.9879 5.1192 10.8707C5.1192 10.3587 5.44311 9.91705 5.96823 9.91705C6.16268 9.91705 6.3884 10.01 6.66494 10.1239C7.10217 10.3041 7.66644 10.5365 8.43495 10.5365C9.1248 10.5365 9.45091 10.3844 9.45091 10.0677C9.45091 9.91641 9.39384 9.8302 9.30252 9.73706C9.07688 9.51765 8.61746 9.3754 8.0873 9.21125C6.87409 8.8356 5.29047 8.34525 5.29047 6.55315C5.29047 5.07512 6.56347 4.16667 8.37854 4.16667C9.00636 4.16667 10.2852 4.30266 10.7875 4.81477C10.9816 5.0126 11.0772 5.25698 11.0772 5.57122C11.0772 6.06002 10.7504 6.42442 10.2252 6.42442C10.0821 6.42442 9.89365 6.37285 9.66454 6.31013C9.30915 6.21285 8.85583 6.08877 8.32147 6.08877C7.77352 6.08877 7.36154 6.1735 7.36154 6.52575C7.36154 6.59753 7.38041 6.6668 7.43516 6.75034C7.46374 6.794 7.50331 6.83711 7.55393 6.8783Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
26
layers/components/atoms/icons/LogoWindow.vue
Normal file
26
layers/components/atoms/icons/LogoWindow.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 20,
|
||||
color: '#ffffff',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 20 20"
|
||||
:fill="color"
|
||||
>
|
||||
<path d="M9.375 3.6375L17.5 2.5V9.6875H9.375V3.6375Z" />
|
||||
<path d="M8.75 3.725L2.5 4.6V9.6875H8.75V3.725Z" />
|
||||
<path d="M8.75 10.3125H2.5V15.4L8.75 16.275V10.3125Z" />
|
||||
<path d="M9.375 16.3625L17.5 17.5V10.3125H9.375V16.3625Z" />
|
||||
</svg>
|
||||
</template>
|
||||
217
layers/components/blocks/Button/Download.vue
Normal file
217
layers/components/blocks/Button/Download.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<script setup lang="ts">
|
||||
import type { CSSProperties } from 'vue'
|
||||
import type {
|
||||
DownloadButtonType,
|
||||
ButtonVariant,
|
||||
} from '#layers/types/components/button'
|
||||
|
||||
type Platform = 'google_play' | 'app_store' | 'pc' | 'stove'
|
||||
|
||||
interface Props {
|
||||
platform: Platform
|
||||
type?: DownloadButtonType
|
||||
variant?: ButtonVariant
|
||||
backgroundColor?: string
|
||||
textColor?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: 'default',
|
||||
variant: 'filled',
|
||||
disabled: false,
|
||||
})
|
||||
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const { gameData } = useGameDataStore()
|
||||
const { isProcessing, validateLauncher } = useCheckGameStart()
|
||||
|
||||
const PLATFORM_ICON_MAP: Record<Platform, string> = {
|
||||
google_play: 'AtomsIconsLogoGoogle',
|
||||
app_store: 'AtomsIconsLogoApple',
|
||||
pc: 'AtomsIconsLogoWindow',
|
||||
stove: 'AtomsIconsLogoStove',
|
||||
} as const
|
||||
|
||||
const DUP_IMAGE_MAP: Record<Platform, string> = {
|
||||
google_play: '/images/common/btn_logo-google.svg',
|
||||
app_store: '/images/common/btn_logo-app.svg',
|
||||
pc: '/images/common/btn_logo-pc.svg',
|
||||
stove: '/images/common/btn_system_normal_stove_pc.svg',
|
||||
} as const
|
||||
|
||||
const componentTag = computed(() => {
|
||||
if (props.platform === 'stove') {
|
||||
return 'a'
|
||||
}
|
||||
return 'button'
|
||||
})
|
||||
const isDuplication = computed(() => props.type === 'duplication')
|
||||
const isSingle = computed(() => props.type === 'single')
|
||||
const isCustom = computed(() => props.type === 'custom')
|
||||
const platformIcon = computed(() => PLATFORM_ICON_MAP[props.platform])
|
||||
const duplicationImage = computed(() =>
|
||||
isDuplication.value ? DUP_IMAGE_MAP[props.platform] : ''
|
||||
)
|
||||
|
||||
const inlineStyle = computed<CSSProperties>(() => {
|
||||
const style: CSSProperties = {}
|
||||
|
||||
if (props.backgroundColor) {
|
||||
style.backgroundColor = props.backgroundColor
|
||||
}
|
||||
if (props.textColor) {
|
||||
style.color = props.textColor
|
||||
}
|
||||
if (props.type === 'duplication') {
|
||||
style.backgroundImage = `url(${duplicationImage.value})`
|
||||
}
|
||||
return style
|
||||
})
|
||||
|
||||
const handleClick = () => {
|
||||
if (props.platform === 'pc') {
|
||||
validateLauncher()
|
||||
return
|
||||
}
|
||||
if (props.platform === 'stove') {
|
||||
const stoveClientDownloadUrl = runtimeConfig.public.stoveClientDownloadUrl
|
||||
location.href = stoveClientDownloadUrl
|
||||
return
|
||||
}
|
||||
|
||||
const url = gameData?.market_json[props.platform]?.url || ''
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
:is="componentTag"
|
||||
v-bind="$attrs"
|
||||
:class="[
|
||||
'btn-base',
|
||||
props.type,
|
||||
{ 'no-text': isSingle && !$slots.default },
|
||||
]"
|
||||
:data-variant="props.variant"
|
||||
:data-platform="props.platform"
|
||||
:style="inlineStyle"
|
||||
:disabled="disabled || isProcessing"
|
||||
@click="handleClick"
|
||||
>
|
||||
<span class="btn-content">
|
||||
<component
|
||||
:is="platformIcon"
|
||||
v-if="!isDuplication && !isCustom"
|
||||
class="icon-platform"
|
||||
/>
|
||||
<span class="text">
|
||||
<slot />
|
||||
</span>
|
||||
<span v-if="type === 'default'" class="icon-download">
|
||||
<AtomsIconsDownloadLine />
|
||||
</span>
|
||||
</span>
|
||||
</component>
|
||||
|
||||
<ClientOnly>
|
||||
<Teleport to="#teleports">
|
||||
<BlocksModalClient />
|
||||
</Teleport>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.btn-base {
|
||||
@apply overflow-hidden inline-block relative font-medium cursor-pointer
|
||||
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-white/10
|
||||
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;
|
||||
}
|
||||
.btn-base:hover {
|
||||
@apply after:opacity-20;
|
||||
}
|
||||
.btn-content {
|
||||
@apply relative flex items-center z-[1] text-left;
|
||||
}
|
||||
.icon-platform {
|
||||
@apply w-5 h-5 flex-shrink-0;
|
||||
}
|
||||
|
||||
.btn-base[data-variant='filled'] {
|
||||
@apply bg-[#383838] text-[#ffffff];
|
||||
}
|
||||
.btn-base[data-variant='outlined'] {
|
||||
@apply bg-white text-[#1F1F1F]
|
||||
before:border-black/10
|
||||
after:hidden;
|
||||
}
|
||||
.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 {
|
||||
@apply fill-[#1F1F1F];
|
||||
}
|
||||
|
||||
/* default */
|
||||
.btn-base.default {
|
||||
@apply w-[296px] py-3.5 px-5 rounded-[4px]
|
||||
md:w-[356px] md:py-4 md:px-6
|
||||
before:rounded-[4px] after:rounded-[4px];
|
||||
}
|
||||
.btn-base.default .text {
|
||||
@apply pl-2 pr-4 line-clamp-2 text-[14px]
|
||||
md:text-[16px];
|
||||
}
|
||||
.btn-base.default .icon-download {
|
||||
@apply border-l border-white/10 ml-auto pl-4;
|
||||
}
|
||||
.btn-base.default[data-platform='stove'] {
|
||||
@apply bg-[#FC4420];
|
||||
}
|
||||
.btn-base.default[data-variant='outlined'] .icon-download {
|
||||
@apply border-black/10;
|
||||
}
|
||||
.btn-base.default[data-variant='outlined'] .icon-download svg {
|
||||
@apply fill-[#1F1F1F];
|
||||
}
|
||||
|
||||
/* duplication */
|
||||
.btn-base.duplication {
|
||||
@apply items-start bg-[16px_50%] bg-[length:auto_28px] bg-no-repeat rounded-[4px]
|
||||
before:rounded-[4px] after:rounded-[4px]
|
||||
pt-[22px] pl-[47px] pr-[22px] pb-[7px] text-[11px]
|
||||
md:h-[64px] md:pt-[30px] md:pl-[64px] md:pr-[28px] md:pb-[11px] md:text-[12px] md:bg-[20px_50%] md:bg-[length:auto_40px];
|
||||
}
|
||||
.btn-base.duplication[data-platform='google_play'] {
|
||||
@apply min-w-[148px] md:min-w-[194px];
|
||||
}
|
||||
.btn-base.duplication[data-platform='app_store'] {
|
||||
@apply min-w-[132px] md:min-w-[174px];
|
||||
}
|
||||
.btn-base.duplication[data-platform='pc'] {
|
||||
@apply min-w-[113px] md:min-w-[150px];
|
||||
}
|
||||
.btn-base.duplication[data-platform='stove'] {
|
||||
@apply min-w-[113px] md:min-w-[150px];
|
||||
}
|
||||
.btn-base.duplication .text {
|
||||
@apply line-clamp-1;
|
||||
}
|
||||
|
||||
/* single */
|
||||
.btn-base.single {
|
||||
@apply justify-center items-center text-[14px]
|
||||
h-[40px] px-3.5 rounded-[4px]
|
||||
md:h-[48px] md:rounded-[8px]
|
||||
before:rounded-[4px] md:before:rounded-[8px];
|
||||
}
|
||||
.btn-base.single.no-text {
|
||||
@apply min-w-[40px] px-0 md:min-w-[48px];
|
||||
}
|
||||
.btn-base.single.no-text .icon-platform {
|
||||
@apply mx-auto;
|
||||
}
|
||||
.btn-base.single .icon-platform {
|
||||
@apply mr-1;
|
||||
}
|
||||
</style>
|
||||
@@ -1,87 +0,0 @@
|
||||
<script setup>
|
||||
const {
|
||||
isProcessing,
|
||||
isShowCheckLauncher,
|
||||
isShowDownloadLauncher,
|
||||
validateLauncher,
|
||||
downloadLauncher,
|
||||
} = useCheckGameStart()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AtomsButton
|
||||
button-size="size-small md:size-small"
|
||||
:class="$attrs?.class"
|
||||
:disabled="isProcessing"
|
||||
style="font-size: 16px"
|
||||
@click="validateLauncher"
|
||||
>
|
||||
<slot />
|
||||
</AtomsButton>
|
||||
|
||||
<ClientOnly>
|
||||
<Teleport to="#teleports">
|
||||
<BlocksModalLayer
|
||||
v-model:is-open="isShowCheckLauncher"
|
||||
:is-show-dimmed="true"
|
||||
:is-outside-close="false"
|
||||
:modal-name="'launcher'"
|
||||
area-class="max-w-[480px] pt-[56px] px-[24px] pb-[24px] rounded-[8px]"
|
||||
close-class="absolute top-[16px] right-[24px]"
|
||||
>
|
||||
<span class="ico-loading"></span>
|
||||
<!-- [TODO] i18n 적용 -->
|
||||
<!-- <p class="text-check">{{ tm('Common_Message_Check_Client').txt }}</p> -->
|
||||
<p class="text-check">pc 클라이언트 실행 중...</p>
|
||||
<Transition name="fade">
|
||||
<div v-if="isShowDownloadLauncher" class="client-area">
|
||||
<!-- <p
|
||||
v-dompurify-html="tm('Common_Message_Download_Client').txt"
|
||||
class="text-info"
|
||||
></p>
|
||||
<button type="button" class="btn-download" @click="downloadLauncher">
|
||||
{{ tm('Common_Message_Install').txt }}
|
||||
</button>
|
||||
<p
|
||||
v-dompurify-html="tm('Common_Message_Download_Close').txt"
|
||||
class="text-tip"
|
||||
></p> -->
|
||||
<p class="text-info">
|
||||
PC 클라이언트가 실행되지 않나요?
|
||||
<br />
|
||||
다운로드 전이라면 다운로드 후 진행해주세요
|
||||
</p>
|
||||
<AtomsButtonVariant class="max-w-[300px]" @click="downloadLauncher">
|
||||
다운로드
|
||||
</AtomsButtonVariant>
|
||||
<p
|
||||
v-dompurify-html="
|
||||
'*PC 클라이언트가 정상 실행되었다면 팝업을 닫아 주세요.'
|
||||
"
|
||||
class="text-tip"
|
||||
></p>
|
||||
</div>
|
||||
</Transition>
|
||||
</BlocksModalLayer>
|
||||
</Teleport>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ico-loading {
|
||||
@apply block mx-auto mb-4 w-[80px] h-[80px] bg-[url('/images/common/stove_loading_light.png')] bg-contain bg-center bg-no-repeat;
|
||||
}
|
||||
.text-check {
|
||||
@apply mb-6 text-center text-[20px] font-bold leading-[30px] tracking-[-0.6px] text-[#333333];
|
||||
}
|
||||
|
||||
.client-area {
|
||||
@apply pt-4 border-t border-[rgba(0,0,0,0.08)] text-center;
|
||||
}
|
||||
.text-info {
|
||||
@apply mb-3 text-[14px] font-medium leading-[24px] tracking-[-0.42px] text-[#333333];
|
||||
}
|
||||
.text-tip {
|
||||
@apply mt-4 text-[14px] leading-[20px] tracking-[-0.42px] text-[#999999];
|
||||
}
|
||||
</style>
|
||||
68
layers/components/blocks/modal/Client.vue
Normal file
68
layers/components/blocks/modal/Client.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<script setup lang="ts">
|
||||
const { isShowCheckLauncher, isShowDownloadLauncher, downloadLauncher } =
|
||||
useCheckGameStart()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AtomsModalLayer
|
||||
v-model:is-open="isShowCheckLauncher"
|
||||
:is-show-dimmed="true"
|
||||
:is-outside-close="false"
|
||||
:modal-name="'launcher'"
|
||||
area-class="max-w-[480px] pt-[56px] px-[24px] pb-[24px] rounded-[8px]"
|
||||
close-class="absolute top-[16px] right-[24px]"
|
||||
>
|
||||
<span class="ico-loading"></span>
|
||||
<!-- [TODO] i18n 적용 -->
|
||||
<!-- <p class="text-check">{{ tm('Common_Message_Check_Client').txt }}</p> -->
|
||||
<p class="text-check">pc 클라이언트 실행 중...</p>
|
||||
<Transition name="fade">
|
||||
<div v-if="isShowDownloadLauncher" class="client-area">
|
||||
<!-- <p
|
||||
v-dompurify-html="tm('Common_Message_Download_Client').txt"
|
||||
class="text-info"
|
||||
></p>
|
||||
<button type="button" class="btn-download" @click="downloadLauncher">
|
||||
{{ tm('Common_Message_Install').txt }}
|
||||
</button>
|
||||
<p
|
||||
v-dompurify-html="tm('Common_Message_Download_Close').txt"
|
||||
class="text-tip"
|
||||
></p> -->
|
||||
<p class="text-info">
|
||||
PC 클라이언트가 실행되지 않나요?
|
||||
<br />
|
||||
다운로드 전이라면 다운로드 후 진행해주세요
|
||||
</p>
|
||||
<AtomsButtonVariant class="max-w-[300px]" @click="downloadLauncher">
|
||||
다운로드
|
||||
</AtomsButtonVariant>
|
||||
<p
|
||||
v-dompurify-html="
|
||||
'*PC 클라이언트가 정상 실행되었다면 팝업을 닫아 주세요.'
|
||||
"
|
||||
class="text-tip"
|
||||
></p>
|
||||
</div>
|
||||
</Transition>
|
||||
</AtomsModalLayer>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ico-loading {
|
||||
@apply block mx-auto mb-4 w-[80px] h-[80px] bg-[url('/images/common/stove_loading_light.png')] bg-contain bg-center bg-no-repeat;
|
||||
}
|
||||
.text-check {
|
||||
@apply mb-6 text-center text-[20px] font-bold leading-[30px] tracking-[-0.6px] text-[#333333];
|
||||
}
|
||||
|
||||
.client-area {
|
||||
@apply pt-4 border-t border-[rgba(0,0,0,0.08)] text-center;
|
||||
}
|
||||
.text-info {
|
||||
@apply mb-3 text-[14px] font-medium leading-[24px] tracking-[-0.42px] text-[#333333];
|
||||
}
|
||||
.text-tip {
|
||||
@apply mt-4 text-[14px] leading-[20px] tracking-[-0.42px] text-[#999999];
|
||||
}
|
||||
</style>
|
||||
@@ -296,9 +296,14 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</nav>
|
||||
<div ref="startRef" class="btn-start">
|
||||
<BlocksButtonLuncher class="w-full md:w-auto">
|
||||
<BlocksButtonDownload
|
||||
type="custom"
|
||||
platform="pc"
|
||||
class="w-full md:w-auto"
|
||||
style="font-size: 16px"
|
||||
>
|
||||
게임 시작
|
||||
</BlocksButtonLuncher>
|
||||
</BlocksButtonDownload>
|
||||
</div>
|
||||
<button class="btn-close" @click="handleMenuClose">
|
||||
<AtomsIconsMenuCloseLine class="mx-auto" />
|
||||
|
||||
@@ -12,7 +12,6 @@ interface ButtonListProps {
|
||||
|
||||
const props = defineProps<ButtonListProps>()
|
||||
|
||||
const { gameData } = useGameDataStore()
|
||||
const { locale } = useI18n()
|
||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
||||
|
||||
@@ -40,38 +39,11 @@ const getButtonType = (btnInfo: PageDataResourceGroupBtnInfo): ButtonType => {
|
||||
return DEFAULT_BUTTON_TYPE
|
||||
}
|
||||
|
||||
const getButtonBackgroundImage = (
|
||||
btnInfo: PageDataResourceGroupBtnInfo
|
||||
): string => {
|
||||
const marketType = btnInfo?.detail?.market_type
|
||||
const marketImageMap: Record<string, string> = {
|
||||
google_play: '/images/common/btn_logo-google.svg',
|
||||
app_store: '/images/common/btn_logo-app.svg',
|
||||
pc: '/images/common/btn_logo-pc.svg',
|
||||
}
|
||||
|
||||
if (marketType && marketImageMap[marketType]) {
|
||||
return marketImageMap[marketType]
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
const handleButtonClick = (
|
||||
btnInfo: PageDataResourceGroupBtnInfo,
|
||||
index: any
|
||||
) => {
|
||||
const handleSendLog = (index: number) => {
|
||||
sendLog(
|
||||
locale.value,
|
||||
useAnalyticsLogDataDirect(props.resourcesData[index], props.pageVerTmplSeq)
|
||||
)
|
||||
|
||||
const marketType = btnInfo?.detail?.market_type
|
||||
if (marketType) {
|
||||
const url = gameData?.market_json[marketType]?.url
|
||||
window.open(url, '_blank')
|
||||
return
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -80,41 +52,29 @@ const handleButtonClick = (
|
||||
v-if="props.resourcesData?.length"
|
||||
class="flex flex-wrap justify-center gap-3 md:gap-4"
|
||||
>
|
||||
<AtomsButton
|
||||
v-for="(button, index) in props.resourcesData"
|
||||
:key="index"
|
||||
:type="getButtonType(button.btn_info)"
|
||||
:target="button.btn_info?.detail?.action?.link_target"
|
||||
:href="button.btn_info?.detail?.action?.url"
|
||||
:rel="button.btn_info?.detail?.action?.rel"
|
||||
:background-color="
|
||||
getColorCode({
|
||||
colorName: button.btn_info?.color_name_btn,
|
||||
colorCode: button.btn_info?.color_code_btn,
|
||||
})
|
||||
"
|
||||
:text-color="
|
||||
getColorCode({
|
||||
colorName: button.btn_info?.color_name_txt,
|
||||
colorCode: button.btn_info?.color_code_txt,
|
||||
})
|
||||
"
|
||||
:disabled="button.btn_info?.disabled"
|
||||
:class="button.btn_info?.detail?.market_type ? 'btn-market' : ''"
|
||||
:style="{
|
||||
backgroundImage: `url(${getButtonBackgroundImage(button.btn_info)})`,
|
||||
}"
|
||||
@click="handleButtonClick(button.btn_info, index)"
|
||||
>
|
||||
{{ button.btn_info?.txt_btn_name }}
|
||||
</AtomsButton>
|
||||
<template v-for="(button, index) in props.resourcesData" :key="index">
|
||||
<AtomsButton
|
||||
:type="getButtonType(button.btn_info)"
|
||||
:target="button.btn_info?.detail?.action?.link_target"
|
||||
:href="button.btn_info?.detail?.action?.url"
|
||||
:rel="button.btn_info?.detail?.action?.rel"
|
||||
:background-color="
|
||||
getColorCode({
|
||||
colorName: button.btn_info?.color_name_btn,
|
||||
colorCode: button.btn_info?.color_code_btn,
|
||||
})
|
||||
"
|
||||
:text-color="
|
||||
getColorCode({
|
||||
colorName: button.btn_info?.color_name_txt,
|
||||
colorCode: button.btn_info?.color_code_txt,
|
||||
})
|
||||
"
|
||||
:disabled="button.btn_info?.disabled"
|
||||
@click="handleSendLog(index)"
|
||||
>
|
||||
{{ button.btn_info?.txt_btn_name }}
|
||||
</AtomsButton>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.btn-market) {
|
||||
@apply flex items-start bg-[16px_50%] bg-[length:auto_28px] bg-no-repeat
|
||||
min-w-[113px] pt-[22px] pl-[44px] pr-[22px] text-[11px]
|
||||
md:min-w-[150px] md:pt-[30px] md:pl-[64px] md:pr-[28px] md:text-[12px] md:bg-[20px_50%] md:bg-[length:auto_40px];
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -33,16 +33,16 @@ const buttonListData = computed(() => {
|
||||
<template>
|
||||
<section class="relative py-[80px] md:py-[120px]">
|
||||
<WidgetsBackground v-if="backgroundData" :resources-data="backgroundData" />
|
||||
<div class="section-content px-0">
|
||||
<div class="section-content">
|
||||
<WidgetsMainTitle
|
||||
v-if="mainTitleData"
|
||||
:resources-data="mainTitleData"
|
||||
class="title-xlg mx-[20px] sm:mx-[40px]"
|
||||
class="title-xlg"
|
||||
/>
|
||||
<WidgetsSubTitle
|
||||
v-if="subTitleData"
|
||||
:resources-data="subTitleData"
|
||||
class="title-sm mt-2 mx-[20px] sm:mx-[40px]"
|
||||
class="title-sm mt-2"
|
||||
/>
|
||||
<div v-if="imgListData" class="img-container">
|
||||
<div v-for="(item, index) in imgListData" :key="index" class="img-item">
|
||||
@@ -57,12 +57,12 @@ const buttonListData = computed(() => {
|
||||
v-if="buttonListData"
|
||||
:resources-data="buttonListData"
|
||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
||||
class="mt-[56px] mx-[20px] sm:mx-[40px]"
|
||||
class="mt-[56px]"
|
||||
/>
|
||||
<WidgetsDescription
|
||||
v-if="descriptionData"
|
||||
:resources-data="descriptionData"
|
||||
class="mt-8 mx-[20px] sm:mx-[40px]"
|
||||
class="mt-8"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
@@ -70,8 +70,11 @@ const buttonListData = computed(() => {
|
||||
|
||||
<style scoped>
|
||||
.img-container {
|
||||
@apply flex flex-wrap items-center justify-center gap-4 box-content mx-auto mt-[32px]
|
||||
max-w-[688px] px-[20px]
|
||||
md:max-w-[944px] md:px-[40px];
|
||||
@apply flex flex-col items-center justify-center gap-4 box-content mx-auto mt-[32px]
|
||||
w-[334px]
|
||||
md:w-[944px];
|
||||
}
|
||||
.img-item {
|
||||
@apply w-full;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -9,6 +9,9 @@ interface Props {
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { locale } = useI18n()
|
||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
||||
|
||||
const backgroundData = computed(() =>
|
||||
getComponentGroup(props.components, 'background')
|
||||
)
|
||||
@@ -24,6 +27,13 @@ const videoPlayData = computed(() =>
|
||||
const buttonListData = computed(() =>
|
||||
getComponentGroupAry(props.components, 'buttonList')
|
||||
)
|
||||
|
||||
const handleSendLog = (index: number) => {
|
||||
sendLog(
|
||||
locale.value,
|
||||
useAnalyticsLogDataDirect(buttonListData.value[index], props.pageVerTmplSeq)
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -49,13 +59,33 @@ const buttonListData = computed(() =>
|
||||
:resources-data="videoPlayData"
|
||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
||||
/>
|
||||
<WidgetsButtonList
|
||||
<div
|
||||
v-if="buttonListData.length > 0"
|
||||
button-type="market"
|
||||
:resources-data="buttonListData"
|
||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
||||
class="mt-[22px] md:mt-[52px]"
|
||||
/>
|
||||
class="flex flex-wrap justify-center gap-3 mt-[22px] md:gap-4 md:mt-[52px]"
|
||||
>
|
||||
<BlocksButtonDownload
|
||||
v-for="(button, index) in buttonListData"
|
||||
:key="index"
|
||||
type="duplication"
|
||||
:platform="button.btn_info?.detail?.market_type"
|
||||
:background-color="
|
||||
getColorCode({
|
||||
colorName: button.btn_info?.color_name_btn,
|
||||
colorCode: button.btn_info?.color_code_btn,
|
||||
})
|
||||
"
|
||||
:text-color="
|
||||
getColorCode({
|
||||
colorName: button.btn_info?.color_name_txt,
|
||||
colorCode: button.btn_info?.color_code_txt,
|
||||
})
|
||||
"
|
||||
:disabled="button.btn_info?.disabled"
|
||||
@click="handleSendLog(index)"
|
||||
>
|
||||
{{ button.btn_info?.txt_btn_name }}
|
||||
</BlocksButtonDownload>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
export type ButtonType = 'internal' | 'external' | 'download' | 'action'
|
||||
export type ButtonType =
|
||||
| 'internal'
|
||||
| 'external'
|
||||
| 'download'
|
||||
| 'action'
|
||||
| 'link'
|
||||
|
||||
export type DownloadButtonType = 'default' | 'single' | 'duplication' | 'custom'
|
||||
|
||||
export type ButtonSize = 'large' | 'medium' | 'small' | 'extra-small'
|
||||
|
||||
export type ButtonVariant = 'filled' | 'outlined'
|
||||
|
||||
Reference in New Issue
Block a user