feat: 프론트엔드 초기 스캐폴딩 (Vue3+TS+Quasar+Capacitor)
- 하늘색 파스텔 브랜드 테마(quasar-variables) - 스팟 지도/체크인/한줄평/메이트 홈 화면 - 강아지 프로필 + 댕비티아이 성향 태그 화면 - 구독 티어 스토어(Pinia) 및 FREE 티어 배너 광고(v-if 제어) - Capacitor(iOS/AOS) 설정, 빌드/타입체크 통과 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<!-- 지도 영역 (추후 네이버/카카오 지도 SDK 연동 자리) -->
|
||||
<q-card flat bordered class="map-placeholder flex flex-center q-mb-md">
|
||||
<div class="text-center text-grey-6">
|
||||
<q-icon name="map" size="48px" color="secondary" />
|
||||
<div class="q-mt-sm text-caption">지도 영역 (스팟 앵커 표시 예정)</div>
|
||||
</div>
|
||||
</q-card>
|
||||
|
||||
<!-- 체크인 -->
|
||||
<q-btn
|
||||
color="primary"
|
||||
class="full-width q-py-sm"
|
||||
unelevated
|
||||
icon="place"
|
||||
:label="checkedIn ? '체크인 완료 · 여기 있어요!' : '나 지금 여기 도착!'"
|
||||
@click="onCheckIn"
|
||||
/>
|
||||
|
||||
<!-- 스팟 한줄평 (네이버 지도식 태그형) -->
|
||||
<div class="text-subtitle1 text-weight-bold q-mt-lg q-mb-sm">
|
||||
한강공원 산책로 · 오늘의 한줄평
|
||||
</div>
|
||||
<div class="row q-gutter-sm">
|
||||
<q-chip
|
||||
v-for="review in spotReviews"
|
||||
:key="review"
|
||||
color="secondary"
|
||||
text-color="accent"
|
||||
icon="tag"
|
||||
>
|
||||
{{ review }}
|
||||
</q-chip>
|
||||
</div>
|
||||
|
||||
<!-- 현재 체크인 중인 메이트 -->
|
||||
<div class="text-subtitle1 text-weight-bold q-mt-lg q-mb-sm">지금 이 스팟의 산책 메이트</div>
|
||||
<q-card
|
||||
v-for="mate in mates"
|
||||
:key="mate.name"
|
||||
flat
|
||||
bordered
|
||||
class="q-mb-sm"
|
||||
>
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
<q-avatar color="secondary" text-color="accent" icon="pets" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label class="text-weight-medium">{{ mate.name }}</q-item-label>
|
||||
<q-item-label caption>{{ mate.traits.join(' · ') }}</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side>
|
||||
<q-btn dense flat round color="primary" icon="chat_bubble_outline" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-card>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useQuasar } from 'quasar'
|
||||
|
||||
const $q = useQuasar()
|
||||
const checkedIn = ref(false)
|
||||
|
||||
const spotReviews = ['진드기 많아요', '풀숲 무성함', '그늘 시원해요', '물그릇 있음']
|
||||
|
||||
const mates = [
|
||||
{ name: '콩이 (말티즈)', traits: ['대형견 무서워함', '사회화 훈련 중'] },
|
||||
{ name: '보리 (리트리버)', traits: ['터그놀이 매니아', '사람 좋아함'] },
|
||||
]
|
||||
|
||||
function onCheckIn() {
|
||||
checkedIn.value = !checkedIn.value
|
||||
$q.notify({
|
||||
color: checkedIn.value ? 'primary' : 'grey-6',
|
||||
message: checkedIn.value ? '체크인 완료! 스팟 채팅이 열렸어요.' : '체크아웃 되었습니다.',
|
||||
icon: checkedIn.value ? 'place' : 'logout',
|
||||
position: 'top',
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.map-placeholder {
|
||||
height: 240px;
|
||||
background: linear-gradient(160deg, #eaf5fe 0%, #d7ecfb 100%);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<!-- 강아지 프로필 카드 -->
|
||||
<q-card flat bordered class="q-mb-md">
|
||||
<q-card-section class="row items-center">
|
||||
<q-avatar size="64px" color="secondary" text-color="accent" icon="pets" />
|
||||
<div class="q-ml-md">
|
||||
<div class="text-h6">몽이</div>
|
||||
<div class="text-caption text-grey-7">푸들 · 3살 · 소형견</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
<!-- 댕비티아이(성향 태그) -->
|
||||
<div class="text-subtitle1 text-weight-bold q-mb-sm">댕비티아이 · 성향 태그</div>
|
||||
<div class="row q-gutter-sm q-mb-lg">
|
||||
<q-chip
|
||||
v-for="trait in traits"
|
||||
:key="trait.code"
|
||||
:color="trait.selected ? 'primary' : 'grey-3'"
|
||||
:text-color="trait.selected ? 'white' : 'grey-7'"
|
||||
clickable
|
||||
@click="trait.selected = !trait.selected"
|
||||
>
|
||||
{{ trait.label }}
|
||||
</q-chip>
|
||||
</div>
|
||||
|
||||
<!-- 구독 티어 전환 (개발용 데모) -->
|
||||
<q-card flat bordered>
|
||||
<q-card-section>
|
||||
<div class="text-subtitle2 q-mb-sm">구독 티어 (데모)</div>
|
||||
<q-btn-toggle
|
||||
v-model="tier"
|
||||
spread
|
||||
no-caps
|
||||
toggle-color="primary"
|
||||
color="grey-3"
|
||||
text-color="grey-8"
|
||||
:options="[
|
||||
{ label: 'FREE', value: 'FREE' },
|
||||
{ label: '광고제거', value: 'AD_FREE' },
|
||||
{ label: 'PREMIUM', value: 'PREMIUM' },
|
||||
]"
|
||||
@update:model-value="onTierChange"
|
||||
/>
|
||||
<div class="text-caption text-grey-6 q-mt-sm">
|
||||
FREE 에서만 하단 배너 광고가 노출됩니다.
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useSubscriptionStore, type SubscriptionTier } from '@/stores/subscription'
|
||||
|
||||
const subscription = useSubscriptionStore()
|
||||
const tier = ref<SubscriptionTier>(subscription.tier)
|
||||
|
||||
const traits = ref([
|
||||
{ code: 'FEAR_BIG_DOG', label: '대형견 무서워함', selected: true },
|
||||
{ code: 'TUG_LOVER', label: '터그놀이 매니아', selected: true },
|
||||
{ code: 'SOCIALIZING', label: '사회화 훈련 중', selected: false },
|
||||
{ code: 'LOVE_PEOPLE', label: '사람 좋아함', selected: true },
|
||||
{ code: 'SHY', label: '낯가림 있음', selected: false },
|
||||
])
|
||||
|
||||
function onTierChange(next: SubscriptionTier) {
|
||||
subscription.setTier(next)
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user