application [cat-mini-app] view page [pages/reservation] development
This commit is contained in:
@@ -29,6 +29,12 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "猫咪情况"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/reservation",
|
||||
"style": {
|
||||
"navigationBarTitleText": "预约排队"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
286
apps/cat-mini-app/src/pages/reservation.vue
Normal file
286
apps/cat-mini-app/src/pages/reservation.vue
Normal 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>
|
||||
Reference in New Issue
Block a user