application [cat-mini-app] view page [pages/reservation] development

This commit is contained in:
NVWA Code Agent
2025-12-11 16:43:04 +00:00
parent eacb2a8f28
commit 44cc8e98f2
2 changed files with 292 additions and 0 deletions

View File

@@ -29,6 +29,12 @@
"style": {
"navigationBarTitleText": "猫咪情况"
}
},
{
"path": "pages/reservation",
"style": {
"navigationBarTitleText": "预约排队"
}
}
],
"globalStyle": {

View File

@@ -0,0 +1,286 @@
<template>
<view class="reservation-page bg-gradient-to-b from-pink-50 to-orange-50 min-h-screen p-4">
<!-- 页面标题 -->
<view class="text-center mb-6">
<text class="text-2xl font-bold text-orange-600">预约排队 🐱</text>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="text-center py-8">
<uni-loading></uni-loading>
<text class="text-gray-500 mt-2">加载中...</text>
</view>
<!-- 未登录 -->
<view v-else-if="!user" class="text-center py-8">
<text class="text-gray-500">请先登录</text>
<button class="mt-4 bg-orange-500 text-white px-6 py-2 rounded-full" @tap="goToLogin">
去登录
</button>
</view>
<!-- 无预约信息 -->
<view v-else-if="!reservation" class="text-center py-8">
<text class="text-gray-500">您还没有预约信息</text>
<text class="text-sm text-gray-400 mt-2">请先交付定金参与排队</text>
</view>
<!-- 预约信息 -->
<view v-else class="space-y-6">
<!-- 排队状态卡片 -->
<uni-card class="rounded-xl shadow-md">
<view class="p-4">
<view class="flex items-center justify-between mb-3">
<text class="text-lg font-semibold text-gray-800">您的排队信息</text>
<view class="flex items-center">
<text class="text-sm text-gray-600 mr-2">{{ statusText }}</text>
<view :class="statusColor" class="w-3 h-3 rounded-full"></view>
</view>
</view>
<view class="space-y-2">
<view class="flex justify-between">
<text class="text-gray-600">队列顺序</text>
<text class="font-medium">{{ reservation.queue_order || '暂无' }}</text>
</view>
<view class="flex justify-between">
<text class="text-gray-600">定金金额</text>
<text class="font-medium">¥{{ reservation.deposit }}</text>
</view>
<view class="flex justify-between">
<text class="text-gray-600">预约时间</text>
<text class="font-medium">{{ formatDate(reservation.created_at) }}</text>
</view>
</view>
</view>
</uni-card>
<!-- 已选择猫咪 -->
<view v-if="selectedCat">
<uni-card class="rounded-xl shadow-md">
<view class="p-4">
<text class="text-lg font-semibold text-gray-800 mb-3">已选择的猫咪</text>
<view class="flex items-center space-x-3">
<image :src="selectedCat.image" class="w-16 h-16 rounded-lg object-cover" />
<view class="flex-1">
<text class="font-medium">{{ selectedCat.name }}</text>
<text class="text-sm text-gray-600">{{ selectedCat.age }}个月 · {{ selectedCat.gender === 'male' ? '公' : '母' }} · 等级{{ selectedCat.grade }}</text>
</view>
</view>
</view>
</uni-card>
</view>
<!-- 挑选猫咪 -->
<view v-else-if="canSelectCat">
<view class="mb-4">
<text class="text-lg font-semibold text-gray-800">🎉 轮到您挑选猫咪了</text>
<text class="text-sm text-gray-600 block mt-1">请选择一只心仪的猫咪</text>
</view>
<view v-if="availableCats.length === 0" class="text-center py-8">
<text class="text-gray-500">暂无可挑选的猫咪</text>
</view>
<view v-else class="space-y-3">
<uni-card v-for="cat in availableCats" :key="cat.id" class="rounded-xl shadow-md cursor-pointer hover:shadow-lg transition-shadow" @tap="selectCat(cat)">
<view class="p-4">
<view class="flex items-center space-x-3">
<image :src="cat.image" class="w-16 h-16 rounded-lg object-cover" />
<view class="flex-1">
<text class="font-medium">{{ cat.name }}</text>
<text class="text-sm text-gray-600">{{ cat.age }}个月 · {{ cat.gender === 'male' ? '公' : '母' }} · 等级{{ cat.grade }}</text>
<text class="text-xs text-gray-500 mt-1">{{ cat.description }}</text>
</view>
<view class="text-orange-500">
<text class="text-2xl">🐱</text>
</view>
</view>
</view>
</uni-card>
</view>
</view>
<!-- 等待中 -->
<view v-else class="text-center py-8">
<view class="text-6xl mb-4"></view>
<text class="text-lg font-medium text-gray-700">请耐心等待</text>
<text class="text-sm text-gray-500 block mt-2">前方还有 {{ reservation.queue_order - 1 }} 位客户正在挑选</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { auth, entities } from '@/lib/nvwa'
import redirectToLogin from '@/custom/redirect-to-login'
// 类型定义
interface Reservation {
id: number
user_id: string
cat_id: number | null
deposit: string
status: 'queuing' | 'reserved' | 'completed' | 'cancelled'
queue_order: number | null
created_at: string
}
interface Cat {
id: number
name: string
age: number
gender: 'male' | 'female'
grade: 'A' | 'B' | 'C' | 'D'
type: 'breeding_male' | 'breeding_female' | 'kitten'
is_available: boolean
description: string | null
image_ids: number[]
}
// 响应式数据
const user = ref(null)
const reservation = ref<Reservation | null>(null)
const availableCats = ref<Cat[]>([])
const selectedCat = ref<Cat | null>(null)
const loading = ref(true)
// 计算属性
const canSelectCat = ref(false)
const statusText = ref('')
const statusColor = ref('')
// 方法
const goToLogin = () => {
uni.navigateTo({ url: '/pages/user/login' })
}
const formatDate = (dateStr: string) => {
return new Date(dateStr).toLocaleDateString('zh-CN')
}
const getCatImage = (imageIds: number[]) => {
// 简单处理假设第一个图片ID对应URL这里需要根据实际逻辑获取
return imageIds.length > 0 ? `/static/cat-${imageIds[0]}.jpg` : '/static/default-cat.jpg'
}
const selectCat = async (cat: Cat) => {
if (!reservation.value) return
try {
const { error } = await entities
.from('reservation')
.update({ cat_id: cat.id, status: 'reserved' })
.eq('id', reservation.value.id)
if (error) throw error
// 更新本地状态
reservation.value.cat_id = cat.id
reservation.value.status = 'reserved'
selectedCat.value = cat
canSelectCat.value = false
uni.showToast({
title: '选择成功',
icon: 'success'
})
} catch (error) {
console.error('选择猫咪失败:', error)
uni.showToast({
title: '选择失败,请重试',
icon: 'error'
})
}
}
// 生命周期
onMounted(async () => {
try {
// 检查登录状态
user.value = await auth.currentUser()
if (!user.value) {
loading.value = false
return
}
// 查询预约信息
const { data: resData, error: resError } = await entities
.from('reservation')
.select('*')
.eq('user_id', user.value.id)
.single()
if (resError && resError.code !== 'PGRST116') { // PGRST116 是没有找到记录的错误码
throw resError
}
reservation.value = resData
if (reservation.value) {
// 设置状态文本和颜色
switch (reservation.value.status) {
case 'queuing':
statusText.value = '排队中'
statusColor.value = 'bg-yellow-400'
break
case 'reserved':
statusText.value = '已预约'
statusColor.value = 'bg-blue-400'
break
case 'completed':
statusText.value = '已完成'
statusColor.value = 'bg-green-400'
break
case 'cancelled':
statusText.value = '已取消'
statusColor.value = 'bg-red-400'
break
}
// 如果已预约,查询选择的猫咪
if (reservation.value.cat_id) {
const { data: catData, error: catError } = await entities
.from('cat')
.select('*')
.eq('id', reservation.value.cat_id)
.single()
if (catError) throw catError
selectedCat.value = {
...catData,
image: getCatImage(catData.image_ids)
}
} else if (reservation.value.queue_order === 1) {
// 如果队列顺序为1可以挑选猫咪
canSelectCat.value = true
// 查询可售卖的猫咪
const { data: catsData, error: catsError } = await entities
.from('cat')
.select('*')
.eq('is_available', true)
.eq('type', 'kitten')
if (catsError) throw catsError
availableCats.value = catsData.map(cat => ({
...cat,
image: getCatImage(cat.image_ids)
}))
}
}
} catch (error) {
console.error('加载数据失败:', error)
uni.showToast({
title: '加载失败',
icon: 'error'
})
} finally {
loading.value = false
}
})
</script>
<style scoped>
.reservation-page {
font-family: 'Arial', sans-serif;
}
</style>