feat. GR_VISUAL_03 컴포넌트 제작
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
@import './base/_theme.css';
|
@import './base/_theme.css';
|
||||||
@import './base/_reset.css';
|
@import './base/_reset.css';
|
||||||
|
@import './components/_swiper-pagination.css';
|
||||||
|
@import './components/_button.css';
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
|
|||||||
44
layers/assets/css/components/_button.css
Normal file
44
layers/assets/css/components/_button.css
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/* Button Size Classes */
|
||||||
|
@layer components {
|
||||||
|
.btn-base {
|
||||||
|
@apply relative inline-flex items-center justify-center font-medium border border-gray-600/30 overflow-hidden;
|
||||||
|
/* 기본 크기: size-medium */
|
||||||
|
--btn-padding: theme('spacing.10');
|
||||||
|
--btn-height: theme('spacing.14');
|
||||||
|
--btn-text: theme('fontSize.base');
|
||||||
|
--btn-radius: theme('borderRadius.lg');
|
||||||
|
@apply px-10 h-14 text-base rounded-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-extra-small {
|
||||||
|
--btn-padding: theme('spacing.6');
|
||||||
|
--btn-height: theme('spacing.10');
|
||||||
|
--btn-text: theme('fontSize.sm');
|
||||||
|
--btn-radius: theme('borderRadius.DEFAULT');
|
||||||
|
@apply px-6 h-10 text-sm rounded;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-small {
|
||||||
|
--btn-padding: theme('spacing.10');
|
||||||
|
--btn-height: theme('spacing.12');
|
||||||
|
--btn-text: theme('fontSize.sm');
|
||||||
|
--btn-radius: theme('borderRadius.lg');
|
||||||
|
@apply px-10 h-12 text-sm rounded-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-medium {
|
||||||
|
--btn-padding: theme('spacing.10');
|
||||||
|
--btn-height: theme('spacing.14');
|
||||||
|
--btn-text: theme('fontSize.base');
|
||||||
|
--btn-radius: theme('borderRadius.lg');
|
||||||
|
@apply px-10 h-14 text-base rounded-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-large {
|
||||||
|
--btn-padding: theme('spacing.10');
|
||||||
|
--btn-height: theme('spacing.16');
|
||||||
|
--btn-text: theme('fontSize.lg');
|
||||||
|
--btn-radius: theme('borderRadius.lg');
|
||||||
|
@apply px-10 h-16 text-lg rounded-lg;
|
||||||
|
}
|
||||||
|
}
|
||||||
73
layers/assets/css/components/_swiper-pagination.css
Normal file
73
layers/assets/css/components/_swiper-pagination.css
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/* 페이지네이션 버튼 */
|
||||||
|
main .slide-pagination.swiper-pagination-bullets {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 48px;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-pagination .swiper-pagination-bullet {
|
||||||
|
position: relative;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: var(--primary);
|
||||||
|
border-radius: 50%;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-pagination .swiper-pagination-bullet:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-pagination .swiper-pagination-bullet-active:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 네비게이션 버튼 */
|
||||||
|
.slide-prev,
|
||||||
|
.slide-next {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 5;
|
||||||
|
|
||||||
|
background-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-prev {
|
||||||
|
left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-next {
|
||||||
|
right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
main .slide-pagination.swiper-pagination-bullets {
|
||||||
|
bottom: 32px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-pagination .swiper-pagination-bullet {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type {
|
|
||||||
ButtonSize,
|
|
||||||
ButtonConfig,
|
|
||||||
ButtonProps,
|
|
||||||
} from '#layers/types/components/button'
|
|
||||||
import type { GameDataKeyCodeCodes } from '#layers/types/api/gameData'
|
import type { GameDataKeyCodeCodes } from '#layers/types/api/gameData'
|
||||||
|
|
||||||
|
interface ButtonProps {
|
||||||
|
backgroundColor?: string
|
||||||
|
textColor?: string
|
||||||
|
icon?: string
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ButtonProps>(), {
|
const props = withDefaults(defineProps<ButtonProps>(), {
|
||||||
size: 'medium',
|
|
||||||
backgroundColor: 'var(--primary)',
|
backgroundColor: 'var(--primary)',
|
||||||
textColor: 'var(--alternative-02)',
|
textColor: 'var(--alternative-02)',
|
||||||
icon: '',
|
icon: '',
|
||||||
@@ -23,63 +24,36 @@ const PARSED_KEY_CODE_CODES_KEYS: (keyof GameDataKeyCodeCodes)[] = [
|
|||||||
'alternative-02',
|
'alternative-02',
|
||||||
]
|
]
|
||||||
|
|
||||||
// 버튼 크기별 설정 상수
|
|
||||||
const BUTTON_CONFIGS: Record<ButtonSize, ButtonConfig> = {
|
|
||||||
large: {
|
|
||||||
padding: 'px-10',
|
|
||||||
height: 'h-16',
|
|
||||||
text: 'text-lg',
|
|
||||||
rounded: 'rounded-lg',
|
|
||||||
},
|
|
||||||
medium: {
|
|
||||||
padding: 'px-10',
|
|
||||||
height: 'h-14',
|
|
||||||
text: 'text-base',
|
|
||||||
rounded: 'rounded-lg',
|
|
||||||
},
|
|
||||||
small: {
|
|
||||||
padding: 'px-10',
|
|
||||||
height: 'h-12',
|
|
||||||
text: 'text-sm',
|
|
||||||
rounded: 'rounded-lg',
|
|
||||||
},
|
|
||||||
'extra-small': {
|
|
||||||
padding: 'px-6',
|
|
||||||
height: 'h-10',
|
|
||||||
text: 'text-sm',
|
|
||||||
rounded: 'rounded',
|
|
||||||
},
|
|
||||||
} as const
|
|
||||||
|
|
||||||
// 색상 값을 CSS 변수로 변환하는 헬퍼 함수
|
// 색상 값을 CSS 변수로 변환하는 헬퍼 함수
|
||||||
const getColorValue = (color: string) =>
|
const getColorValue = (color: string) =>
|
||||||
PARSED_KEY_CODE_CODES_KEYS.includes(color as keyof GameDataKeyCodeCodes)
|
PARSED_KEY_CODE_CODES_KEYS.includes(color as keyof GameDataKeyCodeCodes)
|
||||||
? `var(--${color})`
|
? `var(--${color})`
|
||||||
: color
|
: color
|
||||||
|
|
||||||
const currentConfig = computed(() => BUTTON_CONFIGS[props.size])
|
|
||||||
const buttonClasses = computed(() => [
|
const buttonClasses = computed(() => [
|
||||||
'group relative inline-flex items-center justify-center font-medium border border-gray-600/30 overflow-hidden',
|
'btn-base group relative inline-flex items-center justify-center font-medium border border-gray-600/30 overflow-hidden',
|
||||||
`${currentConfig.value.padding} ${currentConfig.value.height} ${currentConfig.value.text} ${currentConfig.value.rounded}`,
|
|
||||||
props.disabled ? 'cursor-default' : 'cursor-pointer',
|
props.disabled ? 'cursor-default' : 'cursor-pointer',
|
||||||
])
|
])
|
||||||
|
|
||||||
const buttonStyles = computed(() => ({
|
const buttonStyles = computed(() => ({
|
||||||
backgroundColor: getColorValue(props.backgroundColor),
|
backgroundColor: getColorValue(props.backgroundColor),
|
||||||
color: getColorValue(props.textColor),
|
color: getColorValue(props.textColor),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const overlayClasses = computed(() => [
|
const overlayClasses = computed(() => [
|
||||||
'absolute inset-0 -m-px transition-opacity duration-200',
|
'absolute inset-0 -m-px transition-opacity duration-200',
|
||||||
props.disabled
|
props.disabled
|
||||||
? 'opacity-20 z-10'
|
? 'opacity-20 z-10'
|
||||||
: 'bg-white opacity-0 group-hover:opacity-20',
|
: 'bg-white opacity-0 group-hover:opacity-20',
|
||||||
currentConfig.value.rounded,
|
|
||||||
])
|
])
|
||||||
|
|
||||||
const overlayDisabledStyles = computed(
|
const overlayDisabledStyles = computed(
|
||||||
() =>
|
() =>
|
||||||
props.disabled && {
|
props.disabled && {
|
||||||
backgroundColor: props.textColor,
|
backgroundColor: props.textColor,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const contentDisabledStyles = computed(() => props.disabled && { opacity: 0.2 })
|
const contentDisabledStyles = computed(() => props.disabled && { opacity: 0.2 })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
|
|
||||||
const emit = defineEmits<Emits>()
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
// YouTube URL을 임베드 가능한 형태로 변환
|
// [TODO] YouTube URL을 임베드 가능한 형태로 변환
|
||||||
const embedUrl = computed(() => {
|
const embedUrl = computed(() => {
|
||||||
if (!props.youtubeUrl) return ''
|
if (!props.youtubeUrl) return ''
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ watchEffect(() => {
|
|||||||
<component
|
<component
|
||||||
:is="registry[template.template_code]?.component"
|
:is="registry[template.template_code]?.component"
|
||||||
:components="template.components"
|
:components="template.components"
|
||||||
|
:page-ver-tmpl-seq="template.page_ver_tmpl_seq"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import { getHeader } from 'h3'
|
|
||||||
import { useRuntimeConfig, useRequestEvent } from 'nuxt/app'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 게임 별칭을 추출하는 유틸리티 함수
|
|
||||||
* @param host 호스트 문자열
|
|
||||||
* @param baseDomain 기본 도메인
|
|
||||||
* @returns 게임 별칭 또는 빈 문자열
|
|
||||||
*/
|
|
||||||
const extractGameAliasFromHost = (host: string, baseDomain: string): string => {
|
|
||||||
if (!host || !host.includes(baseDomain)) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const subdomain = host.split('.')[0]
|
|
||||||
return subdomain && subdomain !== 'www' ? subdomain : ''
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 서버 사이드에서 게임 별칭을 가져오는 함수
|
|
||||||
* @param baseDomain 기본 도메인
|
|
||||||
* @returns 게임 별칭 또는 빈 문자열
|
|
||||||
*/
|
|
||||||
const getGameAliasFromServer = (baseDomain: string): string => {
|
|
||||||
try {
|
|
||||||
const event = useRequestEvent()
|
|
||||||
|
|
||||||
if (!event) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// 미들웨어에서 설정한 gameAlias가 있다면 우선 사용
|
|
||||||
if (event.context.gameAlias) {
|
|
||||||
return event.context.gameAlias
|
|
||||||
}
|
|
||||||
|
|
||||||
const host = getHeader(event, 'host') || ''
|
|
||||||
return extractGameAliasFromHost(host, baseDomain)
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error('useGetGameAlias server error:', error)
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 클라이언트 사이드에서 게임 별칭을 가져오는 함수
|
|
||||||
* @param baseDomain 기본 도메인
|
|
||||||
* @returns 게임 별칭 또는 빈 문자열
|
|
||||||
*/
|
|
||||||
const getGameAliasFromClient = (baseDomain: string): string => {
|
|
||||||
try {
|
|
||||||
const host = window.location.host
|
|
||||||
return extractGameAliasFromHost(host, baseDomain)
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error('useGetGameAlias client error:', error)
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 게임 별칭을 가져오는 컴포저블 함수
|
|
||||||
* 서버와 클라이언트 환경에서 모두 동작
|
|
||||||
* @returns 게임 별칭 문자열
|
|
||||||
*/
|
|
||||||
export const useGetGameAlias = (): string => {
|
|
||||||
const config = useRuntimeConfig()
|
|
||||||
const baseDomain = (config.public.baseDomain || '.onstove.com') as string
|
|
||||||
|
|
||||||
if (import.meta.client) {
|
|
||||||
return getGameAliasFromClient(baseDomain)
|
|
||||||
}
|
|
||||||
|
|
||||||
return getGameAliasFromServer(baseDomain)
|
|
||||||
}
|
|
||||||
35
layers/composables/useGetGameDomain.ts
Normal file
35
layers/composables/useGetGameDomain.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { getHeader, getRequestHost } from 'h3'
|
||||||
|
import { useRequestEvent } from 'nuxt/app'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 게임 도메인을 가져오는 컴포저블 함수
|
||||||
|
* 서버와 클라이언트 환경에서 모두 동작
|
||||||
|
* @returns 게임 도메인 문자열
|
||||||
|
*/
|
||||||
|
export const useGetGameDomain = (): string => {
|
||||||
|
try {
|
||||||
|
if (import.meta.client) {
|
||||||
|
const host = window.location.host || ''
|
||||||
|
return host.split(':')[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = useRequestEvent()
|
||||||
|
if (!event) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 미들웨어에서 설정한 gameDomain가 있다면 우선 사용
|
||||||
|
if (event.context.gameDomain) {
|
||||||
|
return event.context.gameDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
const host =
|
||||||
|
(getHeader(event, 'host') || getRequestHost(event)).toString() || ''
|
||||||
|
const cleanHost = host.split(':')[0]
|
||||||
|
|
||||||
|
return cleanHost || ''
|
||||||
|
} catch (error) {
|
||||||
|
console.error('useGetGameDomain error:', error)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import { commonFetch } from '#layers/utils/apiUtil'
|
import { commonFetch } from '#layers/utils/apiUtil'
|
||||||
import { usePageDataStore } from '#layers/stores/usePageDataStore'
|
import { usePageDataStore } from '#layers/stores/usePageDataStore'
|
||||||
import { useGetGameAlias } from '#layers/composables/useGetGameAlias'
|
import { useGetGameDomain } from '@/layers/composables/useGetGameDomain'
|
||||||
import { usePathResolver } from '#layers/composables/usePathResolver'
|
import { usePathResolver } from '#layers/composables/usePathResolver'
|
||||||
import type { PageDataResponse } from '#layers/types/api/pageData'
|
import type { PageDataResponse } from '#layers/types/api/pageData'
|
||||||
|
|
||||||
export default defineNuxtRouteMiddleware(async (to, _from) => {
|
export default defineNuxtRouteMiddleware(async (to, _from) => {
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
const store = usePageDataStore()
|
const store = usePageDataStore()
|
||||||
const gameAlias = useGetGameAlias()
|
const gameDomain = useGetGameDomain()
|
||||||
const { getPathAfterLanguage } = usePathResolver()
|
const { getPathAfterLanguage } = usePathResolver()
|
||||||
|
|
||||||
const stoveApiBaseUrl = config.public.stoveApiUrl
|
const stoveApiBaseUrl = config.public.stoveApiUrl
|
||||||
@@ -22,7 +22,7 @@ export default defineNuxtRouteMiddleware(async (to, _from) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const queryParams: Record<string, string> = {
|
const queryParams: Record<string, string> = {
|
||||||
game_alias: gameAlias,
|
game_domain: gameDomain,
|
||||||
lang_code: 'ko',
|
lang_code: 'ko',
|
||||||
page_url: pageUrl,
|
page_url: pageUrl,
|
||||||
_t: Date.now().toString(), // 캐시 무효화를 위한 타임스탬프
|
_t: Date.now().toString(), // 캐시 무효화를 위한 타임스탬프
|
||||||
|
|||||||
@@ -22,14 +22,11 @@ export default defineEventHandler(async event => {
|
|||||||
const host =
|
const host =
|
||||||
(getHeader(event, 'host') || getRequestHost(event)).toString() || ''
|
(getHeader(event, 'host') || getRequestHost(event)).toString() || ''
|
||||||
const baseDomain = process.env.BASE_DOMAIN || '.onstove.com'
|
const baseDomain = process.env.BASE_DOMAIN || '.onstove.com'
|
||||||
const isGameAliasExtractable = host.includes(baseDomain)
|
const isGameDomainExtractable = host.includes(baseDomain)
|
||||||
|
|
||||||
if (isGameAliasExtractable) {
|
if (isGameDomainExtractable) {
|
||||||
const gameAlias = host.split('.')[0]
|
const cleanHost = host.split(':')[0]
|
||||||
|
event.context.gameDomain = cleanHost
|
||||||
if (gameAlias && gameAlias !== 'www') {
|
|
||||||
event.context.gameAlias = gameAlias
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gameData를 직접 가져와서 context에 저장 (API 호출 없이)
|
// gameData를 직접 가져와서 context에 저장 (API 호출 없이)
|
||||||
@@ -52,7 +49,7 @@ export default defineEventHandler(async event => {
|
|||||||
const langCode = pathSegments[0] || 'ko'
|
const langCode = pathSegments[0] || 'ko'
|
||||||
|
|
||||||
const queryParams: Record<string, string> = {
|
const queryParams: Record<string, string> = {
|
||||||
game_alias: event.context.gameAlias || '',
|
game_domain: event.context.gameDomain || '',
|
||||||
lang_code: langCode,
|
lang_code: langCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +60,7 @@ export default defineEventHandler(async event => {
|
|||||||
// 타입 단언을 사용하여 response의 타입 오류를 해결
|
// 타입 단언을 사용하여 response의 타입 오류를 해결
|
||||||
const res = response as { code?: number; value?: unknown }
|
const res = response as { code?: number; value?: unknown }
|
||||||
|
|
||||||
if (res?.code === 0 && 'value' in res) {
|
if (res?.code === 0 && res && typeof res === 'object' && 'value' in res) {
|
||||||
event.context.gameData = res.value
|
event.context.gameData = res.value
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
components: Record<string, any>
|
components: Record<string, any>
|
||||||
|
pageVerTmplSeq: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|||||||
@@ -1,39 +1,101 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { Swiper, SwiperSlide } from 'swiper/vue'
|
||||||
|
import { EffectFade, Navigation, Pagination } from 'swiper/modules'
|
||||||
|
import { getResponsiveClass, getResponsiveSrc } from '#layers/utils/dataUtil'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
components: Record<string, any>
|
components: Record<string, any>
|
||||||
|
pageVerTmplSeq: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const arrow = computed(() => {
|
||||||
|
return props.components.arrow.groups
|
||||||
|
})
|
||||||
|
|
||||||
|
const modules = [EffectFade, Navigation, Pagination]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="relative h-[640px] lg:h-[1000px]">
|
<section class="relative h-[640px] md:h-[1000px]">
|
||||||
<WidgetsBackground
|
<Swiper
|
||||||
v-if="props.components?.background"
|
:modules="modules"
|
||||||
:resources-data="props.components?.background.groups[0]"
|
:loop="true"
|
||||||
/>
|
effect="fade"
|
||||||
<div
|
:pagination="
|
||||||
class="relative h-full flex flex-col items-center justify-center gap-4"
|
{
|
||||||
|
el: '.slide-pagination',
|
||||||
|
clickable: true,
|
||||||
|
} as any
|
||||||
|
"
|
||||||
|
:navigation="
|
||||||
|
{
|
||||||
|
nextEl: '.slide-next',
|
||||||
|
prevEl: '.slide-prev',
|
||||||
|
} as any
|
||||||
|
"
|
||||||
|
class="h-full"
|
||||||
>
|
>
|
||||||
<WidgetsSubTitle
|
<SwiperSlide
|
||||||
v-if="props.components.subTitle"
|
v-for="group in props.components.group_sets"
|
||||||
:resources-data="props.components.subTitle.groups[0]"
|
:key="group.set_order"
|
||||||
class="text-[24px] font-[500] text-[#ffffff] leading-[34px]"
|
class="bg-black"
|
||||||
/>
|
>
|
||||||
<WidgetsMainTitle
|
<WidgetsBackground
|
||||||
v-if="props.components.mainTitle"
|
v-if="group?.background.groups"
|
||||||
:resources-data="props.components.cardMainTitle.groups[0]"
|
:resources-data="group.background.groups[0]"
|
||||||
class="text-[50px] font-[700] text-[#c7a28b] leading-[70px]"
|
/>
|
||||||
/>
|
<div
|
||||||
<WidgetsDescription
|
class="relative h-full flex flex-col items-center justify-center gap-[14px] text-center md:gap-5"
|
||||||
v-if="props.components.description"
|
>
|
||||||
:resources-data="props.components.description.groups[0]"
|
<WidgetsSubTitle
|
||||||
class="text-[20px] font-[500] text-white/70 leading-[30px]"
|
v-if="group.subTitle && group.subTitle.groups"
|
||||||
/>
|
:resources-data="group.subTitle.groups[0]"
|
||||||
<WidgetsButtonList
|
class="line-clamp-2 text-[16px] font-[500] leading-[24px] md:line-clamp-1 md:text-[24px] md:leading-[34px]"
|
||||||
v-if="props.components.buttonList"
|
/>
|
||||||
:groups-data="props.components.buttonList"
|
<WidgetsMainTitle
|
||||||
/>
|
v-if="group.mainTitle && group.mainTitle.groups"
|
||||||
</div>
|
:resources-data="group.mainTitle.groups[0]"
|
||||||
|
class="line-clamp-3 text-[24px] font-[700] leading-[34px] md:text-[50px] md:leading-[70px]"
|
||||||
|
/>
|
||||||
|
<WidgetsDescription
|
||||||
|
v-if="group.description && group.description.groups"
|
||||||
|
:resources-data="group.description.groups[0]"
|
||||||
|
class="line-clamp-3 text-[15px] font-[400] leading-[24px] md:text-[20px] md:leading-[30px]"
|
||||||
|
/>
|
||||||
|
<WidgetsButtonList
|
||||||
|
v-if="group.buttonList && group.buttonList.groups"
|
||||||
|
:groups-data="group.buttonList.groups"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</SwiperSlide>
|
||||||
|
|
||||||
|
<div class="slide-pagination" />
|
||||||
|
|
||||||
|
<!-- Navigation buttons -->
|
||||||
|
<div
|
||||||
|
class="slide-prev hidden md:block"
|
||||||
|
:class="getResponsiveClass()"
|
||||||
|
:style="
|
||||||
|
getResponsiveSrc(arrow[0].res_path, {
|
||||||
|
resourcesType: 'bg',
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span class="sr-only">prev</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="slide-next hidden md:block"
|
||||||
|
:class="getResponsiveClass()"
|
||||||
|
:style="
|
||||||
|
getResponsiveSrc(arrow[1].res_path, {
|
||||||
|
resourcesType: 'bg',
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span class="sr-only">next</span>
|
||||||
|
</div>
|
||||||
|
</Swiper>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"h3": "^1.15.4",
|
"h3": "^1.15.4",
|
||||||
"nuxt": "^4.0.3",
|
"nuxt": "^4.0.3",
|
||||||
"pinia": "^2.3.1",
|
"pinia": "^2.3.1",
|
||||||
|
"swiper": "^12.0.1",
|
||||||
"vue": "^3.5.0",
|
"vue": "^3.5.0",
|
||||||
"vue-dompurify-html": "^5.3.0"
|
"vue-dompurify-html": "^5.3.0"
|
||||||
},
|
},
|
||||||
|
|||||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -29,6 +29,9 @@ importers:
|
|||||||
pinia:
|
pinia:
|
||||||
specifier: ^2.3.1
|
specifier: ^2.3.1
|
||||||
version: 2.3.1(typescript@5.9.2)(vue@3.5.21(typescript@5.9.2))
|
version: 2.3.1(typescript@5.9.2)(vue@3.5.21(typescript@5.9.2))
|
||||||
|
swiper:
|
||||||
|
specifier: ^12.0.1
|
||||||
|
version: 12.0.1
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.5.0
|
specifier: ^3.5.0
|
||||||
version: 3.5.21(typescript@5.9.2)
|
version: 3.5.21(typescript@5.9.2)
|
||||||
@@ -4171,6 +4174,10 @@ packages:
|
|||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
swiper@12.0.1:
|
||||||
|
resolution: {integrity: sha512-Fi+gNw/tfc4hsGowQU5tRC/f1HFknkh4Vz8PaDI4JTinLUMTwhZyaovcH/va+iXq98BNUHN5ok0c2lEI82Fsgw==}
|
||||||
|
engines: {node: '>= 4.7.0'}
|
||||||
|
|
||||||
synckit@0.11.11:
|
synckit@0.11.11:
|
||||||
resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
|
resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
@@ -9190,6 +9197,8 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
sax: 1.4.1
|
sax: 1.4.1
|
||||||
|
|
||||||
|
swiper@12.0.1: {}
|
||||||
|
|
||||||
synckit@0.11.11:
|
synckit@0.11.11:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@pkgr/core': 0.2.9
|
'@pkgr/core': 0.2.9
|
||||||
|
|||||||
Reference in New Issue
Block a user