| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- <template>
- <div class="tree-detail-page">
- <van-nav-bar title="许愿树详情" left-text="返回" left-arrow @click-left="$router.back()" />
- <div v-if="tree">
- <div class="tree-banner" :style="{ background: treeGradient }">
- <span class="banner-emoji">{{ treeEmoji }}</span>
- <h2 class="banner-name">{{ tree.name }}</h2>
- </div>
- <div class="tree-header">
- <h2>{{ tree.name }}</h2>
- <p class="tree-addr"><van-icon name="location-o" /> {{ tree.address }}</p>
- <p class="tree-desc">{{ tree.description }}</p>
- <div class="tree-stats">
- <span>💬 {{ tree.totalWishes }} 个愿望</span>
- <span v-if="tree.distance != null" class="tree-dist">
- <van-tag :type="tree.isInRange ? 'success' : 'warning'" round size="medium">
- {{ tree.isInRange ? '可许愿' : '' }} {{ fmtDist(tree.distance) }}
- </van-tag>
- </span>
- </div>
- <van-button
- type="primary"
- block
- round
- :disabled="!locationStore.located"
- @click="goMakeWish"
- >
- 去许愿 🙏
- </van-button>
- </div>
- <van-tabs v-model:active="activeTab" sticky>
- <van-tab title="公开愿望">
- <van-pull-refresh v-model="refreshing" @refresh="loadWishes">
- <wish-card
- v-for="wish in wishes"
- :key="wish.id"
- :wish="wish"
- @click="goWish(wish.id)"
- />
- <van-empty v-if="wishes.length === 0" description="还没有人在这里许愿,快来第一个!" />
- </van-pull-refresh>
- </van-tab>
- </van-tabs>
- </div>
- <van-loading v-else class="loading" />
- <ChatWidget :tree-id="tree?.id" />
- </div>
- </template>
- <script setup lang="ts">
- import { ref, computed, onMounted } from 'vue'
- import { useRoute, useRouter } from 'vue-router'
- import { useLocationStore } from '@/stores/location'
- import { fetchTreeDetail } from '@/api/tree'
- import { fetchTreeWishes } from '@/api/wish'
- import WishCard from '@/components/WishCard.vue'
- import ChatWidget from '@/components/ChatWidget.vue'
- import { getTreeGradient, getTreeEmoji } from '@/utils/theme'
- import type { WishingTree } from '@/mock/data'
- import type { Wish } from '@/mock/data'
- const route = useRoute()
- const router = useRouter()
- const locationStore = useLocationStore()
- const tree = ref<WishingTree | null>(null)
- const wishes = ref<Wish[]>([])
- const activeTab = ref(0)
- const refreshing = ref(false)
- const treeGradient = computed(() => getTreeGradient(tree.value?.id || 1))
- const treeEmoji = computed(() => getTreeEmoji(tree.value?.id || 1))
- async function loadWishes() {
- const id = Number(route.params.id)
- const result = await fetchTreeWishes(id)
- wishes.value = result.list
- refreshing.value = false
- }
- function goMakeWish() {
- router.push({ path: '/make-wish', query: { treeId: route.params.id } })
- }
- function fmtDist(m: number) {
- return m >= 1000 ? (m / 1000).toFixed(1) + 'km' : m + 'm'
- }
- function goWish(id: number) {
- router.push(`/wish/${id}`)
- }
- onMounted(async () => {
- const id = Number(route.params.id)
- if (!locationStore.located) {
- await locationStore.refreshLocation()
- }
- tree.value = await fetchTreeDetail(id, locationStore.lng ?? undefined, locationStore.lat ?? undefined)
- loadWishes()
- })
- </script>
- <style scoped>
- .tree-detail-page {
- min-height: 100vh;
- background: #f7f8fa;
- }
- .tree-banner {
- width: 100%;
- height: 200px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 8px;
- color: #fff;
- }
- .banner-emoji {
- font-size: 48px;
- text-shadow: 0 1px 4px rgba(0,0,0,0.2);
- }
- .banner-name {
- font-size: 20px;
- font-weight: 600;
- color: #fff;
- margin: 0;
- text-shadow: 0 1px 3px rgba(0,0,0,0.2);
- }
- .tree-header {
- padding: 16px;
- background: #fff;
- margin-bottom: 8px;
- }
- .tree-header h2 {
- font-size: 20px;
- margin-bottom: 8px;
- }
- .tree-addr {
- font-size: 13px;
- color: #666;
- margin-bottom: 10px;
- display: flex;
- align-items: center;
- gap: 4px;
- }
- .tree-desc {
- font-size: 14px;
- color: #555;
- line-height: 1.6;
- margin-bottom: 12px;
- }
- .tree-stats {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16px;
- font-size: 13px;
- }
- .tree-dist {
- margin-left: 8px;
- }
- .loading {
- margin: 100px auto;
- }
- </style>
|