- AdBanner 컴포넌트(애드핏 DAN-n0I5XRBOwAdSFbEX, 728x90), 유료 회원에겐 미표시 - 웹 안내 페이지 하단 배치 - 앱 셸 하단 내비 바로 위 고정 배치(본문 하단 여백 보정) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+21
-1
@@ -4,6 +4,7 @@ import { RouterView, useRoute, useRouter } from 'vue-router'
|
||||
import AppHeader from '@/components/layout/AppHeader.vue'
|
||||
import AppSidebar from '@/components/layout/AppSidebar.vue'
|
||||
import AppBottomNav from '@/components/layout/AppBottomNav.vue'
|
||||
import AdBanner from '@/components/AdBanner.vue'
|
||||
import LoginModal from '@/components/LoginModal.vue'
|
||||
import SignupModal from '@/components/SignupModal.vue'
|
||||
import WebOnlyNotice from '@/components/WebOnlyNotice.vue'
|
||||
@@ -20,6 +21,8 @@ const router = useRouter()
|
||||
|
||||
// 로그인 또는 둘러보기(데모) 상태면 사이드바/전체 셸 표시
|
||||
const authed = computed(() => auth.isAuthenticated || demo.on)
|
||||
// 광고: 유료(PREMIUM) 회원에게는 표시하지 않음
|
||||
const showAds = computed(() => !auth.isPremium)
|
||||
|
||||
function leaveDemo() {
|
||||
exitDemo()
|
||||
@@ -63,7 +66,7 @@ watch(() => route.fullPath, () => ui.closeSidebar())
|
||||
|
||||
<!-- 앱(Capacitor): 전체 기능 -->
|
||||
<template v-else>
|
||||
<div class="layout" :class="{ 'sidebar-open': ui.sidebarOpen, 'no-sidebar': !authed }">
|
||||
<div class="layout" :class="{ 'sidebar-open': ui.sidebarOpen, 'no-sidebar': !authed, 'has-ad': showAds }">
|
||||
<AppHeader class="layout-top" />
|
||||
<!-- 로그인/둘러보기 전에는 메뉴가 없어 사이드바를 숨김(빈 칸 제거·중앙 정렬) -->
|
||||
<template v-if="authed">
|
||||
@@ -80,6 +83,8 @@ watch(() => route.fullPath, () => ui.closeSidebar())
|
||||
</div>
|
||||
<RouterView />
|
||||
</main>
|
||||
<!-- 하단 내비 위 광고 (유료 회원 제외) -->
|
||||
<AdBanner v-if="showAds" class="app-ad" />
|
||||
<AppBottomNav class="layout-bottom" />
|
||||
</div>
|
||||
|
||||
@@ -157,6 +162,21 @@ watch(() => route.fullPath, () => ui.closeSidebar())
|
||||
grid-area: bottom;
|
||||
}
|
||||
|
||||
/* 하단 내비(56px) 바로 위 고정 광고 */
|
||||
.app-ad {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: calc(56px + env(safe-area-inset-bottom, 0));
|
||||
z-index: 999;
|
||||
background: var(--color-background);
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
/* 광고가 있을 땐 본문 하단 여백을 광고 높이만큼 더 확보 */
|
||||
.layout.has-ad .layout-body {
|
||||
padding-bottom: calc(56px + 96px + env(safe-area-inset-bottom, 0) + 1rem);
|
||||
}
|
||||
|
||||
/* 로그인 전: 사이드바 없이 단일 컬럼 — 콘텐츠가 창 중앙에 오도록 */
|
||||
.layout.no-sidebar {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
// 카카오 애드핏 배너. 유료(PREMIUM) 회원에게는 표시하지 않는다.
|
||||
const props = defineProps({
|
||||
unit: { type: String, default: 'DAN-n0I5XRBOwAdSFbEX' },
|
||||
width: { type: Number, default: 728 },
|
||||
height: { type: Number, default: 90 },
|
||||
})
|
||||
|
||||
const auth = useAuthStore()
|
||||
const showAd = computed(() => !auth.isPremium)
|
||||
|
||||
const host = ref(null)
|
||||
const ADFIT_SRC = '//t1.kakaocdn.net/kas/static/ba.min.js'
|
||||
|
||||
onMounted(() => {
|
||||
if (!showAd.value || !host.value) return
|
||||
// SPA 에서 동적 삽입 시 애드핏이 인식하도록 ins 를 직접 만들고 로더를 다시 실행한다.
|
||||
const ins = document.createElement('ins')
|
||||
ins.className = 'kakao_ad_area'
|
||||
ins.style.display = 'none'
|
||||
ins.setAttribute('data-ad-unit', props.unit)
|
||||
ins.setAttribute('data-ad-width', String(props.width))
|
||||
ins.setAttribute('data-ad-height', String(props.height))
|
||||
host.value.appendChild(ins)
|
||||
|
||||
const s = document.createElement('script')
|
||||
s.type = 'text/javascript'
|
||||
s.src = ADFIT_SRC
|
||||
s.async = true
|
||||
host.value.appendChild(s)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="showAd" ref="host" class="ad-banner" :style="{ minHeight: height + 'px' }"></div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ad-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
overflow: hidden; /* 좁은 화면에서 가로 넘침 방지 */
|
||||
}
|
||||
</style>
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup>
|
||||
import AdBanner from '@/components/AdBanner.vue'
|
||||
// PC·모바일 웹에서 노출되는 안내 페이지. 실제 이용은 앱(모바일) 또는 PC 데스크톱 클라이언트에서.
|
||||
// PC 설치파일은 서버 /download/ 경로(영속 디렉터리)에서 서빙 — nginx 설정 참고.
|
||||
const PC_DOWNLOAD_URL = '/download/DonDwaeji-Setup.exe'
|
||||
@@ -38,6 +39,9 @@ const mobileReady = false
|
||||
<li><span class="f-icon">🎯</span><b>예산과 분석</b><span>예산 대비 지출·차트</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 하단 광고 (유료 회원 제외) -->
|
||||
<AdBanner class="web-ad" />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
@@ -45,13 +49,18 @@ const mobileReady = false
|
||||
.web-only {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1.75rem;
|
||||
padding: 1.5rem;
|
||||
background:
|
||||
radial-gradient(120% 120% at 50% 0%, hsla(160, 100%, 37%, 0.12), transparent 60%),
|
||||
var(--color-background);
|
||||
}
|
||||
.web-ad {
|
||||
margin-top: auto; /* 페이지 하단으로 */
|
||||
}
|
||||
.card {
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
|
||||
Reference in New Issue
Block a user