FurBall/pages/discovery/hospital-list.vue

241 lines
5.4 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="hospital-page">
<view class="top-card">
<view>
<text class="top-title">附近医院</text>
<text class="top-desc">保存常去医院紧急情况更快找到联系方式</text>
</view>
<TnIcon name="safe-fill" :size="52" color="#F97316" />
</view>
<view v-if="loading" class="loading-state">
<TnLoading :show="true" mode="circle" color="#F97316" :size="54" />
<text class="muted-text">正在查找医院...</text>
</view>
<view v-else-if="hospitalList.length === 0" class="empty-state">
<TnEmpty mode="data" color="#F97316" size="xl" />
<text class="empty-title">暂无医院数据</text>
<text class="muted-text">可以稍后再试,或在地图 App 中搜索附近宠物医院。</text>
</view>
<scroll-view v-else scroll-y class="hospital-scroll">
<view class="hospital-card" v-for="hospital in hospitalList" :key="hospital._id">
<view class="card-head">
<view class="hospital-icon">
<TnIcon name="safe-fill" :size="34" color="#F97316" />
</view>
<view class="hospital-main">
<view class="title-row">
<text class="hospital-name">{{ hospital.name }}</text>
<TnTag v-if="hospital.verified" type="success" size="sm" shape="round">认证</TnTag>
<TnTag v-if="hospital.is24Hour" type="danger" size="sm" shape="round">24h</TnTag>
</view>
<text class="hospital-address">{{ hospital.address || '暂无地址' }}</text>
</view>
</view>
<view class="meta-row">
<text class="rating">评分 {{ hospital.rating || '-' }}</text>
<text class="hours">{{ hospital.hours || '营业时间待确认' }}</text>
</view>
<view class="tag-row" v-if="hospital.tags?.length">
<TnTag v-for="tag in hospital.tags" :key="tag" bg-color="#FFF7ED" text-color="#F97316" size="sm" shape="round">
{{ tag }}
</TnTag>
</view>
<TnButton v-if="hospital.phone" text-color="#FFFFFF" shape="round" :bold="true"
:custom-style="primaryButtonStyle" width="100%" height="76" @click="callHospital(hospital.phone)">
<TnIcon name="tel" :size="24" />
<text class="btn-text">拨打电话</text>
</TnButton>
</view>
</scroll-view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import TnButton from '@/uni_modules/tuniaoui-vue3/components/button/src/button.vue'
import TnEmpty from '@/uni_modules/tuniaoui-vue3/components/empty/src/empty.vue'
import TnIcon from '@/uni_modules/tuniaoui-vue3/components/icon/src/icon.vue'
import TnLoading from '@/uni_modules/tuniaoui-vue3/components/loading/src/loading.vue'
import TnTag from '@/uni_modules/tuniaoui-vue3/components/tag/src/tag.vue'
interface Hospital {
_id: string
name: string
address?: string
phone?: string
hours?: string
is24Hour?: boolean
tags?: string[]
rating?: number
verified?: boolean
}
const loading = ref(true)
const hospitalList = ref<Hospital[]>([])
const primaryButtonStyle = {
background: 'linear-gradient(135deg, #F97316, #FB923C)',
}
onLoad(() => {
loadHospitals()
})
async function loadHospitals() {
loading.value = true
try {
const db = uniCloud.database()
const res = await db.collection('hospitals').where({ status: 'active' }).limit(20).get()
hospitalList.value = (res.result?.data || res.data || []) as Hospital[]
} catch (e) {
console.warn('加载医院列表失败', e)
hospitalList.value = []
} finally {
loading.value = false
}
}
function callHospital(phone: string) {
uni.makePhoneCall({ phoneNumber: phone })
}
</script>
<style lang="scss" scoped>
$text-dark: #0F172A;
$text-body: #475569;
$text-muted: #64748B;
$card-bg: #FFFFFF;
$shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
.hospital-page {
min-height: 100vh;
background: #F8FAFC;
padding: 24rpx 24rpx 48rpx;
box-sizing: border-box;
}
.top-card {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24rpx;
padding: 28rpx;
border-radius: 28rpx;
background: #FFFFFF;
box-shadow: $shadow;
}
.top-title {
display: block;
font-size: 38rpx;
font-weight: 900;
color: $text-dark;
}
.top-desc {
display: block;
margin-top: 8rpx;
font-size: 25rpx;
line-height: 1.5;
color: $text-muted;
}
.hospital-scroll {
height: calc(100vh - 190rpx);
margin-top: 22rpx;
}
.hospital-card {
margin-bottom: 18rpx;
padding: 24rpx;
border-radius: 24rpx;
background: $card-bg;
box-shadow: $shadow;
}
.card-head {
display: flex;
align-items: flex-start;
gap: 18rpx;
}
.hospital-icon {
width: 72rpx;
height: 72rpx;
border-radius: 20rpx;
background: #FFF7ED;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.hospital-main {
flex: 1;
min-width: 0;
}
.title-row {
display: flex;
align-items: center;
gap: 10rpx;
}
.hospital-name {
flex: 1;
font-size: 30rpx;
font-weight: 800;
color: $text-dark;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.hospital-address,
.muted-text {
font-size: 25rpx;
color: $text-muted;
line-height: 1.55;
}
.meta-row,
.tag-row {
display: flex;
flex-wrap: wrap;
gap: 10rpx;
margin-top: 16rpx;
}
.rating,
.hours {
padding: 8rpx 14rpx;
border-radius: 999rpx;
background: #F8FAFC;
font-size: 22rpx;
color: $text-body;
}
.btn-text {
margin-left: 8rpx;
}
.loading-state,
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
gap: 18rpx;
padding: 140rpx 48rpx 0;
text-align: center;
}
.empty-title {
font-size: 32rpx;
font-weight: 800;
color: $text-dark;
}
</style>